From 1d21f73643002d98c458c04cb031e3856af8edfa Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 12:57:23 +0530 Subject: [PATCH 01/39] docs: design LinkedIn post-comments command --- docs/linkedin-post-comments-design.mdx | 69 ++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 docs/linkedin-post-comments-design.mdx diff --git a/docs/linkedin-post-comments-design.mdx b/docs/linkedin-post-comments-design.mdx new file mode 100644 index 00000000..a3c9c44a --- /dev/null +++ b/docs/linkedin-post-comments-design.mdx @@ -0,0 +1,69 @@ +# LinkedIn Post Comments Command + +## Goal + +Add a read-only command that collects every visible participant in one exact LinkedIn post's comment threads, including reply authors, and deduplicates them by canonical LinkedIn profile URL. + +## Command + +```bash +webcmd linkedin post-comments [--limit N] +``` + +- `` must be an HTTPS LinkedIn post URL under `/feed/update/` or `/posts/`. +- Without `--limit`, the command loads comments and replies until LinkedIn exposes no more. +- With `--limit N`, it returns the first `N` unique profile URLs and stops without treating the intentional truncation as an error. + +## Strategy + +Strategy: `UI_SELECTOR` with DOM extraction +Contract: visible UI + +Evidence from the supplied post: + +- Rendered comments use stable `replaceableComment_urn:li:comment:...` containers. +- Each container exposes its author's exact `/in//` link, name, headline, relative timestamp, and comment text. +- Reply pagination is exposed through visible `See previous replies` controls. +- The observed network alternative is LinkedIn's internal `flagship-web/rsc-action/actions/pagination` action, whose private action payload is a higher-drift contract. + +The command will therefore navigate to the exact post, expand reply controls, advance the post's scrollable workspace, and extract rendered comment containers. It will not replay or reverse engineer LinkedIn's internal RSC action. + +## Output + +One row per canonical profile URL, in first-seen order: + +| Column | Meaning | +| --- | --- | +| `rank` | First-seen position after deduplication | +| `name` | Visible commenter name | +| `headline` | Visible LinkedIn headline | +| `profile_url` | Canonical `https://www.linkedin.com/in//` identity | +| `comment_count` | Number of loaded comments or replies authored by this profile | +| `sample_comment` | First loaded comment text from this profile | +| `commented_at` | Visible relative timestamp for the sample comment | +| `source_post` | Canonical input post URL | + +Mentioned profile links inside comment text are not identities: the first author profile link inside each comment container is authoritative. + +## Data flow + +1. Validate and canonicalize the exact post URL. +2. Open it in the authenticated LinkedIn browser session and reject auth walls. +3. Extract currently rendered comment containers. +4. Expand every visible `See previous replies` control and advance the post workspace. +5. Repeat while new comment containers or pagination controls appear, or until `--limit` unique profiles have been collected. +6. Normalize rows, aggregate duplicate authors, and return first-seen order. + +The unbounded path stops only after a full iteration produces no new comment containers and no actionable reply or pagination controls. + +## Errors + +- Invalid or non-LinkedIn post URL: `ArgumentError`. +- Missing browser session or malformed rendered payload: `CommandExecutionError`. +- Login or checkpoint page: `AuthRequiredError`. +- No visible comments after the post finishes loading: `EmptyResultError`. +- A requested `--limit` is intentional truncation and never an error. + +## Verification + +Use TDD with focused adapter tests for command registration, URL validation, comment parsing, reply-author inclusion, canonical profile deduplication, duplicate aggregation, limit behavior, and malformed/auth/empty states. Then run the LinkedIn adapter test, repository audits, full unit and adapter suite, build, and a live read-only invocation against the supplied post. From c1019eb5fbd2862967bee058c36fe53cc6ffeb80 Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 13:06:35 +0530 Subject: [PATCH 02/39] feat: add LinkedIn post-comments command --- clis/linkedin/__fixtures__/post-comments.html | 33 ++ clis/linkedin/post-comments.js | 307 ++++++++++++++++++ clis/linkedin/post-comments.test.js | 262 +++++++++++++++ 3 files changed, 602 insertions(+) create mode 100644 clis/linkedin/__fixtures__/post-comments.html create mode 100644 clis/linkedin/post-comments.js create mode 100644 clis/linkedin/post-comments.test.js diff --git a/clis/linkedin/__fixtures__/post-comments.html b/clis/linkedin/__fixtures__/post-comments.html new file mode 100644 index 00000000..aab7f073 --- /dev/null +++ b/clis/linkedin/__fixtures__/post-comments.html @@ -0,0 +1,33 @@ + + + +
+
+ + + + +

Alice Example

+

CTO at Acme

+
+

2d

+ Top-level comment + + +
+
+ + diff --git a/clis/linkedin/post-comments.js b/clis/linkedin/post-comments.js new file mode 100644 index 00000000..550e1697 --- /dev/null +++ b/clis/linkedin/post-comments.js @@ -0,0 +1,307 @@ +import { cli, Strategy } from '@agentrhq/webcmd/registry'; +import { + ArgumentError, + AuthRequiredError, + CommandExecutionError, + EmptyResultError, +} from '@agentrhq/webcmd/errors'; +import { + assertLinkedInAuthenticated, + normalizeWhitespace, + unwrapEvaluateResult, +} from './shared.js'; + +const LINKEDIN_DOMAIN = 'www.linkedin.com'; +const MAX_ROUNDS = 200; +const COLUMNS = [ + 'rank', + 'name', + 'headline', + 'profile_url', + 'comment_count', + 'sample_comment', + 'commented_at', + 'source_post', +]; + +function canonicalizePostUrl(value) { + const raw = normalizeWhitespace(value); + let parsed; + try { + parsed = new URL(raw); + } catch { + throw new ArgumentError('post-url must be an exact LinkedIn post URL'); + } + const host = parsed.hostname.toLowerCase(); + const feedPath = /^\/feed\/update\/urn:li:activity:\d+\/?$/i.test(parsed.pathname); + const postsPath = /^\/posts\/[^/?#]+\/?$/i.test(parsed.pathname); + if ( + parsed.protocol !== 'https:' + || parsed.username + || parsed.password + || parsed.port + || (host !== 'linkedin.com' && host !== LINKEDIN_DOMAIN) + || (!feedPath && !postsPath) + ) { + throw new ArgumentError('post-url must be an exact HTTPS LinkedIn /feed/update/ or /posts/ URL'); + } + parsed.hostname = LINKEDIN_DOMAIN; + parsed.search = ''; + parsed.hash = ''; + if (!parsed.pathname.endsWith('/')) parsed.pathname += '/'; + return parsed.toString(); +} + +function parseOptionalLimit(value) { + if (value === undefined || value === null || value === '') return null; + const limit = Number(value); + if (!Number.isInteger(limit) || limit < 1) { + throw new ArgumentError('--limit must be a positive integer'); + } + return limit; +} + +function canonicalizeProfileUrl(value) { + try { + const parsed = new URL(normalizeWhitespace(value), `https://${LINKEDIN_DOMAIN}`); + const host = parsed.hostname.toLowerCase(); + const match = parsed.pathname.match(/^\/in\/([^/?#]+)\/?$/i); + if ( + parsed.protocol !== 'https:' + || parsed.username + || parsed.password + || parsed.port + || (host !== 'linkedin.com' && host !== LINKEDIN_DOMAIN) + || !match + ) return ''; + return `https://${LINKEDIN_DOMAIN}/in/${match[1]}/`; + } catch { + return ''; + } +} + +function buildCommentRoundScript() { + return String.raw`(() => { + const clean = (value) => String(value || '').replace(/[\u00a0\u202f]+/g, ' ').replace(/\s+/g, ' ').trim(); + const commentSelector = '[id^="replaceableComment_"]'; + const owns = (node, element) => element && element.closest(commentSelector) === node; + const authRequired = /linkedin\.com\/(?:login|checkpoint|authwall|uas)/i.test(location.href) + || /\b(sign in|log in|join linkedin|captcha|verification required)\b/i.test(document.body?.innerText || ''); + const nodes = Array.from(document.querySelectorAll(commentSelector)); + const rows = nodes.map((node) => { + const links = Array.from(node.querySelectorAll('a[href*="/in/"]')).filter((link) => owns(node, link)); + const rawProfileUrl = links[0]?.href || ''; + const identity = links.find((link) => link.href === rawProfileUrl && clean(link.textContent)) || links[0]; + const labels = Array.from(node.querySelectorAll('[aria-label]')) + .filter((element) => owns(node, element)) + .map((element) => clean(element.getAttribute('aria-label'))); + const rawName = labels + .map((label) => label.match(/^View (.+?)[’']s profile$/i)?.[1] || '') + .find(Boolean) + || Array.from(identity?.querySelectorAll('p') || []) + .map((paragraph) => clean(paragraph.textContent)) + .find((text) => text && !/^(author|verified profile|[•·]?\s*(?:1st|2nd|3rd))/i.test(text)) + || ''; + const paragraphs = Array.from(identity?.querySelectorAll('p') || []) + .map((paragraph) => clean(paragraph.textContent)) + .filter(Boolean); + const rawHeadline = paragraphs + .filter((text) => text !== rawName && !/^(author|verified profile|[•·]?\s*(?:1st|2nd|3rd))/i.test(text)) + .sort((left, right) => right.length - left.length)[0] + || ''; + const textBox = Array.from(node.querySelectorAll('[data-testid="expandable-text-box"]')) + .find((element) => owns(node, element)); + const ownText = Array.from(node.querySelectorAll('p, span')) + .filter((element) => owns(node, element)) + .map((element) => clean(element.textContent)); + const rawCommentedAt = ownText + .find((text) => /^\d+\s*(?:s|m|h|d|w|mo|yr)(?:\s*•.*)?$/i.test(text)) + || ''; + return { + rawId: node.id, + rawName, + rawHeadline, + rawProfileUrl, + rawComment: clean(textBox?.textContent), + rawCommentedAt, + }; + }); + const controls = Array.from(document.querySelectorAll('button, [role="button"]')).filter((element) => { + const text = clean(element.textContent || element.getAttribute('aria-label')); + return /^(?:see previous replies|(?:load|show|see) more comments?)$/i.test(text); + }); + for (const control of controls) { + try { control.click(); } catch {} + } + const workspace = document.querySelector('#workspace'); + let atEnd = true; + if (workspace && workspace.scrollHeight > workspace.clientHeight) { + const bottom = workspace.scrollHeight - workspace.clientHeight; + workspace.scrollTop = bottom; + atEnd = workspace.scrollTop >= bottom - 2; + } else { + window.scrollTo(0, document.documentElement.scrollHeight); + atEnd = window.scrollY + window.innerHeight >= document.documentElement.scrollHeight - 2; + } + return { + rows, + authRequired, + commentNodeCount: nodes.length, + replyControlsClicked: controls.length, + atEnd, + url: location.href, + }; + })()`; +} + +function normalizeCommentRows(rows, sourcePost) { + if (!Array.isArray(rows)) { + throw new CommandExecutionError('LinkedIn post-comments returned malformed rows'); + } + const people = new Map(); + for (const [index, row] of rows.entries()) { + if (!row || typeof row !== 'object') { + throw new CommandExecutionError(`LinkedIn post-comments returned malformed row at index ${index}`); + } + const rawId = normalizeWhitespace(row.rawId); + const name = normalizeWhitespace(row.rawName); + const profileUrl = canonicalizeProfileUrl(row.rawProfileUrl); + if (!rawId || !name || !profileUrl) { + throw new CommandExecutionError(`LinkedIn post-comments returned row without stable profile identity at index ${index}`); + } + const existing = people.get(profileUrl); + if (existing) { + existing.comment_count += 1; + continue; + } + people.set(profileUrl, { + rank: people.size + 1, + name, + headline: normalizeWhitespace(row.rawHeadline), + profile_url: profileUrl, + comment_count: 1, + sample_comment: normalizeWhitespace(row.rawComment), + commented_at: normalizeWhitespace(row.rawCommentedAt), + source_post: sourcePost, + }); + } + return Array.from(people.values()); +} + +async function collectPostComments(page, args) { + if (!page) throw new CommandExecutionError('Browser session required for linkedin post-comments'); + const sourcePost = canonicalizePostUrl(args?.['post-url']); + const limit = parseOptionalLimit(args?.limit); + try { + await page.goto(sourcePost); + await page.wait(3); + } catch (error) { + throw new CommandExecutionError(`LinkedIn post-comments navigation failed: ${error?.message || error}`); + } + try { + await assertLinkedInAuthenticated(page, 'LinkedIn post-comments'); + } catch (error) { + if (error instanceof AuthRequiredError) throw error; + throw new CommandExecutionError(`LinkedIn post-comments authentication check failed: ${error?.message || error}`); + } + + const commentsById = new Map(); + let stableRounds = 0; + for (let round = 0; round < MAX_ROUNDS; round++) { + let payload; + try { + payload = unwrapEvaluateResult(await page.evaluate(buildCommentRoundScript())); + } catch (error) { + throw new CommandExecutionError(`LinkedIn post-comments extraction failed: ${error?.message || error}`); + } + if (payload?.authRequired) { + throw new AuthRequiredError( + LINKEDIN_DOMAIN, + 'LinkedIn post-comments requires an active signed-in browser session.', + ); + } + if ( + !payload + || !Array.isArray(payload.rows) + || !Number.isInteger(payload.commentNodeCount) + || payload.commentNodeCount < 0 + || !Number.isInteger(payload.replyControlsClicked) + || payload.replyControlsClicked < 0 + || typeof payload.atEnd !== 'boolean' + ) { + throw new CommandExecutionError('LinkedIn post-comments returned malformed extraction payload'); + } + let actualPost; + try { + actualPost = canonicalizePostUrl(payload.url); + } catch { + throw new CommandExecutionError('LinkedIn post-comments extraction ended outside an exact LinkedIn post URL'); + } + if (actualPost !== sourcePost) { + throw new CommandExecutionError(`LinkedIn post-comments post URL mismatch: expected ${sourcePost}; actual ${actualPost}`); + } + + let newComments = 0; + for (const row of payload.rows) { + const id = normalizeWhitespace(row?.rawId); + if (!id) { + throw new CommandExecutionError('LinkedIn post-comments returned a comment without a stable id'); + } + if (!commentsById.has(id)) { + commentsById.set(id, row); + newComments += 1; + } + } + const normalized = normalizeCommentRows(Array.from(commentsById.values()), sourcePost); + if (limit && normalized.length >= limit) return normalized.slice(0, limit); + + const exhausted = newComments === 0 && payload.replyControlsClicked === 0 && payload.atEnd; + stableRounds = exhausted ? stableRounds + 1 : 0; + if (stableRounds >= 2) { + if (normalized.length === 0) { + throw new EmptyResultError( + 'linkedin post-comments', + 'No visible comments were found on the LinkedIn post.', + ); + } + return normalized; + } + await page.wait(1); + } + throw new CommandExecutionError(`LinkedIn post-comments did not reach a stable end after ${MAX_ROUNDS} rounds`); +} + +cli({ + site: 'linkedin', + name: 'post-comments', + access: 'read', + description: 'List unique commenters and reply authors from one exact LinkedIn post URL', + domain: LINKEDIN_DOMAIN, + strategy: Strategy.COOKIE, + browser: true, + args: [ + { + name: 'post-url', + type: 'string', + positional: true, + required: true, + help: 'Exact LinkedIn post URL', + }, + { + name: 'limit', + type: 'int', + required: false, + help: 'Maximum unique commenters to return; omit to fetch all', + }, + ], + columns: COLUMNS, + func: collectPostComments, +}); + +export const __test__ = { + canonicalizePostUrl, + parseOptionalLimit, + canonicalizeProfileUrl, + buildCommentRoundScript, + normalizeCommentRows, +}; diff --git a/clis/linkedin/post-comments.test.js b/clis/linkedin/post-comments.test.js new file mode 100644 index 00000000..e18c0752 --- /dev/null +++ b/clis/linkedin/post-comments.test.js @@ -0,0 +1,262 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { describe, expect, it, vi } from 'vitest'; +import { JSDOM } from 'jsdom'; +import { getRegistry } from '@agentrhq/webcmd/registry'; +import { + ArgumentError, + AuthRequiredError, + CommandExecutionError, + EmptyResultError, +} from '@agentrhq/webcmd/errors'; +import './post-comments.js'; + +const { + canonicalizePostUrl, + parseOptionalLimit, + buildCommentRoundScript, + normalizeCommentRows, +} = await import('./post-comments.js').then((module) => module.__test__); + +const rawComment = (id, handle, name, comment, overrides = {}) => ({ + rawId: `comment-${id}`, + rawName: name, + rawHeadline: `${name} headline`, + rawProfileUrl: `https://www.linkedin.com/in/${handle}/`, + rawComment: comment, + rawCommentedAt: '1d', + ...overrides, +}); + +const round = (rows, overrides = {}) => ({ + rows, + authRequired: false, + commentNodeCount: rows.length, + replyControlsClicked: 0, + atEnd: true, + url: 'https://www.linkedin.com/posts/source/', + ...overrides, +}); + +function makePage(rounds, { authProbe = false, gotoError, evaluateError } = {}) { + const queue = [authProbe, ...rounds]; + return { + goto: vi.fn().mockImplementation(async () => { + if (gotoError) throw gotoError; + }), + wait: vi.fn().mockResolvedValue(undefined), + evaluate: vi.fn().mockImplementation(async () => { + if (evaluateError) throw evaluateError; + return queue.length > 1 ? queue.shift() : queue[0]; + }), + }; +} + +describe('linkedin post-comments', () => { + it('registers a read-only positional command with the exact row contract', () => { + const command = getRegistry().get('linkedin/post-comments'); + expect(command).toMatchObject({ + access: 'read', + browser: true, + strategy: 'cookie', + columns: [ + 'rank', + 'name', + 'headline', + 'profile_url', + 'comment_count', + 'sample_comment', + 'commented_at', + 'source_post', + ], + }); + expect(command.args.find((arg) => arg.name === 'post-url')).toMatchObject({ + positional: true, + required: true, + }); + expect(command.args.find((arg) => arg.name === 'limit').default).toBeUndefined(); + }); + + it('canonicalizes only exact HTTPS LinkedIn post URLs', () => { + expect(canonicalizePostUrl('https://linkedin.com/feed/update/urn:li:activity:7489324344997867521/?x=1')) + .toBe('https://www.linkedin.com/feed/update/urn:li:activity:7489324344997867521/'); + expect(canonicalizePostUrl('https://www.linkedin.com/posts/example_activity-123-abcd')) + .toBe('https://www.linkedin.com/posts/example_activity-123-abcd/'); + for (const value of [ + 'https://evil-linkedin.com/posts/x', + 'http://linkedin.com/posts/x', + 'https://linkedin.com/in/person/', + 'https://user:pass@linkedin.com/posts/x', + ]) { + expect(() => canonicalizePostUrl(value)).toThrow(ArgumentError); + } + }); + + it('keeps limit optional and rejects non-positive integers', () => { + expect(parseOptionalLimit(undefined)).toBeNull(); + expect(parseOptionalLimit('')).toBeNull(); + expect(parseOptionalLimit(3)).toBe(3); + for (const value of [0, -1, 1.5, 'x']) { + expect(() => parseOptionalLimit(value)).toThrow(ArgumentError); + } + }); + + it('extracts top-level and reply authors without promoting mentioned profiles', () => { + const html = fs.readFileSync(path.join(import.meta.dirname, '__fixtures__/post-comments.html'), 'utf8'); + const dom = new JSDOM(html, { + runScripts: 'outside-only', + url: 'https://www.linkedin.com/feed/update/urn:li:activity:1/', + }); + const workspace = dom.window.document.querySelector('#workspace'); + Object.defineProperties(workspace, { + scrollHeight: { value: 1000 }, + clientHeight: { value: 500 }, + scrollTop: { value: 0, writable: true }, + }); + const payload = dom.window.eval(buildCommentRoundScript()); + + expect(payload.rows).toEqual([ + { + rawId: 'replaceableComment_urn:li:comment:(urn:li:activity:1,101)', + rawName: 'Alice Example', + rawHeadline: 'CTO at Acme', + rawProfileUrl: 'https://www.linkedin.com/in/alice-example/', + rawComment: 'Top-level comment', + rawCommentedAt: '2d', + }, + { + rawId: 'replaceableComment_urn:li:comment:(urn:li:activity:1,102)', + rawName: 'Bob Builder', + rawHeadline: 'Founder at BuildCo', + rawProfileUrl: 'https://www.linkedin.com/in/bob-builder/', + rawComment: 'Mentioned Person Reply comment', + rawCommentedAt: '1d', + }, + ]); + expect(payload.replyControlsClicked).toBe(1); + expect(payload.atEnd).toBe(true); + }); + + it('deduplicates canonical profiles and counts distinct comments', () => { + const rows = normalizeCommentRows([ + rawComment(1, 'alice', 'Alice', 'First', { + rawProfileUrl: 'https://linkedin.com/in/alice/?x=1', + rawCommentedAt: '2d', + }), + rawComment(2, 'alice', 'Alice', 'Second'), + ], 'https://www.linkedin.com/posts/source/'); + + expect(rows).toEqual([{ + rank: 1, + name: 'Alice', + headline: 'Alice headline', + profile_url: 'https://www.linkedin.com/in/alice/', + comment_count: 2, + sample_comment: 'First', + commented_at: '2d', + source_post: 'https://www.linkedin.com/posts/source/', + }]); + }); + + it('rejects rendered comments without a stable identity', () => { + expect(() => normalizeCommentRows([ + rawComment(1, 'alice', '', 'Missing name', { rawProfileUrl: '' }), + ], 'https://www.linkedin.com/posts/source/')).toThrow(CommandExecutionError); + expect(() => normalizeCommentRows({}, 'https://www.linkedin.com/posts/source/')) + .toThrow(CommandExecutionError); + }); + + it('continues without a limit until two exhausted rounds are stable', async () => { + const command = getRegistry().get('linkedin/post-comments'); + const alice = rawComment(1, 'alice', 'Alice', 'First'); + const bob = rawComment(2, 'bob', 'Bob', 'Reply'); + const page = makePage([ + round([alice], { replyControlsClicked: 1, atEnd: false }), + round([alice, bob]), + round([alice, bob]), + round([alice, bob]), + ]); + + const rows = await command.func(page, { 'post-url': 'https://www.linkedin.com/posts/source/' }); + + expect(rows.map((row) => row.profile_url)).toEqual([ + 'https://www.linkedin.com/in/alice/', + 'https://www.linkedin.com/in/bob/', + ]); + expect(page.evaluate).toHaveBeenCalledTimes(5); + }); + + it('stops as soon as the optional unique-profile limit is reached', async () => { + const command = getRegistry().get('linkedin/post-comments'); + const page = makePage([round([ + rawComment(1, 'alice', 'Alice', 'First'), + rawComment(2, 'bob', 'Bob', 'Second'), + ], { replyControlsClicked: 1, atEnd: false })]); + + const rows = await command.func(page, { + 'post-url': 'https://www.linkedin.com/posts/source/', + limit: 1, + }); + + expect(rows).toHaveLength(1); + expect(rows[0].profile_url).toBe('https://www.linkedin.com/in/alice/'); + expect(page.evaluate).toHaveBeenCalledTimes(2); + }); + + it('maps authentication walls to AuthRequiredError', async () => { + const command = getRegistry().get('linkedin/post-comments'); + const page = makePage([round([], { authRequired: true })]); + await expect(command.func(page, { 'post-url': 'https://www.linkedin.com/posts/source/' })) + .rejects.toBeInstanceOf(AuthRequiredError); + }); + + it('returns EmptyResultError only after a stable empty page', async () => { + const command = getRegistry().get('linkedin/post-comments'); + const page = makePage([round([]), round([])]); + await expect(command.func(page, { 'post-url': 'https://www.linkedin.com/posts/source/' })) + .rejects.toBeInstanceOf(EmptyResultError); + }); + + it('fails closed for malformed extraction payloads', async () => { + const command = getRegistry().get('linkedin/post-comments'); + for (const payload of [null, {}, { rows: {}, replyControlsClicked: 0, atEnd: true }]) { + const page = makePage([payload]); + await expect(command.func(page, { 'post-url': 'https://www.linkedin.com/posts/source/' })) + .rejects.toBeInstanceOf(CommandExecutionError); + } + }); + + it('fails closed when LinkedIn lands on a different post', async () => { + const command = getRegistry().get('linkedin/post-comments'); + const page = makePage([round([], { url: 'https://www.linkedin.com/posts/different/' })]); + await expect(command.func(page, { 'post-url': 'https://www.linkedin.com/posts/source/' })) + .rejects.toThrow('post URL mismatch'); + }); + + it('requires a browser session', async () => { + const command = getRegistry().get('linkedin/post-comments'); + await expect(command.func(null, { 'post-url': 'https://www.linkedin.com/posts/source/' })) + .rejects.toBeInstanceOf(CommandExecutionError); + }); + + it('fails instead of looping forever when the page never exhausts', async () => { + const command = getRegistry().get('linkedin/post-comments'); + const page = makePage([round([ + rawComment(1, 'alice', 'Alice', 'First'), + ], { atEnd: false })]); + await expect(command.func(page, { 'post-url': 'https://www.linkedin.com/posts/source/' })) + .rejects.toThrow('did not reach a stable end after 200 rounds'); + }); + + it('wraps navigation and extraction failures as CommandExecutionError', async () => { + const command = getRegistry().get('linkedin/post-comments'); + await expect(command.func( + makePage([], { gotoError: new Error('navigation failed') }), + { 'post-url': 'https://www.linkedin.com/posts/source/' }, + )).rejects.toBeInstanceOf(CommandExecutionError); + await expect(command.func( + makePage([], { evaluateError: new Error('evaluate failed') }), + { 'post-url': 'https://www.linkedin.com/posts/source/' }, + )).rejects.toBeInstanceOf(CommandExecutionError); + }); +}); From 01dd7bc39ab514833ac6b1bee981db2e1ff51500 Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 13:24:03 +0530 Subject: [PATCH 03/39] fix: complete LinkedIn comment collection --- cli-manifest.json | 38 +++++++++++ clis/linkedin/__fixtures__/post-comments.html | 14 +++- clis/linkedin/post-comments.js | 45 +++++++++++-- clis/linkedin/post-comments.test.js | 64 +++++++++++++++++++ docs/linkedin-post-comments-design.mdx | 9 +-- 5 files changed, 160 insertions(+), 10 deletions(-) diff --git a/cli-manifest.json b/cli-manifest.json index 5a728ab1..49411c33 100644 --- a/cli-manifest.json +++ b/cli-manifest.json @@ -13451,6 +13451,44 @@ "sourceFile": "linkedin/post-analytics.js", "navigateBefore": "https://www.linkedin.com" }, + { + "site": "linkedin", + "name": "post-comments", + "description": "List unique commenters and reply authors from one exact LinkedIn post URL", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "post-url", + "type": "string", + "required": true, + "positional": true, + "help": "Exact LinkedIn post URL" + }, + { + "name": "limit", + "type": "int", + "required": false, + "help": "Maximum unique commenters to return; omit to fetch all" + } + ], + "columns": [ + "rank", + "name", + "headline", + "profile_url", + "comment_count", + "sample_comment", + "commented_at", + "source_post" + ], + "type": "js", + "modulePath": "linkedin/post-comments.js", + "sourceFile": "linkedin/post-comments.js", + "navigateBefore": "https://www.linkedin.com" + }, { "site": "linkedin", "name": "posts", diff --git a/clis/linkedin/__fixtures__/post-comments.html b/clis/linkedin/__fixtures__/post-comments.html index aab7f073..39ae5d8f 100644 --- a/clis/linkedin/__fixtures__/post-comments.html +++ b/clis/linkedin/__fixtures__/post-comments.html @@ -2,12 +2,13 @@
+

3 comments

+
+ +

Example Org

+

5,000 followers

+
+

3d

+ + Mentioned Person + Organization comment + +
diff --git a/clis/linkedin/post-comments.js b/clis/linkedin/post-comments.js index 550e1697..1d1b2c8a 100644 --- a/clis/linkedin/post-comments.js +++ b/clis/linkedin/post-comments.js @@ -13,6 +13,7 @@ import { const LINKEDIN_DOMAIN = 'www.linkedin.com'; const MAX_ROUNDS = 200; +const UNREACHED_COUNT_STABLE_ROUNDS = 10; const COLUMNS = [ 'rank', 'name', @@ -65,7 +66,7 @@ function canonicalizeProfileUrl(value) { try { const parsed = new URL(normalizeWhitespace(value), `https://${LINKEDIN_DOMAIN}`); const host = parsed.hostname.toLowerCase(); - const match = parsed.pathname.match(/^\/in\/([^/?#]+)\/?$/i); + const match = parsed.pathname.match(/^\/in\/([^/?#]+)(?:\/[a-z]{2})?\/?$/i); if ( parsed.protocol !== 'https:' || parsed.username @@ -80,6 +81,20 @@ function canonicalizeProfileUrl(value) { } } +function isCompanyProfileUrl(value) { + try { + const parsed = new URL(normalizeWhitespace(value), `https://${LINKEDIN_DOMAIN}`); + return parsed.protocol === 'https:' + && !parsed.username + && !parsed.password + && !parsed.port + && (parsed.hostname === 'linkedin.com' || parsed.hostname === LINKEDIN_DOMAIN) + && /^\/company\/[^/?#]+\/(?:posts\/)?$/i.test(parsed.pathname); + } catch { + return false; + } +} + function buildCommentRoundScript() { return String.raw`(() => { const clean = (value) => String(value || '').replace(/[\u00a0\u202f]+/g, ' ').replace(/\s+/g, ' ').trim(); @@ -88,8 +103,14 @@ function buildCommentRoundScript() { const authRequired = /linkedin\.com\/(?:login|checkpoint|authwall|uas)/i.test(location.href) || /\b(sign in|log in|join linkedin|captcha|verification required)\b/i.test(document.body?.innerText || ''); const nodes = Array.from(document.querySelectorAll(commentSelector)); + const expectedCommentCount = Math.max(0, ...Array.from(document.querySelectorAll('p, span, button')) + .map((element) => clean(element.textContent || element.getAttribute('aria-label'))) + .map((text) => text.match(/^(\d[\d,.]*)([km]?)\s+comments?$/i)) + .filter(Boolean) + .map((match) => Math.round(Number(match[1].replace(/,/g, '')) * ({ k: 1e3, m: 1e6 }[match[2].toLowerCase()] || 1)))); const rows = nodes.map((node) => { - const links = Array.from(node.querySelectorAll('a[href*="/in/"]')).filter((link) => owns(node, link)); + const links = Array.from(node.querySelectorAll('a[href]')) + .filter((link) => owns(node, link) && /linkedin\.com\/(?:in|company)\//i.test(link.href)); const rawProfileUrl = links[0]?.href || ''; const identity = links.find((link) => link.href === rawProfileUrl && clean(link.textContent)) || links[0]; const labels = Array.from(node.querySelectorAll('[aria-label]')) @@ -106,7 +127,8 @@ function buildCommentRoundScript() { .map((paragraph) => clean(paragraph.textContent)) .filter(Boolean); const rawHeadline = paragraphs - .filter((text) => text !== rawName && !/^(author|verified profile|[•·]?\s*(?:1st|2nd|3rd))/i.test(text)) + .filter((text) => !(rawName && text.toLowerCase().includes(rawName.toLowerCase())) + && !/^(author|verified profile|[•·]?\s*(?:1st|2nd|3rd))/i.test(text)) .sort((left, right) => right.length - left.length)[0] || ''; const textBox = Array.from(node.querySelectorAll('[data-testid="expandable-text-box"]')) @@ -137,9 +159,11 @@ function buildCommentRoundScript() { let atEnd = true; if (workspace && workspace.scrollHeight > workspace.clientHeight) { const bottom = workspace.scrollHeight - workspace.clientHeight; + workspace.scrollTop = Math.max(0, bottom - 300); workspace.scrollTop = bottom; atEnd = workspace.scrollTop >= bottom - 2; } else { + window.scrollTo(0, Math.max(0, document.documentElement.scrollHeight - window.innerHeight - 300)); window.scrollTo(0, document.documentElement.scrollHeight); atEnd = window.scrollY + window.innerHeight >= document.documentElement.scrollHeight - 2; } @@ -147,6 +171,7 @@ function buildCommentRoundScript() { rows, authRequired, commentNodeCount: nodes.length, + expectedCommentCount, replyControlsClicked: controls.length, atEnd, url: location.href, @@ -166,6 +191,7 @@ function normalizeCommentRows(rows, sourcePost) { const rawId = normalizeWhitespace(row.rawId); const name = normalizeWhitespace(row.rawName); const profileUrl = canonicalizeProfileUrl(row.rawProfileUrl); + if (rawId && name && !profileUrl && isCompanyProfileUrl(row.rawProfileUrl)) continue; if (!rawId || !name || !profileUrl) { throw new CommandExecutionError(`LinkedIn post-comments returned row without stable profile identity at index ${index}`); } @@ -206,6 +232,7 @@ async function collectPostComments(page, args) { } const commentsById = new Map(); + let expectedCommentCount = 0; let stableRounds = 0; for (let round = 0; round < MAX_ROUNDS; round++) { let payload; @@ -225,6 +252,8 @@ async function collectPostComments(page, args) { || !Array.isArray(payload.rows) || !Number.isInteger(payload.commentNodeCount) || payload.commentNodeCount < 0 + || !Number.isInteger(payload.expectedCommentCount) + || payload.expectedCommentCount < 0 || !Number.isInteger(payload.replyControlsClicked) || payload.replyControlsClicked < 0 || typeof payload.atEnd !== 'boolean' @@ -252,12 +281,18 @@ async function collectPostComments(page, args) { newComments += 1; } } + expectedCommentCount = Math.max(expectedCommentCount, payload.expectedCommentCount); const normalized = normalizeCommentRows(Array.from(commentsById.values()), sourcePost); if (limit && normalized.length >= limit) return normalized.slice(0, limit); - const exhausted = newComments === 0 && payload.replyControlsClicked === 0 && payload.atEnd; + const exhausted = newComments === 0 + && payload.replyControlsClicked === 0 + && payload.atEnd; stableRounds = exhausted ? stableRounds + 1 : 0; - if (stableRounds >= 2) { + const stableRoundLimit = expectedCommentCount === 0 || commentsById.size >= expectedCommentCount + ? 2 + : UNREACHED_COUNT_STABLE_ROUNDS; + if (stableRounds >= stableRoundLimit) { if (normalized.length === 0) { throw new EmptyResultError( 'linkedin post-comments', diff --git a/clis/linkedin/post-comments.test.js b/clis/linkedin/post-comments.test.js index e18c0752..13d56ff5 100644 --- a/clis/linkedin/post-comments.test.js +++ b/clis/linkedin/post-comments.test.js @@ -13,6 +13,7 @@ import './post-comments.js'; const { canonicalizePostUrl, + canonicalizeProfileUrl, parseOptionalLimit, buildCommentRoundScript, normalizeCommentRows, @@ -32,6 +33,7 @@ const round = (rows, overrides = {}) => ({ rows, authRequired: false, commentNodeCount: rows.length, + expectedCommentCount: rows.length, replyControlsClicked: 0, atEnd: true, url: 'https://www.linkedin.com/posts/source/', @@ -132,8 +134,17 @@ describe('linkedin post-comments', () => { rawComment: 'Mentioned Person Reply comment', rawCommentedAt: '1d', }, + { + rawId: 'replaceableComment_urn:li:comment:(urn:li:activity:1,103)', + rawName: 'Example Org', + rawHeadline: '5,000 followers', + rawProfileUrl: 'https://www.linkedin.com/company/example-org/posts/', + rawComment: 'Mentioned Person Organization comment', + rawCommentedAt: '3d', + }, ]); expect(payload.replyControlsClicked).toBe(1); + expect(payload.expectedCommentCount).toBe(3); expect(payload.atEnd).toBe(true); }); @@ -158,6 +169,24 @@ describe('linkedin post-comments', () => { }]); }); + it('canonicalizes LinkedIn locale-suffixed person profiles', () => { + expect(canonicalizeProfileUrl('https://www.linkedin.com/in/brad-choi/en/')) + .toBe('https://www.linkedin.com/in/brad-choi/'); + }); + + it('counts but excludes organization commenters from the people result', () => { + const rows = normalizeCommentRows([ + rawComment(1, 'alice', 'Alice', 'First'), + rawComment(2, 'ignored', 'Example Org', 'Company comment', { + rawProfileUrl: 'https://www.linkedin.com/company/example-org/posts/', + }), + ], 'https://www.linkedin.com/posts/source/'); + + expect(rows.map((row) => row.profile_url)).toEqual([ + 'https://www.linkedin.com/in/alice/', + ]); + }); + it('rejects rendered comments without a stable identity', () => { expect(() => normalizeCommentRows([ rawComment(1, 'alice', '', 'Missing name', { rawProfileUrl: '' }), @@ -186,6 +215,41 @@ describe('linkedin post-comments', () => { expect(page.evaluate).toHaveBeenCalledTimes(5); }); + it('does not report a partial result while the advertised comment count is higher', async () => { + const command = getRegistry().get('linkedin/post-comments'); + const alice = rawComment(1, 'alice', 'Alice', 'First'); + const bob = rawComment(2, 'bob', 'Bob', 'Second'); + const carol = rawComment(3, 'carol', 'Carol', 'Third'); + const page = makePage([ + round([alice, bob], { expectedCommentCount: 3 }), + round([alice, bob], { expectedCommentCount: 3 }), + round([alice, bob], { expectedCommentCount: 3 }), + round([alice, bob, carol], { expectedCommentCount: 3 }), + round([alice, bob, carol], { expectedCommentCount: 3 }), + round([alice, bob, carol], { expectedCommentCount: 3 }), + ]); + + const rows = await command.func(page, { 'post-url': 'https://www.linkedin.com/posts/source/' }); + + expect(rows.map((row) => row.profile_url)).toEqual([ + 'https://www.linkedin.com/in/alice/', + 'https://www.linkedin.com/in/bob/', + 'https://www.linkedin.com/in/carol/', + ]); + }); + + it('returns visible people after a longer stable wait when the count includes an unavailable comment', async () => { + const command = getRegistry().get('linkedin/post-comments'); + const alice = rawComment(1, 'alice', 'Alice', 'First'); + const bob = rawComment(2, 'bob', 'Bob', 'Second'); + const page = makePage([round([alice, bob], { expectedCommentCount: 3 })]); + + const rows = await command.func(page, { 'post-url': 'https://www.linkedin.com/posts/source/' }); + + expect(rows).toHaveLength(2); + expect(page.evaluate).toHaveBeenCalledTimes(12); + }); + it('stops as soon as the optional unique-profile limit is reached', async () => { const command = getRegistry().get('linkedin/post-comments'); const page = makePage([round([ diff --git a/docs/linkedin-post-comments-design.mdx b/docs/linkedin-post-comments-design.mdx index a3c9c44a..2eb9d54e 100644 --- a/docs/linkedin-post-comments-design.mdx +++ b/docs/linkedin-post-comments-design.mdx @@ -2,7 +2,7 @@ ## Goal -Add a read-only command that collects every visible participant in one exact LinkedIn post's comment threads, including reply authors, and deduplicates them by canonical LinkedIn profile URL. +Add a read-only command that collects every visible person in one exact LinkedIn post's comment threads, including reply authors, and deduplicates them by canonical LinkedIn profile URL. ## Command @@ -22,7 +22,8 @@ Contract: visible UI Evidence from the supplied post: - Rendered comments use stable `replaceableComment_urn:li:comment:...` containers. -- Each container exposes its author's exact `/in//` link, name, headline, relative timestamp, and comment text. +- Person-authored containers expose an exact `/in//` link, sometimes with a locale suffix, plus name, headline, relative timestamp, and comment text. +- Organization-authored comments expose `/company/` identities; they count toward pagination progress but are excluded from the people result. - Reply pagination is exposed through visible `See previous replies` controls. - The observed network alternative is LinkedIn's internal `flagship-web/rsc-action/actions/pagination` action, whose private action payload is a higher-drift contract. @@ -51,10 +52,10 @@ Mentioned profile links inside comment text are not identities: the first author 2. Open it in the authenticated LinkedIn browser session and reject auth walls. 3. Extract currently rendered comment containers. 4. Expand every visible `See previous replies` control and advance the post workspace. -5. Repeat while new comment containers or pagination controls appear, or until `--limit` unique profiles have been collected. +5. Repeat while new comment containers or pagination controls appear, or until `--limit` unique profiles have been collected. Treat the visible comment count as a progress hint because it can include unavailable or moderated comments. 6. Normalize rows, aggregate duplicate authors, and return first-seen order. -The unbounded path stops only after a full iteration produces no new comment containers and no actionable reply or pagination controls. +The unbounded path stops after two stable bottom rounds when the visible count is reached, or ten stable bottom rounds when LinkedIn's count includes a non-renderable comment. ## Errors From 1bd20adb89a5e4aac24251ccd861212100c1e44a Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 14:36:24 +0530 Subject: [PATCH 04/39] refactor: move LinkedIn commands to community plugin --- README.md | 1 + cli-manifest.json | 1068 ----------------- docs/linkedin-post-comments-design.mdx | 70 -- plugins/linkedin/README.md | 27 + .../linkedin/__fixtures__/post-comments.html | 0 {clis => plugins}/linkedin/auth.js | 2 +- {clis => plugins}/linkedin/company.js | 0 {clis => plugins}/linkedin/connect.js | 0 {clis => plugins}/linkedin/connections.js | 0 {clis => plugins}/linkedin/inbox.js | 0 {clis => plugins}/linkedin/job-detail.js | 0 .../linkedin/jobs-preferences.js | 0 plugins/linkedin/package.json | 9 + {clis => plugins}/linkedin/people-search.js | 0 {clis => plugins}/linkedin/post-analytics.js | 0 {clis => plugins}/linkedin/post-comments.js | 0 {clis => plugins}/linkedin/posts-core.js | 0 {clis => plugins}/linkedin/posts.js | 0 .../linkedin/profile-analytics.js | 0 .../linkedin/profile-experience.js | 0 .../linkedin/profile-projects.js | 0 {clis => plugins}/linkedin/profile-read.js | 0 {clis => plugins}/linkedin/safe-send.js | 0 {clis => plugins}/linkedin/salesnav-inbox.js | 0 .../linkedin/salesnav-message.js | 0 {clis => plugins}/linkedin/salesnav-search.js | 0 {clis => plugins}/linkedin/salesnav-thread.js | 0 {clis => plugins}/linkedin/search.js | 0 .../linkedin/sent-invitations.js | 0 {clis => plugins}/linkedin/services-read.js | 0 {clis => plugins}/linkedin/shared.js | 0 plugins/linkedin/site-auth.js | 119 ++ .../linkedin/test}/company.test.js | 4 +- .../linkedin/test}/connect.test.js | 4 +- .../linkedin/test}/connections.test.js | 4 +- .../linkedin/test}/inbox.test.js | 4 +- .../linkedin/test}/job-detail.test.js | 4 +- .../linkedin/test}/jobs-preferences.test.js | 4 +- .../linkedin/test}/people-search.test.js | 4 +- .../linkedin/test}/post-analytics.test.js | 4 +- .../linkedin/test}/post-comments.test.js | 6 +- .../linkedin/test}/posts.test.js | 4 +- .../linkedin/test}/profile-analytics.test.js | 4 +- .../linkedin/test}/profile-experience.test.js | 4 +- .../linkedin/test}/profile-projects.test.js | 4 +- .../linkedin/test}/profile-read.test.js | 4 +- .../linkedin/test}/safe-send.test.js | 4 +- .../linkedin/test}/salesnav-inbox.test.js | 4 +- .../linkedin/test}/salesnav-message.test.js | 4 +- .../linkedin/test}/salesnav-search.test.js | 4 +- .../linkedin/test}/salesnav-thread.test.js | 4 +- .../linkedin/test}/search.test.js | 2 +- .../linkedin/test}/sent-invitations.test.js | 4 +- .../linkedin/test}/services-read.test.js | 4 +- .../linkedin/test}/thread-snapshot.test.js | 4 +- .../linkedin/test}/timeline.test.js | 4 +- {clis => plugins}/linkedin/thread-snapshot.js | 0 {clis => plugins}/linkedin/timeline.js | 0 plugins/linkedin/webcmd-plugin.json | 10 + scripts/silent-column-drop-baseline.json | 22 - scripts/typed-error-lint-baseline.json | 16 - src/plugin.test.ts | 9 + vitest.config.ts | 2 +- webcmd-plugin.json | 10 + 64 files changed, 235 insertions(+), 1226 deletions(-) delete mode 100644 docs/linkedin-post-comments-design.mdx create mode 100644 plugins/linkedin/README.md rename {clis => plugins}/linkedin/__fixtures__/post-comments.html (100%) rename {clis => plugins}/linkedin/auth.js (97%) rename {clis => plugins}/linkedin/company.js (100%) rename {clis => plugins}/linkedin/connect.js (100%) rename {clis => plugins}/linkedin/connections.js (100%) rename {clis => plugins}/linkedin/inbox.js (100%) rename {clis => plugins}/linkedin/job-detail.js (100%) rename {clis => plugins}/linkedin/jobs-preferences.js (100%) create mode 100644 plugins/linkedin/package.json rename {clis => plugins}/linkedin/people-search.js (100%) rename {clis => plugins}/linkedin/post-analytics.js (100%) rename {clis => plugins}/linkedin/post-comments.js (100%) rename {clis => plugins}/linkedin/posts-core.js (100%) rename {clis => plugins}/linkedin/posts.js (100%) rename {clis => plugins}/linkedin/profile-analytics.js (100%) rename {clis => plugins}/linkedin/profile-experience.js (100%) rename {clis => plugins}/linkedin/profile-projects.js (100%) rename {clis => plugins}/linkedin/profile-read.js (100%) rename {clis => plugins}/linkedin/safe-send.js (100%) rename {clis => plugins}/linkedin/salesnav-inbox.js (100%) rename {clis => plugins}/linkedin/salesnav-message.js (100%) rename {clis => plugins}/linkedin/salesnav-search.js (100%) rename {clis => plugins}/linkedin/salesnav-thread.js (100%) rename {clis => plugins}/linkedin/search.js (100%) rename {clis => plugins}/linkedin/sent-invitations.js (100%) rename {clis => plugins}/linkedin/services-read.js (100%) rename {clis => plugins}/linkedin/shared.js (100%) create mode 100644 plugins/linkedin/site-auth.js rename {clis/linkedin => plugins/linkedin/test}/company.test.js (98%) rename {clis/linkedin => plugins/linkedin/test}/connect.test.js (99%) rename {clis/linkedin => plugins/linkedin/test}/connections.test.js (97%) rename {clis/linkedin => plugins/linkedin/test}/inbox.test.js (98%) rename {clis/linkedin => plugins/linkedin/test}/job-detail.test.js (95%) rename {clis/linkedin => plugins/linkedin/test}/jobs-preferences.test.js (95%) rename {clis/linkedin => plugins/linkedin/test}/people-search.test.js (99%) rename {clis/linkedin => plugins/linkedin/test}/post-analytics.test.js (92%) rename {clis/linkedin => plugins/linkedin/test}/post-comments.test.js (98%) rename {clis/linkedin => plugins/linkedin/test}/posts.test.js (97%) rename {clis/linkedin => plugins/linkedin/test}/profile-analytics.test.js (95%) rename {clis/linkedin => plugins/linkedin/test}/profile-experience.test.js (98%) rename {clis/linkedin => plugins/linkedin/test}/profile-projects.test.js (97%) rename {clis/linkedin => plugins/linkedin/test}/profile-read.test.js (97%) rename {clis/linkedin => plugins/linkedin/test}/safe-send.test.js (98%) rename {clis/linkedin => plugins/linkedin/test}/salesnav-inbox.test.js (97%) rename {clis/linkedin => plugins/linkedin/test}/salesnav-message.test.js (98%) rename {clis/linkedin => plugins/linkedin/test}/salesnav-search.test.js (97%) rename {clis/linkedin => plugins/linkedin/test}/salesnav-thread.test.js (97%) rename {clis/linkedin => plugins/linkedin/test}/search.test.js (99%) rename {clis/linkedin => plugins/linkedin/test}/sent-invitations.test.js (94%) rename {clis/linkedin => plugins/linkedin/test}/services-read.test.js (97%) rename {clis/linkedin => plugins/linkedin/test}/thread-snapshot.test.js (97%) rename {clis/linkedin => plugins/linkedin/test}/timeline.test.js (97%) rename {clis => plugins}/linkedin/thread-snapshot.js (100%) rename {clis => plugins}/linkedin/timeline.js (100%) create mode 100644 plugins/linkedin/webcmd-plugin.json diff --git a/README.md b/README.md index 8a413798..8e3b3449 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ Webcmd Cloud can run supported commands and browser sessions on hosted infrastru | Plugin | Description | Author | | --- | --- | --- | | [`bmwblog`](./plugins/bmwblog/) | BMWBLOG article discovery commands for Webcmd | [WebCMD Agent](https://github.com/agentrhq) | +| [`linkedin`](./plugins/linkedin/) | LinkedIn profile, network, messaging, job, and Sales Navigator commands for WebCMD | [WebCMD Agent](https://github.com/agentrhq) | | [`pypi`](./plugins/pypi/) | Inspect public Python package metadata and releases from PyPI | [Kemal Kaya](https://github.com/yoldaolmak) | | [`skyscanner`](./plugins/skyscanner/) | Skyscanner flight search commands for Webcmd | [Rishabh](https://github.com/rishabhraj36) | | [`techcrunch`](./plugins/techcrunch/) | Search and read TechCrunch stories from its public API | [WebCMD Agent](https://github.com/agentrhq) | diff --git a/cli-manifest.json b/cli-manifest.json index 49411c33..515e034d 100644 --- a/cli-manifest.json +++ b/cli-manifest.json @@ -13124,1074 +13124,6 @@ "modulePath": "lichess/user.js", "sourceFile": "lichess/user.js" }, - { - "site": "linkedin", - "name": "company", - "description": "Read a LinkedIn company page: industry, size, HQ, founded, website, followers, and about text", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "company", - "type": "string", - "required": true, - "positional": true, - "help": "Company universal name, /company/ path, or full URL" - } - ], - "columns": [ - "name", - "industry", - "size", - "headquarters", - "founded", - "website", - "specialties", - "followers", - "about", - "url" - ], - "type": "js", - "modulePath": "linkedin/company.js", - "sourceFile": "linkedin/company.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "connect", - "description": "Fail-closed LinkedIn connection request sender that verifies the exact profile before optionally sending a note", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": true, - "positional": true, - "help": "Exact LinkedIn profile URL to open and verify" - }, - { - "name": "expected-name", - "type": "string", - "required": true, - "help": "Expected visible profile name" - }, - { - "name": "note", - "type": "string", - "default": "", - "required": false, - "help": "Optional connection note, max 300 chars" - }, - { - "name": "send", - "type": "bool", - "default": false, - "required": false, - "help": "Actually click Send. Default is dry-run verification only." - } - ], - "columns": [ - "status", - "recipient", - "reason", - "profile_url", - "note_chars", - "connectable", - "delivery_verified", - "matched_invitation_name", - "matched_invitation_url", - "actualValue", - "blockReason", - "expectedValue", - "observedUrl", - "safety" - ], - "type": "js", - "modulePath": "linkedin/connect.js", - "sourceFile": "linkedin/connect.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "connections", - "description": "List your LinkedIn first-degree connections with names, headlines, and profile URLs", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of connections to return (max 500)" - } - ], - "columns": [ - "rank", - "name", - "occupation", - "public_id", - "connected_at", - "url" - ], - "type": "js", - "modulePath": "linkedin/connections.js", - "sourceFile": "linkedin/connections.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "inbox", - "description": "List LinkedIn messaging inbox conversations and unread messages", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 40, - "required": false, - "help": "Maximum conversations to return (1-100)" - }, - { - "name": "unread-only", - "type": "bool", - "default": false, - "required": false, - "help": "Return only conversations with unread messages" - } - ], - "columns": [ - "rank", - "thread_url", - "thread_id", - "person_name", - "last_message_preview", - "unread", - "counterparty_type", - "category", - "timestamp" - ], - "type": "js", - "modulePath": "linkedin/inbox.js", - "sourceFile": "linkedin/inbox.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "job-detail", - "description": "Read one LinkedIn job page with description, apply URL, workplace type, applicants, and company metadata", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "job-url", - "type": "string", - "required": true, - "positional": true, - "help": "Exact LinkedIn job URL, e.g. https://www.linkedin.com/jobs/view/123/" - } - ], - "columns": [ - "title", - "company", - "location", - "workplace_type", - "job_type", - "applicants", - "listed", - "apply_url", - "company_url", - "url", - "description" - ], - "type": "js", - "modulePath": "linkedin/job-detail.js", - "sourceFile": "linkedin/job-detail.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "jobs-preferences", - "description": "Read visible LinkedIn Jobs preferences and alert settings without changing them", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "open_to_work", - "job_titles", - "locations", - "job_alerts", - "preferences_url", - "alerts_url", - "raw_preferences" - ], - "type": "js", - "modulePath": "linkedin/jobs-preferences.js", - "sourceFile": "linkedin/jobs-preferences.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "login", - "description": "Open linkedin login", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "public_id", - "plain_id", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "linkedin/auth.js", - "sourceFile": "linkedin/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "linkedin", - "name": "people-search", - "description": "Search standard LinkedIn (not Sales Navigator) for people by keyword. Each invocation consumes against LinkedIn's monthly Commercial Use Limit on people search; throttle accordingly.", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "keywords", - "type": "string", - "required": true, - "positional": true, - "help": "People search keywords, e.g. \"site reliability engineer berlin\"" - }, - { - "name": "limit", - "type": "int", - "default": 5, - "required": false, - "help": "Maximum people to return (1-10); each query counts toward LinkedIn's monthly CUL" - } - ], - "columns": [ - "rank", - "name", - "headline", - "location", - "profile_url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "linkedin/people-search.js", - "sourceFile": "linkedin/people-search.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "post-analytics", - "description": "Summarize raw visible LinkedIn post counters without custom scoring or classification", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Maximum posts to summarize (1-100)" - } - ], - "columns": [ - "posts_analyzed", - "total_reactions", - "total_comments", - "total_reposts", - "total_impressions", - "posts_with_media", - "posts_with_urls", - "latest_posted_at", - "latest_reactions", - "latest_comments", - "latest_reposts", - "latest_impressions", - "latest_url" - ], - "type": "js", - "modulePath": "linkedin/post-analytics.js", - "sourceFile": "linkedin/post-analytics.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "post-comments", - "description": "List unique commenters and reply authors from one exact LinkedIn post URL", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-url", - "type": "string", - "required": true, - "positional": true, - "help": "Exact LinkedIn post URL" - }, - { - "name": "limit", - "type": "int", - "required": false, - "help": "Maximum unique commenters to return; omit to fetch all" - } - ], - "columns": [ - "rank", - "name", - "headline", - "profile_url", - "comment_count", - "sample_comment", - "commented_at", - "source_post" - ], - "type": "js", - "modulePath": "linkedin/post-comments.js", - "sourceFile": "linkedin/post-comments.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "posts", - "description": "Export visible posts from a LinkedIn profile activity page with engagement metrics", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum posts to return (1-100)" - } - ], - "columns": [ - "rank", - "author", - "posted_at", - "body", - "reactions", - "comments", - "reposts", - "impressions", - "media", - "media_urls", - "url", - "raw_text" - ], - "type": "js", - "modulePath": "linkedin/posts.js", - "sourceFile": "linkedin/posts.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "profile-analytics", - "description": "Read visible LinkedIn profile dashboard metrics such as profile views, post impressions, and search appearances", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - } - ], - "columns": [ - "profile_url", - "profile_views", - "post_impressions", - "search_appearances", - "followers", - "connections", - "raw_analytics" - ], - "type": "js", - "modulePath": "linkedin/profile-analytics.js", - "sourceFile": "linkedin/profile-analytics.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "profile-experience", - "description": "Read visible LinkedIn profile experience entries with titles, dates, locations, skills, media, and URLs", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - } - ], - "columns": [ - "rank", - "total_count", - "title", - "employment_type", - "company", - "date_range", - "start_date", - "end_date", - "location", - "location_type", - "description", - "skills", - "media", - "urls", - "skill_url", - "media_url", - "profile_url", - "raw_text" - ], - "type": "js", - "modulePath": "linkedin/profile-experience.js", - "sourceFile": "linkedin/profile-experience.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "profile-projects", - "description": "Read visible LinkedIn profile projects with descriptions, dates, skills, media, and URLs", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - } - ], - "columns": [ - "rank", - "title", - "date_range", - "associated_with", - "description", - "skills", - "media", - "urls", - "profile_url", - "raw_text" - ], - "type": "js", - "modulePath": "linkedin/profile-projects.js", - "sourceFile": "linkedin/profile-projects.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "profile-read", - "description": "Read visible LinkedIn profile sections: headline, About, experience, education, services, and featured sections", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - } - ], - "columns": [ - "profile_url", - "name", - "headline", - "location", - "about", - "about_character_count", - "about_skills", - "experience", - "education", - "services", - "featured" - ], - "type": "js", - "modulePath": "linkedin/profile-read.js", - "sourceFile": "linkedin/profile-read.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "safe-send", - "description": "Fail-closed LinkedIn message sender that verifies exact thread, recipient, and latest message before filling/sending", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "thread-url", - "type": "str", - "required": true, - "help": "Exact LinkedIn messaging thread URL to open and verify" - }, - { - "name": "expected-name", - "type": "str", - "required": true, - "help": "Expected visible recipient name in the active thread header" - }, - { - "name": "message", - "type": "str", - "required": true, - "help": "Message body to send or dry-run" - }, - { - "name": "expected-last-text", - "type": "str", - "required": false, - "help": "Substring expected in the currently visible latest conversation context" - }, - { - "name": "expected-last-hash", - "type": "str", - "required": false, - "help": "SHA-256 hash of expected latest visible message text" - }, - { - "name": "send", - "type": "bool", - "default": false, - "required": false, - "help": "Actually click Send. Default is dry-run verification only." - }, - { - "name": "screenshot", - "type": "bool", - "default": false, - "required": false, - "help": "Capture a screenshot during verification" - } - ], - "columns": [ - "status", - "recipient", - "reason", - "thread_url", - "message_chars", - "screenshot" - ], - "type": "js", - "modulePath": "linkedin/safe-send.js", - "sourceFile": "linkedin/safe-send.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "salesnav-inbox", - "description": "List LinkedIn Sales Navigator message conversations with API pagination", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "limit", - "type": "number", - "default": 40, - "required": false, - "help": "Maximum conversations to return (1-500)" - }, - { - "name": "max-pages", - "type": "number", - "default": 30, - "required": false, - "help": "Maximum Sales Navigator API pages to fetch" - }, - { - "name": "unread-only", - "type": "bool", - "default": false, - "required": false, - "help": "Return only unread conversations" - } - ], - "columns": [ - "rank", - "thread_id", - "thread_url", - "person_name", - "last_message_snippet", - "last_activity_time", - "unread", - "unread_count", - "total_message_count", - "archived", - "participants", - "next_page_starts_at" - ], - "type": "js", - "modulePath": "linkedin/salesnav-inbox.js", - "sourceFile": "linkedin/salesnav-inbox.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "salesnav-message", - "description": "Send or dry-run a LinkedIn Sales Navigator InMail to a lead using the Sales Navigator messaging API", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "recipient", - "type": "string", - "required": true, - "positional": true, - "help": "Sales Navigator lead URL, LinkedIn /in/ URL from salesnav-search, or urn:li:fs_salesProfile:(...)" - }, - { - "name": "subject", - "type": "string", - "required": true, - "help": "InMail subject" - }, - { - "name": "body", - "type": "string", - "required": true, - "help": "InMail body" - }, - { - "name": "send", - "type": "bool", - "default": false, - "required": false, - "help": "Actually send the InMail. Default is dry-run validation only." - }, - { - "name": "copy-to-crm", - "type": "bool", - "default": false, - "required": false, - "help": "Set Sales Navigator copyToCrm on the message request" - } - ], - "columns": [ - "status", - "recipient", - "title", - "company", - "credits_remaining", - "credits_before", - "credits_after", - "sent_in_salesnav", - "message_chars", - "subject_chars", - "recipient_urn", - "degree", - "inmail_restriction", - "open_link" - ], - "type": "js", - "modulePath": "linkedin/salesnav-message.js", - "sourceFile": "linkedin/salesnav-message.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "salesnav-search", - "description": "Search LinkedIn Sales Navigator for people leads by keyword", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "keywords", - "type": "string", - "required": true, - "positional": true, - "help": "People search keywords, e.g. \"quality manager food manufacturing\"" - }, - { - "name": "limit", - "type": "number", - "default": 25, - "required": false, - "help": "Maximum leads to return (1-500, fetched 25 per request)" - } - ], - "columns": [ - "rank", - "name", - "title", - "company", - "location", - "degree", - "profile_url", - "lead_url", - "recipient_urn" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "linkedin/salesnav-search.js", - "sourceFile": "linkedin/salesnav-search.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "salesnav-thread", - "description": "Return full Sales Navigator message history for a thread id, Sales Navigator inbox URL, lead URL, recipient urn, or exact recipient name", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "thread-or-recipient", - "type": "string", - "required": true, - "positional": true, - "help": "Sales Navigator inbox URL/thread id, Sales Navigator lead URL, recipient urn, or exact participant name" - }, - { - "name": "limit", - "type": "number", - "default": 200, - "required": false, - "help": "Maximum messages to return (1-500)" - }, - { - "name": "max-pages", - "type": "number", - "default": 30, - "required": false, - "help": "Maximum inbox pages to scan when resolving a recipient" - } - ], - "columns": [ - "index", - "thread_id", - "thread_url", - "sender", - "text", - "timestamp", - "subject", - "message_id", - "sender_urn", - "delivered_at", - "type", - "total_message_count" - ], - "type": "js", - "modulePath": "linkedin/salesnav-thread.js", - "sourceFile": "linkedin/salesnav-thread.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "search", - "description": "Search LinkedIn jobs", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "string", - "required": true, - "positional": true, - "help": "Job search keywords" - }, - { - "name": "location", - "type": "string", - "required": false, - "help": "Location text such as San Francisco Bay Area" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of jobs to return (max 100)" - }, - { - "name": "start", - "type": "int", - "default": 0, - "required": false, - "help": "Result offset for pagination" - }, - { - "name": "details", - "type": "bool", - "default": false, - "required": false, - "help": "Include full job description and apply URL (slower)" - }, - { - "name": "company", - "type": "string", - "required": false, - "help": "Comma-separated company names or LinkedIn company IDs" - }, - { - "name": "experience-level", - "type": "string", - "required": false, - "help": "Comma-separated: internship, entry, associate, mid-senior, director, executive" - }, - { - "name": "job-type", - "type": "string", - "required": false, - "help": "Comma-separated: full-time, part-time, contract, temporary, volunteer, internship, other" - }, - { - "name": "date-posted", - "type": "string", - "required": false, - "help": "One of: any, month, week, 24h" - }, - { - "name": "remote", - "type": "string", - "required": false, - "help": "Comma-separated: on-site, hybrid, remote" - } - ], - "columns": [ - "rank", - "title", - "company", - "location", - "listed", - "salary", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "linkedin/search.js", - "sourceFile": "linkedin/search.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "sent-invitations", - "description": "List pending LinkedIn sent invitations for CRM reconciliation", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "rank", - "name", - "profile_url", - "invited_date_text" - ], - "type": "js", - "modulePath": "linkedin/sent-invitations.js", - "sourceFile": "linkedin/sent-invitations.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "services-read", - "description": "Read LinkedIn Services page details including services, overview, availability, pricing, and media titles/descriptions", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - }, - { - "name": "services-url", - "type": "string", - "required": false, - "help": "LinkedIn /services/page// URL. If omitted, it is discovered from the profile." - } - ], - "columns": [ - "service_url", - "page_title", - "overview", - "availability", - "work_locations", - "pricing", - "services_provided", - "services_count", - "media", - "media_count", - "messages", - "reviews_visibility" - ], - "type": "js", - "modulePath": "linkedin/services-read.js", - "sourceFile": "linkedin/services-read.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "thread-snapshot", - "description": "Load a LinkedIn messaging thread, scroll for available history, and return a full context snapshot", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "thread-url", - "type": "str", - "required": true, - "help": "Exact LinkedIn messaging thread URL to open and snapshot" - }, - { - "name": "max-scrolls", - "type": "number", - "default": 30, - "required": false, - "help": "Maximum upward scroll attempts to load older messages" - }, - { - "name": "json", - "type": "bool", - "default": false, - "required": false, - "help": "Return only JSON snapshot string in the snapshot_json field" - } - ], - "columns": [ - "thread_url", - "recipient", - "message_count", - "latest_text", - "snapshot_json" - ], - "type": "js", - "modulePath": "linkedin/thread-snapshot.js", - "sourceFile": "linkedin/thread-snapshot.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "timeline", - "description": "Read LinkedIn home timeline posts", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return (max 100)" - } - ], - "columns": [ - "rank", - "author", - "author_url", - "headline", - "text", - "posted_at", - "reactions", - "comments", - "url" - ], - "type": "js", - "modulePath": "linkedin/timeline.js", - "sourceFile": "linkedin/timeline.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "whoami", - "description": "Show the current logged-in linkedin account", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "public_id", - "plain_id", - "name" - ], - "type": "js", - "modulePath": "linkedin/auth.js", - "sourceFile": "linkedin/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, { "site": "linkedin-learning", "name": "course", diff --git a/docs/linkedin-post-comments-design.mdx b/docs/linkedin-post-comments-design.mdx deleted file mode 100644 index 2eb9d54e..00000000 --- a/docs/linkedin-post-comments-design.mdx +++ /dev/null @@ -1,70 +0,0 @@ -# LinkedIn Post Comments Command - -## Goal - -Add a read-only command that collects every visible person in one exact LinkedIn post's comment threads, including reply authors, and deduplicates them by canonical LinkedIn profile URL. - -## Command - -```bash -webcmd linkedin post-comments [--limit N] -``` - -- `` must be an HTTPS LinkedIn post URL under `/feed/update/` or `/posts/`. -- Without `--limit`, the command loads comments and replies until LinkedIn exposes no more. -- With `--limit N`, it returns the first `N` unique profile URLs and stops without treating the intentional truncation as an error. - -## Strategy - -Strategy: `UI_SELECTOR` with DOM extraction -Contract: visible UI - -Evidence from the supplied post: - -- Rendered comments use stable `replaceableComment_urn:li:comment:...` containers. -- Person-authored containers expose an exact `/in//` link, sometimes with a locale suffix, plus name, headline, relative timestamp, and comment text. -- Organization-authored comments expose `/company/` identities; they count toward pagination progress but are excluded from the people result. -- Reply pagination is exposed through visible `See previous replies` controls. -- The observed network alternative is LinkedIn's internal `flagship-web/rsc-action/actions/pagination` action, whose private action payload is a higher-drift contract. - -The command will therefore navigate to the exact post, expand reply controls, advance the post's scrollable workspace, and extract rendered comment containers. It will not replay or reverse engineer LinkedIn's internal RSC action. - -## Output - -One row per canonical profile URL, in first-seen order: - -| Column | Meaning | -| --- | --- | -| `rank` | First-seen position after deduplication | -| `name` | Visible commenter name | -| `headline` | Visible LinkedIn headline | -| `profile_url` | Canonical `https://www.linkedin.com/in//` identity | -| `comment_count` | Number of loaded comments or replies authored by this profile | -| `sample_comment` | First loaded comment text from this profile | -| `commented_at` | Visible relative timestamp for the sample comment | -| `source_post` | Canonical input post URL | - -Mentioned profile links inside comment text are not identities: the first author profile link inside each comment container is authoritative. - -## Data flow - -1. Validate and canonicalize the exact post URL. -2. Open it in the authenticated LinkedIn browser session and reject auth walls. -3. Extract currently rendered comment containers. -4. Expand every visible `See previous replies` control and advance the post workspace. -5. Repeat while new comment containers or pagination controls appear, or until `--limit` unique profiles have been collected. Treat the visible comment count as a progress hint because it can include unavailable or moderated comments. -6. Normalize rows, aggregate duplicate authors, and return first-seen order. - -The unbounded path stops after two stable bottom rounds when the visible count is reached, or ten stable bottom rounds when LinkedIn's count includes a non-renderable comment. - -## Errors - -- Invalid or non-LinkedIn post URL: `ArgumentError`. -- Missing browser session or malformed rendered payload: `CommandExecutionError`. -- Login or checkpoint page: `AuthRequiredError`. -- No visible comments after the post finishes loading: `EmptyResultError`. -- A requested `--limit` is intentional truncation and never an error. - -## Verification - -Use TDD with focused adapter tests for command registration, URL validation, comment parsing, reply-author inclusion, canonical profile deduplication, duplicate aggregation, limit behavior, and malformed/auth/empty states. Then run the LinkedIn adapter test, repository audits, full unit and adapter suite, build, and a live read-only invocation against the supplied post. diff --git a/plugins/linkedin/README.md b/plugins/linkedin/README.md new file mode 100644 index 00000000..2086e14a --- /dev/null +++ b/plugins/linkedin/README.md @@ -0,0 +1,27 @@ +# webcmd-plugin-linkedin + +LinkedIn profile, network, messaging, job, post, and Sales Navigator commands for WebCMD. Sign in to LinkedIn in the managed browser before running authenticated commands. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/linkedin +``` + +## Commands + +- Authentication: `linkedin login`, `linkedin whoami` +- Profiles: `linkedin profile-read`, `linkedin profile-experience`, `linkedin profile-projects`, `linkedin profile-analytics`, `linkedin services-read` +- Network: `linkedin people-search`, `linkedin connections`, `linkedin connect`, `linkedin company`, `linkedin sent-invitations` +- Posts: `linkedin posts`, `linkedin post-analytics`, `linkedin post-comments`, `linkedin timeline` +- Jobs: `linkedin search`, `linkedin job-detail`, `linkedin jobs-preferences` +- Messaging: `linkedin inbox`, `linkedin thread-snapshot`, `linkedin safe-send` +- Sales Navigator: `linkedin salesnav-search`, `linkedin salesnav-inbox`, `linkedin salesnav-thread`, `linkedin salesnav-message` + +## Examples + +```bash +webcmd linkedin profile-read https://www.linkedin.com/in/example/ +webcmd linkedin post-comments https://www.linkedin.com/posts/example_activity-123 +webcmd linkedin people-search "product manager" --limit 10 +``` diff --git a/clis/linkedin/__fixtures__/post-comments.html b/plugins/linkedin/__fixtures__/post-comments.html similarity index 100% rename from clis/linkedin/__fixtures__/post-comments.html rename to plugins/linkedin/__fixtures__/post-comments.html diff --git a/clis/linkedin/auth.js b/plugins/linkedin/auth.js similarity index 97% rename from clis/linkedin/auth.js rename to plugins/linkedin/auth.js index 11125232..e1b070b6 100644 --- a/clis/linkedin/auth.js +++ b/plugins/linkedin/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from './site-auth.js'; async function hasLinkedinSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://www.linkedin.com' }); diff --git a/clis/linkedin/company.js b/plugins/linkedin/company.js similarity index 100% rename from clis/linkedin/company.js rename to plugins/linkedin/company.js diff --git a/clis/linkedin/connect.js b/plugins/linkedin/connect.js similarity index 100% rename from clis/linkedin/connect.js rename to plugins/linkedin/connect.js diff --git a/clis/linkedin/connections.js b/plugins/linkedin/connections.js similarity index 100% rename from clis/linkedin/connections.js rename to plugins/linkedin/connections.js diff --git a/clis/linkedin/inbox.js b/plugins/linkedin/inbox.js similarity index 100% rename from clis/linkedin/inbox.js rename to plugins/linkedin/inbox.js diff --git a/clis/linkedin/job-detail.js b/plugins/linkedin/job-detail.js similarity index 100% rename from clis/linkedin/job-detail.js rename to plugins/linkedin/job-detail.js diff --git a/clis/linkedin/jobs-preferences.js b/plugins/linkedin/jobs-preferences.js similarity index 100% rename from clis/linkedin/jobs-preferences.js rename to plugins/linkedin/jobs-preferences.js diff --git a/plugins/linkedin/package.json b/plugins/linkedin/package.json new file mode 100644 index 00000000..abfa5199 --- /dev/null +++ b/plugins/linkedin/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-linkedin", + "version": "0.1.0", + "type": "module", + "description": "LinkedIn profile, network, messaging, job, and Sales Navigator commands for WebCMD", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.5.2" + } +} diff --git a/clis/linkedin/people-search.js b/plugins/linkedin/people-search.js similarity index 100% rename from clis/linkedin/people-search.js rename to plugins/linkedin/people-search.js diff --git a/clis/linkedin/post-analytics.js b/plugins/linkedin/post-analytics.js similarity index 100% rename from clis/linkedin/post-analytics.js rename to plugins/linkedin/post-analytics.js diff --git a/clis/linkedin/post-comments.js b/plugins/linkedin/post-comments.js similarity index 100% rename from clis/linkedin/post-comments.js rename to plugins/linkedin/post-comments.js diff --git a/clis/linkedin/posts-core.js b/plugins/linkedin/posts-core.js similarity index 100% rename from clis/linkedin/posts-core.js rename to plugins/linkedin/posts-core.js diff --git a/clis/linkedin/posts.js b/plugins/linkedin/posts.js similarity index 100% rename from clis/linkedin/posts.js rename to plugins/linkedin/posts.js diff --git a/clis/linkedin/profile-analytics.js b/plugins/linkedin/profile-analytics.js similarity index 100% rename from clis/linkedin/profile-analytics.js rename to plugins/linkedin/profile-analytics.js diff --git a/clis/linkedin/profile-experience.js b/plugins/linkedin/profile-experience.js similarity index 100% rename from clis/linkedin/profile-experience.js rename to plugins/linkedin/profile-experience.js diff --git a/clis/linkedin/profile-projects.js b/plugins/linkedin/profile-projects.js similarity index 100% rename from clis/linkedin/profile-projects.js rename to plugins/linkedin/profile-projects.js diff --git a/clis/linkedin/profile-read.js b/plugins/linkedin/profile-read.js similarity index 100% rename from clis/linkedin/profile-read.js rename to plugins/linkedin/profile-read.js diff --git a/clis/linkedin/safe-send.js b/plugins/linkedin/safe-send.js similarity index 100% rename from clis/linkedin/safe-send.js rename to plugins/linkedin/safe-send.js diff --git a/clis/linkedin/salesnav-inbox.js b/plugins/linkedin/salesnav-inbox.js similarity index 100% rename from clis/linkedin/salesnav-inbox.js rename to plugins/linkedin/salesnav-inbox.js diff --git a/clis/linkedin/salesnav-message.js b/plugins/linkedin/salesnav-message.js similarity index 100% rename from clis/linkedin/salesnav-message.js rename to plugins/linkedin/salesnav-message.js diff --git a/clis/linkedin/salesnav-search.js b/plugins/linkedin/salesnav-search.js similarity index 100% rename from clis/linkedin/salesnav-search.js rename to plugins/linkedin/salesnav-search.js diff --git a/clis/linkedin/salesnav-thread.js b/plugins/linkedin/salesnav-thread.js similarity index 100% rename from clis/linkedin/salesnav-thread.js rename to plugins/linkedin/salesnav-thread.js diff --git a/clis/linkedin/search.js b/plugins/linkedin/search.js similarity index 100% rename from clis/linkedin/search.js rename to plugins/linkedin/search.js diff --git a/clis/linkedin/sent-invitations.js b/plugins/linkedin/sent-invitations.js similarity index 100% rename from clis/linkedin/sent-invitations.js rename to plugins/linkedin/sent-invitations.js diff --git a/clis/linkedin/services-read.js b/plugins/linkedin/services-read.js similarity index 100% rename from clis/linkedin/services-read.js rename to plugins/linkedin/services-read.js diff --git a/clis/linkedin/shared.js b/plugins/linkedin/shared.js similarity index 100% rename from clis/linkedin/shared.js rename to plugins/linkedin/shared.js diff --git a/plugins/linkedin/site-auth.js b/plugins/linkedin/site-auth.js new file mode 100644 index 00000000..8c3281b2 --- /dev/null +++ b/plugins/linkedin/site-auth.js @@ -0,0 +1,119 @@ +import { AuthRequiredError } from '@agentrhq/webcmd/errors'; +import { cli, Strategy } from '@agentrhq/webcmd/registry'; + +const LOGIN_ACTION = 'Complete sign-in in the opened Webcmd browser, then tell the agent when you are done.'; + +function normalizeIdentity(config, identity) { + const row = identity && typeof identity === 'object' && !Array.isArray(identity) + ? identity + : {}; + return { ...blankIdentity(config), ...row, logged_in: true, site: config.site }; +} + +function isAuthRequired(error) { + return error instanceof AuthRequiredError; +} + +async function tryProbe(config, page) { + return normalizeIdentity(config, await config.verify(page, { phase: 'identity' })); +} + +function identityColumns(config) { + return config.columns ?? ['id', 'username', 'name']; +} + +function blankIdentity(config) { + return Object.fromEntries(identityColumns(config).map((column) => [column, ''])); +} + +function commandColumns(config) { + return ['logged_in', 'site', ...identityColumns(config)]; +} + +function loginColumns(config) { + return ['status', ...commandColumns(config), 'action', 'verify_command']; +} + +function normalizeQuickCheck(result) { + if (typeof result === 'boolean') return { logged_in: result }; + if (result && typeof result === 'object' && !Array.isArray(result)) { + return { logged_in: !!result.logged_in, ...result }; + } + return { logged_in: false }; +} + +function normalizeRefreshResult(result) { + if (result && typeof result === 'object' && !Array.isArray(result)) return result; + return { touched: true }; +} + +export function registerSiteAuthCommands(config) { + if (!config?.site || !config?.domain || !config?.loginUrl || typeof config.verify !== 'function') { + throw new Error('registerSiteAuthCommands requires site, domain, loginUrl, and verify(page)'); + } + // Sites whose login is a modal/flow rather than a page can pass + // openLogin(page) to bring the login UI up; default is a plain navigation. + const openLogin = typeof config.openLogin === 'function' + ? config.openLogin + : async (page) => { await page.goto(config.loginUrl); }; + + cli({ + site: config.site, + name: 'whoami', + access: 'read', + description: config.whoamiDescription ?? `Show the current logged-in ${config.site} account`, + domain: config.domain, + strategy: Strategy.COOKIE, + browser: true, + navigateBefore: false, + siteSession: 'persistent', + aliases: config.whoamiAliases ?? [], + args: [], + columns: commandColumns(config), + authStatus: { + ...(typeof config.quickCheck === 'function' + ? { quickCheck: async (page) => normalizeQuickCheck(await config.quickCheck(page)) } + : {}), + ...(typeof config.refresh === 'function' + ? { refresh: async (page, kwargs) => normalizeRefreshResult(await config.refresh(page, kwargs)) } + : {}), + }, + func: async (page) => [await tryProbe(config, page)], + }); + + cli({ + site: config.site, + name: 'login', + access: 'write', + description: config.loginDescription ?? `Open ${config.site} login`, + domain: config.domain, + strategy: Strategy.COOKIE, + browser: true, + navigateBefore: false, + siteSession: 'persistent', + args: [], + columns: loginColumns(config), + func: async (page) => { + try { + return [{ + status: 'already_logged_in', + ...await tryProbe(config, page), + action: '', + verify_command: '', + }]; + } catch (error) { + if (!isAuthRequired(error)) throw error; + } + + await openLogin(page); + return [{ + status: 'action_required', + logged_in: false, + site: config.site, + ...blankIdentity(config), + action: LOGIN_ACTION, + verify_command: `webcmd ${config.site} whoami`, + }]; + }, + }); +} diff --git a/clis/linkedin/company.test.js b/plugins/linkedin/test/company.test.js similarity index 98% rename from clis/linkedin/company.test.js rename to plugins/linkedin/test/company.test.js index fd7b8db5..bcde5469 100644 --- a/clis/linkedin/company.test.js +++ b/plugins/linkedin/test/company.test.js @@ -1,9 +1,9 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './company.js'; +import '../company.js'; -const { normalizeCompanyInfo, normalizeCompanyUrl } = await import('./company.js').then((module) => module.__test__); +const { normalizeCompanyInfo, normalizeCompanyUrl } = await import('../company.js').then((module) => module.__test__); function makePage(evaluateResult) { return { diff --git a/clis/linkedin/connect.test.js b/plugins/linkedin/test/connect.test.js similarity index 99% rename from clis/linkedin/connect.test.js rename to plugins/linkedin/test/connect.test.js index faa92a9d..e4d20d56 100644 --- a/clis/linkedin/connect.test.js +++ b/plugins/linkedin/test/connect.test.js @@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest'; import { JSDOM } from 'jsdom'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './connect.js'; +import '../connect.js'; const { normalizeName, @@ -13,7 +13,7 @@ const { clampNote, assessProfileSafety, buildProfileProbeScript, -} = await import('./connect.js').then((m) => m.__test__); +} = await import('../connect.js').then((m) => m.__test__); function makeFakePage(probe, sendResult = { ok: true, status: 'sent', reason: 'connection_request_sent' }) { return { diff --git a/clis/linkedin/connections.test.js b/plugins/linkedin/test/connections.test.js similarity index 97% rename from clis/linkedin/connections.test.js rename to plugins/linkedin/test/connections.test.js index 9eb7ddc3..241fbd55 100644 --- a/clis/linkedin/connections.test.js +++ b/plugins/linkedin/test/connections.test.js @@ -6,9 +6,9 @@ import { CommandExecutionError, EmptyResultError, } from '@agentrhq/webcmd/errors'; -import './connections.js'; +import '../connections.js'; -const { mapConnection } = await import('./connections.js').then((module) => module.__test__); +const { mapConnection } = await import('../connections.js').then((module) => module.__test__); function makePage({ evaluateResults = [false], cookies = [{ name: 'JSESSIONID', value: '"ajax:12345"' }] } = {}) { const evaluate = vi.fn(); diff --git a/clis/linkedin/inbox.test.js b/plugins/linkedin/test/inbox.test.js similarity index 98% rename from clis/linkedin/inbox.test.js rename to plugins/linkedin/test/inbox.test.js index 5378d376..f5a57cfe 100644 --- a/clis/linkedin/inbox.test.js +++ b/plugins/linkedin/test/inbox.test.js @@ -1,9 +1,9 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './inbox.js'; +import '../inbox.js'; -const { parseConversations, threadUrl } = await import('./inbox.js').then((m) => m.__test__); +const { parseConversations, threadUrl } = await import('../inbox.js').then((m) => m.__test__); const SELF = 'urn:li:fsd_profile:SELF'; diff --git a/clis/linkedin/job-detail.test.js b/plugins/linkedin/test/job-detail.test.js similarity index 95% rename from clis/linkedin/job-detail.test.js rename to plugins/linkedin/test/job-detail.test.js index e11f06d9..ce2b28df 100644 --- a/clis/linkedin/job-detail.test.js +++ b/plugins/linkedin/test/job-detail.test.js @@ -1,9 +1,9 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './job-detail.js'; +import '../job-detail.js'; -const { normalizeJobUrl, decodeLinkedinRedirect, normalizeDetail } = await import('./job-detail.js').then((m) => m.__test__); +const { normalizeJobUrl, decodeLinkedinRedirect, normalizeDetail } = await import('../job-detail.js').then((m) => m.__test__); describe('linkedin job-detail adapter', () => { const command = getRegistry().get('linkedin/job-detail'); diff --git a/clis/linkedin/jobs-preferences.test.js b/plugins/linkedin/test/jobs-preferences.test.js similarity index 95% rename from clis/linkedin/jobs-preferences.test.js rename to plugins/linkedin/test/jobs-preferences.test.js index a3193db1..1e88f1b8 100644 --- a/clis/linkedin/jobs-preferences.test.js +++ b/plugins/linkedin/test/jobs-preferences.test.js @@ -1,9 +1,9 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './jobs-preferences.js'; +import '../jobs-preferences.js'; -const { inferOpenToWork, normalizePreferences } = await import('./jobs-preferences.js').then((m) => m.__test__); +const { inferOpenToWork, normalizePreferences } = await import('../jobs-preferences.js').then((m) => m.__test__); describe('linkedin jobs-preferences adapter', () => { const command = getRegistry().get('linkedin/jobs-preferences'); diff --git a/clis/linkedin/people-search.test.js b/plugins/linkedin/test/people-search.test.js similarity index 99% rename from clis/linkedin/people-search.test.js rename to plugins/linkedin/test/people-search.test.js index cc248577..267b4de9 100644 --- a/clis/linkedin/people-search.test.js +++ b/plugins/linkedin/test/people-search.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './people-search.js'; +import '../people-search.js'; const { parseLimit, @@ -11,7 +11,7 @@ const { normalizePeopleRows, parseNonNegativeCount, extractionScript, -} = await import('./people-search.js').then((m) => m.__test__); +} = await import('../people-search.js').then((m) => m.__test__); function extractionResult(rows, counts = {}) { return { diff --git a/clis/linkedin/post-analytics.test.js b/plugins/linkedin/test/post-analytics.test.js similarity index 92% rename from clis/linkedin/post-analytics.test.js rename to plugins/linkedin/test/post-analytics.test.js index 68eb9c00..c2298ddd 100644 --- a/clis/linkedin/post-analytics.test.js +++ b/plugins/linkedin/test/post-analytics.test.js @@ -1,9 +1,9 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { EmptyResultError } from '@agentrhq/webcmd/errors'; -import './post-analytics.js'; +import '../post-analytics.js'; -const { summarize } = await import('./post-analytics.js').then((m) => m.__test__); +const { summarize } = await import('../post-analytics.js').then((m) => m.__test__); describe('linkedin post-analytics adapter', () => { const command = getRegistry().get('linkedin/post-analytics'); diff --git a/clis/linkedin/post-comments.test.js b/plugins/linkedin/test/post-comments.test.js similarity index 98% rename from clis/linkedin/post-comments.test.js rename to plugins/linkedin/test/post-comments.test.js index 13d56ff5..d2f989c3 100644 --- a/clis/linkedin/post-comments.test.js +++ b/plugins/linkedin/test/post-comments.test.js @@ -9,7 +9,7 @@ import { CommandExecutionError, EmptyResultError, } from '@agentrhq/webcmd/errors'; -import './post-comments.js'; +import '../post-comments.js'; const { canonicalizePostUrl, @@ -17,7 +17,7 @@ const { parseOptionalLimit, buildCommentRoundScript, normalizeCommentRows, -} = await import('./post-comments.js').then((module) => module.__test__); +} = await import('../post-comments.js').then((module) => module.__test__); const rawComment = (id, handle, name, comment, overrides = {}) => ({ rawId: `comment-${id}`, @@ -104,7 +104,7 @@ describe('linkedin post-comments', () => { }); it('extracts top-level and reply authors without promoting mentioned profiles', () => { - const html = fs.readFileSync(path.join(import.meta.dirname, '__fixtures__/post-comments.html'), 'utf8'); + const html = fs.readFileSync(path.join(import.meta.dirname, '../__fixtures__/post-comments.html'), 'utf8'); const dom = new JSDOM(html, { runScripts: 'outside-only', url: 'https://www.linkedin.com/feed/update/urn:li:activity:1/', diff --git a/clis/linkedin/posts.test.js b/plugins/linkedin/test/posts.test.js similarity index 97% rename from clis/linkedin/posts.test.js rename to plugins/linkedin/test/posts.test.js index 443973cb..8c3ebee9 100644 --- a/clis/linkedin/posts.test.js +++ b/plugins/linkedin/test/posts.test.js @@ -2,9 +2,9 @@ import { describe, expect, it } from 'vitest'; import { JSDOM } from 'jsdom'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './posts.js'; +import '../posts.js'; -const { activityUrl, buildPostsScript, parseMetric, parseReactionText, normalizePost } = await import('./posts-core.js'); +const { activityUrl, buildPostsScript, parseMetric, parseReactionText, normalizePost } = await import('../posts-core.js'); describe('linkedin posts adapter', () => { const command = getRegistry().get('linkedin/posts'); diff --git a/clis/linkedin/profile-analytics.test.js b/plugins/linkedin/test/profile-analytics.test.js similarity index 95% rename from clis/linkedin/profile-analytics.test.js rename to plugins/linkedin/test/profile-analytics.test.js index 674f4736..ca96548d 100644 --- a/clis/linkedin/profile-analytics.test.js +++ b/plugins/linkedin/test/profile-analytics.test.js @@ -1,14 +1,14 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './profile-analytics.js'; +import '../profile-analytics.js'; const { normalizeProfileAnalyticsUrl, parseMetric, parseDashboardMetrics, normalizeAnalytics, -} = await import('./profile-analytics.js').then((m) => m.__test__); +} = await import('../profile-analytics.js').then((m) => m.__test__); describe('linkedin profile-analytics adapter', () => { const command = getRegistry().get('linkedin/profile-analytics'); diff --git a/clis/linkedin/profile-experience.test.js b/plugins/linkedin/test/profile-experience.test.js similarity index 98% rename from clis/linkedin/profile-experience.test.js rename to plugins/linkedin/test/profile-experience.test.js index 894d1cc0..f1df5aa4 100644 --- a/clis/linkedin/profile-experience.test.js +++ b/plugins/linkedin/test/profile-experience.test.js @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './profile-experience.js'; +import '../profile-experience.js'; const { normalizeProfileUrl, @@ -15,7 +15,7 @@ const { buildExperienceExtractionScript, buildDialogExtractionScript, normalizeExperience, -} = await import('./profile-experience.js').then((m) => m.__test__); +} = await import('../profile-experience.js').then((m) => m.__test__); describe('linkedin profile-experience adapter', () => { const command = getRegistry().get('linkedin/profile-experience'); diff --git a/clis/linkedin/profile-projects.test.js b/plugins/linkedin/test/profile-projects.test.js similarity index 97% rename from clis/linkedin/profile-projects.test.js rename to plugins/linkedin/test/profile-projects.test.js index 14ab9ae0..cf3faed0 100644 --- a/clis/linkedin/profile-projects.test.js +++ b/plugins/linkedin/test/profile-projects.test.js @@ -1,9 +1,9 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './profile-projects.js'; +import '../profile-projects.js'; -const { normalizeProfileUrl, profileProjectsUrl, parseProjectText, parseProjectsSectionText, decodeLinkedInSafetyUrl, normalizeProject } = await import('./profile-projects.js').then((m) => m.__test__); +const { normalizeProfileUrl, profileProjectsUrl, parseProjectText, parseProjectsSectionText, decodeLinkedInSafetyUrl, normalizeProject } = await import('../profile-projects.js').then((m) => m.__test__); describe('linkedin profile-projects adapter', () => { const command = getRegistry().get('linkedin/profile-projects'); diff --git a/clis/linkedin/profile-read.test.js b/plugins/linkedin/test/profile-read.test.js similarity index 97% rename from clis/linkedin/profile-read.test.js rename to plugins/linkedin/test/profile-read.test.js index 0d6b700c..3d49a5bc 100644 --- a/clis/linkedin/profile-read.test.js +++ b/plugins/linkedin/test/profile-read.test.js @@ -1,9 +1,9 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './profile-read.js'; +import '../profile-read.js'; -const { normalizeProfileReadUrl, normalizeProfile } = await import('./profile-read.js').then((m) => m.__test__); +const { normalizeProfileReadUrl, normalizeProfile } = await import('../profile-read.js').then((m) => m.__test__); describe('linkedin profile-read adapter', () => { const command = getRegistry().get('linkedin/profile-read'); diff --git a/clis/linkedin/safe-send.test.js b/plugins/linkedin/test/safe-send.test.js similarity index 98% rename from clis/linkedin/safe-send.test.js rename to plugins/linkedin/test/safe-send.test.js index 4c099be7..f7980360 100644 --- a/clis/linkedin/safe-send.test.js +++ b/plugins/linkedin/test/safe-send.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './safe-send.js'; +import '../safe-send.js'; const { normalizeWhitespace, @@ -9,7 +9,7 @@ const { canonicalizeLinkedInThreadUrl, hashText, assessThreadSafety, -} = await import('./safe-send.js').then((m) => m.__test__); +} = await import('../safe-send.js').then((m) => m.__test__); function makeFakePage(probe) { let composerText = probe.composerText || ''; diff --git a/clis/linkedin/salesnav-inbox.test.js b/plugins/linkedin/test/salesnav-inbox.test.js similarity index 97% rename from clis/linkedin/salesnav-inbox.test.js rename to plugins/linkedin/test/salesnav-inbox.test.js index 8c7f295f..692764b3 100644 --- a/clis/linkedin/salesnav-inbox.test.js +++ b/plugins/linkedin/test/salesnav-inbox.test.js @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './salesnav-inbox.js'; +import '../salesnav-inbox.js'; const { THREAD_DECORATION, @@ -9,7 +9,7 @@ const { parseSalesnavThreads, salesnavThreadUrl, threadListUrl, -} = await import('./salesnav-inbox.js').then((m) => m.__test__); +} = await import('../salesnav-inbox.js').then((m) => m.__test__); describe('linkedin salesnav-inbox command', () => { it('percent-encodes Rest.li decoration parentheses for Sales Navigator messaging', () => { diff --git a/clis/linkedin/salesnav-message.test.js b/plugins/linkedin/test/salesnav-message.test.js similarity index 98% rename from clis/linkedin/salesnav-message.test.js rename to plugins/linkedin/test/salesnav-message.test.js index a30d5d20..9ebd0874 100644 --- a/clis/linkedin/salesnav-message.test.js +++ b/plugins/linkedin/test/salesnav-message.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './salesnav-message.js'; +import '../salesnav-message.js'; const { parseSalesProfileUrn, @@ -13,7 +13,7 @@ const { profileSummary, requireProfileSummary, salesPageShowsSentMessage, -} = await import('./salesnav-message.js').then((m) => m.__test__); +} = await import('../salesnav-message.js').then((m) => m.__test__); function createPageMock(evaluateResults = []) { const evaluate = vi.fn(); diff --git a/clis/linkedin/salesnav-search.test.js b/plugins/linkedin/test/salesnav-search.test.js similarity index 97% rename from clis/linkedin/salesnav-search.test.js rename to plugins/linkedin/test/salesnav-search.test.js index 994bc43e..b691615a 100644 --- a/clis/linkedin/salesnav-search.test.js +++ b/plugins/linkedin/test/salesnav-search.test.js @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './salesnav-search.js'; +import '../salesnav-search.js'; const { parseLimit, @@ -9,7 +9,7 @@ const { leadUrlFromEntityUrn, parseLeads, requireLeadSearchResult, -} = await import('./salesnav-search.js').then((m) => m.__test__); +} = await import('../salesnav-search.js').then((m) => m.__test__); describe('linkedin salesnav-search command', () => { it('builds a salesApiLeadSearch URL with encoded keywords and pagination', () => { diff --git a/clis/linkedin/salesnav-thread.test.js b/plugins/linkedin/test/salesnav-thread.test.js similarity index 97% rename from clis/linkedin/salesnav-thread.test.js rename to plugins/linkedin/test/salesnav-thread.test.js index 9d65b50d..27989b6f 100644 --- a/clis/linkedin/salesnav-thread.test.js +++ b/plugins/linkedin/test/salesnav-thread.test.js @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './salesnav-thread.js'; +import '../salesnav-thread.js'; const { parseThreadInput, @@ -8,7 +8,7 @@ const { parseSalesnavThreadMessages, threadMatchesInput, salesnavThreadUrl, -} = await import('./salesnav-thread.js').then((m) => m.__test__); +} = await import('../salesnav-thread.js').then((m) => m.__test__); describe('linkedin salesnav-thread command', () => { it('accepts Sales Navigator inbox URLs, raw thread ids, lead URLs, urns, and names', () => { diff --git a/clis/linkedin/search.test.js b/plugins/linkedin/test/search.test.js similarity index 99% rename from clis/linkedin/search.test.js rename to plugins/linkedin/test/search.test.js index b6b57172..fe9e7d27 100644 --- a/clis/linkedin/search.test.js +++ b/plugins/linkedin/test/search.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError } from '@agentrhq/webcmd/errors'; -import { __test__ } from './search.js'; +import { __test__ } from '../search.js'; const { parseCsvArg, diff --git a/clis/linkedin/sent-invitations.test.js b/plugins/linkedin/test/sent-invitations.test.js similarity index 94% rename from clis/linkedin/sent-invitations.test.js rename to plugins/linkedin/test/sent-invitations.test.js index 919abc93..d8598d7d 100644 --- a/clis/linkedin/sent-invitations.test.js +++ b/plugins/linkedin/test/sent-invitations.test.js @@ -1,9 +1,9 @@ import { describe, expect, it } from 'vitest'; import { JSDOM } from 'jsdom'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './sent-invitations.js'; +import '../sent-invitations.js'; -const { buildSentInvitationsScript } = await import('./sent-invitations.js').then((m) => m.__test__); +const { buildSentInvitationsScript } = await import('../sent-invitations.js').then((m) => m.__test__); describe('linkedin sent-invitations command', () => { it('registers with structured columns that do not include raw blobs', () => { diff --git a/clis/linkedin/services-read.test.js b/plugins/linkedin/test/services-read.test.js similarity index 97% rename from clis/linkedin/services-read.test.js rename to plugins/linkedin/test/services-read.test.js index 11874646..53ff7ca5 100644 --- a/clis/linkedin/services-read.test.js +++ b/plugins/linkedin/test/services-read.test.js @@ -1,14 +1,14 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './services-read.js'; +import '../services-read.js'; const { normalizeProfileUrl, normalizeServicesUrl, normalizeServices, pairsToMedia, -} = await import('./services-read.js').then((m) => m.__test__); +} = await import('../services-read.js').then((m) => m.__test__); describe('linkedin services-read adapter', () => { const command = getRegistry().get('linkedin/services-read'); diff --git a/clis/linkedin/thread-snapshot.test.js b/plugins/linkedin/test/thread-snapshot.test.js similarity index 97% rename from clis/linkedin/thread-snapshot.test.js rename to plugins/linkedin/test/thread-snapshot.test.js index 898efb08..2970ebd6 100644 --- a/clis/linkedin/thread-snapshot.test.js +++ b/plugins/linkedin/test/thread-snapshot.test.js @@ -1,9 +1,9 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './thread-snapshot.js'; +import '../thread-snapshot.js'; -const { canonicalizeLinkedInThreadUrl, parseMaxScrolls } = await import('./thread-snapshot.js').then((m) => m.__test__); +const { canonicalizeLinkedInThreadUrl, parseMaxScrolls } = await import('../thread-snapshot.js').then((m) => m.__test__); function makeFakePage(snapshot) { return { diff --git a/clis/linkedin/timeline.test.js b/plugins/linkedin/test/timeline.test.js similarity index 97% rename from clis/linkedin/timeline.test.js rename to plugins/linkedin/test/timeline.test.js index ccc95101..1ab50326 100644 --- a/clis/linkedin/timeline.test.js +++ b/plugins/linkedin/test/timeline.test.js @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './timeline.js'; -const { parseMetric, buildPostId, mergeTimelinePosts } = await import('./timeline.js').then((m) => m.__test__); +import '../timeline.js'; +const { parseMetric, buildPostId, mergeTimelinePosts } = await import('../timeline.js').then((m) => m.__test__); describe('linkedin timeline adapter', () => { const command = getRegistry().get('linkedin/timeline'); it('registers the command with correct shape', () => { diff --git a/clis/linkedin/thread-snapshot.js b/plugins/linkedin/thread-snapshot.js similarity index 100% rename from clis/linkedin/thread-snapshot.js rename to plugins/linkedin/thread-snapshot.js diff --git a/clis/linkedin/timeline.js b/plugins/linkedin/timeline.js similarity index 100% rename from clis/linkedin/timeline.js rename to plugins/linkedin/timeline.js diff --git a/plugins/linkedin/webcmd-plugin.json b/plugins/linkedin/webcmd-plugin.json new file mode 100644 index 00000000..d0164be2 --- /dev/null +++ b/plugins/linkedin/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "linkedin", + "version": "0.1.0", + "description": "LinkedIn profile, network, messaging, job, and Sales Navigator commands for WebCMD", + "webcmd": ">=0.5.2", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/scripts/silent-column-drop-baseline.json b/scripts/silent-column-drop-baseline.json index 997464ef..7ec6c18a 100644 --- a/scripts/silent-column-drop-baseline.json +++ b/scripts/silent-column-drop-baseline.json @@ -247,28 +247,6 @@ "state" ] }, - { - "command": "linkedin/timeline", - "file": "clis/linkedin/timeline.js", - "missing": [ - "authorUrl", - "postedAt" - ] - }, - { - "command": "linkedin/timeline", - "file": "clis/linkedin/timeline.js", - "missing": [ - "id" - ] - }, - { - "command": "linkedin/timeline", - "file": "clis/linkedin/timeline.js", - "missing": [ - "postedAt" - ] - }, { "command": "paperreview/review", "file": "clis/paperreview/review.js", diff --git a/scripts/typed-error-lint-baseline.json b/scripts/typed-error-lint-baseline.json index 446d1b3e..22c9f62b 100644 --- a/scripts/typed-error-lint-baseline.json +++ b/scripts/typed-error-lint-baseline.json @@ -159,22 +159,6 @@ "text": "const limit = Math.max(1, Math.min(Number(args.limit) || 20, 100));", "occurrence": 0 }, - { - "rule": "silent-clamp", - "command": "linkedin/search", - "file": "clis/linkedin/search.js", - "line": 256, - "text": "const count = Math.min(MAX_BATCH, input.limit - allJobs.length);", - "occurrence": 0 - }, - { - "rule": "silent-clamp", - "command": "linkedin/timeline", - "file": "clis/linkedin/timeline.js", - "line": 479, - "text": "const limit = Math.max(1, Math.min(kwargs.limit ?? 20, 100));", - "occurrence": 0 - }, { "rule": "silent-clamp", "command": "producthunt/browse", diff --git a/src/plugin.test.ts b/src/plugin.test.ts index 29630deb..cfd69a8d 100644 --- a/src/plugin.test.ts +++ b/src/plugin.test.ts @@ -819,6 +819,15 @@ describe('updateAllPlugins', () => { // ── Monorepo-specific tests ───────────────────────────────────────────────── describe('parseSource with monorepo subplugin', () => { + it('resolves the LinkedIn catalog source as a WebCMD subplugin', () => { + expect(_parseSource('github:agentrhq/webcmd/linkedin')).toEqual({ + type: 'git', + cloneUrl: 'https://github.com/agentrhq/webcmd.git', + name: 'webcmd', + subPlugin: 'linkedin', + }); + }); + it('parses github:user/repo/subplugin format', () => { const result = _parseSource('github:ByteYue/webcmd-plugins/polymarket'); expect(result).toEqual({ diff --git a/vitest.config.ts b/vitest.config.ts index 964ecc35..59674a86 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -15,7 +15,7 @@ export default defineConfig({ { test: { name: 'adapter', - include: ['clis/**/*.test.{ts,js}'], + include: ['clis/**/*.test.{ts,js}', 'plugins/linkedin/test/**/*.test.{ts,js}'], sequence: { groupOrder: 1 }, }, }, diff --git a/webcmd-plugin.json b/webcmd-plugin.json index 7ed717b3..d9e57f13 100644 --- a/webcmd-plugin.json +++ b/webcmd-plugin.json @@ -14,6 +14,16 @@ "handle": "agentrhq" } }, + "linkedin": { + "path": "plugins/linkedin", + "version": "0.1.0", + "description": "LinkedIn profile, network, messaging, job, and Sales Navigator commands for WebCMD", + "webcmd": ">=0.5.2", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "pypi": { "path": "plugins/pypi", "version": "0.1.0", From e291f2e466372f255aa819d5b77918a68842de6b Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 15:37:34 +0530 Subject: [PATCH 05/39] feat: add public Webcmd plugin runtime --- clis/_shared/site-auth.test.js | 184 - package.json | 3 +- src/plugin-runtime.test.ts | 347 + src/plugin-runtime.ts | 295 + test/fixtures/core-cli-manifest-v0.5.3.json | 27680 ++++++++++++++++++ 5 files changed, 28324 insertions(+), 185 deletions(-) delete mode 100644 clis/_shared/site-auth.test.js create mode 100644 src/plugin-runtime.test.ts create mode 100644 src/plugin-runtime.ts create mode 100644 test/fixtures/core-cli-manifest-v0.5.3.json diff --git a/clis/_shared/site-auth.test.js b/clis/_shared/site-auth.test.js deleted file mode 100644 index 9f0e0028..00000000 --- a/clis/_shared/site-auth.test.js +++ /dev/null @@ -1,184 +0,0 @@ -import { describe, expect, it, vi } from 'vitest'; -import { AuthRequiredError } from '@agentrhq/webcmd/errors'; -import { getRegistry } from '@agentrhq/webcmd/registry'; -import { registerSiteAuthCommands } from './site-auth.js'; - -function pageMock() { - return { - goto: vi.fn().mockResolvedValue(undefined), - wait: vi.fn().mockResolvedValue(undefined), - }; -} - -describe('site auth command helper', () => { - it('registers whoami aliases and foreground login columns', () => { - registerSiteAuthCommands({ - site: 'auth-helper-registration', - domain: 'example.com', - loginUrl: 'https://example.com/login', - columns: ['username'], - whoamiAliases: ['auth-status'], - verify: async () => ({ username: 'alice' }), - }); - - expect(getRegistry().get('auth-helper-registration/whoami')).toMatchObject({ - access: 'read', - browser: true, - navigateBefore: false, - aliases: ['auth-status'], - columns: ['logged_in', 'site', 'username'], - }); - expect(getRegistry().get('auth-helper-registration/auth-status')) - .toBe(getRegistry().get('auth-helper-registration/whoami')); - const login = getRegistry().get('auth-helper-registration/login'); - expect(login).toMatchObject({ - access: 'write', - browser: true, - navigateBefore: false, - siteSession: 'persistent', - }); - expect(login.args).toEqual([]); - expect(login.columns).toEqual([ - 'status', 'logged_in', 'site', 'username', 'action', 'verify_command', - ]); - }); - - it('whoami returns normalized identity without opening login', async () => { - registerSiteAuthCommands({ - site: 'auth-helper-whoami', - domain: 'example.com', - loginUrl: 'https://example.com/login', - columns: ['username'], - verify: async () => ({ username: 'alice' }), - }); - const cmd = getRegistry().get('auth-helper-whoami/whoami'); - const page = pageMock(); - - await expect(cmd.func(page, {})).resolves.toEqual([{ - logged_in: true, - site: 'auth-helper-whoami', - username: 'alice', - }]); - expect(page.goto).not.toHaveBeenCalled(); - }); - - it('login returns the existing authenticated identity', async () => { - registerSiteAuthCommands({ - site: 'auth-helper-authenticated', - domain: 'example.com', - loginUrl: 'https://example.com/login', - columns: ['username'], - verify: async () => ({ username: 'alice' }), - }); - const login = getRegistry().get('auth-helper-authenticated/login'); - const page = pageMock(); - - await expect(login.func(page, {})).resolves.toEqual([{ - status: 'already_logged_in', - logged_in: true, - site: 'auth-helper-authenticated', - username: 'alice', - action: '', - verify_command: '', - }]); - expect(page.goto).not.toHaveBeenCalled(); - }); - - it('completes and canonicalizes successful identity rows', async () => { - registerSiteAuthCommands({ - site: 'auth-helper-canonical', - domain: 'example.com', - loginUrl: 'https://example.com/login', - columns: ['username', 'name'], - verify: async () => ({ - logged_in: false, - site: 'wrong-site', - username: 'alice', - extra: 'preserved', - }), - }); - const page = pageMock(); - const identity = { - logged_in: true, - site: 'auth-helper-canonical', - username: 'alice', - name: '', - extra: 'preserved', - }; - - await expect(getRegistry().get('auth-helper-canonical/whoami').func(page, {})) - .resolves.toEqual([identity]); - await expect(getRegistry().get('auth-helper-canonical/login').func(page, {})) - .resolves.toEqual([{ - status: 'already_logged_in', - ...identity, - action: '', - verify_command: '', - }]); - }); - - it('opens the default login URL and returns an immediate handoff', async () => { - registerSiteAuthCommands({ - site: 'auth-helper-login', - domain: 'example.com', - loginUrl: 'https://example.com/login', - columns: ['username'], - verify: async () => { throw new AuthRequiredError('example.com', 'missing'); }, - }); - const login = getRegistry().get('auth-helper-login/login'); - const page = pageMock(); - - expect(login.args).toEqual([]); - expect(login.columns).toEqual([ - 'status', 'logged_in', 'site', 'username', 'action', 'verify_command', - ]); - await expect(login.func(page, {})).resolves.toEqual([{ - status: 'action_required', - logged_in: false, - site: 'auth-helper-login', - username: '', - action: 'Complete sign-in in the opened Webcmd browser, then tell the agent when you are done.', - verify_command: 'webcmd auth-helper-login whoami', - }]); - expect(page.goto).toHaveBeenCalledWith('https://example.com/login'); - expect(page.wait).not.toHaveBeenCalled(); - }); - - it('uses a custom opener for the immediate handoff', async () => { - const openLogin = vi.fn().mockResolvedValue(undefined); - registerSiteAuthCommands({ - site: 'auth-helper-custom-login', - domain: 'example.com', - loginUrl: 'https://example.com/login', - verify: async () => { throw new AuthRequiredError('example.com', 'missing'); }, - openLogin, - }); - const login = getRegistry().get('auth-helper-custom-login/login'); - const page = pageMock(); - - await login.func(page, {}); - expect(openLogin).toHaveBeenCalledOnce(); - expect(page.goto).not.toHaveBeenCalled(); - }); - - it('propagates non-auth probe and opener errors', async () => { - registerSiteAuthCommands({ - site: 'auth-helper-probe-error', - domain: 'example.com', - loginUrl: 'https://example.com/login', - verify: async () => { throw new Error('probe broke'); }, - }); - registerSiteAuthCommands({ - site: 'auth-helper-open-error', - domain: 'example.com', - loginUrl: 'https://example.com/login', - verify: async () => { throw new AuthRequiredError('example.com', 'missing'); }, - openLogin: async () => { throw new Error('open broke'); }, - }); - - await expect(getRegistry().get('auth-helper-probe-error/login').func(pageMock(), {})) - .rejects.toThrow('probe broke'); - await expect(getRegistry().get('auth-helper-open-error/login').func(pageMock(), {})) - .rejects.toThrow('open broke'); - }); -}); diff --git a/package.json b/package.json index 92084509..abf8aef7 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "./download/media-download": "./dist/src/download/media-download.js", "./download/progress": "./dist/src/download/progress.js", "./fetch/command": "./dist/src/fetch/command.js", - "./pipeline": "./dist/src/pipeline/index.js" + "./pipeline": "./dist/src/pipeline/index.js", + "./plugin-runtime": "./dist/src/plugin-runtime.js" }, "files": [ "dist/src/", diff --git a/src/plugin-runtime.test.ts b/src/plugin-runtime.test.ts new file mode 100644 index 00000000..938e9db4 --- /dev/null +++ b/src/plugin-runtime.test.ts @@ -0,0 +1,347 @@ +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { describe, expect, it, vi } from 'vitest'; +import { + ArgumentError, + AuthRequiredError, + CommandExecutionError, + EmptyResultError, +} from './errors.js'; +import { getRegistry } from './registry-api.js'; +import { + clampInt, + emptySearchResults, + makeDumpCommand, + makeNewCommand, + makeScreenshotCommand, + makeStatusCommand, + registerSiteAuthCommands, + requireBoundedInteger, + requireNonEmptyQuery, + requireNonNegativeInteger, + requireRows, + requireSearchQuery, + runBrowserStep, + toHttpsUrl, + unwrapBrowserResult, +} from './plugin-runtime.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, '..'); + +function runBrowserCommand(command: { func?: unknown }, page: unknown, kwargs: Record = {}) { + return (command.func as (page: unknown, kwargs: Record) => Promise)(page, kwargs); +} + +describe('core CLI manifest v0.5.3', () => { + it('freezes the bundled command surface before plugin migration', () => { + const manifest = JSON.parse(fs.readFileSync( + path.join(ROOT, 'test/fixtures/core-cli-manifest-v0.5.3.json'), + 'utf8', + )) as Array<{ site: string; name: string }>; + const keys = manifest.map(({ site, name }) => `${site}/${name}`); + + expect(manifest).toHaveLength(780); + expect(new Set(manifest.map(({ site }) => site)).size).toBe(108); + expect(new Set(keys).size).toBe(780); + expect(keys.filter((key) => key.startsWith('linkedin/'))).toEqual([]); + }); +}); + +describe('plugin runtime search helpers', () => { + it('normalizes and validates command arguments', () => { + expect(clampInt('4.9', 1, 2, 4)).toBe(4); + expect(clampInt('nope', 1, 2, 4)).toBe(1); + expect(requireNonEmptyQuery(' hello ')).toBe('hello'); + expect(requireSearchQuery(' hello ')).toBe('hello'); + expect(requireBoundedInteger(undefined, 2, 1, 3, 'limit')).toBe(2); + expect(requireNonNegativeInteger(undefined, 0, 'offset')).toBe(0); + + expect(() => requireNonEmptyQuery(' ')).toThrow(ArgumentError); + expect(() => requireSearchQuery(' ')).toThrow(ArgumentError); + expect(() => requireBoundedInteger('2.5', 1, 1, 3, 'limit')).toThrow(ArgumentError); + expect(() => requireNonNegativeInteger(-1, 0, 'offset')).toThrow(ArgumentError); + }); + + it('normalizes browser data and typed errors without changing their identity', async () => { + const rows = [{ title: 'one' }]; + const argumentError = new ArgumentError('bad query'); + const codedFunction = Object.assign(() => undefined, { code: 'CUSTOM' }); + + expect(unwrapBrowserResult({ session: 'x', data: rows })).toBe(rows); + expect(unwrapBrowserResult(rows)).toBe(rows); + expect(requireRows({ session: 'x', data: rows }, 'search')).toBe(rows); + expect(() => requireRows({}, 'search')).toThrow(CommandExecutionError); + expect(toHttpsUrl('/path', 'https://example.com/base')).toBe('https://example.com/path'); + expect(toHttpsUrl('javascript:alert(1)', 'https://example.com')).toBe(''); + expect(emptySearchResults('example', 'needle')).toBeInstanceOf(EmptyResultError); + await expect(runBrowserStep('search', async () => 'ok')).resolves.toBe('ok'); + await expect(runBrowserStep('search', async () => { throw argumentError; })).rejects.toBe(argumentError); + await expect(runBrowserStep('search', async () => { throw codedFunction; })).rejects.toBe(codedFunction); + await expect(runBrowserStep('search', async () => { throw new Error('broke'); })) + .rejects.toThrow('search failed: broke'); + }); +}); + +describe('plugin runtime desktop command factories', () => { + it('creates screenshot, status, new, and dump commands with their existing behavior', async () => { + const site = `runtime-test-${process.pid}`; + const output = `/tmp/${site}.txt`; + const page = { + evaluate: vi.fn() + .mockResolvedValueOnce('snapshot') + .mockResolvedValueOnce('https://example.com') + .mockResolvedValueOnce('Example') + .mockResolvedValueOnce('dump'), + snapshot: vi.fn() + .mockResolvedValueOnce({ tree: 'snapshot' }) + .mockResolvedValueOnce({ tree: 'dump' }), + pressKey: vi.fn().mockResolvedValue(undefined), + wait: vi.fn().mockResolvedValue(undefined), + }; + const screenshot = makeScreenshotCommand(site, 'Runtime Test'); + const status = makeStatusCommand(site, 'Runtime Test'); + const create = makeNewCommand(site, 'Runtime Test'); + const dump = makeDumpCommand(site); + + try { + await expect(runBrowserCommand(screenshot, page, { output })).resolves.toEqual([ + { Status: 'Success', File: `/tmp/${site}-dom.html` }, + { Status: 'Success', File: `/tmp/${site}-a11y.txt` }, + ]); + await expect(runBrowserCommand(status, page)).resolves.toEqual([ + { Status: 'Connected', Url: 'https://example.com', Title: 'Example' }, + ]); + await expect(runBrowserCommand(create, page)).resolves.toEqual([{ Status: 'Success' }]); + await expect(runBrowserCommand(dump, page)).resolves.toEqual([{ + action: 'Dom extraction finished', + files: `/tmp/${site}-dom.html, /tmp/${site}-snapshot.json`, + }]); + + expect(fs.readFileSync(`/tmp/${site}-a11y.txt`, 'utf8')).toBe('{\n "tree": "snapshot"\n}'); + expect(fs.readFileSync(`/tmp/${site}-dom.html`, 'utf8')).toBe('dump'); + expect(fs.readFileSync(`/tmp/${site}-snapshot.json`, 'utf8')).toBe('{\n "tree": "dump"\n}'); + expect(page.pressKey).toHaveBeenCalledWith(process.platform === 'darwin' ? 'Meta+N' : 'Control+N'); + expect(page.wait).toHaveBeenCalledWith(1); + } finally { + for (const file of [`/tmp/${site}-dom.html`, `/tmp/${site}-a11y.txt`, `/tmp/${site}-snapshot.json`]) { + fs.rmSync(file, { force: true }); + } + } + }); +}); + +function pageMock() { + return { + goto: vi.fn().mockResolvedValue(undefined), + wait: vi.fn().mockResolvedValue(undefined), + }; +} + +describe('site auth command helper', () => { + it('registers whoami aliases and foreground login columns', () => { + registerSiteAuthCommands({ + site: 'auth-helper-registration', + domain: 'example.com', + loginUrl: 'https://example.com/login', + columns: ['username'], + whoamiAliases: ['auth-status'], + verify: async () => ({ username: 'alice' }), + }); + + expect(getRegistry().get('auth-helper-registration/whoami')).toMatchObject({ + access: 'read', + browser: true, + navigateBefore: false, + aliases: ['auth-status'], + columns: ['logged_in', 'site', 'username'], + }); + expect(getRegistry().get('auth-helper-registration/auth-status')) + .toBe(getRegistry().get('auth-helper-registration/whoami')); + const login = getRegistry().get('auth-helper-registration/login')!; + expect(login).toMatchObject({ + access: 'write', + browser: true, + navigateBefore: false, + siteSession: 'persistent', + }); + expect(login.args).toEqual([]); + expect(login.columns).toEqual([ + 'status', 'logged_in', 'site', 'username', 'action', 'verify_command', + ]); + }); + + it('whoami returns normalized identity without opening login', async () => { + registerSiteAuthCommands({ + site: 'auth-helper-whoami', + domain: 'example.com', + loginUrl: 'https://example.com/login', + columns: ['username'], + verify: async () => ({ username: 'alice' }), + }); + const cmd = getRegistry().get('auth-helper-whoami/whoami')!; + const page = pageMock(); + + await expect(runBrowserCommand(cmd, page)).resolves.toEqual([{ + logged_in: true, + site: 'auth-helper-whoami', + username: 'alice', + }]); + expect(page.goto).not.toHaveBeenCalled(); + }); + + it('login returns the existing authenticated identity', async () => { + registerSiteAuthCommands({ + site: 'auth-helper-authenticated', + domain: 'example.com', + loginUrl: 'https://example.com/login', + columns: ['username'], + verify: async () => ({ username: 'alice' }), + }); + const login = getRegistry().get('auth-helper-authenticated/login')!; + const page = pageMock(); + + await expect(runBrowserCommand(login, page)).resolves.toEqual([{ + status: 'already_logged_in', + logged_in: true, + site: 'auth-helper-authenticated', + username: 'alice', + action: '', + verify_command: '', + }]); + expect(page.goto).not.toHaveBeenCalled(); + }); + + it('completes and canonicalizes successful identity rows', async () => { + registerSiteAuthCommands({ + site: 'auth-helper-canonical', + domain: 'example.com', + loginUrl: 'https://example.com/login', + columns: ['username', 'name'], + verify: async () => ({ + logged_in: false, + site: 'wrong-site', + username: 'alice', + extra: 'preserved', + }), + }); + const page = pageMock(); + const identity = { + logged_in: true, + site: 'auth-helper-canonical', + username: 'alice', + name: '', + extra: 'preserved', + }; + + await expect(runBrowserCommand(getRegistry().get('auth-helper-canonical/whoami')!, page)) + .resolves.toEqual([identity]); + await expect(runBrowserCommand(getRegistry().get('auth-helper-canonical/login')!, page)) + .resolves.toEqual([{ + status: 'already_logged_in', + ...identity, + action: '', + verify_command: '', + }]); + }); + + it('opens the default login URL and returns an immediate handoff', async () => { + registerSiteAuthCommands({ + site: 'auth-helper-login', + domain: 'example.com', + loginUrl: 'https://example.com/login', + columns: ['username'], + verify: async () => { throw new AuthRequiredError('example.com', 'missing'); }, + }); + const login = getRegistry().get('auth-helper-login/login')!; + const page = pageMock(); + + expect(login.args).toEqual([]); + expect(login.columns).toEqual([ + 'status', 'logged_in', 'site', 'username', 'action', 'verify_command', + ]); + await expect(runBrowserCommand(login, page)).resolves.toEqual([{ + status: 'action_required', + logged_in: false, + site: 'auth-helper-login', + username: '', + action: 'Complete sign-in in the opened Webcmd browser, then tell the agent when you are done.', + verify_command: 'webcmd auth-helper-login whoami', + }]); + expect(page.goto).toHaveBeenCalledWith('https://example.com/login'); + expect(page.wait).not.toHaveBeenCalled(); + }); + + it('uses a custom opener for the immediate handoff', async () => { + const openLogin = vi.fn().mockResolvedValue(undefined); + registerSiteAuthCommands({ + site: 'auth-helper-custom-login', + domain: 'example.com', + loginUrl: 'https://example.com/login', + verify: async () => { throw new AuthRequiredError('example.com', 'missing'); }, + openLogin, + }); + const login = getRegistry().get('auth-helper-custom-login/login')!; + const page = pageMock(); + + await runBrowserCommand(login, page); + expect(openLogin).toHaveBeenCalledOnce(); + expect(page.goto).not.toHaveBeenCalled(); + }); + + it('keeps optional auth-status hooks tolerant of empty responses', async () => { + registerSiteAuthCommands({ + site: 'auth-helper-hooks', + domain: 'example.com', + loginUrl: 'https://example.com/login', + verify: async () => ({ username: 'alice' }), + quickCheck: async () => null as never, + refresh: async () => null as never, + }); + const hooks = getRegistry().get('auth-helper-hooks/whoami')!.authStatus!; + + await expect(hooks.quickCheck!(pageMock() as never, {})).resolves.toEqual({ logged_in: false }); + await expect(hooks.refresh!(pageMock() as never, {})).resolves.toEqual({ touched: true }); + }); + + it('uses defaults when optional auth callbacks are not functions', async () => { + registerSiteAuthCommands({ + site: 'auth-helper-optional-callbacks', + domain: 'example.com', + loginUrl: 'https://example.com/login', + verify: async () => { throw new AuthRequiredError('example.com', 'missing'); }, + openLogin: true as never, + quickCheck: true as never, + refresh: true as never, + }); + const whoami = getRegistry().get('auth-helper-optional-callbacks/whoami')!; + const login = getRegistry().get('auth-helper-optional-callbacks/login')!; + const page = pageMock(); + + expect(whoami.authStatus).toEqual({}); + await expect(runBrowserCommand(login, page)).resolves.toMatchObject([{ status: 'action_required' }]); + expect(page.goto).toHaveBeenCalledWith('https://example.com/login'); + }); + + it('propagates non-auth probe and opener errors', async () => { + registerSiteAuthCommands({ + site: 'auth-helper-probe-error', + domain: 'example.com', + loginUrl: 'https://example.com/login', + verify: async () => { throw new Error('probe broke'); }, + }); + registerSiteAuthCommands({ + site: 'auth-helper-open-error', + domain: 'example.com', + loginUrl: 'https://example.com/login', + verify: async () => { throw new AuthRequiredError('example.com', 'missing'); }, + openLogin: async () => { throw new Error('open broke'); }, + }); + + await expect(runBrowserCommand(getRegistry().get('auth-helper-probe-error/login')!, pageMock())) + .rejects.toThrow('probe broke'); + await expect(runBrowserCommand(getRegistry().get('auth-helper-open-error/login')!, pageMock())) + .rejects.toThrow('open broke'); + }); +}); diff --git a/src/plugin-runtime.ts b/src/plugin-runtime.ts new file mode 100644 index 00000000..3e186d7f --- /dev/null +++ b/src/plugin-runtime.ts @@ -0,0 +1,295 @@ +import * as fs from 'node:fs'; +import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from './errors.js'; +import { cli, Strategy, type CommandArgs, type CliOptions } from './registry-api.js'; +import type { IPage } from './types.js'; + +export function clampInt(raw: unknown, fallback: number, min: number, max: number): number { + const parsed = Number(raw); + return Number.isFinite(parsed) ? Math.max(min, Math.min(Math.floor(parsed), max)) : fallback; +} + +export function requireNonEmptyQuery(value: unknown, label = 'query'): string { + const normalized = String(value ?? '').trim(); + if (!normalized) throw new ArgumentError(`${label} cannot be empty`); + return normalized; +} + +export function requireSearchQuery(value: unknown, label = 'keyword'): string { + const query = String(value ?? '').trim(); + if (!query) throw new ArgumentError(`${label} cannot be empty`); + return query; +} + +export function requireBoundedInteger( + value: unknown, + defaultValue: number, + min: number, + max: number, + label: string, +): number { + const raw = value ?? defaultValue; + const parsed = typeof raw === 'number' ? raw : Number(raw); + if (!Number.isInteger(parsed)) { + throw new ArgumentError(`${label} must be an integer between ${min} and ${max}, got ${JSON.stringify(value)}`); + } + if (parsed < min || parsed > max) { + throw new ArgumentError(`${label} must be between ${min} and ${max}, got ${parsed}`); + } + return parsed; +} + +export function requireNonNegativeInteger(value: unknown, defaultValue: number, label: string): number { + const raw = value ?? defaultValue; + const parsed = typeof raw === 'number' ? raw : Number(raw); + if (!Number.isInteger(parsed) || parsed < 0) { + throw new ArgumentError(`${label} must be a non-negative integer, got ${JSON.stringify(value)}`); + } + return parsed; +} + +export function unwrapBrowserResult(value: unknown): unknown { + if (value && typeof value === 'object' && !Array.isArray(value) && 'session' in value && 'data' in value) { + return value.data; + } + return value; +} + +export function requireRows(value: unknown, label: string): unknown[] { + const rows = unwrapBrowserResult(value); + if (!Array.isArray(rows)) { + throw new CommandExecutionError(`${label} returned an unexpected payload shape; expected an array of result rows.`); + } + return rows; +} + +export function toHttpsUrl(value: unknown, baseUrl: string): string { + const raw = String(value ?? '').trim(); + if (!raw) return ''; + try { + const url = new URL(raw, baseUrl); + return url.protocol === 'http:' || url.protocol === 'https:' ? url.href : ''; + } catch { + return ''; + } +} + +export function emptySearchResults(site: string, query: string): EmptyResultError { + return new EmptyResultError(`${site} search`, `No ${site} results matched "${query}".`); +} + +export async function runBrowserStep(label: string, fn: () => Promise): Promise { + try { + return await fn(); + } catch (error) { + const typedError = error as { code?: unknown; name?: string } | undefined; + if (typedError?.code || typedError?.name === 'ArgumentError') throw error; + throw new CommandExecutionError(`${label} failed: ${error instanceof Error ? error.message : String(error)}`); + } +} + +type DesktopCommandExtra = Partial>; + +export function makeScreenshotCommand(site: string, displayName?: string, extra: DesktopCommandExtra = {}) { + const label = displayName ?? site; + return cli({ + ...extra, + site, + name: 'screenshot', + access: 'read', + description: `Capture a snapshot of the current ${label} window (DOM + Accessibility tree)`, + domain: 'localhost', + strategy: Strategy.UI, + browser: true, + args: [{ name: 'output', required: false, help: `Output file path (default: /tmp/${site}-snapshot.txt)` }], + columns: ['Status', 'File'], + func: async (page: IPage, kwargs: CommandArgs) => { + const outputPath = kwargs.output || `/tmp/${site}-snapshot.txt`; + const snap = await page.snapshot({ compact: true }); + const html = await page.evaluate('document.documentElement.outerHTML'); + const htmlPath = String(outputPath).replace(/\.\w+$/, '') + '-dom.html'; + const snapPath = String(outputPath).replace(/\.\w+$/, '') + '-a11y.txt'; + fs.writeFileSync(htmlPath, html); + fs.writeFileSync(snapPath, typeof snap === 'string' ? snap : JSON.stringify(snap, null, 2)); + return [{ Status: 'Success', File: htmlPath }, { Status: 'Success', File: snapPath }]; + }, + }); +} + +export function makeStatusCommand(site: string, displayName?: string, extra: DesktopCommandExtra = {}) { + const label = displayName ?? site; + return cli({ + ...extra, + site, + name: 'status', + access: 'read', + description: `Check active CDP connection to ${label}`, + domain: 'localhost', + strategy: Strategy.UI, + browser: true, + args: [], + columns: ['Status', 'Url', 'Title'], + func: async (page: IPage) => [{ + Status: 'Connected', + Url: await page.evaluate('window.location.href'), + Title: await page.evaluate('document.title'), + }], + }); +} + +export function makeNewCommand(site: string, displayName?: string, extra: DesktopCommandExtra = {}) { + const label = displayName ?? site; + return cli({ + ...extra, + site, + name: 'new', + access: 'write', + description: `Start a new ${label} session`, + domain: 'localhost', + strategy: Strategy.UI, + browser: true, + args: [], + columns: ['Status'], + func: async (page: IPage) => { + await page.pressKey(process.platform === 'darwin' ? 'Meta+N' : 'Control+N'); + await page.wait(1); + return [{ Status: 'Success' }]; + }, + }); +} + +export function makeDumpCommand(site: string) { + return cli({ + site, + name: 'dump', + access: 'read', + description: `Dump the DOM and Accessibility tree of ${site} for reverse-engineering`, + domain: 'localhost', + strategy: Strategy.UI, + browser: true, + args: [], + columns: ['action', 'files'], + func: async (page: IPage) => { + fs.writeFileSync(`/tmp/${site}-dom.html`, await page.evaluate('document.body.innerHTML')); + fs.writeFileSync(`/tmp/${site}-snapshot.json`, JSON.stringify(await page.snapshot({ interactive: false }), null, 2)); + return [{ action: 'Dom extraction finished', files: `/tmp/${site}-dom.html, /tmp/${site}-snapshot.json` }]; + }, + }); +} + +type Identity = Record; +type MaybePromise = T | Promise; +export interface SiteAuthConfig { + site: string; + domain: string; + loginUrl: string; + verify: (page: IPage, context: { phase: 'identity' }) => MaybePromise; + columns?: string[]; + whoamiDescription?: string; + whoamiAliases?: string[]; + loginDescription?: string; + openLogin?: (page: IPage) => MaybePromise; + quickCheck?: (page: IPage) => MaybePromise; + refresh?: (page: IPage, kwargs: CommandArgs) => MaybePromise; +} + +const LOGIN_ACTION = 'Complete sign-in in the opened Webcmd browser, then tell the agent when you are done.'; + +function identityColumns(config: SiteAuthConfig): string[] { + return config.columns ?? ['id', 'username', 'name']; +} + +function blankIdentity(config: SiteAuthConfig): Identity { + return Object.fromEntries(identityColumns(config).map((column) => [column, ''])); +} + +function normalizeIdentity(config: SiteAuthConfig, identity: unknown): Identity { + const row = identity && typeof identity === 'object' && !Array.isArray(identity) ? identity : {}; + return { ...blankIdentity(config), ...row, logged_in: true, site: config.site }; +} + +function commandColumns(config: SiteAuthConfig): string[] { + return ['logged_in', 'site', ...identityColumns(config)]; +} + +function loginColumns(config: SiteAuthConfig): string[] { + return ['status', ...commandColumns(config), 'action', 'verify_command']; +} + +function normalizeQuickCheck(result: unknown): Identity { + if (typeof result === 'boolean') return { logged_in: result }; + if (result && typeof result === 'object' && !Array.isArray(result)) { + const row = result as Identity; + return { logged_in: !!row.logged_in, ...row }; + } + return { logged_in: false }; +} + +function normalizeRefreshResult(result: unknown): Identity { + return result && typeof result === 'object' && !Array.isArray(result) ? result as Identity : { touched: true }; +} + +export function registerSiteAuthCommands(config: SiteAuthConfig): void { + if (!config?.site || !config?.domain || !config?.loginUrl || typeof config.verify !== 'function') { + throw new Error('registerSiteAuthCommands requires site, domain, loginUrl, and verify(page)'); + } + const openLogin = typeof config.openLogin === 'function' + ? config.openLogin + : async (page: IPage) => { await page.goto(config.loginUrl); }; + const tryProbe = async (page: IPage) => normalizeIdentity(config, await config.verify(page, { phase: 'identity' })); + const quickCheck = config.quickCheck; + const refresh = config.refresh; + + cli({ + site: config.site, + name: 'whoami', + access: 'read', + description: config.whoamiDescription ?? `Show the current logged-in ${config.site} account`, + domain: config.domain, + strategy: Strategy.COOKIE, + browser: true, + navigateBefore: false, + siteSession: 'persistent', + aliases: config.whoamiAliases ?? [], + args: [], + columns: commandColumns(config), + authStatus: { + ...(typeof quickCheck === 'function' + ? { quickCheck: async (page) => normalizeQuickCheck(await quickCheck(page)) } + : {}), + ...(typeof refresh === 'function' + ? { refresh: async (page, kwargs) => normalizeRefreshResult(await refresh(page, kwargs)) } + : {}), + }, + func: async (page) => [await tryProbe(page)], + }); + + cli({ + site: config.site, + name: 'login', + access: 'write', + description: config.loginDescription ?? `Open ${config.site} login`, + domain: config.domain, + strategy: Strategy.COOKIE, + browser: true, + navigateBefore: false, + siteSession: 'persistent', + args: [], + columns: loginColumns(config), + func: async (page) => { + try { + return [{ status: 'already_logged_in', ...await tryProbe(page), action: '', verify_command: '' }]; + } catch (error) { + if (!(error instanceof AuthRequiredError)) throw error; + } + await openLogin(page); + return [{ + status: 'action_required', + logged_in: false, + site: config.site, + ...blankIdentity(config), + action: LOGIN_ACTION, + verify_command: `webcmd ${config.site} whoami`, + }]; + }, + }); +} diff --git a/test/fixtures/core-cli-manifest-v0.5.3.json b/test/fixtures/core-cli-manifest-v0.5.3.json new file mode 100644 index 00000000..515e034d --- /dev/null +++ b/test/fixtures/core-cli-manifest-v0.5.3.json @@ -0,0 +1,27680 @@ +[ + { + "site": "amazon", + "name": "bestsellers", + "description": "Amazon Best Sellers pages for category candidate discovery", + "access": "read", + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": false, + "positional": true, + "help": "Ranking URL or supported Amazon path. Omit to use the list root." + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Maximum number of ranked items to return (default 100)" + } + ], + "columns": [ + "list_type", + "rank", + "asin", + "title", + "price_text", + "rating_value", + "review_count" + ], + "type": "js", + "modulePath": "amazon/bestsellers.js", + "sourceFile": "amazon/bestsellers.js", + "navigateBefore": false + }, + { + "site": "amazon", + "name": "discussion", + "description": "Amazon review summary and sample customer discussion from product review pages", + "access": "read", + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": true, + "positional": true, + "help": "ASIN or product URL, for example B0FJS72893" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum number of review samples to return (default 10)" + } + ], + "columns": [ + "asin", + "average_rating_value", + "total_review_count" + ], + "type": "js", + "modulePath": "amazon/discussion.js", + "sourceFile": "amazon/discussion.js", + "navigateBefore": false + }, + { + "site": "amazon", + "name": "login", + "description": "Open amazon login", + "access": "write", + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "amazon/auth.js", + "sourceFile": "amazon/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "amazon", + "name": "movers-shakers", + "description": "Amazon Movers & Shakers pages for short-term growth signals", + "access": "read", + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": false, + "positional": true, + "help": "Ranking URL or supported Amazon path. Omit to use the list root." + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Maximum number of ranked items to return (default 100)" + } + ], + "columns": [ + "list_type", + "rank", + "asin", + "title", + "price_text", + "rating_value", + "review_count" + ], + "type": "js", + "modulePath": "amazon/movers-shakers.js", + "sourceFile": "amazon/movers-shakers.js", + "navigateBefore": false + }, + { + "site": "amazon", + "name": "new-releases", + "description": "Amazon New Releases pages for early momentum discovery", + "access": "read", + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": false, + "positional": true, + "help": "Ranking URL or supported Amazon path. Omit to use the list root." + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Maximum number of ranked items to return (default 100)" + } + ], + "columns": [ + "list_type", + "rank", + "asin", + "title", + "price_text", + "rating_value", + "review_count" + ], + "type": "js", + "modulePath": "amazon/new-releases.js", + "sourceFile": "amazon/new-releases.js", + "navigateBefore": false + }, + { + "site": "amazon", + "name": "offer", + "description": "Amazon seller, buy box, and fulfillment facts from the product page", + "access": "read", + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": true, + "positional": true, + "help": "ASIN or product URL, for example B0FJS72893" + } + ], + "columns": [ + "asin", + "price_text", + "sold_by", + "ships_from", + "is_amazon_sold", + "is_amazon_fulfilled" + ], + "type": "js", + "modulePath": "amazon/offer.js", + "sourceFile": "amazon/offer.js", + "navigateBefore": false + }, + { + "site": "amazon", + "name": "product", + "description": "Amazon product page facts for candidate validation", + "access": "read", + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": true, + "positional": true, + "help": "ASIN or product URL, for example B0FJS72893" + } + ], + "columns": [ + "asin", + "title", + "price_text", + "rating_value", + "review_count" + ], + "type": "js", + "modulePath": "amazon/product.js", + "sourceFile": "amazon/product.js", + "navigateBefore": false + }, + { + "site": "amazon", + "name": "search", + "description": "Amazon search results for product discovery and coarse filtering", + "access": "read", + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query, for example \"desk shelf organizer\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum number of results to return (default 20)" + } + ], + "columns": [ + "rank", + "asin", + "title", + "price_text", + "rating_value", + "review_count" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "amazon/search.js", + "sourceFile": "amazon/search.js", + "navigateBefore": false + }, + { + "site": "amazon", + "name": "whoami", + "description": "Show the current logged-in amazon account", + "access": "read", + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_name" + ], + "type": "js", + "modulePath": "amazon/auth.js", + "sourceFile": "amazon/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "amazon-in", + "name": "checkout", + "description": "Prepare a guarded Amazon.in checkout with browser-only payment handoff", + "access": "write", + "domain": "amazon.in", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": true, + "positional": true, + "help": "Amazon.in product URL or ASIN" + }, + { + "name": "quantity", + "type": "int", + "default": 1, + "required": false, + "help": "Quantity (1-10)" + }, + { + "name": "size", + "type": "str", + "required": false, + "help": "Exact visible size label" + }, + { + "name": "colour", + "type": "str", + "required": false, + "help": "Exact visible colour label" + }, + { + "name": "payment", + "type": "str", + "required": true, + "help": "Payment method; secrets remain browser-only", + "choices": [ + "upi", + "saved-card", + "new-card", + "cod" + ] + }, + { + "name": "card-last4", + "type": "str", + "required": false, + "help": "Saved-card selector: exactly four digits" + }, + { + "name": "place-order", + "type": "boolean", + "default": false, + "required": false, + "help": "Submit the final Amazon action once" + } + ], + "columns": [ + "status", + "asin", + "title", + "size", + "colour", + "quantity", + "item_price", + "total", + "payment_method", + "delivery_date", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "amazon-in/checkout.js", + "sourceFile": "amazon-in/checkout.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true + }, + { + "site": "amazon-in", + "name": "checkout-status", + "description": "Read the current Amazon.in checkout or payment state without clicking", + "access": "read", + "domain": "amazon.in", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "status", + "order_id", + "total", + "payment_method", + "action" + ], + "type": "js", + "modulePath": "amazon-in/checkout-status.js", + "sourceFile": "amazon-in/checkout-status.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "amazon-in", + "name": "login", + "description": "Open amazon-in login", + "access": "write", + "domain": "amazon.in", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "amazon-in/auth.js", + "sourceFile": "amazon-in/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "amazon-in", + "name": "product", + "description": "Fetch the current Amazon.in price and selected product variant", + "access": "read", + "domain": "amazon.in", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": true, + "positional": true, + "help": "Amazon.in product URL or ASIN" + } + ], + "columns": [ + "asin", + "title", + "price", + "mrp", + "discount", + "availability", + "size", + "colour", + "image_url", + "product_url" + ], + "type": "js", + "modulePath": "amazon-in/product.js", + "sourceFile": "amazon-in/product.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "amazon-in", + "name": "search", + "description": "Search Amazon.in products with inclusive INR price bounds and images", + "access": "read", + "domain": "amazon.in", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Product search query" + }, + { + "name": "min-price", + "type": "number", + "required": false, + "help": "Inclusive minimum price in rupees" + }, + { + "name": "max-price", + "type": "number", + "required": false, + "help": "Inclusive maximum price in rupees" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum results (1-50)" + } + ], + "columns": [ + "rank", + "asin", + "title", + "price", + "mrp", + "rating", + "review_count", + "image_url", + "product_url", + "is_sponsored" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "amazon-in/search.js", + "sourceFile": "amazon-in/search.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "amazon-in", + "name": "whoami", + "description": "Show the current logged-in amazon-in account", + "access": "read", + "domain": "amazon.in", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_name" + ], + "type": "js", + "modulePath": "amazon-in/auth.js", + "sourceFile": "amazon-in/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "amazon-in", + "name": "wishlist", + "description": "Fetch current prices for products in the default Amazon.in wishlist", + "access": "read", + "domain": "amazon.in", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "filter", + "type": "str", + "default": "unpurchased", + "required": false, + "help": "Wishlist items to include", + "choices": [ + "unpurchased", + "all" + ] + } + ], + "columns": [ + "list_name", + "item_id", + "asin", + "title", + "price", + "mrp", + "availability", + "size", + "colour", + "image_url", + "product_url" + ], + "type": "js", + "modulePath": "amazon-in/wishlist.js", + "sourceFile": "amazon-in/wishlist.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "antigravity", + "name": "add-context", + "description": "Click the Add context button in the composer (opens file/URL picker for context attachment).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "cookies", + "description": "List cookies on the Antigravity renderer (JS-visible via document.cookie).", + "access": "read", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" + ], + "type": "js", + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "copy-code", + "description": "Return the text of a code block in the current conversation. Default: last code block; pass --index N (1-based from top) to pick a specific one.", + "access": "read", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "index", + "type": "int", + "required": false, + "help": "1-based index of code block (default: last)" + } + ], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "copy-message", + "description": "Return the text of the last assistant message (best-effort: walks up from the last visible Copy button).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "click-button", + "type": "boolean", + "default": false, + "required": false, + "help": "Also click the in-UI Copy button" + } + ], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "delete", + "description": "Delete an Antigravity conversation by ID. Antigravity asks for confirmation; we click through it. Require --yes to actually delete.", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" + }, + { + "name": "yes", + "type": "boolean", + "default": false, + "required": false, + "help": "Actually delete (default: dry-run preview)" + } + ], + "columns": [ + "status", + "id" + ], + "type": "js", + "modulePath": "antigravity/delete.js", + "sourceFile": "antigravity/delete.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "display-options", + "description": "Open the Display Options menu and list its items.", + "access": "read", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Item" + ], + "type": "js", + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "dump", + "description": "Dump the DOM to help AI understand the UI", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "htmlFile", + "snapFile" + ], + "type": "js", + "modulePath": "antigravity/dump.js", + "sourceFile": "antigravity/dump.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "extract-code", + "description": "Extract multi-line code blocks from the current Antigravity conversation", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "code" + ], + "type": "js", + "modulePath": "antigravity/extract-code.js", + "sourceFile": "antigravity/extract-code.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "history", + "description": "List visible Antigravity conversations from the sidebar", + "access": "read", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max conversations to return" + } + ], + "columns": [ + "Index", + "Id", + "Title" + ], + "type": "js", + "modulePath": "antigravity/history.js", + "sourceFile": "antigravity/history.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "idb-list", + "description": "List IndexedDB databases on the Antigravity renderer.", + "access": "read", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" + ], + "type": "js", + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "mark-read", + "description": "Mark an unread Antigravity conversation as read. Fails if the row is already read or the postcondition cannot be verified.", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" + } + ], + "columns": [ + "status", + "id", + "clicked" + ], + "type": "js", + "modulePath": "antigravity/mark-read.js", + "sourceFile": "antigravity/mark-read.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "model", + "description": "Read or switch the active model in Antigravity. Without arguments, reports the current model. With (substring, case-insensitive), switches.", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": false, + "positional": true, + "help": "Substring (case-insensitive) of target model name. Omit to read current." + }, + { + "name": "list", + "type": "boolean", + "default": false, + "required": false, + "help": "List models in the picker (does not switch)" + } + ], + "columns": [ + "Status", + "Model" + ], + "type": "js", + "modulePath": "antigravity/model.js", + "sourceFile": "antigravity/model.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "nav", + "description": "Click Go Back or Go Forward (Antigravity in-app history).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "direction", + "type": "str", + "required": true, + "positional": true, + "help": "back or forward" + } + ], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "new", + "description": "Start a new conversation / clear context in Antigravity", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "antigravity/new.js", + "sourceFile": "antigravity/new.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "react", + "description": "Click \"Good response\" or \"Bad response\" on the LAST assistant message.", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "kind", + "type": "str", + "required": true, + "positional": true, + "help": "good or bad" + } + ], + "columns": [ + "Status", + "Reaction" + ], + "type": "js", + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "read", + "description": "Read the latest chat messages from Antigravity AI", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "last", + "type": "str", + "required": false, + "help": "Number of recent messages to read (not fully implemented due to generic structure, currently returns full history text or latest chunk)" + } + ], + "columns": [ + "role", + "content" + ], + "type": "js", + "modulePath": "antigravity/read.js", + "sourceFile": "antigravity/read.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "recent-paths", + "description": "Show Antigravity's recently-opened folders/files (history.recentlyOpenedPathsList).", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max rows to return" + } + ], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" + ], + "type": "js", + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js" + }, + { + "site": "antigravity", + "name": "rename", + "description": "Rename an Antigravity conversation by ID (NOT YET IMPLEMENTED — see source comment).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" + }, + { + "name": "title", + "type": "string", + "required": true, + "positional": true, + "help": "New title" + } + ], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "antigravity/rename.js", + "sourceFile": "antigravity/rename.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "revert", + "description": "Click the revert button (per-message revert for agent changes). Requires --yes (this modifies your workspace).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "yes", + "type": "boolean", + "default": false, + "required": false, + "help": "Actually revert (default: dry-run)" + } + ], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "send", + "description": "Send a message to Antigravity AI via the internal Lexical editor", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "message", + "type": "str", + "required": true, + "positional": true, + "help": "The message text to send" + } + ], + "columns": [ + "Status", + "Message" + ], + "type": "js", + "modulePath": "antigravity/send.js", + "sourceFile": "antigravity/send.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "settings", + "description": "Click the Antigravity settings button (matched by data-testid=\"settings-button\").", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "settings-read", + "description": "Read Antigravity's user settings.json (theme, proxy, agCockpit, tfa.system.autoAccept, etc.).", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" + ], + "type": "js", + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js" + }, + { + "site": "antigravity", + "name": "sidebar-toggle", + "description": "Click Toggle Sidebar (collapses/expands the Antigravity sidebar).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "state-get", + "description": "Read one value from Antigravity's state.vscdb. Pass --workspace for per-workspace.", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "Storage key name" + }, + { + "name": "workspace", + "type": "str", + "required": false, + "help": "Workspace id (from workspaces-list) to query per-workspace DB" + }, + { + "name": "max-bytes", + "type": "int", + "default": 8000, + "required": false, + "help": "Truncate value to this many chars" + } + ], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" + ], + "type": "js", + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js" + }, + { + "site": "antigravity", + "name": "state-keys", + "description": "List keys in Antigravity's globalStorage state.vscdb (VSCode-style). Pass --workspace to query a per-workspace DB. Works while Antigravity is closed.", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "filter", + "type": "str", + "required": false, + "help": "Case-insensitive substring filter over keys" + }, + { + "name": "workspace", + "type": "str", + "required": false, + "help": "Workspace id (from workspaces-list) to query per-workspace DB" + }, + { + "name": "limit", + "type": "int", + "default": 200, + "required": false, + "help": "Max rows to return" + } + ], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" + ], + "type": "js", + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js" + }, + { + "site": "antigravity", + "name": "status", + "description": "Check Antigravity CDP connection and get current page state", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "status", + "url", + "title" + ], + "type": "js", + "modulePath": "antigravity/status.js", + "sourceFile": "antigravity/status.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "storage-get", + "description": "Read a single localStorage / sessionStorage value on the Antigravity renderer.", + "access": "read", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "Storage key name" + }, + { + "name": "storage", + "type": "str", + "default": "local", + "required": false, + "help": "\"local\" or \"session\"" + }, + { + "name": "max-bytes", + "type": "int", + "default": 4000, + "required": false, + "help": "Truncate value to this many chars" + } + ], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" + ], + "type": "js", + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "storage-keys", + "description": "List localStorage / sessionStorage keys on the Antigravity renderer (CDP).", + "access": "read", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "storage", + "type": "str", + "default": "local", + "required": false, + "help": "\"local\" or \"session\"" + }, + { + "name": "filter", + "type": "str", + "required": false, + "help": "Case-insensitive substring filter" + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Max rows to return" + } + ], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" + ], + "type": "js", + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "toggle-aux", + "description": "Toggle the Auxiliary Pane (Antigravity's secondary panel for code/preview).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "watch", + "description": "Stream new chat messages from Antigravity in real-time", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "timeout", + "type": "int", + "default": 86400, + "required": false, + "help": "Max seconds to keep watching (default: 86400 — 24h)" + } + ], + "columns": [], + "type": "js", + "modulePath": "antigravity/watch.js", + "sourceFile": "antigravity/watch.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "workspaces-list", + "description": "List Antigravity workspaceStorage entries (each represents a previously-opened folder).", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max rows to return" + } + ], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" + ], + "type": "js", + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js" + }, + { + "site": "apple-podcasts", + "name": "episodes", + "description": "List recent episodes of an Apple Podcast (use ID from search)", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Podcast ID (collectionId from search output)" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Max episodes to show" + } + ], + "columns": [ + "title", + "duration", + "date" + ], + "type": "js", + "modulePath": "apple-podcasts/episodes.js", + "sourceFile": "apple-podcasts/episodes.js" + }, + { + "site": "apple-podcasts", + "name": "search", + "description": "Search Apple Podcasts", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results" + } + ], + "columns": [ + "id", + "title", + "author", + "episodes", + "genre", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "apple-podcasts/search.js", + "sourceFile": "apple-podcasts/search.js" + }, + { + "site": "apple-podcasts", + "name": "top", + "description": "Top podcasts chart on Apple Podcasts", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of podcasts (max 100)" + }, + { + "name": "country", + "type": "str", + "default": "us", + "required": false, + "help": "Country code (e.g. us, cn, gb, jp)" + } + ], + "columns": [ + "rank", + "title", + "author", + "id" + ], + "type": "js", + "modulePath": "apple-podcasts/top.js", + "sourceFile": "apple-podcasts/top.js" + }, + { + "site": "archive", + "name": "item", + "description": "Fetch metadata for a single Internet Archive item by identifier.", + "access": "read", + "domain": "archive.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "identifier", + "type": "str", + "required": true, + "positional": true, + "help": "Archive item identifier (e.g. \"open-syllabus\", \"FinalFantasy2_356\")." + } + ], + "columns": [ + "identifier", + "title", + "creator", + "date", + "mediatype", + "collection", + "description", + "file_count", + "url" + ], + "type": "js", + "modulePath": "archive/item.js", + "sourceFile": "archive/item.js" + }, + { + "site": "archive", + "name": "search", + "description": "Search Internet Archive items across books, movies, audio, software, and web.", + "access": "read", + "domain": "archive.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Full-text query (matches title, description, creator, subject)." + }, + { + "name": "mediatype", + "type": "string", + "required": false, + "help": "Restrict to mediatype: texts, movies, audio, software, image, web, data, collection" + }, + { + "name": "sort", + "type": "string", + "default": "downloads", + "required": false, + "help": "Sort key: downloads, date, addeddate, week, title" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max items (max 100; one API page)." + } + ], + "columns": [ + "rank", + "identifier", + "title", + "creator", + "date", + "mediatype", + "downloads", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "archive/search.js", + "sourceFile": "archive/search.js" + }, + { + "site": "archive", + "name": "snapshots", + "description": "List Wayback Machine snapshots over time for a URL via the CDX API.", + "access": "read", + "domain": "archive.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "URL to look up (with or without scheme)." + }, + { + "name": "from", + "type": "string", + "required": false, + "help": "Earliest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" + }, + { + "name": "to", + "type": "string", + "required": false, + "help": "Latest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max snapshots to return (max 1000)." + } + ], + "columns": [ + "timestamp", + "snapshot_url", + "status", + "mimetype", + "original_url" + ], + "type": "js", + "modulePath": "archive/snapshots.js", + "sourceFile": "archive/snapshots.js" + }, + { + "site": "archive", + "name": "wayback", + "description": "Look up the closest Wayback Machine snapshot for a URL.", + "access": "read", + "domain": "archive.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "URL to look up (with or without scheme)." + }, + { + "name": "timestamp", + "type": "string", + "required": false, + "help": "Target timestamp (YYYY[MM[DD[hh[mm[ss]]]]] or ISO date). Defaults to most recent snapshot." + } + ], + "columns": [ + "original_url", + "requested_timestamp", + "snapshot_timestamp", + "snapshot_url", + "status" + ], + "type": "js", + "modulePath": "archive/wayback.js", + "sourceFile": "archive/wayback.js" + }, + { + "site": "arxiv", + "name": "author", + "description": "List arXiv papers by a given author (newest first)", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "author", + "type": "str", + "required": true, + "positional": true, + "help": "Author name (e.g. \"Yoshua Bengio\" or \"Y Bengio\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max papers to return (max 50)" + } + ], + "columns": [ + "id", + "title", + "authors", + "published", + "primary_category", + "url" + ], + "type": "js", + "modulePath": "arxiv/author.js", + "sourceFile": "arxiv/author.js" + }, + { + "site": "arxiv", + "name": "paper", + "description": "Get arXiv paper details by ID", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "arXiv paper ID (e.g. 1706.03762)" + } + ], + "columns": [ + "id", + "title", + "authors", + "published", + "updated", + "primary_category", + "categories", + "abstract", + "comment", + "pdf", + "url" + ], + "type": "js", + "modulePath": "arxiv/paper.js", + "sourceFile": "arxiv/paper.js" + }, + { + "site": "arxiv", + "name": "recent", + "description": "List recent arXiv submissions in a category", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "category", + "type": "str", + "required": true, + "positional": true, + "help": "arXiv category (e.g. cs.CL, cs.LG, math.PR, q-bio.NC)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results (max 50)" + } + ], + "columns": [ + "id", + "title", + "authors", + "published", + "primary_category", + "url" + ], + "type": "js", + "modulePath": "arxiv/recent.js", + "sourceFile": "arxiv/recent.js" + }, + { + "site": "arxiv", + "name": "search", + "description": "Search arXiv papers", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"attention is all you need\")" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results (max 25)" + } + ], + "columns": [ + "id", + "title", + "authors", + "published", + "primary_category", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "arxiv/search.js", + "sourceFile": "arxiv/search.js" + }, + { + "site": "band", + "name": "bands", + "description": "List all Bands you belong to", + "access": "read", + "domain": "www.band.us", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "band_no", + "name", + "members" + ], + "type": "js", + "modulePath": "band/bands.js", + "sourceFile": "band/bands.js", + "navigateBefore": "https://www.band.us" + }, + { + "site": "band", + "name": "login", + "description": "Open band login", + "access": "write", + "domain": "band.us", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "band/auth.js", + "sourceFile": "band/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "band", + "name": "mentions", + "description": "Show Band notifications where you are @mentioned", + "access": "read", + "domain": "www.band.us", + "strategy": "intercept", + "browser": true, + "args": [ + { + "name": "filter", + "type": "str", + "default": "mentioned", + "required": false, + "help": "Filter: mentioned (default) | all | post | comment", + "choices": [ + "mentioned", + "all", + "post", + "comment" + ] + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results" + }, + { + "name": "unread", + "type": "bool", + "default": false, + "required": false, + "help": "Show only unread notifications" + } + ], + "columns": [ + "time", + "band", + "type", + "from", + "text", + "url" + ], + "type": "js", + "modulePath": "band/mentions.js", + "sourceFile": "band/mentions.js", + "navigateBefore": true + }, + { + "site": "band", + "name": "post", + "description": "Export full content of a post including comments", + "access": "read", + "domain": "www.band.us", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "band_no", + "type": "int", + "required": true, + "positional": true, + "help": "Band number" + }, + { + "name": "post_no", + "type": "int", + "required": true, + "positional": true, + "help": "Post number" + }, + { + "name": "output", + "type": "str", + "default": "", + "required": false, + "help": "Directory to save attached photos" + }, + { + "name": "comments", + "type": "bool", + "default": true, + "required": false, + "help": "Include comments (default: true)" + } + ], + "columns": [ + "type", + "author", + "date", + "text" + ], + "type": "js", + "modulePath": "band/post.js", + "sourceFile": "band/post.js", + "navigateBefore": false + }, + { + "site": "band", + "name": "posts", + "description": "List posts from a Band", + "access": "read", + "domain": "www.band.us", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "band_no", + "type": "int", + "required": true, + "positional": true, + "help": "Band number (get it from: band bands)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results" + } + ], + "columns": [ + "date", + "author", + "content", + "comments", + "url" + ], + "type": "js", + "modulePath": "band/posts.js", + "sourceFile": "band/posts.js", + "navigateBefore": false + }, + { + "site": "band", + "name": "whoami", + "description": "Show the current logged-in band account", + "access": "read", + "domain": "band.us", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id" + ], + "type": "js", + "modulePath": "band/auth.js", + "sourceFile": "band/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "barchart", + "name": "flow", + "description": "Barchart unusual options activity / options flow", + "access": "read", + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "type", + "type": "str", + "default": "all", + "required": false, + "help": "Filter: all, call, or put", + "choices": [ + "all", + "call", + "put" + ] + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "symbol", + "type", + "strike", + "expiration", + "last", + "volume", + "openInterest", + "volOiRatio", + "iv" + ], + "type": "js", + "modulePath": "barchart/flow.js", + "sourceFile": "barchart/flow.js", + "navigateBefore": "https://www.barchart.com" + }, + { + "site": "barchart", + "name": "greeks", + "description": "Barchart options greeks overview (IV, delta, gamma, theta, vega)", + "access": "read", + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Stock ticker (e.g. AAPL)" + }, + { + "name": "expiration", + "type": "str", + "required": false, + "help": "Expiration date (YYYY-MM-DD). Defaults to the nearest available expiration." + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of near-the-money strikes per type (1-100)" + } + ], + "columns": [ + "type", + "strike", + "last", + "iv", + "delta", + "gamma", + "theta", + "vega", + "rho", + "volume", + "openInterest", + "expiration" + ], + "type": "js", + "modulePath": "barchart/greeks.js", + "sourceFile": "barchart/greeks.js", + "navigateBefore": "https://www.barchart.com" + }, + { + "site": "barchart", + "name": "options", + "description": "Barchart options chain with greeks, IV, volume, and open interest", + "access": "read", + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Stock ticker (e.g. AAPL)" + }, + { + "name": "type", + "type": "str", + "default": "Call", + "required": false, + "help": "Option type: Call or Put", + "choices": [ + "Call", + "Put" + ] + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max number of strikes to return" + } + ], + "columns": [ + "strike", + "bid", + "ask", + "last", + "change", + "volume", + "openInterest", + "iv", + "delta", + "gamma", + "theta", + "vega", + "expiration" + ], + "type": "js", + "modulePath": "barchart/options.js", + "sourceFile": "barchart/options.js", + "navigateBefore": "https://www.barchart.com" + }, + { + "site": "barchart", + "name": "quote", + "description": "Barchart stock quote with price, volume, and key metrics", + "access": "read", + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" + } + ], + "columns": [ + "symbol", + "name", + "price", + "change", + "changePct", + "open", + "high", + "low", + "prevClose", + "volume", + "avgVolume", + "marketCap", + "peRatio", + "eps" + ], + "type": "js", + "modulePath": "barchart/quote.js", + "sourceFile": "barchart/quote.js", + "navigateBefore": "https://www.barchart.com" + }, + { + "site": "bbc", + "name": "news", + "description": "BBC News headlines (RSS)", + "access": "read", + "domain": "www.bbc.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of headlines (max 50)" + } + ], + "columns": [ + "rank", + "title", + "description", + "url" + ], + "type": "js", + "modulePath": "bbc/news.js", + "sourceFile": "bbc/news.js" + }, + { + "site": "bbc", + "name": "topic", + "description": "BBC News headlines for a specific section (RSS feed)", + "access": "read", + "domain": "www.bbc.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "topic", + "type": "str", + "required": true, + "positional": true, + "help": "Section name (world / business / politics / health / education / science_and_environment / technology / entertainment_and_arts)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max headlines (1-50)" + } + ], + "columns": [ + "rank", + "title", + "description", + "pubDate", + "url" + ], + "type": "js", + "modulePath": "bbc/topic.js", + "sourceFile": "bbc/topic.js" + }, + { + "site": "bigbasket", + "name": "add-to-cart", + "description": "Add a BigBasket product to cart", + "access": "write", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product", + "type": "str", + "required": true, + "positional": true, + "help": "Product ID or URL" + }, + { + "name": "quantity", + "type": "int", + "default": 1, + "required": false, + "help": "Quantity to add (max 20)" + } + ], + "columns": [ + "ok", + "product_id", + "quantity", + "url", + "message" + ], + "type": "js", + "modulePath": "bigbasket/add-to-cart.js", + "sourceFile": "bigbasket/add-to-cart.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "cart", + "description": "Read BigBasket cart line items", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "product_id", + "title", + "quantity", + "price", + "line_total", + "availability", + "url" + ], + "type": "js", + "modulePath": "bigbasket/cart.js", + "sourceFile": "bigbasket/cart.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "category", + "description": "Read BigBasket category product cards", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "category", + "type": "str", + "required": true, + "positional": true, + "help": "Category URL or slug" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum products to return (max 50)" + } + ], + "columns": [ + "rank", + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", + "url" + ], + "type": "js", + "modulePath": "bigbasket/category.js", + "sourceFile": "bigbasket/category.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "checkout", + "description": "Open BigBasket checkout review without placing an order", + "access": "write", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "ok", + "stage", + "cart_total", + "address_ready", + "delivery_ready", + "payment_ready", + "next_action", + "url" + ], + "type": "js", + "modulePath": "bigbasket/checkout.js", + "sourceFile": "bigbasket/checkout.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "location", + "description": "Show the selected BigBasket delivery location", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "selected", + "label", + "area", + "city", + "pincode", + "source" + ], + "type": "js", + "modulePath": "bigbasket/location.js", + "sourceFile": "bigbasket/location.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "product", + "description": "Read BigBasket product details", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product", + "type": "str", + "required": true, + "positional": true, + "help": "Product ID or URL" + } + ], + "columns": [ + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", + "delivery", + "image_url", + "url" + ], + "type": "js", + "modulePath": "bigbasket/product.js", + "sourceFile": "bigbasket/product.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "search", + "description": "Search BigBasket products", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum products to return (max 50)" + } + ], + "columns": [ + "rank", + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "bigbasket/search.js", + "sourceFile": "bigbasket/search.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "binance", + "name": "asks", + "description": "Order book ask prices for a trading pair", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of price levels (5, 10, 20, 50, 100)" + } + ], + "columns": [ + "rank", + "ask_price", + "ask_qty" + ], + "type": "js", + "modulePath": "binance/asks.js", + "sourceFile": "binance/asks.js" + }, + { + "site": "binance", + "name": "depth", + "description": "Order book bid and ask prices for a trading pair", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of price levels (5, 10, 20, 50, 100)" + } + ], + "columns": [ + "rank", + "bid_price", + "bid_qty", + "ask_price", + "ask_qty" + ], + "type": "js", + "modulePath": "binance/depth.js", + "sourceFile": "binance/depth.js" + }, + { + "site": "binance", + "name": "gainers", + "description": "Top gaining trading pairs by 24h price change", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of trading pairs" + } + ], + "columns": [ + "rank", + "symbol", + "price", + "change_24h", + "volume" + ], + "type": "js", + "modulePath": "binance/gainers.js", + "sourceFile": "binance/gainers.js" + }, + { + "site": "binance", + "name": "klines", + "description": "Candlestick/kline data for a trading pair", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, + { + "name": "interval", + "type": "str", + "default": "1d", + "required": false, + "help": "Kline interval (1m, 5m, 15m, 1h, 4h, 1d, 1w, 1M)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of klines (max 1000)" + } + ], + "columns": [ + "open", + "high", + "low", + "close", + "volume" + ], + "type": "js", + "modulePath": "binance/klines.js", + "sourceFile": "binance/klines.js" + }, + { + "site": "binance", + "name": "losers", + "description": "Top losing trading pairs by 24h price change", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of trading pairs" + } + ], + "columns": [ + "rank", + "symbol", + "price", + "change_24h", + "volume" + ], + "type": "js", + "modulePath": "binance/losers.js", + "sourceFile": "binance/losers.js" + }, + { + "site": "binance", + "name": "pairs", + "description": "List active trading pairs on Binance", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of trading pairs" + } + ], + "columns": [ + "symbol", + "base", + "quote", + "status" + ], + "type": "js", + "modulePath": "binance/pairs.js", + "sourceFile": "binance/pairs.js" + }, + { + "site": "binance", + "name": "price", + "description": "Quick price check for a trading pair", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + } + ], + "columns": [ + "symbol", + "price", + "change", + "change_pct", + "high", + "low", + "volume", + "quote_volume", + "trades" + ], + "type": "js", + "modulePath": "binance/price.js", + "sourceFile": "binance/price.js" + }, + { + "site": "binance", + "name": "prices", + "description": "Latest prices for all trading pairs", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of prices" + } + ], + "columns": [ + "rank", + "symbol", + "price" + ], + "type": "js", + "modulePath": "binance/prices.js", + "sourceFile": "binance/prices.js" + }, + { + "site": "binance", + "name": "ticker", + "description": "24h ticker statistics for top trading pairs by volume", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of tickers" + } + ], + "columns": [ + "symbol", + "price", + "change_pct", + "high", + "low", + "volume", + "quote_vol", + "trades" + ], + "type": "js", + "modulePath": "binance/ticker.js", + "sourceFile": "binance/ticker.js" + }, + { + "site": "binance", + "name": "top", + "description": "Top trading pairs by 24h volume on Binance", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of trading pairs" + } + ], + "columns": [ + "rank", + "symbol", + "price", + "change_24h", + "high", + "low", + "volume" + ], + "type": "js", + "modulePath": "binance/top.js", + "sourceFile": "binance/top.js" + }, + { + "site": "binance", + "name": "trades", + "description": "Recent trades for a trading pair", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of trades (max 1000)" + } + ], + "columns": [ + "id", + "price", + "qty", + "quote_qty", + "buyer_maker" + ], + "type": "js", + "modulePath": "binance/trades.js", + "sourceFile": "binance/trades.js" + }, + { + "site": "blinkit", + "name": "add-to-cart", + "description": "Add a Blinkit product to cart", + "access": "write", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "productId", + "type": "str", + "required": true, + "positional": true, + "help": "Blinkit product id" + }, + { + "name": "quantity", + "type": "int", + "default": 1, + "required": false, + "help": "Quantity to add (default 1, max 12)" + }, + { + "name": "lat", + "type": "str", + "required": false, + "help": "Delivery latitude (defaults to current Blinkit browser location)" + }, + { + "name": "lon", + "type": "str", + "required": false, + "help": "Delivery longitude (defaults to current Blinkit browser location)" + } + ], + "columns": [ + "status", + "productId", + "quantity", + "itemCount", + "itemsTotal", + "payable", + "message" + ], + "type": "js", + "modulePath": "blinkit/add-to-cart.js", + "sourceFile": "blinkit/add-to-cart.js", + "navigateBefore": false + }, + { + "site": "blinkit", + "name": "cart", + "description": "Show the current Blinkit cart", + "access": "read", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "productId", + "name", + "variant", + "price", + "quantity", + "total", + "itemCount", + "payable", + "cartState" + ], + "type": "js", + "modulePath": "blinkit/cart.js", + "sourceFile": "blinkit/cart.js", + "navigateBefore": false + }, + { + "site": "blinkit", + "name": "checkout", + "description": "Review Blinkit checkout totals and blockers without placing an order", + "access": "read", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "itemCount", + "itemsTotal", + "deliveryCharge", + "handlingCharge", + "payable", + "cartState", + "checkoutBlocked", + "validations" + ], + "type": "js", + "modulePath": "blinkit/checkout.js", + "sourceFile": "blinkit/checkout.js", + "navigateBefore": false + }, + { + "site": "blinkit", + "name": "location", + "description": "Show the selected Blinkit delivery location", + "access": "read", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "selected", + "label", + "area", + "city", + "pincode", + "hasCoordinates", + "source" + ], + "type": "js", + "modulePath": "blinkit/location.js", + "sourceFile": "blinkit/location.js", + "navigateBefore": "https://blinkit.com" + }, + { + "site": "blinkit", + "name": "login", + "description": "Open blinkit login", + "access": "write", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "phone", + "user_id", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "blinkit/auth.js", + "sourceFile": "blinkit/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "blinkit", + "name": "place-order", + "description": "Submit the visible Blinkit final order/payment action. Requires --confirm.", + "access": "write", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "confirm", + "type": "bool", + "default": false, + "required": false, + "help": "Required acknowledgement that this may place/pay for a real order" + } + ], + "columns": [ + "status", + "confirmed", + "itemCount", + "payable", + "orderId", + "url", + "message" + ], + "type": "js", + "modulePath": "blinkit/place-order.js", + "sourceFile": "blinkit/place-order.js", + "navigateBefore": false + }, + { + "site": "blinkit", + "name": "product", + "description": "Read Blinkit product details for a delivery location", + "access": "read", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "productId", + "type": "str", + "required": true, + "positional": true, + "help": "Blinkit product id" + }, + { + "name": "lat", + "type": "str", + "required": false, + "help": "Delivery latitude (defaults to current Blinkit browser location)" + }, + { + "name": "lon", + "type": "str", + "required": false, + "help": "Delivery longitude (defaults to current Blinkit browser location)" + } + ], + "columns": [ + "productId", + "name", + "brand", + "variant", + "price", + "mrp", + "currency", + "inventory", + "available", + "imageUrl", + "url" + ], + "type": "js", + "modulePath": "blinkit/product.js", + "sourceFile": "blinkit/product.js", + "navigateBefore": false + }, + { + "site": "blinkit", + "name": "search", + "description": "Search Blinkit products for a delivery location", + "access": "read", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (max 48)" + }, + { + "name": "lat", + "type": "str", + "required": false, + "help": "Delivery latitude (defaults to current Blinkit browser location)" + }, + { + "name": "lon", + "type": "str", + "required": false, + "help": "Delivery longitude (defaults to current Blinkit browser location)" + } + ], + "columns": [ + "rank", + "productId", + "name", + "brand", + "variant", + "price", + "mrp", + "currency", + "inventory", + "available", + "imageUrl", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "blinkit/search.js", + "sourceFile": "blinkit/search.js", + "navigateBefore": false + }, + { + "site": "blinkit", + "name": "whoami", + "description": "Show the current logged-in blinkit account", + "access": "read", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "phone", + "user_id" + ], + "type": "js", + "modulePath": "blinkit/auth.js", + "sourceFile": "blinkit/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "bloomberg", + "name": "businessweek", + "description": "Bloomberg Businessweek top stories", + "access": "read", + "domain": "www.bloomberg.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of stories to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "bloomberg/businessweek.js", + "sourceFile": "bloomberg/businessweek.js" + }, + { + "site": "bloomberg", + "name": "crypto", + "description": "Bloomberg Crypto top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "bloomberg/crypto.js", + "sourceFile": "bloomberg/crypto.js" + }, + { + "site": "bloomberg", + "name": "economics", + "description": "Bloomberg Economics top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "bloomberg/economics.js", + "sourceFile": "bloomberg/economics.js" + }, + { + "site": "bloomberg", + "name": "feeds", + "description": "List the Bloomberg RSS feed aliases used by the adapter", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [], + "columns": [ + "name", + "url" + ], + "type": "js", + "modulePath": "bloomberg/feeds.js", + "sourceFile": "bloomberg/feeds.js" + }, + { + "site": "bloomberg", + "name": "green", + "description": "Bloomberg Green (climate & energy) top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "bloomberg/green.js", + "sourceFile": "bloomberg/green.js" + }, + { + "site": "bloomberg", + "name": "industries", + "description": "Bloomberg Industries top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "bloomberg/industries.js", + "sourceFile": "bloomberg/industries.js" + }, + { + "site": "bloomberg", + "name": "main", + "description": "Bloomberg homepage top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "bloomberg/main.js", + "sourceFile": "bloomberg/main.js" + }, + { + "site": "bloomberg", + "name": "markets", + "description": "Bloomberg Markets top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "bloomberg/markets.js", + "sourceFile": "bloomberg/markets.js" + }, + { + "site": "bloomberg", + "name": "news", + "description": "Read a Bloomberg story/article page and return title, full content, and media links", + "access": "read", + "domain": "www.bloomberg.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "link", + "type": "str", + "required": true, + "positional": true, + "help": "Bloomberg story/article URL or relative Bloomberg path" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks", + "content" + ], + "type": "js", + "modulePath": "bloomberg/news.js", + "sourceFile": "bloomberg/news.js", + "navigateBefore": "https://www.bloomberg.com" + }, + { + "site": "bloomberg", + "name": "opinions", + "description": "Bloomberg Opinion top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "bloomberg/opinions.js", + "sourceFile": "bloomberg/opinions.js" + }, + { + "site": "bloomberg", + "name": "politics", + "description": "Bloomberg Politics top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "bloomberg/politics.js", + "sourceFile": "bloomberg/politics.js" + }, + { + "site": "bloomberg", + "name": "pursuits", + "description": "Bloomberg Pursuits (lifestyle) top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "bloomberg/pursuits.js", + "sourceFile": "bloomberg/pursuits.js" + }, + { + "site": "bloomberg", + "name": "tech", + "description": "Bloomberg Tech top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "bloomberg/tech.js", + "sourceFile": "bloomberg/tech.js" + }, + { + "site": "bluesky", + "name": "feeds", + "description": "Popular Bluesky feed generators", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of feeds" + } + ], + "columns": [ + "rank", + "name", + "likes", + "creator", + "description" + ], + "type": "js", + "modulePath": "bluesky/feeds.js", + "sourceFile": "bluesky/feeds.js" + }, + { + "site": "bluesky", + "name": "followers", + "description": "List followers of a Bluesky user", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of followers" + } + ], + "columns": [ + "rank", + "handle", + "name", + "description" + ], + "type": "js", + "modulePath": "bluesky/followers.js", + "sourceFile": "bluesky/followers.js" + }, + { + "site": "bluesky", + "name": "following", + "description": "List accounts a Bluesky user is following", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of accounts" + } + ], + "columns": [ + "rank", + "handle", + "name", + "description" + ], + "type": "js", + "modulePath": "bluesky/following.js", + "sourceFile": "bluesky/following.js" + }, + { + "site": "bluesky", + "name": "profile", + "description": "Get Bluesky user profile info", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle (e.g. bsky.app, jay.bsky.team)" + } + ], + "columns": [ + "handle", + "name", + "followers", + "following", + "posts", + "description" + ], + "type": "js", + "modulePath": "bluesky/profile.js", + "sourceFile": "bluesky/profile.js" + }, + { + "site": "bluesky", + "name": "search", + "description": "Search Bluesky users", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "handle", + "name", + "followers", + "description" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "bluesky/search.js", + "sourceFile": "bluesky/search.js" + }, + { + "site": "bluesky", + "name": "starter-packs", + "description": "Get starter packs created by a Bluesky user", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of starter packs" + } + ], + "columns": [ + "rank", + "name", + "description", + "members", + "joins" + ], + "type": "js", + "modulePath": "bluesky/starter-packs.js", + "sourceFile": "bluesky/starter-packs.js" + }, + { + "site": "bluesky", + "name": "thread", + "description": "Get a Bluesky post thread with replies", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "uri", + "type": "str", + "required": true, + "positional": true, + "help": "Post AT URI (at://did:.../app.bsky.feed.post/...) or bsky.app URL" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of replies" + } + ], + "columns": [ + "author", + "text", + "likes", + "reposts", + "replies_count" + ], + "type": "js", + "modulePath": "bluesky/thread.js", + "sourceFile": "bluesky/thread.js" + }, + { + "site": "bluesky", + "name": "trending", + "description": "Trending topics on Bluesky", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of topics" + } + ], + "columns": [ + "rank", + "topic", + "link" + ], + "type": "js", + "modulePath": "bluesky/trending.js", + "sourceFile": "bluesky/trending.js" + }, + { + "site": "bluesky", + "name": "user", + "description": "Get recent posts from a Bluesky user", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle (e.g. bsky.app)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts" + } + ], + "columns": [ + "rank", + "uri", + "text", + "likes", + "reposts", + "replies" + ], + "type": "js", + "modulePath": "bluesky/user.js", + "sourceFile": "bluesky/user.js" + }, + { + "site": "booking", + "name": "search", + "description": "Search Booking.com hotels by destination and dates (server-rendered card scrape).", + "access": "read", + "example": "webcmd booking search Tokyo --checkin 2026-06-15 --checkout 2026-06-17 -f yaml", + "domain": "www.booking.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "destination", + "type": "str", + "required": true, + "positional": true, + "help": "Destination keyword (city, district, or hotel name)" + }, + { + "name": "checkin", + "type": "str", + "required": true, + "help": "Check-in date YYYY-MM-DD" + }, + { + "name": "checkout", + "type": "str", + "required": true, + "help": "Check-out date YYYY-MM-DD" + }, + { + "name": "adults", + "type": "int", + "default": 2, + "required": false, + "help": "Number of adults (1-30)" + }, + { + "name": "rooms", + "type": "int", + "default": 1, + "required": false, + "help": "Number of rooms (1-30)" + }, + { + "name": "children", + "type": "int", + "default": 0, + "required": false, + "help": "Number of children (0-10)" + }, + { + "name": "currency", + "type": "str", + "required": false, + "help": "Force result currency (e.g. USD, JPY, CNY)" + }, + { + "name": "lang", + "type": "str", + "required": false, + "help": "Force result language (e.g. en-us, zh-cn, ja)" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max rows to return (1-100; Booking pages 25 per request)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Result offset for pagination (multiple of 25)" + } + ], + "columns": [ + "rank", + "name", + "country", + "slug", + "star_rating", + "review_score", + "review_count", + "price_amount", + "price_currency", + "distance", + "recommended_room", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "booking/search.js", + "sourceFile": "booking/search.js" + }, + { + "site": "brave", + "name": "search", + "description": "Search Brave Search", + "access": "read", + "domain": "search.brave.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results per page (max 18)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Page offset (0, 1, 2...). Brave returns ~18 results per page" + } + ], + "columns": [ + "rank", + "title", + "url", + "snippet" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "brave/search.js", + "sourceFile": "brave/search.js" + }, + { + "site": "chatgpt", + "name": "ask", + "description": "Send a prompt to ChatGPT web and wait for the response", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait for response" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Continue an existing ChatGPT conversation ID or /c/ URL" + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + }, + { + "name": "wait", + "type": "boolean", + "default": true, + "required": false, + "help": "Wait for the assistant response after sending" + }, + { + "name": "deep-research", + "type": "boolean", + "default": false, + "required": false, + "help": "Enable ChatGPT Deep Research (Deep Research)" + }, + { + "name": "web-search", + "type": "boolean", + "default": false, + "required": false, + "help": "Enable ChatGPT Web Search (Web Search)" + } + ], + "columns": [ + "conversationId", + "conversationUrl", + "tool", + "response" + ], + "type": "js", + "modulePath": "chatgpt/ask.js", + "sourceFile": "chatgpt/ask.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "deep-research-result", + "description": "Read a ChatGPT Deep Research report or progress from the conversation payload", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Conversation ID or full /c/ URL" + }, + { + "name": "wait", + "type": "boolean", + "default": false, + "required": false, + "help": "Wait until Deep Research completes or becomes extractable" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait when --wait is true" + }, + { + "name": "stable", + "type": "int", + "default": 6, + "required": false, + "help": "Seconds the report text must remain unchanged when --wait is true" + } + ], + "columns": [ + "conversationId", + "status", + "report", + "sources", + "progress", + "asyncTaskConversationId", + "widgetSessionId", + "asyncStatus", + "venusMessageType", + "venusStatus", + "waitingForUserUntil", + "planTitle", + "planId", + "url", + "method", + "diagnostics" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "chatgpt/deep-research-result.js", + "sourceFile": "chatgpt/deep-research-result.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "detail", + "description": "Open a ChatGPT web conversation by ID and read its messages", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Conversation ID or full /c/ URL" + }, + { + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" + }, + { + "name": "wait", + "type": "boolean", + "default": false, + "required": false, + "help": "Wait until the conversation stops generating and stabilizes" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait when --wait is true" + }, + { + "name": "stable", + "type": "int", + "default": 6, + "required": false, + "help": "Seconds the final messages must remain unchanged when --wait is true" + } + ], + "columns": [ + "Index", + "Role", + "Text", + "Generating", + "StableSeconds" + ], + "type": "js", + "modulePath": "chatgpt/detail.js", + "sourceFile": "chatgpt/detail.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "history", + "description": "List visible ChatGPT web conversation history from the sidebar", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max conversations to show" + } + ], + "columns": [ + "Index", + "Id", + "Title", + "Url" + ], + "type": "js", + "modulePath": "chatgpt/history.js", + "sourceFile": "chatgpt/history.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "image", + "description": "Generate images with ChatGPT web and save them locally", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Image prompt to send to ChatGPT" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Local image path to attach before prompting; comma-separated paths are supported", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start image generation inside a ChatGPT project ID or /g/g-p- URL" + }, + { + "name": "op", + "type": "str", + "required": false, + "help": "Output directory (default: ~/Pictures/chatgpt)", + "file": { + "direction": "output", + "pathKind": "directory", + "multiple": false, + "defaultPath": "~/Pictures/chatgpt" + } + }, + { + "name": "sd", + "type": "boolean", + "default": false, + "required": false, + "help": "Skip download shorthand; only show ChatGPT link" + }, + { + "name": "timeout", + "type": "int", + "default": 240, + "required": false, + "help": "Max seconds for the overall command (default: 240)" + } + ], + "columns": [ + "status", + "file", + "link" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "chatgpt/image.js", + "sourceFile": "chatgpt/image.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "login", + "description": "Open chatgpt login", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "chatgpt/auth.js", + "sourceFile": "chatgpt/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "model", + "description": "Switch ChatGPT web model or intelligence level (GPT-5.6 Pro, fast, balanced, advanced, very-high, pro)", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "model", + "type": "str", + "required": true, + "positional": true, + "help": "ChatGPT model or intelligence level to switch to", + "choices": [ + "fast", + "speed", + "instant", + "balanced", + "balance", + "medium", + "advanced", + "high", + "thinking", + "very-high", + "ultra", + "xhigh", + "x-high", + "extra-high", + "very high", + "gpt-5.6-pro", + "gpt-5-6-pro", + "gpt-5.6-sol-pro", + "gpt-5-6-sol-pro", + "gpt-5.6", + "gpt-5-6", + "5.6-pro", + "5.6", + "pro", + "professional" + ] + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Open a ChatGPT project ID or /g/g-p- URL before switching intelligence level" + } + ], + "columns": [ + "Status", + "Model" + ], + "type": "js", + "modulePath": "chatgpt/model.js", + "sourceFile": "chatgpt/model.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "new", + "description": "Start a new ChatGPT web conversation", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + } + ], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "chatgpt/new.js", + "sourceFile": "chatgpt/new.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "project-file-add", + "description": "Upload files to a ChatGPT project as project knowledge (not just conversation attachments)", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "file", + "type": "str", + "required": true, + "positional": true, + "help": "Local file path(s) to upload; comma-separated paths are supported", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "application/pdf", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + }, + { + "name": "id", + "type": "str", + "required": true, + "help": "Project ID or /g/g-p- URL" + } + ], + "columns": [ + "Status", + "File" + ], + "type": "js", + "modulePath": "chatgpt/project-file-add.js", + "sourceFile": "chatgpt/project-file-add.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "project-list", + "description": "List visible ChatGPT projects from the sidebar", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max projects to show" + } + ], + "columns": [ + "Index", + "Id", + "Title", + "Url" + ], + "type": "js", + "modulePath": "chatgpt/project-list.js", + "sourceFile": "chatgpt/project-list.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "read", + "description": "Read messages in the current ChatGPT web conversation", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" + } + ], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "chatgpt/read.js", + "sourceFile": "chatgpt/read.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "send", + "description": "Send a prompt to ChatGPT web without waiting for the response", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Continue an existing ChatGPT conversation ID or /c/ URL" + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + } + ], + "columns": [ + "Status", + "InjectedText" + ], + "type": "js", + "modulePath": "chatgpt/send.js", + "sourceFile": "chatgpt/send.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "status", + "description": "Check ChatGPT web page availability and login state", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status", + "Login", + "Url" + ], + "type": "js", + "modulePath": "chatgpt/status.js", + "sourceFile": "chatgpt/status.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "whoami", + "description": "Show the current logged-in chatgpt account", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" + ], + "type": "js", + "modulePath": "chatgpt/auth.js", + "sourceFile": "chatgpt/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt-app", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "model", + "type": "str", + "required": false, + "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds to wait for response (default: 30)" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Path to local image to attach (optional)" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "chatgpt-app/ask.js", + "sourceFile": "chatgpt-app/ask.js" + }, + { + "site": "chatgpt-app", + "name": "model", + "description": "Switch ChatGPT Desktop model/mode (auto, instant, thinking, 5.2-instant, 5.2-thinking)", + "access": "read", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "model", + "type": "str", + "required": true, + "positional": true, + "help": "Model to switch to", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] + } + ], + "columns": [ + "Status", + "Model" + ], + "type": "js", + "modulePath": "chatgpt-app/model.js", + "sourceFile": "chatgpt-app/model.js" + }, + { + "site": "chatgpt-app", + "name": "new", + "description": "Open a new chat in ChatGPT Desktop App", + "access": "write", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "temp", + "type": "boolean", + "default": false, + "required": false, + "help": "Open a temporary chat with privacy protection" + } + ], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "chatgpt-app/new.js", + "sourceFile": "chatgpt-app/new.js" + }, + { + "site": "chatgpt-app", + "name": "read", + "description": "Read the last visible message from the focused ChatGPT Desktop window", + "access": "read", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "chatgpt-app/read.js", + "sourceFile": "chatgpt-app/read.js" + }, + { + "site": "chatgpt-app", + "name": "send", + "description": "Send a message to the active ChatGPT Desktop App window", + "access": "write", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Message to send" + }, + { + "name": "model", + "type": "str", + "required": false, + "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] + } + ], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "chatgpt-app/send.js", + "sourceFile": "chatgpt-app/send.js" + }, + { + "site": "chatgpt-app", + "name": "status", + "description": "Check if ChatGPT Desktop App is running natively on macOS", + "access": "read", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "chatgpt-app/status.js", + "sourceFile": "chatgpt-app/status.js" + }, + { + "site": "chatwise", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds to wait (default: 30)" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "chatwise/ask.js", + "sourceFile": "chatwise/ask.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "export", + "description": "Export the current ChatWise conversation to a Markdown file", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file (default: /tmp/chatwise-export.md)" + } + ], + "columns": [ + "Status", + "File", + "Messages" + ], + "type": "js", + "modulePath": "chatwise/export.js", + "sourceFile": "chatwise/export.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "history", + "description": "List conversation history in ChatWise sidebar", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Title" + ], + "type": "js", + "modulePath": "chatwise/history.js", + "sourceFile": "chatwise/history.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "model", + "description": "Get or switch the active AI model in ChatWise", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "model-name", + "type": "str", + "required": false, + "positional": true, + "help": "Model to switch to (e.g. gpt-4, claude-3)" + } + ], + "columns": [ + "Status", + "Model" + ], + "type": "js", + "modulePath": "chatwise/model.js", + "sourceFile": "chatwise/model.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "new", + "description": "Start a new ChatWise conversation session", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "chatwise/new.js", + "sourceFile": "chatwise/new.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "read", + "description": "Read the current ChatWise conversation history", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Content" + ], + "type": "js", + "modulePath": "chatwise/read.js", + "sourceFile": "chatwise/read.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "screenshot", + "description": "Capture a snapshot of the current ChatWise window (DOM + Accessibility tree)", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file path (default: /tmp/chatwise-snapshot.txt)" + } + ], + "columns": [ + "Status", + "File" + ], + "type": "js", + "modulePath": "chatwise/screenshot.js", + "sourceFile": "chatwise/screenshot.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "send", + "description": "Send a message to the active ChatWise conversation", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Message to send" + } + ], + "columns": [ + "Status", + "InjectedText" + ], + "type": "js", + "modulePath": "chatwise/send.js", + "sourceFile": "chatwise/send.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "status", + "description": "Check active CDP connection to ChatWise Desktop", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "chatwise/status.js", + "sourceFile": "chatwise/status.js", + "navigateBefore": true + }, + { + "site": "chess", + "name": "analyze", + "description": "Open a Chess.com game in the browser analysis board", + "access": "read", + "domain": "www.chess.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "game-url", + "type": "string", + "required": true, + "positional": true, + "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" + } + ], + "columns": [ + "kind", + "game_id", + "analysis_url" + ], + "type": "js", + "modulePath": "chess/analyze.js", + "sourceFile": "chess/analyze.js", + "navigateBefore": false + }, + { + "site": "chess", + "name": "game", + "description": "Chess.com single-game detail (white, black, result, ECO, time control) by full game URL", + "access": "read", + "domain": "www.chess.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "game-url", + "type": "string", + "required": true, + "positional": true, + "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" + } + ], + "columns": [ + "kind", + "game_id", + "date", + "white", + "white_rating", + "black", + "black_rating", + "result", + "winner_color", + "termination", + "eco", + "time_control", + "rated", + "ply_count", + "url" + ], + "type": "js", + "modulePath": "chess/game.js", + "sourceFile": "chess/game.js" + }, + { + "site": "chess", + "name": "games", + "description": "Chess.com recent games for a player, newest first", + "access": "read", + "domain": "api.chess.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Chess.com username" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of recent games (1-100)" + } + ], + "columns": [ + "date", + "time_class", + "rated", + "my_color", + "my_rating", + "my_result", + "opponent", + "opponent_rating", + "accuracy_white", + "accuracy_black", + "eco", + "opening_name", + "url" + ], + "type": "js", + "modulePath": "chess/games.js", + "sourceFile": "chess/games.js" + }, + { + "site": "chess", + "name": "stats", + "description": "Chess.com player ratings + win/loss record across game kinds", + "access": "read", + "domain": "api.chess.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Chess.com username (case-insensitive)" + } + ], + "columns": [ + "kind", + "rating_current", + "rating_best", + "wins", + "losses", + "draws" + ], + "type": "js", + "modulePath": "chess/stats.js", + "sourceFile": "chess/stats.js" + }, + { + "site": "claude", + "name": "ask", + "description": "Send a prompt to Claude and get the response", + "access": "write", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait for response" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" + }, + { + "name": "model", + "type": "str", + "default": "sonnet", + "required": false, + "help": "Model to use: sonnet, opus, or haiku", + "choices": [ + "sonnet", + "opus", + "haiku" + ] + }, + { + "name": "think", + "type": "boolean", + "default": false, + "required": false, + "help": "Enable Adaptive thinking" + }, + { + "name": "file", + "type": "str", + "required": false, + "help": "Attach a file (image, PDF, text) with the prompt", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "application/pdf", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + } + ], + "columns": [ + "response" + ], + "type": "js", + "modulePath": "claude/ask.js", + "sourceFile": "claude/ask.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "detail", + "description": "Open a Claude conversation by ID and read its messages", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Conversation ID (UUID from /chat/)" + } + ], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "claude/detail.js", + "sourceFile": "claude/detail.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "history", + "description": "List conversation history from Claude /recents", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max conversations to show" + } + ], + "columns": [ + "Index", + "Id", + "Title", + "Url" + ], + "type": "js", + "modulePath": "claude/history.js", + "sourceFile": "claude/history.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "login", + "description": "Open claude login", + "access": "write", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "org_name", + "org_uuid", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "claude/auth.js", + "sourceFile": "claude/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "new", + "description": "Start a new conversation in Claude", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "claude/new.js", + "sourceFile": "claude/new.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "read", + "description": "Read the current Claude conversation", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "claude/read.js", + "sourceFile": "claude/read.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "send", + "description": "Send a prompt to Claude without waiting for the response", + "access": "write", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" + } + ], + "columns": [ + "Status", + "SubmittedBy", + "InjectedText" + ], + "type": "js", + "modulePath": "claude/send.js", + "sourceFile": "claude/send.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "status", + "description": "Check Claude page availability and login state", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status", + "Login", + "Url" + ], + "type": "js", + "modulePath": "claude/status.js", + "sourceFile": "claude/status.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "whoami", + "description": "Show the current logged-in claude account", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "org_name", + "org_uuid" + ], + "type": "js", + "modulePath": "claude/auth.js", + "sourceFile": "claude/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "codex", + "name": "archive", + "description": "Archive (Codex's term for delete) the selected conversation via the Chat actions header menu. No confirmation in UI — pass --yes to actually archive.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "yes", + "type": "boolean", + "default": false, + "required": false, + "help": "Actually archive (default: dry-run preview)" + }, + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" + } + ], + "columns": [ + "status", + "thread_id", + "project", + "conversation" + ], + "type": "js", + "modulePath": "codex/archive.js", + "sourceFile": "codex/archive.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "ask", + "description": "Send a prompt to the current or selected Codex conversation and wait for the AI response", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 60, + "required": false, + "help": "Max seconds to wait for response (default: 60)" + }, + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" + } + ], + "columns": [ + "Role", + "Project", + "Conversation", + "Text" + ], + "type": "js", + "modulePath": "codex/ask.js", + "sourceFile": "codex/ask.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "dump", + "description": "Dump the DOM and Accessibility tree of codex for reverse-engineering", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "action", + "files" + ], + "type": "js", + "modulePath": "codex/dump.js", + "sourceFile": "codex/dump.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "export", + "description": "Export the current Codex conversation to a Markdown file", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file (default: /tmp/codex-export.md)" + } + ], + "columns": [ + "Status", + "File", + "Messages" + ], + "type": "js", + "modulePath": "codex/export.js", + "sourceFile": "codex/export.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "extract-diff", + "description": "Extract visual code review diff patches from Codex", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "File", + "Diff" + ], + "type": "js", + "modulePath": "codex/extract-diff.js", + "sourceFile": "codex/extract-diff.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "history", + "description": "List visible Codex conversation threads grouped by project", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "project", + "type": "str", + "required": false, + "help": "Filter by project label or path" + }, + { + "name": "limit", + "type": "str", + "required": false, + "help": "Max conversations per project" + } + ], + "columns": [ + "Project", + "Index", + "Title", + "Updated", + "Active" + ], + "type": "js", + "modulePath": "codex/history.js", + "sourceFile": "codex/history.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "model", + "description": "Read, list, or switch the active model / reasoning level in Codex Desktop. The composer toolbar button toggles a menu that mixes model variants (GPT-5.5, Speed) with reasoning levels (Low/Medium/High/Extra High).", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": false, + "positional": true, + "help": "Substring (case-insensitive) of a model / reasoning level to switch to. Omit to read current." + }, + { + "name": "list", + "type": "boolean", + "default": false, + "required": false, + "help": "List all menu options (does not switch)" + } + ], + "columns": [ + "Status", + "Model" + ], + "type": "js", + "modulePath": "codex/model.js", + "sourceFile": "codex/model.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "new", + "description": "Start a new Codex conversation session", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "codex/new.js", + "sourceFile": "codex/new.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "pin", + "description": "Pin the selected Codex conversation via the Chat actions header menu.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" + } + ], + "columns": [ + "status", + "thread_id", + "project", + "conversation" + ], + "type": "js", + "modulePath": "codex/pin.js", + "sourceFile": "codex/pin.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "projects", + "description": "List Codex projects and visible conversations from the sidebar", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "project", + "type": "str", + "required": false, + "help": "Filter by project label or path" + }, + { + "name": "limit", + "type": "str", + "required": false, + "help": "Max conversations per project" + } + ], + "columns": [ + "Project", + "Index", + "Title", + "Updated", + "Active" + ], + "type": "js", + "modulePath": "codex/projects.js", + "sourceFile": "codex/projects.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "read", + "description": "Read the contents of the current or selected Codex conversation thread", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" + } + ], + "columns": [ + "Project", + "Conversation", + "Content" + ], + "type": "js", + "modulePath": "codex/read.js", + "sourceFile": "codex/read.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "rename", + "description": "Rename the selected Codex conversation. Opens the Chat actions menu → \"Rename chat\", then types the new title.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "title", + "type": "str", + "required": true, + "positional": true, + "help": "New title (single line, no newlines)" + }, + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" + } + ], + "columns": [ + "status", + "title", + "thread_id", + "project" + ], + "type": "js", + "modulePath": "codex/rename.js", + "sourceFile": "codex/rename.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "screenshot", + "description": "Capture a snapshot of the current Codex window (DOM + Accessibility tree)", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file path (default: /tmp/codex-snapshot.txt)" + } + ], + "columns": [ + "Status", + "File" + ], + "type": "js", + "modulePath": "codex/screenshot.js", + "sourceFile": "codex/screenshot.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "send", + "description": "Send text/commands to the current or selected Codex AI composer", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Text, command (e.g. /review), or skill (e.g. $imagegen)" + }, + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" + } + ], + "columns": [ + "Status", + "Project", + "Conversation", + "InjectedText" + ], + "type": "js", + "modulePath": "codex/send.js", + "sourceFile": "codex/send.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "status", + "description": "Check active CDP connection to OpenAI Codex App", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "codex/status.js", + "sourceFile": "codex/status.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "unpin", + "description": "Unpin the selected Codex conversation via the Chat actions header menu.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" + } + ], + "columns": [ + "status", + "thread_id", + "project", + "conversation" + ], + "type": "js", + "modulePath": "codex/pin.js", + "sourceFile": "codex/pin.js", + "navigateBefore": true + }, + { + "site": "coingecko", + "name": "categories", + "description": "Crypto categories ranked by aggregated market cap", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "sort", + "type": "str", + "default": "market_cap_desc", + "required": false, + "help": "Sort order (market_cap_desc / market_cap_asc / name_desc / name_asc / market_cap_change_24h_desc / market_cap_change_24h_asc)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of categories (1-100; CoinGecko returns ~120 max)" + } + ], + "columns": [ + "rank", + "id", + "name", + "marketCap", + "volume24h", + "marketCapChange24hPct", + "top3Coins" + ], + "type": "js", + "modulePath": "coingecko/categories.js", + "sourceFile": "coingecko/categories.js" + }, + { + "site": "coingecko", + "name": "coin", + "description": "Fetch a single cryptocurrency's market data by CoinGecko id (e.g. bitcoin, ethereum).", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "CoinGecko coin id (lowercase, e.g. bitcoin / ethereum / solana)." + }, + { + "name": "currency", + "type": "string", + "default": "usd", + "required": false, + "help": "Quote currency (usd, cny, eur, jpy, ...)." + } + ], + "columns": [ + "id", + "symbol", + "name", + "rank", + "price", + "marketCap", + "volume24h", + "change24hPct", + "change7dPct", + "change30dPct", + "ath", + "athDate", + "atl", + "atlDate", + "circulatingSupply", + "totalSupply", + "maxSupply", + "genesisDate", + "homepage" + ], + "type": "js", + "modulePath": "coingecko/coin.js", + "sourceFile": "coingecko/coin.js" + }, + { + "site": "coingecko", + "name": "derivatives", + "description": "Top crypto derivative (perpetual / futures) markets by 24h volume", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max rows to return (1-500; CoinGecko returns one large page)." + }, + { + "name": "symbol", + "type": "string", + "required": false, + "help": "Optional symbol substring filter (e.g. \"BTC\", \"ETHUSDT\")." + } + ], + "columns": [ + "rank", + "market", + "symbol", + "indexId", + "contractType", + "price", + "change24hPct", + "fundingRate", + "openInterestUsd", + "volume24hUsd", + "expired" + ], + "type": "js", + "modulePath": "coingecko/derivatives.js", + "sourceFile": "coingecko/derivatives.js" + }, + { + "site": "coingecko", + "name": "exchanges", + "description": "Top crypto exchanges by 24h BTC trading volume", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of exchanges (1-250, CoinGecko per_page upper bound)" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number (1-based)" + } + ], + "columns": [ + "rank", + "id", + "name", + "trustScore", + "volume24hBtc", + "country", + "yearEstablished", + "url" + ], + "type": "js", + "modulePath": "coingecko/exchanges.js", + "sourceFile": "coingecko/exchanges.js" + }, + { + "site": "coingecko", + "name": "global", + "description": "Aggregate crypto market stats: total market cap, volume, dominance", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "currency", + "type": "string", + "default": "usd", + "required": false, + "help": "Quote currency for total market cap / volume (usd, cny, eur, jpy, ...)" + } + ], + "columns": [ + "currency", + "totalMarketCap", + "totalVolume24h", + "marketCapChange24hPct", + "btcDominancePct", + "ethDominancePct", + "activeCryptocurrencies", + "markets", + "ongoingIcos", + "updatedAt" + ], + "type": "js", + "modulePath": "coingecko/global.js", + "sourceFile": "coingecko/global.js" + }, + { + "site": "coingecko", + "name": "top", + "description": "Cryptocurrency quotes by market cap (default USD)", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "currency", + "type": "string", + "default": "usd", + "required": false, + "help": "quote currency (usd / cny / eur / jpy ...)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number to return (default 10, maximum 250)" + } + ], + "columns": [ + "rank", + "symbol", + "name", + "price", + "change24hPct", + "marketCap", + "volume24h", + "high24h", + "low24h" + ], + "type": "js", + "modulePath": "coingecko/top.js", + "sourceFile": "coingecko/top.js" + }, + { + "site": "coingecko", + "name": "trending", + "description": "Top trending cryptocurrencies on CoinGecko in the last 24h (search-volume based).", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [], + "columns": [ + "rank", + "id", + "symbol", + "name", + "marketCapRank", + "priceBtc", + "thumb" + ], + "type": "js", + "modulePath": "coingecko/trending.js", + "sourceFile": "coingecko/trending.js" + }, + { + "site": "confluence", + "name": "create", + "description": "Create a Confluence page from Markdown or storage XHTML", + "access": "write", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "space", + "type": "string", + "required": true, + "help": "Cloud space id, or Data Center space key" + }, + { + "name": "title", + "type": "string", + "required": true, + "help": "Page title" + }, + { + "name": "file", + "type": "string", + "required": true, + "help": "Markdown file path" + }, + { + "name": "parent", + "type": "string", + "required": false, + "help": "Optional parent page id" + }, + { + "name": "representation", + "type": "string", + "default": "markdown", + "required": false, + "help": "Input file format", + "choices": [ + "markdown", + "storage" + ] + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually create the remote page" + } + ], + "columns": [ + "status", + "id", + "title", + "spaceId", + "spaceKey", + "version", + "url" + ], + "type": "js", + "modulePath": "confluence/create.js", + "sourceFile": "confluence/create.js" + }, + { + "site": "confluence", + "name": "page", + "description": "Confluence page by id with storage and Markdown body", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Confluence page id" + } + ], + "columns": [ + "id", + "title", + "status", + "spaceId", + "spaceKey", + "version", + "url" + ], + "type": "js", + "modulePath": "confluence/page.js", + "sourceFile": "confluence/page.js" + }, + { + "site": "confluence", + "name": "search", + "description": "Search Confluence content with CQL", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "cql", + "type": "str", + "required": true, + "positional": true, + "help": "CQL query, e.g. \"type = page and title ~ \\\"RCA\\\"\"" + }, + { + "name": "space", + "type": "string", + "required": false, + "help": "Limit search to a Confluence space key" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results to return (1-100)" + } + ], + "columns": [ + "id", + "title", + "type", + "spaceKey", + "status", + "lastModified", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "confluence/search.js", + "sourceFile": "confluence/search.js" + }, + { + "site": "confluence", + "name": "update", + "description": "Update a Confluence page body from Markdown or storage XHTML", + "access": "write", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Confluence page id" + }, + { + "name": "file", + "type": "string", + "required": true, + "help": "Markdown file path" + }, + { + "name": "title", + "type": "string", + "required": false, + "help": "Optional replacement title; defaults to current title" + }, + { + "name": "version-message", + "type": "string", + "required": false, + "help": "Confluence version message" + }, + { + "name": "representation", + "type": "string", + "default": "markdown", + "required": false, + "help": "Input file format", + "choices": [ + "markdown", + "storage" + ] + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually update the remote page" + } + ], + "columns": [ + "status", + "id", + "title", + "spaceId", + "spaceKey", + "version", + "url" + ], + "type": "js", + "modulePath": "confluence/update.js", + "sourceFile": "confluence/update.js" + }, + { + "site": "coupang", + "name": "add-to-cart", + "description": "Add a Coupang product to cart using logged-in browser session", + "access": "write", + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product-id", + "type": "str", + "required": false, + "positional": true, + "help": "Coupang product ID" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Canonical product URL" + } + ], + "columns": [ + "ok", + "product_id", + "url", + "message" + ], + "type": "js", + "modulePath": "coupang/add-to-cart.js", + "sourceFile": "coupang/add-to-cart.js", + "navigateBefore": "https://www.coupang.com" + }, + { + "site": "coupang", + "name": "login", + "description": "Open coupang login", + "access": "write", + "domain": "coupang.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "coupang/auth.js", + "sourceFile": "coupang/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "coupang", + "name": "product", + "description": "Read full product detail (price, rating, seller, delivery) for a Coupang product", + "access": "read", + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product-id", + "type": "str", + "required": false, + "positional": true, + "help": "Coupang product ID (digits only)" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Canonical Coupang product URL (alternative to --product-id)" + } + ], + "columns": [ + "product_id", + "title", + "price", + "original_price", + "discount_rate", + "rating", + "review_count", + "seller", + "brand", + "rocket", + "delivery_promise", + "image_url", + "url" + ], + "type": "js", + "modulePath": "coupang/product.js", + "sourceFile": "coupang/product.js", + "navigateBefore": "https://www.coupang.com" + }, + { + "site": "coupang", + "name": "search", + "description": "Search Coupang products with logged-in browser session", + "access": "read", + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Search result page number" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (max 50)" + }, + { + "name": "filter", + "type": "str", + "required": false, + "help": "Optional search filter (currently supports: rocket)" + } + ], + "columns": [ + "rank", + "product_id", + "title", + "price", + "unit_price", + "rating", + "review_count", + "rocket", + "delivery_type", + "delivery_promise", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "coupang/search.js", + "sourceFile": "coupang/search.js", + "navigateBefore": "https://www.coupang.com" + }, + { + "site": "coupang", + "name": "whoami", + "description": "Show the current logged-in coupang account", + "access": "read", + "domain": "coupang.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name" + ], + "type": "js", + "modulePath": "coupang/auth.js", + "sourceFile": "coupang/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "crates", + "name": "crate", + "description": "Single crates.io crate metadata (latest version, downloads, license, repo)", + "access": "read", + "domain": "crates.io", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "crates.io crate name (e.g. \"serde\", \"tokio\")" + } + ], + "columns": [ + "name", + "latestVersion", + "description", + "downloads", + "recentDownloads", + "versions", + "license", + "homepage", + "documentation", + "repository", + "keywords", + "categories", + "created", + "updated", + "url" + ], + "type": "js", + "modulePath": "crates/crate.js", + "sourceFile": "crates/crate.js" + }, + { + "site": "crates", + "name": "search", + "description": "Search the public crates.io registry by keyword", + "access": "read", + "domain": "crates.io", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"serde\", \"async runtime\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + } + ], + "columns": [ + "rank", + "name", + "latestVersion", + "description", + "downloads", + "recentDownloads", + "repository", + "updated", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "crates/search.js", + "sourceFile": "crates/search.js" + }, + { + "site": "cursor", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds to wait for response (default: 30)" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "cursor/ask.js", + "sourceFile": "cursor/ask.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "composer", + "description": "Send a prompt directly into Cursor Composer (Cmd+I shortcut)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Text to send into Composer" + } + ], + "columns": [ + "Status", + "InjectedText" + ], + "type": "js", + "modulePath": "cursor/composer.js", + "sourceFile": "cursor/composer.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "dump", + "description": "Dump the DOM and Accessibility tree of cursor for reverse-engineering", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "action", + "files" + ], + "type": "js", + "modulePath": "cursor/dump.js", + "sourceFile": "cursor/dump.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "export", + "description": "Export the current cursor conversation to a Markdown file", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file (default: /tmp/cursor-export.md)" + } + ], + "columns": [ + "Status", + "File", + "Messages" + ], + "type": "js", + "modulePath": "cursor/export.js", + "sourceFile": "cursor/export.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "extract-code", + "description": "Extract multi-line code blocks from the current Cursor conversation", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Code" + ], + "type": "js", + "modulePath": "cursor/extract-code.js", + "sourceFile": "cursor/extract-code.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "history", + "description": "List recent chat sessions from the Cursor sidebar", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Title" + ], + "type": "js", + "modulePath": "cursor/history.js", + "sourceFile": "cursor/history.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "model", + "description": "Get or switch the currently active AI model in Cursor", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "model-name", + "type": "str", + "required": false, + "positional": true, + "help": "The ID of the model to switch to (e.g. claude-3.5-sonnet)" + } + ], + "columns": [ + "Status", + "Model" + ], + "type": "js", + "modulePath": "cursor/model.js", + "sourceFile": "cursor/model.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "new", + "description": "Start a new Cursor chat or Composer session", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "cursor/new.js", + "sourceFile": "cursor/new.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "read", + "description": "Read the current Cursor chat/composer conversation history", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "cursor/read.js", + "sourceFile": "cursor/read.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "screenshot", + "description": "Capture a snapshot of the current cursor window (DOM + Accessibility tree)", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file path (default: /tmp/cursor-snapshot.txt)" + } + ], + "columns": [ + "Status", + "File" + ], + "type": "js", + "modulePath": "cursor/screenshot.js", + "sourceFile": "cursor/screenshot.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "send", + "description": "Send a prompt directly into Cursor Composer/Chat", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Text to send into Cursor" + } + ], + "columns": [ + "Status", + "InjectedText" + ], + "type": "js", + "modulePath": "cursor/send.js", + "sourceFile": "cursor/send.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "status", + "description": "Check active CDP connection to Cursor AI Editor", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "cursor/status.js", + "sourceFile": "cursor/status.js", + "navigateBefore": true + }, + { + "site": "dblp", + "name": "author", + "description": "List dblp publications by a given author (newest first; resolves to top PID match)", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "author", + "type": "str", + "required": false, + "positional": true, + "help": "Author name (e.g. \"Yoshua Bengio\"). Optional when --pid is given." + }, + { + "name": "pid", + "type": "str", + "required": false, + "help": "Canonical dblp PID (e.g. \"56/953\"). Bypasses author search." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max publications (1-200)" + } + ], + "columns": [ + "rank", + "key", + "title", + "authors", + "venue", + "year", + "type", + "doi", + "pid", + "url" + ], + "type": "js", + "modulePath": "dblp/author.js", + "sourceFile": "dblp/author.js" + }, + { + "site": "dblp", + "name": "paper", + "aliases": [ + "detail", + "view" + ], + "description": "Fetch a dblp record by canonical key (e.g. conf/nips/VaswaniSPUJGKP17)", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "dblp record key (round-tripped from the `key` column of `dblp search`)" + } + ], + "columns": [ + "key", + "type", + "title", + "authors", + "venue", + "year", + "pages", + "doi", + "open_access_url", + "dblp_url" + ], + "type": "js", + "modulePath": "dblp/paper.js", + "sourceFile": "dblp/paper.js" + }, + { + "site": "dblp", + "name": "search", + "description": "Search dblp computer-science bibliography by free-text query", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (title / author / venue, e.g. \"attention is all you need\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100, single dblp page)" + } + ], + "columns": [ + "rank", + "key", + "title", + "authors", + "venue", + "year", + "type", + "doi", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "dblp/search.js", + "sourceFile": "dblp/search.js" + }, + { + "site": "dblp", + "name": "venue", + "description": "Search dblp venue registry (conferences / journals) by name or acronym", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Venue name or acronym (e.g. \"ICLR\", \"neural networks\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max venues (1-100, single dblp page)" + } + ], + "columns": [ + "rank", + "acronym", + "venue", + "type", + "url" + ], + "type": "js", + "modulePath": "dblp/venue.js", + "sourceFile": "dblp/venue.js" + }, + { + "site": "defillama", + "name": "protocol", + "description": "Single DefiLlama protocol details (current TVL, mcap, chains, twitter, github, description)", + "access": "read", + "domain": "defillama.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "slug", + "type": "string", + "required": true, + "positional": true, + "help": "DefiLlama protocol slug (e.g. \"aave\", \"lido\")" + } + ], + "columns": [ + "slug", + "name", + "category", + "isParent", + "tvl", + "tvlAt", + "mcap", + "chains", + "twitter", + "github", + "audits", + "listedAt", + "description", + "website", + "url" + ], + "type": "js", + "modulePath": "defillama/protocol.js", + "sourceFile": "defillama/protocol.js" + }, + { + "site": "defillama", + "name": "protocols", + "description": "Top DeFi protocols on DefiLlama by current TVL (slug, name, category, TVL, mcap, change_1d/7d, chains)", + "access": "read", + "domain": "defillama.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Number of rows to return (1-500)" + } + ], + "columns": [ + "rank", + "slug", + "name", + "category", + "tvl", + "mcap", + "change_1d", + "change_7d", + "chains", + "listedAt", + "url" + ], + "type": "js", + "modulePath": "defillama/protocols.js", + "sourceFile": "defillama/protocols.js" + }, + { + "site": "devto", + "name": "latest", + "description": "Newest dev.to articles (firehose, all tags)", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Articles per page (1-100)" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number (1-based)" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "tags", + "reactions", + "comments", + "published", + "url" + ], + "type": "js", + "modulePath": "devto/latest.js", + "sourceFile": "devto/latest.js" + }, + { + "site": "devto", + "name": "read", + "description": "Read a DEV.to article body by id", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "DEV.to article id (numeric, e.g. 3605688)" + }, + { + "name": "max-length", + "type": "int", + "default": 20000, + "required": false, + "help": "Max characters of body to return (min 100)" + } + ], + "columns": [ + "id", + "title", + "author", + "reactions", + "reading_time", + "tags", + "published_at", + "body", + "url" + ], + "type": "js", + "modulePath": "devto/read.js", + "sourceFile": "devto/read.js" + }, + { + "site": "devto", + "name": "tag", + "description": "Latest DEV.to articles for a specific tag", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "tag", + "type": "str", + "required": true, + "positional": true, + "help": "Tag name (e.g. javascript, python, webdev)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of articles" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "devto/tag.js", + "sourceFile": "devto/tag.js" + }, + { + "site": "devto", + "name": "top", + "description": "Top DEV.to articles of the day", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of articles" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "devto/top.js", + "sourceFile": "devto/top.js" + }, + { + "site": "devto", + "name": "user", + "description": "Recent DEV.to articles from a specific user", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "DEV.to username (e.g. ben, thepracticaldev)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of articles" + } + ], + "columns": [ + "rank", + "id", + "title", + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "devto/user.js", + "sourceFile": "devto/user.js" + }, + { + "site": "dictionary", + "name": "examples", + "description": "Read real-world example sentences utilizing the word", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to get example sentences for" + } + ], + "columns": [ + "word", + "example" + ], + "type": "js", + "modulePath": "dictionary/examples.js", + "sourceFile": "dictionary/examples.js" + }, + { + "site": "dictionary", + "name": "search", + "description": "Search the Free Dictionary API for definitions, parts of speech, and pronunciations.", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to define (e.g., serendipity)" + } + ], + "columns": [ + "word", + "phonetic", + "type", + "definition" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "dictionary/search.js", + "sourceFile": "dictionary/search.js" + }, + { + "site": "dictionary", + "name": "synonyms", + "description": "Find synonyms for a specific word", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to find synonyms for (e.g., serendipity)" + } + ], + "columns": [ + "word", + "synonyms" + ], + "type": "js", + "modulePath": "dictionary/synonyms.js", + "sourceFile": "dictionary/synonyms.js" + }, + { + "site": "discord-app", + "name": "channels", + "description": "List channels in the current Discord server", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Channel", + "Type", + "guild_id", + "channel_id", + "url" + ], + "type": "js", + "modulePath": "discord-app/channels.js", + "sourceFile": "discord-app/channels.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "delete", + "description": "Delete a message by its ID in the active Discord channel", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "message_id", + "type": "string", + "required": true, + "positional": true, + "help": "The ID of the message to delete (visible via Developer Mode or the read command)" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "discord-app/delete.js", + "sourceFile": "discord-app/delete.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "goto", + "description": "Open a Discord channel by id/name/url without sending messages", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "guild", + "type": "str", + "required": false, + "help": "Guild/server id or visible name" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Channel id or visible name" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Discord channel URL" + }, + { + "name": "timeout", + "type": "str", + "default": "8", + "required": false, + "help": "Seconds to wait for Discord to show the route (default: 8)" + } + ], + "columns": [ + "Status", + "guild_id", + "channel_id", + "url" + ], + "type": "js", + "modulePath": "discord-app/goto.js", + "sourceFile": "discord-app/goto.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "members", + "description": "List online members in the current Discord channel", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Name", + "Status" + ], + "type": "js", + "modulePath": "discord-app/members.js", + "sourceFile": "discord-app/members.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "read", + "description": "Read recent messages from the active or targeted Discord channel", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "count", + "type": "str", + "default": "20", + "required": false, + "help": "Number of messages to read (default: 20)" + }, + { + "name": "guild", + "type": "str", + "required": false, + "help": "Guild/server id or visible name for targeted reads" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Channel id or visible name for targeted reads" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Discord channel URL to open before reading" + } + ], + "columns": [ + "Author", + "Time", + "Message", + "channel_id", + "message_id" + ], + "type": "js", + "modulePath": "discord-app/read.js", + "sourceFile": "discord-app/read.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "search", + "description": "Search messages in the current Discord server/channel (Cmd+F)", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + } + ], + "columns": [ + "Index", + "Author", + "Message" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "discord-app/search.js", + "sourceFile": "discord-app/search.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "send", + "description": "Send a message in the active Discord channel", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Message to send" + } + ], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "discord-app/send.js", + "sourceFile": "discord-app/send.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "servers", + "description": "List all Discord servers (guilds) in the sidebar", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Server", + "guild_id", + "url" + ], + "type": "js", + "modulePath": "discord-app/servers.js", + "sourceFile": "discord-app/servers.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "status", + "description": "Check active CDP connection to Discord Desktop", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "discord-app/status.js", + "sourceFile": "discord-app/status.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "thread-read", + "description": "Read recent messages from a Discord thread/post by id or URL", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "thread", + "type": "str", + "required": false, + "help": "Thread/post id, or a full Discord thread/post URL" + }, + { + "name": "count", + "type": "str", + "default": "20", + "required": false, + "help": "Number of messages to read (default: 20)" + }, + { + "name": "guild", + "type": "str", + "required": false, + "help": "Parent guild/server id or visible name" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Parent forum/channel id or visible name" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Discord thread/post URL" + } + ], + "columns": [ + "Author", + "Time", + "Message", + "channel_id", + "message_id" + ], + "type": "js", + "modulePath": "discord-app/thread-read.js", + "sourceFile": "discord-app/thread-read.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "threads", + "description": "List visible Discord forum/thread posts in the active or targeted channel", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "limit", + "type": "str", + "default": "30", + "required": false, + "help": "Maximum thread/post cards to return (default: 30)" + }, + { + "name": "guild", + "type": "str", + "required": false, + "help": "Guild/server id or visible name for targeted thread listing" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Forum/channel id or visible name for targeted thread listing" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Discord forum/channel URL to open before listing threads" + } + ], + "columns": [ + "Index", + "Thread", + "Author", + "Updated", + "Preview", + "guild_id", + "channel_id", + "thread_id", + "url" + ], + "type": "js", + "modulePath": "discord-app/threads.js", + "sourceFile": "discord-app/threads.js", + "navigateBefore": true + }, + { + "site": "district", + "name": "checkout", + "description": "Select District movie seats and open the UPI QR payment scanner", + "access": "write", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "show", + "type": "str", + "required": true, + "positional": true, + "help": "District seat-layout URL or showId from district showtimes" + }, + { + "name": "seats", + "type": "str", + "required": true, + "help": "Comma-separated seat labels to select, e.g. I22,I21" + }, + { + "name": "format-id", + "type": "str", + "required": false, + "help": "District formatId from showtimes; required when show is a showId" + }, + { + "name": "content-id", + "type": "str", + "required": false, + "help": "District content id; required when show is a showId" + }, + { + "name": "timeout", + "type": "int", + "default": 45, + "required": false, + "help": "Maximum seconds to wait for selection, review page, and payment handoff" + }, + { + "name": "payment", + "type": "str", + "default": "upi-qr", + "required": false, + "help": "Payment handoff target: upi-qr opens the scanner; review stops on order review" + } + ], + "columns": [ + "status", + "movie", + "cinema", + "date", + "time", + "seats", + "ticketCount", + "orderAmount", + "bookingCharge", + "total", + "paymentMethod", + "paymentState", + "upiQrVisible", + "paymentAmount", + "paymentUrl", + "showId" + ], + "type": "js", + "modulePath": "district/checkout.js", + "sourceFile": "district/checkout.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true + }, + { + "site": "district", + "name": "listings", + "aliases": [ + "ls" + ], + "description": "List public District by Zomato movies, events, and nearby going-out cards", + "access": "read", + "domain": "www.district.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "input", + "type": "str", + "default": "home", + "required": false, + "positional": true, + "help": "home, movies, events, a district.in URL, or a District path" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum rows to return (1-100)" + } + ], + "columns": [ + "rank", + "title", + "category", + "date", + "venue", + "price", + "url" + ], + "type": "js", + "modulePath": "district/listings.js", + "sourceFile": "district/listings.js" + }, + { + "site": "district", + "name": "locations", + "aliases": [ + "location-search" + ], + "description": "Search District-supported cities, areas, malls, and places for booking filters", + "access": "read", + "domain": "www.district.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "City, area, mall, or locality, for example \"bangalore\" or \"indiranagar\"" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum location rows to return (1-50)" + } + ], + "columns": [ + "rank", + "name", + "kind", + "city", + "state", + "cityKey", + "cityId", + "placeId", + "lat", + "lng", + "distanceKm", + "source" + ], + "type": "js", + "modulePath": "district/locations.js", + "sourceFile": "district/locations.js" + }, + { + "site": "district", + "name": "login", + "description": "Open district login", + "access": "write", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "phone_number", + "email", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "district/auth.js", + "sourceFile": "district/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "district", + "name": "search", + "aliases": [ + "s" + ], + "description": "Search District by Zomato across movies, events, dining, stores, activities, and play", + "access": "read", + "domain": "www.district.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query, for example \"hamlet\" or \"arijit\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum rows to return (1-100)" + }, + { + "name": "tab", + "type": "str", + "default": "all", + "required": false, + "help": "Search tab: all, dining, events, movies, stores, activities, or play" + } + ], + "columns": [ + "rank", + "title", + "category", + "date", + "venue", + "price", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "district/search.js", + "sourceFile": "district/search.js" + }, + { + "site": "district", + "name": "seats", + "description": "List available seats for a District movie showtime", + "access": "read", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "show", + "type": "str", + "required": true, + "positional": true, + "help": "District seat-layout URL or showId from district showtimes" + }, + { + "name": "format-id", + "type": "str", + "required": false, + "help": "District formatId from showtimes; required when show is a showId" + }, + { + "name": "content-id", + "type": "str", + "required": false, + "help": "District content id; required when show is a showId" + }, + { + "name": "class", + "type": "str", + "required": false, + "help": "Optional seat class filter, e.g. premium, premium xl, or recliner" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Number of seats to choose (1-10); without count, seats are listed normally" + }, + { + "name": "together", + "type": "str", + "required": false, + "help": "Require selected seats to be adjacent when count is provided" + }, + { + "name": "max-price", + "type": "float", + "required": false, + "help": "Maximum price per seat" + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Maximum seats to return (1-300)" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Maximum seconds to wait for the seat map to render" + } + ], + "columns": [ + "rank", + "seat", + "row", + "number", + "column", + "seatClass", + "price", + "status", + "flags", + "showId", + "formatId", + "url" + ], + "type": "js", + "modulePath": "district/seats.js", + "sourceFile": "district/seats.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "district", + "name": "set-location", + "aliases": [ + "setlocation" + ], + "description": "Set the District browser session location for movie booking filters", + "access": "write", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "location", + "type": "str", + "required": true, + "positional": true, + "help": "City, area, mall, or locality, for example \"Bangalore\" or \"Indiranagar\"" + }, + { + "name": "rank", + "type": "int", + "default": 1, + "required": false, + "help": "Pick the Nth District location result (1-20), default: 1" + }, + { + "name": "timeout", + "type": "int", + "default": 45, + "required": false, + "help": "Maximum seconds to wait for the picker and location change" + } + ], + "columns": [ + "status", + "name", + "city", + "state", + "cityKey", + "cityId", + "placeId", + "subzoneId", + "lat", + "lng", + "availableTabs", + "source" + ], + "type": "js", + "modulePath": "district/set-location.js", + "sourceFile": "district/set-location.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "district", + "name": "showtimes", + "aliases": [ + "shows" + ], + "description": "List District movie showtimes with location, time, cinema, language, price, and format filters", + "access": "read", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "movie", + "type": "str", + "required": true, + "positional": true, + "help": "Movie name or District movie URL" + }, + { + "name": "date", + "type": "str", + "required": false, + "help": "Show date in YYYY-MM-DD format; defaults to District selected date" + }, + { + "name": "city", + "type": "str", + "required": false, + "help": "District city name/key, for example Bangalore or Bengaluru" + }, + { + "name": "near", + "type": "str", + "required": false, + "help": "Area, mall, or locality to search near, for example Indiranagar" + }, + { + "name": "city-key", + "type": "str", + "required": false, + "help": "Legacy District city key override, for example bengaluru" + }, + { + "name": "after", + "type": "str", + "required": false, + "help": "Only shows at or after HH:MM, 24-hour time" + }, + { + "name": "before", + "type": "str", + "required": false, + "help": "Only shows at or before HH:MM, 24-hour time" + }, + { + "name": "cinema", + "type": "str", + "required": false, + "help": "Filter cinema/theatre name, for example PVR, INOX, Orion" + }, + { + "name": "language", + "type": "str", + "required": false, + "help": "Filter movie language, for example English, Hindi, Kannada" + }, + { + "name": "max-price", + "type": "float", + "required": false, + "help": "Only shows with at least one ticket class at or below this price" + }, + { + "name": "quality", + "type": "str", + "required": false, + "help": "Generic format/quality filter, for example 2D, 3D, IMAX, IMAX 3D, 4DX" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Maximum showtime rows to return (1-200)" + } + ], + "columns": [ + "rank", + "movie", + "language", + "date", + "time", + "cinema", + "format", + "priceRange", + "available", + "showId", + "formatId", + "url" + ], + "type": "js", + "modulePath": "district/showtimes.js", + "sourceFile": "district/showtimes.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "district", + "name": "whoami", + "description": "Show the current logged-in district account", + "access": "read", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name", + "phone_number", + "email" + ], + "type": "js", + "modulePath": "district/auth.js", + "sourceFile": "district/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "dockerhub", + "name": "image", + "description": "Fetch a Docker Hub repository's public metadata (stars, pulls, last updated, status)", + "access": "read", + "domain": "hub.docker.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image name (e.g. \"nginx\", \"library/nginx\", \"bitnami/redis\")" + } + ], + "columns": [ + "image", + "official", + "stars", + "pulls", + "description", + "lastUpdated", + "lastModified", + "registered", + "status", + "url" + ], + "type": "js", + "modulePath": "dockerhub/image.js", + "sourceFile": "dockerhub/image.js" + }, + { + "site": "dockerhub", + "name": "search", + "description": "Search Docker Hub repositories by keyword", + "access": "read", + "domain": "hub.docker.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"nginx\", \"bitnami redis\")" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max repositories (1-100, single Docker Hub page)" + } + ], + "columns": [ + "rank", + "image", + "official", + "stars", + "pulls", + "description", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "dockerhub/search.js", + "sourceFile": "dockerhub/search.js" + }, + { + "site": "duckduckgo", + "name": "search", + "description": "Search DuckDuckGo", + "access": "read", + "domain": "html.duckduckgo.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results per page (1-10). For multi-page, use --offset" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Result offset for pagination (0, 10, 20...). Uses XHR POST internally" + }, + { + "name": "region", + "type": "str", + "required": false, + "help": "Region code (e.g. jp-jp, us-en, cn-zh). Default: all regions" + }, + { + "name": "time", + "type": "str", + "required": false, + "help": "Time range: d (day), w (week), m (month), y (year)" + } + ], + "columns": [ + "rank", + "title", + "url", + "snippet", + "displayUrl", + "icon", + "resultType" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "duckduckgo/search.js", + "sourceFile": "duckduckgo/search.js" + }, + { + "site": "duckduckgo", + "name": "suggest", + "description": "DuckDuckGo search suggestions", + "access": "read", + "domain": "duckduckgo.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query prefix" + }, + { + "name": "limit", + "type": "int", + "default": 8, + "required": false, + "help": "Max number of suggestions" + } + ], + "columns": [ + "phrase" + ], + "type": "js", + "modulePath": "duckduckgo/suggest.js", + "sourceFile": "duckduckgo/suggest.js" + }, + { + "site": "endoflife", + "name": "product", + "description": "Release cycles + EOL / LTS / support dates for one product on endoflife.date", + "access": "read", + "domain": "endoflife.date", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "product", + "type": "string", + "required": true, + "positional": true, + "help": "endoflife.date product slug (e.g. \"nodejs\", \"python\", \"ubuntu\")" + } + ], + "columns": [ + "product", + "cycle", + "releaseDate", + "latest", + "latestReleaseDate", + "lts", + "support", + "eol", + "extendedSupport", + "eolStatus", + "url" + ], + "type": "js", + "modulePath": "endoflife/product.js", + "sourceFile": "endoflife/product.js" + }, + { + "site": "facebook", + "name": "add-friend", + "description": "Send a friend request on Facebook", + "access": "write", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Facebook username or profile URL" + } + ], + "columns": [ + "status", + "username" + ], + "type": "js", + "modulePath": "facebook/add-friend.js", + "sourceFile": "facebook/add-friend.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "events", + "description": "Browse Facebook event categories", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Number of categories" + } + ], + "columns": [ + "index", + "name" + ], + "type": "js", + "modulePath": "facebook/events.js", + "sourceFile": "facebook/events.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "feed", + "description": "Get your Facebook news feed", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of posts" + } + ], + "columns": [ + "index", + "author", + "content", + "likes", + "comments", + "shares" + ], + "type": "js", + "modulePath": "facebook/feed.js", + "sourceFile": "facebook/feed.js", + "navigateBefore": false + }, + { + "site": "facebook", + "name": "friends", + "description": "Get Facebook friend suggestions", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of friend suggestions" + } + ], + "columns": [ + "index", + "name", + "mutual" + ], + "type": "js", + "modulePath": "facebook/friends.js", + "sourceFile": "facebook/friends.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "groups", + "description": "List your Facebook groups", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of groups" + } + ], + "columns": [ + "index", + "name", + "last_post", + "url" + ], + "type": "js", + "modulePath": "facebook/groups.js", + "sourceFile": "facebook/groups.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "join-group", + "description": "Join a Facebook group", + "access": "write", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "group", + "type": "str", + "required": true, + "positional": true, + "help": "Group ID or URL path (e.g. '1876150192925481' or group name)" + } + ], + "columns": [ + "status", + "group" + ], + "type": "js", + "modulePath": "facebook/join-group.js", + "sourceFile": "facebook/join-group.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "login", + "description": "Open facebook login", + "access": "write", + "domain": "facebook.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "vanity", + "profile_url", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "facebook/auth.js", + "sourceFile": "facebook/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "facebook", + "name": "marketplace-inbox", + "description": "List recent Facebook Marketplace buyer/seller conversations", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of conversations to return" + } + ], + "columns": [ + "index", + "buyer", + "listing", + "snippet", + "time", + "unread" + ], + "type": "js", + "modulePath": "facebook/marketplace-inbox.js", + "sourceFile": "facebook/marketplace-inbox.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "marketplace-listings", + "description": "List your Facebook Marketplace seller listings", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of listings to return" + } + ], + "columns": [ + "index", + "title", + "price", + "status", + "listed", + "clicks", + "actions" + ], + "type": "js", + "modulePath": "facebook/marketplace-listings.js", + "sourceFile": "facebook/marketplace-listings.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "memories", + "description": "Get your Facebook memories (On This Day)", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of memories" + } + ], + "columns": [ + "index", + "source", + "content", + "time" + ], + "type": "js", + "modulePath": "facebook/memories.js", + "sourceFile": "facebook/memories.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "notifications", + "description": "Get recent Facebook notifications (includes unread / time / url / notif_id / notif_type columns)", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Number of notifications (1-100)" + } + ], + "columns": [ + "index", + "unread", + "text", + "time", + "url", + "notif_id", + "notif_type" + ], + "type": "js", + "modulePath": "facebook/notifications.js", + "sourceFile": "facebook/notifications.js", + "navigateBefore": false + }, + { + "site": "facebook", + "name": "profile", + "description": "Get Facebook user/page profile info", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Facebook username or page name" + } + ], + "columns": [ + "name", + "username", + "friends", + "followers", + "url" + ], + "type": "js", + "modulePath": "facebook/profile.js", + "sourceFile": "facebook/profile.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "search", + "description": "Search Facebook for people, pages, or posts", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "index", + "title", + "text", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "facebook/search.js", + "sourceFile": "facebook/search.js", + "navigateBefore": false + }, + { + "site": "facebook", + "name": "whoami", + "description": "Show the current logged-in facebook account", + "access": "read", + "domain": "facebook.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "vanity", + "profile_url" + ], + "type": "js", + "modulePath": "facebook/auth.js", + "sourceFile": "facebook/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "flathub", + "name": "app", + "description": "Full Flathub appstream metadata for an app id (license, categories, latest release)", + "access": "read", + "domain": "flathub.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "appId", + "type": "str", + "required": true, + "positional": true, + "help": "AppStream id (e.g. \"org.mozilla.firefox\", \"org.gnome.Calculator\")" + } + ], + "columns": [ + "appId", + "name", + "summary", + "developer", + "license", + "isFreeLicense", + "isEol", + "categories", + "keywords", + "latestVersion", + "latestReleaseDate", + "homepage", + "bugtracker", + "donation", + "url" + ], + "type": "js", + "modulePath": "flathub/app.js", + "sourceFile": "flathub/app.js" + }, + { + "site": "flathub", + "name": "search", + "description": "Search Flathub apps by keyword", + "access": "read", + "domain": "flathub.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max apps (1-100)" + } + ], + "columns": [ + "rank", + "appId", + "name", + "summary", + "developer", + "license", + "isFreeLicense", + "mainCategories", + "installsLastMonth", + "updatedAt", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "flathub/search.js", + "sourceFile": "flathub/search.js" + }, + { + "site": "gemini", + "name": "ask", + "description": "Send a prompt to Gemini and return only the assistant response", + "access": "write", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "model", + "type": "string", + "required": false, + "help": "Gemini model to use (e.g. \"2.5-flash\"). Use \"webcmd gemini models\" to list available values." + }, + { + "name": "timeout", + "type": "int", + "default": 60, + "required": false, + "help": "Max seconds to wait (default: 60)" + }, + { + "name": "new", + "type": "str", + "default": "false", + "required": false, + "help": "Start a new chat first (true/false, default: false)" + }, + { + "name": "thinking", + "type": "str", + "default": null, + "required": false, + "help": "Thinking level: standard or extended (omitted = leave unchanged)" + } + ], + "columns": [ + "response" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "gemini/ask.js", + "sourceFile": "gemini/ask.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "deep-research", + "description": "Start a Gemini Deep Research run and confirm it", + "access": "write", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 180, + "required": false, + "help": "Max seconds for the overall command (default: 180; confirm-wait clamps internally to 6-20s)" + }, + { + "name": "tool", + "type": "str", + "required": false, + "help": "Override tool label (default: Deep Research)" + }, + { + "name": "confirm", + "type": "str", + "required": false, + "help": "Override confirm button label (default: Start research)" + } + ], + "columns": [ + "status", + "url" + ], + "tags": [ + "search" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "gemini/deep-research.js", + "sourceFile": "gemini/deep-research.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "deep-research-result", + "description": "Export Deep Research report URL from a Gemini conversation", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": false, + "positional": true, + "help": "Conversation title or URL (optional; defaults to latest conversation)" + }, + { + "name": "match", + "type": "str", + "default": "contains", + "required": false, + "help": "Match mode", + "choices": [ + "contains", + "exact" + ] + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait for Docs export (default: 120)" + } + ], + "columns": [ + "response" + ], + "tags": [ + "search" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "gemini/deep-research-result.js", + "sourceFile": "gemini/deep-research-result.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "detail", + "description": "Open a Gemini web conversation by id, URL, or sidebar title and read its turns", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Conversation id, /app/ URL, or sidebar title" + } + ], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "gemini/detail.js", + "sourceFile": "gemini/detail.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "history", + "description": "List visible Gemini web conversation history from the sidebar", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max conversations to show" + } + ], + "columns": [ + "Index", + "Id", + "Title", + "Url" + ], + "type": "js", + "modulePath": "gemini/history.js", + "sourceFile": "gemini/history.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "image", + "description": "Generate images with Gemini web and save them locally", + "access": "write", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Image prompt to send to Gemini" + }, + { + "name": "rt", + "type": "str", + "default": "1:1", + "required": false, + "help": "Ratio shorthand for aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3)" + }, + { + "name": "st", + "type": "str", + "default": "", + "required": false, + "help": "Style shorthand, e.g. anime, icon, watercolor" + }, + { + "name": "op", + "type": "str", + "default": "~/tmp/gemini-images", + "required": false, + "help": "Output directory shorthand" + }, + { + "name": "sd", + "type": "boolean", + "default": false, + "required": false, + "help": "Skip download shorthand; only show Gemini page link" + }, + { + "name": "timeout", + "type": "int", + "default": 240, + "required": false, + "help": "Max seconds for the overall command (default: 240)" + } + ], + "columns": [ + "status", + "file", + "link" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "gemini/image.js", + "sourceFile": "gemini/image.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "login", + "description": "Open gemini login", + "access": "write", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "gemini/auth.js", + "sourceFile": "gemini/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "models", + "description": "List available Gemini models from the web UI", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "model", + "thinkingValues" + ], + "type": "js", + "modulePath": "gemini/models.js", + "sourceFile": "gemini/models.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "new", + "description": "Start a new conversation in Gemini web chat", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status", + "Action" + ], + "type": "js", + "modulePath": "gemini/new.js", + "sourceFile": "gemini/new.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "read", + "description": "Read the turns visible in the current Gemini web conversation", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "gemini/read.js", + "sourceFile": "gemini/read.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "status", + "description": "Check Gemini web page availability and login state", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status", + "Login", + "Url" + ], + "type": "js", + "modulePath": "gemini/status.js", + "sourceFile": "gemini/status.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "whoami", + "description": "Show the current logged-in gemini account", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name" + ], + "type": "js", + "modulePath": "gemini/auth.js", + "sourceFile": "gemini/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "geogebra", + "name": "add-circle", + "description": "Create a circle by center+radius or center+point", + "access": "write", + "example": "webcmd geogebra add-circle --center A --radius 3", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "center", + "type": "str", + "required": true, + "help": "Center point label (e.g. A)" + }, + { + "name": "radius", + "type": "str", + "required": false, + "help": "Radius value (number) or a point label on the circle" + }, + { + "name": "point", + "type": "str", + "required": false, + "help": "Alternative: a point label on the circle (use instead of --radius for Circle(center,point))" + } + ], + "columns": [ + "label", + "center", + "radius" + ], + "type": "js", + "modulePath": "geogebra/add-circle.js", + "sourceFile": "geogebra/add-circle.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "add-line", + "description": "Create a line through two points or a segment between two points", + "access": "write", + "example": "webcmd geogebra add-line --points A,B --type segment", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "points", + "type": "str", + "required": true, + "help": "Two point labels separated by comma (e.g. \"A,B\")" + }, + { + "name": "type", + "type": "str", + "default": "line", + "required": false, + "help": "Type: line, segment, or ray (default: line)", + "choices": [ + "line", + "segment", + "ray" + ] + } + ], + "columns": [ + "label", + "type", + "points" + ], + "type": "js", + "modulePath": "geogebra/add-line.js", + "sourceFile": "geogebra/add-line.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "add-point", + "description": "Create a point with given label and coordinates", + "access": "write", + "example": "webcmd geogebra add-point --name A --coords 1,2", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "help": "Point label (e.g. A, B, P1)" + }, + { + "name": "coords", + "type": "str", + "required": true, + "help": "Coordinates as x,y (e.g. \"1,2\")" + } + ], + "columns": [ + "name", + "x", + "y" + ], + "type": "js", + "modulePath": "geogebra/add-point.js", + "sourceFile": "geogebra/add-point.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "add-polygon", + "description": "Create a polygon from a list of point labels", + "access": "write", + "example": "webcmd geogebra add-polygon --points A,B,C", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "points", + "type": "str", + "required": true, + "help": "Comma-separated point labels (e.g. \"A,B,C\" or \"A,B,C,D\")" + } + ], + "columns": [ + "label", + "vertices" + ], + "type": "js", + "modulePath": "geogebra/add-polygon.js", + "sourceFile": "geogebra/add-polygon.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "eval", + "description": "Execute one or more GeoGebra command strings (semicolon-separated)", + "access": "write", + "example": "webcmd geogebra eval \"A=(0,0);B=(4,0);c=Circle(A,B);d=Circle(B,A);C=Intersect(c,d,1);Polygon(A,B,C)\"", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "command", + "type": "str", + "required": true, + "positional": true, + "help": "GeoGebra command string (use ; to chain multiple commands)" + } + ], + "columns": [ + "command", + "result" + ], + "type": "js", + "modulePath": "geogebra/eval.js", + "sourceFile": "geogebra/eval.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "hexagon", + "description": "Draw a regular hexagon centered at the origin", + "access": "write", + "example": "webcmd geogebra hexagon --size 3", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "size", + "type": "str", + "default": "2", + "required": false, + "help": "Radius of the hexagon (default: 2)" + } + ], + "columns": [ + "step", + "result" + ], + "type": "js", + "modulePath": "geogebra/hexagon.js", + "sourceFile": "geogebra/hexagon.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "info", + "description": "Get detailed properties of a GeoGebra object", + "access": "read", + "example": "webcmd geogebra info --name A", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "help": "Object label (e.g. A, c1, poly1)" + } + ], + "columns": [ + "property", + "value" + ], + "type": "js", + "modulePath": "geogebra/info.js", + "sourceFile": "geogebra/info.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "list", + "description": "List all geometric objects on the GeoGebra canvas", + "access": "read", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "type", + "type": "str", + "required": false, + "help": "Filter by object type (e.g. \"point\", \"line\", \"circle\")" + } + ], + "columns": [ + "name", + "type", + "value", + "visible" + ], + "type": "js", + "modulePath": "geogebra/list.js", + "sourceFile": "geogebra/list.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "triangle", + "description": "Draw an equilateral triangle from a horizontal base segment", + "access": "write", + "example": "webcmd geogebra triangle --size 4", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "size", + "type": "str", + "default": "2", + "required": false, + "help": "Side length of the triangle (default: 2)" + } + ], + "columns": [ + "step", + "result" + ], + "type": "js", + "modulePath": "geogebra/triangle.js", + "sourceFile": "geogebra/triangle.js", + "navigateBefore": false + }, + { + "site": "github", + "name": "login", + "description": "Open github login", + "access": "write", + "domain": "github.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "id", + "username", + "name", + "url", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "github/auth.js", + "sourceFile": "github/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "github", + "name": "whoami", + "description": "Show the current logged-in github account", + "access": "read", + "domain": "github.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "id", + "username", + "name", + "url" + ], + "type": "js", + "modulePath": "github/auth.js", + "sourceFile": "github/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "github-trending", + "name": "repos", + "description": "GitHub Trending repositories (public, no login). Filter by --language and --since.", + "access": "read", + "domain": "github.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "since", + "type": "string", + "default": "daily", + "required": false, + "help": "Time range: daily / weekly / monthly" + }, + { + "name": "language", + "type": "string", + "default": "", + "required": false, + "help": "Filter by programming language slug, e.g. python, rust, \"c++\"" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Number of repositories to return (max 25)" + } + ], + "columns": [ + "rank", + "repo", + "description", + "language", + "stars", + "forks", + "starsSince", + "url" + ], + "type": "js", + "modulePath": "github-trending/repos.js", + "sourceFile": "github-trending/repos.js" + }, + { + "site": "google", + "name": "images", + "description": "Search Google Images for photos and image results", + "access": "read", + "domain": "google.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Image search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of image results (1-100)" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language short code (e.g. en, zh)" + }, + { + "name": "resolve", + "type": "bool", + "default": true, + "required": false, + "help": "Click image previews to resolve original imgurl values" + } + ], + "columns": [ + "rank", + "title", + "imageUrl", + "thumbnailUrl", + "sourceUrl", + "source", + "width", + "height" + ], + "type": "js", + "modulePath": "google/images.js", + "sourceFile": "google/images.js", + "navigateBefore": false + }, + { + "site": "google", + "name": "news", + "description": "Get Google News headlines", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "keyword", + "type": "str", + "required": false, + "positional": true, + "help": "Search query (omit for top stories)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language short code (e.g. en, zh)" + }, + { + "name": "region", + "type": "str", + "default": "US", + "required": false, + "help": "Region code (e.g. US, CN)" + } + ], + "columns": [ + "title", + "source", + "date", + "url" + ], + "type": "js", + "modulePath": "google/news.js", + "sourceFile": "google/news.js" + }, + { + "site": "google", + "name": "search", + "description": "Search Google", + "access": "read", + "domain": "google.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results (1-100)" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language short code (e.g. en, zh)" + } + ], + "columns": [ + "type", + "title", + "url", + "snippet" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "google/search.js", + "sourceFile": "google/search.js" + }, + { + "site": "google", + "name": "suggest", + "description": "Get Google search suggestions", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "lang", + "type": "str", + "default": "zh-CN", + "required": false, + "help": "Language code" + } + ], + "columns": [ + "suggestion" + ], + "type": "js", + "modulePath": "google/suggest.js", + "sourceFile": "google/suggest.js" + }, + { + "site": "google", + "name": "trends", + "description": "Get Google Trends daily trending searches", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "region", + "type": "str", + "default": "US", + "required": false, + "help": "Region code (e.g. US, CN, JP)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "title", + "traffic", + "date" + ], + "type": "js", + "modulePath": "google/trends.js", + "sourceFile": "google/trends.js" + }, + { + "site": "google-scholar", + "name": "cite", + "description": "Get citation for a Google Scholar paper", + "access": "read", + "domain": "scholar.google.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Paper title to search for" + }, + { + "name": "style", + "type": "str", + "default": "bibtex", + "required": false, + "help": "Citation format", + "choices": [ + "bibtex", + "endnote", + "refman", + "refworks" + ] + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Which search result to cite (1-based)" + } + ], + "columns": [ + "title", + "format", + "citation" + ], + "type": "js", + "modulePath": "google-scholar/cite.js", + "sourceFile": "google-scholar/cite.js" + }, + { + "site": "google-scholar", + "name": "profile", + "description": "View a Google Scholar author profile", + "access": "read", + "domain": "scholar.google.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "author", + "type": "str", + "required": true, + "positional": true, + "help": "Author name or Scholar user ID (e.g. JicYPdAAAAAJ)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max papers to show (max 20)" + } + ], + "columns": [ + "rank", + "title", + "cited", + "year" + ], + "type": "js", + "modulePath": "google-scholar/profile.js", + "sourceFile": "google-scholar/profile.js" + }, + { + "site": "google-scholar", + "name": "search", + "description": "Google Scholar scholar search", + "access": "read", + "domain": "scholar.google.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results to return (max 20)" + } + ], + "columns": [ + "rank", + "title", + "authors", + "source", + "year", + "cited", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "google-scholar/search.js", + "sourceFile": "google-scholar/search.js" + }, + { + "site": "goproxy", + "name": "module", + "description": "Latest version + VCS origin metadata for a Go module on proxy.golang.org", + "access": "read", + "domain": "proxy.golang.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "module", + "type": "string", + "required": true, + "positional": true, + "help": "Go module path (e.g. \"github.com/gin-gonic/gin\", \"golang.org/x/net\")" + } + ], + "columns": [ + "module", + "version", + "publishedAt", + "vcs", + "repository", + "commit", + "ref", + "pkgGoDevUrl", + "url" + ], + "type": "js", + "modulePath": "goproxy/module.js", + "sourceFile": "goproxy/module.js" + }, + { + "site": "goproxy", + "name": "versions", + "description": "Published version tags for a Go module (newest first), optionally with publish times", + "access": "read", + "domain": "proxy.golang.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "module", + "type": "string", + "required": true, + "positional": true, + "help": "Go module path (e.g. \"github.com/gin-gonic/gin\")" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max rows to return (1-200)" + }, + { + "name": "with-time", + "type": "boolean", + "default": false, + "required": false, + "help": "Fetch each version's publish time (one extra request per row)" + } + ], + "columns": [ + "rank", + "module", + "version", + "publishedAt", + "url" + ], + "type": "js", + "modulePath": "goproxy/versions.js", + "sourceFile": "goproxy/versions.js" + }, + { + "site": "grok", + "name": "ask", + "description": "Send a message to Grok and get response", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "string", + "required": true, + "positional": true, + "help": "Prompt to send to Grok" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait for response (default: 120)" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending (default: false)" + } + ], + "columns": [ + "response" + ], + "type": "js", + "modulePath": "grok/ask.js", + "sourceFile": "grok/ask.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "delete", + "description": "Delete a Grok conversation by ID. Grok takes effect immediately with no confirmation dialog — require --yes to actually delete.", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID or grok.com/c/ URL" + }, + { + "name": "yes", + "type": "boolean", + "default": false, + "required": false, + "help": "Actually delete (default is a dry-run preview)" + } + ], + "columns": [ + "status", + "id" + ], + "type": "js", + "modulePath": "grok/delete.js", + "sourceFile": "grok/delete.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "detail", + "description": "Open a Grok conversation by ID and read its messages", + "access": "read", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Session ID (UUID) or full https://grok.com/c/ URL" + }, + { + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "grok/detail.js", + "sourceFile": "grok/detail.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "export", + "description": "Export all visible Grok conversation history metadata", + "access": "read", + "example": "webcmd grok export -f yaml", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 0, + "required": false, + "help": "Max conversations to export; 0 means all loaded history" + }, + { + "name": "maxScrolls", + "type": "int", + "default": 80, + "required": false, + "help": "Max history-list scroll rounds when limit is 0 (max 500)" + } + ], + "columns": [ + "index", + "id", + "title", + "date", + "url" + ], + "type": "js", + "modulePath": "grok/export.js", + "sourceFile": "grok/export.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "export-all", + "description": "Export Grok conversation history and each conversation transcript", + "access": "read", + "example": "webcmd grok export-all --limit 5 -f json", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 0, + "required": false, + "help": "Max conversations to export; 0 means all loaded history" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Skip this many conversations before exporting" + }, + { + "name": "manifestPath", + "type": "string", + "default": "", + "required": false, + "help": "Optional grok/export JSON manifest path; skips history dialog and visits listed /c pages directly" + }, + { + "name": "maxScrolls", + "type": "int", + "default": 80, + "required": false, + "help": "Max history-list scroll rounds when limit is 0 (max 500)" + }, + { + "name": "pageScrolls", + "type": "int", + "default": 30, + "required": false, + "help": "Max per-conversation scroll-to-bottom rounds (max 200)" + }, + { + "name": "pageTimeoutMs", + "type": "int", + "default": 30000, + "required": false, + "help": "Max wait for each conversation page to show messages" + }, + { + "name": "delayMinMs", + "type": "int", + "default": 0, + "required": false, + "help": "Minimum polite delay after a conversation page loads" + }, + { + "name": "delayMaxMs", + "type": "int", + "default": 5000, + "required": false, + "help": "Maximum polite delay after a conversation page loads" + } + ], + "columns": [ + "index", + "id", + "title", + "date", + "url", + "status", + "messageCount", + "error", + "messagesJson" + ], + "type": "js", + "modulePath": "grok/export-all.js", + "sourceFile": "grok/export-all.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "history", + "description": "List recent Grok conversations from the sidebar (requires login)", + "access": "read", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max conversations to show (default 20, max 100)" + } + ], + "columns": [ + "Index", + "Title", + "Url" + ], + "type": "js", + "modulePath": "grok/history.js", + "sourceFile": "grok/history.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "image", + "description": "Generate images on grok.com and return image URLs", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "string", + "required": true, + "positional": true, + "help": "Image generation prompt" + }, + { + "name": "timeout", + "type": "int", + "default": 240, + "required": false, + "help": "Max seconds to wait for the image (default: 240)" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending (default: false)" + }, + { + "name": "count", + "type": "int", + "default": 1, + "required": false, + "help": "Minimum images to wait for before returning (default: 1)" + }, + { + "name": "out", + "type": "string", + "default": "", + "required": false, + "help": "Directory to save downloaded images (uses browser session to bypass auth)" + } + ], + "columns": [ + "url", + "width", + "height", + "path" + ], + "type": "js", + "modulePath": "grok/image.js", + "sourceFile": "grok/image.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "login", + "description": "Open grok login", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "grok/auth.js", + "sourceFile": "grok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "new", + "description": "Start a new conversation in Grok", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "grok/new.js", + "sourceFile": "grok/new.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "pin", + "description": "Pin a Grok conversation by ID", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID or grok.com/c/ URL" + } + ], + "columns": [ + "status", + "id" + ], + "type": "js", + "modulePath": "grok/pin.js", + "sourceFile": "grok/pin.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "read", + "description": "Read messages in the current Grok conversation", + "access": "read", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "grok/read.js", + "sourceFile": "grok/read.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "send", + "description": "Fire-and-forget: send a prompt to Grok without waiting for the reply", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send to Grok" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" + } + ], + "columns": [ + "Status", + "Prompt" + ], + "type": "js", + "modulePath": "grok/send.js", + "sourceFile": "grok/send.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "status", + "description": "Check Grok page availability, login state, current session and model", + "access": "read", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status", + "Login", + "Model", + "SessionId", + "Url" + ], + "type": "js", + "modulePath": "grok/status.js", + "sourceFile": "grok/status.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "unpin", + "description": "Unpin a Grok conversation by ID", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID or grok.com/c/ URL" + } + ], + "columns": [ + "status", + "id" + ], + "type": "js", + "modulePath": "grok/pin.js", + "sourceFile": "grok/pin.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "whoami", + "description": "Show the current logged-in grok account", + "access": "read", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" + ], + "type": "js", + "modulePath": "grok/auth.js", + "sourceFile": "grok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "hackernews", + "name": "ask", + "description": "Hacker News Ask HN posts", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "hackernews/ask.js", + "sourceFile": "hackernews/ask.js" + }, + { + "site": "hackernews", + "name": "best", + "description": "Hacker News best stories", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "hackernews/best.js", + "sourceFile": "hackernews/best.js" + }, + { + "site": "hackernews", + "name": "jobs", + "description": "Hacker News job postings", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of job postings" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "url" + ], + "type": "js", + "modulePath": "hackernews/jobs.js", + "sourceFile": "hackernews/jobs.js" + }, + { + "site": "hackernews", + "name": "new", + "description": "Hacker News newest stories", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "hackernews/new.js", + "sourceFile": "hackernews/new.js" + }, + { + "site": "hackernews", + "name": "read", + "description": "Read a Hacker News story and its comment tree", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "HN item ID (e.g. 39847301)" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max top-level comments" + }, + { + "name": "depth", + "type": "int", + "default": 2, + "required": false, + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + }, + { + "name": "replies", + "type": "int", + "default": 5, + "required": false, + "help": "Max replies shown per comment at each level" + }, + { + "name": "max-length", + "type": "int", + "default": 2000, + "required": false, + "help": "Max characters per comment body (min 100)" + } + ], + "columns": [ + "type", + "author", + "score", + "text" + ], + "type": "js", + "modulePath": "hackernews/read.js", + "sourceFile": "hackernews/read.js" + }, + { + "site": "hackernews", + "name": "search", + "description": "Search Hacker News stories", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance or date", + "choices": [ + "relevance", + "date" + ] + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "hackernews/search.js", + "sourceFile": "hackernews/search.js" + }, + { + "site": "hackernews", + "name": "show", + "description": "Hacker News Show HN posts", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "hackernews/show.js", + "sourceFile": "hackernews/show.js" + }, + { + "site": "hackernews", + "name": "top", + "description": "Hacker News top stories", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "hackernews/top.js", + "sourceFile": "hackernews/top.js" + }, + { + "site": "hackernews", + "name": "user", + "description": "Hacker News user profile", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "HN username" + } + ], + "columns": [ + "username", + "karma", + "created", + "about" + ], + "type": "js", + "modulePath": "hackernews/user.js", + "sourceFile": "hackernews/user.js" + }, + { + "site": "hf", + "name": "datasets", + "description": "Top Hugging Face datasets (downloads / likes / trending / freshness).", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "sort", + "type": "string", + "default": "downloads", + "required": false, + "help": "Sort key: downloads, likes, trending, created_at, last_modified" + }, + { + "name": "search", + "type": "string", + "required": false, + "help": "Optional name/owner substring filter." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max datasets (max 100; one API page)." + } + ], + "columns": [ + "rank", + "id", + "author", + "downloads", + "likes", + "tags", + "lastModified", + "url" + ], + "type": "js", + "modulePath": "hf/datasets.js", + "sourceFile": "hf/datasets.js" + }, + { + "site": "hf", + "name": "login", + "description": "Open hf login", + "access": "write", + "domain": "huggingface.co", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "username", + "fullname", + "type", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "hf/auth.js", + "sourceFile": "hf/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "hf", + "name": "models", + "description": "Top Hugging Face models (downloads / likes / trending / freshness).", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "sort", + "type": "string", + "default": "downloads", + "required": false, + "help": "Sort key: downloads, likes, trending, created_at, last_modified" + }, + { + "name": "search", + "type": "string", + "required": false, + "help": "Optional name/owner substring filter (e.g. \"llama\", \"mistralai/\")" + }, + { + "name": "pipeline", + "type": "string", + "required": false, + "help": "Filter by pipeline tag (e.g. text-generation, image-classification)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max models (max 100; one API page)." + } + ], + "columns": [ + "rank", + "id", + "author", + "pipelineTag", + "downloads", + "likes", + "tags", + "lastModified", + "url" + ], + "type": "js", + "modulePath": "hf/models.js", + "sourceFile": "hf/models.js" + }, + { + "site": "hf", + "name": "paper", + "description": "Hugging Face paper detail by arXiv id (full title / summary / authors / AI keywords)", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "arXiv id (e.g. \"1706.03762\") — same value HF uses to mirror the paper" + } + ], + "columns": [ + "id", + "title", + "authors", + "publishedAt", + "upvotes", + "aiKeywords", + "summary", + "aiSummary", + "url" + ], + "type": "js", + "modulePath": "hf/paper.js", + "sourceFile": "hf/paper.js" + }, + { + "site": "hf", + "name": "spaces", + "description": "Top Hugging Face Spaces (likes / created_at / last_modified).", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "sort", + "type": "string", + "default": "likes", + "required": false, + "help": "Sort key: likes, created_at, last_modified" + }, + { + "name": "search", + "type": "string", + "required": false, + "help": "Optional name/owner substring filter (e.g. \"stability\", \"openai/\")" + }, + { + "name": "sdk", + "type": "string", + "required": false, + "help": "Filter by Space SDK: gradio / streamlit / docker / static" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max spaces (max 100; one API page)." + } + ], + "columns": [ + "rank", + "id", + "author", + "sdk", + "likes", + "tags", + "lastModified", + "url" + ], + "type": "js", + "modulePath": "hf/spaces.js", + "sourceFile": "hf/spaces.js" + }, + { + "site": "hf", + "name": "top", + "description": "Top upvoted Hugging Face papers", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of papers" + }, + { + "name": "all", + "type": "bool", + "default": false, + "required": false, + "help": "Return all papers (ignore limit)" + }, + { + "name": "date", + "type": "str", + "required": false, + "help": "Date (YYYY-MM-DD), defaults to most recent" + }, + { + "name": "period", + "type": "str", + "default": "daily", + "required": false, + "help": "Time period: daily, weekly, or monthly", + "choices": [ + "daily", + "weekly", + "monthly" + ] + } + ], + "columns": [ + "rank", + "id", + "title", + "upvotes", + "authors" + ], + "type": "js", + "modulePath": "hf/top.js", + "sourceFile": "hf/top.js" + }, + { + "site": "hf", + "name": "whoami", + "description": "Show the current logged-in hf account", + "access": "read", + "domain": "huggingface.co", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "username", + "fullname", + "type" + ], + "type": "js", + "modulePath": "hf/auth.js", + "sourceFile": "hf/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "homebrew", + "name": "cask", + "description": "Fetch a Homebrew cask's metadata (version, homepage, deprecation, download URL)", + "access": "read", + "domain": "formulae.brew.sh", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "token", + "type": "str", + "required": true, + "positional": true, + "help": "Cask token (e.g. \"firefox\", \"visual-studio-code\", \"google-chrome\")" + } + ], + "columns": [ + "cask", + "tap", + "name", + "version", + "description", + "homepage", + "deprecated", + "disabled", + "download", + "url" + ], + "type": "js", + "modulePath": "homebrew/cask.js", + "sourceFile": "homebrew/cask.js" + }, + { + "site": "homebrew", + "name": "formula", + "description": "Fetch a Homebrew formula's metadata (version, license, deps, deprecation, source)", + "access": "read", + "domain": "formulae.brew.sh", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Formula name (e.g. \"wget\", \"gcc@13\", \"imagemagick\")" + } + ], + "columns": [ + "formula", + "tap", + "version", + "license", + "description", + "homepage", + "dependencies", + "deprecated", + "disabled", + "source", + "url" + ], + "type": "js", + "modulePath": "homebrew/formula.js", + "sourceFile": "homebrew/formula.js" + }, + { + "site": "homebrew", + "name": "popular", + "description": "List most-installed Homebrew formulae or casks (Homebrew's analytics ranking)", + "access": "read", + "domain": "formulae.brew.sh", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "type", + "type": "str", + "default": "formula", + "required": false, + "help": "Package type (formula / cask)" + }, + { + "name": "window", + "type": "str", + "default": "30d", + "required": false, + "help": "Time window (30d / 90d / 365d)" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max rows (1-500)" + } + ], + "columns": [ + "rank", + "token", + "type", + "installs", + "percent", + "window", + "url" + ], + "type": "js", + "modulePath": "homebrew/popular.js", + "sourceFile": "homebrew/popular.js" + }, + { + "site": "imdb", + "name": "person", + "description": "Get actor or director info", + "access": "read", + "domain": "www.imdb.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "IMDb person ID (nm0634240) or URL" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max filmography entries" + } + ], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "imdb/person.js", + "sourceFile": "imdb/person.js" + }, + { + "site": "imdb", + "name": "reviews", + "description": "Get user reviews for a movie or TV show", + "access": "read", + "domain": "www.imdb.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "IMDb title ID (tt1375666) or URL" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of reviews" + } + ], + "columns": [ + "rank", + "title", + "rating", + "author", + "date", + "text" + ], + "type": "js", + "modulePath": "imdb/reviews.js", + "sourceFile": "imdb/reviews.js" + }, + { + "site": "imdb", + "name": "search", + "description": "Search IMDb for movies, TV shows, and people", + "access": "read", + "domain": "www.imdb.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "id", + "title", + "year", + "type", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "imdb/search.js", + "sourceFile": "imdb/search.js" + }, + { + "site": "imdb", + "name": "title", + "description": "Get movie or TV show details", + "access": "read", + "domain": "www.imdb.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "IMDb title ID (tt1375666) or URL" + } + ], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "imdb/title.js", + "sourceFile": "imdb/title.js" + }, + { + "site": "imdb", + "name": "top", + "description": "IMDb Top 250 Movies", + "access": "read", + "domain": "www.imdb.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "rating", + "votes", + "genre", + "url" + ], + "type": "js", + "modulePath": "imdb/top.js", + "sourceFile": "imdb/top.js" + }, + { + "site": "imdb", + "name": "trending", + "description": "IMDb Most Popular Movies", + "access": "read", + "domain": "www.imdb.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "rating", + "genre", + "url" + ], + "type": "js", + "modulePath": "imdb/trending.js", + "sourceFile": "imdb/trending.js" + }, + { + "site": "indeed", + "name": "job", + "aliases": [ + "detail", + "view" + ], + "description": "Read the full Indeed job posting by jk (job key)", + "access": "read", + "domain": "www.indeed.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Job key (16-char hex from `indeed search`, e.g. \"dccc07ac5a6a3683\")" + } + ], + "columns": [ + "id", + "title", + "company", + "location", + "salary", + "job_type", + "description", + "url" + ], + "type": "js", + "modulePath": "indeed/job.js", + "sourceFile": "indeed/job.js", + "navigateBefore": false + }, + { + "site": "indeed", + "name": "search", + "description": "Indeed keyword job search (rendered DOM via browser session, US site)", + "access": "read", + "domain": "www.indeed.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Job keyword (title / skill / company)" + }, + { + "name": "location", + "type": "string", + "default": "", + "required": false, + "help": "Location filter (e.g. \"remote\", \"New York, NY\", \"San Francisco\")" + }, + { + "name": "fromage", + "type": "string", + "default": "", + "required": false, + "help": "Recency filter, days back: 1 / 3 / 7 / 14" + }, + { + "name": "sort", + "type": "string", + "default": "relevance", + "required": false, + "help": "Sort order: relevance | date" + }, + { + "name": "start", + "type": "int", + "default": 0, + "required": false, + "help": "Pagination offset (multiple of 10, 0-based)" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Max rows to return (1-25, capped at one page)" + } + ], + "columns": [ + "rank", + "id", + "title", + "company", + "location", + "salary", + "tags", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "indeed/search.js", + "sourceFile": "indeed/search.js", + "navigateBefore": false + }, + { + "site": "instagram", + "name": "collection-create", + "description": "Create a new Instagram saved-posts collection (folder)", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Name of the collection to create" + } + ], + "columns": [ + "status", + "collectionId", + "collectionName", + "mediaCount" + ], + "type": "js", + "modulePath": "instagram/collection-create.js", + "sourceFile": "instagram/collection-create.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "collection-delete", + "description": "Delete an Instagram saved-posts collection (folder) by name or id", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "target", + "type": "str", + "required": true, + "positional": true, + "help": "Collection name (case-insensitive) or numeric collection_id" + } + ], + "columns": [ + "status", + "collectionId", + "collectionName" + ], + "type": "js", + "modulePath": "instagram/collection-delete.js", + "sourceFile": "instagram/collection-delete.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "comment", + "description": "Comment on an Instagram post", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Username of the post author" + }, + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Comment text" + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Post index (1 = most recent)" + } + ], + "columns": [ + "status", + "user", + "text" + ], + "type": "js", + "modulePath": "instagram/comment.js", + "sourceFile": "instagram/comment.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "download", + "description": "Download images and videos from Instagram posts and reels", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram post / reel / tv URL" + }, + { + "name": "path", + "type": "str", + "default": "~/Downloads/Instagram", + "required": false, + "help": "Download directory" + } + ], + "type": "js", + "modulePath": "instagram/download.js", + "sourceFile": "instagram/download.js", + "navigateBefore": false + }, + { + "site": "instagram", + "name": "explore", + "description": "Instagram explore/discover trending posts", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts" + } + ], + "columns": [ + "rank", + "user", + "caption", + "likes", + "comments", + "type" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "instagram/explore.js", + "sourceFile": "instagram/explore.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "follow", + "description": "Follow an Instagram user", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram username to follow" + } + ], + "columns": [ + "status", + "username" + ], + "type": "js", + "modulePath": "instagram/follow.js", + "sourceFile": "instagram/follow.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "followers", + "description": "List followers of an Instagram user", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram username" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of followers" + } + ], + "columns": [ + "rank", + "username", + "name", + "verified", + "private" + ], + "type": "js", + "modulePath": "instagram/followers.js", + "sourceFile": "instagram/followers.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "following", + "description": "List accounts an Instagram user is following", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram username" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of accounts" + } + ], + "columns": [ + "rank", + "username", + "name", + "verified", + "private" + ], + "type": "js", + "modulePath": "instagram/following.js", + "sourceFile": "instagram/following.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "like", + "description": "Like an Instagram post", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Username of the post author" + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Post index (1 = most recent)" + } + ], + "columns": [ + "status", + "user", + "post" + ], + "type": "js", + "modulePath": "instagram/like.js", + "sourceFile": "instagram/like.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "login", + "description": "Open instagram login", + "access": "write", + "domain": "instagram.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "username", + "full_name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "instagram/auth.js", + "sourceFile": "instagram/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "instagram", + "name": "note", + "description": "Publish a text Instagram note", + "access": "write", + "domain": "www.instagram.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "content", + "type": "str", + "required": true, + "positional": true, + "help": "Note text (max 60 characters)" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds for the overall command (default: 120)" + } + ], + "columns": [ + "status", + "detail", + "noteId" + ], + "type": "js", + "modulePath": "instagram/note.js", + "sourceFile": "instagram/note.js", + "navigateBefore": true + }, + { + "site": "instagram", + "name": "post", + "description": "Post an Instagram feed image or mixed-media carousel", + "access": "write", + "domain": "www.instagram.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "media", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Comma-separated media paths (images/videos, up to 10)", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "image/jpeg", + "image/png", + "image/webp", + "video/mp4" + ], + "maxBytes": 262144000 + } + }, + { + "name": "content", + "type": "str", + "required": false, + "positional": true, + "help": "Caption text" + }, + { + "name": "timeout", + "type": "int", + "default": 300, + "required": false, + "help": "Max seconds for the overall command (default: 300)" + } + ], + "columns": [ + "status", + "detail", + "url" + ], + "type": "js", + "modulePath": "instagram/post.js", + "sourceFile": "instagram/post.js", + "navigateBefore": true + }, + { + "site": "instagram", + "name": "profile", + "description": "Get Instagram user profile info", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram username" + } + ], + "columns": [ + "username", + "name", + "followers", + "following", + "posts", + "verified", + "bio" + ], + "type": "js", + "modulePath": "instagram/profile.js", + "sourceFile": "instagram/profile.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "reel", + "description": "Post an Instagram reel video", + "access": "write", + "domain": "www.instagram.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "video", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Path to a single .mp4 video file", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "video/mp4" + ], + "maxBytes": 262144000 + } + }, + { + "name": "content", + "type": "str", + "required": false, + "positional": true, + "help": "Caption text" + }, + { + "name": "timeout", + "type": "int", + "default": 600, + "required": false, + "help": "Max seconds for the overall command (default: 600)" + } + ], + "columns": [ + "status", + "detail", + "url" + ], + "type": "js", + "modulePath": "instagram/reel.js", + "sourceFile": "instagram/reel.js", + "navigateBefore": true + }, + { + "site": "instagram", + "name": "save", + "description": "Save (bookmark) an Instagram post", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Username of the post author" + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Post index (1 = most recent)" + } + ], + "columns": [ + "status", + "user", + "post" + ], + "type": "js", + "modulePath": "instagram/save.js", + "sourceFile": "instagram/save.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "saved", + "description": "Get your saved Instagram posts (optionally from a specific collection)", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of saved posts" + }, + { + "name": "collection", + "type": "str", + "required": false, + "help": "Collection name (case-insensitive). Omit for the default \"All posts\" feed." + } + ], + "columns": [ + "index", + "user", + "caption", + "likes", + "comments", + "type" + ], + "type": "js", + "modulePath": "instagram/saved.js", + "sourceFile": "instagram/saved.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "search", + "description": "Search Instagram users", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "username", + "name", + "verified", + "private", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "instagram/search.js", + "sourceFile": "instagram/search.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "story", + "description": "Post a single Instagram story image or video", + "access": "write", + "domain": "www.instagram.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "media", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Path to a single story image or video file" + }, + { + "name": "timeout", + "type": "int", + "default": 300, + "required": false, + "help": "Max seconds for the overall command (default: 300)" + } + ], + "columns": [ + "status", + "detail", + "url" + ], + "type": "js", + "modulePath": "instagram/story.js", + "sourceFile": "instagram/story.js", + "navigateBefore": true + }, + { + "site": "instagram", + "name": "unfollow", + "description": "Unfollow an Instagram user", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram username to unfollow" + } + ], + "columns": [ + "status", + "username" + ], + "type": "js", + "modulePath": "instagram/unfollow.js", + "sourceFile": "instagram/unfollow.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "unlike", + "description": "Unlike an Instagram post", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Username of the post author" + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Post index (1 = most recent)" + } + ], + "columns": [ + "status", + "user", + "post" + ], + "type": "js", + "modulePath": "instagram/unlike.js", + "sourceFile": "instagram/unlike.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "unsave", + "description": "Unsave (remove bookmark) an Instagram post", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Username of the post author" + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Post index (1 = most recent)" + } + ], + "columns": [ + "status", + "user", + "post" + ], + "type": "js", + "modulePath": "instagram/unsave.js", + "sourceFile": "instagram/unsave.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "user", + "description": "Get recent posts from an Instagram user", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram username" + }, + { + "name": "limit", + "type": "int", + "default": 12, + "required": false, + "help": "Number of posts" + } + ], + "columns": [ + "index", + "caption", + "likes", + "comments", + "type", + "date" + ], + "type": "js", + "modulePath": "instagram/user.js", + "sourceFile": "instagram/user.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "whoami", + "description": "Show the current logged-in instagram account", + "access": "read", + "domain": "instagram.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "username", + "full_name" + ], + "type": "js", + "modulePath": "instagram/auth.js", + "sourceFile": "instagram/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "jira", + "name": "attachments", + "description": "Jira issue attachment metadata", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "Jira issue key, e.g. PROJ-123" + } + ], + "columns": [ + "id", + "filename", + "mimeType", + "size", + "url" + ], + "type": "js", + "modulePath": "jira/attachments.js", + "sourceFile": "jira/attachments.js" + }, + { + "site": "jira", + "name": "comments", + "description": "Jira issue comments as Markdown", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "Jira issue key, e.g. PROJ-123" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max comments to return (1-100)" + } + ], + "columns": [ + "id", + "author", + "created", + "updated", + "markdown" + ], + "type": "js", + "modulePath": "jira/comments.js", + "sourceFile": "jira/comments.js" + }, + { + "site": "jira", + "name": "issue", + "description": "Jira issue detail normalized for agents (description, comments, attachments, links)", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "Jira issue key, e.g. PROJ-123" + }, + { + "name": "comments-limit", + "type": "int", + "default": 100, + "required": false, + "help": "Max comments to include (1-100)" + } + ], + "columns": [ + "key", + "summary", + "issueType", + "status", + "priority", + "assignee", + "updated", + "url" + ], + "type": "js", + "modulePath": "jira/issue.js", + "sourceFile": "jira/issue.js" + }, + { + "site": "jira", + "name": "links", + "description": "Jira issue links", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "Jira issue key, e.g. PROJ-123" + } + ], + "columns": [ + "key", + "type", + "direction" + ], + "type": "js", + "modulePath": "jira/links.js", + "sourceFile": "jira/links.js" + }, + { + "site": "jira", + "name": "search", + "description": "Search Jira issues with JQL", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "jql", + "type": "str", + "required": true, + "positional": true, + "help": "JQL query, e.g. \"project = PROJ order by updated desc\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max issues to return (1-100)" + } + ], + "columns": [ + "key", + "summary", + "issueType", + "status", + "priority", + "assignee", + "updated", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "jira/search.js", + "sourceFile": "jira/search.js" + }, + { + "site": "lesswrong", + "name": "comments", + "description": "Top comments on a post", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "url-or-id", + "type": "string", + "required": true, + "positional": true, + "help": "Post URL or LessWrong post ID" + }, + { + "name": "limit", + "type": "int", + "default": 5, + "required": false, + "help": "Number of comments" + } + ], + "columns": [ + "rank", + "score", + "author", + "text" + ], + "type": "js", + "modulePath": "lesswrong/comments.js", + "sourceFile": "lesswrong/comments.js" + }, + { + "site": "lesswrong", + "name": "curated", + "description": "Curated editor's picks", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "lesswrong/curated.js", + "sourceFile": "lesswrong/curated.js" + }, + { + "site": "lesswrong", + "name": "frontpage", + "description": "Algorithmic frontpage", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "lesswrong/frontpage.js", + "sourceFile": "lesswrong/frontpage.js" + }, + { + "site": "lesswrong", + "name": "new", + "description": "Latest posts", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "lesswrong/new.js", + "sourceFile": "lesswrong/new.js" + }, + { + "site": "lesswrong", + "name": "read", + "description": "Read full post by URL or ID", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "url-or-id", + "type": "string", + "required": true, + "positional": true, + "help": "Post URL or LessWrong post ID" + } + ], + "columns": [ + "title", + "author", + "karma", + "comments", + "tags", + "content", + "url" + ], + "type": "js", + "modulePath": "lesswrong/read.js", + "sourceFile": "lesswrong/read.js" + }, + { + "site": "lesswrong", + "name": "sequences", + "description": "List post collections", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author" + ], + "type": "js", + "modulePath": "lesswrong/sequences.js", + "sourceFile": "lesswrong/sequences.js" + }, + { + "site": "lesswrong", + "name": "shortform", + "description": "Quick takes / shortform posts", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "lesswrong/shortform.js", + "sourceFile": "lesswrong/shortform.js" + }, + { + "site": "lesswrong", + "name": "tag", + "description": "Posts by tag", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "tag", + "type": "string", + "required": true, + "positional": true, + "help": "Tag slug or name" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "lesswrong/tag.js", + "sourceFile": "lesswrong/tag.js" + }, + { + "site": "lesswrong", + "name": "tags", + "description": "List popular tags", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "name", + "posts" + ], + "type": "js", + "modulePath": "lesswrong/tags.js", + "sourceFile": "lesswrong/tags.js" + }, + { + "site": "lesswrong", + "name": "top", + "description": "Top all-time", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "lesswrong/top.js", + "sourceFile": "lesswrong/top.js" + }, + { + "site": "lesswrong", + "name": "top-month", + "description": "Top this month", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "lesswrong/top-month.js", + "sourceFile": "lesswrong/top-month.js" + }, + { + "site": "lesswrong", + "name": "top-week", + "description": "Top this week", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "lesswrong/top-week.js", + "sourceFile": "lesswrong/top-week.js" + }, + { + "site": "lesswrong", + "name": "top-year", + "description": "Top this year", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "lesswrong/top-year.js", + "sourceFile": "lesswrong/top-year.js" + }, + { + "site": "lesswrong", + "name": "user", + "description": "User profile", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "LessWrong username or slug" + } + ], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "lesswrong/user.js", + "sourceFile": "lesswrong/user.js" + }, + { + "site": "lesswrong", + "name": "user-posts", + "description": "List a user's posts", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "LessWrong username or slug" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "karma", + "comments", + "date", + "url" + ], + "type": "js", + "modulePath": "lesswrong/user-posts.js", + "sourceFile": "lesswrong/user-posts.js" + }, + { + "site": "lichess", + "name": "top", + "description": "Top-N Lichess leaderboard for a perf type (bullet/blitz/rapid/classical/...)", + "access": "read", + "domain": "lichess.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "perf", + "type": "str", + "required": true, + "positional": true, + "help": "Perf type (bullet, blitz, rapid, classical, ultraBullet, chess960, ...)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Top-N rows (1-200)" + } + ], + "columns": [ + "rank", + "username", + "id", + "title", + "rating", + "progress", + "patron", + "url" + ], + "type": "js", + "modulePath": "lichess/top.js", + "sourceFile": "lichess/top.js" + }, + { + "site": "lichess", + "name": "user", + "description": "Fetch a Lichess player profile by username (rating, perfs, counts)", + "access": "read", + "domain": "lichess.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Lichess username (case-insensitive)" + } + ], + "columns": [ + "username", + "id", + "title", + "patron", + "online", + "tosViolation", + "createdAt", + "seenAt", + "gamesAll", + "gamesWin", + "gamesLoss", + "gamesDraw", + "topPerfName", + "topPerfRating", + "topPerfGames", + "fideRating", + "country", + "bio", + "url" + ], + "type": "js", + "modulePath": "lichess/user.js", + "sourceFile": "lichess/user.js" + }, + { + "site": "linkedin-learning", + "name": "course", + "description": "Get LinkedIn Learning course detail by slug or course URL", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "slug", + "type": "string", + "required": true, + "positional": true, + "help": "Course slug (e.g. agentic-ai-build-your-first-agentic-ai-system) or full /learning/ URL" + } + ], + "columns": [ + "title", + "slug", + "description", + "difficulty", + "duration_sec", + "videos_count", + "rating", + "rating_count", + "released", + "url" + ], + "type": "js", + "modulePath": "linkedin-learning/course.js", + "sourceFile": "linkedin-learning/course.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin-learning", + "name": "login", + "description": "Open linkedin-learning login", + "access": "write", + "domain": "linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "public_id", + "plain_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "linkedin-learning/auth.js", + "sourceFile": "linkedin-learning/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "linkedin-learning", + "name": "search", + "description": "Search LinkedIn Learning courses, videos, and learning paths by keyword", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "keywords", + "type": "string", + "required": true, + "positional": true, + "help": "Search keywords, e.g. \"AI agent\"" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum results to return (1-50)" + } + ], + "columns": [ + "rank", + "type", + "title", + "instructor", + "difficulty", + "duration_sec", + "rating", + "rating_count", + "viewers", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "linkedin-learning/search.js", + "sourceFile": "linkedin-learning/search.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin-learning", + "name": "trending", + "description": "Browse LinkedIn Learning recommended courses across personalized carousels", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum results to return (1-50)" + } + ], + "columns": [ + "rank", + "group", + "type", + "title", + "difficulty", + "viewers", + "url" + ], + "type": "js", + "modulePath": "linkedin-learning/trending.js", + "sourceFile": "linkedin-learning/trending.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin-learning", + "name": "whoami", + "description": "Show the current logged-in linkedin-learning account", + "access": "read", + "domain": "linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "public_id", + "plain_id", + "name" + ], + "type": "js", + "modulePath": "linkedin-learning/auth.js", + "sourceFile": "linkedin-learning/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "lobsters", + "name": "active", + "description": "Lobste.rs most active discussions", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "lobsters/active.js", + "sourceFile": "lobsters/active.js" + }, + { + "site": "lobsters", + "name": "domain", + "description": "Lobste.rs stories submitted from a specific domain", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "domain", + "type": "str", + "required": true, + "positional": true, + "help": "Source domain (e.g. github.com, arxiv.org, blog.cloudflare.com)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories (1-25 — single page)" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "submission_url", + "comments_url" + ], + "type": "js", + "modulePath": "lobsters/domain.js", + "sourceFile": "lobsters/domain.js" + }, + { + "site": "lobsters", + "name": "hot", + "description": "Lobste.rs hottest stories", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "lobsters/hot.js", + "sourceFile": "lobsters/hot.js" + }, + { + "site": "lobsters", + "name": "newest", + "description": "Lobste.rs newest stories", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "lobsters/newest.js", + "sourceFile": "lobsters/newest.js" + }, + { + "site": "lobsters", + "name": "read", + "description": "Read a Lobste.rs story and its comment tree", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Lobste.rs short_id (e.g. 6cmh6h)" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max top-level comments" + }, + { + "name": "depth", + "type": "int", + "default": 2, + "required": false, + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + }, + { + "name": "replies", + "type": "int", + "default": 5, + "required": false, + "help": "Max replies shown per comment at each level" + }, + { + "name": "max-length", + "type": "int", + "default": 2000, + "required": false, + "help": "Max characters per comment body (min 100)" + } + ], + "columns": [ + "type", + "author", + "score", + "text" + ], + "type": "js", + "modulePath": "lobsters/read.js", + "sourceFile": "lobsters/read.js" + }, + { + "site": "lobsters", + "name": "tag", + "description": "Lobste.rs stories by tag", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "tag", + "type": "str", + "required": true, + "positional": true, + "help": "Tag name (e.g. programming, rust, security, ai)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "lobsters/tag.js", + "sourceFile": "lobsters/tag.js" + }, + { + "site": "manus", + "name": "connectors", + "description": "List available Manus connectors (integrations).", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max connectors to return" + } + ], + "columns": [ + "UID", + "Name", + "Brief" + ], + "type": "js", + "modulePath": "manus/connectors.js", + "sourceFile": "manus/connectors.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "credits", + "description": "Show Manus credit balance and refresh details.", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "manus/credits.js", + "sourceFile": "manus/credits.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "list", + "description": "List Manus sessions (tasks).", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max sessions to return" + }, + { + "name": "archived", + "type": "bool", + "default": false, + "required": false, + "help": "Include archived sessions" + } + ], + "columns": [ + "id", + "Title", + "Status", + "Last Message", + "Last Updated", + "Credits" + ], + "type": "js", + "modulePath": "manus/list.js", + "sourceFile": "manus/list.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "login", + "description": "Open manus login", + "access": "write", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "manus/auth.js", + "sourceFile": "manus/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "read", + "description": "Show details for a specific Manus session.", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "uid", + "type": "str", + "required": true, + "positional": true, + "help": "Session UID" + } + ], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "manus/read.js", + "sourceFile": "manus/read.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "skills", + "description": "List Manus skills (user-added and system).", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "ID", + "Name", + "Description", + "Source" + ], + "type": "js", + "modulePath": "manus/skills.js", + "sourceFile": "manus/skills.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "status", + "description": "Show current Manus user profile and credit summary.", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "manus/status.js", + "sourceFile": "manus/status.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "whoami", + "description": "Show the current logged-in manus account", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" + ], + "type": "js", + "modulePath": "manus/auth.js", + "sourceFile": "manus/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "maven", + "name": "artifact", + "description": "Fetch a Maven Central artifact's version history (groupId:artifactId[:version])", + "access": "read", + "domain": "search.maven.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "coordinate", + "type": "str", + "required": true, + "positional": true, + "help": "Maven coord \"groupId:artifactId\" or \"groupId:artifactId:version\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max versions (1-200, ignored when version is pinned)" + } + ], + "columns": [ + "groupId", + "artifactId", + "version", + "packaging", + "publishedAt", + "tags", + "url" + ], + "type": "js", + "modulePath": "maven/artifact.js", + "sourceFile": "maven/artifact.js" + }, + { + "site": "maven", + "name": "search", + "description": "Search Maven Central by keyword (artifact name, groupId, tag)", + "access": "read", + "domain": "search.maven.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"jackson\", \"guava\", \"ai.koog\")" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max artifacts (1-200)" + } + ], + "columns": [ + "rank", + "coordinate", + "groupId", + "artifactId", + "latestVersion", + "packaging", + "versions", + "lastPublished", + "repository", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "maven/search.js", + "sourceFile": "maven/search.js" + }, + { + "site": "mdn", + "name": "search", + "description": "Search MDN Web Docs by keyword", + "access": "read", + "domain": "developer.mozilla.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"fetch\", \"flexbox\", \"Array.prototype.map\")" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results (1-50)" + }, + { + "name": "locale", + "type": "str", + "default": "en-US", + "required": false, + "help": "Doc locale (en-US default; de / es / fr / ja / ko / pt-BR / ru / zh-CN / zh-TW)" + } + ], + "columns": [ + "rank", + "title", + "slug", + "locale", + "summary", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "mdn/search.js", + "sourceFile": "mdn/search.js" + }, + { + "site": "medium", + "name": "feed", + "description": "Medium popular posts Feed", + "access": "read", + "domain": "medium.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "topic", + "type": "str", + "default": "", + "required": false, + "help": "Topic (for example technology, programming, ai)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" + } + ], + "columns": [ + "rank", + "title", + "author", + "date", + "readTime", + "claps" + ], + "type": "js", + "modulePath": "medium/feed.js", + "sourceFile": "medium/feed.js", + "navigateBefore": "https://medium.com" + }, + { + "site": "medium", + "name": "search", + "description": "Search Medium posts", + "access": "read", + "domain": "medium.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" + } + ], + "columns": [ + "rank", + "title", + "author", + "date", + "readTime", + "claps", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "medium/search.js", + "sourceFile": "medium/search.js", + "navigateBefore": "https://medium.com" + }, + { + "site": "medium", + "name": "tag", + "description": "Latest Medium articles tagged with a given keyword (RSS feed)", + "access": "read", + "domain": "medium.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "tag", + "type": "str", + "required": true, + "positional": true, + "help": "Lowercase tag slug (e.g. \"programming\", \"machine-learning\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max articles (1-25 — single RSS page)" + } + ], + "columns": [ + "rank", + "title", + "author", + "description", + "categories", + "published", + "url" + ], + "type": "js", + "modulePath": "medium/tag.js", + "sourceFile": "medium/tag.js" + }, + { + "site": "medium", + "name": "user", + "description": "Get Medium user posts", + "access": "read", + "domain": "medium.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Medium username(for example @username or username)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" + } + ], + "columns": [ + "rank", + "title", + "date", + "readTime", + "claps", + "url" + ], + "type": "js", + "modulePath": "medium/user.js", + "sourceFile": "medium/user.js", + "navigateBefore": "https://medium.com" + }, + { + "site": "mercury", + "name": "check-login", + "description": "Open Mercury reimbursements and report whether the active browser profile is logged in", + "access": "read", + "example": "webcmd --profile mercury check-login -f json", + "domain": "app.mercury.com", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "status", + "loggedIn", + "url", + "hasSubmitExpense", + "hasReimbursements", + "title" + ], + "type": "js", + "modulePath": "mercury/check-login.js", + "sourceFile": "mercury/check-login.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "mercury", + "name": "reimbursement-draft", + "description": "Create a Mercury reimbursement draft from a local receipt, correct OCR fields, and stop at Review", + "access": "write", + "example": "webcmd --profile mercury reimbursement-draft --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", + "domain": "app.mercury.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "receipt", + "type": "str", + "required": true, + "help": "Local receipt/proof file path", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp", + "application/pdf" + ], + "maxBytes": 26214400 + } + }, + { + "name": "amount", + "type": "str", + "required": true, + "help": "Original-currency amount, e.g. 140.00" + }, + { + "name": "currency", + "type": "str", + "default": "CNY", + "required": false, + "help": "Original currency code" + }, + { + "name": "date", + "type": "str", + "required": true, + "help": "Expense date as YYYY-MM-DD" + }, + { + "name": "merchant", + "type": "str", + "required": true, + "help": "Merchant shown on the reimbursement" + }, + { + "name": "category", + "type": "str", + "default": "Marketing & Advertising", + "required": false, + "help": "Mercury expense category" + }, + { + "name": "notes", + "type": "str", + "required": true, + "help": "Business purpose / reimbursement notes" + }, + { + "name": "ocr-wait-seconds", + "type": "str", + "default": "8", + "required": false, + "help": "Seconds to wait after receipt upload before correcting OCR-overwritten fields" + }, + { + "name": "close-after-review", + "type": "boolean", + "default": false, + "required": false, + "help": "Close the Review dialog after verification; final Submit is still never clicked" + } + ], + "columns": [ + "status", + "url", + "receipt", + "uploaded", + "fieldsTouched", + "reviewReady", + "submitBlocked", + "warnings" + ], + "type": "js", + "modulePath": "mercury/reimbursement-draft.js", + "sourceFile": "mercury/reimbursement-draft.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "mercury", + "name": "reimbursement-plan", + "description": "Validate Mercury reimbursement inputs and print the draft plan without opening a browser", + "access": "read", + "example": "webcmd mercury reimbursement-plan --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "receipt", + "type": "str", + "required": true, + "help": "Local receipt/proof file path" + }, + { + "name": "amount", + "type": "str", + "required": true, + "help": "Original-currency amount, e.g. 140.00" + }, + { + "name": "currency", + "type": "str", + "default": "CNY", + "required": false, + "help": "Original currency code" + }, + { + "name": "date", + "type": "str", + "required": true, + "help": "Expense date as YYYY-MM-DD" + }, + { + "name": "merchant", + "type": "str", + "required": true, + "help": "Merchant shown on the reimbursement" + }, + { + "name": "category", + "type": "str", + "default": "Marketing & Advertising", + "required": false, + "help": "Mercury expense category" + }, + { + "name": "notes", + "type": "str", + "required": true, + "help": "Business purpose / reimbursement notes" + }, + { + "name": "ocr-wait-seconds", + "type": "str", + "default": "8", + "required": false, + "help": "Seconds the draft command waits after receipt upload before correcting OCR fields" + }, + { + "name": "close-after-review", + "type": "boolean", + "default": false, + "required": false, + "help": "For draft command: close the Review dialog after verification" + } + ], + "columns": [ + "status", + "receipt", + "amount", + "currency", + "date", + "merchant", + "category", + "notes", + "safety" + ], + "type": "js", + "modulePath": "mercury/reimbursement-plan.js", + "sourceFile": "mercury/reimbursement-plan.js" + }, + { + "site": "notebooklm", + "name": "add-source", + "description": "Add a URL, text, or local file source to an existing NotebookLM notebook", + "access": "write", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "notebook", + "type": "str", + "required": true, + "positional": true, + "help": "Notebook id from `notebooklm list` or full notebook URL" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Source URL to add (http/https). Pass exactly one of --url, --content, --file." + }, + { + "name": "content", + "type": "str", + "required": false, + "help": "Raw text content to add as a Text source (max 10 MB)." + }, + { + "name": "file", + "type": "str", + "required": false, + "help": "Local file path to upload as a source (max 52428800 bytes; pdf / txt / md / html / docx / etc.). Uses Google Drive's 3-step resumable upload protocol." + }, + { + "name": "title", + "type": "str", + "required": false, + "help": "Title for the text source (default \"Text Source\"). Ignored for --url and --file." + }, + { + "name": "mime-type", + "type": "str", + "required": false, + "help": "Override the auto-detected MIME type when --file is given." + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually add the remote source to the NotebookLM notebook" + } + ], + "columns": [ + "notebook_id", + "source_id", + "kind", + "identifier", + "notebook_url" + ], + "type": "js", + "modulePath": "notebooklm/add-source.js", + "sourceFile": "notebooklm/add-source.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "create", + "description": "Create a new NotebookLM notebook with the given title", + "access": "write", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "title", + "type": "str", + "required": true, + "positional": true, + "help": "Notebook title (1-200 chars)" + }, + { + "name": "emoji", + "type": "str", + "required": false, + "help": "Notebook emoji icon (default 📒)" + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually create the remote NotebookLM notebook" + } + ], + "columns": [ + "id", + "title", + "emoji", + "url" + ], + "type": "js", + "modulePath": "notebooklm/create.js", + "sourceFile": "notebooklm/create.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "current", + "description": "Show metadata for the currently opened NotebookLM notebook tab", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "id", + "title", + "url", + "source" + ], + "type": "js", + "modulePath": "notebooklm/current.js", + "sourceFile": "notebooklm/current.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "generate-audio", + "description": "Trigger an Audio Overview (Deep Dive podcast) generation for a NotebookLM notebook, using all of its sources", + "access": "write", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "notebook", + "type": "str", + "required": true, + "positional": true, + "help": "Notebook id from `notebooklm list` or full notebook URL" + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually trigger remote NotebookLM audio generation" + } + ], + "columns": [ + "notebook_id", + "audio_id", + "source_count", + "status", + "notebook_url" + ], + "type": "js", + "modulePath": "notebooklm/generate-audio.js", + "sourceFile": "notebooklm/generate-audio.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "generate-slides", + "description": "Trigger a Slide Deck (AI presentation) generation for a NotebookLM notebook, using all of its sources", + "access": "write", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "notebook", + "type": "str", + "required": true, + "positional": true, + "help": "Notebook id from `notebooklm list` or full notebook URL" + }, + { + "name": "length", + "type": "str", + "required": false, + "help": "Slide deck length: 1=Short, 3=Default (default 3)" + }, + { + "name": "language", + "type": "str", + "required": false, + "help": "Language code (default en)" + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually trigger remote NotebookLM slide deck generation" + } + ], + "columns": [ + "notebook_id", + "slides_id", + "source_count", + "status", + "notebook_url" + ], + "type": "js", + "modulePath": "notebooklm/generate-slides.js", + "sourceFile": "notebooklm/generate-slides.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "get", + "aliases": [ + "metadata" + ], + "description": "Get rich metadata for the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "id", + "title", + "emoji", + "source_count", + "created_at", + "updated_at", + "url", + "source" + ], + "type": "js", + "modulePath": "notebooklm/get.js", + "sourceFile": "notebooklm/get.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "history", + "description": "List NotebookLM conversation history threads in the current notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "thread_id", + "item_count", + "preview", + "source", + "notebook_id", + "url" + ], + "type": "js", + "modulePath": "notebooklm/history.js", + "sourceFile": "notebooklm/history.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "list", + "description": "List NotebookLM notebooks via in-page batchexecute RPC in the current logged-in session", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "title", + "id", + "is_owner", + "created_at", + "source", + "url" + ], + "type": "js", + "modulePath": "notebooklm/list.js", + "sourceFile": "notebooklm/list.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "login", + "description": "Open notebooklm login", + "access": "write", + "domain": "google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "authuser", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "notebooklm/auth.js", + "sourceFile": "notebooklm/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "notebooklm", + "name": "note-list", + "aliases": [ + "notes-list" + ], + "description": "List saved notes from the Studio panel of the current NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "title", + "created_at", + "source", + "url" + ], + "type": "js", + "modulePath": "notebooklm/note-list.js", + "sourceFile": "notebooklm/note-list.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "notes-get", + "description": "Get one note from the current NotebookLM notebook by title from the visible note editor", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "note", + "type": "str", + "required": true, + "positional": true, + "help": "Note title or id from the current notebook" + } + ], + "columns": [ + "title", + "content", + "source", + "url" + ], + "type": "js", + "modulePath": "notebooklm/notes-get.js", + "sourceFile": "notebooklm/notes-get.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "open", + "aliases": [ + "select" + ], + "description": "Open one NotebookLM notebook in the adapter session by id or URL", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "notebook", + "type": "str", + "required": true, + "positional": true, + "help": "Notebook id from list output, or a full NotebookLM notebook URL" + } + ], + "columns": [ + "id", + "title", + "url", + "source" + ], + "type": "js", + "modulePath": "notebooklm/open.js", + "sourceFile": "notebooklm/open.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "source-fulltext", + "description": "Get the extracted fulltext for one source in the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "source", + "type": "str", + "required": true, + "positional": true, + "help": "Source id or title from the current notebook" + } + ], + "columns": [ + "title", + "kind", + "char_count", + "url", + "source" + ], + "type": "js", + "modulePath": "notebooklm/source-fulltext.js", + "sourceFile": "notebooklm/source-fulltext.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "source-get", + "description": "Get one source from the currently opened NotebookLM notebook by id or title", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "source", + "type": "str", + "required": true, + "positional": true, + "help": "Source id or title from the current notebook" + } + ], + "columns": [ + "title", + "id", + "type", + "size", + "created_at", + "updated_at", + "url", + "source" + ], + "type": "js", + "modulePath": "notebooklm/source-get.js", + "sourceFile": "notebooklm/source-get.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "source-guide", + "description": "Get the guide summary and keywords for one source in the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "source", + "type": "str", + "required": true, + "positional": true, + "help": "Source id or title from the current notebook" + } + ], + "columns": [ + "source_id", + "notebook_id", + "title", + "type", + "summary", + "keywords", + "source" + ], + "type": "js", + "modulePath": "notebooklm/source-guide.js", + "sourceFile": "notebooklm/source-guide.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "source-list", + "description": "List sources for the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "title", + "id", + "type", + "size", + "created_at", + "updated_at", + "url", + "source" + ], + "type": "js", + "modulePath": "notebooklm/source-list.js", + "sourceFile": "notebooklm/source-list.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "status", + "description": "Check NotebookLM page availability and login state in the current Chrome session", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "login", + "page", + "url", + "title", + "notebooks" + ], + "type": "js", + "modulePath": "notebooklm/status.js", + "sourceFile": "notebooklm/status.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "summary", + "description": "Get the summary block from the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "title", + "summary", + "source", + "url" + ], + "type": "js", + "modulePath": "notebooklm/summary.js", + "sourceFile": "notebooklm/summary.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "whoami", + "description": "Show the current logged-in notebooklm account", + "access": "read", + "domain": "google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name", + "authuser" + ], + "type": "js", + "modulePath": "notebooklm/auth.js", + "sourceFile": "notebooklm/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "notebooklm", + "name": "write-note", + "description": "Create a Studio note in an existing NotebookLM notebook with the given title and Markdown content", + "access": "write", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "notebook", + "type": "str", + "required": true, + "positional": true, + "help": "Notebook id from `notebooklm list` or full notebook URL" + }, + { + "name": "title", + "type": "str", + "required": true, + "help": "Note title (1-200 chars)" + }, + { + "name": "content", + "type": "str", + "required": true, + "help": "Note body as Markdown" + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually create the remote NotebookLM note" + } + ], + "columns": [ + "notebook_id", + "note_id", + "title", + "notebook_url" + ], + "type": "js", + "modulePath": "notebooklm/write-note.js", + "sourceFile": "notebooklm/write-note.js", + "navigateBefore": false + }, + { + "site": "npm", + "name": "downloads", + "description": "Daily download counts for an npm package over a window", + "access": "read", + "domain": "api.npmjs.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "npm package name (e.g. \"react\", \"@vercel/og\")" + }, + { + "name": "period", + "type": "str", + "default": "last-week", + "required": false, + "help": "last-day / last-week / last-month / last-year, or YYYY-MM-DD:YYYY-MM-DD" + } + ], + "columns": [ + "rank", + "package", + "day", + "downloads" + ], + "type": "js", + "modulePath": "npm/downloads.js", + "sourceFile": "npm/downloads.js" + }, + { + "site": "npm", + "name": "package", + "description": "Single npm package metadata (latest version, license, homepage, repository). Use `npm downloads` for stats.", + "access": "read", + "domain": "registry.npmjs.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "npm package name (e.g. \"react\", \"@vercel/og\")" + } + ], + "columns": [ + "name", + "latestVersion", + "description", + "license", + "homepage", + "repository", + "bugs", + "maintainers", + "keywords", + "created", + "modified", + "url" + ], + "type": "js", + "modulePath": "npm/package.js", + "sourceFile": "npm/package.js" + }, + { + "site": "npm", + "name": "search", + "description": "Search the public npm registry by keyword", + "access": "read", + "domain": "registry.npmjs.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"react\", \"graphql client\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-250)" + } + ], + "columns": [ + "rank", + "name", + "version", + "description", + "weeklyDownloads", + "dependents", + "license", + "publisher", + "updated", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "npm/search.js", + "sourceFile": "npm/search.js" + }, + { + "site": "nuget", + "name": "package", + "description": "Full NuGet package version history (catalogEntry per release)", + "access": "read", + "domain": "api.nuget.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "NuGet package id (e.g. \"Newtonsoft.Json\", case-insensitive)" + } + ], + "columns": [ + "rank", + "id", + "version", + "title", + "authors", + "tags", + "language", + "licenseExpression", + "projectUrl", + "published", + "listed", + "url" + ], + "type": "js", + "modulePath": "nuget/package.js", + "sourceFile": "nuget/package.js" + }, + { + "site": "nuget", + "name": "search", + "description": "Search NuGet packages by keyword", + "access": "read", + "domain": "api.nuget.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max packages (1-1000)" + }, + { + "name": "prerelease", + "type": "boolean", + "default": false, + "required": false, + "help": "Include prerelease versions" + } + ], + "columns": [ + "rank", + "id", + "version", + "title", + "description", + "authors", + "tags", + "totalDownloads", + "verified", + "projectUrl", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "nuget/search.js", + "sourceFile": "nuget/search.js" + }, + { + "site": "nvd", + "name": "cve", + "description": "NIST NVD CVE detail (description, CVSS, CWE, KEV flag)", + "access": "read", + "domain": "services.nvd.nist.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "CVE identifier (e.g. \"CVE-2021-44228\")" + } + ], + "columns": [ + "id", + "published", + "lastModified", + "vulnStatus", + "baseScore", + "severity", + "attackVector", + "cwe", + "kevAdded", + "description", + "url" + ], + "type": "js", + "modulePath": "nvd/cve.js", + "sourceFile": "nvd/cve.js" + }, + { + "site": "oeis", + "name": "search", + "description": "Search OEIS sequences by keyword or numeric pattern", + "access": "read", + "domain": "oeis.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword or comma-separated terms (e.g. \"fibonacci\", \"1,1,2,3,5,8\")" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max sequences (1-100)" + } + ], + "columns": [ + "rank", + "id", + "name", + "keywords", + "preview", + "author", + "created", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "oeis/search.js", + "sourceFile": "oeis/search.js" + }, + { + "site": "oeis", + "name": "sequence", + "description": "Full OEIS sequence detail by A-number (terms, name, keywords, formula counts)", + "access": "read", + "domain": "oeis.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "OEIS sequence id (e.g. \"A000045\" for Fibonacci)" + } + ], + "columns": [ + "id", + "name", + "keywords", + "preview", + "termCount", + "offset", + "author", + "created", + "revision", + "commentCount", + "formulaCount", + "referenceCount", + "xrefCount", + "linkCount", + "url" + ], + "type": "js", + "modulePath": "oeis/sequence.js", + "sourceFile": "oeis/sequence.js" + }, + { + "site": "openalex", + "name": "search", + "description": "Search OpenAlex Works (papers, books, preprints) by keyword", + "access": "read", + "domain": "api.openalex.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search text (e.g. \"transformers\", \"open access scholarly\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max works (1-200, single OpenAlex page)" + } + ], + "columns": [ + "rank", + "id", + "title", + "year", + "citations", + "firstAuthor", + "venue", + "openAccess", + "type", + "doi", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "openalex/search.js", + "sourceFile": "openalex/search.js" + }, + { + "site": "openalex", + "name": "work", + "description": "Fetch a single OpenAlex Work (paper / preprint / book) — metadata + abstract", + "access": "read", + "domain": "api.openalex.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "OpenAlex Work id (\"W2741809807\"), DOI (\"10.7717/peerj.4375\"), or full URL" + } + ], + "columns": [ + "id", + "title", + "type", + "year", + "date", + "language", + "authors", + "venue", + "citations", + "openAccess", + "openAccessUrl", + "referencedCount", + "doi", + "abstract", + "url" + ], + "type": "js", + "modulePath": "openalex/work.js", + "sourceFile": "openalex/work.js" + }, + { + "site": "openfda", + "name": "drug-label", + "description": "Search FDA-approved drug labels (brand or generic name)", + "access": "read", + "domain": "fda.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Brand or generic drug name (e.g. \"aspirin\", \"lisinopril\")" + }, + { + "name": "limit", + "type": "int", + "default": 5, + "required": false, + "help": "Max rows (1-25, default 5; openFDA caps anonymous tier at 25/page)" + } + ], + "columns": [ + "rank", + "id", + "brandName", + "genericName", + "manufacturer", + "productType", + "route", + "productNdc", + "pharmClass", + "purpose", + "indications", + "warnings", + "dosage", + "effectiveTime" + ], + "type": "js", + "modulePath": "openfda/drug-label.js", + "sourceFile": "openfda/drug-label.js" + }, + { + "site": "openfda", + "name": "food-recall", + "description": "FDA food recall and enforcement actions (most recent first)", + "access": "read", + "domain": "fda.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": false, + "help": "Free-text Lucene query (e.g. \"salmonella\", \"listeria\"); default: all recent recalls" + }, + { + "name": "status", + "type": "str", + "required": false, + "help": "Filter by status: \"Ongoing\", \"Completed\", \"Terminated\"" + }, + { + "name": "classification", + "type": "str", + "required": false, + "help": "Filter by class: \"Class I\" (most serious), \"Class II\", \"Class III\"" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max rows (1-100, default 10; openFDA caps anonymous tier at 100/page)" + } + ], + "columns": [ + "rank", + "recallNumber", + "status", + "classification", + "voluntary", + "recallingFirm", + "city", + "state", + "country", + "productDescription", + "reasonForRecall", + "productQuantity", + "distributionPattern", + "reportDate", + "recallInitiationDate", + "terminationDate" + ], + "type": "js", + "modulePath": "openfda/food-recall.js", + "sourceFile": "openfda/food-recall.js" + }, + { + "site": "openreview", + "name": "author", + "description": "List OpenReview submissions by an author profile id (newest first)", + "access": "read", + "domain": "openreview.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "profile", + "type": "str", + "required": true, + "positional": true, + "help": "OpenReview profile id (e.g. \"~Yoshua_Bengio1\"). Find it on the author profile URL on openreview.net." + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max submissions (1-1000)" + } + ], + "columns": [ + "rank", + "id", + "title", + "authors", + "venue", + "pdate", + "url" + ], + "type": "js", + "modulePath": "openreview/author.js", + "sourceFile": "openreview/author.js" + }, + { + "site": "openreview", + "name": "paper", + "description": "Show full metadata for a single OpenReview paper", + "access": "read", + "domain": "openreview.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "OpenReview note id (e.g. \"5sRnsubyAK\")" + } + ], + "columns": [ + "id", + "title", + "authors", + "keywords", + "venue", + "venueid", + "primary_area", + "abstract", + "pdate", + "pdf", + "url" + ], + "type": "js", + "modulePath": "openreview/paper.js", + "sourceFile": "openreview/paper.js" + }, + { + "site": "openreview", + "name": "reviews", + "description": "Show full review thread (paper + reviews + decisions) for an OpenReview forum", + "access": "read", + "domain": "openreview.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "forum", + "type": "str", + "required": true, + "positional": true, + "help": "OpenReview forum id (same as paper id)" + }, + { + "name": "max-length", + "type": "int", + "default": 4000, + "required": false, + "help": "Per-row text truncation (min 200)" + } + ], + "columns": [ + "type", + "author", + "rating", + "confidence", + "text" + ], + "type": "js", + "modulePath": "openreview/reviews.js", + "sourceFile": "openreview/reviews.js" + }, + { + "site": "openreview", + "name": "search", + "description": "Search OpenReview papers by free-text query", + "access": "read", + "domain": "openreview.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"diffusion model\")" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max results (max 50)" + } + ], + "columns": [ + "rank", + "id", + "title", + "authors", + "venue", + "pdate", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "openreview/search.js", + "sourceFile": "openreview/search.js" + }, + { + "site": "openreview", + "name": "venue", + "description": "List papers at an OpenReview venue (e.g. \"ICLR 2024 oral\" or full invitation id)", + "access": "read", + "domain": "openreview.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "venue", + "type": "str", + "required": true, + "positional": true, + "help": "Venue name (\"ICLR 2024 oral\") or invitation (\"ICLR.cc/2025/Conference/-/Submission\")" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max results (max 200)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Pagination offset" + } + ], + "columns": [ + "rank", + "id", + "title", + "authors", + "keywords", + "primary_area", + "pdate", + "pdf", + "url" + ], + "type": "js", + "modulePath": "openreview/venue.js", + "sourceFile": "openreview/venue.js" + }, + { + "site": "osv", + "name": "query", + "description": "OSV.dev vulnerabilities affecting a package (optionally pinned to a version)", + "access": "read", + "domain": "osv.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "package", + "type": "string", + "required": true, + "positional": true, + "help": "Package name (e.g. \"lodash\", \"django\")" + }, + { + "name": "ecosystem", + "type": "string", + "required": true, + "help": "OSV ecosystem (npm / PyPI / Go / Maven / NuGet / RubyGems / crates.io / Packagist / ...)" + }, + { + "name": "version", + "type": "string", + "required": false, + "help": "Pin to a specific version (e.g. \"4.17.20\"); omit for all known vulns" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max rows to return (1-200)" + } + ], + "columns": [ + "rank", + "id", + "summary", + "severity", + "aliases", + "published", + "modified", + "affectedPackages", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "osv/query.js", + "sourceFile": "osv/query.js" + }, + { + "site": "osv", + "name": "vulnerability", + "description": "Single OSV.dev vulnerability detail (severity, affected packages, CVE/GHSA aliases)", + "access": "read", + "domain": "osv.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "OSV vulnerability id (e.g. \"GHSA-29mw-wpgm-hmr9\", \"CVE-2020-28500\")" + } + ], + "columns": [ + "id", + "summary", + "severity", + "aliases", + "published", + "modified", + "affectedPackages", + "cwes", + "referenceCount", + "url" + ], + "type": "js", + "modulePath": "osv/vulnerability.js", + "sourceFile": "osv/vulnerability.js" + }, + { + "site": "packagist", + "name": "package", + "description": "Fetch a Packagist package's metadata (version, downloads, license, repo, GitHub stars)", + "access": "read", + "domain": "packagist.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Composer package \"/\" (e.g. \"symfony/console\", \"monolog/monolog\")" + } + ], + "columns": [ + "package", + "version", + "releasedAt", + "license", + "description", + "repository", + "githubStars", + "favers", + "downloads", + "monthlyDownloads", + "dailyDownloads", + "url" + ], + "type": "js", + "modulePath": "packagist/package.js", + "sourceFile": "packagist/package.js" + }, + { + "site": "packagist", + "name": "search", + "description": "Search Packagist (PHP / Composer) packages by keyword", + "access": "read", + "domain": "packagist.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"symfony\", \"laravel http\")" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max packages (1-100, single Packagist page)" + } + ], + "columns": [ + "rank", + "package", + "description", + "downloads", + "favers", + "repository", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "packagist/search.js", + "sourceFile": "packagist/search.js" + }, + { + "site": "paperreview", + "name": "feedback", + "description": "Submit feedback for a paperreview.ai review token", + "access": "write", + "domain": "paperreview.ai", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "token", + "type": "str", + "required": true, + "positional": true, + "help": "Review token returned by paperreview.ai" + }, + { + "name": "helpfulness", + "type": "int", + "required": true, + "help": "Helpfulness score from 1 to 5" + }, + { + "name": "critical-error", + "type": "str", + "required": true, + "help": "Whether the review contains a critical error", + "choices": [ + "yes", + "no" + ] + }, + { + "name": "actionable-suggestions", + "type": "str", + "required": true, + "help": "Whether the review contains actionable suggestions", + "choices": [ + "yes", + "no" + ] + }, + { + "name": "additional-comments", + "type": "str", + "required": false, + "help": "Optional free-text feedback" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds for the overall command (default: 30)" + } + ], + "columns": [ + "status", + "token", + "helpfulness", + "critical_error", + "actionable_suggestions", + "message" + ], + "type": "js", + "modulePath": "paperreview/feedback.js", + "sourceFile": "paperreview/feedback.js" + }, + { + "site": "paperreview", + "name": "review", + "description": "Fetch a paperreview.ai review by token", + "access": "read", + "domain": "paperreview.ai", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "token", + "type": "str", + "required": true, + "positional": true, + "help": "Review token returned by paperreview.ai" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds for the overall command (default: 30)" + } + ], + "columns": [ + "status", + "title", + "venue", + "numerical_score", + "has_feedback", + "review_url" + ], + "type": "js", + "modulePath": "paperreview/review.js", + "sourceFile": "paperreview/review.js" + }, + { + "site": "paperreview", + "name": "submit", + "description": "Submit a PDF to paperreview.ai for review", + "access": "write", + "domain": "paperreview.ai", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "pdf", + "type": "str", + "required": true, + "positional": true, + "help": "Path to the paper PDF" + }, + { + "name": "email", + "type": "str", + "required": true, + "help": "Email address for the submission" + }, + { + "name": "venue", + "type": "str", + "required": false, + "help": "Optional target venue such as ICLR or NeurIPS" + }, + { + "name": "dry-run", + "type": "bool", + "default": false, + "required": false, + "help": "Validate the input and stop before remote submission" + }, + { + "name": "prepare-only", + "type": "bool", + "default": false, + "required": false, + "help": "Request an upload slot but stop before uploading the PDF" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds for the overall command (default: 120)" + } + ], + "columns": [ + "status", + "file", + "email", + "venue", + "token", + "review_url", + "message" + ], + "type": "js", + "modulePath": "paperreview/submit.js", + "sourceFile": "paperreview/submit.js" + }, + { + "site": "pixiv", + "name": "detail", + "description": "View illustration details (tags, stats, URLs)", + "access": "read", + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Illustration ID" + } + ], + "columns": [ + "illust_id", + "title", + "author", + "type", + "pages", + "bookmarks", + "likes", + "views", + "tags", + "created", + "url" + ], + "type": "js", + "modulePath": "pixiv/detail.js", + "sourceFile": "pixiv/detail.js", + "navigateBefore": "https://www.pixiv.net" + }, + { + "site": "pixiv", + "name": "download", + "description": "Download illustration images from Pixiv", + "access": "read", + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "illust-id", + "type": "str", + "required": true, + "positional": true, + "help": "Illustration ID" + }, + { + "name": "output", + "type": "str", + "default": "./pixiv-downloads", + "required": false, + "help": "Output directory" + } + ], + "columns": [ + "index", + "type", + "status", + "size" + ], + "type": "js", + "modulePath": "pixiv/download.js", + "sourceFile": "pixiv/download.js", + "navigateBefore": "https://www.pixiv.net" + }, + { + "site": "pixiv", + "name": "illusts", + "description": "List a Pixiv artist's illustrations", + "access": "read", + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "user-id", + "type": "str", + "required": true, + "positional": true, + "help": "Pixiv user ID" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "illust_id", + "pages", + "bookmarks", + "tags", + "created", + "url" + ], + "type": "js", + "modulePath": "pixiv/illusts.js", + "sourceFile": "pixiv/illusts.js", + "navigateBefore": "https://www.pixiv.net" + }, + { + "site": "pixiv", + "name": "login", + "description": "Open pixiv login", + "access": "write", + "domain": "pixiv.net", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "pixiv/auth.js", + "sourceFile": "pixiv/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "pixiv", + "name": "ranking", + "description": "Pixiv illustration rankings (daily/weekly/monthly)", + "access": "read", + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "mode", + "type": "str", + "default": "daily", + "required": false, + "help": "Ranking mode", + "choices": [ + "daily", + "weekly", + "monthly", + "rookie", + "original", + "male", + "female", + "daily_r18", + "weekly_r18" + ] + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "user_id", + "illust_id", + "pages", + "bookmarks", + "url" + ], + "type": "js", + "modulePath": "pixiv/ranking.js", + "sourceFile": "pixiv/ranking.js", + "navigateBefore": "https://www.pixiv.net" + }, + { + "site": "pixiv", + "name": "search", + "description": "Search Pixiv illustrations by keyword", + "access": "read", + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword or tag" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + }, + { + "name": "order", + "type": "str", + "default": "date_d", + "required": false, + "help": "Sort order", + "choices": [ + "date_d", + "date", + "popular_d", + "popular_male_d", + "popular_female_d" + ] + }, + { + "name": "mode", + "type": "str", + "default": "all", + "required": false, + "help": "Search mode", + "choices": [ + "all", + "safe", + "r18" + ] + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number" + } + ], + "columns": [ + "rank", + "title", + "author", + "user_id", + "illust_id", + "pages", + "bookmarks", + "tags", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "pixiv/search.js", + "sourceFile": "pixiv/search.js", + "navigateBefore": "https://www.pixiv.net" + }, + { + "site": "pixiv", + "name": "user", + "description": "View Pixiv artist profile", + "access": "read", + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "uid", + "type": "str", + "required": true, + "positional": true, + "help": "Pixiv user ID" + } + ], + "columns": [ + "user_id", + "name", + "premium", + "following", + "illusts", + "manga", + "novels", + "comment", + "url" + ], + "type": "js", + "modulePath": "pixiv/user.js", + "sourceFile": "pixiv/user.js", + "navigateBefore": "https://www.pixiv.net" + }, + { + "site": "pixiv", + "name": "whoami", + "description": "Show the current logged-in pixiv account", + "access": "read", + "domain": "pixiv.net", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" + ], + "type": "js", + "modulePath": "pixiv/auth.js", + "sourceFile": "pixiv/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "practo", + "name": "appointment", + "description": "Show logged-in Practo Drive appointment details", + "access": "read", + "domain": "drive.practo.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "appointment_id", + "type": "str", + "required": true, + "positional": true, + "help": "Appointment id from `practo appointments`" + } + ], + "columns": [ + "appointment_id", + "status", + "summary" + ], + "type": "js", + "modulePath": "practo/appointment.js", + "sourceFile": "practo/appointment.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "practo", + "name": "appointments", + "description": "List logged-in Practo Drive appointments", + "access": "read", + "domain": "drive.practo.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "appointment_id", + "doctor", + "practice", + "time", + "status" + ], + "type": "js", + "modulePath": "practo/appointments.js", + "sourceFile": "practo/appointments.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "practo", + "name": "book-confirm", + "description": "Confirm a Practo clinic visit booking after explicit confirmation", + "access": "write", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "practice_doctor_id", + "type": "str", + "required": true, + "positional": true, + "help": "Practo practice_doctor_id" + }, + { + "name": "time", + "type": "str", + "required": true, + "help": "Slot time YYYY-MM-DD HH:mm:ss" + }, + { + "name": "profile-url", + "type": "str", + "required": false, + "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "Required. Set --confirm true to create the appointment." + } + ], + "columns": [ + "status", + "practice_doctor_id", + "time", + "url" + ], + "type": "js", + "modulePath": "practo/book-confirm.js", + "sourceFile": "practo/book-confirm.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "practo", + "name": "book-preview", + "description": "Preview Practo booking details for a selected slot without confirming", + "access": "read", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "practice_doctor_id", + "type": "str", + "required": true, + "positional": true, + "help": "Practo practice_doctor_id" + }, + { + "name": "time", + "type": "str", + "required": true, + "help": "Slot time YYYY-MM-DD HH:mm:ss" + }, + { + "name": "profile-url", + "type": "str", + "required": false, + "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" + } + ], + "columns": [ + "practice_doctor_id", + "time", + "amount", + "prepaid", + "payment_mode", + "requires_payment", + "confirm_button", + "booking_url" + ], + "type": "js", + "modulePath": "practo/book-preview.js", + "sourceFile": "practo/book-preview.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "practo", + "name": "booking-link", + "description": "Build a Practo booking URL for a selected slot without confirming it", + "access": "read", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "practice_doctor_id", + "type": "str", + "required": true, + "positional": true, + "help": "Practo practice_doctor_id" + }, + { + "name": "time", + "type": "str", + "required": true, + "help": "Slot time YYYY-MM-DD HH:mm:ss" + }, + { + "name": "profile-url", + "type": "str", + "required": false, + "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" + } + ], + "columns": [ + "practice_doctor_id", + "time", + "booking_url" + ], + "type": "js", + "modulePath": "practo/booking-link.js", + "sourceFile": "practo/booking-link.js", + "navigateBefore": false + }, + { + "site": "practo", + "name": "cancel", + "description": "Cancel a logged-in Practo Drive appointment after explicit confirmation", + "access": "write", + "domain": "drive.practo.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "appointment_id", + "type": "str", + "required": true, + "positional": true, + "help": "Appointment id from `practo appointments`" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "Required. Set --confirm true to cancel the appointment." + } + ], + "columns": [ + "status", + "appointment_id" + ], + "type": "js", + "modulePath": "practo/cancel.js", + "sourceFile": "practo/cancel.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "practo", + "name": "contact", + "description": "Get Practo virtual contact number for a practice_doctor_id", + "access": "read", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "practice_doctor_id", + "type": "str", + "required": true, + "positional": true, + "help": "Practo practice_doctor_id from search results" + } + ], + "columns": [ + "practice_doctor_id", + "phone", + "raw" + ], + "type": "js", + "modulePath": "practo/contact.js", + "sourceFile": "practo/contact.js", + "navigateBefore": false + }, + { + "site": "practo", + "name": "login", + "description": "Open practo login", + "access": "write", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "practo/login.js", + "sourceFile": "practo/login.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "practo", + "name": "profile", + "description": "Read public details from a Practo doctor profile URL", + "access": "read", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "Practo doctor profile URL" + } + ], + "columns": [ + "name", + "specialty", + "experience", + "fee", + "profile_url" + ], + "type": "js", + "modulePath": "practo/profile.js", + "sourceFile": "practo/profile.js", + "navigateBefore": false + }, + { + "site": "practo", + "name": "search", + "description": "Search Practo doctors by specialty, city, and optional locality", + "access": "read", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "specialty", + "type": "str", + "required": true, + "positional": true, + "help": "Doctor specialty, e.g. orthopedist or dermatologist" + }, + { + "name": "city", + "type": "str", + "default": "bangalore", + "required": false, + "help": "City, e.g. bangalore" + }, + { + "name": "locality", + "type": "str", + "required": false, + "help": "Optional locality, e.g. indiranagar" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max doctors to return (1-25)" + } + ], + "columns": [ + "rank", + "practice_doctor_id", + "doctor_id", + "practice_id", + "name", + "specialty", + "experience_years", + "locality", + "clinic", + "fee", + "next_available", + "profile_url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "practo/search.js", + "sourceFile": "practo/search.js", + "navigateBefore": false + }, + { + "site": "practo", + "name": "slots", + "description": "List available Practo appointment slots for a practice_doctor_id", + "access": "read", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "practice_doctor_id", + "type": "str", + "required": true, + "positional": true, + "help": "Practo practice_doctor_id from search results" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max slots to return (1-25)" + } + ], + "columns": [ + "practice_doctor_id", + "time", + "available", + "amount", + "prepaid", + "appointment_token" + ], + "type": "js", + "modulePath": "practo/slots.js", + "sourceFile": "practo/slots.js", + "navigateBefore": false + }, + { + "site": "practo", + "name": "whoami", + "aliases": [ + "auth-status" + ], + "description": "Show the current logged-in practo account", + "access": "read", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name" + ], + "type": "js", + "modulePath": "practo/login.js", + "sourceFile": "practo/login.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "producthunt", + "name": "browse", + "description": "Best products in a Product Hunt category", + "access": "read", + "domain": "www.producthunt.com", + "strategy": "intercept", + "browser": true, + "args": [ + { + "name": "category", + "type": "string", + "required": true, + "positional": true, + "help": "Category slug, e.g. vibe-coding, ai-agents, developer-tools" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results (max 50)" + } + ], + "columns": [ + "rank", + "name", + "tagline", + "reviews", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "producthunt/browse.js", + "sourceFile": "producthunt/browse.js", + "navigateBefore": true + }, + { + "site": "producthunt", + "name": "hot", + "description": "Today's top Product Hunt launches with vote counts", + "access": "read", + "domain": "www.producthunt.com", + "strategy": "intercept", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results (max 50)" + } + ], + "columns": [ + "rank", + "name", + "votes", + "url" + ], + "type": "js", + "modulePath": "producthunt/hot.js", + "sourceFile": "producthunt/hot.js", + "navigateBefore": true + }, + { + "site": "producthunt", + "name": "posts", + "description": "Latest Product Hunt launches (optional category filter)", + "access": "read", + "domain": "www.producthunt.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results (max 50)" + }, + { + "name": "category", + "type": "string", + "default": "", + "required": false, + "help": "Category filter: ai-agents, ai-coding-agents, ai-code-editors, ai-chatbots, ai-workflow-automation, vibe-coding, developer-tools, productivity, design-creative, marketing-sales, no-code-platforms, llms, finance, social-community, engineering-development" + } + ], + "columns": [ + "rank", + "name", + "tagline", + "author", + "date", + "url" + ], + "type": "js", + "modulePath": "producthunt/posts.js", + "sourceFile": "producthunt/posts.js" + }, + { + "site": "producthunt", + "name": "today", + "description": "Today's Product Hunt launches (most recent day in feed)", + "access": "read", + "domain": "www.producthunt.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results" + } + ], + "columns": [ + "rank", + "name", + "tagline", + "author", + "url" + ], + "type": "js", + "modulePath": "producthunt/today.js", + "sourceFile": "producthunt/today.js" + }, + { + "site": "pubmed", + "name": "article", + "aliases": [ + "paper", + "read" + ], + "description": "Get detailed information for a PubMed article by PMID", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "pmid", + "type": "str", + "required": true, + "positional": true, + "help": "PubMed ID, e.g. 37780221" + }, + { + "name": "full-abstract", + "type": "boolean", + "default": false, + "required": false, + "help": "Do not truncate the abstract in table output" + } + ], + "columns": [ + "pmid", + "title", + "authors", + "journal", + "year", + "date", + "article_type", + "language", + "doi", + "pmc", + "affiliations", + "grants", + "mesh_terms", + "keywords", + "abstract", + "url" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "pubmed/article.js", + "sourceFile": "pubmed/article.js" + }, + { + "site": "pubmed", + "name": "author", + "description": "Search PubMed articles by author name and optional affiliation", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Author name, e.g. \"Smith J\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "affiliation", + "type": "str", + "required": false, + "help": "Filter by author affiliation" + }, + { + "name": "position", + "type": "str", + "default": "any", + "required": false, + "help": "Author position: any, first, or last", + "choices": [ + "any", + "first", + "last" + ] + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "sort", + "type": "str", + "default": "date", + "required": false, + "help": "Sort by date or relevance", + "choices": [ + "date", + "relevance" + ] + } + ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" + ], + "type": "js", + "modulePath": "pubmed/author.js", + "sourceFile": "pubmed/author.js" + }, + { + "site": "pubmed", + "name": "citations", + "description": "Get PubMed citation relationships for an article", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "pmid", + "type": "str", + "required": true, + "positional": true, + "help": "PubMed ID, e.g. 37780221" + }, + { + "name": "direction", + "type": "str", + "default": "citedby", + "required": false, + "help": "citedby or references", + "choices": [ + "citedby", + "references" + ] + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + } + ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" + ], + "type": "js", + "modulePath": "pubmed/citations.js", + "sourceFile": "pubmed/citations.js" + }, + { + "site": "pubmed", + "name": "clinical-trial", + "description": "Search PubMed clinical trials with a trial-study preset", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Clinical topic query, e.g. \"breast cancer\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "free-full-text", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include free full text articles" + }, + { + "name": "sort", + "type": "str", + "default": "date", + "required": false, + "help": "Sort by date or relevance", + "choices": [ + "date", + "relevance" + ] + } + ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" + ], + "type": "js", + "modulePath": "pubmed/clinical-trial.js", + "sourceFile": "pubmed/clinical-trial.js" + }, + { + "site": "pubmed", + "name": "journal", + "description": "Search PubMed articles by journal name", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "journal", + "type": "str", + "required": true, + "positional": true, + "help": "Journal name, e.g. \"Nature\" or \"The Lancet\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance or date", + "choices": [ + "relevance", + "date" + ] + } + ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" + ], + "type": "js", + "modulePath": "pubmed/journal.js", + "sourceFile": "pubmed/journal.js" + }, + { + "site": "pubmed", + "name": "mesh", + "description": "Search PubMed articles by MeSH term", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "term", + "type": "str", + "required": true, + "positional": true, + "help": "MeSH term, e.g. \"Neoplasms\" or \"Machine Learning\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "major", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include articles where this is a major MeSH topic" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance or date", + "choices": [ + "relevance", + "date" + ] + } + ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" + ], + "type": "js", + "modulePath": "pubmed/mesh.js", + "sourceFile": "pubmed/mesh.js" + }, + { + "site": "pubmed", + "name": "related", + "description": "Find articles related to a PubMed article", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "pmid", + "type": "str", + "required": true, + "positional": true, + "help": "PubMed ID, e.g. 37780221" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "score", + "type": "boolean", + "default": false, + "required": false, + "help": "Show similarity scores when available" + } + ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "score", + "doi", + "url" + ], + "type": "js", + "modulePath": "pubmed/related.js", + "sourceFile": "pubmed/related.js" + }, + { + "site": "pubmed", + "name": "review", + "description": "Search PubMed review articles with a review preset", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Review topic query, e.g. \"immunotherapy\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "has-abstract", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include articles with abstracts" + }, + { + "name": "sort", + "type": "str", + "default": "date", + "required": false, + "help": "Sort by date or relevance", + "choices": [ + "date", + "relevance" + ] + } + ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" + ], + "type": "js", + "modulePath": "pubmed/review.js", + "sourceFile": "pubmed/review.js" + }, + { + "site": "pubmed", + "name": "search", + "description": "Search PubMed articles with advanced filters", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query, e.g. \"machine learning cancer\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "author", + "type": "str", + "required": false, + "help": "Filter by author name" + }, + { + "name": "journal", + "type": "str", + "required": false, + "help": "Filter by journal name" + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "article-type", + "type": "str", + "required": false, + "help": "Filter by publication type, e.g. Review or Clinical Trial" + }, + { + "name": "has-abstract", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include articles with abstracts" + }, + { + "name": "free-full-text", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include free full text articles" + }, + { + "name": "humans-only", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include human studies" + }, + { + "name": "english-only", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include English articles" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance, date, author, or journal", + "choices": [ + "relevance", + "date", + "author", + "journal" + ] + } + ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "pubmed/search.js", + "sourceFile": "pubmed/search.js" + }, + { + "site": "pypi", + "name": "downloads", + "description": "PyPI download stats for a package (recent totals or full daily history)", + "access": "read", + "domain": "pypistats.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "PyPI package name (e.g. \"requests\", \"pandas\")" + }, + { + "name": "period", + "type": "str", + "default": "recent", + "required": false, + "help": "recent (default — 1 row, last day/week/month) or overall (1 row per day)" + } + ], + "columns": [ + "rank", + "package", + "period", + "date", + "downloads" + ], + "type": "js", + "modulePath": "pypi/downloads.js", + "sourceFile": "pypi/downloads.js" + }, + { + "site": "pypi", + "name": "package", + "description": "Single PyPI package metadata (latest version, license, homepage, classifiers)", + "access": "read", + "domain": "pypi.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "PyPI package name (e.g. \"requests\", \"pandas\")" + } + ], + "columns": [ + "name", + "latestVersion", + "summary", + "author", + "license", + "homepage", + "repository", + "requiresPython", + "keywords", + "releases", + "firstReleased", + "lastReleased", + "url" + ], + "type": "js", + "modulePath": "pypi/package.js", + "sourceFile": "pypi/package.js" + }, + { + "site": "qoder", + "name": "account", + "description": "Click the account button (username) in the Qoder sidebar and return the visible account dropdown items.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": false, + "help": "Username text shown in the sidebar (default: tries common short labels)" + } + ], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "add-workspace", + "description": "Click \"Add Workspace\" — opens the folder picker. Note: this opens a system file-picker dialog that Qoder controls; the actual folder selection must be done in the UI by the user.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "ask", + "description": "Send a prompt to Qoder and wait up to --timeout seconds for the reply (best-effort: polls for the chat turn count to grow + stabilize).", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt text" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait" + } + ], + "columns": [ + "Role", + "Text", + "WaitedSeconds" + ], + "type": "js", + "modulePath": "qoder/quest.js", + "sourceFile": "qoder/quest.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "credits", + "description": "Click \"Credits Usage\" and return the credits-usage display text.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "history", + "description": "List Quests visible in the Qoder sidebar. Returns title + visible metadata.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "" + } + ], + "columns": [ + "Index", + "Title" + ], + "type": "js", + "modulePath": "qoder/history.js", + "sourceFile": "qoder/history.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "knowledge", + "description": "Open the Knowledge view (Qoder's personal/team knowledge base).", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "marketplace", + "description": "Open the Qoder Marketplace.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "more-actions", + "description": "Click the \"More Actions\" button and list its menu items.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Item" + ], + "type": "js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "new", + "description": "Start a new Qoder Quest (conversation). Clicks the \"New Quest\" button in the sidebar (or its ⌘N variant).", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "qoder/quest.js", + "sourceFile": "qoder/quest.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "open-editor", + "description": "Click \"Open Editor\" — opens the current draft in a full editor pane.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "qoder/composer.js", + "sourceFile": "qoder/composer.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "open-panel", + "description": "Open / close the Qoder bottom panel (Output / Terminal / Debug Console). ⌥⌘B equivalent.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "prompt-enhance", + "description": "Click \"Prompt Enhance\" — Qoder rewrites the current composer draft for better LLM consumption.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "qoder/composer.js", + "sourceFile": "qoder/composer.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "read", + "description": "Read messages in the current Qoder Quest. Returns role + text for each visible turn.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "" + } + ], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "qoder/read.js", + "sourceFile": "qoder/read.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "search", + "description": "Open Qoder Search palette (⌘P), type a query, return matched options.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search text" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "" + } + ], + "columns": [ + "Index", + "Item" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "send", + "description": "Type text into the Qoder composer and click \"Send message\" (fire-and-forget).", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Text to send" + } + ], + "columns": [ + "Status", + "Length" + ], + "type": "js", + "modulePath": "qoder/quest.js", + "sourceFile": "qoder/quest.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "settings", + "description": "Click the Settings button in the Qoder sidebar.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "sidebar-toggle", + "description": "Collapse / Expand the Qoder Quest List sidebar (⌘B).", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "status", + "description": "Check Qoder CDP connection and report the current renderer URL + title.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "qoder/status.js", + "sourceFile": "qoder/status.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "view-all", + "description": "Click \"View all\" to show all Quests.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true + }, + { + "site": "reddit", + "name": "comment", + "description": "Post a comment on a Reddit post", + "access": "write", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "post-id", + "type": "string", + "required": true, + "positional": true, + "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" + }, + { + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "Comment text" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "reddit/comment.js", + "sourceFile": "reddit/comment.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "frontpage", + "description": "Reddit Frontpage / r/all", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "" + } + ], + "columns": [ + "title", + "subreddit", + "author", + "upvotes", + "comments", + "url", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" + ], + "type": "js", + "modulePath": "reddit/frontpage.js", + "sourceFile": "reddit/frontpage.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "home", + "description": "Reddit personalized home feed (Best, requires login)", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Number of posts (1–100)" + } + ], + "columns": [ + "rank", + "title", + "subreddit", + "score", + "comments", + "postId", + "author", + "url", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" + ], + "type": "js", + "modulePath": "reddit/home.js", + "sourceFile": "reddit/home.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "hot", + "description": "Reddit hot posts", + "access": "read", + "domain": "www.reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "subreddit", + "type": "str", + "default": "", + "required": false, + "help": "Subreddit name (e.g. programming). Empty for frontpage" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts" + } + ], + "columns": [ + "rank", + "title", + "subreddit", + "score", + "comments", + "postId", + "author", + "url", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" + ], + "type": "js", + "modulePath": "reddit/hot.js", + "sourceFile": "reddit/hot.js", + "navigateBefore": "https://www.reddit.com" + }, + { + "site": "reddit", + "name": "login", + "description": "Open reddit login", + "access": "write", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "username", + "id", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "reddit/auth.js", + "sourceFile": "reddit/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "reddit", + "name": "popular", + "description": "Reddit Popular posts (/r/popular)", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "" + } + ], + "columns": [ + "rank", + "id", + "title", + "subreddit", + "score", + "comments", + "author", + "url", + "created_utc", + "selftext", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" + ], + "type": "js", + "modulePath": "reddit/popular.js", + "sourceFile": "reddit/popular.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "read", + "description": "Read a Reddit post and its comments", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "post-id", + "type": "str", + "required": true, + "positional": true, + "help": "Post ID (e.g. 1abc123) or full URL" + }, + { + "name": "sort", + "type": "str", + "default": "best", + "required": false, + "help": "Comment sort: best, top, new, controversial, old, qa" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Number of top-level comments" + }, + { + "name": "depth", + "type": "int", + "default": 2, + "required": false, + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + }, + { + "name": "replies", + "type": "int", + "default": 5, + "required": false, + "help": "Max replies shown per comment at each level (sorted by score)" + }, + { + "name": "max-length", + "type": "int", + "default": 2000, + "required": false, + "help": "Max characters per comment body (min 100)" + }, + { + "name": "expand-more", + "type": "bool", + "default": false, + "required": false, + "help": "Follow Reddit \"more comments\" stubs by calling /api/morechildren.json" + }, + { + "name": "expand-rounds", + "type": "int", + "default": 2, + "required": false, + "help": "Max expansion passes when --expand-more is on (1–5; each round can fan out new \"more\" stubs)" + } + ], + "columns": [ + "type", + "author", + "score", + "text", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" + ], + "type": "js", + "modulePath": "reddit/read.js", + "sourceFile": "reddit/read.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "reply", + "description": "Reply to a Reddit comment", + "access": "write", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "comment-id", + "type": "string", + "required": true, + "positional": true, + "help": "Comment ID (e.g. okf3s7u) or fullname (t1_xxx)" + }, + { + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "Reply text" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "reddit/reply.js", + "sourceFile": "reddit/reply.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "save", + "description": "Save or unsave a Reddit post", + "access": "write", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "post-id", + "type": "string", + "required": true, + "positional": true, + "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" + }, + { + "name": "undo", + "type": "boolean", + "default": false, + "required": false, + "help": "Unsave instead of save" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "reddit/save.js", + "sourceFile": "reddit/save.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "saved", + "description": "Browse your saved Reddit posts", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "" + } + ], + "columns": [ + "title", + "subreddit", + "score", + "comments", + "url" + ], + "type": "js", + "modulePath": "reddit/saved.js", + "sourceFile": "reddit/saved.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "search", + "description": "Search Reddit Posts", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "string", + "required": true, + "positional": true, + "help": "Reddit search query" + }, + { + "name": "subreddit", + "type": "string", + "default": "", + "required": false, + "help": "Search within a specific subreddit" + }, + { + "name": "sort", + "type": "string", + "default": "relevance", + "required": false, + "help": "Sort order: relevance, hot, top, new, comments" + }, + { + "name": "time", + "type": "string", + "default": "all", + "required": false, + "help": "Time filter: hour, day, week, month, year, all" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "" + } + ], + "columns": [ + "id", + "title", + "subreddit", + "author", + "score", + "comments", + "url", + "created_utc", + "selftext", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "reddit/search.js", + "sourceFile": "reddit/search.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "subreddit", + "description": "Get posts from a specific Subreddit", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "name", + "type": "string", + "required": true, + "positional": true, + "help": "Subreddit name (no `r/` prefix; e.g. `python`)" + }, + { + "name": "sort", + "type": "string", + "default": "hot", + "required": false, + "help": "Sorting method: hot, new, top, rising, controversial" + }, + { + "name": "time", + "type": "string", + "default": "all", + "required": false, + "help": "Time filter for top/controversial: hour, day, week, month, year, all" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "" + } + ], + "columns": [ + "id", + "title", + "subreddit", + "author", + "upvotes", + "comments", + "url", + "created_utc", + "selftext", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" + ], + "type": "js", + "modulePath": "reddit/subreddit.js", + "sourceFile": "reddit/subreddit.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "subreddit-info", + "description": "Show metadata for a Reddit subreddit (subscribers, description, created date, NSFW)", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "name", + "type": "string", + "required": true, + "positional": true, + "help": "Subreddit name (no `r/` prefix needed)" + } + ], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "reddit/subreddit-info.js", + "sourceFile": "reddit/subreddit-info.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "subscribe", + "description": "Subscribe or unsubscribe to a subreddit", + "access": "write", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "subreddit", + "type": "string", + "required": true, + "positional": true, + "help": "Subreddit name (e.g. python)" + }, + { + "name": "undo", + "type": "boolean", + "default": false, + "required": false, + "help": "Unsubscribe instead of subscribe" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "reddit/subscribe.js", + "sourceFile": "reddit/subscribe.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "subscribed", + "description": "List subreddits you are subscribed to", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Max subreddits to return (1-1000, auto-paginates)" + } + ], + "columns": [ + "id", + "subreddit", + "title", + "subscribers", + "description", + "url" + ], + "type": "js", + "modulePath": "reddit/subscribed.js", + "sourceFile": "reddit/subscribed.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "upvote", + "description": "Upvote or downvote a Reddit post", + "access": "write", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "post-id", + "type": "string", + "required": true, + "positional": true, + "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" + }, + { + "name": "direction", + "type": "string", + "default": "up", + "required": false, + "help": "Vote direction: up, down, none" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "reddit/upvote.js", + "sourceFile": "reddit/upvote.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "upvoted", + "description": "Browse your upvoted Reddit posts", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "" + } + ], + "columns": [ + "title", + "subreddit", + "score", + "comments", + "url" + ], + "type": "js", + "modulePath": "reddit/upvoted.js", + "sourceFile": "reddit/upvoted.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "user", + "description": "View a Reddit user profile", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Reddit username (no `u/` prefix needed)" + } + ], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "reddit/user.js", + "sourceFile": "reddit/user.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "user-comments", + "description": "View a Reddit user's comment history", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Reddit username (no `u/` prefix needed)" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "" + } + ], + "columns": [ + "subreddit", + "score", + "body", + "url" + ], + "type": "js", + "modulePath": "reddit/user-comments.js", + "sourceFile": "reddit/user-comments.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "user-posts", + "description": "View a Reddit user's submitted posts", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Reddit username (no `u/` prefix needed)" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "" + } + ], + "columns": [ + "title", + "subreddit", + "score", + "comments", + "url" + ], + "type": "js", + "modulePath": "reddit/user-posts.js", + "sourceFile": "reddit/user-posts.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "whoami", + "description": "Show the currently logged-in Reddit user", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "reddit/whoami.js", + "sourceFile": "reddit/whoami.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "rest-countries", + "name": "country", + "description": "Look up countries by name (common / official, substring match)", + "access": "read", + "domain": "restcountries.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Country name (e.g. \"japan\", \"united kingdom\")" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max rows (1-250)" + } + ], + "columns": [ + "rank", + "commonName", + "officialName", + "cca2", + "cca3", + "ccn3", + "capital", + "region", + "subregion", + "population", + "area", + "languages", + "currencies", + "latitude", + "longitude", + "timezones", + "independent", + "unMember", + "landlocked", + "flag", + "url" + ], + "type": "js", + "modulePath": "rest-countries/country.js", + "sourceFile": "rest-countries/country.js" + }, + { + "site": "rest-countries", + "name": "region", + "description": "List countries in a region (africa / americas / asia / europe / oceania / antarctic)", + "access": "read", + "domain": "restcountries.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "region", + "type": "str", + "required": true, + "positional": true, + "help": "Region name (case-insensitive)" + }, + { + "name": "limit", + "type": "int", + "default": 250, + "required": false, + "help": "Max rows (1-250)" + } + ], + "columns": [ + "rank", + "commonName", + "officialName", + "cca2", + "cca3", + "ccn3", + "capital", + "region", + "subregion", + "population", + "area", + "languages", + "currencies", + "latitude", + "longitude", + "timezones", + "independent", + "unMember", + "landlocked", + "flag", + "url" + ], + "type": "js", + "modulePath": "rest-countries/region.js", + "sourceFile": "rest-countries/region.js" + }, + { + "site": "reuters", + "name": "article-detail", + "description": "Reuters Reuters article detail:title/author/body text", + "access": "read", + "domain": "www.reuters.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "Reuters article URL (must be on reuters.com)" + } + ], + "columns": [ + "title", + "date", + "section", + "section_path", + "authors", + "description", + "word_count", + "url", + "body" + ], + "type": "js", + "modulePath": "reuters/article-detail.js", + "sourceFile": "reuters/article-detail.js", + "navigateBefore": "https://www.reuters.com" + }, + { + "site": "reuters", + "name": "login", + "description": "Open reuters login", + "access": "write", + "domain": "reuters.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "subscribed", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "reuters/auth.js", + "sourceFile": "reuters/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "reuters", + "name": "search", + "description": "Reuters Reuters news search", + "access": "read", + "domain": "www.reuters.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results (1-40)" + } + ], + "columns": [ + "rank", + "title", + "date", + "section", + "section_path", + "authors", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "reuters/search.js", + "sourceFile": "reuters/search.js", + "navigateBefore": "https://www.reuters.com" + }, + { + "site": "reuters", + "name": "whoami", + "description": "Show the current logged-in reuters account", + "access": "read", + "domain": "reuters.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "subscribed" + ], + "type": "js", + "modulePath": "reuters/auth.js", + "sourceFile": "reuters/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "rfc", + "name": "rfc", + "description": "Single IETF RFC metadata (title, abstract, working group, authors, std level)", + "access": "read", + "domain": "datatracker.ietf.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "number", + "type": "int", + "required": true, + "positional": true, + "help": "RFC number (e.g. 9000, 791, 2616)" + } + ], + "columns": [ + "rfc", + "title", + "state", + "stdLevel", + "group", + "groupType", + "pages", + "published", + "authors", + "abstract", + "rfcEditorUrl", + "url" + ], + "type": "js", + "modulePath": "rfc/rfc.js", + "sourceFile": "rfc/rfc.js" + }, + { + "site": "rubygems", + "name": "gem", + "description": "Fetch a RubyGems.org gem's metadata (version, downloads, license, links)", + "access": "read", + "domain": "rubygems.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Gem name (e.g. \"rails\", \"sidekiq\")" + } + ], + "columns": [ + "gem", + "version", + "releasedAt", + "downloads", + "versionDownloads", + "license", + "authors", + "homepage", + "source", + "bugs", + "info", + "url" + ], + "type": "js", + "modulePath": "rubygems/gem.js", + "sourceFile": "rubygems/gem.js" + }, + { + "site": "rubygems", + "name": "search", + "description": "Search RubyGems.org gems by keyword", + "access": "read", + "domain": "rubygems.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"rails\", \"redis\")" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max gems (1-100, single RubyGems page)" + } + ], + "columns": [ + "rank", + "gem", + "version", + "downloads", + "license", + "authors", + "info", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "rubygems/search.js", + "sourceFile": "rubygems/search.js" + }, + { + "site": "semanticscholar", + "name": "citations", + "description": "List papers that cite a Semantic Scholar paper (paginated)", + "access": "read", + "domain": "api.semanticscholar.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max citing papers (1-1000, single Semantic Scholar page)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Page offset (0-based)" + } + ], + "columns": [ + "rank", + "paperId", + "doi", + "title", + "year", + "firstAuthor", + "citationCount", + "url" + ], + "type": "js", + "modulePath": "semanticscholar/citations.js", + "sourceFile": "semanticscholar/citations.js" + }, + { + "site": "semanticscholar", + "name": "paper", + "description": "Semantic Scholar paper detail (citation graph + AI tldr) by paperId, DOI, or arXiv id", + "access": "read", + "domain": "api.semanticscholar.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id (e.g. \"ARXIV:1706.03762\", \"PMID:12345\")" + } + ], + "columns": [ + "paperId", + "doi", + "title", + "year", + "firstAuthor", + "citationCount", + "influentialCitationCount", + "referenceCount", + "tldr", + "url" + ], + "type": "js", + "modulePath": "semanticscholar/paper.js", + "sourceFile": "semanticscholar/paper.js" + }, + { + "site": "semanticscholar", + "name": "recommendations", + "description": "Semantic Scholar AI-curated related papers for a paperId, DOI, or arXiv id", + "access": "read", + "domain": "api.semanticscholar.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max recommendations (1-500)" + } + ], + "columns": [ + "rank", + "paperId", + "doi", + "title", + "year", + "firstAuthor", + "citationCount", + "url" + ], + "type": "js", + "modulePath": "semanticscholar/recommendations.js", + "sourceFile": "semanticscholar/recommendations.js" + }, + { + "site": "semanticscholar", + "name": "search", + "description": "Search Semantic Scholar papers by free text", + "access": "read", + "domain": "api.semanticscholar.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search text (e.g. \"attention is all you need\", \"diffusion model\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max papers (1-100, single Semantic Scholar page)" + } + ], + "columns": [ + "rank", + "paperId", + "doi", + "title", + "year", + "firstAuthor", + "citationCount", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "semanticscholar/search.js", + "sourceFile": "semanticscholar/search.js" + }, + { + "site": "slock", + "name": "attachment-download", + "description": "Download an attachment to a local file. Resolves a signed CDN URL in the page, then fetches bytes node-side (no CORS).", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "attachmentId", + "type": "str", + "required": true, + "positional": true, + "help": "Attachment UUID" + }, + { + "name": "out", + "type": "str", + "required": false, + "help": "Local path to write to. Defaults to ./.bin" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server slug" + } + ], + "columns": [ + "attachmentId", + "out", + "sizeBytes" + ], + "type": "js", + "modulePath": "slock/attachment-download.js", + "sourceFile": "slock/attachment-download.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "attachment-upload", + "description": "Upload a local file to Slock attachments. Prints the attachmentId for use with `message-send --attach`.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "file", + "type": "str", + "required": true, + "positional": true, + "help": "Local file path to upload (single file; max 50 MB)" + }, + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name — server requires the attachment be scoped to a channel" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server slug" + } + ], + "columns": [ + "attachmentId", + "filename", + "mimeType", + "sizeBytes" + ], + "type": "js", + "modulePath": "slock/attachment-upload.js", + "sourceFile": "slock/attachment-upload.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "attachment-url", + "description": "Get a short-lived signed CDN URL for an attachment (does not download bytes).", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "attachmentId", + "type": "str", + "required": true, + "positional": true, + "help": "Attachment UUID" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server slug" + } + ], + "columns": [ + "attachmentId", + "url", + "expiresAt" + ], + "type": "js", + "modulePath": "slock/attachment-url.js", + "sourceFile": "slock/attachment-url.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "bookmark-add", + "description": "Bookmark a message (POST /channels/saved). Requires full messageId UUID.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "messageId", + "type": "str", + "required": true, + "positional": true, + "help": "Full messageId UUID (short ids rejected)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "messageId", + "saved" + ], + "type": "js", + "modulePath": "slock/bookmark-add.js", + "sourceFile": "slock/bookmark-add.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "bookmark-list", + "description": "List bookmarks (saved messages) in the active server", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max results" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Offset" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "id", + "messageId", + "content", + "savedAt" + ], + "type": "js", + "modulePath": "slock/bookmark-list.js", + "sourceFile": "slock/bookmark-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "bookmark-remove", + "description": "Remove a bookmark (DELETE /channels/saved/:messageId). 404 is treated as already-removed.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "messageId", + "type": "str", + "required": true, + "positional": true, + "help": "Full messageId UUID" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "messageId", + "removed", + "note" + ], + "type": "js", + "modulePath": "slock/bookmark-remove.js", + "sourceFile": "slock/bookmark-remove.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "channel-archive", + "description": "Archive a channel — admin only (POST /channels/:id/archive)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "channel", + "id", + "archivedAt", + "result" + ], + "type": "js", + "modulePath": "slock/channel-archive.js", + "sourceFile": "slock/channel-archive.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "channel-create", + "description": "Create a channel — admin only (POST /channels/). Public unless --private.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Channel name" + }, + { + "name": "description", + "type": "str", + "required": false, + "help": "Channel description / topic (≤500 chars)" + }, + { + "name": "private", + "type": "bool", + "default": false, + "required": false, + "help": "Create a private channel instead of public" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "id", + "name", + "type", + "result" + ], + "type": "js", + "modulePath": "slock/channel-create.js", + "sourceFile": "slock/channel-create.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "channel-files", + "description": "List files shared in a channel (GET /channels/:id/files)", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max files" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "id", + "filename", + "mimeType", + "sizeBytes", + "messageId", + "createdAt" + ], + "type": "js", + "modulePath": "slock/channel-files.js", + "sourceFile": "slock/channel-files.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "channel-info", + "description": "Show one channel's details (GET /channels/:id)", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "id", + "name", + "type", + "topic", + "joined", + "archivedAt" + ], + "type": "js", + "modulePath": "slock/channel-info.js", + "sourceFile": "slock/channel-info.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "channel-join", + "description": "Join a public channel (POST /channels/:id/join)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "channel", + "id", + "archivedAt", + "result" + ], + "type": "js", + "modulePath": "slock/channel-join.js", + "sourceFile": "slock/channel-join.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "channel-leave", + "description": "Leave a channel (POST /channels/:id/leave)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "channel", + "id", + "archivedAt", + "result" + ], + "type": "js", + "modulePath": "slock/channel-leave.js", + "sourceFile": "slock/channel-leave.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "channel-list", + "description": "List channels in the active slock server", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server (slug or id) for this call" + } + ], + "columns": [ + "id", + "name", + "topic" + ], + "type": "js", + "modulePath": "slock/channel-list.js", + "sourceFile": "slock/channel-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "channel-mark", + "description": "Mark a channel read (default), read up to --seq, or --unread.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "seq", + "type": "int", + "required": false, + "help": "Mark read up to this seq (omit for read-all)" + }, + { + "name": "unread", + "type": "bool", + "default": false, + "required": false, + "help": "Mark the channel unread instead of read" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "channel", + "action", + "result" + ], + "type": "js", + "modulePath": "slock/channel-mark.js", + "sourceFile": "slock/channel-mark.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "channel-members", + "description": "List members of a channel", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server (slug or id)" + } + ], + "columns": [ + "userId", + "name", + "kind", + "role" + ], + "type": "js", + "modulePath": "slock/channel-members.js", + "sourceFile": "slock/channel-members.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "channel-unarchive", + "description": "Unarchive a channel — admin only (POST /channels/:id/unarchive). #name lookups exclude archived channels; pass the channelId UUID for archived ones.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "channel", + "id", + "archivedAt", + "result" + ], + "type": "js", + "modulePath": "slock/channel-unarchive.js", + "sourceFile": "slock/channel-unarchive.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "dm-list", + "description": "List DM channels in the active server (GET /channels/dm)", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server (slug or id)" + } + ], + "columns": [ + "channelId", + "peerName", + "peerId", + "createdAt" + ], + "type": "js", + "modulePath": "slock/dm-list.js", + "sourceFile": "slock/dm-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "inbox", + "description": "List unified inbox items (channels, DMs, followed threads) that need attention.", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "filter", + "type": "str", + "default": "all", + "required": false, + "help": "all | unread | mentions" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max items (server caps at 100)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Pagination offset" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "kind", + "id", + "name", + "unreadCount", + "hasMention", + "lastActivityAt", + "preview" + ], + "type": "js", + "modulePath": "slock/inbox.js", + "sourceFile": "slock/inbox.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "inbox-done", + "description": "Mark one chat as done / clear it from the inbox (POST /channels/inbox/done)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "channel", + "result" + ], + "type": "js", + "modulePath": "slock/inbox-done.js", + "sourceFile": "slock/inbox-done.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "inbox-read-all", + "description": "Mark the entire inbox as read (POST /channels/inbox/read-all)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "result", + "markedCount" + ], + "type": "js", + "modulePath": "slock/inbox-read-all.js", + "sourceFile": "slock/inbox-read-all.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "login", + "description": "Open slock login", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "id", + "name", + "email", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "slock/whoami.js", + "sourceFile": "slock/whoami.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "message-read", + "description": "Read messages in a channel or thread. Thread form: \"#channel:msgIdOrShort\". Use --after seq|UUID for cursor.", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID, \"#name\", or \"#channel:msgIdOrShort\"" + }, + { + "name": "after", + "type": "str", + "required": false, + "help": "Cursor: seq number or messageId UUID (exclusive)" + }, + { + "name": "before", + "type": "str", + "required": false, + "help": "seq to page before" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max messages" + }, + { + "name": "no-threads", + "type": "bool", + "default": false, + "required": false, + "help": "Skip /threads enrichment" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "id", + "seq", + "createdAt", + "senderName", + "content", + "threadChannelId", + "replyCount", + "unreadCount", + "lastReplyAt" + ], + "type": "js", + "modulePath": "slock/message-read.js", + "sourceFile": "slock/message-read.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "message-search", + "description": "Search messages", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Restrict to a channel (UUID or #name)" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max results" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "id", + "channelId", + "createdAt", + "senderName", + "content" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "slock/message-search.js", + "sourceFile": "slock/message-search.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "message-send", + "description": "Send a message to a channel, DM, or thread (content sent verbatim)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "target", + "type": "str", + "required": true, + "positional": true, + "help": "\"#channel\", \"#channel:msgIdOrShort\", \"dm:@name\", \"dm:\", or channel UUID" + }, + { + "name": "content", + "type": "str", + "required": true, + "positional": true, + "help": "Message body (sent verbatim, no marker)" + }, + { + "name": "dry-run", + "type": "bool", + "default": false, + "required": false, + "help": "Print the planned payload without sending" + }, + { + "name": "as-task", + "type": "bool", + "default": false, + "required": false, + "help": "Create the message as a task (asTask)" + }, + { + "name": "attach", + "type": "str", + "required": false, + "help": "Comma-separated attachmentId UUIDs (upload separately first)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server (slug or id)" + } + ], + "columns": [ + "target", + "channelId", + "content", + "result", + "messageId" + ], + "type": "js", + "modulePath": "slock/message-send.js", + "sourceFile": "slock/message-send.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "reaction-add", + "description": "Add an emoji reaction to a message (POST /messages/:id/reactions). Idempotent server-side.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "messageId", + "type": "str", + "required": true, + "positional": true, + "help": "Full messageId UUID (short ids rejected)" + }, + { + "name": "emoji", + "type": "str", + "required": true, + "positional": true, + "help": "A single unicode emoji, e.g. 👍" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "messageId", + "emoji", + "result" + ], + "type": "js", + "modulePath": "slock/reaction-add.js", + "sourceFile": "slock/reaction-add.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "reaction-remove", + "description": "Remove your emoji reaction from a message (DELETE /messages/:id/reactions).", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "messageId", + "type": "str", + "required": true, + "positional": true, + "help": "Full messageId UUID (short ids rejected)" + }, + { + "name": "emoji", + "type": "str", + "required": true, + "positional": true, + "help": "The unicode emoji to remove, e.g. 👍" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "messageId", + "emoji", + "result" + ], + "type": "js", + "modulePath": "slock/reaction-remove.js", + "sourceFile": "slock/reaction-remove.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "server-list", + "description": "List slock servers you belong to; marks active per localStorage slug", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "id", + "slug", + "name", + "active" + ], + "type": "js", + "modulePath": "slock/server-list.js", + "sourceFile": "slock/server-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "server-use", + "description": "Set the active slock server (writes localStorage.slock_last_server_slug)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": true, + "positional": true, + "help": "server slug, \"#slug\", or UUID id" + } + ], + "columns": [ + "id", + "slug", + "name", + "written" + ], + "type": "js", + "modulePath": "slock/server-use.js", + "sourceFile": "slock/server-use.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-claim", + "description": "Claim a chat task (PATCH /tasks/:id/claim). Requires full task UUID (= message id).", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "taskId", + "type": "str", + "required": true, + "positional": true, + "help": "Full task UUID (= message id; short ids rejected)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "taskId", + "taskStatus", + "assigneeId", + "taskNumber" + ], + "type": "js", + "modulePath": "slock/task-claim.js", + "sourceFile": "slock/task-claim.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-convert", + "description": "Convert a message into a chat task (POST /tasks/convert-message). Accepts a message UUID or \"#channel:shortId\".", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "messageId", + "type": "str", + "required": true, + "positional": true, + "help": "Full message UUID, or \"#channel:shortId\" (short id expanded via /messages/context)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "id", + "taskNumber", + "title", + "taskStatus", + "channelId" + ], + "type": "js", + "modulePath": "slock/task-convert.js", + "sourceFile": "slock/task-convert.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-create", + "description": "Create a task in a channel (single title; batch 1-50 is server-supported but client surface is single — see backlog R4).", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "title", + "type": "str", + "required": true, + "positional": true, + "help": "Task title (single; batch TODO via R4)" + }, + { + "name": "desc", + "type": "str", + "required": false, + "help": "Optional description body for the task" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "id", + "taskNumber", + "title", + "taskStatus", + "channelId" + ], + "type": "js", + "modulePath": "slock/task-create.js", + "sourceFile": "slock/task-create.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-delete", + "description": "Delete a chat task (DELETE /tasks/:taskId). Requires --confirm — destructive, irreversible.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "taskId", + "type": "str", + "required": true, + "positional": true, + "help": "Full task UUID (= message id; short ids rejected)" + }, + { + "name": "confirm", + "type": "bool", + "default": false, + "required": false, + "help": "Required acknowledgement: deletion is irreversible" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "taskId", + "deleted" + ], + "type": "js", + "modulePath": "slock/task-delete.js", + "sourceFile": "slock/task-delete.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-get", + "description": "Fetch a task by channel + taskNumber (GET /tasks/channel/:channelId/number/:taskNumber).", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "number", + "type": "str", + "required": true, + "positional": true, + "help": "taskNumber (per-channel integer, as shown in \"task #N\")" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "id", + "taskNumber", + "title", + "taskStatus", + "assigneeId" + ], + "type": "js", + "modulePath": "slock/task-get.js", + "sourceFile": "slock/task-get.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-list", + "description": "List tasks (chat tasks = messages with task fields) attached to a channel. Optional --status filter.", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "status", + "type": "str", + "required": false, + "help": "Filter by status: todo|in_progress|in_review|done|closed" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "id", + "taskNumber", + "title", + "taskStatus", + "assigneeId" + ], + "type": "js", + "modulePath": "slock/task-list.js", + "sourceFile": "slock/task-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-list-server", + "description": "List tasks across all channels in the active server (GET /tasks/server). Optional --status filter.", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "status", + "type": "str", + "required": false, + "help": "Filter by status: todo|in_progress|in_review|done|closed" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "id", + "taskNumber", + "title", + "taskStatus", + "channelId", + "assigneeId" + ], + "type": "js", + "modulePath": "slock/task-list-server.js", + "sourceFile": "slock/task-list-server.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-status", + "description": "Set a task's status (PATCH /tasks/:taskId/status, body {status}). One of todo|in_progress|in_review|done|closed.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "taskId", + "type": "str", + "required": true, + "positional": true, + "help": "Full task UUID (= message id; short ids rejected)" + }, + { + "name": "status", + "type": "str", + "required": true, + "positional": true, + "help": "One of: todo|in_progress|in_review|done|closed" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "taskId", + "taskStatus", + "assigneeId", + "taskNumber" + ], + "type": "js", + "modulePath": "slock/task-status.js", + "sourceFile": "slock/task-status.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-unclaim", + "description": "Release ownership of a chat task (PATCH /tasks/:id/unclaim).", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "taskId", + "type": "str", + "required": true, + "positional": true, + "help": "Full task UUID (= message id; short ids rejected)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "taskId", + "taskStatus", + "assigneeId", + "taskNumber" + ], + "type": "js", + "modulePath": "slock/task-unclaim.js", + "sourceFile": "slock/task-unclaim.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "thread-done", + "description": "Mark a thread as done / hide it from the active list (POST /channels/threads/done)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "threadChannelId", + "type": "str", + "required": true, + "positional": true, + "help": "Thread channel UUID (from thread-list / message-read)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "threadChannelId", + "result" + ], + "type": "js", + "modulePath": "slock/thread-done.js", + "sourceFile": "slock/thread-done.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "thread-follow", + "description": "Follow the thread on a parent message (POST /channels/threads/follow)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "parentMessageId", + "type": "str", + "required": true, + "positional": true, + "help": "Full parent messageId UUID (short ids rejected)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "parentMessageId", + "threadChannelId", + "result" + ], + "type": "js", + "modulePath": "slock/thread-follow.js", + "sourceFile": "slock/thread-follow.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "thread-list", + "description": "List followed threads in the active server (GET /channels/threads/followed)", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "threadChannelId", + "parentMessageId", + "parentChannelName", + "unreadCount", + "replyCount", + "lastReplyAt" + ], + "type": "js", + "modulePath": "slock/thread-list.js", + "sourceFile": "slock/thread-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "thread-undone", + "description": "Restore a done thread to the active list (POST /channels/threads/undone)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "threadChannelId", + "type": "str", + "required": true, + "positional": true, + "help": "Thread channel UUID (from thread-list / message-read)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "threadChannelId", + "result" + ], + "type": "js", + "modulePath": "slock/thread-undone.js", + "sourceFile": "slock/thread-undone.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "thread-unfollow", + "description": "Stop following a thread (POST /channels/threads/unfollow)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "threadChannelId", + "type": "str", + "required": true, + "positional": true, + "help": "Thread channel UUID (from thread-list / message-read)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "threadChannelId", + "result" + ], + "type": "js", + "modulePath": "slock/thread-unfollow.js", + "sourceFile": "slock/thread-unfollow.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "unread-summary", + "description": "Global unread counts across every server you belong to.", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "serverId", + "slug", + "name", + "unreadCount" + ], + "type": "js", + "modulePath": "slock/unread-summary.js", + "sourceFile": "slock/unread-summary.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "whoami", + "description": "Show the current logged-in slock account", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "id", + "name", + "email" + ], + "type": "js", + "modulePath": "slock/whoami.js", + "sourceFile": "slock/whoami.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "spotify", + "name": "auth", + "description": "Authenticate with Spotify (OAuth — run once)", + "access": "write", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "next", + "description": "Skip to next track", + "access": "write", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "pause", + "description": "Pause playback", + "access": "write", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "play", + "description": "Resume playback or search and play a track/artist", + "access": "write", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "default": "", + "required": false, + "positional": true, + "help": "Track or artist to play (optional)" + } + ], + "columns": [ + "track", + "artist", + "status" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "prev", + "description": "Skip to previous track", + "access": "write", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "queue", + "description": "Add a track to the playback queue", + "access": "write", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Track to add to queue" + } + ], + "columns": [ + "track", + "artist", + "status" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "repeat", + "description": "Set repeat mode (off / track / context)", + "access": "write", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "mode", + "type": "str", + "default": "context", + "required": false, + "positional": true, + "help": "off / track / context", + "choices": [ + "off", + "track", + "context" + ] + } + ], + "columns": [ + "repeat" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "search", + "description": "Search for tracks", + "access": "read", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results (default: 10)" + } + ], + "columns": [ + "track", + "artist", + "album", + "uri" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "shuffle", + "description": "Toggle shuffle on/off", + "access": "write", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "state", + "type": "str", + "default": "on", + "required": false, + "positional": true, + "help": "on or off", + "choices": [ + "on", + "off" + ] + } + ], + "columns": [ + "shuffle" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "status", + "description": "Show current playback status", + "access": "read", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "track", + "artist", + "album", + "status", + "progress" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "volume", + "description": "Set playback volume (0-100)", + "access": "write", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "level", + "type": "int", + "default": 50, + "required": true, + "positional": true, + "help": "Volume 0–100" + } + ], + "columns": [ + "volume" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "stackoverflow", + "name": "bounties", + "description": "Active bounties on Stack Overflow", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max number of results" + } + ], + "columns": [ + "rank", + "id", + "bounty", + "title", + "score", + "answers", + "views", + "is_answered", + "tags", + "author", + "creation_date", + "url" + ], + "type": "js", + "modulePath": "stackoverflow/bounties.js", + "sourceFile": "stackoverflow/bounties.js" + }, + { + "site": "stackoverflow", + "name": "hot", + "description": "Hot Stack Overflow questions", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max number of results" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "answers", + "views", + "is_answered", + "tags", + "author", + "creation_date", + "url" + ], + "type": "js", + "modulePath": "stackoverflow/hot.js", + "sourceFile": "stackoverflow/hot.js" + }, + { + "site": "stackoverflow", + "name": "read", + "description": "Read a Stack Overflow question with answers and comments", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Stack Overflow question id (numeric, e.g. 79935770)" + }, + { + "name": "answers-limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max answers to include (1-100; accepted answer always included first)" + }, + { + "name": "comments-limit", + "type": "int", + "default": 5, + "required": false, + "help": "Max comments per question/answer (1-100)" + }, + { + "name": "max-length", + "type": "int", + "default": 4000, + "required": false, + "help": "Max characters per body / answer / comment (min 100)" + } + ], + "columns": [ + "type", + "author", + "score", + "accepted", + "text" + ], + "type": "js", + "modulePath": "stackoverflow/read.js", + "sourceFile": "stackoverflow/read.js" + }, + { + "site": "stackoverflow", + "name": "related", + "description": "List Stack Overflow questions related to a given question id.", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Stack Overflow question id (numeric, e.g. 79935770)." + }, + { + "name": "sort", + "type": "string", + "default": "rank", + "required": false, + "help": "Sort key: rank, activity, votes, creation (rank = SO relevance default)." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max related questions (1-100)." + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "answers", + "views", + "isAnswered", + "tags", + "author", + "createdAt", + "lastActivityAt", + "url" + ], + "type": "js", + "modulePath": "stackoverflow/related.js", + "sourceFile": "stackoverflow/related.js" + }, + { + "site": "stackoverflow", + "name": "search", + "description": "Search Stack Overflow questions", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "string", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max number of results" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "answers", + "views", + "is_answered", + "tags", + "author", + "creation_date", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "stackoverflow/search.js", + "sourceFile": "stackoverflow/search.js" + }, + { + "site": "stackoverflow", + "name": "tag", + "description": "List Stack Overflow questions tagged with a given tag (most active first).", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "tag", + "type": "string", + "required": true, + "positional": true, + "help": "Tag slug (e.g. python, rust, typescript)." + }, + { + "name": "sort", + "type": "string", + "default": "activity", + "required": false, + "help": "Sort key: activity, votes, creation, hot, week, month" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max questions to return (max 100)." + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "answers", + "views", + "isAnswered", + "tags", + "author", + "createdAt", + "lastActivityAt", + "url" + ], + "type": "js", + "modulePath": "stackoverflow/tag.js", + "sourceFile": "stackoverflow/tag.js" + }, + { + "site": "stackoverflow", + "name": "unanswered", + "description": "Top voted unanswered questions on Stack Overflow", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max number of results" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "answers", + "views", + "tags", + "author", + "creation_date", + "url" + ], + "type": "js", + "modulePath": "stackoverflow/unanswered.js", + "sourceFile": "stackoverflow/unanswered.js" + }, + { + "site": "stackoverflow", + "name": "user", + "description": "Find Stack Overflow users by display name (highest reputation first).", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "string", + "required": true, + "positional": true, + "help": "Display name (or substring) to search." + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max users to return (max 100)." + } + ], + "columns": [ + "userId", + "displayName", + "reputation", + "goldBadges", + "silverBadges", + "bronzeBadges", + "location", + "createdAt", + "lastAccessAt", + "url" + ], + "type": "js", + "modulePath": "stackoverflow/user.js", + "sourceFile": "stackoverflow/user.js" + }, + { + "site": "steam", + "name": "app", + "description": "Steam storefront detail for a single app id", + "access": "read", + "domain": "store.steampowered.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Numeric Steam app id (e.g. \"620\" for Portal 2)" + }, + { + "name": "currency", + "type": "str", + "default": "us", + "required": false, + "help": "Storefront country code (e.g. us / cn / jp / de)" + } + ], + "columns": [ + "id", + "name", + "type", + "isFree", + "releaseDate", + "developers", + "publishers", + "price", + "currency", + "metacritic", + "recommendations", + "genres", + "categories", + "shortDescription", + "website", + "url" + ], + "type": "js", + "modulePath": "steam/app.js", + "sourceFile": "steam/app.js" + }, + { + "site": "steam", + "name": "search", + "description": "Search the Steam storefront by name keyword", + "access": "read", + "domain": "store.steampowered.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"portal\", \"stardew\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-50)" + }, + { + "name": "currency", + "type": "str", + "default": "us", + "required": false, + "help": "Storefront country code (e.g. us / cn / jp / de)" + } + ], + "columns": [ + "rank", + "id", + "name", + "price", + "currency", + "metascore", + "platforms", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "steam/search.js", + "sourceFile": "steam/search.js" + }, + { + "site": "steam", + "name": "top-sellers", + "description": "Steam top selling games", + "access": "read", + "domain": "store.steampowered.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of games" + } + ], + "columns": [ + "rank", + "name", + "price", + "discount", + "url" + ], + "type": "js", + "modulePath": "steam/top-sellers.js", + "sourceFile": "steam/top-sellers.js" + }, + { + "site": "substack", + "name": "feed", + "description": "Substack popular posts Feed", + "access": "read", + "domain": "substack.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "category", + "type": "str", + "default": "all", + "required": false, + "help": "Post category: all, tech, business, culture, politics, science, health" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" + } + ], + "columns": [ + "rank", + "title", + "author", + "date", + "readTime", + "url" + ], + "type": "js", + "modulePath": "substack/feed.js", + "sourceFile": "substack/feed.js", + "navigateBefore": "https://substack.com" + }, + { + "site": "substack", + "name": "publication", + "description": "Get a specific Substack Newsletter latest posts", + "access": "read", + "domain": "substack.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "Newsletter URL(for example https://example.substack.com)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" + } + ], + "columns": [ + "rank", + "title", + "date", + "description", + "url" + ], + "type": "js", + "modulePath": "substack/publication.js", + "sourceFile": "substack/publication.js", + "navigateBefore": "https://substack.com" + }, + { + "site": "substack", + "name": "search", + "description": "Search Substack posts and newsletters", + "access": "read", + "domain": "substack.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "type", + "type": "str", + "default": "posts", + "required": false, + "help": "Search type(posts=posts, publications=Newsletter)", + "choices": [ + "posts", + "publications" + ] + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results to return" + } + ], + "columns": [ + "rank", + "title", + "author", + "date", + "description", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "substack/search.js", + "sourceFile": "substack/search.js" + }, + { + "site": "suno", + "name": "download", + "description": "Download an existing Suno clip (MP3 + optional WAV/M4A/video) by id", + "access": "write", + "domain": "suno.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "clip", + "type": "str", + "required": true, + "positional": true, + "help": "Clip UUID or https://suno.com/song/ URL" + }, + { + "name": "formats", + "type": "str", + "required": false, + "help": "Comma-separated formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" + }, + { + "name": "op", + "type": "str", + "required": false, + "help": "Output directory (default: ~/Music/suno)" + }, + { + "name": "confirm-paid", + "type": "boolean", + "default": false, + "required": false, + "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." + } + ], + "columns": [ + "status", + "clip", + "title", + "files", + "link" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "suno/download.js", + "sourceFile": "suno/download.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "suno", + "name": "generate", + "description": "Generate music with Suno (V5.5 chirp-fenix by default) and download clips locally", + "access": "write", + "domain": "suno.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": false, + "positional": true, + "help": "Simple-mode description (ignored when --lyrics is provided)" + }, + { + "name": "lyrics", + "type": "str", + "required": false, + "help": "Custom-mode lyrics (with [Verse]/[Chorus] metatags). Triggers Custom mode." + }, + { + "name": "tags", + "type": "str", + "required": false, + "help": "Custom-mode style tags (genre, BPM, instruments...). Used with --lyrics." + }, + { + "name": "negative-tags", + "type": "str", + "required": false, + "help": "Custom-mode style exclusions (e.g. \"no vocals, no autotune\"). Used with --lyrics." + }, + { + "name": "title", + "type": "str", + "required": false, + "help": "Song title (default: auto-derived from prompt)" + }, + { + "name": "instrumental", + "type": "boolean", + "default": false, + "required": false, + "help": "No vocals" + }, + { + "name": "model", + "type": "str", + "required": false, + "help": "Model id: chirp-fenix, chirp-bluejay, chirp-v4, chirp-v3-5. Default: chirp-fenix" + }, + { + "name": "weirdness", + "type": "str", + "required": false, + "help": "Creative weirdness slider (0..1). Default: 0.5" + }, + { + "name": "style-weight", + "type": "str", + "required": false, + "help": "Style adherence slider (0..1). Default: 0.5" + }, + { + "name": "formats", + "type": "str", + "required": false, + "help": "Comma-separated download formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" + }, + { + "name": "op", + "type": "str", + "required": false, + "help": "Output directory (default: ~/Music/suno)" + }, + { + "name": "timeout", + "type": "int", + "default": 300, + "required": false, + "help": "Max seconds to wait for clips to finish (default: 300)" + }, + { + "name": "sd", + "type": "boolean", + "default": false, + "required": false, + "help": "Skip download; only print clip ids and Suno URLs" + }, + { + "name": "confirm-paid", + "type": "boolean", + "default": false, + "required": false, + "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." + } + ], + "columns": [ + "status", + "clip", + "title", + "files", + "link" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "suno/generate.js", + "sourceFile": "suno/generate.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "suno", + "name": "list", + "description": "List recent Suno clips in your library (id, title, status, created_at, link)", + "access": "read", + "domain": "suno.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max clips to list (default: 20)" + }, + { + "name": "page", + "type": "int", + "default": 0, + "required": false, + "help": "Pagination offset, 0-based (default: 0)" + } + ], + "columns": [ + "rank", + "clip", + "title", + "status", + "created", + "link" + ], + "type": "js", + "modulePath": "suno/list.js", + "sourceFile": "suno/list.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "suno", + "name": "login", + "description": "Open suno login", + "access": "write", + "domain": "suno.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "suno/auth.js", + "sourceFile": "suno/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "suno", + "name": "status", + "description": "Check Suno login, plan, credit balance, and captcha readiness", + "access": "read", + "domain": "suno.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status", + "Plan", + "Credits", + "Monthly", + "Captcha" + ], + "type": "js", + "modulePath": "suno/status.js", + "sourceFile": "suno/status.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "suno", + "name": "whoami", + "description": "Show the current logged-in suno account", + "access": "read", + "domain": "suno.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" + ], + "type": "js", + "modulePath": "suno/auth.js", + "sourceFile": "suno/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "tiktok", + "name": "comment", + "description": "Post a comment on a TikTok video", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "TikTok video URL (https://www.tiktok.com/@user/video/)" + }, + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Comment text (≤150 chars)" + } + ], + "columns": [ + "url", + "text", + "result" + ], + "type": "js", + "modulePath": "tiktok/comment.js", + "sourceFile": "tiktok/comment.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "creator-videos", + "description": "TikTok Studio creator content list (views/likes/comments/saves/shares)", + "access": "read", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of creator videos to return (max 250)" + }, + { + "name": "cursor", + "type": "string", + "default": "0", + "required": false, + "help": "Non-negative TikTok Studio pagination cursor" + } + ], + "columns": [ + "video_id", + "title", + "date", + "views", + "likes", + "comments", + "saves", + "shares", + "url" + ], + "type": "js", + "modulePath": "tiktok/creator-videos.js", + "sourceFile": "tiktok/creator-videos.js", + "navigateBefore": "https://www.tiktok.com/tiktokstudio/content" + }, + { + "site": "tiktok", + "name": "explore", + "description": "Get trending TikTok videos from the recommend feed via page-context APIs", + "access": "read", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of videos to return (max 120)" + } + ], + "columns": [ + "index", + "id", + "author", + "url", + "cover", + "title", + "desc", + "plays", + "likes", + "comments", + "shares", + "createTime" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "tiktok/explore.js", + "sourceFile": "tiktok/explore.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "follow", + "description": "Follow a TikTok user by username", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "TikTok username (without @)" + } + ], + "columns": [ + "username", + "url", + "result" + ], + "type": "js", + "modulePath": "tiktok/follow.js", + "sourceFile": "tiktok/follow.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "following", + "description": "List accounts the logged-in user follows on TikTok via page-context APIs", + "access": "read", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of accounts (max 200)" + } + ], + "columns": [ + "index", + "username", + "name", + "secUid", + "verified", + "followers", + "following", + "url" + ], + "type": "js", + "modulePath": "tiktok/following.js", + "sourceFile": "tiktok/following.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "friends", + "description": "Get TikTok friend / who-to-follow suggestions via page-context APIs", + "access": "read", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of suggestions (max 100)" + } + ], + "columns": [ + "index", + "username", + "name", + "secUid", + "verified", + "followers", + "following", + "url" + ], + "type": "js", + "modulePath": "tiktok/friends.js", + "sourceFile": "tiktok/friends.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "like", + "description": "Like a TikTok video", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "TikTok video URL" + } + ], + "columns": [ + "status", + "likes", + "url" + ], + "type": "js", + "modulePath": "tiktok/like.js", + "sourceFile": "tiktok/like.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "live", + "description": "Browse TikTok live streams via page-context APIs", + "access": "read", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of streams (max 60)" + } + ], + "columns": [ + "index", + "streamer", + "name", + "title", + "viewers", + "likes", + "secUid", + "url" + ], + "type": "js", + "modulePath": "tiktok/live.js", + "sourceFile": "tiktok/live.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "login", + "description": "Open tiktok login", + "access": "write", + "domain": "tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "sec_uid", + "username", + "nickname", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "tiktok/auth.js", + "sourceFile": "tiktok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "tiktok", + "name": "notifications", + "description": "Read TikTok inbox notifications (likes, comments, mentions, followers) via page-context APIs", + "access": "read", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Number of notifications (max 100)" + }, + { + "name": "type", + "type": "str", + "default": "all", + "required": false, + "help": "Notification type", + "choices": [ + "all", + "likes", + "comments", + "mentions", + "followers" + ] + } + ], + "columns": [ + "index", + "id", + "from", + "text", + "createTime" + ], + "type": "js", + "modulePath": "tiktok/notifications.js", + "sourceFile": "tiktok/notifications.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "profile", + "description": "Get TikTok user profile info", + "access": "read", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "TikTok username (without @)" + } + ], + "columns": [ + "username", + "name", + "followers", + "following", + "likes", + "videos", + "verified", + "bio" + ], + "type": "js", + "modulePath": "tiktok/profile.js", + "sourceFile": "tiktok/profile.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "save", + "description": "Add a TikTok video to Favorites", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "TikTok video URL" + } + ], + "columns": [ + "status", + "url" + ], + "type": "js", + "modulePath": "tiktok/save.js", + "sourceFile": "tiktok/save.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "search", + "description": "Search TikTok videos", + "access": "read", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "desc", + "author", + "url", + "plays", + "likes", + "comments", + "shares" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "tiktok/search.js", + "sourceFile": "tiktok/search.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "unfollow", + "description": "Unfollow a TikTok user by username", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "TikTok username (without @)" + } + ], + "columns": [ + "username", + "url", + "result" + ], + "type": "js", + "modulePath": "tiktok/unfollow.js", + "sourceFile": "tiktok/unfollow.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "unlike", + "description": "Unlike a TikTok video", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "TikTok video URL" + } + ], + "columns": [ + "status", + "likes", + "url" + ], + "type": "js", + "modulePath": "tiktok/unlike.js", + "sourceFile": "tiktok/unlike.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "unsave", + "description": "Remove a TikTok video from Favorites", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "TikTok video URL" + } + ], + "columns": [ + "status", + "url" + ], + "type": "js", + "modulePath": "tiktok/unsave.js", + "sourceFile": "tiktok/unsave.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "user", + "description": "Get recent videos from a TikTok user via page-context APIs", + "access": "read", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "TikTok username (without @)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of videos to return (max 120)" + } + ], + "columns": [ + "index", + "id", + "source", + "author", + "url", + "cover", + "title", + "desc", + "plays", + "likes", + "comments", + "shares", + "createTime" + ], + "type": "js", + "modulePath": "tiktok/user.js", + "sourceFile": "tiktok/user.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "whoami", + "description": "Show the current logged-in tiktok account", + "access": "read", + "domain": "tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "sec_uid", + "username", + "nickname" + ], + "type": "js", + "modulePath": "tiktok/auth.js", + "sourceFile": "tiktok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "trae-solo", + "name": "automation-list", + "description": "List Trae SOLO Automation tab content. Default tab is \"Configured\"; pass --tab to switch.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "tab", + "type": "str", + "default": "configured", + "required": false, + "help": "Tab to view: configured / run-history / task-template" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "" + } + ], + "columns": [ + "Index", + "Title", + "Summary" + ], + "type": "js", + "modulePath": "trae-solo/automation.js", + "sourceFile": "trae-solo/automation.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "cookies", + "description": "List cookies on the Trae SOLO renderer (JS-visible via document.cookie; httpOnly cookies not shown).", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version" + ], + "type": "js", + "modulePath": "trae-solo/renderer-storage.js", + "sourceFile": "trae-solo/renderer-storage.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "extensions-list", + "description": "List VSCode extensions installed in Trae SOLO (~/.trae/extensions/extensions.json). Works while Trae is closed.", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "Index", + "Workspace Id", + "Kind", + "Target", + "Modified", + "Id", + "Version", + "Source", + "Installed" + ], + "type": "js", + "modulePath": "trae-solo/workspaces-fs.js", + "sourceFile": "trae-solo/workspaces-fs.js" + }, + { + "site": "trae-solo", + "name": "history", + "description": "List Trae SOLO projects and the tasks within each (from the project-list view sidebar).", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "project", + "type": "str", + "required": false, + "help": "Filter by project name (substring, case-insensitive)" + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Max tasks per project" + } + ], + "columns": [ + "Project", + "Task Index", + "Task" + ], + "type": "js", + "modulePath": "trae-solo/history.js", + "sourceFile": "trae-solo/history.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "idb-list", + "description": "List IndexedDB databases on the Trae SOLO renderer. Trae ships an @byted/ve-rtc DB used by the Volcengine RTC voice/video infrastructure.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version" + ], + "type": "js", + "modulePath": "trae-solo/renderer-storage.js", + "sourceFile": "trae-solo/renderer-storage.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "mode", + "description": "Read or switch TRAE SOLO between Code mode and Work mode.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "target", + "type": "str", + "required": false, + "positional": true, + "help": "Target mode: code or work. Omit to read current." + } + ], + "columns": [ + "Status", + "Mode" + ], + "type": "js", + "modulePath": "trae-solo/mode.js", + "sourceFile": "trae-solo/mode.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "model", + "description": "Read or switch the current AI model in TRAE SOLO. Without arguments, reports the current model. With argument (substring, case-insensitive), switches to a matching model. Pass --list to enumerate available models.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": false, + "positional": true, + "help": "Target model name (substring match, case-insensitive). Omit to read current." + }, + { + "name": "list", + "type": "boolean", + "default": false, + "required": false, + "help": "List all available models (does not switch)" + } + ], + "columns": [ + "Status", + "Model" + ], + "type": "js", + "modulePath": "trae-solo/model.js", + "sourceFile": "trae-solo/model.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "recent-workspaces", + "description": "Show Trae SOLO's recently-opened workspaces (the File → Open Recent menu, stored under key \"history.recentlyOpenedPathsList\" in state.vscdb).", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "" + } + ], + "columns": [ + "Index", + "Key", + "Kind", + "Path" + ], + "type": "js", + "modulePath": "trae-solo/state-fs.js", + "sourceFile": "trae-solo/state-fs.js" + }, + { + "site": "trae-solo", + "name": "settings-read", + "description": "Parse and pretty-print Trae SOLO user settings.json (~/Library/Application Support/TRAE SOLO/User/settings.json). Handles VSCode JSONC syntax (line comments + trailing commas).", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "trae-solo/settings.js", + "sourceFile": "trae-solo/settings.js" + }, + { + "site": "trae-solo", + "name": "skill-category", + "description": "Filter Skills Marketplace by category. Pass --list to see categories.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": false, + "positional": true, + "help": "Category name (substring; case-insensitive). Common: All / Developer Tools / Data Analysis / UI Design / Content Creation / Productivity" + }, + { + "name": "list", + "type": "boolean", + "default": false, + "required": false, + "help": "List available categories" + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "" + } + ], + "columns": [ + "Index", + "Name", + "Description" + ], + "type": "js", + "modulePath": "trae-solo/skill.js", + "sourceFile": "trae-solo/skill.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "skill-fs-installed", + "description": "List INSTALLED Trae SOLO skills (managedSkills entry in ~/.trae/skill-config.json).", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "Index", + "Name", + "Description", + "Source" + ], + "type": "js", + "modulePath": "trae-solo/skill-fs.js", + "sourceFile": "trae-solo/skill-fs.js" + }, + { + "site": "trae-solo", + "name": "skill-fs-list", + "description": "List all Trae SOLO skills present on disk under ~/.trae/skills/. Reads SKILL.md front-matter for descriptions. Works while Trae is closed.", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 200, + "required": false, + "help": "Max rows" + } + ], + "columns": [ + "Index", + "Name", + "Description", + "Source" + ], + "type": "js", + "modulePath": "trae-solo/skill-fs.js", + "sourceFile": "trae-solo/skill-fs.js" + }, + { + "site": "trae-solo", + "name": "skill-fs-show", + "description": "Print a skill's SKILL.md content + on-disk path.", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Skill name (folder under ~/.trae/skills/)" + } + ], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "trae-solo/skill-fs.js", + "sourceFile": "trae-solo/skill-fs.js" + }, + { + "site": "trae-solo", + "name": "skill-list", + "description": "List Trae SOLO Skills — by default the Marketplace; pass --installed to list installed ones.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "installed", + "type": "boolean", + "default": false, + "required": false, + "help": "List installed skills instead of the marketplace" + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Max rows to return" + } + ], + "columns": [ + "Index", + "Name", + "Description" + ], + "type": "js", + "modulePath": "trae-solo/skill.js", + "sourceFile": "trae-solo/skill.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "skill-search", + "description": "Filter Skills Marketplace by keyword.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (substring)" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max rows" + } + ], + "columns": [ + "Index", + "Name", + "Description" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "trae-solo/skill.js", + "sourceFile": "trae-solo/skill.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "state-get", + "description": "Read a single key from Trae SOLO's globalStorage state.vscdb. Pass --workspace to query a per-workspace DB instead. Returns parsed JSON if the value is JSON.", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "State key (use state-keys to discover)" + }, + { + "name": "workspace", + "type": "str", + "required": false, + "help": "Workspace id (from workspaces-list) to query a per-workspace DB" + }, + { + "name": "max-bytes", + "type": "int", + "default": 8000, + "required": false, + "help": "Truncate value to this many bytes" + } + ], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "trae-solo/state-fs.js", + "sourceFile": "trae-solo/state-fs.js" + }, + { + "site": "trae-solo", + "name": "state-keys", + "description": "List all keys present in Trae SOLO's globalStorage state.vscdb (VSCode-style UI/agent state). Pass --workspace to query a per-workspace DB instead. Use state-get to read a specific value. (See renderer storage-keys for browser-side LS/SS.)", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "filter", + "type": "str", + "required": false, + "help": "Case-insensitive substring filter over keys" + }, + { + "name": "workspace", + "type": "str", + "required": false, + "help": "Workspace id (from workspaces-list) to query a per-workspace DB" + }, + { + "name": "limit", + "type": "int", + "default": 200, + "required": false, + "help": "" + } + ], + "columns": [ + "Index", + "Key", + "Kind", + "Path" + ], + "type": "js", + "modulePath": "trae-solo/state-fs.js", + "sourceFile": "trae-solo/state-fs.js" + }, + { + "site": "trae-solo", + "name": "status", + "description": "Check active CDP connection to Trae SOLO Desktop", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "trae-solo/status.js", + "sourceFile": "trae-solo/status.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "storage-get", + "description": "Read a single localStorage / sessionStorage value on the Trae SOLO renderer.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "Storage key (use storage-keys to discover)" + }, + { + "name": "storage", + "type": "str", + "default": "local", + "required": false, + "help": "\"local\" or \"session\"" + }, + { + "name": "max-bytes", + "type": "int", + "default": 4000, + "required": false, + "help": "Truncate value to this many chars" + } + ], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "trae-solo/renderer-storage.js", + "sourceFile": "trae-solo/renderer-storage.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "storage-keys", + "description": "List localStorage / sessionStorage keys on the Trae SOLO renderer (CDP). For the on-disk VSCode state.vscdb, see state-keys.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "storage", + "type": "str", + "default": "local", + "required": false, + "help": "\"local\" or \"session\"" + }, + { + "name": "filter", + "type": "str", + "required": false, + "help": "Case-insensitive substring filter" + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Max rows to return" + } + ], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version" + ], + "type": "js", + "modulePath": "trae-solo/renderer-storage.js", + "sourceFile": "trae-solo/renderer-storage.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "task-fs-list", + "description": "List Trae SOLO task ids from disk (snapshot/ + agentconfig/.json). Works while Trae is closed.", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "" + } + ], + "columns": [ + "Index", + "Task Id", + "Has Snapshot", + "Has Config", + "Modified", + "Phase", + "Turn Id", + "Commit" + ], + "type": "js", + "modulePath": "trae-solo/task-fs.js", + "sourceFile": "trae-solo/task-fs.js" + }, + { + "site": "trae-solo", + "name": "task-fs-show", + "description": "Show the workspace tree at a given chat-turn ref (via git ls-tree). Pass --turn to pick a turn; otherwise the latest after-chat-turn ref.", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "task-id", + "type": "str", + "required": true, + "positional": true, + "help": "Task UUID" + }, + { + "name": "turn", + "type": "str", + "required": false, + "help": "Specific turn id (omit for latest after-chat-turn)" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "" + } + ], + "columns": [ + "Mode", + "Path", + "Size" + ], + "type": "js", + "modulePath": "trae-solo/task-fs.js", + "sourceFile": "trae-solo/task-fs.js" + }, + { + "site": "trae-solo", + "name": "task-fs-turns", + "description": "Show the chat-turn timeline for a Trae SOLO task as git tags (before-chat-turn-* / after-chat-turn-*).", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "task-id", + "type": "str", + "required": true, + "positional": true, + "help": "Task UUID (folder name under snapshot/)" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "" + } + ], + "columns": [ + "Index", + "Task Id", + "Has Snapshot", + "Has Config", + "Modified", + "Phase", + "Turn Id", + "Commit" + ], + "type": "js", + "modulePath": "trae-solo/task-fs.js", + "sourceFile": "trae-solo/task-fs.js" + }, + { + "site": "trae-solo", + "name": "user-rules", + "description": "Print Trae SOLO user rules (~/.trae/user_rules.md).", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "trae-solo/user-rules.js", + "sourceFile": "trae-solo/user-rules.js" + }, + { + "site": "trae-solo", + "name": "workspaces-list", + "description": "List Trae SOLO workspaceStorage entries (~/Library/.../TRAE SOLO/User/workspaceStorage//), resolving each workspace.json to its single-folder path or multi-folder workspace target. Works while Trae is closed.", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "" + } + ], + "columns": [ + "Index", + "Workspace Id", + "Kind", + "Target", + "Modified", + "Id", + "Version", + "Source", + "Installed" + ], + "type": "js", + "modulePath": "trae-solo/workspaces-fs.js", + "sourceFile": "trae-solo/workspaces-fs.js" + }, + { + "site": "trip", + "name": "attraction", + "description": "Search Trip.com attractions and experiences by destination keyword", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Destination or attraction keyword (e.g. Tokyo / Paris / Louvre)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results (1-50)" + } + ], + "columns": [ + "rank", + "name", + "rating", + "reviews", + "booked", + "price", + "currency", + "url" + ], + "type": "js", + "modulePath": "trip/attraction.js", + "sourceFile": "trip/attraction.js", + "navigateBefore": false + }, + { + "site": "trip", + "name": "car", + "description": "List Trip.com car-rental vehicles for a city (category, model, seats, daily price)", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "city", + "type": "str", + "required": true, + "positional": true, + "help": "Numeric Trip.com carhire city id (discover via the carhire search box)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of vehicles (1-50)" + } + ], + "columns": [ + "rank", + "category", + "vehicle", + "seats", + "price", + "currency", + "url" + ], + "type": "js", + "modulePath": "trip/car.js", + "sourceFile": "trip/car.js", + "navigateBefore": false + }, + { + "site": "trip", + "name": "deals", + "description": "List Trip.com live promotions from the Top Deals hub: campaign title, offer, discount, and link", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of deals (1-50)" + } + ], + "columns": [ + "rank", + "title", + "offer", + "discount", + "url" + ], + "type": "js", + "modulePath": "trip/deals.js", + "sourceFile": "trip/deals.js", + "navigateBefore": false + }, + { + "site": "trip", + "name": "flight", + "description": "Search Trip.com one-way flights by IATA route + departure date", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "from", + "type": "str", + "required": true, + "positional": true, + "help": "Departure IATA code (e.g. LON / LHR)" + }, + { + "name": "to", + "type": "str", + "required": true, + "positional": true, + "help": "Arrival IATA code (e.g. NYC / JFK)" + }, + { + "name": "date", + "type": "str", + "required": true, + "help": "Departure date (YYYY-MM-DD)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of flights (1-50)" + } + ], + "columns": [ + "rank", + "airline", + "departureTime", + "departureAirport", + "arrivalTime", + "arrivalAirport", + "duration", + "stops", + "price", + "currency", + "url" + ], + "type": "js", + "modulePath": "trip/flight.js", + "sourceFile": "trip/flight.js", + "navigateBefore": false + }, + { + "site": "trip", + "name": "flight-round", + "description": "Search Trip.com round-trip flights by IATA route + depart/return dates", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "from", + "type": "str", + "required": true, + "positional": true, + "help": "Departure IATA code (e.g. LON / LHR)" + }, + { + "name": "to", + "type": "str", + "required": true, + "positional": true, + "help": "Arrival IATA code (e.g. NYC / JFK)" + }, + { + "name": "depart", + "type": "str", + "required": true, + "help": "Outbound date (YYYY-MM-DD)" + }, + { + "name": "return", + "type": "str", + "required": true, + "help": "Return date (YYYY-MM-DD)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of flights (1-50)" + } + ], + "columns": [ + "rank", + "airline", + "departureTime", + "departureAirport", + "arrivalTime", + "arrivalAirport", + "duration", + "stops", + "price", + "currency", + "url" + ], + "type": "js", + "modulePath": "trip/flight-round.js", + "sourceFile": "trip/flight-round.js", + "navigateBefore": false + }, + { + "site": "trip", + "name": "hotel", + "description": "Show a Trip.com hotel detail by id (rating breakdown, amenities, check-in/out policy)", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Numeric Trip.com hotel id (discover via the hotels list; e.g. 715233)" + } + ], + "columns": [ + "hotelId", + "name", + "enName", + "star", + "score", + "scoreLabel", + "reviewCount", + "ratingBreakdown", + "facilities", + "checkInOut", + "cityName", + "address", + "lat", + "lon", + "url" + ], + "type": "js", + "modulePath": "trip/hotel.js", + "sourceFile": "trip/hotel.js", + "navigateBefore": false + }, + { + "site": "trip", + "name": "hotel-search", + "description": "List Trip.com hotels for a city id + check-in/out date range", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "city", + "type": "str", + "required": true, + "positional": true, + "help": "Numeric Trip.com city id (discover via the hotels search box; e.g. 338 for London)" + }, + { + "name": "checkin", + "type": "str", + "required": true, + "help": "Check-in date (YYYY-MM-DD)" + }, + { + "name": "checkout", + "type": "str", + "required": true, + "help": "Check-out date (YYYY-MM-DD)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of hotels (1-50)" + } + ], + "columns": [ + "rank", + "name", + "score", + "reviewLabel", + "reviews", + "location", + "room", + "price", + "currency", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "trip/hotel-search.js", + "sourceFile": "trip/hotel-search.js", + "navigateBefore": false + }, + { + "site": "trip", + "name": "package", + "description": "Search Trip.com flight+hotel packages by route + dates; lists the package flight options priced at the bundle rate", + "access": "read", + "domain": "trip.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "from", + "type": "str", + "required": true, + "positional": true, + "help": "Origin city keyword (e.g. Seoul / London / Bangkok)" + }, + { + "name": "to", + "type": "str", + "required": true, + "positional": true, + "help": "Destination city keyword (e.g. Tokyo / Paris / Singapore)" + }, + { + "name": "depart", + "type": "str", + "required": true, + "help": "Outbound date (YYYY-MM-DD)" + }, + { + "name": "return", + "type": "str", + "required": true, + "help": "Return date (YYYY-MM-DD)" + }, + { + "name": "adults", + "type": "int", + "default": 2, + "required": false, + "help": "Number of adults (1-9, default 2)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of packages (1-50)" + } + ], + "columns": [ + "rank", + "airline", + "flightNo", + "from", + "to", + "departure", + "arrival", + "stops", + "price", + "currency" + ], + "type": "js", + "modulePath": "trip/package.js", + "sourceFile": "trip/package.js" + }, + { + "site": "trip", + "name": "search", + "description": "Suggest Trip.com destinations (cities, airports) for a keyword; resolves the ids the other commands take", + "access": "read", + "domain": "trip.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Destination keyword (e.g. Tokyo / Bali / London)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of suggestions (1-50)" + } + ], + "columns": [ + "rank", + "name", + "type", + "cityId", + "airportCode", + "province", + "country" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "trip/search.js", + "sourceFile": "trip/search.js" + }, + { + "site": "trip", + "name": "tour", + "description": "Search Trip.com tour packages by destination keyword (private or group tours)", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Destination or tour keyword (e.g. Tokyo / Kyoto / Bali)" + }, + { + "name": "type", + "type": "str", + "default": "private", + "required": false, + "help": "Tour line: private or group (default private)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of tours (1-50)" + } + ], + "columns": [ + "rank", + "name", + "type", + "rating", + "reviews", + "price", + "currency", + "url" + ], + "type": "js", + "modulePath": "trip/tour.js", + "sourceFile": "trip/tour.js", + "navigateBefore": false + }, + { + "site": "trip", + "name": "train", + "description": "Show a Trip.com train route timetable (departure/arrival times, duration, changes)", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "from", + "type": "str", + "required": true, + "positional": true, + "help": "Departure city (e.g. London / Paris / Shanghai)" + }, + { + "name": "to", + "type": "str", + "required": true, + "positional": true, + "help": "Arrival city (e.g. Manchester / Lyon / Beijing)" + }, + { + "name": "country", + "type": "str", + "required": true, + "help": "Route country slug (e.g. uk / france / italy / spain / germany / china)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of journeys (1-50)" + } + ], + "columns": [ + "rank", + "departureTime", + "fromStation", + "arrivalTime", + "toStation", + "duration", + "changes", + "url" + ], + "type": "js", + "modulePath": "trip/train.js", + "sourceFile": "trip/train.js", + "navigateBefore": false + }, + { + "site": "trip", + "name": "transfer", + "description": "List Trip.com airport-transfer vehicles for a city + airport (type, seats, from-price)", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "city", + "type": "str", + "required": true, + "positional": true, + "help": "Airport city (e.g. Bangkok / Beijing / Da Nang)" + }, + { + "name": "airport", + "type": "str", + "required": true, + "positional": true, + "help": "3-letter airport IATA code (e.g. DMK / PKX / DAD)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of vehicles (1-50)" + } + ], + "columns": [ + "rank", + "type", + "passengers", + "luggage", + "price", + "currency", + "url" + ], + "type": "js", + "modulePath": "trip/transfer.js", + "sourceFile": "trip/transfer.js", + "navigateBefore": false + }, + { + "site": "tvmaze", + "name": "search", + "description": "TVmaze TV show search by title (returns id, name, network, premiered/ended, rating)", + "access": "read", + "domain": "tvmaze.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "string", + "required": true, + "positional": true, + "help": "TV show title or fragment to search for" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max rows to return (1-50)" + } + ], + "columns": [ + "rank", + "id", + "name", + "type", + "language", + "genres", + "status", + "premiered", + "ended", + "network", + "rating", + "matchScore", + "summary", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "tvmaze/search.js", + "sourceFile": "tvmaze/search.js" + }, + { + "site": "tvmaze", + "name": "show", + "description": "Single TVmaze TV show detail by id (network, schedule, rating, IMDB/TheTVDB cross-refs)", + "access": "read", + "domain": "tvmaze.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "int", + "required": true, + "positional": true, + "help": "TVmaze show id (positive integer)" + } + ], + "columns": [ + "id", + "name", + "type", + "language", + "genres", + "status", + "premiered", + "ended", + "runtime", + "averageRuntime", + "network", + "country", + "schedule", + "rating", + "imdb", + "thetvdb", + "officialSite", + "summary", + "url" + ], + "type": "js", + "modulePath": "tvmaze/show.js", + "sourceFile": "tvmaze/show.js" + }, + { + "site": "twitter", + "name": "accept", + "description": "Auto-accept DM requests containing specific keywords", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "query", + "type": "string", + "required": true, + "positional": true, + "help": "Keywords to match (comma-separated for OR, e.g. \"invoice,urgent\")" + }, + { + "name": "max", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum number of requests to accept (default: 20)" + }, + { + "name": "timeout", + "type": "int", + "default": 600, + "required": false, + "help": "Max seconds for the overall command (default: 600 — batch op)" + } + ], + "columns": [ + "index", + "status", + "user", + "message" + ], + "type": "js", + "modulePath": "twitter/accept.js", + "sourceFile": "twitter/accept.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "article", + "description": "Fetch a Twitter Article (long-form content) and export as Markdown", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "tweet-id", + "type": "string", + "required": true, + "positional": true, + "help": "Tweet ID or URL containing the article" + } + ], + "columns": [ + "title", + "author", + "content", + "url" + ], + "type": "js", + "modulePath": "twitter/article.js", + "sourceFile": "twitter/article.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "block", + "description": "Block a Twitter user", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Twitter screen name (without @)" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/block.js", + "sourceFile": "twitter/block.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "bookmark", + "description": "Bookmark a tweet", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "Tweet URL to bookmark" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/bookmark.js", + "sourceFile": "twitter/bookmark.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "bookmark-folder", + "description": "Read the tweets inside a single Twitter/X bookmark folder. Get the folder id from `webcmd twitter bookmark-folders`.", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "folder-id", + "type": "string", + "required": true, + "positional": true, + "help": "Folder id from `webcmd twitter bookmark-folders`." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum number of bookmarks to return (default 20)." + }, + { + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank the folder by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (saved-time) ordering." + } + ], + "columns": [ + "id", + "author", + "text", + "likes", + "retweets", + "bookmarks", + "created_at", + "url", + "has_media", + "media_urls", + "media_posters" + ], + "type": "js", + "modulePath": "twitter/bookmark-folder.js", + "sourceFile": "twitter/bookmark-folder.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "bookmark-folders", + "description": "List your Twitter/X bookmark folders (the user-created collections under Bookmarks). Returns folder id, name, item count, and created_at.", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "id", + "name", + "items", + "created_at" + ], + "type": "js", + "modulePath": "twitter/bookmark-folders.js", + "sourceFile": "twitter/bookmark-folders.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "bookmarks", + "description": "Fetch your Twitter/X bookmarks (the logged-in user's saved tweets, newest first)", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum number of bookmarks to return (default 20)." + }, + { + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank the bookmarks by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (saved-time) ordering." + } + ], + "columns": [ + "id", + "author", + "text", + "likes", + "retweets", + "bookmarks", + "created_at", + "url", + "has_media", + "media_urls", + "media_posters" + ], + "type": "js", + "modulePath": "twitter/bookmarks.js", + "sourceFile": "twitter/bookmarks.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "delete", + "description": "Delete a specific tweet by URL", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "The URL of the tweet to delete" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/delete.js", + "sourceFile": "twitter/delete.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "device-follow", + "description": "Read the /i/timeline device-follow notification stream (tweets aggregated under a bell-icon \"new posts from @userA and N others\" notification)", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum number of tweets to return (1-200, default 20)" + }, + { + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank by weighted engagement and return the top N. Default 0 keeps upstream ordering." + } + ], + "columns": [ + "id", + "author", + "text", + "likes", + "retweets", + "replies", + "views", + "created_at", + "url" + ], + "type": "js", + "modulePath": "twitter/device-follow.js", + "sourceFile": "twitter/device-follow.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "download", + "description": "Download Twitter/X media (images and videos). Provide either to fetch every media item from their profile via the GraphQL UserMedia endpoint with cursor pagination, or --tweet-url to download a single tweet.", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": false, + "positional": true, + "help": "Twitter username (with or without @) to scan their profile media. Either or --tweet-url is required." + }, + { + "name": "tweet-url", + "type": "str", + "required": false, + "help": "Single tweet URL to download. Use this OR , not both required at once." + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum number of media items to download when scanning a profile (default 10). Ignored when --tweet-url is used." + }, + { + "name": "output", + "type": "str", + "default": "./twitter-downloads", + "required": false, + "help": "Output directory (default ./twitter-downloads). A per-source subdir is created inside.", + "file": { + "direction": "output", + "pathKind": "directory", + "multiple": false + } + } + ], + "columns": [ + "index", + "tweet_id", + "url", + "type", + "status", + "size" + ], + "type": "js", + "modulePath": "twitter/download.js", + "sourceFile": "twitter/download.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "follow", + "description": "Follow a Twitter user", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Twitter screen name (without @)" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/follow.js", + "sourceFile": "twitter/follow.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "follow-batch", + "description": "Follow multiple Twitter/X users from a comma-separated username list", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "usernames", + "type": "string", + "required": true, + "positional": true, + "help": "Comma-separated Twitter/X screen names, with or without @" + }, + { + "name": "delay-ms", + "type": "int", + "default": 3000, + "required": false, + "help": "Delay between follow attempts in milliseconds" + } + ], + "columns": [ + "username", + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/follow-batch.js", + "sourceFile": "twitter/follow-batch.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "followers", + "description": "Get accounts following a Twitter/X user (defaults to the logged-in user when no user is given)", + "access": "read", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "user", + "type": "string", + "required": false, + "positional": true, + "help": "Twitter/X handle (with or without @). Omit to fetch followers of the currently logged-in account." + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Maximum number of follower rows to return (default 50). Must be a positive integer." + } + ], + "columns": [ + "screen_name", + "name", + "bio" + ], + "type": "js", + "modulePath": "twitter/followers.js", + "sourceFile": "twitter/followers.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "following", + "description": "Get accounts a Twitter/X user is following (defaults to the logged-in user when no user is given)", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "user", + "type": "string", + "required": false, + "positional": true, + "help": "Twitter/X handle (with or without @). Omit to fetch the accounts the currently logged-in user follows." + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Maximum number of following rows to return (default 50). Must be a positive integer." + } + ], + "columns": [ + "screen_name", + "name", + "bio", + "followers" + ], + "type": "js", + "modulePath": "twitter/following.js", + "sourceFile": "twitter/following.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "hide-reply", + "description": "Hide a reply on your tweet (useful for hiding bot/spam replies)", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "The URL of the reply tweet to hide" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/hide-reply.js", + "sourceFile": "twitter/hide-reply.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "like", + "description": "Like a specific tweet", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "The URL of the tweet to like" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/like.js", + "sourceFile": "twitter/like.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "likes", + "description": "Fetch liked tweets of a Twitter user (defaults to the logged-in user when no username is given)", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "string", + "required": false, + "positional": true, + "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum number of liked tweets to return (default 20)." + }, + { + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank the liked tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (recency) ordering." + } + ], + "columns": [ + "id", + "author", + "name", + "text", + "likes", + "retweets", + "created_at", + "url", + "has_media", + "media_urls", + "media_posters" + ], + "type": "js", + "modulePath": "twitter/likes.js", + "sourceFile": "twitter/likes.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "list-add", + "description": "Add a user to a Twitter/X list you own (no-op if already a member)", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "listId", + "type": "string", + "required": true, + "positional": true, + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" + }, + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Twitter/X handle to add (with or without @)" + } + ], + "columns": [ + "listId", + "username", + "userId", + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/list-add.js", + "sourceFile": "twitter/list-add.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "list-add-batch", + "description": "Add multiple users to a Twitter/X list you own from a comma-separated username list", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "listId", + "type": "string", + "required": true, + "positional": true, + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" + }, + { + "name": "usernames", + "type": "string", + "required": true, + "positional": true, + "help": "Comma-separated Twitter/X handles to add (with or without @)" + }, + { + "name": "interval", + "type": "int", + "default": 5, + "required": false, + "help": "Seconds to wait between account additions (default: 5)" + }, + { + "name": "timeout", + "type": "int", + "default": 600, + "required": false, + "help": "Max seconds for the overall batch command (default: 600)" + } + ], + "columns": [ + "listId", + "username", + "userId", + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/list-add-batch.js", + "sourceFile": "twitter/list-add-batch.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "list-create", + "description": "Create a new Twitter/X list (returns the new list id)", + "access": "write", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "name", + "type": "string", + "required": true, + "positional": true, + "help": "List name (max 25 chars)" + }, + { + "name": "description", + "type": "string", + "default": "", + "required": false, + "help": "Optional list description (max 100 chars)" + }, + { + "name": "mode", + "type": "string", + "default": "public", + "required": false, + "help": "public | private" + } + ], + "columns": [ + "id", + "name", + "description", + "mode", + "status" + ], + "type": "js", + "modulePath": "twitter/list-create.js", + "sourceFile": "twitter/list-create.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "list-delete", + "description": "Delete a Twitter/X list you own after explicit confirmation", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "listId", + "type": "string", + "required": true, + "positional": true, + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "Required. Set --confirm true to delete the list." + }, + { + "name": "timeout", + "type": "int", + "default": 300, + "required": false, + "help": "Max seconds for the overall delete command (default: 300)" + } + ], + "columns": [ + "listId", + "name", + "members", + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/list-delete.js", + "sourceFile": "twitter/list-delete.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "list-remove", + "description": "Remove a user from a Twitter/X list you own (toggles via UI; no-op if not currently a member)", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "listId", + "type": "string", + "required": true, + "positional": true, + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" + }, + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Twitter/X handle to remove (with or without @)" + } + ], + "columns": [ + "listId", + "username", + "userId", + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/list-remove.js", + "sourceFile": "twitter/list-remove.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "list-remove-batch", + "description": "Remove multiple users from a Twitter/X list you own from a comma-separated username list", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "listId", + "type": "string", + "required": true, + "positional": true, + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" + }, + { + "name": "usernames", + "type": "string", + "required": true, + "positional": true, + "help": "Comma-separated Twitter/X handles to remove (with or without @)" + }, + { + "name": "interval", + "type": "int", + "default": 5, + "required": false, + "help": "Seconds to wait between account removals (default: 5)" + }, + { + "name": "timeout", + "type": "int", + "default": 600, + "required": false, + "help": "Max seconds for the overall batch command (default: 600)" + } + ], + "columns": [ + "listId", + "username", + "userId", + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/list-remove-batch.js", + "sourceFile": "twitter/list-remove-batch.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "list-tweets", + "description": "Fetch tweets from a Twitter/X list timeline", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "listId", + "type": "string", + "required": true, + "positional": true, + "help": "Numeric ID of a Twitter/X list (e.g. from `webcmd twitter lists`)" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "" + }, + { + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank the list timeline by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the list's native (recency) ordering." + } + ], + "columns": [ + "id", + "author", + "bio", + "text", + "likes", + "retweets", + "replies", + "created_at", + "url", + "has_media", + "media_urls", + "media_posters", + "card", + "quoted_tweet" + ], + "type": "js", + "modulePath": "twitter/list-tweets.js", + "sourceFile": "twitter/list-tweets.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "lists", + "description": "Get Twitter/X lists for the logged-in user (owned + subscribed)", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Maximum number of lists to return (default 50)." + } + ], + "columns": [ + "id", + "name", + "members", + "followers", + "mode" + ], + "type": "js", + "modulePath": "twitter/lists.js", + "sourceFile": "twitter/lists.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "login", + "description": "Open twitter login", + "access": "write", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "username", + "url", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "twitter/auth.js", + "sourceFile": "twitter/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "twitter", + "name": "notifications", + "description": "Get your Twitter/X notifications (the logged-in user's likes/replies/follows feed, newest first)", + "access": "read", + "domain": "x.com", + "strategy": "intercept", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum number of notifications to return (default 20)." + } + ], + "columns": [ + "id", + "action", + "author", + "text", + "url" + ], + "type": "js", + "modulePath": "twitter/notifications.js", + "sourceFile": "twitter/notifications.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "post", + "description": "Post a new tweet/thread", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "The text content of the tweet" + }, + { + "name": "images", + "type": "string", + "required": false, + "help": "Image paths, comma-separated, max 4 (jpg/png/gif/webp)", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + } + ], + "columns": [ + "status", + "message", + "text", + "id", + "url" + ], + "type": "js", + "modulePath": "twitter/post.js", + "sourceFile": "twitter/post.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "profile", + "description": "Fetch a Twitter user profile — bio, stats, etc. (defaults to the logged-in user when no username is given)", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "string", + "required": false, + "positional": true, + "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." + } + ], + "columns": [ + "screen_name", + "name", + "bio", + "location", + "url", + "followers", + "following", + "tweets", + "likes", + "verified", + "created_at" + ], + "type": "js", + "modulePath": "twitter/profile.js", + "sourceFile": "twitter/profile.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "quote", + "description": "Quote-tweet a specific tweet with your own text, optionally with a local or remote image", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "The URL of the tweet to quote" + }, + { + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "The text content of your quote" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Optional local image path to attach to the quote tweet", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + }, + { + "name": "image-url", + "type": "str", + "required": false, + "help": "Optional remote image URL to download and attach to the quote tweet" + } + ], + "columns": [ + "status", + "message", + "text" + ], + "type": "js", + "modulePath": "twitter/quote.js", + "sourceFile": "twitter/quote.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "reply", + "description": "Reply to a specific tweet, optionally with a local or remote image", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "The URL of the tweet to reply to" + }, + { + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "The text content of your reply" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Optional local image path to attach to the reply", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + }, + { + "name": "image-url", + "type": "str", + "required": false, + "help": "Optional remote image URL to download and attach to the reply" + } + ], + "columns": [ + "status", + "message", + "text", + "url" + ], + "type": "js", + "modulePath": "twitter/reply.js", + "sourceFile": "twitter/reply.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "reply-dm", + "description": "Send a message to recent DM conversations", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "Message text to send (e.g. \"my messaging handle wxkabi\")" + }, + { + "name": "max", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum number of conversations to reply to (default: 20)" + }, + { + "name": "skip-replied", + "type": "boolean", + "default": true, + "required": false, + "help": "Skip conversations where you already sent the same text (default: true)" + }, + { + "name": "timeout", + "type": "int", + "default": 600, + "required": false, + "help": "Max seconds for the overall command (default: 600 — batch op)" + } + ], + "columns": [ + "index", + "status", + "user", + "message" + ], + "type": "js", + "modulePath": "twitter/reply-dm.js", + "sourceFile": "twitter/reply-dm.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "retweet", + "description": "Retweet a specific tweet", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "The URL of the tweet to retweet" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/retweet.js", + "sourceFile": "twitter/retweet.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "search", + "description": "Search Twitter/X for tweets, with optional --from / --has / --exclude / --product filters mapped to X's search operators", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "string", + "required": true, + "positional": true, + "help": "Search query. Raw X operators (e.g. \"exact phrase\", #tag, OR, lang:en, since:YYYY-MM-DD, from:, since:) are passed through unchanged." + }, + { + "name": "filter", + "type": "string", + "default": "top", + "required": false, + "help": "Legacy alias for --product. Kept for backwards compatibility; if --product is set it wins.", + "choices": [ + "top", + "live" + ] + }, + { + "name": "product", + "type": "string", + "required": false, + "help": "Which X search tab to read: top (default), live (Latest), photos, videos. Maps to the f= URL param.", + "choices": [ + "top", + "live", + "photos", + "videos" + ] + }, + { + "name": "from", + "type": "string", + "required": false, + "help": "Restrict to tweets authored by . Leading @ is stripped. Equivalent to appending `from:` to the query." + }, + { + "name": "has", + "type": "string", + "required": false, + "help": "Restrict to tweets that have media|images|videos|links|replies. Maps to X's `filter:` operator.", + "choices": [ + "media", + "images", + "videos", + "links", + "replies" + ] + }, + { + "name": "exclude", + "type": "string", + "required": false, + "help": "Exclude tweets matching : replies|retweets|media|links. Maps to X's `-filter:` operator (retweets → -filter:nativeretweets).", + "choices": [ + "replies", + "retweets", + "media", + "links" + ] + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Maximum number of tweets to return (default 15). Result count after server-side filtering." + }, + { + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank the results by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X's native ordering." + } + ], + "columns": [ + "id", + "author", + "bio", + "text", + "created_at", + "likes", + "views", + "url", + "has_media", + "media_urls", + "media_posters", + "card", + "quoted_tweet" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "twitter/search.js", + "sourceFile": "twitter/search.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "thread", + "description": "Get a tweet thread (original + all replies)", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "tweet-id", + "type": "string", + "required": true, + "positional": true, + "help": "Tweet numeric ID (e.g. 1234567890) or full status URL" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "" + }, + { + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank the thread by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the conversation's structural ordering." + } + ], + "columns": [ + "id", + "author", + "bio", + "text", + "likes", + "retweets", + "url", + "has_media", + "media_urls", + "media_posters", + "card", + "quoted_tweet" + ], + "type": "js", + "modulePath": "twitter/thread.js", + "sourceFile": "twitter/thread.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "timeline", + "description": "Fetch the logged-in user's home timeline (for-you algorithmic feed by default; pass --type following for the chronological feed of accounts you follow)", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "type", + "type": "str", + "default": "for-you", + "required": false, + "help": "Which home-timeline feed to read. Default for-you (algorithmic). Use following for the chronological feed of accounts you follow.", + "choices": [ + "for-you", + "following" + ] + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum number of tweets to return (default 20)." + }, + { + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank the timeline by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X's native ordering." + } + ], + "columns": [ + "id", + "author", + "bio", + "text", + "likes", + "retweets", + "replies", + "quotes", + "bookmarks", + "views", + "created_at", + "url", + "has_media", + "media_urls", + "media_posters", + "card", + "quoted_tweet" + ], + "type": "js", + "modulePath": "twitter/timeline.js", + "sourceFile": "twitter/timeline.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "trending", + "description": "Twitter/X trending topics", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of trends to show" + } + ], + "columns": [ + "rank", + "topic", + "category" + ], + "type": "js", + "modulePath": "twitter/trending.js", + "sourceFile": "twitter/trending.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "tweets", + "description": "Fetch a Twitter user's most recent tweets (chronological, excludes pinned; defaults to the logged-in user when no username is given)", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "string", + "required": false, + "positional": true, + "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max tweets to return (1-10000; fetched across cursor pages)" + }, + { + "name": "page-delay", + "type": "int", + "default": 2, + "required": false, + "help": "Seconds to wait between paginated timeline requests to reduce rate-limit risk. Use 0 to disable." + }, + { + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank the tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the chronological ordering." + } + ], + "columns": [ + "id", + "author", + "created_at", + "is_retweet", + "text", + "likes", + "retweets", + "replies", + "views", + "url", + "has_media", + "media_urls", + "media_posters", + "quoted_tweet" + ], + "type": "js", + "modulePath": "twitter/tweets.js", + "sourceFile": "twitter/tweets.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "unblock", + "description": "Unblock a Twitter user", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Twitter screen name (without @)" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/unblock.js", + "sourceFile": "twitter/unblock.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "unbookmark", + "description": "Remove a tweet from bookmarks", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "Tweet URL to unbookmark" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/unbookmark.js", + "sourceFile": "twitter/unbookmark.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "unfollow", + "description": "Unfollow a Twitter user", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Twitter screen name (without @)" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/unfollow.js", + "sourceFile": "twitter/unfollow.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "unlike", + "description": "Remove a like from a specific tweet", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "The URL of the tweet to unlike" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/unlike.js", + "sourceFile": "twitter/unlike.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "unretweet", + "description": "Undo a retweet on a specific tweet", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "The URL of the tweet to unretweet" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "twitter/unretweet.js", + "sourceFile": "twitter/unretweet.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "whoami", + "description": "Show the current logged-in twitter account", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "username", + "url" + ], + "type": "js", + "modulePath": "twitter/auth.js", + "sourceFile": "twitter/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "uiverse", + "name": "code", + "description": "Export Uiverse component code (HTML, CSS, React, or Vue)", + "access": "read", + "domain": "uiverse.io", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": true, + "positional": true, + "help": "Uiverse URL or author/slug identifier" + }, + { + "name": "target", + "type": "str", + "required": true, + "help": "Code target to export", + "choices": [ + "html", + "css", + "react", + "vue" + ] + } + ], + "columns": [ + "target", + "username", + "slug", + "language", + "length" + ], + "type": "js", + "modulePath": "uiverse/code.js", + "sourceFile": "uiverse/code.js", + "navigateBefore": "https://uiverse.io" + }, + { + "site": "uiverse", + "name": "preview", + "description": "Capture a screenshot of the Uiverse preview element", + "access": "read", + "domain": "uiverse.io", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": true, + "positional": true, + "help": "Uiverse URL or author/slug identifier" + }, + { + "name": "output", + "type": "str", + "required": false, + "help": "Output image path (defaults to a temp file)" + }, + { + "name": "padding", + "type": "int", + "default": 8, + "required": false, + "help": "Extra padding around the captured preview in pixels" + } + ], + "columns": [ + "username", + "slug", + "width", + "height", + "output" + ], + "type": "js", + "modulePath": "uiverse/preview.js", + "sourceFile": "uiverse/preview.js", + "navigateBefore": "https://uiverse.io" + }, + { + "site": "upwork", + "name": "detail", + "aliases": [ + "job", + "view" + ], + "description": "Read the full Upwork job posting by ciphertext id (e.g. ~022054964136512093518)", + "access": "read", + "domain": "www.upwork.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Job ciphertext id (~01… / ~02…) or full /jobs/~02… URL" + } + ], + "columns": [ + "id", + "title", + "type", + "budget", + "experienceLevel", + "workload", + "category", + "skills", + "description", + "clientCountry", + "clientSpent", + "clientHires", + "clientRating", + "proposalsCount", + "publishedOn", + "url" + ], + "type": "js", + "modulePath": "upwork/detail.js", + "sourceFile": "upwork/detail.js", + "navigateBefore": false + }, + { + "site": "upwork", + "name": "feed", + "aliases": [ + "best-matches" + ], + "description": "Upwork personalized jobs feed (best-matches | most-recent) — requires login", + "access": "read", + "domain": "www.upwork.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "tab", + "type": "str", + "default": "best-matches", + "required": false, + "positional": true, + "help": "Feed tab: best-matches | most-recent" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max rows to return (1-50, capped at one page)" + } + ], + "columns": [ + "rank", + "id", + "title", + "type", + "budget", + "experienceLevel", + "proposalsTier", + "skills", + "clientCountry", + "clientRating", + "publishedOn", + "url" + ], + "type": "js", + "modulePath": "upwork/feed.js", + "sourceFile": "upwork/feed.js", + "navigateBefore": false + }, + { + "site": "upwork", + "name": "login", + "description": "Open upwork login", + "access": "write", + "domain": "upwork.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "ciphertext", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "upwork/auth.js", + "sourceFile": "upwork/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "upwork", + "name": "search", + "description": "Upwork keyword job search (logged-in browser session, US site)", + "access": "read", + "domain": "www.upwork.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Job keyword (skill / title / company)" + }, + { + "name": "location", + "type": "string", + "default": "", + "required": false, + "help": "Country/city filter (e.g. \"United States\", \"Remote\")" + }, + { + "name": "category", + "type": "string", + "default": "", + "required": false, + "help": "Category uid filter (advanced; from job detail `category` slug)" + }, + { + "name": "sort", + "type": "string", + "default": "recency", + "required": false, + "help": "Sort: recency | relevance | client_total_charge | client_total_reviews" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number (1-based)" + }, + { + "name": "per_page", + "type": "int", + "default": 10, + "required": false, + "help": "Rows per page (10-50, capped at one page)" + } + ], + "columns": [ + "rank", + "id", + "title", + "type", + "budget", + "experienceLevel", + "proposalsTier", + "skills", + "clientCountry", + "clientRating", + "publishedOn", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "upwork/search.js", + "sourceFile": "upwork/search.js", + "navigateBefore": false + }, + { + "site": "upwork", + "name": "whoami", + "description": "Show the current logged-in upwork account", + "access": "read", + "domain": "upwork.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "ciphertext" + ], + "type": "js", + "modulePath": "upwork/auth.js", + "sourceFile": "upwork/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "web", + "name": "fetch-browser", + "description": "Fetch any web page and export as Markdown", + "access": "read", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "help": "Any web page URL" + }, + { + "name": "output", + "type": "str", + "default": "./web-articles", + "required": false, + "help": "Output directory" + }, + { + "name": "download-images", + "type": "boolean", + "default": true, + "required": false, + "help": "Download images locally" + }, + { + "name": "wait", + "type": "int", + "default": 3, + "required": false, + "help": "Seconds to wait after page load" + }, + { + "name": "wait-for", + "type": "str", + "required": false, + "valueRequired": true, + "help": "CSS selector to wait for in the main document or same-origin iframes" + }, + { + "name": "wait-until", + "type": "str", + "default": "domstable", + "required": false, + "help": "Readiness policy after navigation: domstable or networkidle", + "choices": [ + "domstable", + "networkidle" + ] + }, + { + "name": "frames", + "type": "str", + "default": "same-origin", + "required": false, + "help": "Iframe handling mode: relevant same-origin, all-same-origin, or none", + "choices": [ + "same-origin", + "all-same-origin", + "none" + ] + }, + { + "name": "diagnose", + "type": "boolean", + "default": false, + "required": false, + "help": "Print render diagnostics (frames, empty containers, XHR/API-like requests) to stderr" + }, + { + "name": "stdout", + "type": "boolean", + "default": false, + "required": false, + "help": "Print markdown to stdout instead of saving to a file" + } + ], + "columns": [ + "title", + "author", + "publish_time", + "status", + "size", + "saved" + ], + "type": "js", + "modulePath": "web/fetch-browser.js", + "sourceFile": "web/fetch-browser.js", + "navigateBefore": false + }, + { + "site": "wikidata", + "name": "entity", + "description": "Fetch a Wikidata entity by Q/P/L id (label, description, aliases, claim summary)", + "access": "read", + "domain": "www.wikidata.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Entity id (e.g. Q937 = Albert Einstein, P31 = instance of)" + }, + { + "name": "language", + "type": "str", + "default": "en", + "required": false, + "help": "Display language (ISO 639, falls back to English when missing)" + } + ], + "columns": [ + "qid", + "type", + "label", + "description", + "aliases", + "claimPropertyCount", + "sitelinkCount", + "enwikiTitle", + "modified", + "url" + ], + "type": "js", + "modulePath": "wikidata/entity.js", + "sourceFile": "wikidata/entity.js" + }, + { + "site": "wikidata", + "name": "search", + "description": "Search Wikidata items by keyword (returns Q-IDs)", + "access": "read", + "domain": "www.wikidata.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (label / alias)" + }, + { + "name": "language", + "type": "str", + "default": "en", + "required": false, + "help": "Search & display language (ISO 639, e.g. en, fr, zh)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max items (1-50)" + } + ], + "columns": [ + "rank", + "qid", + "label", + "description", + "matchType", + "matchText", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "wikidata/search.js", + "sourceFile": "wikidata/search.js" + }, + { + "site": "wikipedia", + "name": "page", + "description": "Full plain-text extract of a Wikipedia article (optional paragraph cap).", + "access": "read", + "domain": "wikipedia.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "title", + "type": "string", + "required": true, + "positional": true, + "help": "Article title (e.g. \"Transformer (machine learning model)\")" + }, + { + "name": "lang", + "type": "string", + "default": "en", + "required": false, + "help": "Language code (en, zh, ja, de, ...)." + }, + { + "name": "paragraphs", + "type": "int", + "default": 0, + "required": false, + "help": "Cap to first N paragraphs (0 = full article)." + } + ], + "columns": [ + "title", + "description", + "pageId", + "paragraphs", + "extract", + "url" + ], + "type": "js", + "modulePath": "wikipedia/page.js", + "sourceFile": "wikipedia/page.js" + }, + { + "site": "wikipedia", + "name": "random", + "description": "Get a random Wikipedia article", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language code (e.g. en, zh, ja)" + } + ], + "columns": [ + "title", + "description", + "extract", + "url" + ], + "type": "js", + "modulePath": "wikipedia/random.js", + "sourceFile": "wikipedia/random.js" + }, + { + "site": "wikipedia", + "name": "search", + "description": "Search Wikipedia articles", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language code (e.g. en, zh, ja)" + } + ], + "columns": [ + "title", + "snippet", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "wikipedia/search.js", + "sourceFile": "wikipedia/search.js" + }, + { + "site": "wikipedia", + "name": "summary", + "description": "Get Wikipedia article summary", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "title", + "type": "str", + "required": true, + "positional": true, + "help": "Article title (e.g. \"Transformer (machine learning model)\")" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language code (e.g. en, zh, ja)" + } + ], + "columns": [ + "title", + "description", + "extract", + "url" + ], + "type": "js", + "modulePath": "wikipedia/summary.js", + "sourceFile": "wikipedia/summary.js" + }, + { + "site": "wikipedia", + "name": "trending", + "description": "Most-read Wikipedia articles (yesterday)", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language code (e.g. en, zh, ja)" + } + ], + "columns": [ + "rank", + "title", + "description", + "views" + ], + "type": "js", + "modulePath": "wikipedia/trending.js", + "sourceFile": "wikipedia/trending.js" + }, + { + "site": "wttr", + "name": "current", + "description": "Current weather conditions for a location (city, lat,lon, or airport code)", + "access": "read", + "domain": "wttr.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "location", + "type": "str", + "required": true, + "positional": true, + "help": "City name, \"lat,lon\", airport ICAO code, or \"@domain\"" + } + ], + "columns": [ + "location", + "region", + "country", + "latitude", + "longitude", + "observedAt", + "tempC", + "tempF", + "feelsLikeC", + "feelsLikeF", + "description", + "humidity", + "cloudCover", + "pressure", + "precipMm", + "visibilityKm", + "uvIndex", + "windKmph", + "windDirection", + "windDirectionDegree" + ], + "type": "js", + "modulePath": "wttr/current.js", + "sourceFile": "wttr/current.js" + }, + { + "site": "wttr", + "name": "forecast", + "description": "Multi-day weather forecast (up to 3 days, wttr.in free tier max)", + "access": "read", + "domain": "wttr.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "location", + "type": "str", + "required": true, + "positional": true, + "help": "City name, \"lat,lon\", airport ICAO code, or \"@domain\"" + }, + { + "name": "days", + "type": "int", + "default": 3, + "required": false, + "help": "Max forecast days (1-3, wttr.in caps the response at 3 days)" + } + ], + "columns": [ + "rank", + "date", + "minTempC", + "maxTempC", + "avgTempC", + "minTempF", + "maxTempF", + "avgTempF", + "sunHour", + "totalSnowCm", + "uvIndex", + "description", + "sunrise", + "sunset" + ], + "type": "js", + "modulePath": "wttr/forecast.js", + "sourceFile": "wttr/forecast.js" + }, + { + "site": "yahoo", + "name": "search", + "description": "Search Yahoo (powered by Bing)", + "access": "read", + "domain": "search.yahoo.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 7, + "required": false, + "help": "Number of results per page (max 7)" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number (1, 2, 3...). Yahoo returns ~7 results per page" + } + ], + "columns": [ + "rank", + "title", + "url", + "snippet" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "yahoo/search.js", + "sourceFile": "yahoo/search.js" + }, + { + "site": "yahoo-finance", + "name": "quote", + "description": "Yahoo Finance stock quote", + "access": "read", + "domain": "finance.yahoo.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" + } + ], + "columns": [ + "symbol", + "name", + "price", + "change", + "changePercent", + "open", + "high", + "low", + "volume", + "marketCap" + ], + "type": "js", + "modulePath": "yahoo-finance/quote.js", + "sourceFile": "yahoo-finance/quote.js", + "navigateBefore": "https://finance.yahoo.com" + }, + { + "site": "yollomi", + "name": "background", + "description": "Generate AI background for a product/object image (5 credits)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image URL (upload via \"webcmd yollomi upload\" first)" + }, + { + "name": "prompt", + "type": "str", + "default": "", + "required": false, + "help": "Background description (optional)" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "yollomi/background.js", + "sourceFile": "yollomi/background.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "edit", + "description": "Edit images with AI text prompts (Qwen image edit)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Input image URL (upload via \"webcmd yollomi upload\" first)" + }, + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Editing instruction (e.g. \"Make it look vintage\")" + }, + { + "name": "model", + "type": "str", + "default": "qwen-image-edit", + "required": false, + "help": "Edit model", + "choices": [ + "qwen-image-edit", + "qwen-image-edit-plus" + ] + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "credits", + "url" + ], + "type": "js", + "modulePath": "yollomi/edit.js", + "sourceFile": "yollomi/edit.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "face-swap", + "description": "Swap faces between two photos (3 credits)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "source", + "type": "str", + "required": true, + "help": "Source face image URL" + }, + { + "name": "target", + "type": "str", + "required": true, + "help": "Target photo URL" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "yollomi/face-swap.js", + "sourceFile": "yollomi/face-swap.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "generate", + "description": "Generate images with AI (text-to-image or image-to-image)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Text prompt describing the image" + }, + { + "name": "model", + "type": "str", + "default": "z-image-turbo", + "required": false, + "help": "Model ID (z-image-turbo, flux-schnell, nano-banana, flux-2-pro, ...)" + }, + { + "name": "ratio", + "type": "str", + "default": "1:1", + "required": false, + "help": "Aspect ratio", + "choices": [ + "1:1", + "16:9", + "9:16", + "4:3", + "3:4" + ] + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Input image URL for image-to-image (upload via \"webcmd yollomi upload\" first)" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URLs, skip download" + } + ], + "columns": [ + "index", + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "yollomi/generate.js", + "sourceFile": "yollomi/generate.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "models", + "description": "List available Yollomi AI models (image, video, tools)", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "type", + "type": "str", + "default": "all", + "required": false, + "help": "Filter by model type", + "choices": [ + "all", + "image", + "video", + "tool" + ] + } + ], + "columns": [ + "type", + "model", + "credits", + "description" + ], + "type": "js", + "modulePath": "yollomi/models.js", + "sourceFile": "yollomi/models.js" + }, + { + "site": "yollomi", + "name": "object-remover", + "description": "Remove unwanted objects from images (3 credits)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image URL" + }, + { + "name": "mask", + "type": "str", + "required": true, + "positional": true, + "help": "Mask image URL (white = area to remove)" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "yollomi/object-remover.js", + "sourceFile": "yollomi/object-remover.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "remove-bg", + "description": "Remove image background with AI (free)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image URL to remove background from" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "yollomi/remove-bg.js", + "sourceFile": "yollomi/remove-bg.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "restore", + "description": "Restore old or damaged photos with AI (4 credits)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image URL to restore" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "yollomi/restore.js", + "sourceFile": "yollomi/restore.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "try-on", + "description": "Virtual try-on — see how clothes look on a person (3 credits)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "person", + "type": "str", + "required": true, + "help": "Person photo URL (upload via \"webcmd yollomi upload\" first)" + }, + { + "name": "cloth", + "type": "str", + "required": true, + "help": "Clothing image URL" + }, + { + "name": "cloth-type", + "type": "str", + "default": "upper", + "required": false, + "help": "Clothing type", + "choices": [ + "upper", + "lower", + "overall" + ] + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "yollomi/try-on.js", + "sourceFile": "yollomi/try-on.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "upload", + "description": "Upload an image or video to Yollomi (returns URL for other commands)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "file", + "type": "str", + "required": true, + "positional": true, + "help": "Local file path to upload" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "yollomi/upload.js", + "sourceFile": "yollomi/upload.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "upscale", + "description": "Upscale image resolution with AI (1 credit)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image URL to upscale" + }, + { + "name": "scale", + "type": "str", + "default": "2", + "required": false, + "help": "Upscale factor (2 or 4)", + "choices": [ + "2", + "4" + ] + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "scale", + "url" + ], + "type": "js", + "modulePath": "yollomi/upscale.js", + "sourceFile": "yollomi/upscale.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "video", + "description": "Generate videos with AI (text-to-video or image-to-video)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Text prompt describing the video" + }, + { + "name": "model", + "type": "str", + "default": "kling-2-1", + "required": false, + "help": "Model (kling-2-1, openai-sora-2, google-veo-3-1, wan-2-5-t2v, ...)" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Input image URL for image-to-video" + }, + { + "name": "ratio", + "type": "str", + "default": "16:9", + "required": false, + "help": "Aspect ratio", + "choices": [ + "1:1", + "16:9", + "9:16", + "4:3", + "3:4" + ] + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL, skip download" + } + ], + "columns": [ + "status", + "file", + "size", + "credits", + "url" + ], + "type": "js", + "modulePath": "yollomi/video.js", + "sourceFile": "yollomi/video.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "youtube", + "name": "channel", + "description": "Get YouTube channel info and recent videos", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Channel ID (UCxxxx) or handle (@name)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max recent videos (max 30)" + } + ], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "youtube/channel.js", + "sourceFile": "youtube/channel.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "comments", + "description": "Get YouTube video comments", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "YouTube video URL or video ID" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max comments (max 100)" + } + ], + "columns": [ + "rank", + "author", + "text", + "likes", + "replies", + "time" + ], + "type": "js", + "modulePath": "youtube/comments.js", + "sourceFile": "youtube/comments.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "feed", + "description": "Get YouTube homepage recommended videos", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max videos to return (default 20, max 100)" + } + ], + "columns": [ + "rank", + "title", + "channel", + "video_id", + "views", + "duration", + "published", + "url" + ], + "type": "js", + "modulePath": "youtube/feed.js", + "sourceFile": "youtube/feed.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "history", + "description": "Get YouTube watch history", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max videos to return (default 30, max 200)" + } + ], + "columns": [ + "rank", + "title", + "channel", + "views", + "duration", + "url" + ], + "type": "js", + "modulePath": "youtube/history.js", + "sourceFile": "youtube/history.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "like", + "description": "Like a YouTube video", + "access": "write", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "YouTube video URL or video ID" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "youtube/like.js", + "sourceFile": "youtube/like.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "login", + "description": "Open youtube login", + "access": "write", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "youtube/auth.js", + "sourceFile": "youtube/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "youtube", + "name": "playlist", + "description": "Get YouTube playlist info and video list", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Playlist URL or playlist ID (PLxxxxxx)" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max videos to return (default 50, max 200)" + } + ], + "columns": [ + "rank", + "title", + "channel", + "duration", + "views", + "published", + "url" + ], + "type": "js", + "modulePath": "youtube/playlist.js", + "sourceFile": "youtube/playlist.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "search", + "description": "Search YouTube videos", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (max 50)" + }, + { + "name": "type", + "type": "str", + "default": "", + "required": false, + "help": "Filter type: shorts, video, channel, playlist" + }, + { + "name": "upload", + "type": "str", + "default": "", + "required": false, + "help": "Upload date: hour, today, week, month, year" + }, + { + "name": "sort", + "type": "str", + "default": "", + "required": false, + "help": "Sort by: relevance, date, views, rating" + } + ], + "columns": [ + "rank", + "title", + "channel", + "views", + "duration", + "published", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "youtube/search.js", + "sourceFile": "youtube/search.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "subscribe", + "description": "Subscribe to a YouTube channel", + "access": "write", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "Channel ID (UCxxxx) or handle (@name)" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "youtube/subscribe.js", + "sourceFile": "youtube/subscribe.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "subscriptions", + "description": "List subscribed YouTube channels", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max channels to return (default 50)" + } + ], + "columns": [ + "rank", + "name", + "handle", + "subscribers", + "url" + ], + "type": "js", + "modulePath": "youtube/subscriptions.js", + "sourceFile": "youtube/subscriptions.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "transcript", + "description": "Get YouTube video transcript/subtitles", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "YouTube video URL or video ID" + }, + { + "name": "lang", + "type": "str", + "required": false, + "help": "Language code (e.g. en, zh-Hans). Omit to auto-select" + }, + { + "name": "mode", + "type": "str", + "default": "grouped", + "required": false, + "help": "Output mode: grouped (readable paragraphs) or raw (every segment)" + } + ], + "type": "js", + "modulePath": "youtube/transcript.js", + "sourceFile": "youtube/transcript.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "unlike", + "description": "Remove like from a YouTube video", + "access": "write", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "YouTube video URL or video ID" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "youtube/unlike.js", + "sourceFile": "youtube/unlike.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "unsubscribe", + "description": "Unsubscribe from a YouTube channel", + "access": "write", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "Channel ID (UCxxxx) or handle (@name)" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "youtube/unsubscribe.js", + "sourceFile": "youtube/unsubscribe.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "video", + "description": "Get YouTube video metadata (title, views, description, etc.)", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "YouTube video URL or video ID" + } + ], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "youtube/video.js", + "sourceFile": "youtube/video.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "watch-later", + "description": "Get your YouTube Watch Later queue", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max videos to return (default 50, max 200)" + } + ], + "columns": [ + "rank", + "title", + "channel", + "duration", + "views", + "published", + "url" + ], + "type": "js", + "modulePath": "youtube/watch-later.js", + "sourceFile": "youtube/watch-later.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "whoami", + "description": "Show the current logged-in youtube account", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name" + ], + "type": "js", + "modulePath": "youtube/auth.js", + "sourceFile": "youtube/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "zepto", + "name": "add-to-cart", + "description": "Add a Zepto product to cart", + "access": "write", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product", + "type": "str", + "required": true, + "positional": true, + "help": "Product URL from Zepto search results" + }, + { + "name": "quantity", + "type": "int", + "default": 1, + "required": false, + "help": "Quantity to add (max 12)" + } + ], + "columns": [ + "ok", + "product_id", + "quantity", + "item_count", + "message" + ], + "type": "js", + "modulePath": "zepto/add-to-cart.js", + "sourceFile": "zepto/add-to-cart.js", + "navigateBefore": false + }, + { + "site": "zepto", + "name": "cart", + "description": "Read Zepto cart line items", + "access": "read", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "rank", + "product_id", + "title", + "pack_size", + "quantity", + "price", + "mrp", + "availability" + ], + "type": "js", + "modulePath": "zepto/cart.js", + "sourceFile": "zepto/cart.js", + "navigateBefore": false + }, + { + "site": "zepto", + "name": "checkout", + "description": "Open Zepto checkout review without placing an order", + "access": "write", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "ok", + "stage", + "item_count", + "next_action", + "url" + ], + "type": "js", + "modulePath": "zepto/checkout.js", + "sourceFile": "zepto/checkout.js", + "navigateBefore": false + }, + { + "site": "zepto", + "name": "location", + "description": "Show the selected Zepto delivery location", + "access": "read", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "selected", + "label", + "area", + "city", + "pincode", + "hasCoordinates", + "source" + ], + "type": "js", + "modulePath": "zepto/location.js", + "sourceFile": "zepto/location.js", + "navigateBefore": false + }, + { + "site": "zepto", + "name": "login", + "description": "Open zepto login", + "access": "write", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "zepto/auth.js", + "sourceFile": "zepto/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "zepto", + "name": "place-order", + "description": "Submit a real Zepto order only when --confirm true is passed", + "access": "write", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "Required. Set true to submit a real Zepto order/payment action." + } + ], + "columns": [ + "status", + "confirmed", + "message" + ], + "type": "js", + "modulePath": "zepto/place-order.js", + "sourceFile": "zepto/place-order.js", + "navigateBefore": false + }, + { + "site": "zepto", + "name": "product", + "description": "Read Zepto product details", + "access": "read", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product", + "type": "str", + "required": true, + "positional": true, + "help": "Product URL from Zepto search results" + } + ], + "columns": [ + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "availability", + "url" + ], + "type": "js", + "modulePath": "zepto/product.js", + "sourceFile": "zepto/product.js", + "navigateBefore": false + }, + { + "site": "zepto", + "name": "search", + "description": "Search Zepto products", + "access": "read", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum products to return (max 50)" + } + ], + "columns": [ + "rank", + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "availability", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "zepto/search.js", + "sourceFile": "zepto/search.js", + "navigateBefore": false + }, + { + "site": "zepto", + "name": "whoami", + "description": "Show the current logged-in zepto account", + "access": "read", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site" + ], + "type": "js", + "modulePath": "zepto/auth.js", + "sourceFile": "zepto/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "zlibrary", + "name": "info", + "description": "Get book details and available download formats from a Z-Library book page", + "access": "read", + "domain": "z-library.im", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "Z-Library book page URL (e.g. https://z-library.im/book/...)" + } + ], + "columns": [ + "title", + "pdf", + "epub", + "url" + ], + "type": "js", + "modulePath": "zlibrary/info.js", + "sourceFile": "zlibrary/info.js", + "navigateBefore": false + }, + { + "site": "zlibrary", + "name": "search", + "description": "Search Z-Library for books by title, author, ISBN, or keyword", + "access": "read", + "domain": "z-library.im", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (title, author, ISBN, etc.)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results (1–25)" + } + ], + "columns": [ + "rank", + "title", + "author", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "zlibrary/search.js", + "sourceFile": "zlibrary/search.js", + "navigateBefore": false + } +] From cbd1fdfdca0683383b744c590938354292cfebd1 Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 15:41:49 +0530 Subject: [PATCH 06/39] fix: preserve plugin runtime error messages --- src/plugin-runtime.test.ts | 2 ++ src/plugin-runtime.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugin-runtime.test.ts b/src/plugin-runtime.test.ts index 938e9db4..a7278ed8 100644 --- a/src/plugin-runtime.test.ts +++ b/src/plugin-runtime.test.ts @@ -81,6 +81,8 @@ describe('plugin runtime search helpers', () => { await expect(runBrowserStep('search', async () => { throw codedFunction; })).rejects.toBe(codedFunction); await expect(runBrowserStep('search', async () => { throw new Error('broke'); })) .rejects.toThrow('search failed: broke'); + await expect(runBrowserStep('search', async () => { throw { message: 'browser failed' }; })) + .rejects.toThrow('search failed: browser failed'); }); }); diff --git a/src/plugin-runtime.ts b/src/plugin-runtime.ts index 3e186d7f..523cdd8b 100644 --- a/src/plugin-runtime.ts +++ b/src/plugin-runtime.ts @@ -83,7 +83,7 @@ export async function runBrowserStep(label: string, fn: () => Promise): Pr } catch (error) { const typedError = error as { code?: unknown; name?: string } | undefined; if (typedError?.code || typedError?.name === 'ArgumentError') throw error; - throw new CommandExecutionError(`${label} failed: ${error instanceof Error ? error.message : String(error)}`); + throw new CommandExecutionError(`${label} failed: ${(error as { message?: unknown } | null)?.message ?? error}`); } } From 4b7faa458224a0a23971d27dbbdc79a2b3ddfd4a Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 15:56:53 +0530 Subject: [PATCH 07/39] feat: add independent plugin migration infrastructure --- package.json | 4 +- plugin-command-manifest.json | 2591 +++++++++++++++++ plugins/pypi/test/pypi.test.js | 4 +- plugins/techcrunch/test/techcrunch.test.js | 2 +- .../ycombinator/{ => test}/company.test.js | 6 +- scripts/check-plugin-command-parity.mjs | 49 + scripts/check-silent-column-drop.mjs | 5 + scripts/check-typed-error-lint.mjs | 5 + scripts/migrate-cli-sites.mjs | 112 + scripts/silent-column-drop-baseline.json | 60 + scripts/typed-error-lint-baseline.json | 16 + src/build-plugin-command-manifest.test.ts | 100 + src/build-plugin-command-manifest.ts | 86 + src/cli.test.ts | 29 + src/cli.ts | 8 +- src/convention-audit.test.ts | 17 + src/convention-audit.ts | 14 +- src/discovery.ts | 18 +- src/engine.test.ts | 21 + src/hosted/main-lifecycle.test.ts | 7 +- src/hosted/root-command-surface.test.ts | 15 +- src/hosted/runner.test.ts | 9 +- src/hosted/runner.ts | 3 +- src/main.ts | 4 +- src/migrate-cli-sites.test.ts | 98 + vitest.config.ts | 2 +- 26 files changed, 3254 insertions(+), 31 deletions(-) create mode 100644 plugin-command-manifest.json rename plugins/ycombinator/{ => test}/company.test.js (92%) create mode 100644 scripts/check-plugin-command-parity.mjs create mode 100644 scripts/migrate-cli-sites.mjs create mode 100644 src/build-plugin-command-manifest.test.ts create mode 100644 src/build-plugin-command-manifest.ts create mode 100644 src/migrate-cli-sites.test.ts diff --git a/package.json b/package.json index abf8aef7..1c9822d9 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "build": "npm run clean-dist && npm run copy-yaml && npm run compile && npm run build-manifest", "compile": "tsc --build && node -e \"require('fs').chmodSync('dist/src/main.js', 0o755)\"", "build-manifest": "tsx src/build-manifest.ts", + "build-plugin-manifest": "tsx src/build-plugin-command-manifest.ts", "check:codex-plugin": "node scripts/check-codex-plugin.mjs", "check:hosted-contract": "node scripts/check-hosted-contract.mjs", "clean-dist": "node scripts/clean-dist.cjs", @@ -71,7 +72,8 @@ "advise:listing-id-pairing": "node scripts/check-listing-id-pairing.mjs", "check:package-bin": "node scripts/check-package-bin.mjs", "check:silent-column-drop": "node scripts/check-silent-column-drop.mjs", - "check:typed-error-lint": "node scripts/check-typed-error-lint.mjs" + "check:typed-error-lint": "node scripts/check-typed-error-lint.mjs", + "check:plugin-parity": "node scripts/check-plugin-command-parity.mjs" }, "keywords": [ "cli", diff --git a/plugin-command-manifest.json b/plugin-command-manifest.json new file mode 100644 index 00000000..fcf68785 --- /dev/null +++ b/plugin-command-manifest.json @@ -0,0 +1,2591 @@ +[ + { + "site": "bmwblog", + "name": "article", + "description": "Read a BMWBLOG article by URL or slug", + "access": "read", + "domain": "www.bmwblog.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "url-or-slug", + "type": "str", + "required": true, + "positional": true, + "help": "BMWBLOG article URL or slug" + } + ], + "columns": [ + "title", + "date", + "author", + "sections", + "excerpt", + "url", + "content" + ], + "type": "js", + "modulePath": "plugins/bmwblog/article.js", + "sourceFile": "plugins/bmwblog/article.js" + }, + { + "site": "bmwblog", + "name": "latest", + "description": "List the latest BMWBLOG articles", + "access": "read", + "domain": "www.bmwblog.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of articles (1-50)" + } + ], + "columns": [ + "rank", + "title", + "date", + "author", + "section", + "excerpt", + "url" + ], + "type": "js", + "modulePath": "plugins/bmwblog/latest.js", + "sourceFile": "plugins/bmwblog/latest.js" + }, + { + "site": "bmwblog", + "name": "search", + "description": "Search BMWBLOG articles", + "access": "read", + "domain": "www.bmwblog.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results (1-50)" + } + ], + "columns": [ + "rank", + "title", + "date", + "author", + "section", + "excerpt", + "url" + ], + "type": "js", + "modulePath": "plugins/bmwblog/search.js", + "sourceFile": "plugins/bmwblog/search.js" + }, + { + "site": "cincinnati", + "name": "export-postgraduate-courses", + "description": "Export University of Cincinnati graduate and professional programs from official public sources.", + "access": "read", + "example": "webcmd cincinnati export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.grad.uc.edu", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/cincinnati/export-postgraduate-courses.js", + "sourceFile": "plugins/cincinnati/export-postgraduate-courses.js" + }, + { + "site": "concordia", + "name": "export-postgraduate-courses", + "description": "Export Concordia University Montreal postgraduate programs using official public sources.", + "access": "read", + "example": "webcmd concordia export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.concordia.ca", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/concordia/export-postgraduate-courses.js", + "sourceFile": "plugins/concordia/export-postgraduate-courses.js" + }, + { + "site": "goettingen", + "name": "export-postgraduate-courses", + "description": "Export University of Göttingen postgraduate programmes from the official A-Z API.", + "access": "read", + "example": "webcmd goettingen export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.uni-goettingen.de", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programmes after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/goettingen/export-postgraduate-courses.js", + "sourceFile": "plugins/goettingen/export-postgraduate-courses.js" + }, + { + "site": "heidelberg", + "name": "export-postgraduate-courses", + "description": "Export Heidelberg University non-bachelor/postgraduate programs using the official study finder.", + "access": "read", + "example": "webcmd heidelberg export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.uni-heidelberg.de", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/heidelberg/export-postgraduate-courses.js", + "sourceFile": "plugins/heidelberg/export-postgraduate-courses.js" + }, + { + "site": "hft", + "name": "export-postgraduate-courses", + "description": "Export HFT Stuttgart postgraduate programs using the official public programme pages.", + "access": "read", + "example": "webcmd hft export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.hft-stuttgart.de", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/hft/export-postgraduate-courses.js", + "sourceFile": "plugins/hft/export-postgraduate-courses.js" + }, + { + "site": "iit", + "name": "export-postgraduate-courses", + "description": "Export Illinois Tech postgraduate programs using official public sources.", + "access": "read", + "example": "webcmd iit export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.iit.edu", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/iit/export-postgraduate-courses.js", + "sourceFile": "plugins/iit/export-postgraduate-courses.js" + }, + { + "site": "jhu", + "name": "export-postgraduate-courses", + "description": "Export Johns Hopkins University postgraduate programs using the official Academic Catalogue.", + "access": "read", + "example": "webcmd jhu export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "e-catalogue.jhu.edu", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/jhu/export-postgraduate-courses.js", + "sourceFile": "plugins/jhu/export-postgraduate-courses.js" + }, + { + "site": "linkedin", + "name": "company", + "description": "Read a LinkedIn company page: industry, size, HQ, founded, website, followers, and about text", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "company", + "type": "string", + "required": true, + "positional": true, + "help": "Company universal name, /company/ path, or full URL" + } + ], + "columns": [ + "name", + "industry", + "size", + "headquarters", + "founded", + "website", + "specialties", + "followers", + "about", + "url" + ], + "type": "js", + "modulePath": "plugins/linkedin/company.js", + "sourceFile": "plugins/linkedin/company.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "connect", + "description": "Fail-closed LinkedIn connection request sender that verifies the exact profile before optionally sending a note", + "access": "write", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": true, + "positional": true, + "help": "Exact LinkedIn profile URL to open and verify" + }, + { + "name": "expected-name", + "type": "string", + "required": true, + "help": "Expected visible profile name" + }, + { + "name": "note", + "type": "string", + "default": "", + "required": false, + "help": "Optional connection note, max 300 chars" + }, + { + "name": "send", + "type": "bool", + "default": false, + "required": false, + "help": "Actually click Send. Default is dry-run verification only." + } + ], + "columns": [ + "status", + "recipient", + "reason", + "profile_url", + "note_chars", + "connectable", + "delivery_verified", + "matched_invitation_name", + "matched_invitation_url", + "actualValue", + "blockReason", + "expectedValue", + "observedUrl", + "safety" + ], + "type": "js", + "modulePath": "plugins/linkedin/connect.js", + "sourceFile": "plugins/linkedin/connect.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "connections", + "description": "List your LinkedIn first-degree connections with names, headlines, and profile URLs", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of connections to return (max 500)" + } + ], + "columns": [ + "rank", + "name", + "occupation", + "public_id", + "connected_at", + "url" + ], + "type": "js", + "modulePath": "plugins/linkedin/connections.js", + "sourceFile": "plugins/linkedin/connections.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "inbox", + "description": "List LinkedIn messaging inbox conversations and unread messages", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 40, + "required": false, + "help": "Maximum conversations to return (1-100)" + }, + { + "name": "unread-only", + "type": "bool", + "default": false, + "required": false, + "help": "Return only conversations with unread messages" + } + ], + "columns": [ + "rank", + "thread_url", + "thread_id", + "person_name", + "last_message_preview", + "unread", + "counterparty_type", + "category", + "timestamp" + ], + "type": "js", + "modulePath": "plugins/linkedin/inbox.js", + "sourceFile": "plugins/linkedin/inbox.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "job-detail", + "description": "Read one LinkedIn job page with description, apply URL, workplace type, applicants, and company metadata", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "job-url", + "type": "string", + "required": true, + "positional": true, + "help": "Exact LinkedIn job URL, e.g. https://www.linkedin.com/jobs/view/123/" + } + ], + "columns": [ + "title", + "company", + "location", + "workplace_type", + "job_type", + "applicants", + "listed", + "apply_url", + "company_url", + "url", + "description" + ], + "type": "js", + "modulePath": "plugins/linkedin/job-detail.js", + "sourceFile": "plugins/linkedin/job-detail.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "jobs-preferences", + "description": "Read visible LinkedIn Jobs preferences and alert settings without changing them", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "open_to_work", + "job_titles", + "locations", + "job_alerts", + "preferences_url", + "alerts_url", + "raw_preferences" + ], + "type": "js", + "modulePath": "plugins/linkedin/jobs-preferences.js", + "sourceFile": "plugins/linkedin/jobs-preferences.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "login", + "description": "Open linkedin login", + "access": "write", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "public_id", + "plain_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/linkedin/auth.js", + "sourceFile": "plugins/linkedin/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "linkedin", + "name": "people-search", + "description": "Search standard LinkedIn (not Sales Navigator) for people by keyword. Each invocation consumes against LinkedIn's monthly Commercial Use Limit on people search; throttle accordingly.", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "keywords", + "type": "string", + "required": true, + "positional": true, + "help": "People search keywords, e.g. \"site reliability engineer berlin\"" + }, + { + "name": "limit", + "type": "int", + "default": 5, + "required": false, + "help": "Maximum people to return (1-10); each query counts toward LinkedIn's monthly CUL" + } + ], + "columns": [ + "rank", + "name", + "headline", + "location", + "profile_url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/linkedin/people-search.js", + "sourceFile": "plugins/linkedin/people-search.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "post-analytics", + "description": "Summarize raw visible LinkedIn post counters without custom scoring or classification", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Maximum posts to summarize (1-100)" + } + ], + "columns": [ + "posts_analyzed", + "total_reactions", + "total_comments", + "total_reposts", + "total_impressions", + "posts_with_media", + "posts_with_urls", + "latest_posted_at", + "latest_reactions", + "latest_comments", + "latest_reposts", + "latest_impressions", + "latest_url" + ], + "type": "js", + "modulePath": "plugins/linkedin/post-analytics.js", + "sourceFile": "plugins/linkedin/post-analytics.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "post-comments", + "description": "List unique commenters and reply authors from one exact LinkedIn post URL", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "post-url", + "type": "string", + "required": true, + "positional": true, + "help": "Exact LinkedIn post URL" + }, + { + "name": "limit", + "type": "int", + "required": false, + "help": "Maximum unique commenters to return; omit to fetch all" + } + ], + "columns": [ + "rank", + "name", + "headline", + "profile_url", + "comment_count", + "sample_comment", + "commented_at", + "source_post" + ], + "type": "js", + "modulePath": "plugins/linkedin/post-comments.js", + "sourceFile": "plugins/linkedin/post-comments.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "posts", + "description": "Export visible posts from a LinkedIn profile activity page with engagement metrics", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum posts to return (1-100)" + } + ], + "columns": [ + "rank", + "author", + "posted_at", + "body", + "reactions", + "comments", + "reposts", + "impressions", + "media", + "media_urls", + "url", + "raw_text" + ], + "type": "js", + "modulePath": "plugins/linkedin/posts.js", + "sourceFile": "plugins/linkedin/posts.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "profile-analytics", + "description": "Read visible LinkedIn profile dashboard metrics such as profile views, post impressions, and search appearances", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], + "columns": [ + "profile_url", + "profile_views", + "post_impressions", + "search_appearances", + "followers", + "connections", + "raw_analytics" + ], + "type": "js", + "modulePath": "plugins/linkedin/profile-analytics.js", + "sourceFile": "plugins/linkedin/profile-analytics.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "profile-experience", + "description": "Read visible LinkedIn profile experience entries with titles, dates, locations, skills, media, and URLs", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], + "columns": [ + "rank", + "total_count", + "title", + "employment_type", + "company", + "date_range", + "start_date", + "end_date", + "location", + "location_type", + "description", + "skills", + "media", + "urls", + "skill_url", + "media_url", + "profile_url", + "raw_text" + ], + "type": "js", + "modulePath": "plugins/linkedin/profile-experience.js", + "sourceFile": "plugins/linkedin/profile-experience.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "profile-projects", + "description": "Read visible LinkedIn profile projects with descriptions, dates, skills, media, and URLs", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], + "columns": [ + "rank", + "title", + "date_range", + "associated_with", + "description", + "skills", + "media", + "urls", + "profile_url", + "raw_text" + ], + "type": "js", + "modulePath": "plugins/linkedin/profile-projects.js", + "sourceFile": "plugins/linkedin/profile-projects.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "profile-read", + "description": "Read visible LinkedIn profile sections: headline, About, experience, education, services, and featured sections", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], + "columns": [ + "profile_url", + "name", + "headline", + "location", + "about", + "about_character_count", + "about_skills", + "experience", + "education", + "services", + "featured" + ], + "type": "js", + "modulePath": "plugins/linkedin/profile-read.js", + "sourceFile": "plugins/linkedin/profile-read.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "safe-send", + "description": "Fail-closed LinkedIn message sender that verifies exact thread, recipient, and latest message before filling/sending", + "access": "write", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "thread-url", + "type": "str", + "required": true, + "help": "Exact LinkedIn messaging thread URL to open and verify" + }, + { + "name": "expected-name", + "type": "str", + "required": true, + "help": "Expected visible recipient name in the active thread header" + }, + { + "name": "message", + "type": "str", + "required": true, + "help": "Message body to send or dry-run" + }, + { + "name": "expected-last-text", + "type": "str", + "required": false, + "help": "Substring expected in the currently visible latest conversation context" + }, + { + "name": "expected-last-hash", + "type": "str", + "required": false, + "help": "SHA-256 hash of expected latest visible message text" + }, + { + "name": "send", + "type": "bool", + "default": false, + "required": false, + "help": "Actually click Send. Default is dry-run verification only." + }, + { + "name": "screenshot", + "type": "bool", + "default": false, + "required": false, + "help": "Capture a screenshot during verification" + } + ], + "columns": [ + "status", + "recipient", + "reason", + "thread_url", + "message_chars", + "screenshot" + ], + "type": "js", + "modulePath": "plugins/linkedin/safe-send.js", + "sourceFile": "plugins/linkedin/safe-send.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "salesnav-inbox", + "description": "List LinkedIn Sales Navigator message conversations with API pagination", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "limit", + "type": "number", + "default": 40, + "required": false, + "help": "Maximum conversations to return (1-500)" + }, + { + "name": "max-pages", + "type": "number", + "default": 30, + "required": false, + "help": "Maximum Sales Navigator API pages to fetch" + }, + { + "name": "unread-only", + "type": "bool", + "default": false, + "required": false, + "help": "Return only unread conversations" + } + ], + "columns": [ + "rank", + "thread_id", + "thread_url", + "person_name", + "last_message_snippet", + "last_activity_time", + "unread", + "unread_count", + "total_message_count", + "archived", + "participants", + "next_page_starts_at" + ], + "type": "js", + "modulePath": "plugins/linkedin/salesnav-inbox.js", + "sourceFile": "plugins/linkedin/salesnav-inbox.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "salesnav-message", + "description": "Send or dry-run a LinkedIn Sales Navigator InMail to a lead using the Sales Navigator messaging API", + "access": "write", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "recipient", + "type": "string", + "required": true, + "positional": true, + "help": "Sales Navigator lead URL, LinkedIn /in/ URL from salesnav-search, or urn:li:fs_salesProfile:(...)" + }, + { + "name": "subject", + "type": "string", + "required": true, + "help": "InMail subject" + }, + { + "name": "body", + "type": "string", + "required": true, + "help": "InMail body" + }, + { + "name": "send", + "type": "bool", + "default": false, + "required": false, + "help": "Actually send the InMail. Default is dry-run validation only." + }, + { + "name": "copy-to-crm", + "type": "bool", + "default": false, + "required": false, + "help": "Set Sales Navigator copyToCrm on the message request" + } + ], + "columns": [ + "status", + "recipient", + "title", + "company", + "credits_remaining", + "credits_before", + "credits_after", + "sent_in_salesnav", + "message_chars", + "subject_chars", + "recipient_urn", + "degree", + "inmail_restriction", + "open_link" + ], + "type": "js", + "modulePath": "plugins/linkedin/salesnav-message.js", + "sourceFile": "plugins/linkedin/salesnav-message.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "salesnav-search", + "description": "Search LinkedIn Sales Navigator for people leads by keyword", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "keywords", + "type": "string", + "required": true, + "positional": true, + "help": "People search keywords, e.g. \"quality manager food manufacturing\"" + }, + { + "name": "limit", + "type": "number", + "default": 25, + "required": false, + "help": "Maximum leads to return (1-500, fetched 25 per request)" + } + ], + "columns": [ + "rank", + "name", + "title", + "company", + "location", + "degree", + "profile_url", + "lead_url", + "recipient_urn" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/linkedin/salesnav-search.js", + "sourceFile": "plugins/linkedin/salesnav-search.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "salesnav-thread", + "description": "Return full Sales Navigator message history for a thread id, Sales Navigator inbox URL, lead URL, recipient urn, or exact recipient name", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "thread-or-recipient", + "type": "string", + "required": true, + "positional": true, + "help": "Sales Navigator inbox URL/thread id, Sales Navigator lead URL, recipient urn, or exact participant name" + }, + { + "name": "limit", + "type": "number", + "default": 200, + "required": false, + "help": "Maximum messages to return (1-500)" + }, + { + "name": "max-pages", + "type": "number", + "default": 30, + "required": false, + "help": "Maximum inbox pages to scan when resolving a recipient" + } + ], + "columns": [ + "index", + "thread_id", + "thread_url", + "sender", + "text", + "timestamp", + "subject", + "message_id", + "sender_urn", + "delivered_at", + "type", + "total_message_count" + ], + "type": "js", + "modulePath": "plugins/linkedin/salesnav-thread.js", + "sourceFile": "plugins/linkedin/salesnav-thread.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "search", + "description": "Search LinkedIn jobs", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "string", + "required": true, + "positional": true, + "help": "Job search keywords" + }, + { + "name": "location", + "type": "string", + "required": false, + "help": "Location text such as San Francisco Bay Area" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of jobs to return (max 100)" + }, + { + "name": "start", + "type": "int", + "default": 0, + "required": false, + "help": "Result offset for pagination" + }, + { + "name": "details", + "type": "bool", + "default": false, + "required": false, + "help": "Include full job description and apply URL (slower)" + }, + { + "name": "company", + "type": "string", + "required": false, + "help": "Comma-separated company names or LinkedIn company IDs" + }, + { + "name": "experience-level", + "type": "string", + "required": false, + "help": "Comma-separated: internship, entry, associate, mid-senior, director, executive" + }, + { + "name": "job-type", + "type": "string", + "required": false, + "help": "Comma-separated: full-time, part-time, contract, temporary, volunteer, internship, other" + }, + { + "name": "date-posted", + "type": "string", + "required": false, + "help": "One of: any, month, week, 24h" + }, + { + "name": "remote", + "type": "string", + "required": false, + "help": "Comma-separated: on-site, hybrid, remote" + } + ], + "columns": [ + "rank", + "title", + "company", + "location", + "listed", + "salary", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/linkedin/search.js", + "sourceFile": "plugins/linkedin/search.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "sent-invitations", + "description": "List pending LinkedIn sent invitations for CRM reconciliation", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "rank", + "name", + "profile_url", + "invited_date_text" + ], + "type": "js", + "modulePath": "plugins/linkedin/sent-invitations.js", + "sourceFile": "plugins/linkedin/sent-invitations.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "services-read", + "description": "Read LinkedIn Services page details including services, overview, availability, pricing, and media titles/descriptions", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + }, + { + "name": "services-url", + "type": "string", + "required": false, + "help": "LinkedIn /services/page// URL. If omitted, it is discovered from the profile." + } + ], + "columns": [ + "service_url", + "page_title", + "overview", + "availability", + "work_locations", + "pricing", + "services_provided", + "services_count", + "media", + "media_count", + "messages", + "reviews_visibility" + ], + "type": "js", + "modulePath": "plugins/linkedin/services-read.js", + "sourceFile": "plugins/linkedin/services-read.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "thread-snapshot", + "description": "Load a LinkedIn messaging thread, scroll for available history, and return a full context snapshot", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "thread-url", + "type": "str", + "required": true, + "help": "Exact LinkedIn messaging thread URL to open and snapshot" + }, + { + "name": "max-scrolls", + "type": "number", + "default": 30, + "required": false, + "help": "Maximum upward scroll attempts to load older messages" + }, + { + "name": "json", + "type": "bool", + "default": false, + "required": false, + "help": "Return only JSON snapshot string in the snapshot_json field" + } + ], + "columns": [ + "thread_url", + "recipient", + "message_count", + "latest_text", + "snapshot_json" + ], + "type": "js", + "modulePath": "plugins/linkedin/thread-snapshot.js", + "sourceFile": "plugins/linkedin/thread-snapshot.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "timeline", + "description": "Read LinkedIn home timeline posts", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return (max 100)" + } + ], + "columns": [ + "rank", + "author", + "author_url", + "headline", + "text", + "posted_at", + "reactions", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/linkedin/timeline.js", + "sourceFile": "plugins/linkedin/timeline.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "whoami", + "description": "Show the current logged-in linkedin account", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "public_id", + "plain_id", + "name" + ], + "type": "js", + "modulePath": "plugins/linkedin/auth.js", + "sourceFile": "plugins/linkedin/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "luma", + "name": "create-event", + "description": "Create a free single-session Luma event", + "access": "write", + "domain": "luma.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "start", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "end", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "timezone", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "calendar", + "type": "str", + "required": false, + "help": "" + }, + { + "name": "description", + "type": "str", + "required": false, + "help": "" + }, + { + "name": "location", + "type": "str", + "required": false, + "help": "" + }, + { + "name": "virtual-url", + "type": "str", + "required": false, + "help": "" + }, + { + "name": "visibility", + "type": "str", + "default": "public", + "required": false, + "help": "", + "choices": [ + "public", + "private", + "members-only" + ] + }, + { + "name": "capacity", + "type": "int", + "required": false, + "help": "" + }, + { + "name": "require-approval", + "type": "boolean", + "default": false, + "required": false, + "help": "" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "" + } + ], + "columns": [ + "eventId", + "name", + "startsAt", + "endsAt", + "timezone", + "visibility", + "requireApproval", + "capacity", + "eventUrl", + "manageUrl" + ], + "type": "js", + "modulePath": "plugins/luma/create-event.js", + "sourceFile": "plugins/luma/create-event.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true + }, + { + "site": "luma", + "name": "events", + "description": "List upcoming or past Luma events managed by the logged-in account", + "access": "read", + "example": "webcmd luma events --period future --limit 25 -f json", + "domain": "luma.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "period", + "type": "str", + "default": "future", + "required": false, + "help": "List future or past events", + "choices": [ + "future", + "past" + ] + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Maximum number of events to request" + } + ], + "columns": [ + "eventId", + "name", + "startsAt", + "endsAt", + "timezone", + "guestCount", + "requireApproval", + "managerLevel", + "location", + "manageUrl", + "eventUrl" + ], + "type": "js", + "modulePath": "plugins/luma/events.js", + "sourceFile": "plugins/luma/events.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "luma", + "name": "guests", + "description": "List guests and all custom registration answers for a managed Luma event", + "access": "read", + "example": "webcmd luma guests evt-abc --status pending_approval --limit 100 -f json", + "domain": "luma.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "eventId", + "type": "str", + "required": true, + "positional": true, + "help": "Luma event ID returned by webcmd luma events" + }, + { + "name": "status", + "type": "str", + "default": "all", + "required": false, + "help": "Filter by guest approval status", + "choices": [ + "all", + "approved", + "pending_approval", + "declined", + "waitlist", + "invited" + ] + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Maximum matching guests to return" + }, + { + "name": "query", + "type": "str", + "default": "", + "required": false, + "help": "Search text passed to Luma guest search" + } + ], + "columns": [ + "eventId", + "guestId", + "userId", + "name", + "email", + "phone", + "status", + "registeredAt", + "profiles", + "answers" + ], + "type": "js", + "modulePath": "plugins/luma/guests.js", + "sourceFile": "plugins/luma/guests.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "luma", + "name": "login", + "description": "Open Luma sign in", + "access": "write", + "domain": "luma.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "email", + "url", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/luma/auth.js", + "sourceFile": "plugins/luma/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "luma", + "name": "set-registration-questions", + "description": "Append or replace custom registration questions on a managed Luma event", + "access": "write", + "domain": "luma.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "eventId", + "type": "str", + "required": true, + "positional": true, + "help": "" + }, + { + "name": "questions-file", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "mode", + "type": "str", + "required": true, + "help": "", + "choices": [ + "append", + "replace" + ] + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "" + } + ], + "columns": [ + "eventId", + "mode", + "previousCount", + "questionCount", + "questions", + "registrationUrl" + ], + "type": "js", + "modulePath": "plugins/luma/set-registration-questions.js", + "sourceFile": "plugins/luma/set-registration-questions.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true + }, + { + "site": "luma", + "name": "update-guest-status", + "description": "Approve or decline a pending Luma guest after explicit confirmation", + "access": "write", + "example": "webcmd luma update-guest-status evt-abc gst-abc --status approved --confirm true -f json", + "domain": "luma.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "eventId", + "type": "str", + "required": true, + "positional": true, + "help": "Luma event ID returned by webcmd luma events" + }, + { + "name": "guestId", + "type": "str", + "required": true, + "positional": true, + "help": "Luma guest ID returned by webcmd luma guests" + }, + { + "name": "status", + "type": "str", + "required": true, + "help": "New guest status", + "choices": [ + "approved", + "declined" + ] + }, + { + "name": "suppress-email", + "type": "boolean", + "default": false, + "required": false, + "help": "Set true to prevent Luma from emailing the guest" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "Required. Set --confirm true to change the real guest status" + } + ], + "columns": [ + "eventId", + "guestId", + "name", + "email", + "previousStatus", + "status", + "emailSuppressed" + ], + "type": "js", + "modulePath": "plugins/luma/update-guest-status.js", + "sourceFile": "plugins/luma/update-guest-status.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true + }, + { + "site": "luma", + "name": "whoami", + "description": "Show the current logged-in Luma account", + "access": "read", + "domain": "luma.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name", + "email", + "url" + ], + "type": "js", + "modulePath": "plugins/luma/auth.js", + "sourceFile": "plugins/luma/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "pypi", + "name": "package", + "description": "Inspect public PyPI package metadata", + "access": "read", + "domain": "pypi.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "string", + "required": true, + "positional": true, + "help": "Python package name, for example django" + } + ], + "columns": [ + "name", + "version", + "summary", + "author", + "license", + "requiresPython", + "uploadedAt", + "projectUrl", + "homepage", + "repository" + ], + "type": "js", + "modulePath": "plugins/pypi/package.js", + "sourceFile": "plugins/pypi/package.js" + }, + { + "site": "pypi", + "name": "releases", + "description": "List recent public PyPI package releases", + "access": "read", + "domain": "pypi.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "string", + "required": true, + "positional": true, + "help": "Python package name, for example django" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum releases to return (1-50)" + } + ], + "columns": [ + "version", + "uploadedAt", + "fileCount", + "pythonVersions", + "yanked", + "url" + ], + "type": "js", + "modulePath": "plugins/pypi/releases.js", + "sourceFile": "plugins/pypi/releases.js" + }, + { + "site": "skyscanner", + "name": "flights", + "description": "Skyscanner visible round-trip flight results from a warmed browser session", + "access": "read", + "domain": "www.skyscanner.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "origin", + "type": "str", + "required": true, + "positional": true, + "help": "Skyscanner origin route code, for example nyca" + }, + { + "name": "destination", + "type": "str", + "required": true, + "positional": true, + "help": "Skyscanner destination route code, for example lond" + }, + { + "name": "depart-date", + "type": "str", + "required": true, + "help": "Outbound date as YYYY-MM-DD" + }, + { + "name": "return-date", + "type": "str", + "required": true, + "help": "Return date as YYYY-MM-DD" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum flight rows to return (1-30)" + } + ], + "columns": [ + "rank", + "priceText", + "airlines", + "outboundTime", + "outboundRoute", + "outboundDuration", + "outboundStops", + "returnTime", + "returnRoute", + "returnDuration", + "returnStops", + "url" + ], + "type": "js", + "modulePath": "plugins/skyscanner/flights.js", + "sourceFile": "plugins/skyscanner/flights.js", + "navigateBefore": false + }, + { + "site": "techcrunch", + "name": "article", + "description": "Read a TechCrunch article from its URL", + "access": "read", + "domain": "techcrunch.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "TechCrunch article URL" + } + ], + "columns": [ + "title", + "author", + "publishedAt", + "categories", + "description", + "content", + "url" + ], + "type": "js", + "modulePath": "plugins/techcrunch/article.js", + "sourceFile": "plugins/techcrunch/article.js" + }, + { + "site": "techcrunch", + "name": "search", + "description": "Search TechCrunch stories or list the latest stories", + "access": "read", + "domain": "techcrunch.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "string", + "required": false, + "positional": true, + "help": "Words to search for" + }, + { + "name": "latest", + "type": "boolean", + "default": false, + "required": false, + "help": "List the latest stories instead of searching" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum stories to return (1-50)" + } + ], + "columns": [ + "rank", + "title", + "author", + "publishedAt", + "description", + "url" + ], + "type": "js", + "modulePath": "plugins/techcrunch/search.js", + "sourceFile": "plugins/techcrunch/search.js" + }, + { + "site": "ualberta", + "name": "export-postgraduate-courses", + "description": "Export University of Alberta postgraduate programs from the official graduate-program catalogue.", + "access": "read", + "example": "webcmd ualberta export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.ualberta.ca", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/ualberta/export-postgraduate-courses.js", + "sourceFile": "plugins/ualberta/export-postgraduate-courses.js", + "navigateBefore": false + }, + { + "site": "yale", + "name": "export-postgraduate-courses", + "description": "Export Yale University postgraduate and professional programs from official Yale sources.", + "access": "read", + "example": "webcmd yale export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "yale.edu", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/yale/export-postgraduate-courses.js", + "sourceFile": "plugins/yale/export-postgraduate-courses.js" + }, + { + "site": "ycombinator", + "name": "companies", + "description": "Search the public Y Combinator startup directory", + "access": "read", + "domain": "www.ycombinator.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": false, + "positional": true, + "help": "Company name, product, or keyword such as AI" + }, + { + "name": "batch", + "type": "str", + "required": false, + "help": "Exact YC batch, for example Spring 2026" + }, + { + "name": "industry", + "type": "str", + "required": false, + "help": "Exact YC industry, for example B2B" + }, + { + "name": "recent", + "type": "boolean", + "default": false, + "required": false, + "help": "Sort matches by launch date, newest first" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum companies to return (1-40)" + } + ], + "columns": [ + "rank", + "name", + "batch", + "location", + "description", + "industries", + "url" + ], + "type": "js", + "modulePath": "plugins/ycombinator/companies.js", + "sourceFile": "plugins/ycombinator/companies.js", + "navigateBefore": false + }, + { + "site": "ycombinator", + "name": "company", + "description": "Read a public Y Combinator company profile", + "access": "read", + "domain": "www.ycombinator.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "company", + "type": "str", + "required": true, + "positional": true, + "help": "YC company slug or full company URL" + } + ], + "columns": [ + "name", + "description", + "batch", + "status", + "location", + "founded", + "teamSize", + "website", + "founders", + "jobCount", + "url" + ], + "type": "js", + "modulePath": "plugins/ycombinator/company.js", + "sourceFile": "plugins/ycombinator/company.js", + "navigateBefore": false + } +] diff --git a/plugins/pypi/test/pypi.test.js b/plugins/pypi/test/pypi.test.js index 3a3ee85b..5fb3340a 100644 --- a/plugins/pypi/test/pypi.test.js +++ b/plugins/pypi/test/pypi.test.js @@ -1,7 +1,7 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; -import test, { after } from 'node:test'; +import { afterAll, test } from 'vitest'; import { fileURLToPath } from 'node:url'; const pluginRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); @@ -16,7 +16,7 @@ if (!fs.existsSync(peerLink)) { createdPeerLink = true; } -after(() => { +afterAll(() => { if (!createdPeerLink) return; fs.rmSync(peerLink, { force: true, recursive: true }); for (const dir of [peerScopeDir, path.dirname(peerScopeDir)]) { diff --git a/plugins/techcrunch/test/techcrunch.test.js b/plugins/techcrunch/test/techcrunch.test.js index 46ee2990..de52de14 100644 --- a/plugins/techcrunch/test/techcrunch.test.js +++ b/plugins/techcrunch/test/techcrunch.test.js @@ -1,5 +1,5 @@ import assert from 'node:assert/strict'; -import test from 'node:test'; +import { test } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { articleTechCrunch } from '../article.js'; diff --git a/plugins/ycombinator/company.test.js b/plugins/ycombinator/test/company.test.js similarity index 92% rename from plugins/ycombinator/company.test.js rename to plugins/ycombinator/test/company.test.js index 8b60e184..304e4454 100644 --- a/plugins/ycombinator/company.test.js +++ b/plugins/ycombinator/test/company.test.js @@ -1,9 +1,9 @@ import assert from 'node:assert/strict'; -import test from 'node:test'; +import { test } from 'vitest'; import { JSDOM } from 'jsdom'; test('normalizes YC company slugs and URLs', async () => { - const { __test__ } = await import('./company.js'); + const { __test__ } = await import('../company.js'); assert.equal( __test__.normalizeCompanyUrl('fenrock-ai'), 'https://www.ycombinator.com/companies/fenrock-ai', @@ -16,7 +16,7 @@ test('normalizes YC company slugs and URLs', async () => { }); test('extracts one company row from YC page state', async () => { - const { __test__ } = await import('./company.js'); + const { __test__ } = await import('../company.js'); const dom = new JSDOM('
'); dom.window.document.querySelector('div').setAttribute('data-page', JSON.stringify({ props: { diff --git a/scripts/check-plugin-command-parity.mjs b/scripts/check-plugin-command-parity.mjs new file mode 100644 index 00000000..610f2f81 --- /dev/null +++ b/scripts/check-plugin-command-parity.mjs @@ -0,0 +1,49 @@ +#!/usr/bin/env node + +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const plugins = read('plugin-command-manifest.json'); +const core = read('cli-manifest.json'); +const frozen = read('test/fixtures/core-cli-manifest-v0.5.3.json'); +const coreKeys = new Set(core.map(key)); +const pluginByKey = new Map(plugins.map(entry => [key(entry), entry])); +const fields = [ + 'aliases', 'access', 'domain', 'strategy', 'browser', 'args', 'columns', 'tags', 'keywords', + 'defaultFormat', 'pipeline', 'navigateBefore', 'siteSession', 'freshPage', +]; +const issues = []; + +for (const expected of frozen) { + const command = key(expected); + if (coreKeys.has(command)) continue; + const actual = pluginByKey.get(command); + if (!actual) { + issues.push(`${command} is missing from plugin-command-manifest.json`); + continue; + } + if (JSON.stringify(pick(actual)) !== JSON.stringify(pick(expected))) { + issues.push(`${command} executable metadata differs from frozen core manifest`); + } +} + +if (issues.length) { + console.error(`Plugin parity failed (${issues.length} issue(s)):`); + for (const issue of issues) console.error(` - ${issue}`); + process.exit(1); +} +console.log(`OK - plugin parity preserved for ${frozen.length - coreKeys.size} migrated command(s).`); + +function read(relative) { + return JSON.parse(fs.readFileSync(path.join(root, relative), 'utf8')); +} + +function key(entry) { + return `${entry.site}/${entry.name}`; +} + +function pick(entry) { + return Object.fromEntries(fields.map(field => [field, entry[field]])); +} diff --git a/scripts/check-silent-column-drop.mjs b/scripts/check-silent-column-drop.mjs index 2de94398..dcfdae65 100644 --- a/scripts/check-silent-column-drop.mjs +++ b/scripts/check-silent-column-drop.mjs @@ -20,6 +20,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const PROJECT_ROOT = resolve(__dirname, '..'); const DIST_AUDIT = resolve(PROJECT_ROOT, 'dist', 'src', 'convention-audit.js'); +const PLUGIN_MANIFEST = resolve(PROJECT_ROOT, 'plugin-command-manifest.json'); const BASELINE_PATH = resolve(__dirname, 'silent-column-drop-baseline.json'); const UPDATE = process.argv.includes('--update-baseline'); @@ -27,6 +28,10 @@ if (!existsSync(DIST_AUDIT)) { console.error('dist/src/convention-audit.js not found. Run npm run build before this check.'); process.exit(1); } +if (!existsSync(PLUGIN_MANIFEST)) { + console.error('plugin-command-manifest.json not found. Run npm run build-plugin-manifest before this check.'); + process.exit(1); +} const { runConventionAudit } = await import(pathToFileURL(DIST_AUDIT).href); const report = runConventionAudit({ projectRoot: PROJECT_ROOT }); diff --git a/scripts/check-typed-error-lint.mjs b/scripts/check-typed-error-lint.mjs index 23065165..6a9e1e9d 100644 --- a/scripts/check-typed-error-lint.mjs +++ b/scripts/check-typed-error-lint.mjs @@ -14,6 +14,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const PROJECT_ROOT = resolve(__dirname, '..'); const DIST_AUDIT = resolve(PROJECT_ROOT, 'dist', 'src', 'convention-audit.js'); +const PLUGIN_MANIFEST = resolve(PROJECT_ROOT, 'plugin-command-manifest.json'); const BASELINE_PATH = resolve(__dirname, 'typed-error-lint-baseline.json'); const UPDATE = process.argv.includes('--update-baseline'); const RULES = new Set(['silent-clamp', 'silent-empty-fallback', 'silent-sentinel']); @@ -22,6 +23,10 @@ if (!existsSync(DIST_AUDIT)) { console.error('dist/src/convention-audit.js not found. Run npm run build before this check.'); process.exit(1); } +if (!existsSync(PLUGIN_MANIFEST)) { + console.error('plugin-command-manifest.json not found. Run npm run build-plugin-manifest before this check.'); + process.exit(1); +} const { runConventionAudit } = await import(pathToFileURL(DIST_AUDIT).href); const report = runConventionAudit({ projectRoot: PROJECT_ROOT }); diff --git a/scripts/migrate-cli-sites.mjs b/scripts/migrate-cli-sites.mjs new file mode 100644 index 00000000..d8210d8b --- /dev/null +++ b/scripts/migrate-cli-sites.mjs @@ -0,0 +1,112 @@ +#!/usr/bin/env node + +import * as fs from 'node:fs'; +import * as path from 'node:path'; + +const root = process.cwd(); +const sites = process.argv.slice(2); +const sharedRuntime = /((?:\.\.\/)+)_shared\/(?:common|desktop-commands|search-adapter|site-auth)\.js/g; + +if (sites.length === 0) fail('Usage: node scripts/migrate-cli-sites.mjs '); +for (const site of sites) { + if (!/^[a-z0-9][a-z0-9-]*$/.test(site)) fail(`Invalid site name: ${site}`); + if (!fs.existsSync(path.join(root, 'clis', site))) fail(`clis/${site} does not exist`); + if (site !== 'pypi' && fs.existsSync(path.join(root, 'plugins', site))) { + fail(`plugins/${site} already exists`); + } +} + +const manifest = readJson(path.join(root, 'cli-manifest.json'), []); +for (const site of sites) migrate(site, manifest.filter(entry => entry.site === site)); + +function migrate(site, commands) { + const source = path.join(root, 'clis', site); + const plugin = path.join(root, 'plugins', site); + const files = walk(source); + const destinations = new Map(files.map(file => [file, destination(file, source, plugin)])); + + fs.mkdirSync(plugin, { recursive: true }); + for (const file of files) { + const target = destinations.get(file); + fs.mkdirSync(path.dirname(target), { recursive: true }); + let content = fs.readFileSync(file); + if (/\.[cm]?js$/.test(file)) { + let sourceText = content.toString().replace(sharedRuntime, '@agentrhq/webcmd/plugin-runtime'); + if (/\.test\.[cm]?js$/.test(file)) sourceText = rewriteTestImports(sourceText, file, target, destinations); + content = Buffer.from(sourceText); + } + fs.writeFileSync(target, content); + } + fs.rmSync(source, { recursive: true, force: true }); + + const description = `Webcmd commands for ${site}`; + writeJson(path.join(plugin, 'package.json'), { + name: `webcmd-plugin-${site}`, + version: '0.1.0', + type: 'module', + description, + peerDependencies: { '@agentrhq/webcmd': '>=0.6.0' }, + }); + writeJson(path.join(plugin, 'webcmd-plugin.json'), { + name: site, + version: '0.1.0', + description, + webcmd: '>=0.6.0', + author: { name: 'WebCMD Agent', handle: 'agentrhq' }, + }); + fs.writeFileSync(path.join(plugin, 'README.md'), readme(site, description, commands)); + + for (const baseline of ['silent-column-drop-baseline.json', 'typed-error-lint-baseline.json']) { + const file = path.join(root, 'scripts', baseline); + if (!fs.existsSync(file)) continue; + const before = fs.readFileSync(file, 'utf8'); + fs.writeFileSync(file, before.replaceAll(`clis/${site}/`, `plugins/${site}/`)); + } + console.log(`Migrated ${site}: ${commands.length} command(s)`); +} + +function destination(file, source, plugin) { + const relative = path.relative(source, file); + return /\.test\.[cm]?js$/.test(file) + ? path.join(plugin, 'test', path.basename(relative)) + : path.join(plugin, relative); +} + +function rewriteTestImports(source, oldFile, newFile, destinations) { + return source.replace(/(['"])(\.\.?\/[^'"]+)\1/g, (match, quote, specifier) => { + const oldTarget = path.resolve(path.dirname(oldFile), specifier); + const newTarget = destinations.get(oldTarget); + if (!newTarget) return match; + let relative = path.relative(path.dirname(newFile), newTarget).replaceAll(path.sep, '/'); + if (!relative.startsWith('.')) relative = `./${relative}`; + return `${quote}${relative}${quote}`; + }); +} + +function walk(dir) { + return fs.readdirSync(dir, { withFileTypes: true }).flatMap(entry => { + const file = path.join(dir, entry.name); + return entry.isDirectory() ? walk(file) : [file]; + }); +} + +function readme(site, description, commands) { + const rows = commands + .slice() + .sort((a, b) => String(a.name).localeCompare(String(b.name))) + .map(command => `| \`webcmd ${site} ${command.name}\` | ${String(command.description ?? '').replaceAll('|', '\\|')} |`); + return `# webcmd-plugin-${site}\n\n${description}.\n\n## Install\n\n\`\`\`bash\nwebcmd plugin install github:agentrhq/webcmd/plugins/${site}\n\`\`\`\n\n## Commands\n\n| Command | Description |\n| --- | --- |\n${rows.join('\n')}\n`; +} + +function readJson(file, fallback) { + return fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : fallback; +} + +function writeJson(file, value) { + fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`); +} + +function fail(message) { + console.error(message); + process.exit(1); +} diff --git a/scripts/silent-column-drop-baseline.json b/scripts/silent-column-drop-baseline.json index 7ec6c18a..daa415de 100644 --- a/scripts/silent-column-drop-baseline.json +++ b/scripts/silent-column-drop-baseline.json @@ -247,6 +247,66 @@ "state" ] }, + { + "command": "linkedin/timeline", + "file": "plugins/linkedin/timeline.js", + "missing": [ + "authorUrl", + "postedAt" + ] + }, + { + "command": "linkedin/timeline", + "file": "plugins/linkedin/timeline.js", + "missing": [ + "id" + ] + }, + { + "command": "linkedin/timeline", + "file": "plugins/linkedin/timeline.js", + "missing": [ + "postedAt" + ] + }, + { + "command": "luma/guests", + "file": "plugins/luma/guests.js", + "missing": [ + "kind" + ] + }, + { + "command": "luma/login", + "file": "plugins/luma/auth.js", + "missing": [ + "pageUrl", + "title" + ] + }, + { + "command": "luma/update-guest-status", + "file": "plugins/luma/update-guest-status.js", + "missing": [ + "detail", + "kind" + ] + }, + { + "command": "luma/update-guest-status", + "file": "plugins/luma/update-guest-status.js", + "missing": [ + "kind" + ] + }, + { + "command": "luma/whoami", + "file": "plugins/luma/auth.js", + "missing": [ + "pageUrl", + "title" + ] + }, { "command": "paperreview/review", "file": "clis/paperreview/review.js", diff --git a/scripts/typed-error-lint-baseline.json b/scripts/typed-error-lint-baseline.json index 22c9f62b..04a01cd2 100644 --- a/scripts/typed-error-lint-baseline.json +++ b/scripts/typed-error-lint-baseline.json @@ -159,6 +159,22 @@ "text": "const limit = Math.max(1, Math.min(Number(args.limit) || 20, 100));", "occurrence": 0 }, + { + "rule": "silent-clamp", + "command": "linkedin/search", + "file": "plugins/linkedin/search.js", + "line": 256, + "text": "const count = Math.min(MAX_BATCH, input.limit - allJobs.length);", + "occurrence": 0 + }, + { + "rule": "silent-clamp", + "command": "linkedin/timeline", + "file": "plugins/linkedin/timeline.js", + "line": 479, + "text": "const limit = Math.max(1, Math.min(kwargs.limit ?? 20, 100));", + "occurrence": 0 + }, { "rule": "silent-clamp", "command": "producthunt/browse", diff --git a/src/build-plugin-command-manifest.test.ts b/src/build-plugin-command-manifest.test.ts new file mode 100644 index 00000000..d335f7b9 --- /dev/null +++ b/src/build-plugin-command-manifest.test.ts @@ -0,0 +1,100 @@ +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import { pathToFileURL } from 'node:url'; +import { afterEach, describe, expect, it } from 'vitest'; +import type { ManifestEntry } from './manifest-types.js'; + +const roots: string[] = []; + +afterEach(() => { + for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true }); +}); + +function fixture(files: Record): string { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-plugin-manifest-')); + roots.push(root); + for (const [relative, source] of Object.entries(files)) { + const file = path.join(root, 'plugins', relative); + fs.mkdirSync(path.dirname(file), { recursive: true }); + fs.writeFileSync(file, source); + } + return root; +} + +function command(site: string, name: string, overrides: Record = {}) { + return { + site, + name, + description: `${name} description`, + access: 'read', + aliases: ['find'], + example: `webcmd ${site} ${name}`, + domain: 'example.com', + strategy: 'public', + browser: false, + args: [{ name: 'limit', type: 'int', default: 10, required: false, help: 'Limit' }], + columns: ['id'], + tags: ['search'], + keywords: ['lookup'], + defaultFormat: 'json', + navigateBefore: false, + siteSession: 'ephemeral', + freshPage: false, + ...overrides, + }; +} + +describe('plugin command manifest', () => { + it('scans flat command modules and emits deterministic plugin source paths', async () => { + const root = fixture({ + 'zeta/search.js': 'cli({ site: "zeta", name: "search" });', + 'alpha/list.js': 'cli({ site: "alpha", name: "list" });', + 'alpha/helper.js': 'export const helper = true;', + 'alpha/test/ignored.test.js': 'cli({ site: "alpha", name: "ignored" });', + }); + const modules = new Map([ + [pathToFileURL(path.join(root, 'plugins/zeta/search.js')).href, { zeta: command('zeta', 'search') }], + [pathToFileURL(path.join(root, 'plugins/alpha/list.js')).href, { alpha: command('alpha', 'list', { aliases: undefined }) }], + ]); + const { scanPluginCommandModules } = await import('./build-plugin-command-manifest.js'); + + const entries = await scanPluginCommandModules(path.join(root, 'plugins'), href => Promise.resolve(modules.get(href))); + + expect(entries.map(entry => `${entry.site}/${entry.name}`)).toEqual(['alpha/list', 'zeta/search']); + expect(entries.map(entry => entry.sourceFile)).toEqual(['plugins/alpha/list.js', 'plugins/zeta/search.js']); + expect(entries.map(entry => entry.modulePath)).toEqual(['plugins/alpha/list.js', 'plugins/zeta/search.js']); + }); + + it('rejects duplicate canonical keys and aliases', async () => { + const root = fixture({ + 'alpha/one.js': 'cli({ site: "alpha", name: "one" });', + 'alpha/two.js': 'cli({ site: "alpha", name: "two" });', + }); + const modules = new Map([ + [pathToFileURL(path.join(root, 'plugins/alpha/one.js')).href, { one: command('alpha', 'one', { aliases: ['shared'] }) }], + [pathToFileURL(path.join(root, 'plugins/alpha/two.js')).href, { two: command('alpha', 'two', { aliases: ['shared'] }) }], + ]); + const { scanPluginCommandModules } = await import('./build-plugin-command-manifest.js'); + + await expect(scanPluginCommandModules(path.join(root, 'plugins'), href => Promise.resolve(modules.get(href)))) + .rejects.toThrow('duplicate plugin command or alias alpha/shared'); + }); + + it('preserves executable metadata and reports a changed argument default', async () => { + const root = fixture({ 'alpha/search.js': 'cli({ site: "alpha", name: "search" });' }); + const runtime = command('alpha', 'search'); + const moduleHref = pathToFileURL(path.join(root, 'plugins/alpha/search.js')).href; + const { findPluginCommandParityIssues, scanPluginCommandModules } = await import('./build-plugin-command-manifest.js'); + const entries = await scanPluginCommandModules(path.join(root, 'plugins'), async href => href === moduleHref ? { runtime } : {}); + const frozen = [{ ...entries[0], modulePath: 'alpha/search.js', sourceFile: 'alpha/search.js' }] as ManifestEntry[]; + + expect(findPluginCommandParityIssues(entries, frozen)).toEqual([]); + + const changed = structuredClone(entries); + changed[0]!.args![0]!.default = 20; + expect(findPluginCommandParityIssues(changed, frozen)).toEqual([ + 'alpha/search executable metadata differs from frozen core manifest', + ]); + }); +}); diff --git a/src/build-plugin-command-manifest.ts b/src/build-plugin-command-manifest.ts new file mode 100644 index 00000000..fae0cfbe --- /dev/null +++ b/src/build-plugin-command-manifest.ts @@ -0,0 +1,86 @@ +#!/usr/bin/env node + +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { loadManifestEntries } from './build-manifest.js'; +import type { ManifestEntry } from './manifest-types.js'; +import { findPackageRoot } from './package-paths.js'; + +const EXECUTABLE_FIELDS = [ + 'aliases', 'access', 'domain', 'strategy', 'browser', 'args', 'columns', 'tags', 'keywords', + 'defaultFormat', 'pipeline', 'navigateBefore', 'siteSession', 'freshPage', +] as const satisfies readonly (keyof ManifestEntry)[]; + +type Importer = (moduleHref: string) => Promise; + +export async function scanPluginCommandModules( + pluginsDir: string, + importer: Importer = moduleHref => import(moduleHref), +): Promise { + if (!fs.existsSync(pluginsDir)) return []; + const projectRoot = path.dirname(pluginsDir); + const entries: ManifestEntry[] = []; + const owners = new Map(); + + for (const site of fs.readdirSync(pluginsDir).sort()) { + const pluginDir = path.join(pluginsDir, site); + if (!fs.statSync(pluginDir).isDirectory()) continue; + for (const file of fs.readdirSync(pluginDir).sort()) { + if (!file.endsWith('.js') || file.endsWith('.test.js') || file === 'index.js') continue; + const filePath = path.join(pluginDir, file); + if (!fs.statSync(filePath).isFile()) continue; + const loaded = await loadManifestEntries(filePath, site, importer, projectRoot); + for (const entry of loaded) { + const sourceFile = path.relative(projectRoot, filePath).replaceAll(path.sep, '/'); + const normalized = { ...entry, modulePath: sourceFile, sourceFile }; + claim(`${entry.site}/${entry.name}`, `${entry.site}/${entry.name}`, owners); + for (const alias of entry.aliases ?? []) claim(`${entry.site}/${alias}`, `${entry.site}/${entry.name}`, owners); + entries.push(normalized); + } + } + } + + return entries.sort((a, b) => a.site.localeCompare(b.site) || a.name.localeCompare(b.name)); +} + +export function findPluginCommandParityIssues( + pluginEntries: readonly ManifestEntry[], + frozenEntries: readonly ManifestEntry[], +): string[] { + const frozen = new Map(frozenEntries.map(entry => [`${entry.site}/${entry.name}`, entry])); + const issues: string[] = []; + for (const entry of pluginEntries) { + const key = `${entry.site}/${entry.name}`; + const expected = frozen.get(key); + if (!expected) continue; + const actualMetadata = Object.fromEntries(EXECUTABLE_FIELDS.map(field => [field, entry[field]])); + const expectedMetadata = Object.fromEntries(EXECUTABLE_FIELDS.map(field => [field, expected[field]])); + if (JSON.stringify(actualMetadata) !== JSON.stringify(expectedMetadata)) { + issues.push(`${key} executable metadata differs from frozen core manifest`); + } + } + return issues.sort(); +} + +function claim(key: string, owner: string, owners: Map): void { + const existing = owners.get(key); + if (existing) throw new Error(`duplicate plugin command or alias ${key}: ${existing}, ${owner}`); + owners.set(key, owner); +} + +export function serializePluginCommandManifest(entries: readonly ManifestEntry[]): string { + return `${JSON.stringify(entries, null, 2)}\n`; +} + +async function main(): Promise { + const packageRoot = findPackageRoot(fileURLToPath(import.meta.url)); + const entries = await scanPluginCommandModules(path.join(packageRoot, 'plugins')); + const output = path.join(packageRoot, 'plugin-command-manifest.json'); + fs.writeFileSync(output, serializePluginCommandManifest(entries)); + process.stderr.write(`✅ Plugin command manifest compiled: ${entries.length} entries → ${output}\n`); +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + await main(); +} diff --git a/src/cli.test.ts b/src/cli.test.ts index 6b98a601..0fa180d5 100644 --- a/src/cli.test.ts +++ b/src/cli.test.ts @@ -138,6 +138,35 @@ describe('createProgram root help descriptions', () => { expect(program.helpInformation()).toBe(formatRootHelp(presentation!)); }); + it('guides an absent site to explicit plugin search and install without side effects', async () => { + const plugin = await import('./plugin.js'); + const catalog = await import('./plugin-catalog.js'); + const install = vi.spyOn(plugin, 'installPlugin'); + const search = vi.spyOn(catalog, 'searchCatalogPlugins'); + const stderr = vi.spyOn(console, 'error').mockImplementation(() => {}); + const previousExitCode = process.exitCode; + const program = createProgram('', ''); + program.outputHelp = vi.fn(); + + try { + await program.parseAsync(['example', 'missing-command'], { from: 'user' }); + + expect(stderr.mock.calls.map(([line]) => line).join('\n')).toContain([ + 'Site "example" is not installed.', + 'Search: webcmd plugin search example', + 'Install using the installSource returned by search.', + ].join('\n')); + expect(install).not.toHaveBeenCalled(); + expect(search).not.toHaveBeenCalled(); + expect(process.exitCode).toBe(2); + } finally { + process.exitCode = previousExitCode; + stderr.mockRestore(); + install.mockRestore(); + search.mockRestore(); + } + }); + it('keeps site adapters out of root commands and lists sites in the root help tail', () => { const registry = getRegistry(); const snapshot = new Map(registry); diff --git a/src/cli.ts b/src/cli.ts index 55237cf5..7821be00 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -47,6 +47,7 @@ import { CLI_COMMAND } from './brand.js'; import type { BrowserDownloadWaitResult, IPage, ScreenshotOptions } from './types.js'; import type { BrowserWindowMode } from './runtime.js'; import { configureRootCommandSurface } from './root-command-surface.js'; +import { missingPluginGuidance } from './discovery.js'; const CLI_FILE = fileURLToPath(import.meta.url); const BROWSER_TAB_OPTION_DESCRIPTION = 'Target tab/page identity returned by "browser open", "browser tab new", or "browser tab list"'; @@ -3751,11 +3752,8 @@ cli({ // Only explicitly registered external CLIs are allowed. program.on('command:*', (operands: string[]) => { - const binary = operands[0]; - console.error(`error: unknown command '${binary}'`); - if (isBinaryInstalled(binary)) { - console.error(` Tip: '${binary}' exists on your PATH. Use 'webcmd external register ${binary}' to add it as an external CLI.`); - } + const binary = operands[0]!; + console.error(missingPluginGuidance(binary)); program.outputHelp(); process.exitCode = EXIT_CODES.USAGE_ERROR; }); diff --git a/src/convention-audit.test.ts b/src/convention-audit.test.ts index 211f9f22..82388b01 100644 --- a/src/convention-audit.test.ts +++ b/src/convention-audit.test.ts @@ -113,6 +113,23 @@ describe('convention audit', () => { expect(runConventionAudit({ projectRoot: root, target: 'missing' }).summary.commands).toBe(0); }); + it('includes plugin inventory entries and resolves plugin source paths', () => { + const root = makeProject([], {}); + const pluginFile = path.join(root, 'plugins', 'demo', 'search.js'); + fs.mkdirSync(path.dirname(pluginFile), { recursive: true }); + fs.writeFileSync(pluginFile, 'export function run() { rows.push({ id: 1, hidden: true }); }'); + fs.writeFileSync(path.join(root, 'plugin-command-manifest.json'), JSON.stringify([{ + site: 'demo', name: 'search', access: 'read', columns: ['id'], sourceFile: 'plugins/demo/search.js', + }])); + + const report = runConventionAudit({ projectRoot: root }); + + expect(report.summary).toMatchObject({ commands: 1, files_scanned: 1 }); + expect(report.categories.find(item => item.rule === 'silent-column-drop')!.violations[0]).toMatchObject({ + command: 'demo/search', file: 'plugins/demo/search.js', + }); + }); + it('renders a compact text report', () => { const root = makeProject([ { site: 'demo', name: 'search', access: 'read', columns: ['id'], sourceFile: 'demo/search.js' }, diff --git a/src/convention-audit.ts b/src/convention-audit.ts index 8476638c..4ba8ccd5 100644 --- a/src/convention-audit.ts +++ b/src/convention-audit.ts @@ -126,8 +126,14 @@ const WRITE_PAIR_RULES: Array<{ ]; export function runConventionAudit(opts: ConventionAuditOptions): ConventionAuditReport { - const manifestPath = opts.manifestPath ?? path.join(opts.projectRoot, 'cli-manifest.json'); - const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) as ManifestCommand[]; + const manifestPaths = opts.manifestPath + ? [opts.manifestPath] + : ['cli-manifest.json', 'plugin-command-manifest.json'] + .map(file => path.join(opts.projectRoot, file)) + .filter(fs.existsSync); + const manifest = manifestPaths.flatMap(manifestPath => ( + JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) as ManifestCommand[] + )); const filtered = manifest.filter((entry) => matchesTarget(entry, opts)); const violations: ConventionViolation[] = []; const sourceCache = new Map(); @@ -234,7 +240,9 @@ function matchesTarget(entry: ManifestCommand, opts: Pick { * Each subdirectory is treated as a plugin (site = directory name). * Files inside are scanned flat (no nested site subdirs). */ -export async function discoverPlugins(): Promise { - try { await fs.promises.access(PLUGINS_DIR); } catch { return; } - const entries = await fs.promises.readdir(PLUGINS_DIR, { withFileTypes: true }); +export async function discoverPlugins(pluginsDir: string = PLUGINS_DIR): Promise { + try { await fs.promises.access(pluginsDir); } catch { return; } + const entries = await fs.promises.readdir(pluginsDir, { withFileTypes: true }); await Promise.all(entries.map(async (entry) => { - const pluginDir = path.join(PLUGINS_DIR, entry.name); + const pluginDir = path.join(pluginsDir, entry.name); if (!(await isDiscoverablePluginDir(entry, pluginDir))) return; await discoverPluginDir(pluginDir, entry.name); })); } +export function missingPluginGuidance(site: string): string { + return [ + `Site "${site}" is not installed.`, + `Search: ${CLI_COMMAND} plugin search ${site}`, + 'Install using the installSource returned by search.', + ].join('\n'); +} + /** * Flat scan: read ts/js files directly in a plugin directory. * Unlike discoverClisFromFs, this does NOT expect nested site subdirectories. diff --git a/src/engine.test.ts b/src/engine.test.ts index d6c5a651..eaa3e9bf 100644 --- a/src/engine.test.ts +++ b/src/engine.test.ts @@ -291,6 +291,27 @@ browser: false await expect(discoverPlugins()).resolves.not.toThrow(); }); + it('discovers only the explicitly supplied installed-plugin root', async () => { + const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'webcmd-installed-plugins-')); + const pluginDir = path.join(root, 'explicit-plugin'); + try { + await fs.promises.mkdir(pluginDir, { recursive: true }); + await fs.promises.writeFile(path.join(pluginDir, 'hello.js'), ` +import { cli, Strategy } from '${pathToFileURL(path.join(process.cwd(), 'src', 'registry.ts')).href}'; +cli({ + site: 'explicit-plugin', name: 'hello', access: 'read', description: 'hello', + strategy: Strategy.PUBLIC, browser: false, func: async () => [{ ok: true }], +}); +`); + + await discoverPlugins(root); + + expect(getRegistry().get('explicit-plugin/hello')).toBeDefined(); + } finally { + await fs.promises.rm(root, { recursive: true, force: true }); + } + }); + it('ignores YAML files in symlinked plugin directories (YAML format removed)', async () => { await fs.promises.mkdir(PLUGINS_DIR, { recursive: true }); await fs.promises.mkdir(symlinkTargetDir, { recursive: true }); diff --git a/src/hosted/main-lifecycle.test.ts b/src/hosted/main-lifecycle.test.ts index 4c805c3c..be5868d1 100644 --- a/src/hosted/main-lifecycle.test.ts +++ b/src/hosted/main-lifecycle.test.ts @@ -102,7 +102,12 @@ describe('hosted CLI process lifecycle', () => { const result = await runCli(['missing-site', 'child', '--format', 'json'], fixture.env); expect(result.status).toBe(2); - expect(result.stderr).toBe("error: unknown command 'missing-site'\n"); + expect(result.stderr).toBe([ + 'Site "missing-site" is not installed.', + 'Search: webcmd plugin search missing-site', + 'Install using the installSource returned by search.', + '', + ].join('\n')); expect(result.stdout).toContain('Local-only commands:'); await expect(readFile(fixture.discoverySentinel, 'utf8')).rejects.toMatchObject({ code: 'ENOENT' }); }, 20_000); diff --git a/src/hosted/root-command-surface.test.ts b/src/hosted/root-command-surface.test.ts index c0098f0f..92f1c793 100644 --- a/src/hosted/root-command-surface.test.ts +++ b/src/hosted/root-command-surface.test.ts @@ -14,6 +14,13 @@ import { } from '../root-command-surface.js'; import { runHostedCli } from './runner.js'; +const MISSING_SITE_GUIDANCE = [ + 'Site "missing" is not installed.', + 'Search: webcmd plugin search missing', + 'Install using the installSource returned by search.', + '', +].join('\n'); + function sink(): { stream: Writable; text: () => string } { let data = ''; return { @@ -436,7 +443,7 @@ describe('hosted root preflight call order', () => { }); expect(result).toEqual({ handled: true, exitCode: 2 }); - expect(stderr.text()).toBe("error: unknown command 'missing'\n"); + expect(stderr.text()).toBe(MISSING_SITE_GUIDANCE); expect(stdout.text()).toBe(formatRootHelp(HOSTED_ROOT_HELP)); }); @@ -670,9 +677,9 @@ describe('hosted root preflight call order', () => { expect(local.stderr).toBe(''); } else { expect(stdout.text()).toBe(formatRootHelp(HOSTED_ROOT_HELP)); - expect(stderr.text()).toBe("error: unknown command 'missing'\n"); + expect(stderr.text()).toBe(MISSING_SITE_GUIDANCE); expect(local.stdout).not.toBe(''); - expect(local.stderr).toBe("error: unknown command 'missing'\n"); + expect(local.stderr).toBe(MISSING_SITE_GUIDANCE); } expect(fetchImpl).toHaveBeenCalledTimes(1); expect(String(fetchImpl.mock.calls[0]![0])).toBe('https://api.example.com/v1/manifest'); @@ -719,7 +726,7 @@ describe('hosted root preflight call order', () => { fetchImpl, }); - expect(local).toMatchObject({ exitCode: 2, stderr: "error: unknown command 'missing'\n" }); + expect(local).toMatchObject({ exitCode: 2, stderr: MISSING_SITE_GUIDANCE }); expect(local.stdout).not.toBe(''); expect(hosted).toEqual({ handled: true, exitCode: local.exitCode }); expect(stdout.text()).toBe(formatRootHelp(HOSTED_ROOT_HELP)); diff --git a/src/hosted/runner.test.ts b/src/hosted/runner.test.ts index d9d4c57f..0a1d1aed 100644 --- a/src/hosted/runner.test.ts +++ b/src/hosted/runner.test.ts @@ -466,7 +466,7 @@ describe('runHostedCli', () => { ['missing-site', 'child', 'grandchild'], ['missing-site', '--format', 'json'], ['missing-site', '--trace=on'], - ])('matches local unknown-site bytes when argv is %j', async (...argv) => { + ])('guides an unknown site without searching, installing, or retrying when argv is %j', async (...argv) => { const stdout = sink(); const stderr = sink(); const fetchImpl = vi.fn(async () => manifestResponse()); @@ -479,10 +479,15 @@ describe('runHostedCli', () => { }); expect(result).toEqual({ handled: true, exitCode: 2 }); - expect(stderr.text()).toBe("error: unknown command 'missing-site'\n"); + expect(stderr.text()).toContain([ + 'Site "missing-site" is not installed.', + 'Search: webcmd plugin search missing-site', + 'Install using the installSource returned by search.', + ].join('\n')); expect(stdout.text()).toBe(formatRootHelp(HOSTED_ROOT_HELP)); expect(fetchImpl).toHaveBeenCalledTimes(1); expect(String(fetchImpl.mock.calls[0]![0])).toMatch(/\/v1\/manifest$/); + expect(fetchImpl.mock.calls.some(([url]) => /plugin|execute/.test(String(url)))).toBe(false); }); it('matches local Commander bytes for an unknown site command', async () => { diff --git a/src/hosted/runner.ts b/src/hosted/runner.ts index 815bf426..ffabc230 100644 --- a/src/hosted/runner.ts +++ b/src/hosted/runner.ts @@ -20,6 +20,7 @@ import { PKG_VERSION } from '../version.js'; import { getCompletionScriptFast } from '../completion-fast.js'; import { browserCommandCatalog } from '../browser/command-catalog.js'; import { CLI_COMMAND } from '../brand.js'; +import { missingPluginGuidance } from '../discovery.js'; import { HostedClient, HostedClientError, resolveWorkspace } from './client.js'; import { parseHostedInvocation } from './args.js'; import { HostedBrowserHelp, parseHostedBrowserStructure } from './browser-args.js'; @@ -261,7 +262,7 @@ async function dispatchHosted( return; } throw new CommanderCompatibleError( - `error: unknown command '${site}'\n`, + `${missingPluginGuidance(site)}\n`, EXIT_CODES.USAGE_ERROR, formatRootHelp(HOSTED_ROOT_HELP), ); diff --git a/src/main.ts b/src/main.ts index 96b1eceb..fe1c98b0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -122,7 +122,7 @@ if (getCompIdx !== -1) { // ── Full startup path ─────────────────────────────────────────────────── // Dynamic imports: these are deferred so the fast path above never pays the cost. -const { discoverClis, discoverPlugins, ensureUserCliCompatShims, ensureUserAdapters } = await import('./discovery.js'); +const { discoverClis, discoverPlugins, ensureUserCliCompatShims, ensureUserAdapters, PLUGINS_DIR } = await import('./discovery.js'); const { getCompletions } = await import('./completion.js'); const { runCli } = await import('./cli.js'); const { emitHook } = await import('./hooks.js'); @@ -149,7 +149,7 @@ if (skipUserDiscovery) { discoverClis(BUILTIN_CLIS), ]); await discoverClis(USER_CLIS); - await discoverPlugins(); + await discoverPlugins(PLUGINS_DIR); } // Register exit hook: notice appears after command output (same as npm/gh/yarn) diff --git a/src/migrate-cli-sites.test.ts b/src/migrate-cli-sites.test.ts new file mode 100644 index 00000000..13214c06 --- /dev/null +++ b/src/migrate-cli-sites.test.ts @@ -0,0 +1,98 @@ +import { execFileSync, spawnSync } from 'node:child_process'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import { afterEach, describe, expect, it } from 'vitest'; + +const script = path.resolve('scripts/migrate-cli-sites.mjs'); +const roots: string[] = []; + +afterEach(() => { + for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true }); +}); + +function fixture(): string { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-migrate-')); + roots.push(root); + fs.mkdirSync(path.join(root, 'clis', 'example'), { recursive: true }); + fs.mkdirSync(path.join(root, 'plugins', 'sibling'), { recursive: true }); + fs.mkdirSync(path.join(root, 'scripts'), { recursive: true }); + fs.writeFileSync(path.join(root, 'clis', 'example', 'search.js'), ` + import { requireSearchQuery } from '../_shared/common.js'; + cli({ site: 'example', name: 'search', description: 'Search examples' }); + `); + fs.writeFileSync(path.join(root, 'clis', 'example', 'helper.js'), 'export const helper = true;\n'); + fs.writeFileSync(path.join(root, 'clis', 'example', 'fixture.html'), '
fixture
\n'); + fs.writeFileSync(path.join(root, 'clis', 'example', 'search.test.js'), ` + import { search } from './search.js'; + import { requireSearchQuery } from '../_shared/common.js'; + `); + fs.writeFileSync(path.join(root, 'plugins', 'sibling', 'keep.txt'), 'unchanged\n'); + fs.writeFileSync(path.join(root, 'cli-manifest.json'), JSON.stringify([{ + site: 'example', name: 'search', description: 'Search examples', sourceFile: 'example/search.js', + }])); + fs.writeFileSync(path.join(root, 'scripts', 'silent-column-drop-baseline.json'), JSON.stringify([{ + command: 'example/search', file: 'clis/example/search.js', missing: ['url'], + }])); + fs.writeFileSync(path.join(root, 'scripts', 'typed-error-lint-baseline.json'), JSON.stringify([{ + rule: 'silent-clamp', command: 'example/search', file: 'clis/example/search.js', line: 1, text: 'x', occurrence: 0, + }])); + return root; +} + +describe('migrate-cli-sites', () => { + it('moves one site into a self-contained plugin without touching siblings', () => { + const root = fixture(); + + execFileSync(process.execPath, [script, 'example'], { cwd: root }); + + const plugin = path.join(root, 'plugins', 'example'); + expect(fs.existsSync(path.join(root, 'clis', 'example'))).toBe(false); + expect(fs.readFileSync(path.join(plugin, 'helper.js'), 'utf8')).toContain('helper'); + expect(fs.readFileSync(path.join(plugin, 'fixture.html'), 'utf8')).toContain('fixture'); + expect(fs.readFileSync(path.join(plugin, 'search.js'), 'utf8')).toContain("from '@agentrhq/webcmd/plugin-runtime'"); + expect(fs.readFileSync(path.join(plugin, 'test', 'search.test.js'), 'utf8')).toContain("from '../search.js'"); + expect(fs.readFileSync(path.join(plugin, 'test', 'search.test.js'), 'utf8')).toContain("from '@agentrhq/webcmd/plugin-runtime'"); + expect(JSON.parse(fs.readFileSync(path.join(plugin, 'package.json'), 'utf8'))).toEqual({ + name: 'webcmd-plugin-example', + version: '0.1.0', + type: 'module', + description: 'Webcmd commands for example', + peerDependencies: { '@agentrhq/webcmd': '>=0.6.0' }, + }); + expect(JSON.parse(fs.readFileSync(path.join(plugin, 'webcmd-plugin.json'), 'utf8'))).toEqual({ + name: 'example', + version: '0.1.0', + description: 'Webcmd commands for example', + webcmd: '>=0.6.0', + author: { name: 'WebCMD Agent', handle: 'agentrhq' }, + }); + expect(fs.readFileSync(path.join(plugin, 'README.md'), 'utf8')).toContain('| `webcmd example search` | Search examples |'); + expect(fs.readFileSync(path.join(root, 'plugins', 'sibling', 'keep.txt'), 'utf8')).toBe('unchanged\n'); + expect(fs.readFileSync(path.join(root, 'scripts', 'silent-column-drop-baseline.json'), 'utf8')).toContain('plugins/example/search.js'); + expect(fs.readFileSync(path.join(root, 'scripts', 'typed-error-lint-baseline.json'), 'utf8')).toContain('plugins/example/search.js'); + }); + + it('refuses an existing plugin collision before moving anything', () => { + const root = fixture(); + fs.mkdirSync(path.join(root, 'plugins', 'example')); + + const result = spawnSync(process.execPath, [script, 'example'], { cwd: root, encoding: 'utf8' }); + + expect(result.status).toBe(1); + expect(result.stderr).toContain('plugins/example already exists'); + expect(fs.existsSync(path.join(root, 'clis', 'example', 'search.js'))).toBe(true); + }); + + it('allows the planned PyPI merge and preserves existing plugin-only files', () => { + const root = fixture(); + fs.renameSync(path.join(root, 'clis', 'example'), path.join(root, 'clis', 'pypi')); + fs.mkdirSync(path.join(root, 'plugins', 'pypi')); + fs.writeFileSync(path.join(root, 'plugins', 'pypi', 'releases.js'), 'plugin only\n'); + + execFileSync(process.execPath, [script, 'pypi'], { cwd: root }); + + expect(fs.readFileSync(path.join(root, 'plugins', 'pypi', 'releases.js'), 'utf8')).toBe('plugin only\n'); + expect(fs.existsSync(path.join(root, 'plugins', 'pypi', 'search.js'))).toBe(true); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index 59674a86..9936ebae 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -15,7 +15,7 @@ export default defineConfig({ { test: { name: 'adapter', - include: ['clis/**/*.test.{ts,js}', 'plugins/linkedin/test/**/*.test.{ts,js}'], + include: ['clis/**/*.test.{ts,js}', 'plugins/*/test/**/*.test.{ts,js}'], sequence: { groupOrder: 1 }, }, }, From 32255d32bb515327212ccaa8215adfc0416fb77c Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 16:03:39 +0530 Subject: [PATCH 08/39] fix: make plugin migrations collision safe --- scripts/migrate-cli-sites.mjs | 58 +++++++++++++++++++++++++---------- src/migrate-cli-sites.test.ts | 33 ++++++++++++++++++-- 2 files changed, 71 insertions(+), 20 deletions(-) diff --git a/scripts/migrate-cli-sites.mjs b/scripts/migrate-cli-sites.mjs index d8210d8b..3d31b3ef 100644 --- a/scripts/migrate-cli-sites.mjs +++ b/scripts/migrate-cli-sites.mjs @@ -14,10 +14,27 @@ for (const site of sites) { if (site !== 'pypi' && fs.existsSync(path.join(root, 'plugins', site))) { fail(`plugins/${site} already exists`); } + if (site === 'pypi') { + const source = path.join(root, 'clis', site); + const plugin = path.join(root, 'plugins', site); + for (const file of walk(source)) { + const target = destination(file, source, plugin); + if (fs.existsSync(target)) { + fail(`${path.relative(root, target)} already exists; merge pypi manually`); + } + } + } } -const manifest = readJson(path.join(root, 'cli-manifest.json'), []); -for (const site of sites) migrate(site, manifest.filter(entry => entry.site === site)); +const manifestPath = path.join(root, 'cli-manifest.json'); +let manifest = readJson(manifestPath, []); +for (const site of sites) { + migrate(site, manifest.filter(entry => entry.site === site)); + manifest = manifest + .filter(entry => entry.site !== site) + .sort((a, b) => String(a.site).localeCompare(String(b.site)) || String(a.name).localeCompare(String(b.name))); + writeJson(manifestPath, manifest); +} function migrate(site, commands) { const source = path.join(root, 'clis', site); @@ -40,21 +57,28 @@ function migrate(site, commands) { fs.rmSync(source, { recursive: true, force: true }); const description = `Webcmd commands for ${site}`; - writeJson(path.join(plugin, 'package.json'), { - name: `webcmd-plugin-${site}`, - version: '0.1.0', - type: 'module', - description, - peerDependencies: { '@agentrhq/webcmd': '>=0.6.0' }, - }); - writeJson(path.join(plugin, 'webcmd-plugin.json'), { - name: site, - version: '0.1.0', - description, - webcmd: '>=0.6.0', - author: { name: 'WebCMD Agent', handle: 'agentrhq' }, - }); - fs.writeFileSync(path.join(plugin, 'README.md'), readme(site, description, commands)); + const packageJson = path.join(plugin, 'package.json'); + if (!fs.existsSync(packageJson)) { + writeJson(packageJson, { + name: `webcmd-plugin-${site}`, + version: '0.1.0', + type: 'module', + description, + peerDependencies: { '@agentrhq/webcmd': '>=0.6.0' }, + }); + } + const pluginManifest = path.join(plugin, 'webcmd-plugin.json'); + if (!fs.existsSync(pluginManifest)) { + writeJson(pluginManifest, { + name: site, + version: '0.1.0', + description, + webcmd: '>=0.6.0', + author: { name: 'WebCMD Agent', handle: 'agentrhq' }, + }); + } + const readmePath = path.join(plugin, 'README.md'); + if (!fs.existsSync(readmePath)) fs.writeFileSync(readmePath, readme(site, description, commands)); for (const baseline of ['silent-column-drop-baseline.json', 'typed-error-lint-baseline.json']) { const file = path.join(root, 'scripts', baseline); diff --git a/src/migrate-cli-sites.test.ts b/src/migrate-cli-sites.test.ts index 13214c06..64f0e083 100644 --- a/src/migrate-cli-sites.test.ts +++ b/src/migrate-cli-sites.test.ts @@ -28,9 +28,11 @@ function fixture(): string { import { requireSearchQuery } from '../_shared/common.js'; `); fs.writeFileSync(path.join(root, 'plugins', 'sibling', 'keep.txt'), 'unchanged\n'); - fs.writeFileSync(path.join(root, 'cli-manifest.json'), JSON.stringify([{ - site: 'example', name: 'search', description: 'Search examples', sourceFile: 'example/search.js', - }])); + fs.writeFileSync(path.join(root, 'cli-manifest.json'), JSON.stringify([ + { site: 'zeta', name: 'last', description: 'Last', sourceFile: 'zeta/last.js' }, + { site: 'example', name: 'search', description: 'Search examples', sourceFile: 'example/search.js' }, + { site: 'alpha', name: 'first', description: 'First', sourceFile: 'alpha/first.js' }, + ])); fs.writeFileSync(path.join(root, 'scripts', 'silent-column-drop-baseline.json'), JSON.stringify([{ command: 'example/search', file: 'clis/example/search.js', missing: ['url'], }])); @@ -71,6 +73,8 @@ describe('migrate-cli-sites', () => { expect(fs.readFileSync(path.join(root, 'plugins', 'sibling', 'keep.txt'), 'utf8')).toBe('unchanged\n'); expect(fs.readFileSync(path.join(root, 'scripts', 'silent-column-drop-baseline.json'), 'utf8')).toContain('plugins/example/search.js'); expect(fs.readFileSync(path.join(root, 'scripts', 'typed-error-lint-baseline.json'), 'utf8')).toContain('plugins/example/search.js'); + expect(JSON.parse(fs.readFileSync(path.join(root, 'cli-manifest.json'), 'utf8')).map((entry: { site: string; name: string }) => `${entry.site}/${entry.name}`)) + .toEqual(['alpha/first', 'zeta/last']); }); it('refuses an existing plugin collision before moving anything', () => { @@ -89,10 +93,33 @@ describe('migrate-cli-sites', () => { fs.renameSync(path.join(root, 'clis', 'example'), path.join(root, 'clis', 'pypi')); fs.mkdirSync(path.join(root, 'plugins', 'pypi')); fs.writeFileSync(path.join(root, 'plugins', 'pypi', 'releases.js'), 'plugin only\n'); + fs.writeFileSync(path.join(root, 'plugins', 'pypi', 'README.md'), 'existing readme\n'); + fs.writeFileSync(path.join(root, 'plugins', 'pypi', 'package.json'), '{"existing":true}\n'); + fs.writeFileSync(path.join(root, 'plugins', 'pypi', 'webcmd-plugin.json'), '{"author":{"name":"Kemal Kaya","handle":"yoldaolmak"}}\n'); execFileSync(process.execPath, [script, 'pypi'], { cwd: root }); expect(fs.readFileSync(path.join(root, 'plugins', 'pypi', 'releases.js'), 'utf8')).toBe('plugin only\n'); expect(fs.existsSync(path.join(root, 'plugins', 'pypi', 'search.js'))).toBe(true); + expect(fs.readFileSync(path.join(root, 'plugins', 'pypi', 'README.md'), 'utf8')).toBe('existing readme\n'); + expect(fs.readFileSync(path.join(root, 'plugins', 'pypi', 'package.json'), 'utf8')).toBe('{"existing":true}\n'); + expect(fs.readFileSync(path.join(root, 'plugins', 'pypi', 'webcmd-plugin.json'), 'utf8')).toContain('Kemal Kaya'); + }); + + it('refuses a colliding PyPI command before overwriting either side or attribution', () => { + const root = fixture(); + fs.renameSync(path.join(root, 'clis', 'example'), path.join(root, 'clis', 'pypi')); + fs.mkdirSync(path.join(root, 'plugins', 'pypi')); + fs.writeFileSync(path.join(root, 'plugins', 'pypi', 'search.js'), 'existing plugin command\n'); + fs.writeFileSync(path.join(root, 'plugins', 'pypi', 'webcmd-plugin.json'), '{"author":{"name":"Kemal Kaya","handle":"yoldaolmak"}}\n'); + + const result = spawnSync(process.execPath, [script, 'pypi'], { cwd: root, encoding: 'utf8' }); + + expect(result.status).toBe(1); + expect(result.stderr).toContain('plugins/pypi/search.js already exists; merge pypi manually'); + expect(fs.readFileSync(path.join(root, 'plugins', 'pypi', 'search.js'), 'utf8')).toBe('existing plugin command\n'); + expect(fs.readFileSync(path.join(root, 'plugins', 'pypi', 'webcmd-plugin.json'), 'utf8')).toContain('Kemal Kaya'); + expect(fs.existsSync(path.join(root, 'clis', 'pypi', 'search.js'))).toBe(true); + expect(fs.existsSync(path.join(root, 'plugins', 'pypi', 'helper.js'))).toBe(false); }); }); From 29dcbdd7fd078be9de85cf01aa80b83fce01aa5b Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 16:10:48 +0530 Subject: [PATCH 09/39] refactor: migrate public API adapters to plugins --- cli-manifest.json | 8048 ++++++----------- plugin-command-manifest.json | 3064 ++++++- plugins/apple-podcasts/README.md | 17 + {clis => plugins}/apple-podcasts/episodes.js | 0 plugins/apple-podcasts/package.json | 9 + {clis => plugins}/apple-podcasts/search.js | 0 .../apple-podcasts/test}/commands.test.js | 4 +- .../apple-podcasts/test}/utils.test.js | 2 +- {clis => plugins}/apple-podcasts/top.js | 0 {clis => plugins}/apple-podcasts/utils.js | 0 plugins/apple-podcasts/webcmd-plugin.json | 10 + plugins/archive/README.md | 18 + {clis => plugins}/archive/item.js | 0 plugins/archive/package.json | 9 + {clis => plugins}/archive/search.js | 0 {clis => plugins}/archive/snapshots.js | 0 .../archive/test}/archive.test.js | 8 +- {clis => plugins}/archive/wayback.js | 0 plugins/archive/webcmd-plugin.json | 10 + plugins/arxiv/README.md | 18 + {clis => plugins}/arxiv/author.js | 0 plugins/arxiv/package.json | 9 + {clis => plugins}/arxiv/paper.js | 0 {clis => plugins}/arxiv/recent.js | 0 {clis => plugins}/arxiv/search.js | 0 .../arxiv/test}/arxiv.test.js | 8 +- {clis => plugins}/arxiv/utils.js | 0 plugins/arxiv/webcmd-plugin.json | 10 + plugins/bbc/README.md | 16 + {clis => plugins}/bbc/news.js | 0 plugins/bbc/package.json | 9 + {clis => plugins}/bbc/topic.js | 0 {clis => plugins}/bbc/utils.js | 0 plugins/bbc/webcmd-plugin.json | 10 + plugins/binance/README.md | 25 + {clis => plugins}/binance/asks.js | 0 {clis => plugins}/binance/depth.js | 0 {clis => plugins}/binance/gainers.js | 0 {clis => plugins}/binance/klines.js | 0 {clis => plugins}/binance/losers.js | 0 plugins/binance/package.json | 9 + {clis => plugins}/binance/pairs.js | 0 {clis => plugins}/binance/price.js | 0 {clis => plugins}/binance/prices.js | 0 .../binance/test}/commands.test.js | 6 +- {clis => plugins}/binance/ticker.js | 0 {clis => plugins}/binance/top.js | 0 {clis => plugins}/binance/trades.js | 0 plugins/binance/webcmd-plugin.json | 10 + plugins/bluesky/README.md | 23 + {clis => plugins}/bluesky/feeds.js | 0 {clis => plugins}/bluesky/followers.js | 0 {clis => plugins}/bluesky/following.js | 0 plugins/bluesky/package.json | 9 + {clis => plugins}/bluesky/profile.js | 0 {clis => plugins}/bluesky/search.js | 0 {clis => plugins}/bluesky/starter-packs.js | 0 {clis => plugins}/bluesky/thread.js | 0 {clis => plugins}/bluesky/trending.js | 0 {clis => plugins}/bluesky/user.js | 0 plugins/bluesky/webcmd-plugin.json | 10 + plugins/coingecko/README.md | 21 + {clis => plugins}/coingecko/categories.js | 0 {clis => plugins}/coingecko/coin.js | 0 {clis => plugins}/coingecko/derivatives.js | 0 {clis => plugins}/coingecko/exchanges.js | 0 {clis => plugins}/coingecko/global.js | 0 plugins/coingecko/package.json | 9 + .../coingecko/test}/coingecko.test.js | 4 +- {clis => plugins}/coingecko/top.js | 0 {clis => plugins}/coingecko/trending.js | 0 plugins/coingecko/webcmd-plugin.json | 10 + plugins/crates/README.md | 16 + {clis => plugins}/crates/crate.js | 0 plugins/crates/package.json | 9 + {clis => plugins}/crates/search.js | 0 {clis => plugins}/crates/utils.js | 0 plugins/crates/webcmd-plugin.json | 10 + plugins/dblp/README.md | 18 + {clis => plugins}/dblp/author.js | 0 plugins/dblp/package.json | 9 + {clis => plugins}/dblp/paper.js | 0 {clis => plugins}/dblp/search.js | 0 {clis/dblp => plugins/dblp/test}/dblp.test.js | 6 +- {clis => plugins}/dblp/utils.js | 0 {clis => plugins}/dblp/venue.js | 0 plugins/dblp/webcmd-plugin.json | 10 + plugins/defillama/README.md | 16 + plugins/defillama/package.json | 9 + {clis => plugins}/defillama/protocol.js | 0 {clis => plugins}/defillama/protocols.js | 0 .../defillama/test}/defillama.test.js | 4 +- {clis => plugins}/defillama/utils.js | 0 plugins/defillama/webcmd-plugin.json | 10 + plugins/devto/README.md | 19 + {clis => plugins}/devto/latest.js | 0 plugins/devto/package.json | 9 + {clis => plugins}/devto/read.js | 0 {clis => plugins}/devto/tag.js | 0 .../devto/test}/devto.test.js | 8 +- {clis => plugins}/devto/top.js | 0 {clis => plugins}/devto/user.js | 0 plugins/devto/webcmd-plugin.json | 10 + plugins/dictionary/README.md | 17 + {clis => plugins}/dictionary/examples.js | 0 plugins/dictionary/package.json | 9 + {clis => plugins}/dictionary/search.js | 0 {clis => plugins}/dictionary/synonyms.js | 0 plugins/dictionary/webcmd-plugin.json | 10 + plugins/dockerhub/README.md | 16 + {clis => plugins}/dockerhub/image.js | 0 plugins/dockerhub/package.json | 9 + {clis => plugins}/dockerhub/search.js | 0 {clis => plugins}/dockerhub/utils.js | 0 plugins/dockerhub/webcmd-plugin.json | 10 + plugins/endoflife/README.md | 15 + plugins/endoflife/package.json | 9 + {clis => plugins}/endoflife/product.js | 0 .../endoflife/test}/endoflife.test.js | 2 +- {clis => plugins}/endoflife/utils.js | 0 plugins/endoflife/webcmd-plugin.json | 10 + plugins/flathub/README.md | 16 + {clis => plugins}/flathub/app.js | 0 plugins/flathub/package.json | 9 + {clis => plugins}/flathub/search.js | 0 .../flathub/test}/flathub.test.js | 4 +- {clis => plugins}/flathub/utils.js | 0 plugins/flathub/webcmd-plugin.json | 10 + plugins/github-trending/README.md | 15 + plugins/github-trending/package.json | 9 + {clis => plugins}/github-trending/repos.js | 0 .../github-trending/test}/repos.test.js | 2 +- plugins/github-trending/webcmd-plugin.json | 10 + plugins/goproxy/README.md | 16 + {clis => plugins}/goproxy/module.js | 0 plugins/goproxy/package.json | 9 + .../goproxy/test}/goproxy.test.js | 4 +- {clis => plugins}/goproxy/utils.js | 0 {clis => plugins}/goproxy/versions.js | 0 plugins/goproxy/webcmd-plugin.json | 10 + plugins/hackernews/README.md | 23 + {clis => plugins}/hackernews/ask.js | 0 {clis => plugins}/hackernews/best.js | 0 {clis => plugins}/hackernews/jobs.js | 0 {clis => plugins}/hackernews/new.js | 0 plugins/hackernews/package.json | 9 + {clis => plugins}/hackernews/read.js | 0 {clis => plugins}/hackernews/search.js | 0 {clis => plugins}/hackernews/show.js | 0 .../hackernews/test}/hackernews.test.js | 16 +- {clis => plugins}/hackernews/top.js | 0 {clis => plugins}/hackernews/user.js | 0 plugins/hackernews/webcmd-plugin.json | 10 + scripts/silent-column-drop-baseline.json | 10 +- scripts/typed-error-lint-baseline.json | 20 +- webcmd-plugin.json | 180 + 156 files changed, 6460 insertions(+), 5607 deletions(-) create mode 100644 plugins/apple-podcasts/README.md rename {clis => plugins}/apple-podcasts/episodes.js (100%) create mode 100644 plugins/apple-podcasts/package.json rename {clis => plugins}/apple-podcasts/search.js (100%) rename {clis/apple-podcasts => plugins/apple-podcasts/test}/commands.test.js (99%) rename {clis/apple-podcasts => plugins/apple-podcasts/test}/utils.test.js (96%) rename {clis => plugins}/apple-podcasts/top.js (100%) rename {clis => plugins}/apple-podcasts/utils.js (100%) create mode 100644 plugins/apple-podcasts/webcmd-plugin.json create mode 100644 plugins/archive/README.md rename {clis => plugins}/archive/item.js (100%) create mode 100644 plugins/archive/package.json rename {clis => plugins}/archive/search.js (100%) rename {clis => plugins}/archive/snapshots.js (100%) rename {clis/archive => plugins/archive/test}/archive.test.js (99%) rename {clis => plugins}/archive/wayback.js (100%) create mode 100644 plugins/archive/webcmd-plugin.json create mode 100644 plugins/arxiv/README.md rename {clis => plugins}/arxiv/author.js (100%) create mode 100644 plugins/arxiv/package.json rename {clis => plugins}/arxiv/paper.js (100%) rename {clis => plugins}/arxiv/recent.js (100%) rename {clis => plugins}/arxiv/search.js (100%) rename {clis/arxiv => plugins/arxiv/test}/arxiv.test.js (98%) rename {clis => plugins}/arxiv/utils.js (100%) create mode 100644 plugins/arxiv/webcmd-plugin.json create mode 100644 plugins/bbc/README.md rename {clis => plugins}/bbc/news.js (100%) create mode 100644 plugins/bbc/package.json rename {clis => plugins}/bbc/topic.js (100%) rename {clis => plugins}/bbc/utils.js (100%) create mode 100644 plugins/bbc/webcmd-plugin.json create mode 100644 plugins/binance/README.md rename {clis => plugins}/binance/asks.js (100%) rename {clis => plugins}/binance/depth.js (100%) rename {clis => plugins}/binance/gainers.js (100%) rename {clis => plugins}/binance/klines.js (100%) rename {clis => plugins}/binance/losers.js (100%) create mode 100644 plugins/binance/package.json rename {clis => plugins}/binance/pairs.js (100%) rename {clis => plugins}/binance/price.js (100%) rename {clis => plugins}/binance/prices.js (100%) rename {clis/binance => plugins/binance/test}/commands.test.js (97%) rename {clis => plugins}/binance/ticker.js (100%) rename {clis => plugins}/binance/top.js (100%) rename {clis => plugins}/binance/trades.js (100%) create mode 100644 plugins/binance/webcmd-plugin.json create mode 100644 plugins/bluesky/README.md rename {clis => plugins}/bluesky/feeds.js (100%) rename {clis => plugins}/bluesky/followers.js (100%) rename {clis => plugins}/bluesky/following.js (100%) create mode 100644 plugins/bluesky/package.json rename {clis => plugins}/bluesky/profile.js (100%) rename {clis => plugins}/bluesky/search.js (100%) rename {clis => plugins}/bluesky/starter-packs.js (100%) rename {clis => plugins}/bluesky/thread.js (100%) rename {clis => plugins}/bluesky/trending.js (100%) rename {clis => plugins}/bluesky/user.js (100%) create mode 100644 plugins/bluesky/webcmd-plugin.json create mode 100644 plugins/coingecko/README.md rename {clis => plugins}/coingecko/categories.js (100%) rename {clis => plugins}/coingecko/coin.js (100%) rename {clis => plugins}/coingecko/derivatives.js (100%) rename {clis => plugins}/coingecko/exchanges.js (100%) rename {clis => plugins}/coingecko/global.js (100%) create mode 100644 plugins/coingecko/package.json rename {clis/coingecko => plugins/coingecko/test}/coingecko.test.js (98%) rename {clis => plugins}/coingecko/top.js (100%) rename {clis => plugins}/coingecko/trending.js (100%) create mode 100644 plugins/coingecko/webcmd-plugin.json create mode 100644 plugins/crates/README.md rename {clis => plugins}/crates/crate.js (100%) create mode 100644 plugins/crates/package.json rename {clis => plugins}/crates/search.js (100%) rename {clis => plugins}/crates/utils.js (100%) create mode 100644 plugins/crates/webcmd-plugin.json create mode 100644 plugins/dblp/README.md rename {clis => plugins}/dblp/author.js (100%) create mode 100644 plugins/dblp/package.json rename {clis => plugins}/dblp/paper.js (100%) rename {clis => plugins}/dblp/search.js (100%) rename {clis/dblp => plugins/dblp/test}/dblp.test.js (99%) rename {clis => plugins}/dblp/utils.js (100%) rename {clis => plugins}/dblp/venue.js (100%) create mode 100644 plugins/dblp/webcmd-plugin.json create mode 100644 plugins/defillama/README.md create mode 100644 plugins/defillama/package.json rename {clis => plugins}/defillama/protocol.js (100%) rename {clis => plugins}/defillama/protocols.js (100%) rename {clis/defillama => plugins/defillama/test}/defillama.test.js (98%) rename {clis => plugins}/defillama/utils.js (100%) create mode 100644 plugins/defillama/webcmd-plugin.json create mode 100644 plugins/devto/README.md rename {clis => plugins}/devto/latest.js (100%) create mode 100644 plugins/devto/package.json rename {clis => plugins}/devto/read.js (100%) rename {clis => plugins}/devto/tag.js (100%) rename {clis/devto => plugins/devto/test}/devto.test.js (99%) rename {clis => plugins}/devto/top.js (100%) rename {clis => plugins}/devto/user.js (100%) create mode 100644 plugins/devto/webcmd-plugin.json create mode 100644 plugins/dictionary/README.md rename {clis => plugins}/dictionary/examples.js (100%) create mode 100644 plugins/dictionary/package.json rename {clis => plugins}/dictionary/search.js (100%) rename {clis => plugins}/dictionary/synonyms.js (100%) create mode 100644 plugins/dictionary/webcmd-plugin.json create mode 100644 plugins/dockerhub/README.md rename {clis => plugins}/dockerhub/image.js (100%) create mode 100644 plugins/dockerhub/package.json rename {clis => plugins}/dockerhub/search.js (100%) rename {clis => plugins}/dockerhub/utils.js (100%) create mode 100644 plugins/dockerhub/webcmd-plugin.json create mode 100644 plugins/endoflife/README.md create mode 100644 plugins/endoflife/package.json rename {clis => plugins}/endoflife/product.js (100%) rename {clis/endoflife => plugins/endoflife/test}/endoflife.test.js (99%) rename {clis => plugins}/endoflife/utils.js (100%) create mode 100644 plugins/endoflife/webcmd-plugin.json create mode 100644 plugins/flathub/README.md rename {clis => plugins}/flathub/app.js (100%) create mode 100644 plugins/flathub/package.json rename {clis => plugins}/flathub/search.js (100%) rename {clis/flathub => plugins/flathub/test}/flathub.test.js (99%) rename {clis => plugins}/flathub/utils.js (100%) create mode 100644 plugins/flathub/webcmd-plugin.json create mode 100644 plugins/github-trending/README.md create mode 100644 plugins/github-trending/package.json rename {clis => plugins}/github-trending/repos.js (100%) rename {clis/github-trending => plugins/github-trending/test}/repos.test.js (99%) create mode 100644 plugins/github-trending/webcmd-plugin.json create mode 100644 plugins/goproxy/README.md rename {clis => plugins}/goproxy/module.js (100%) create mode 100644 plugins/goproxy/package.json rename {clis/goproxy => plugins/goproxy/test}/goproxy.test.js (98%) rename {clis => plugins}/goproxy/utils.js (100%) rename {clis => plugins}/goproxy/versions.js (100%) create mode 100644 plugins/goproxy/webcmd-plugin.json create mode 100644 plugins/hackernews/README.md rename {clis => plugins}/hackernews/ask.js (100%) rename {clis => plugins}/hackernews/best.js (100%) rename {clis => plugins}/hackernews/jobs.js (100%) rename {clis => plugins}/hackernews/new.js (100%) create mode 100644 plugins/hackernews/package.json rename {clis => plugins}/hackernews/read.js (100%) rename {clis => plugins}/hackernews/search.js (100%) rename {clis => plugins}/hackernews/show.js (100%) rename {clis/hackernews => plugins/hackernews/test}/hackernews.test.js (96%) rename {clis => plugins}/hackernews/top.js (100%) rename {clis => plugins}/hackernews/user.js (100%) create mode 100644 plugins/hackernews/webcmd-plugin.json diff --git a/cli-manifest.json b/cli-manifest.json index 515e034d..8ef3ec5c 100644 --- a/cli-manifest.json +++ b/cli-manifest.json @@ -1444,1564 +1444,1721 @@ "sourceFile": "antigravity/storage.js" }, { - "site": "apple-podcasts", - "name": "episodes", - "description": "List recent episodes of an Apple Podcast (use ID from search)", + "site": "band", + "name": "bands", + "description": "List all Bands you belong to", "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Podcast ID (collectionId from search output)" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Max episodes to show" - } + "domain": "www.band.us", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "band_no", + "name", + "members" ], + "type": "js", + "modulePath": "band/bands.js", + "sourceFile": "band/bands.js", + "navigateBefore": "https://www.band.us" + }, + { + "site": "band", + "name": "login", + "description": "Open band login", + "access": "write", + "domain": "band.us", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "title", - "duration", - "date" + "status", + "logged_in", + "site", + "user_id", + "action", + "verify_command" ], "type": "js", - "modulePath": "apple-podcasts/episodes.js", - "sourceFile": "apple-podcasts/episodes.js" + "modulePath": "band/auth.js", + "sourceFile": "band/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "apple-podcasts", - "name": "search", - "description": "Search Apple Podcasts", + "site": "band", + "name": "mentions", + "description": "Show Band notifications where you are @mentioned", "access": "read", - "strategy": "public", - "browser": false, + "domain": "www.band.us", + "strategy": "intercept", + "browser": true, "args": [ { - "name": "query", + "name": "filter", "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" + "default": "mentioned", + "required": false, + "help": "Filter: mentioned (default) | all | post | comment", + "choices": [ + "mentioned", + "all", + "post", + "comment" + ] }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, "help": "Max results" + }, + { + "name": "unread", + "type": "bool", + "default": false, + "required": false, + "help": "Show only unread notifications" } ], "columns": [ - "id", - "title", - "author", - "episodes", - "genre", + "time", + "band", + "type", + "from", + "text", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "apple-podcasts/search.js", - "sourceFile": "apple-podcasts/search.js" + "modulePath": "band/mentions.js", + "sourceFile": "band/mentions.js", + "navigateBefore": true }, { - "site": "apple-podcasts", - "name": "top", - "description": "Top podcasts chart on Apple Podcasts", + "site": "band", + "name": "post", + "description": "Export full content of a post including comments", "access": "read", - "strategy": "public", - "browser": false, + "domain": "www.band.us", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", + "name": "band_no", "type": "int", - "default": 20, - "required": false, - "help": "Number of podcasts (max 100)" + "required": true, + "positional": true, + "help": "Band number" }, { - "name": "country", + "name": "post_no", + "type": "int", + "required": true, + "positional": true, + "help": "Post number" + }, + { + "name": "output", "type": "str", - "default": "us", + "default": "", + "required": false, + "help": "Directory to save attached photos" + }, + { + "name": "comments", + "type": "bool", + "default": true, "required": false, - "help": "Country code (e.g. us, cn, gb, jp)" + "help": "Include comments (default: true)" } ], "columns": [ - "rank", - "title", + "type", "author", - "id" + "date", + "text" ], "type": "js", - "modulePath": "apple-podcasts/top.js", - "sourceFile": "apple-podcasts/top.js" + "modulePath": "band/post.js", + "sourceFile": "band/post.js", + "navigateBefore": false }, { - "site": "archive", - "name": "item", - "description": "Fetch metadata for a single Internet Archive item by identifier.", + "site": "band", + "name": "posts", + "description": "List posts from a Band", "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, + "domain": "www.band.us", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "identifier", - "type": "str", + "name": "band_no", + "type": "int", "required": true, "positional": true, - "help": "Archive item identifier (e.g. \"open-syllabus\", \"FinalFantasy2_356\")." + "help": "Band number (get it from: band bands)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results" } ], "columns": [ - "identifier", - "title", - "creator", "date", - "mediatype", - "collection", - "description", - "file_count", + "author", + "content", + "comments", "url" ], "type": "js", - "modulePath": "archive/item.js", - "sourceFile": "archive/item.js" + "modulePath": "band/posts.js", + "sourceFile": "band/posts.js", + "navigateBefore": false }, { - "site": "archive", - "name": "search", - "description": "Search Internet Archive items across books, movies, audio, software, and web.", + "site": "band", + "name": "whoami", + "description": "Show the current logged-in band account", "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, + "domain": "band.us", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id" + ], + "type": "js", + "modulePath": "band/auth.js", + "sourceFile": "band/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "barchart", + "name": "flow", + "description": "Barchart unusual options activity / options flow", + "access": "read", + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "type", "type": "str", - "required": true, - "positional": true, - "help": "Full-text query (matches title, description, creator, subject)." - }, - { - "name": "mediatype", - "type": "string", - "required": false, - "help": "Restrict to mediatype: texts, movies, audio, software, image, web, data, collection" - }, - { - "name": "sort", - "type": "string", - "default": "downloads", + "default": "all", "required": false, - "help": "Sort key: downloads, date, addeddate, week, title" + "help": "Filter: all, call, or put", + "choices": [ + "all", + "call", + "put" + ] }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max items (max 100; one API page)." + "help": "Number of results" } ], "columns": [ - "rank", - "identifier", - "title", - "creator", - "date", - "mediatype", - "downloads", - "url" - ], - "tags": [ - "search" + "symbol", + "type", + "strike", + "expiration", + "last", + "volume", + "openInterest", + "volOiRatio", + "iv" ], "type": "js", - "modulePath": "archive/search.js", - "sourceFile": "archive/search.js" + "modulePath": "barchart/flow.js", + "sourceFile": "barchart/flow.js", + "navigateBefore": "https://www.barchart.com" }, { - "site": "archive", - "name": "snapshots", - "description": "List Wayback Machine snapshots over time for a URL via the CDX API.", + "site": "barchart", + "name": "greeks", + "description": "Barchart options greeks overview (IV, delta, gamma, theta, vega)", "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "url", + "name": "symbol", "type": "str", "required": true, "positional": true, - "help": "URL to look up (with or without scheme)." + "help": "Stock ticker (e.g. AAPL)" }, { - "name": "from", - "type": "string", - "required": false, - "help": "Earliest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" - }, - { - "name": "to", - "type": "string", + "name": "expiration", + "type": "str", "required": false, - "help": "Latest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" + "help": "Expiration date (YYYY-MM-DD). Defaults to the nearest available expiration." }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Max snapshots to return (max 1000)." + "help": "Number of near-the-money strikes per type (1-100)" } ], "columns": [ - "timestamp", - "snapshot_url", - "status", - "mimetype", - "original_url" + "type", + "strike", + "last", + "iv", + "delta", + "gamma", + "theta", + "vega", + "rho", + "volume", + "openInterest", + "expiration" ], "type": "js", - "modulePath": "archive/snapshots.js", - "sourceFile": "archive/snapshots.js" + "modulePath": "barchart/greeks.js", + "sourceFile": "barchart/greeks.js", + "navigateBefore": "https://www.barchart.com" }, { - "site": "archive", - "name": "wayback", - "description": "Look up the closest Wayback Machine snapshot for a URL.", + "site": "barchart", + "name": "options", + "description": "Barchart options chain with greeks, IV, volume, and open interest", "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "url", + "name": "symbol", "type": "str", "required": true, "positional": true, - "help": "URL to look up (with or without scheme)." + "help": "Stock ticker (e.g. AAPL)" }, { - "name": "timestamp", - "type": "string", - "required": false, - "help": "Target timestamp (YYYY[MM[DD[hh[mm[ss]]]]] or ISO date). Defaults to most recent snapshot." - } - ], - "columns": [ - "original_url", - "requested_timestamp", - "snapshot_timestamp", - "snapshot_url", - "status" - ], - "type": "js", - "modulePath": "archive/wayback.js", - "sourceFile": "archive/wayback.js" - }, - { - "site": "arxiv", - "name": "author", - "description": "List arXiv papers by a given author (newest first)", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "author", + "name": "type", "type": "str", - "required": true, - "positional": true, - "help": "Author name (e.g. \"Yoshua Bengio\" or \"Y Bengio\")" + "default": "Call", + "required": false, + "help": "Option type: Call or Put", + "choices": [ + "Call", + "Put" + ] }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max papers to return (max 50)" + "help": "Max number of strikes to return" } ], "columns": [ - "id", - "title", - "authors", - "published", - "primary_category", - "url" + "strike", + "bid", + "ask", + "last", + "change", + "volume", + "openInterest", + "iv", + "delta", + "gamma", + "theta", + "vega", + "expiration" ], "type": "js", - "modulePath": "arxiv/author.js", - "sourceFile": "arxiv/author.js" + "modulePath": "barchart/options.js", + "sourceFile": "barchart/options.js", + "navigateBefore": "https://www.barchart.com" }, { - "site": "arxiv", - "name": "paper", - "description": "Get arXiv paper details by ID", + "site": "barchart", + "name": "quote", + "description": "Barchart stock quote with price, volume, and key metrics", "access": "read", - "strategy": "public", - "browser": false, + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "symbol", "type": "str", "required": true, "positional": true, - "help": "arXiv paper ID (e.g. 1706.03762)" + "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" } ], "columns": [ - "id", - "title", - "authors", - "published", - "updated", - "primary_category", - "categories", - "abstract", - "comment", - "pdf", - "url" + "symbol", + "name", + "price", + "change", + "changePct", + "open", + "high", + "low", + "prevClose", + "volume", + "avgVolume", + "marketCap", + "peRatio", + "eps" ], "type": "js", - "modulePath": "arxiv/paper.js", - "sourceFile": "arxiv/paper.js" + "modulePath": "barchart/quote.js", + "sourceFile": "barchart/quote.js", + "navigateBefore": "https://www.barchart.com" }, { - "site": "arxiv", - "name": "recent", - "description": "List recent arXiv submissions in a category", - "access": "read", - "strategy": "public", - "browser": false, + "site": "bigbasket", + "name": "add-to-cart", + "description": "Add a BigBasket product to cart", + "access": "write", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "category", + "name": "product", "type": "str", "required": true, "positional": true, - "help": "arXiv category (e.g. cs.CL, cs.LG, math.PR, q-bio.NC)" + "help": "Product ID or URL" }, { - "name": "limit", + "name": "quantity", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Max results (max 50)" + "help": "Quantity to add (max 20)" } ], "columns": [ - "id", + "ok", + "product_id", + "quantity", + "url", + "message" + ], + "type": "js", + "modulePath": "bigbasket/add-to-cart.js", + "sourceFile": "bigbasket/add-to-cart.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "cart", + "description": "Read BigBasket cart line items", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "product_id", "title", - "authors", - "published", - "primary_category", + "quantity", + "price", + "line_total", + "availability", "url" ], "type": "js", - "modulePath": "arxiv/recent.js", - "sourceFile": "arxiv/recent.js" + "modulePath": "bigbasket/cart.js", + "sourceFile": "bigbasket/cart.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "arxiv", - "name": "search", - "description": "Search arXiv papers", + "site": "bigbasket", + "name": "category", + "description": "Read BigBasket category product cards", "access": "read", - "strategy": "public", - "browser": false, + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "category", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"attention is all you need\")" + "help": "Category URL or slug" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Max results (max 25)" + "help": "Maximum products to return (max 50)" } ], "columns": [ - "id", + "rank", + "product_id", "title", - "authors", - "published", - "primary_category", + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "arxiv/search.js", - "sourceFile": "arxiv/search.js" + "modulePath": "bigbasket/category.js", + "sourceFile": "bigbasket/category.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "band", - "name": "bands", - "description": "List all Bands you belong to", - "access": "read", - "domain": "www.band.us", + "site": "bigbasket", + "name": "checkout", + "description": "Open BigBasket checkout review without placing an order", + "access": "write", + "domain": "www.bigbasket.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "band_no", - "name", - "members" + "ok", + "stage", + "cart_total", + "address_ready", + "delivery_ready", + "payment_ready", + "next_action", + "url" ], "type": "js", - "modulePath": "band/bands.js", - "sourceFile": "band/bands.js", - "navigateBefore": "https://www.band.us" + "modulePath": "bigbasket/checkout.js", + "sourceFile": "bigbasket/checkout.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "band", - "name": "login", - "description": "Open band login", - "access": "write", - "domain": "band.us", + "site": "bigbasket", + "name": "location", + "description": "Show the selected BigBasket delivery location", + "access": "read", + "domain": "www.bigbasket.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "status", - "logged_in", - "site", - "user_id", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "band/auth.js", - "sourceFile": "band/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "selected", + "label", + "area", + "city", + "pincode", + "source" + ], + "type": "js", + "modulePath": "bigbasket/location.js", + "sourceFile": "bigbasket/location.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "band", - "name": "mentions", - "description": "Show Band notifications where you are @mentioned", + "site": "bigbasket", + "name": "product", + "description": "Read BigBasket product details", "access": "read", - "domain": "www.band.us", - "strategy": "intercept", + "domain": "www.bigbasket.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "filter", + "name": "product", "type": "str", - "default": "mentioned", - "required": false, - "help": "Filter: mentioned (default) | all | post | comment", - "choices": [ - "mentioned", - "all", - "post", - "comment" - ] + "required": true, + "positional": true, + "help": "Product ID or URL" + } + ], + "columns": [ + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", + "delivery", + "image_url", + "url" + ], + "type": "js", + "modulePath": "bigbasket/product.js", + "sourceFile": "bigbasket/product.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "search", + "description": "Search BigBasket products", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max results" - }, - { - "name": "unread", - "type": "bool", - "default": false, - "required": false, - "help": "Show only unread notifications" + "help": "Maximum products to return (max 50)" } ], "columns": [ - "time", - "band", - "type", - "from", - "text", + "rank", + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "band/mentions.js", - "sourceFile": "band/mentions.js", - "navigateBefore": true + "modulePath": "bigbasket/search.js", + "sourceFile": "bigbasket/search.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "band", - "name": "post", - "description": "Export full content of a post including comments", - "access": "read", - "domain": "www.band.us", + "site": "blinkit", + "name": "add-to-cart", + "description": "Add a Blinkit product to cart", + "access": "write", + "domain": "blinkit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "band_no", - "type": "int", + "name": "productId", + "type": "str", "required": true, "positional": true, - "help": "Band number" + "help": "Blinkit product id" }, { - "name": "post_no", + "name": "quantity", "type": "int", - "required": true, - "positional": true, - "help": "Post number" + "default": 1, + "required": false, + "help": "Quantity to add (default 1, max 12)" }, { - "name": "output", + "name": "lat", "type": "str", - "default": "", "required": false, - "help": "Directory to save attached photos" + "help": "Delivery latitude (defaults to current Blinkit browser location)" }, { - "name": "comments", - "type": "bool", - "default": true, + "name": "lon", + "type": "str", "required": false, - "help": "Include comments (default: true)" + "help": "Delivery longitude (defaults to current Blinkit browser location)" } ], "columns": [ - "type", - "author", - "date", - "text" + "status", + "productId", + "quantity", + "itemCount", + "itemsTotal", + "payable", + "message" ], "type": "js", - "modulePath": "band/post.js", - "sourceFile": "band/post.js", + "modulePath": "blinkit/add-to-cart.js", + "sourceFile": "blinkit/add-to-cart.js", "navigateBefore": false }, { - "site": "band", - "name": "posts", - "description": "List posts from a Band", + "site": "blinkit", + "name": "cart", + "description": "Show the current Blinkit cart", "access": "read", - "domain": "www.band.us", + "domain": "blinkit.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "band_no", - "type": "int", - "required": true, - "positional": true, - "help": "Band number (get it from: band bands)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results" - } - ], + "args": [], "columns": [ - "date", - "author", - "content", - "comments", - "url" + "status", + "productId", + "name", + "variant", + "price", + "quantity", + "total", + "itemCount", + "payable", + "cartState" ], "type": "js", - "modulePath": "band/posts.js", - "sourceFile": "band/posts.js", + "modulePath": "blinkit/cart.js", + "sourceFile": "blinkit/cart.js", "navigateBefore": false }, { - "site": "band", - "name": "whoami", - "description": "Show the current logged-in band account", + "site": "blinkit", + "name": "checkout", + "description": "Review Blinkit checkout totals and blockers without placing an order", "access": "read", - "domain": "band.us", + "domain": "blinkit.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "user_id" + "status", + "itemCount", + "itemsTotal", + "deliveryCharge", + "handlingCharge", + "payable", + "cartState", + "checkoutBlocked", + "validations" ], "type": "js", - "modulePath": "band/auth.js", - "sourceFile": "band/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "blinkit/checkout.js", + "sourceFile": "blinkit/checkout.js", + "navigateBefore": false }, { - "site": "barchart", - "name": "flow", - "description": "Barchart unusual options activity / options flow", + "site": "blinkit", + "name": "location", + "description": "Show the selected Blinkit delivery location", "access": "read", - "domain": "www.barchart.com", + "domain": "blinkit.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "type", - "type": "str", - "default": "all", - "required": false, - "help": "Filter: all, call, or put", - "choices": [ - "all", - "call", - "put" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], + "args": [], "columns": [ - "symbol", - "type", - "strike", - "expiration", - "last", - "volume", - "openInterest", - "volOiRatio", - "iv" + "selected", + "label", + "area", + "city", + "pincode", + "hasCoordinates", + "source" ], "type": "js", - "modulePath": "barchart/flow.js", - "sourceFile": "barchart/flow.js", - "navigateBefore": "https://www.barchart.com" + "modulePath": "blinkit/location.js", + "sourceFile": "blinkit/location.js", + "navigateBefore": "https://blinkit.com" }, { - "site": "barchart", - "name": "greeks", - "description": "Barchart options greeks overview (IV, delta, gamma, theta, vega)", + "site": "blinkit", + "name": "login", + "description": "Open blinkit login", + "access": "write", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "phone", + "user_id", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "blinkit/auth.js", + "sourceFile": "blinkit/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "blinkit", + "name": "place-order", + "description": "Submit the visible Blinkit final order/payment action. Requires --confirm.", + "access": "write", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "confirm", + "type": "bool", + "default": false, + "required": false, + "help": "Required acknowledgement that this may place/pay for a real order" + } + ], + "columns": [ + "status", + "confirmed", + "itemCount", + "payable", + "orderId", + "url", + "message" + ], + "type": "js", + "modulePath": "blinkit/place-order.js", + "sourceFile": "blinkit/place-order.js", + "navigateBefore": false + }, + { + "site": "blinkit", + "name": "product", + "description": "Read Blinkit product details for a delivery location", "access": "read", - "domain": "www.barchart.com", + "domain": "blinkit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "symbol", + "name": "productId", "type": "str", "required": true, "positional": true, - "help": "Stock ticker (e.g. AAPL)" + "help": "Blinkit product id" }, { - "name": "expiration", + "name": "lat", "type": "str", "required": false, - "help": "Expiration date (YYYY-MM-DD). Defaults to the nearest available expiration." + "help": "Delivery latitude (defaults to current Blinkit browser location)" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "lon", + "type": "str", "required": false, - "help": "Number of near-the-money strikes per type (1-100)" + "help": "Delivery longitude (defaults to current Blinkit browser location)" } ], "columns": [ - "type", - "strike", - "last", - "iv", - "delta", - "gamma", - "theta", - "vega", - "rho", - "volume", - "openInterest", - "expiration" + "productId", + "name", + "brand", + "variant", + "price", + "mrp", + "currency", + "inventory", + "available", + "imageUrl", + "url" ], "type": "js", - "modulePath": "barchart/greeks.js", - "sourceFile": "barchart/greeks.js", - "navigateBefore": "https://www.barchart.com" + "modulePath": "blinkit/product.js", + "sourceFile": "blinkit/product.js", + "navigateBefore": false }, { - "site": "barchart", - "name": "options", - "description": "Barchart options chain with greeks, IV, volume, and open interest", + "site": "blinkit", + "name": "search", + "description": "Search Blinkit products for a delivery location", "access": "read", - "domain": "www.barchart.com", + "domain": "blinkit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "symbol", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Stock ticker (e.g. AAPL)" + "help": "Search keyword" }, { - "name": "type", + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (max 48)" + }, + { + "name": "lat", "type": "str", - "default": "Call", "required": false, - "help": "Option type: Call or Put", - "choices": [ - "Call", - "Put" - ] + "help": "Delivery latitude (defaults to current Blinkit browser location)" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "lon", + "type": "str", "required": false, - "help": "Max number of strikes to return" + "help": "Delivery longitude (defaults to current Blinkit browser location)" } ], "columns": [ - "strike", - "bid", - "ask", - "last", - "change", - "volume", - "openInterest", - "iv", - "delta", - "gamma", - "theta", - "vega", - "expiration" + "rank", + "productId", + "name", + "brand", + "variant", + "price", + "mrp", + "currency", + "inventory", + "available", + "imageUrl", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "barchart/options.js", - "sourceFile": "barchart/options.js", - "navigateBefore": "https://www.barchart.com" + "modulePath": "blinkit/search.js", + "sourceFile": "blinkit/search.js", + "navigateBefore": false }, { - "site": "barchart", - "name": "quote", - "description": "Barchart stock quote with price, volume, and key metrics", + "site": "blinkit", + "name": "whoami", + "description": "Show the current logged-in blinkit account", "access": "read", - "domain": "www.barchart.com", + "domain": "blinkit.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" - } - ], + "args": [], "columns": [ - "symbol", - "name", - "price", - "change", - "changePct", - "open", - "high", - "low", - "prevClose", - "volume", - "avgVolume", - "marketCap", - "peRatio", - "eps" + "logged_in", + "site", + "phone", + "user_id" ], "type": "js", - "modulePath": "barchart/quote.js", - "sourceFile": "barchart/quote.js", - "navigateBefore": "https://www.barchart.com" + "modulePath": "blinkit/auth.js", + "sourceFile": "blinkit/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "bbc", - "name": "news", - "description": "BBC News headlines (RSS)", + "site": "bloomberg", + "name": "businessweek", + "description": "Bloomberg Businessweek top stories", "access": "read", - "domain": "www.bbc.com", + "domain": "www.bloomberg.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of headlines (max 50)" + "help": "Number of stories to return (max 20)" } ], "columns": [ - "rank", "title", - "description", - "url" - ], - "type": "js", - "modulePath": "bbc/news.js", - "sourceFile": "bbc/news.js" + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "bloomberg/businessweek.js", + "sourceFile": "bloomberg/businessweek.js" }, { - "site": "bbc", - "name": "topic", - "description": "BBC News headlines for a specific section (RSS feed)", + "site": "bloomberg", + "name": "crypto", + "description": "Bloomberg Crypto top stories (RSS)", "access": "read", - "domain": "www.bbc.com", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ - { - "name": "topic", - "type": "str", - "required": true, - "positional": true, - "help": "Section name (world / business / politics / health / education / science_and_environment / technology / entertainment_and_arts)" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Max headlines (1-50)" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", "title", - "description", - "pubDate", - "url" + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "bbc/topic.js", - "sourceFile": "bbc/topic.js" + "modulePath": "bloomberg/crypto.js", + "sourceFile": "bloomberg/crypto.js" }, { - "site": "bigbasket", - "name": "add-to-cart", - "description": "Add a BigBasket product to cart", - "access": "write", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, + "site": "bloomberg", + "name": "economics", + "description": "Bloomberg Economics top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "product", - "type": "str", - "required": true, - "positional": true, - "help": "Product ID or URL" - }, - { - "name": "quantity", + "name": "limit", "type": "int", "default": 1, "required": false, - "help": "Quantity to add (max 20)" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "ok", - "product_id", - "quantity", - "url", - "message" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "bigbasket/add-to-cart.js", - "sourceFile": "bigbasket/add-to-cart.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "bloomberg/economics.js", + "sourceFile": "bloomberg/economics.js" }, { - "site": "bigbasket", - "name": "cart", - "description": "Read BigBasket cart line items", + "site": "bloomberg", + "name": "feeds", + "description": "List the Bloomberg RSS feed aliases used by the adapter", "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, "args": [], "columns": [ - "product_id", - "title", - "quantity", - "price", - "line_total", - "availability", + "name", "url" ], "type": "js", - "modulePath": "bigbasket/cart.js", - "sourceFile": "bigbasket/cart.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "bloomberg/feeds.js", + "sourceFile": "bloomberg/feeds.js" }, { - "site": "bigbasket", - "name": "category", - "description": "Read BigBasket category product cards", + "site": "bloomberg", + "name": "green", + "description": "Bloomberg Green (climate & energy) top stories (RSS)", "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, "args": [ - { - "name": "category", - "type": "str", - "required": true, - "positional": true, - "help": "Category URL or slug" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Maximum products to return (max 50)" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "product_id", "title", - "brand", - "pack_size", - "price", - "mrp", - "discount", - "availability", - "url" - ], - "type": "js", - "modulePath": "bigbasket/category.js", - "sourceFile": "bigbasket/category.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "checkout", - "description": "Open BigBasket checkout review without placing an order", - "access": "write", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "ok", - "stage", - "cart_total", - "address_ready", - "delivery_ready", - "payment_ready", - "next_action", - "url" + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "bigbasket/checkout.js", - "sourceFile": "bigbasket/checkout.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "bloomberg/green.js", + "sourceFile": "bloomberg/green.js" }, { - "site": "bigbasket", - "name": "location", - "description": "Show the selected BigBasket delivery location", + "site": "bloomberg", + "name": "industries", + "description": "Bloomberg Industries top stories (RSS)", "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [], + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], "columns": [ - "selected", - "label", - "area", - "city", - "pincode", - "source" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "bigbasket/location.js", - "sourceFile": "bigbasket/location.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "bloomberg/industries.js", + "sourceFile": "bloomberg/industries.js" }, { - "site": "bigbasket", - "name": "product", - "description": "Read BigBasket product details", + "site": "bloomberg", + "name": "main", + "description": "Bloomberg homepage top stories (RSS)", "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "product", - "type": "str", - "required": true, - "positional": true, - "help": "Product ID or URL" + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "product_id", "title", - "brand", - "pack_size", - "price", - "mrp", - "discount", - "availability", - "delivery", - "image_url", - "url" + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "bigbasket/product.js", - "sourceFile": "bigbasket/product.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "bloomberg/main.js", + "sourceFile": "bloomberg/main.js" }, { - "site": "bigbasket", - "name": "search", - "description": "Search BigBasket products", + "site": "bloomberg", + "name": "markets", + "description": "Bloomberg Markets top stories (RSS)", "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Maximum products to return (max 50)" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "product_id", "title", - "brand", - "pack_size", - "price", - "mrp", - "discount", - "availability", - "url" - ], - "tags": [ - "search" + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "bigbasket/search.js", - "sourceFile": "bigbasket/search.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "bloomberg/markets.js", + "sourceFile": "bloomberg/markets.js" }, { - "site": "binance", - "name": "asks", - "description": "Order book ask prices for a trading pair", + "site": "bloomberg", + "name": "news", + "description": "Read a Bloomberg story/article page and return title, full content, and media links", "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, + "domain": "www.bloomberg.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "symbol", + "name": "link", "type": "str", "required": true, "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of price levels (5, 10, 20, 50, 100)" + "help": "Bloomberg story/article URL or relative Bloomberg path" } ], "columns": [ - "rank", - "ask_price", - "ask_qty" + "title", + "summary", + "link", + "mediaLinks", + "content" ], "type": "js", - "modulePath": "binance/asks.js", - "sourceFile": "binance/asks.js" + "modulePath": "bloomberg/news.js", + "sourceFile": "bloomberg/news.js", + "navigateBefore": "https://www.bloomberg.com" }, { - "site": "binance", - "name": "depth", - "description": "Order book bid and ask prices for a trading pair", + "site": "bloomberg", + "name": "opinions", + "description": "Bloomberg Opinion top stories (RSS)", "access": "read", - "domain": "data-api.binance.vision", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Number of price levels (5, 10, 20, 50, 100)" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "bid_price", - "bid_qty", - "ask_price", - "ask_qty" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "binance/depth.js", - "sourceFile": "binance/depth.js" + "modulePath": "bloomberg/opinions.js", + "sourceFile": "bloomberg/opinions.js" }, { - "site": "binance", - "name": "gainers", - "description": "Top gaining trading pairs by 24h price change", + "site": "bloomberg", + "name": "politics", + "description": "Bloomberg Politics top stories (RSS)", "access": "read", - "domain": "data-api.binance.vision", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Number of trading pairs" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "symbol", - "price", - "change_24h", - "volume" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "binance/gainers.js", - "sourceFile": "binance/gainers.js" + "modulePath": "bloomberg/politics.js", + "sourceFile": "bloomberg/politics.js" }, { - "site": "binance", - "name": "klines", - "description": "Candlestick/kline data for a trading pair", + "site": "bloomberg", + "name": "pursuits", + "description": "Bloomberg Pursuits (lifestyle) top stories (RSS)", "access": "read", - "domain": "data-api.binance.vision", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" - }, - { - "name": "interval", - "type": "str", - "default": "1d", - "required": false, - "help": "Kline interval (1m, 5m, 15m, 1h, 4h, 1d, 1w, 1M)" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Number of klines (max 1000)" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "open", - "high", - "low", - "close", - "volume" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "binance/klines.js", - "sourceFile": "binance/klines.js" + "modulePath": "bloomberg/pursuits.js", + "sourceFile": "bloomberg/pursuits.js" }, { - "site": "binance", - "name": "losers", - "description": "Top losing trading pairs by 24h price change", + "site": "bloomberg", + "name": "tech", + "description": "Bloomberg Tech top stories (RSS)", "access": "read", - "domain": "data-api.binance.vision", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Number of trading pairs" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "symbol", - "price", - "change_24h", - "volume" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "binance/losers.js", - "sourceFile": "binance/losers.js" + "modulePath": "bloomberg/tech.js", + "sourceFile": "bloomberg/tech.js" }, { - "site": "binance", - "name": "pairs", - "description": "List active trading pairs on Binance", + "site": "booking", + "name": "search", + "description": "Search Booking.com hotels by destination and dates (server-rendered card scrape).", "access": "read", - "domain": "data-api.binance.vision", + "example": "webcmd booking search Tokyo --checkin 2026-06-15 --checkout 2026-06-17 -f yaml", + "domain": "www.booking.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ + { + "name": "destination", + "type": "str", + "required": true, + "positional": true, + "help": "Destination keyword (city, district, or hotel name)" + }, + { + "name": "checkin", + "type": "str", + "required": true, + "help": "Check-in date YYYY-MM-DD" + }, + { + "name": "checkout", + "type": "str", + "required": true, + "help": "Check-out date YYYY-MM-DD" + }, + { + "name": "adults", + "type": "int", + "default": 2, + "required": false, + "help": "Number of adults (1-30)" + }, + { + "name": "rooms", + "type": "int", + "default": 1, + "required": false, + "help": "Number of rooms (1-30)" + }, + { + "name": "children", + "type": "int", + "default": 0, + "required": false, + "help": "Number of children (0-10)" + }, + { + "name": "currency", + "type": "str", + "required": false, + "help": "Force result currency (e.g. USD, JPY, CNY)" + }, + { + "name": "lang", + "type": "str", + "required": false, + "help": "Force result language (e.g. en-us, zh-cn, ja)" + }, { "name": "limit", "type": "int", - "default": 20, + "default": 25, + "required": false, + "help": "Max rows to return (1-100; Booking pages 25 per request)" + }, + { + "name": "offset", + "type": "int", + "default": 0, "required": false, - "help": "Number of trading pairs" + "help": "Result offset for pagination (multiple of 25)" } ], "columns": [ - "symbol", - "base", - "quote", - "status" + "rank", + "name", + "country", + "slug", + "star_rating", + "review_score", + "review_count", + "price_amount", + "price_currency", + "distance", + "recommended_room", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "binance/pairs.js", - "sourceFile": "binance/pairs.js" + "modulePath": "booking/search.js", + "sourceFile": "booking/search.js" }, { - "site": "binance", - "name": "price", - "description": "Quick price check for a trading pair", + "site": "brave", + "name": "search", + "description": "Search Brave Search", "access": "read", - "domain": "data-api.binance.vision", + "domain": "search.brave.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "symbol", + "name": "keyword", "type": "str", "required": true, "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" - } - ], - "columns": [ - "symbol", - "price", - "change", - "change_pct", - "high", - "low", - "volume", - "quote_volume", - "trades" - ], - "type": "js", - "modulePath": "binance/price.js", - "sourceFile": "binance/price.js" - }, - { - "site": "binance", - "name": "prices", - "description": "Latest prices for all trading pairs", - "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, - "args": [ + "help": "Search query" + }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, + "required": false, + "help": "Number of results per page (max 18)" + }, + { + "name": "offset", + "type": "int", + "default": 0, "required": false, - "help": "Number of prices" + "help": "Page offset (0, 1, 2...). Brave returns ~18 results per page" } ], "columns": [ "rank", - "symbol", - "price" + "title", + "url", + "snippet" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "binance/prices.js", - "sourceFile": "binance/prices.js" + "modulePath": "brave/search.js", + "sourceFile": "brave/search.js" }, { - "site": "binance", - "name": "ticker", - "description": "24h ticker statistics for top trading pairs by volume", - "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, + "site": "chatgpt", + "name": "ask", + "description": "Send a prompt to ChatGPT web and wait for the response", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", "type": "int", - "default": 20, + "default": 120, "required": false, - "help": "Number of tickers" - } - ], - "columns": [ - "symbol", - "price", - "change_pct", - "high", - "low", - "volume", - "quote_vol", - "trades" - ], - "type": "js", - "modulePath": "binance/ticker.js", - "sourceFile": "binance/ticker.js" - }, - { - "site": "binance", - "name": "top", - "description": "Top trading pairs by 24h volume on Binance", - "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, - "args": [ + "help": "Max seconds to wait for response" + }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Continue an existing ChatGPT conversation ID or /c/ URL" + }, + { + "name": "project", + "type": "str", "required": false, - "help": "Number of trading pairs" + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + }, + { + "name": "wait", + "type": "boolean", + "default": true, + "required": false, + "help": "Wait for the assistant response after sending" + }, + { + "name": "deep-research", + "type": "boolean", + "default": false, + "required": false, + "help": "Enable ChatGPT Deep Research (Deep Research)" + }, + { + "name": "web-search", + "type": "boolean", + "default": false, + "required": false, + "help": "Enable ChatGPT Web Search (Web Search)" } ], "columns": [ - "rank", - "symbol", - "price", - "change_24h", - "high", - "low", - "volume" + "conversationId", + "conversationUrl", + "tool", + "response" ], "type": "js", - "modulePath": "binance/top.js", - "sourceFile": "binance/top.js" + "modulePath": "chatgpt/ask.js", + "sourceFile": "chatgpt/ask.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "binance", - "name": "trades", - "description": "Recent trades for a trading pair", + "site": "chatgpt", + "name": "deep-research-result", + "description": "Read a ChatGPT Deep Research report or progress from the conversation payload", "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "symbol", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + "help": "Conversation ID or full /c/ URL" }, { - "name": "limit", + "name": "wait", + "type": "boolean", + "default": false, + "required": false, + "help": "Wait until Deep Research completes or becomes extractable" + }, + { + "name": "timeout", "type": "int", - "default": 20, + "default": 120, + "required": false, + "help": "Max seconds to wait when --wait is true" + }, + { + "name": "stable", + "type": "int", + "default": 6, "required": false, - "help": "Number of trades (max 1000)" + "help": "Seconds the report text must remain unchanged when --wait is true" } ], "columns": [ - "id", - "price", - "qty", - "quote_qty", - "buyer_maker" + "conversationId", + "status", + "report", + "sources", + "progress", + "asyncTaskConversationId", + "widgetSessionId", + "asyncStatus", + "venusMessageType", + "venusStatus", + "waitingForUserUntil", + "planTitle", + "planId", + "url", + "method", + "diagnostics" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "binance/trades.js", - "sourceFile": "binance/trades.js" + "modulePath": "chatgpt/deep-research-result.js", + "sourceFile": "chatgpt/deep-research-result.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "blinkit", - "name": "add-to-cart", - "description": "Add a Blinkit product to cart", - "access": "write", - "domain": "blinkit.com", + "site": "chatgpt", + "name": "detail", + "description": "Open a ChatGPT web conversation by ID and read its messages", + "access": "read", + "domain": "chatgpt.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "productId", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Blinkit product id" + "help": "Conversation ID or full /c/ URL" }, { - "name": "quantity", - "type": "int", - "default": 1, + "name": "markdown", + "type": "boolean", + "default": false, "required": false, - "help": "Quantity to add (default 1, max 12)" + "help": "Emit assistant replies as markdown" }, { - "name": "lat", - "type": "str", + "name": "wait", + "type": "boolean", + "default": false, "required": false, - "help": "Delivery latitude (defaults to current Blinkit browser location)" + "help": "Wait until the conversation stops generating and stabilizes" }, { - "name": "lon", - "type": "str", + "name": "timeout", + "type": "int", + "default": 120, "required": false, - "help": "Delivery longitude (defaults to current Blinkit browser location)" + "help": "Max seconds to wait when --wait is true" + }, + { + "name": "stable", + "type": "int", + "default": 6, + "required": false, + "help": "Seconds the final messages must remain unchanged when --wait is true" } ], "columns": [ - "status", - "productId", - "quantity", - "itemCount", - "itemsTotal", - "payable", - "message" + "Index", + "Role", + "Text", + "Generating", + "StableSeconds" ], "type": "js", - "modulePath": "blinkit/add-to-cart.js", - "sourceFile": "blinkit/add-to-cart.js", - "navigateBefore": false + "modulePath": "chatgpt/detail.js", + "sourceFile": "chatgpt/detail.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "blinkit", - "name": "cart", - "description": "Show the current Blinkit cart", + "site": "chatgpt", + "name": "history", + "description": "List visible ChatGPT web conversation history from the sidebar", "access": "read", - "domain": "blinkit.com", + "domain": "chatgpt.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max conversations to show" + } + ], "columns": [ - "status", - "productId", - "name", - "variant", - "price", - "quantity", - "total", - "itemCount", - "payable", - "cartState" + "Index", + "Id", + "Title", + "Url" ], "type": "js", - "modulePath": "blinkit/cart.js", - "sourceFile": "blinkit/cart.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "checkout", - "description": "Review Blinkit checkout totals and blockers without placing an order", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "itemCount", - "itemsTotal", - "deliveryCharge", - "handlingCharge", - "payable", - "cartState", - "checkoutBlocked", - "validations" - ], - "type": "js", - "modulePath": "blinkit/checkout.js", - "sourceFile": "blinkit/checkout.js", - "navigateBefore": false + "modulePath": "chatgpt/history.js", + "sourceFile": "chatgpt/history.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "blinkit", - "name": "location", - "description": "Show the selected Blinkit delivery location", - "access": "read", - "domain": "blinkit.com", + "site": "chatgpt", + "name": "image", + "description": "Generate images with ChatGPT web and save them locally", + "access": "write", + "domain": "chatgpt.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Image prompt to send to ChatGPT" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Local image path to attach before prompting; comma-separated paths are supported", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start image generation inside a ChatGPT project ID or /g/g-p- URL" + }, + { + "name": "op", + "type": "str", + "required": false, + "help": "Output directory (default: ~/Pictures/chatgpt)", + "file": { + "direction": "output", + "pathKind": "directory", + "multiple": false, + "defaultPath": "~/Pictures/chatgpt" + } + }, + { + "name": "sd", + "type": "boolean", + "default": false, + "required": false, + "help": "Skip download shorthand; only show ChatGPT link" + }, + { + "name": "timeout", + "type": "int", + "default": 240, + "required": false, + "help": "Max seconds for the overall command (default: 240)" + } + ], "columns": [ - "selected", - "label", - "area", - "city", - "pincode", - "hasCoordinates", - "source" + "status", + "file", + "link" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "blinkit/location.js", - "sourceFile": "blinkit/location.js", - "navigateBefore": "https://blinkit.com" + "modulePath": "chatgpt/image.js", + "sourceFile": "chatgpt/image.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "blinkit", + "site": "chatgpt", "name": "login", - "description": "Open blinkit login", + "description": "Open chatgpt login", "access": "write", - "domain": "blinkit.com", + "domain": "chatgpt.com", "strategy": "cookie", "browser": true, "args": [], @@ -3009,4437 +3166,2325 @@ "status", "logged_in", "site", - "phone", "user_id", + "name", "action", "verify_command" ], "type": "js", - "modulePath": "blinkit/auth.js", - "sourceFile": "blinkit/auth.js", + "modulePath": "chatgpt/auth.js", + "sourceFile": "chatgpt/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "blinkit", - "name": "place-order", - "description": "Submit the visible Blinkit final order/payment action. Requires --confirm.", + "site": "chatgpt", + "name": "model", + "description": "Switch ChatGPT web model or intelligence level (GPT-5.6 Pro, fast, balanced, advanced, very-high, pro)", "access": "write", - "domain": "blinkit.com", + "domain": "chatgpt.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "confirm", - "type": "bool", - "default": false, + "name": "model", + "type": "str", + "required": true, + "positional": true, + "help": "ChatGPT model or intelligence level to switch to", + "choices": [ + "fast", + "speed", + "instant", + "balanced", + "balance", + "medium", + "advanced", + "high", + "thinking", + "very-high", + "ultra", + "xhigh", + "x-high", + "extra-high", + "very high", + "gpt-5.6-pro", + "gpt-5-6-pro", + "gpt-5.6-sol-pro", + "gpt-5-6-sol-pro", + "gpt-5.6", + "gpt-5-6", + "5.6-pro", + "5.6", + "pro", + "professional" + ] + }, + { + "name": "project", + "type": "str", "required": false, - "help": "Required acknowledgement that this may place/pay for a real order" + "valueRequired": true, + "help": "Open a ChatGPT project ID or /g/g-p- URL before switching intelligence level" } ], "columns": [ - "status", - "confirmed", - "itemCount", - "payable", - "orderId", - "url", - "message" + "Status", + "Model" ], "type": "js", - "modulePath": "blinkit/place-order.js", - "sourceFile": "blinkit/place-order.js", - "navigateBefore": false + "modulePath": "chatgpt/model.js", + "sourceFile": "chatgpt/model.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "blinkit", - "name": "product", - "description": "Read Blinkit product details for a delivery location", + "site": "chatgpt", + "name": "new", + "description": "Start a new ChatGPT web conversation", "access": "read", - "domain": "blinkit.com", + "domain": "chatgpt.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "productId", - "type": "str", - "required": true, - "positional": true, - "help": "Blinkit product id" - }, - { - "name": "lat", - "type": "str", - "required": false, - "help": "Delivery latitude (defaults to current Blinkit browser location)" - }, - { - "name": "lon", + "name": "project", "type": "str", "required": false, - "help": "Delivery longitude (defaults to current Blinkit browser location)" + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" } ], "columns": [ - "productId", - "name", - "brand", - "variant", - "price", - "mrp", - "currency", - "inventory", - "available", - "imageUrl", - "url" + "Status" ], "type": "js", - "modulePath": "blinkit/product.js", - "sourceFile": "blinkit/product.js", - "navigateBefore": false + "modulePath": "chatgpt/new.js", + "sourceFile": "chatgpt/new.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "blinkit", - "name": "search", - "description": "Search Blinkit products for a delivery location", - "access": "read", - "domain": "blinkit.com", + "site": "chatgpt", + "name": "project-file-add", + "description": "Upload files to a ChatGPT project as project knowledge (not just conversation attachments)", + "access": "write", + "domain": "chatgpt.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", + "name": "file", "type": "str", "required": true, "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (max 48)" - }, - { - "name": "lat", - "type": "str", - "required": false, - "help": "Delivery latitude (defaults to current Blinkit browser location)" + "help": "Local file path(s) to upload; comma-separated paths are supported", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "application/pdf", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } }, { - "name": "lon", + "name": "id", "type": "str", - "required": false, - "help": "Delivery longitude (defaults to current Blinkit browser location)" + "required": true, + "help": "Project ID or /g/g-p- URL" } ], "columns": [ - "rank", - "productId", - "name", - "brand", - "variant", - "price", - "mrp", - "currency", - "inventory", - "available", - "imageUrl", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "blinkit/search.js", - "sourceFile": "blinkit/search.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "whoami", - "description": "Show the current logged-in blinkit account", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "phone", - "user_id" + "Status", + "File" ], "type": "js", - "modulePath": "blinkit/auth.js", - "sourceFile": "blinkit/auth.js", + "modulePath": "chatgpt/project-file-add.js", + "sourceFile": "chatgpt/project-file-add.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "bloomberg", - "name": "businessweek", - "description": "Bloomberg Businessweek top stories", + "site": "chatgpt", + "name": "project-list", + "description": "List visible ChatGPT projects from the sidebar", "access": "read", - "domain": "www.bloomberg.com", - "strategy": "public", + "domain": "chatgpt.com", + "strategy": "cookie", "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Number of stories to return (max 20)" + "help": "Max projects to show" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "Index", + "Id", + "Title", + "Url" ], "type": "js", - "modulePath": "bloomberg/businessweek.js", - "sourceFile": "bloomberg/businessweek.js" + "modulePath": "chatgpt/project-list.js", + "sourceFile": "chatgpt/project-list.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "bloomberg", - "name": "crypto", - "description": "Bloomberg Crypto top stories (RSS)", + "site": "chatgpt", + "name": "read", + "description": "Read messages in the current ChatGPT web conversation", "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 1, + "name": "markdown", + "type": "boolean", + "default": false, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Emit assistant replies as markdown" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "Index", + "Role", + "Text" ], "type": "js", - "modulePath": "bloomberg/crypto.js", - "sourceFile": "bloomberg/crypto.js" + "modulePath": "chatgpt/read.js", + "sourceFile": "chatgpt/read.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "bloomberg", - "name": "economics", - "description": "Bloomberg Economics top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, + "site": "chatgpt", + "name": "send", + "description": "Send a prompt to ChatGPT web without waiting for the response", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 1, + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "new", + "type": "boolean", + "default": false, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Start a new chat before sending" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Continue an existing ChatGPT conversation ID or /c/ URL" + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "Status", + "InjectedText" ], "type": "js", - "modulePath": "bloomberg/economics.js", - "sourceFile": "bloomberg/economics.js" + "modulePath": "chatgpt/send.js", + "sourceFile": "chatgpt/send.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "bloomberg", - "name": "feeds", - "description": "List the Bloomberg RSS feed aliases used by the adapter", + "site": "chatgpt", + "name": "status", + "description": "Check ChatGPT web page availability and login state", "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, "args": [], "columns": [ - "name", - "url" + "Status", + "Login", + "Url" ], "type": "js", - "modulePath": "bloomberg/feeds.js", - "sourceFile": "bloomberg/feeds.js" + "modulePath": "chatgpt/status.js", + "sourceFile": "chatgpt/status.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "bloomberg", - "name": "green", - "description": "Bloomberg Green (climate & energy) top stories (RSS)", + "site": "chatgpt", + "name": "whoami", + "description": "Show the current logged-in chatgpt account", "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "logged_in", + "site", + "user_id", + "name" ], "type": "js", - "modulePath": "bloomberg/green.js", - "sourceFile": "bloomberg/green.js" + "modulePath": "chatgpt/auth.js", + "sourceFile": "chatgpt/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "bloomberg", - "name": "industries", - "description": "Bloomberg Industries top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", + "site": "chatgpt-app", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "public", "browser": false, "args": [ { - "name": "limit", + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "model", + "type": "str", + "required": false, + "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] + }, + { + "name": "timeout", "type": "int", - "default": 1, + "default": 30, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Max seconds to wait for response (default: 30)" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Path to local image to attach (optional)" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "Role", + "Text" ], "type": "js", - "modulePath": "bloomberg/industries.js", - "sourceFile": "bloomberg/industries.js" + "modulePath": "chatgpt-app/ask.js", + "sourceFile": "chatgpt-app/ask.js" }, { - "site": "bloomberg", - "name": "main", - "description": "Bloomberg homepage top stories (RSS)", + "site": "chatgpt-app", + "name": "model", + "description": "Switch ChatGPT Desktop model/mode (auto, instant, thinking, 5.2-instant, 5.2-thinking)", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "localhost", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" + "name": "model", + "type": "str", + "required": true, + "positional": true, + "help": "Model to switch to", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "Status", + "Model" ], "type": "js", - "modulePath": "bloomberg/main.js", - "sourceFile": "bloomberg/main.js" + "modulePath": "chatgpt-app/model.js", + "sourceFile": "chatgpt-app/model.js" }, { - "site": "bloomberg", - "name": "markets", - "description": "Bloomberg Markets top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", + "site": "chatgpt-app", + "name": "new", + "description": "Open a new chat in ChatGPT Desktop App", + "access": "write", + "domain": "localhost", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 1, + "name": "temp", + "type": "boolean", + "default": false, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Open a temporary chat with privacy protection" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "Status" ], "type": "js", - "modulePath": "bloomberg/markets.js", - "sourceFile": "bloomberg/markets.js" + "modulePath": "chatgpt-app/new.js", + "sourceFile": "chatgpt-app/new.js" }, { - "site": "bloomberg", - "name": "news", - "description": "Read a Bloomberg story/article page and return title, full content, and media links", + "site": "chatgpt-app", + "name": "read", + "description": "Read the last visible message from the focused ChatGPT Desktop window", "access": "read", - "domain": "www.bloomberg.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "link", - "type": "str", - "required": true, - "positional": true, - "help": "Bloomberg story/article URL or relative Bloomberg path" - } - ], + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [], "columns": [ - "title", - "summary", - "link", - "mediaLinks", - "content" + "Role", + "Text" ], "type": "js", - "modulePath": "bloomberg/news.js", - "sourceFile": "bloomberg/news.js", - "navigateBefore": "https://www.bloomberg.com" + "modulePath": "chatgpt-app/read.js", + "sourceFile": "chatgpt-app/read.js" }, { - "site": "bloomberg", - "name": "opinions", - "description": "Bloomberg Opinion top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", + "site": "chatgpt-app", + "name": "send", + "description": "Send a message to the active ChatGPT Desktop App window", + "access": "write", + "domain": "localhost", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 1, + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Message to send" + }, + { + "name": "model", + "type": "str", "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "Status" ], "type": "js", - "modulePath": "bloomberg/opinions.js", - "sourceFile": "bloomberg/opinions.js" + "modulePath": "chatgpt-app/send.js", + "sourceFile": "chatgpt-app/send.js" }, { - "site": "bloomberg", - "name": "politics", - "description": "Bloomberg Politics top stories (RSS)", + "site": "chatgpt-app", + "name": "status", + "description": "Check if ChatGPT Desktop App is running natively on macOS", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "localhost", "strategy": "public", "browser": false, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "chatgpt-app/status.js", + "sourceFile": "chatgpt-app/status.js" + }, + { + "site": "chatwise", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", "type": "int", - "default": 1, + "default": 30, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Max seconds to wait (default: 30)" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "Role", + "Text" ], "type": "js", - "modulePath": "bloomberg/politics.js", - "sourceFile": "bloomberg/politics.js" + "modulePath": "chatwise/ask.js", + "sourceFile": "chatwise/ask.js", + "navigateBefore": true }, { - "site": "bloomberg", - "name": "pursuits", - "description": "Bloomberg Pursuits (lifestyle) top stories (RSS)", + "site": "chatwise", + "name": "export", + "description": "Export the current ChatWise conversation to a Markdown file", "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 1, + "name": "output", + "type": "str", "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Output file (default: /tmp/chatwise-export.md)" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], + "Status", + "File", + "Messages" + ], "type": "js", - "modulePath": "bloomberg/pursuits.js", - "sourceFile": "bloomberg/pursuits.js" + "modulePath": "chatwise/export.js", + "sourceFile": "chatwise/export.js", + "navigateBefore": true }, { - "site": "bloomberg", - "name": "tech", - "description": "Bloomberg Tech top stories (RSS)", + "site": "chatwise", + "name": "history", + "description": "List conversation history in ChatWise sidebar", "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "Index", + "Title" ], "type": "js", - "modulePath": "bloomberg/tech.js", - "sourceFile": "bloomberg/tech.js" + "modulePath": "chatwise/history.js", + "sourceFile": "chatwise/history.js", + "navigateBefore": true }, { - "site": "bluesky", - "name": "feeds", - "description": "Popular Bluesky feed generators", + "site": "chatwise", + "name": "model", + "description": "Get or switch the active AI model in ChatWise", "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "model-name", + "type": "str", "required": false, - "help": "Number of feeds" + "positional": true, + "help": "Model to switch to (e.g. gpt-4, claude-3)" } ], "columns": [ - "rank", - "name", - "likes", - "creator", - "description" + "Status", + "Model" ], "type": "js", - "modulePath": "bluesky/feeds.js", - "sourceFile": "bluesky/feeds.js" + "modulePath": "chatwise/model.js", + "sourceFile": "chatwise/model.js", + "navigateBefore": true }, { - "site": "bluesky", - "name": "followers", - "description": "List followers of a Bluesky user", + "site": "chatwise", + "name": "new", + "description": "Start a new ChatWise conversation session", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "chatwise/new.js", + "sourceFile": "chatwise/new.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "read", + "description": "Read the current ChatWise conversation history", "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Content" + ], + "type": "js", + "modulePath": "chatwise/read.js", + "sourceFile": "chatwise/read.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "screenshot", + "description": "Capture a snapshot of the current ChatWise window (DOM + Accessibility tree)", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "handle", + "name": "output", "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle" - }, - { - "name": "limit", - "type": "int", - "default": 20, "required": false, - "help": "Number of followers" + "help": "Output file path (default: /tmp/chatwise-snapshot.txt)" } ], "columns": [ - "rank", - "handle", - "name", - "description" + "Status", + "File" ], "type": "js", - "modulePath": "bluesky/followers.js", - "sourceFile": "bluesky/followers.js" + "modulePath": "chatwise/screenshot.js", + "sourceFile": "chatwise/screenshot.js", + "navigateBefore": true }, { - "site": "bluesky", - "name": "following", - "description": "List accounts a Bluesky user is following", - "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, + "site": "chatwise", + "name": "send", + "description": "Send a message to the active ChatWise conversation", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "handle", + "name": "text", "type": "str", "required": true, "positional": true, - "help": "Bluesky handle" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of accounts" + "help": "Message to send" } ], "columns": [ - "rank", - "handle", - "name", - "description" + "Status", + "InjectedText" ], "type": "js", - "modulePath": "bluesky/following.js", - "sourceFile": "bluesky/following.js" + "modulePath": "chatwise/send.js", + "sourceFile": "chatwise/send.js", + "navigateBefore": true }, { - "site": "bluesky", - "name": "profile", - "description": "Get Bluesky user profile info", + "site": "chatwise", + "name": "status", + "description": "Check active CDP connection to ChatWise Desktop", "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "chatwise/status.js", + "sourceFile": "chatwise/status.js", + "navigateBefore": true + }, + { + "site": "chess", + "name": "analyze", + "description": "Open a Chess.com game in the browser analysis board", + "access": "read", + "domain": "www.chess.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "handle", - "type": "str", + "name": "game-url", + "type": "string", "required": true, "positional": true, - "help": "Bluesky handle (e.g. bsky.app, jay.bsky.team)" + "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" } ], "columns": [ - "handle", - "name", - "followers", - "following", - "posts", - "description" + "kind", + "game_id", + "analysis_url" ], "type": "js", - "modulePath": "bluesky/profile.js", - "sourceFile": "bluesky/profile.js" + "modulePath": "chess/analyze.js", + "sourceFile": "chess/analyze.js", + "navigateBefore": false }, { - "site": "bluesky", - "name": "search", - "description": "Search Bluesky users", + "site": "chess", + "name": "game", + "description": "Chess.com single-game detail (white, black, result, ECO, time control) by full game URL", "access": "read", - "domain": "public.api.bsky.app", + "domain": "www.chess.com", "strategy": "public", "browser": false, "args": [ { - "name": "query", - "type": "str", + "name": "game-url", + "type": "string", "required": true, "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" + "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" } ], "columns": [ - "rank", - "handle", - "name", - "followers", - "description" - ], - "tags": [ - "search" + "kind", + "game_id", + "date", + "white", + "white_rating", + "black", + "black_rating", + "result", + "winner_color", + "termination", + "eco", + "time_control", + "rated", + "ply_count", + "url" ], "type": "js", - "modulePath": "bluesky/search.js", - "sourceFile": "bluesky/search.js" + "modulePath": "chess/game.js", + "sourceFile": "chess/game.js" }, { - "site": "bluesky", - "name": "starter-packs", - "description": "Get starter packs created by a Bluesky user", + "site": "chess", + "name": "games", + "description": "Chess.com recent games for a player, newest first", "access": "read", - "domain": "public.api.bsky.app", + "domain": "api.chess.com", "strategy": "public", "browser": false, "args": [ { - "name": "handle", - "type": "str", + "name": "username", + "type": "string", "required": true, "positional": true, - "help": "Bluesky handle" + "help": "Chess.com username" }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Number of starter packs" + "help": "Number of recent games (1-100)" } ], "columns": [ - "rank", - "name", - "description", - "members", - "joins" - ], - "type": "js", - "modulePath": "bluesky/starter-packs.js", - "sourceFile": "bluesky/starter-packs.js" - }, - { - "site": "bluesky", - "name": "thread", - "description": "Get a Bluesky post thread with replies", - "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "uri", - "type": "str", - "required": true, - "positional": true, - "help": "Post AT URI (at://did:.../app.bsky.feed.post/...) or bsky.app URL" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of replies" - } - ], - "columns": [ - "author", - "text", - "likes", - "reposts", - "replies_count" - ], - "type": "js", - "modulePath": "bluesky/thread.js", - "sourceFile": "bluesky/thread.js" - }, - { - "site": "bluesky", - "name": "trending", - "description": "Trending topics on Bluesky", - "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of topics" - } - ], - "columns": [ - "rank", - "topic", - "link" + "date", + "time_class", + "rated", + "my_color", + "my_rating", + "my_result", + "opponent", + "opponent_rating", + "accuracy_white", + "accuracy_black", + "eco", + "opening_name", + "url" ], "type": "js", - "modulePath": "bluesky/trending.js", - "sourceFile": "bluesky/trending.js" + "modulePath": "chess/games.js", + "sourceFile": "chess/games.js" }, { - "site": "bluesky", - "name": "user", - "description": "Get recent posts from a Bluesky user", + "site": "chess", + "name": "stats", + "description": "Chess.com player ratings + win/loss record across game kinds", "access": "read", - "domain": "public.api.bsky.app", + "domain": "api.chess.com", "strategy": "public", "browser": false, "args": [ { - "name": "handle", - "type": "str", + "name": "username", + "type": "string", "required": true, "positional": true, - "help": "Bluesky handle (e.g. bsky.app)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts" + "help": "Chess.com username (case-insensitive)" } ], "columns": [ - "rank", - "uri", - "text", - "likes", - "reposts", - "replies" + "kind", + "rating_current", + "rating_best", + "wins", + "losses", + "draws" ], "type": "js", - "modulePath": "bluesky/user.js", - "sourceFile": "bluesky/user.js" + "modulePath": "chess/stats.js", + "sourceFile": "chess/stats.js" }, { - "site": "booking", - "name": "search", - "description": "Search Booking.com hotels by destination and dates (server-rendered card scrape).", - "access": "read", - "example": "webcmd booking search Tokyo --checkin 2026-06-15 --checkout 2026-06-17 -f yaml", - "domain": "www.booking.com", - "strategy": "public", + "site": "claude", + "name": "ask", + "description": "Send a prompt to Claude and get the response", + "access": "write", + "domain": "claude.ai", + "strategy": "cookie", "browser": true, "args": [ { - "name": "destination", + "name": "prompt", "type": "str", "required": true, "positional": true, - "help": "Destination keyword (city, district, or hotel name)" - }, - { - "name": "checkin", - "type": "str", - "required": true, - "help": "Check-in date YYYY-MM-DD" - }, - { - "name": "checkout", - "type": "str", - "required": true, - "help": "Check-out date YYYY-MM-DD" - }, - { - "name": "adults", - "type": "int", - "default": 2, - "required": false, - "help": "Number of adults (1-30)" - }, - { - "name": "rooms", - "type": "int", - "default": 1, - "required": false, - "help": "Number of rooms (1-30)" + "help": "Prompt to send" }, { - "name": "children", + "name": "timeout", "type": "int", - "default": 0, + "default": 120, "required": false, - "help": "Number of children (0-10)" + "help": "Max seconds to wait for response" }, { - "name": "currency", - "type": "str", + "name": "new", + "type": "boolean", + "default": false, "required": false, - "help": "Force result currency (e.g. USD, JPY, CNY)" + "help": "Start a new chat before sending" }, { - "name": "lang", + "name": "model", "type": "str", + "default": "sonnet", "required": false, - "help": "Force result language (e.g. en-us, zh-cn, ja)" + "help": "Model to use: sonnet, opus, or haiku", + "choices": [ + "sonnet", + "opus", + "haiku" + ] }, { - "name": "limit", - "type": "int", - "default": 25, + "name": "think", + "type": "boolean", + "default": false, "required": false, - "help": "Max rows to return (1-100; Booking pages 25 per request)" + "help": "Enable Adaptive thinking" }, { - "name": "offset", - "type": "int", - "default": 0, + "name": "file", + "type": "str", "required": false, - "help": "Result offset for pagination (multiple of 25)" + "help": "Attach a file (image, PDF, text) with the prompt", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "application/pdf", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } } ], "columns": [ - "rank", - "name", - "country", - "slug", - "star_rating", - "review_score", - "review_count", - "price_amount", - "price_currency", - "distance", - "recommended_room", - "url" - ], - "tags": [ - "search" + "response" ], "type": "js", - "modulePath": "booking/search.js", - "sourceFile": "booking/search.js" + "modulePath": "claude/ask.js", + "sourceFile": "claude/ask.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "brave", - "name": "search", - "description": "Search Brave Search", + "site": "claude", + "name": "detail", + "description": "Open a Claude conversation by ID and read its messages", "access": "read", - "domain": "search.brave.com", - "strategy": "public", + "domain": "claude.ai", + "strategy": "cookie", "browser": true, "args": [ { - "name": "keyword", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results per page (max 18)" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Page offset (0, 1, 2...). Brave returns ~18 results per page" + "help": "Conversation ID (UUID from /chat/)" } ], "columns": [ - "rank", - "title", - "url", - "snippet" - ], - "tags": [ - "search" + "Index", + "Role", + "Text" ], "type": "js", - "modulePath": "brave/search.js", - "sourceFile": "brave/search.js" + "modulePath": "claude/detail.js", + "sourceFile": "claude/detail.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "chatgpt", - "name": "ask", - "description": "Send a prompt to ChatGPT web and wait for the response", - "access": "write", - "domain": "chatgpt.com", + "site": "claude", + "name": "history", + "description": "List conversation history from Claude /recents", + "access": "read", + "domain": "claude.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", + "name": "limit", "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait for response" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Continue an existing ChatGPT conversation ID or /c/ URL" - }, - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" - }, - { - "name": "wait", - "type": "boolean", - "default": true, - "required": false, - "help": "Wait for the assistant response after sending" - }, - { - "name": "deep-research", - "type": "boolean", - "default": false, - "required": false, - "help": "Enable ChatGPT Deep Research (Deep Research)" - }, - { - "name": "web-search", - "type": "boolean", - "default": false, + "default": 20, "required": false, - "help": "Enable ChatGPT Web Search (Web Search)" + "help": "Max conversations to show" } ], "columns": [ - "conversationId", - "conversationUrl", - "tool", - "response" + "Index", + "Id", + "Title", + "Url" ], "type": "js", - "modulePath": "chatgpt/ask.js", - "sourceFile": "chatgpt/ask.js", + "modulePath": "claude/history.js", + "sourceFile": "claude/history.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "chatgpt", - "name": "deep-research-result", - "description": "Read a ChatGPT Deep Research report or progress from the conversation payload", - "access": "read", - "domain": "chatgpt.com", + "site": "claude", + "name": "login", + "description": "Open claude login", + "access": "write", + "domain": "claude.ai", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Conversation ID or full /c/ URL" - }, - { - "name": "wait", - "type": "boolean", - "default": false, - "required": false, - "help": "Wait until Deep Research completes or becomes extractable" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait when --wait is true" - }, - { - "name": "stable", - "type": "int", - "default": 6, - "required": false, - "help": "Seconds the report text must remain unchanged when --wait is true" - } - ], + "args": [], "columns": [ - "conversationId", "status", - "report", - "sources", - "progress", - "asyncTaskConversationId", - "widgetSessionId", - "asyncStatus", - "venusMessageType", - "venusStatus", - "waitingForUserUntil", - "planTitle", - "planId", - "url", - "method", - "diagnostics" + "logged_in", + "site", + "user_id", + "org_name", + "org_uuid", + "action", + "verify_command" ], - "tags": [ - "search" + "type": "js", + "modulePath": "claude/auth.js", + "sourceFile": "claude/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "new", + "description": "Start a new conversation in Claude", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status" ], "type": "js", - "modulePath": "chatgpt/deep-research-result.js", - "sourceFile": "chatgpt/deep-research-result.js", + "modulePath": "claude/new.js", + "sourceFile": "claude/new.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "chatgpt", - "name": "detail", - "description": "Open a ChatGPT web conversation by ID and read its messages", + "site": "claude", + "name": "read", + "description": "Read the current Claude conversation", "access": "read", - "domain": "chatgpt.com", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "claude/read.js", + "sourceFile": "claude/read.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "send", + "description": "Send a prompt to Claude without waiting for the response", + "access": "write", + "domain": "claude.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", + "name": "prompt", "type": "str", "required": true, "positional": true, - "help": "Conversation ID or full /c/ URL" - }, - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" + "help": "Prompt to send" }, { - "name": "wait", + "name": "new", "type": "boolean", "default": false, "required": false, - "help": "Wait until the conversation stops generating and stabilizes" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait when --wait is true" - }, - { - "name": "stable", - "type": "int", - "default": 6, - "required": false, - "help": "Seconds the final messages must remain unchanged when --wait is true" + "help": "Start a new chat before sending" } ], "columns": [ - "Index", - "Role", - "Text", - "Generating", - "StableSeconds" + "Status", + "SubmittedBy", + "InjectedText" ], "type": "js", - "modulePath": "chatgpt/detail.js", - "sourceFile": "chatgpt/detail.js", + "modulePath": "claude/send.js", + "sourceFile": "claude/send.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "chatgpt", - "name": "history", - "description": "List visible ChatGPT web conversation history from the sidebar", + "site": "claude", + "name": "status", + "description": "Check Claude page availability and login state", "access": "read", - "domain": "chatgpt.com", + "domain": "claude.ai", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max conversations to show" - } - ], + "args": [], "columns": [ - "Index", - "Id", - "Title", + "Status", + "Login", "Url" ], "type": "js", - "modulePath": "chatgpt/history.js", - "sourceFile": "chatgpt/history.js", + "modulePath": "claude/status.js", + "sourceFile": "claude/status.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "chatgpt", - "name": "image", - "description": "Generate images with ChatGPT web and save them locally", - "access": "write", - "domain": "chatgpt.com", + "site": "claude", + "name": "whoami", + "description": "Show the current logged-in claude account", + "access": "read", + "domain": "claude.ai", "strategy": "cookie", "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "org_name", + "org_uuid" + ], + "type": "js", + "modulePath": "claude/auth.js", + "sourceFile": "claude/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "codex", + "name": "archive", + "description": "Archive (Codex's term for delete) the selected conversation via the Chat actions header menu. No confirmation in UI — pass --yes to actually archive.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Image prompt to send to ChatGPT" - }, - { - "name": "image", - "type": "str", + "name": "yes", + "type": "boolean", + "default": false, "required": false, - "help": "Local image path to attach before prompting; comma-separated paths are supported", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } + "help": "Actually archive (default: dry-run preview)" }, { "name": "project", "type": "str", "required": false, - "valueRequired": true, - "help": "Start image generation inside a ChatGPT project ID or /g/g-p- URL" + "help": "Project label or path to select before running the command" }, { - "name": "op", + "name": "conversation", "type": "str", "required": false, - "help": "Output directory (default: ~/Pictures/chatgpt)", - "file": { - "direction": "output", - "pathKind": "directory", - "multiple": false, - "defaultPath": "~/Pictures/chatgpt" - } + "help": "Conversation title to select within --project" }, { - "name": "sd", - "type": "boolean", - "default": false, + "name": "index", + "type": "str", "required": false, - "help": "Skip download shorthand; only show ChatGPT link" + "help": "1-based conversation index within --project" }, { - "name": "timeout", - "type": "int", - "default": 240, + "name": "thread-id", + "type": "str", "required": false, - "help": "Max seconds for the overall command (default: 240)" + "help": "Exact Codex thread id to select" } ], "columns": [ "status", - "file", - "link" - ], - "defaultFormat": "plain", - "type": "js", - "modulePath": "chatgpt/image.js", - "sourceFile": "chatgpt/image.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "login", - "description": "Open chatgpt login", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" + "thread_id", + "project", + "conversation" ], "type": "js", - "modulePath": "chatgpt/auth.js", - "sourceFile": "chatgpt/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "codex/archive.js", + "sourceFile": "codex/archive.js", + "navigateBefore": true }, { - "site": "chatgpt", - "name": "model", - "description": "Switch ChatGPT web model or intelligence level (GPT-5.6 Pro, fast, balanced, advanced, very-high, pro)", + "site": "codex", + "name": "ask", + "description": "Send a prompt to the current or selected Codex conversation and wait for the AI response", "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "model", + "name": "text", "type": "str", "required": true, "positional": true, - "help": "ChatGPT model or intelligence level to switch to", - "choices": [ - "fast", - "speed", - "instant", - "balanced", - "balance", - "medium", - "advanced", - "high", - "thinking", - "very-high", - "ultra", - "xhigh", - "x-high", - "extra-high", - "very high", - "gpt-5.6-pro", - "gpt-5-6-pro", - "gpt-5.6-sol-pro", - "gpt-5-6-sol-pro", - "gpt-5.6", - "gpt-5-6", - "5.6-pro", - "5.6", - "pro", - "professional" - ] + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 60, + "required": false, + "help": "Max seconds to wait for response (default: 60)" }, { "name": "project", "type": "str", "required": false, - "valueRequired": true, - "help": "Open a ChatGPT project ID or /g/g-p- URL before switching intelligence level" + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "Status", - "Model" + "Role", + "Project", + "Conversation", + "Text" ], "type": "js", - "modulePath": "chatgpt/model.js", - "sourceFile": "chatgpt/model.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "codex/ask.js", + "sourceFile": "codex/ask.js", + "navigateBefore": true }, { - "site": "chatgpt", - "name": "new", - "description": "Start a new ChatGPT web conversation", + "site": "codex", + "name": "dump", + "description": "Dump the DOM and Accessibility tree of codex for reverse-engineering", "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "action", + "files" + ], + "type": "js", + "modulePath": "codex/dump.js", + "sourceFile": "codex/dump.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "export", + "description": "Export the current Codex conversation to a Markdown file", + "access": "read", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "project", + "name": "output", "type": "str", "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + "help": "Output file (default: /tmp/codex-export.md)" } ], "columns": [ - "Status" + "Status", + "File", + "Messages" ], "type": "js", - "modulePath": "chatgpt/new.js", - "sourceFile": "chatgpt/new.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "codex/export.js", + "sourceFile": "codex/export.js", + "navigateBefore": true }, { - "site": "chatgpt", - "name": "project-file-add", - "description": "Upload files to a ChatGPT project as project knowledge (not just conversation attachments)", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "file", - "type": "str", - "required": true, - "positional": true, - "help": "Local file path(s) to upload; comma-separated paths are supported", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "application/pdf", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - }, - { - "name": "id", - "type": "str", - "required": true, - "help": "Project ID or /g/g-p- URL" - } - ], - "columns": [ - "Status", - "File" - ], - "type": "js", - "modulePath": "chatgpt/project-file-add.js", - "sourceFile": "chatgpt/project-file-add.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "project-list", - "description": "List visible ChatGPT projects from the sidebar", + "site": "codex", + "name": "extract-diff", + "description": "Extract visual code review diff patches from Codex", "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max projects to show" - } - ], + "args": [], "columns": [ - "Index", - "Id", - "Title", - "Url" + "File", + "Diff" ], "type": "js", - "modulePath": "chatgpt/project-list.js", - "sourceFile": "chatgpt/project-list.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "codex/extract-diff.js", + "sourceFile": "codex/extract-diff.js", + "navigateBefore": true }, { - "site": "chatgpt", - "name": "read", - "description": "Read messages in the current ChatGPT web conversation", + "site": "codex", + "name": "history", + "description": "List visible Codex conversation threads grouped by project", "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "markdown", - "type": "boolean", - "default": false, + "name": "project", + "type": "str", "required": false, - "help": "Emit assistant replies as markdown" + "help": "Filter by project label or path" + }, + { + "name": "limit", + "type": "str", + "required": false, + "help": "Max conversations per project" } ], "columns": [ + "Project", "Index", - "Role", - "Text" + "Title", + "Updated", + "Active" ], "type": "js", - "modulePath": "chatgpt/read.js", - "sourceFile": "chatgpt/read.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "codex/history.js", + "sourceFile": "codex/history.js", + "navigateBefore": true }, { - "site": "chatgpt", - "name": "send", - "description": "Send a prompt to ChatGPT web without waiting for the response", + "site": "codex", + "name": "model", + "description": "Read, list, or switch the active model / reasoning level in Codex Desktop. The composer toolbar button toggles a menu that mixes model variants (GPT-5.5, Speed) with reasoning levels (Low/Medium/High/Extra High).", "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "prompt", + "name": "name", "type": "str", - "required": true, + "required": false, "positional": true, - "help": "Prompt to send" + "help": "Substring (case-insensitive) of a model / reasoning level to switch to. Omit to read current." }, { - "name": "new", + "name": "list", "type": "boolean", "default": false, "required": false, - "help": "Start a new chat before sending" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Continue an existing ChatGPT conversation ID or /c/ URL" - }, - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + "help": "List all menu options (does not switch)" } ], "columns": [ "Status", - "InjectedText" - ], - "type": "js", - "modulePath": "chatgpt/send.js", - "sourceFile": "chatgpt/send.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "status", - "description": "Check ChatGPT web page availability and login state", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status", - "Login", - "Url" + "Model" ], "type": "js", - "modulePath": "chatgpt/status.js", - "sourceFile": "chatgpt/status.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "codex/model.js", + "sourceFile": "codex/model.js", + "navigateBefore": true }, { - "site": "chatgpt", - "name": "whoami", - "description": "Show the current logged-in chatgpt account", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", + "site": "codex", + "name": "new", + "description": "Start a new Codex conversation session", + "access": "write", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "user_id", - "name" + "Status" ], "type": "js", - "modulePath": "chatgpt/auth.js", - "sourceFile": "chatgpt/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "codex/new.js", + "sourceFile": "codex/new.js", + "navigateBefore": true }, { - "site": "chatgpt-app", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", + "site": "codex", + "name": "pin", + "description": "Pin the selected Codex conversation via the Chat actions header menu.", "access": "write", "domain": "localhost", - "strategy": "public", - "browser": false, + "strategy": "ui", + "browser": true, "args": [ { - "name": "text", + "name": "project", "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" + "required": false, + "help": "Project label or path to select before running the command" }, { - "name": "model", + "name": "conversation", "type": "str", "required": false, - "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] + "help": "Conversation title to select within --project" }, { - "name": "timeout", - "type": "int", - "default": 30, + "name": "index", + "type": "str", "required": false, - "help": "Max seconds to wait for response (default: 30)" + "help": "1-based conversation index within --project" }, { - "name": "image", + "name": "thread-id", "type": "str", "required": false, - "help": "Path to local image to attach (optional)" + "help": "Exact Codex thread id to select" } ], "columns": [ - "Role", - "Text" + "status", + "thread_id", + "project", + "conversation" ], "type": "js", - "modulePath": "chatgpt-app/ask.js", - "sourceFile": "chatgpt-app/ask.js" + "modulePath": "codex/pin.js", + "sourceFile": "codex/pin.js", + "navigateBefore": true }, { - "site": "chatgpt-app", - "name": "model", - "description": "Switch ChatGPT Desktop model/mode (auto, instant, thinking, 5.2-instant, 5.2-thinking)", + "site": "codex", + "name": "projects", + "description": "List Codex projects and visible conversations from the sidebar", "access": "read", "domain": "localhost", - "strategy": "public", - "browser": false, + "strategy": "ui", + "browser": true, "args": [ { - "name": "model", + "name": "project", "type": "str", - "required": true, - "positional": true, - "help": "Model to switch to", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] + "required": false, + "help": "Filter by project label or path" + }, + { + "name": "limit", + "type": "str", + "required": false, + "help": "Max conversations per project" } ], "columns": [ - "Status", - "Model" + "Project", + "Index", + "Title", + "Updated", + "Active" ], "type": "js", - "modulePath": "chatgpt-app/model.js", - "sourceFile": "chatgpt-app/model.js" + "modulePath": "codex/projects.js", + "sourceFile": "codex/projects.js", + "navigateBefore": true }, { - "site": "chatgpt-app", - "name": "new", - "description": "Open a new chat in ChatGPT Desktop App", - "access": "write", + "site": "codex", + "name": "read", + "description": "Read the contents of the current or selected Codex conversation thread", + "access": "read", "domain": "localhost", - "strategy": "public", - "browser": false, + "strategy": "ui", + "browser": true, "args": [ { - "name": "temp", - "type": "boolean", - "default": false, + "name": "project", + "type": "str", "required": false, - "help": "Open a temporary chat with privacy protection" + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "Status" - ], - "type": "js", - "modulePath": "chatgpt-app/new.js", - "sourceFile": "chatgpt-app/new.js" - }, - { - "site": "chatgpt-app", - "name": "read", - "description": "Read the last visible message from the focused ChatGPT Desktop window", - "access": "read", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "Role", - "Text" + "Project", + "Conversation", + "Content" ], "type": "js", - "modulePath": "chatgpt-app/read.js", - "sourceFile": "chatgpt-app/read.js" + "modulePath": "codex/read.js", + "sourceFile": "codex/read.js", + "navigateBefore": true }, { - "site": "chatgpt-app", - "name": "send", - "description": "Send a message to the active ChatGPT Desktop App window", + "site": "codex", + "name": "rename", + "description": "Rename the selected Codex conversation. Opens the Chat actions menu → \"Rename chat\", then types the new title.", "access": "write", "domain": "localhost", - "strategy": "public", - "browser": false, + "strategy": "ui", + "browser": true, "args": [ { - "name": "text", + "name": "title", "type": "str", "required": true, "positional": true, - "help": "Message to send" + "help": "New title (single line, no newlines)" }, { - "name": "model", + "name": "project", "type": "str", "required": false, - "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "Status" + "status", + "title", + "thread_id", + "project" ], "type": "js", - "modulePath": "chatgpt-app/send.js", - "sourceFile": "chatgpt-app/send.js" + "modulePath": "codex/rename.js", + "sourceFile": "codex/rename.js", + "navigateBefore": true }, { - "site": "chatgpt-app", - "name": "status", - "description": "Check if ChatGPT Desktop App is running natively on macOS", + "site": "codex", + "name": "screenshot", + "description": "Capture a snapshot of the current Codex window (DOM + Accessibility tree)", "access": "read", "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "chatgpt-app/status.js", - "sourceFile": "chatgpt-app/status.js" - }, - { - "site": "chatwise", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", - "access": "write", - "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "text", + "name": "output", "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 30, "required": false, - "help": "Max seconds to wait (default: 30)" + "help": "Output file path (default: /tmp/codex-snapshot.txt)" } ], "columns": [ - "Role", - "Text" + "Status", + "File" ], "type": "js", - "modulePath": "chatwise/ask.js", - "sourceFile": "chatwise/ask.js", + "modulePath": "codex/screenshot.js", + "sourceFile": "codex/screenshot.js", "navigateBefore": true }, { - "site": "chatwise", - "name": "export", - "description": "Export the current ChatWise conversation to a Markdown file", - "access": "read", + "site": "codex", + "name": "send", + "description": "Send text/commands to the current or selected Codex AI composer", + "access": "write", "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "output", + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Text, command (e.g. /review), or skill (e.g. $imagegen)" + }, + { + "name": "project", "type": "str", "required": false, - "help": "Output file (default: /tmp/chatwise-export.md)" + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ "Status", - "File", - "Messages" + "Project", + "Conversation", + "InjectedText" ], "type": "js", - "modulePath": "chatwise/export.js", - "sourceFile": "chatwise/export.js", + "modulePath": "codex/send.js", + "sourceFile": "codex/send.js", "navigateBefore": true }, { - "site": "chatwise", - "name": "history", - "description": "List conversation history in ChatWise sidebar", + "site": "codex", + "name": "status", + "description": "Check active CDP connection to OpenAI Codex App", "access": "read", "domain": "localhost", "strategy": "ui", "browser": true, "args": [], "columns": [ - "Index", + "Status", + "Url", "Title" ], "type": "js", - "modulePath": "chatwise/history.js", - "sourceFile": "chatwise/history.js", + "modulePath": "codex/status.js", + "sourceFile": "codex/status.js", "navigateBefore": true }, { - "site": "chatwise", - "name": "model", - "description": "Get or switch the active AI model in ChatWise", - "access": "read", + "site": "codex", + "name": "unpin", + "description": "Unpin the selected Codex conversation via the Chat actions header menu.", + "access": "write", "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "model-name", + "name": "project", "type": "str", "required": false, - "positional": true, - "help": "Model to switch to (e.g. gpt-4, claude-3)" - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "chatwise/model.js", - "sourceFile": "chatwise/model.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "new", - "description": "Start a new ChatWise conversation session", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "chatwise/new.js", - "sourceFile": "chatwise/new.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "read", - "description": "Read the current ChatWise conversation history", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Content" - ], - "type": "js", - "modulePath": "chatwise/read.js", - "sourceFile": "chatwise/read.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "screenshot", - "description": "Capture a snapshot of the current ChatWise window (DOM + Accessibility tree)", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Project label or path to select before running the command" + }, { - "name": "output", + "name": "conversation", "type": "str", "required": false, - "help": "Output file path (default: /tmp/chatwise-snapshot.txt)" + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "Status", - "File" + "status", + "thread_id", + "project", + "conversation" ], "type": "js", - "modulePath": "chatwise/screenshot.js", - "sourceFile": "chatwise/screenshot.js", + "modulePath": "codex/pin.js", + "sourceFile": "codex/pin.js", "navigateBefore": true }, { - "site": "chatwise", - "name": "send", - "description": "Send a message to the active ChatWise conversation", + "site": "confluence", + "name": "create", + "description": "Create a Confluence page from Markdown or storage XHTML", "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "text", - "type": "str", + "name": "space", + "type": "string", "required": true, - "positional": true, - "help": "Message to send" - } - ], - "columns": [ - "Status", - "InjectedText" - ], - "type": "js", - "modulePath": "chatwise/send.js", - "sourceFile": "chatwise/send.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "status", - "description": "Check active CDP connection to ChatWise Desktop", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" - ], - "type": "js", - "modulePath": "chatwise/status.js", - "sourceFile": "chatwise/status.js", - "navigateBefore": true - }, - { - "site": "chess", - "name": "analyze", - "description": "Open a Chess.com game in the browser analysis board", - "access": "read", - "domain": "www.chess.com", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Cloud space id, or Data Center space key" + }, { - "name": "game-url", + "name": "title", "type": "string", "required": true, - "positional": true, - "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" + "help": "Page title" + }, + { + "name": "file", + "type": "string", + "required": true, + "help": "Markdown file path" + }, + { + "name": "parent", + "type": "string", + "required": false, + "help": "Optional parent page id" + }, + { + "name": "representation", + "type": "string", + "default": "markdown", + "required": false, + "help": "Input file format", + "choices": [ + "markdown", + "storage" + ] + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually create the remote page" } ], "columns": [ - "kind", - "game_id", - "analysis_url" + "status", + "id", + "title", + "spaceId", + "spaceKey", + "version", + "url" ], "type": "js", - "modulePath": "chess/analyze.js", - "sourceFile": "chess/analyze.js", - "navigateBefore": false + "modulePath": "confluence/create.js", + "sourceFile": "confluence/create.js" }, { - "site": "chess", - "name": "game", - "description": "Chess.com single-game detail (white, black, result, ECO, time control) by full game URL", + "site": "confluence", + "name": "page", + "description": "Confluence page by id with storage and Markdown body", "access": "read", - "domain": "www.chess.com", + "domain": "atlassian.net", "strategy": "public", "browser": false, "args": [ { - "name": "game-url", - "type": "string", + "name": "id", + "type": "str", "required": true, "positional": true, - "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" + "help": "Confluence page id" } ], "columns": [ - "kind", - "game_id", - "date", - "white", - "white_rating", - "black", - "black_rating", - "result", - "winner_color", - "termination", - "eco", - "time_control", - "rated", - "ply_count", + "id", + "title", + "status", + "spaceId", + "spaceKey", + "version", "url" ], "type": "js", - "modulePath": "chess/game.js", - "sourceFile": "chess/game.js" + "modulePath": "confluence/page.js", + "sourceFile": "confluence/page.js" }, { - "site": "chess", - "name": "games", - "description": "Chess.com recent games for a player, newest first", + "site": "confluence", + "name": "search", + "description": "Search Confluence content with CQL", "access": "read", - "domain": "api.chess.com", + "domain": "atlassian.net", "strategy": "public", "browser": false, "args": [ { - "name": "username", - "type": "string", + "name": "cql", + "type": "str", "required": true, "positional": true, - "help": "Chess.com username" + "help": "CQL query, e.g. \"type = page and title ~ \\\"RCA\\\"\"" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "space", + "type": "string", "required": false, - "help": "Number of recent games (1-100)" - } - ], - "columns": [ - "date", - "time_class", - "rated", - "my_color", - "my_rating", - "my_result", - "opponent", - "opponent_rating", - "accuracy_white", - "accuracy_black", - "eco", - "opening_name", - "url" - ], - "type": "js", - "modulePath": "chess/games.js", - "sourceFile": "chess/games.js" - }, - { - "site": "chess", - "name": "stats", - "description": "Chess.com player ratings + win/loss record across game kinds", - "access": "read", - "domain": "api.chess.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Chess.com username (case-insensitive)" - } - ], - "columns": [ - "kind", - "rating_current", - "rating_best", - "wins", - "losses", - "draws" - ], - "type": "js", - "modulePath": "chess/stats.js", - "sourceFile": "chess/stats.js" - }, - { - "site": "claude", - "name": "ask", - "description": "Send a prompt to Claude and get the response", - "access": "write", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait for response" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - }, - { - "name": "model", - "type": "str", - "default": "sonnet", - "required": false, - "help": "Model to use: sonnet, opus, or haiku", - "choices": [ - "sonnet", - "opus", - "haiku" - ] - }, - { - "name": "think", - "type": "boolean", - "default": false, - "required": false, - "help": "Enable Adaptive thinking" - }, - { - "name": "file", - "type": "str", - "required": false, - "help": "Attach a file (image, PDF, text) with the prompt", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "application/pdf", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - } - ], - "columns": [ - "response" - ], - "type": "js", - "modulePath": "claude/ask.js", - "sourceFile": "claude/ask.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "detail", - "description": "Open a Claude conversation by ID and read its messages", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Conversation ID (UUID from /chat/)" - } - ], - "columns": [ - "Index", - "Role", - "Text" - ], - "type": "js", - "modulePath": "claude/detail.js", - "sourceFile": "claude/detail.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "history", - "description": "List conversation history from Claude /recents", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max conversations to show" - } - ], - "columns": [ - "Index", - "Id", - "Title", - "Url" - ], - "type": "js", - "modulePath": "claude/history.js", - "sourceFile": "claude/history.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "login", - "description": "Open claude login", - "access": "write", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "org_name", - "org_uuid", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "claude/auth.js", - "sourceFile": "claude/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "new", - "description": "Start a new conversation in Claude", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "claude/new.js", - "sourceFile": "claude/new.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "read", - "description": "Read the current Claude conversation", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Index", - "Role", - "Text" - ], - "type": "js", - "modulePath": "claude/read.js", - "sourceFile": "claude/read.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "send", - "description": "Send a prompt to Claude without waiting for the response", - "access": "write", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - } - ], - "columns": [ - "Status", - "SubmittedBy", - "InjectedText" - ], - "type": "js", - "modulePath": "claude/send.js", - "sourceFile": "claude/send.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "status", - "description": "Check Claude page availability and login state", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status", - "Login", - "Url" - ], - "type": "js", - "modulePath": "claude/status.js", - "sourceFile": "claude/status.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "whoami", - "description": "Show the current logged-in claude account", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "org_name", - "org_uuid" - ], - "type": "js", - "modulePath": "claude/auth.js", - "sourceFile": "claude/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "codex", - "name": "archive", - "description": "Archive (Codex's term for delete) the selected conversation via the Chat actions header menu. No confirmation in UI — pass --yes to actually archive.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "yes", - "type": "boolean", - "default": false, - "required": false, - "help": "Actually archive (default: dry-run preview)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "status", - "thread_id", - "project", - "conversation" - ], - "type": "js", - "modulePath": "codex/archive.js", - "sourceFile": "codex/archive.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "ask", - "description": "Send a prompt to the current or selected Codex conversation and wait for the AI response", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 60, - "required": false, - "help": "Max seconds to wait for response (default: 60)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "Role", - "Project", - "Conversation", - "Text" - ], - "type": "js", - "modulePath": "codex/ask.js", - "sourceFile": "codex/ask.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "dump", - "description": "Dump the DOM and Accessibility tree of codex for reverse-engineering", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "action", - "files" - ], - "type": "js", - "modulePath": "codex/dump.js", - "sourceFile": "codex/dump.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "export", - "description": "Export the current Codex conversation to a Markdown file", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "output", - "type": "str", - "required": false, - "help": "Output file (default: /tmp/codex-export.md)" - } - ], - "columns": [ - "Status", - "File", - "Messages" - ], - "type": "js", - "modulePath": "codex/export.js", - "sourceFile": "codex/export.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "extract-diff", - "description": "Extract visual code review diff patches from Codex", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "File", - "Diff" - ], - "type": "js", - "modulePath": "codex/extract-diff.js", - "sourceFile": "codex/extract-diff.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "history", - "description": "List visible Codex conversation threads grouped by project", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "help": "Filter by project label or path" - }, - { - "name": "limit", - "type": "str", - "required": false, - "help": "Max conversations per project" - } - ], - "columns": [ - "Project", - "Index", - "Title", - "Updated", - "Active" - ], - "type": "js", - "modulePath": "codex/history.js", - "sourceFile": "codex/history.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "model", - "description": "Read, list, or switch the active model / reasoning level in Codex Desktop. The composer toolbar button toggles a menu that mixes model variants (GPT-5.5, Speed) with reasoning levels (Low/Medium/High/Extra High).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": false, - "positional": true, - "help": "Substring (case-insensitive) of a model / reasoning level to switch to. Omit to read current." - }, - { - "name": "list", - "type": "boolean", - "default": false, - "required": false, - "help": "List all menu options (does not switch)" - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "codex/model.js", - "sourceFile": "codex/model.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "new", - "description": "Start a new Codex conversation session", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "codex/new.js", - "sourceFile": "codex/new.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "pin", - "description": "Pin the selected Codex conversation via the Chat actions header menu.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "status", - "thread_id", - "project", - "conversation" - ], - "type": "js", - "modulePath": "codex/pin.js", - "sourceFile": "codex/pin.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "projects", - "description": "List Codex projects and visible conversations from the sidebar", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "help": "Filter by project label or path" - }, - { - "name": "limit", - "type": "str", - "required": false, - "help": "Max conversations per project" - } - ], - "columns": [ - "Project", - "Index", - "Title", - "Updated", - "Active" - ], - "type": "js", - "modulePath": "codex/projects.js", - "sourceFile": "codex/projects.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "read", - "description": "Read the contents of the current or selected Codex conversation thread", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "Project", - "Conversation", - "Content" - ], - "type": "js", - "modulePath": "codex/read.js", - "sourceFile": "codex/read.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "rename", - "description": "Rename the selected Codex conversation. Opens the Chat actions menu → \"Rename chat\", then types the new title.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "title", - "type": "str", - "required": true, - "positional": true, - "help": "New title (single line, no newlines)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "status", - "title", - "thread_id", - "project" - ], - "type": "js", - "modulePath": "codex/rename.js", - "sourceFile": "codex/rename.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "screenshot", - "description": "Capture a snapshot of the current Codex window (DOM + Accessibility tree)", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "output", - "type": "str", - "required": false, - "help": "Output file path (default: /tmp/codex-snapshot.txt)" - } - ], - "columns": [ - "Status", - "File" - ], - "type": "js", - "modulePath": "codex/screenshot.js", - "sourceFile": "codex/screenshot.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "send", - "description": "Send text/commands to the current or selected Codex AI composer", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Text, command (e.g. /review), or skill (e.g. $imagegen)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "Status", - "Project", - "Conversation", - "InjectedText" - ], - "type": "js", - "modulePath": "codex/send.js", - "sourceFile": "codex/send.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "status", - "description": "Check active CDP connection to OpenAI Codex App", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" - ], - "type": "js", - "modulePath": "codex/status.js", - "sourceFile": "codex/status.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "unpin", - "description": "Unpin the selected Codex conversation via the Chat actions header menu.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "status", - "thread_id", - "project", - "conversation" - ], - "type": "js", - "modulePath": "codex/pin.js", - "sourceFile": "codex/pin.js", - "navigateBefore": true - }, - { - "site": "coingecko", - "name": "categories", - "description": "Crypto categories ranked by aggregated market cap", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "sort", - "type": "str", - "default": "market_cap_desc", - "required": false, - "help": "Sort order (market_cap_desc / market_cap_asc / name_desc / name_asc / market_cap_change_24h_desc / market_cap_change_24h_asc)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of categories (1-100; CoinGecko returns ~120 max)" - } - ], - "columns": [ - "rank", - "id", - "name", - "marketCap", - "volume24h", - "marketCapChange24hPct", - "top3Coins" - ], - "type": "js", - "modulePath": "coingecko/categories.js", - "sourceFile": "coingecko/categories.js" - }, - { - "site": "coingecko", - "name": "coin", - "description": "Fetch a single cryptocurrency's market data by CoinGecko id (e.g. bitcoin, ethereum).", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "CoinGecko coin id (lowercase, e.g. bitcoin / ethereum / solana)." - }, - { - "name": "currency", - "type": "string", - "default": "usd", - "required": false, - "help": "Quote currency (usd, cny, eur, jpy, ...)." - } - ], - "columns": [ - "id", - "symbol", - "name", - "rank", - "price", - "marketCap", - "volume24h", - "change24hPct", - "change7dPct", - "change30dPct", - "ath", - "athDate", - "atl", - "atlDate", - "circulatingSupply", - "totalSupply", - "maxSupply", - "genesisDate", - "homepage" - ], - "type": "js", - "modulePath": "coingecko/coin.js", - "sourceFile": "coingecko/coin.js" - }, - { - "site": "coingecko", - "name": "derivatives", - "description": "Top crypto derivative (perpetual / futures) markets by 24h volume", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max rows to return (1-500; CoinGecko returns one large page)." - }, - { - "name": "symbol", - "type": "string", - "required": false, - "help": "Optional symbol substring filter (e.g. \"BTC\", \"ETHUSDT\")." - } - ], - "columns": [ - "rank", - "market", - "symbol", - "indexId", - "contractType", - "price", - "change24hPct", - "fundingRate", - "openInterestUsd", - "volume24hUsd", - "expired" - ], - "type": "js", - "modulePath": "coingecko/derivatives.js", - "sourceFile": "coingecko/derivatives.js" - }, - { - "site": "coingecko", - "name": "exchanges", - "description": "Top crypto exchanges by 24h BTC trading volume", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of exchanges (1-250, CoinGecko per_page upper bound)" - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number (1-based)" - } - ], - "columns": [ - "rank", - "id", - "name", - "trustScore", - "volume24hBtc", - "country", - "yearEstablished", - "url" - ], - "type": "js", - "modulePath": "coingecko/exchanges.js", - "sourceFile": "coingecko/exchanges.js" - }, - { - "site": "coingecko", - "name": "global", - "description": "Aggregate crypto market stats: total market cap, volume, dominance", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "currency", - "type": "string", - "default": "usd", - "required": false, - "help": "Quote currency for total market cap / volume (usd, cny, eur, jpy, ...)" - } - ], - "columns": [ - "currency", - "totalMarketCap", - "totalVolume24h", - "marketCapChange24hPct", - "btcDominancePct", - "ethDominancePct", - "activeCryptocurrencies", - "markets", - "ongoingIcos", - "updatedAt" - ], - "type": "js", - "modulePath": "coingecko/global.js", - "sourceFile": "coingecko/global.js" - }, - { - "site": "coingecko", - "name": "top", - "description": "Cryptocurrency quotes by market cap (default USD)", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "currency", - "type": "string", - "default": "usd", - "required": false, - "help": "quote currency (usd / cny / eur / jpy ...)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number to return (default 10, maximum 250)" - } - ], - "columns": [ - "rank", - "symbol", - "name", - "price", - "change24hPct", - "marketCap", - "volume24h", - "high24h", - "low24h" - ], - "type": "js", - "modulePath": "coingecko/top.js", - "sourceFile": "coingecko/top.js" - }, - { - "site": "coingecko", - "name": "trending", - "description": "Top trending cryptocurrencies on CoinGecko in the last 24h (search-volume based).", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "rank", - "id", - "symbol", - "name", - "marketCapRank", - "priceBtc", - "thumb" - ], - "type": "js", - "modulePath": "coingecko/trending.js", - "sourceFile": "coingecko/trending.js" - }, - { - "site": "confluence", - "name": "create", - "description": "Create a Confluence page from Markdown or storage XHTML", - "access": "write", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "space", - "type": "string", - "required": true, - "help": "Cloud space id, or Data Center space key" - }, - { - "name": "title", - "type": "string", - "required": true, - "help": "Page title" - }, - { - "name": "file", - "type": "string", - "required": true, - "help": "Markdown file path" - }, - { - "name": "parent", - "type": "string", - "required": false, - "help": "Optional parent page id" - }, - { - "name": "representation", - "type": "string", - "default": "markdown", - "required": false, - "help": "Input file format", - "choices": [ - "markdown", - "storage" - ] - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually create the remote page" - } - ], - "columns": [ - "status", - "id", - "title", - "spaceId", - "spaceKey", - "version", - "url" - ], - "type": "js", - "modulePath": "confluence/create.js", - "sourceFile": "confluence/create.js" - }, - { - "site": "confluence", - "name": "page", - "description": "Confluence page by id with storage and Markdown body", - "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Confluence page id" - } - ], - "columns": [ - "id", - "title", - "status", - "spaceId", - "spaceKey", - "version", - "url" - ], - "type": "js", - "modulePath": "confluence/page.js", - "sourceFile": "confluence/page.js" - }, - { - "site": "confluence", - "name": "search", - "description": "Search Confluence content with CQL", - "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "cql", - "type": "str", - "required": true, - "positional": true, - "help": "CQL query, e.g. \"type = page and title ~ \\\"RCA\\\"\"" - }, - { - "name": "space", - "type": "string", - "required": false, - "help": "Limit search to a Confluence space key" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results to return (1-100)" - } - ], - "columns": [ - "id", - "title", - "type", - "spaceKey", - "status", - "lastModified", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "confluence/search.js", - "sourceFile": "confluence/search.js" - }, - { - "site": "confluence", - "name": "update", - "description": "Update a Confluence page body from Markdown or storage XHTML", - "access": "write", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Confluence page id" - }, - { - "name": "file", - "type": "string", - "required": true, - "help": "Markdown file path" - }, - { - "name": "title", - "type": "string", - "required": false, - "help": "Optional replacement title; defaults to current title" - }, - { - "name": "version-message", - "type": "string", - "required": false, - "help": "Confluence version message" - }, - { - "name": "representation", - "type": "string", - "default": "markdown", - "required": false, - "help": "Input file format", - "choices": [ - "markdown", - "storage" - ] - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually update the remote page" - } - ], - "columns": [ - "status", - "id", - "title", - "spaceId", - "spaceKey", - "version", - "url" - ], - "type": "js", - "modulePath": "confluence/update.js", - "sourceFile": "confluence/update.js" - }, - { - "site": "coupang", - "name": "add-to-cart", - "description": "Add a Coupang product to cart using logged-in browser session", - "access": "write", - "domain": "www.coupang.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "product-id", - "type": "str", - "required": false, - "positional": true, - "help": "Coupang product ID" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Canonical product URL" - } - ], - "columns": [ - "ok", - "product_id", - "url", - "message" - ], - "type": "js", - "modulePath": "coupang/add-to-cart.js", - "sourceFile": "coupang/add-to-cart.js", - "navigateBefore": "https://www.coupang.com" - }, - { - "site": "coupang", - "name": "login", - "description": "Open coupang login", - "access": "write", - "domain": "coupang.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "coupang/auth.js", - "sourceFile": "coupang/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "coupang", - "name": "product", - "description": "Read full product detail (price, rating, seller, delivery) for a Coupang product", - "access": "read", - "domain": "www.coupang.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "product-id", - "type": "str", - "required": false, - "positional": true, - "help": "Coupang product ID (digits only)" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Canonical Coupang product URL (alternative to --product-id)" - } - ], - "columns": [ - "product_id", - "title", - "price", - "original_price", - "discount_rate", - "rating", - "review_count", - "seller", - "brand", - "rocket", - "delivery_promise", - "image_url", - "url" - ], - "type": "js", - "modulePath": "coupang/product.js", - "sourceFile": "coupang/product.js", - "navigateBefore": "https://www.coupang.com" - }, - { - "site": "coupang", - "name": "search", - "description": "Search Coupang products with logged-in browser session", - "access": "read", - "domain": "www.coupang.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Search result page number" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (max 50)" - }, - { - "name": "filter", - "type": "str", - "required": false, - "help": "Optional search filter (currently supports: rocket)" - } - ], - "columns": [ - "rank", - "product_id", - "title", - "price", - "unit_price", - "rating", - "review_count", - "rocket", - "delivery_type", - "delivery_promise", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "coupang/search.js", - "sourceFile": "coupang/search.js", - "navigateBefore": "https://www.coupang.com" - }, - { - "site": "coupang", - "name": "whoami", - "description": "Show the current logged-in coupang account", - "access": "read", - "domain": "coupang.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name" - ], - "type": "js", - "modulePath": "coupang/auth.js", - "sourceFile": "coupang/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "crates", - "name": "crate", - "description": "Single crates.io crate metadata (latest version, downloads, license, repo)", - "access": "read", - "domain": "crates.io", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "crates.io crate name (e.g. \"serde\", \"tokio\")" - } - ], - "columns": [ - "name", - "latestVersion", - "description", - "downloads", - "recentDownloads", - "versions", - "license", - "homepage", - "documentation", - "repository", - "keywords", - "categories", - "created", - "updated", - "url" - ], - "type": "js", - "modulePath": "crates/crate.js", - "sourceFile": "crates/crate.js" - }, - { - "site": "crates", - "name": "search", - "description": "Search the public crates.io registry by keyword", - "access": "read", - "domain": "crates.io", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"serde\", \"async runtime\")" + "help": "Limit search to a Confluence space key" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max results (1-100)" + "help": "Max results to return (1-100)" } ], "columns": [ - "rank", - "name", - "latestVersion", - "description", - "downloads", - "recentDownloads", - "repository", - "updated", + "id", + "title", + "type", + "spaceKey", + "status", + "lastModified", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "crates/search.js", - "sourceFile": "crates/search.js" + "modulePath": "confluence/search.js", + "sourceFile": "confluence/search.js" }, { - "site": "cursor", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", + "site": "confluence", + "name": "update", + "description": "Update a Confluence page body from Markdown or storage XHTML", "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "text", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Prompt to send" + "help": "Confluence page id" }, { - "name": "timeout", - "type": "int", - "default": 30, - "required": false, - "help": "Max seconds to wait for response (default: 30)" - } - ], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "cursor/ask.js", - "sourceFile": "cursor/ask.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "composer", - "description": "Send a prompt directly into Cursor Composer (Cmd+I shortcut)", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", + "name": "file", + "type": "string", "required": true, - "positional": true, - "help": "Text to send into Composer" - } - ], - "columns": [ - "Status", - "InjectedText" - ], - "type": "js", - "modulePath": "cursor/composer.js", - "sourceFile": "cursor/composer.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "dump", - "description": "Dump the DOM and Accessibility tree of cursor for reverse-engineering", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "action", - "files" - ], - "type": "js", - "modulePath": "cursor/dump.js", - "sourceFile": "cursor/dump.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "export", - "description": "Export the current cursor conversation to a Markdown file", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Markdown file path" + }, { - "name": "output", - "type": "str", + "name": "title", + "type": "string", "required": false, - "help": "Output file (default: /tmp/cursor-export.md)" - } - ], - "columns": [ - "Status", - "File", - "Messages" - ], - "type": "js", - "modulePath": "cursor/export.js", - "sourceFile": "cursor/export.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "extract-code", - "description": "Extract multi-line code blocks from the current Cursor conversation", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Code" - ], - "type": "js", - "modulePath": "cursor/extract-code.js", - "sourceFile": "cursor/extract-code.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "history", - "description": "List recent chat sessions from the Cursor sidebar", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Title" - ], - "type": "js", - "modulePath": "cursor/history.js", - "sourceFile": "cursor/history.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "model", - "description": "Get or switch the currently active AI model in Cursor", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Optional replacement title; defaults to current title" + }, { - "name": "model-name", - "type": "str", + "name": "version-message", + "type": "string", "required": false, - "positional": true, - "help": "The ID of the model to switch to (e.g. claude-3.5-sonnet)" - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "cursor/model.js", - "sourceFile": "cursor/model.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "new", - "description": "Start a new Cursor chat or Composer session", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "cursor/new.js", - "sourceFile": "cursor/new.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "read", - "description": "Read the current Cursor chat/composer conversation history", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "cursor/read.js", - "sourceFile": "cursor/read.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "screenshot", - "description": "Capture a snapshot of the current cursor window (DOM + Accessibility tree)", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Confluence version message" + }, + { + "name": "representation", + "type": "string", + "default": "markdown", + "required": false, + "help": "Input file format", + "choices": [ + "markdown", + "storage" + ] + }, { - "name": "output", - "type": "str", + "name": "execute", + "type": "boolean", "required": false, - "help": "Output file path (default: /tmp/cursor-snapshot.txt)" + "help": "Actually update the remote page" } ], "columns": [ - "Status", - "File" + "status", + "id", + "title", + "spaceId", + "spaceKey", + "version", + "url" ], "type": "js", - "modulePath": "cursor/screenshot.js", - "sourceFile": "cursor/screenshot.js", - "navigateBefore": true + "modulePath": "confluence/update.js", + "sourceFile": "confluence/update.js" }, { - "site": "cursor", - "name": "send", - "description": "Send a prompt directly into Cursor Composer/Chat", + "site": "coupang", + "name": "add-to-cart", + "description": "Add a Coupang product to cart using logged-in browser session", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "www.coupang.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "text", + "name": "product-id", "type": "str", - "required": true, + "required": false, "positional": true, - "help": "Text to send into Cursor" + "help": "Coupang product ID" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Canonical product URL" } ], "columns": [ - "Status", - "InjectedText" + "ok", + "product_id", + "url", + "message" ], "type": "js", - "modulePath": "cursor/send.js", - "sourceFile": "cursor/send.js", - "navigateBefore": true + "modulePath": "coupang/add-to-cart.js", + "sourceFile": "coupang/add-to-cart.js", + "navigateBefore": "https://www.coupang.com" }, { - "site": "cursor", - "name": "status", - "description": "Check active CDP connection to Cursor AI Editor", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "coupang", + "name": "login", + "description": "Open coupang login", + "access": "write", + "domain": "coupang.com", + "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Status", - "Url", - "Title" + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" ], "type": "js", - "modulePath": "cursor/status.js", - "sourceFile": "cursor/status.js", - "navigateBefore": true + "modulePath": "coupang/auth.js", + "sourceFile": "coupang/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "dblp", - "name": "author", - "description": "List dblp publications by a given author (newest first; resolves to top PID match)", + "site": "coupang", + "name": "product", + "description": "Read full product detail (price, rating, seller, delivery) for a Coupang product", "access": "read", - "domain": "dblp.org", - "strategy": "public", - "browser": false, + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "author", + "name": "product-id", "type": "str", "required": false, "positional": true, - "help": "Author name (e.g. \"Yoshua Bengio\"). Optional when --pid is given." + "help": "Coupang product ID (digits only)" }, { - "name": "pid", + "name": "url", "type": "str", "required": false, - "help": "Canonical dblp PID (e.g. \"56/953\"). Bypasses author search." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max publications (1-200)" + "help": "Canonical Coupang product URL (alternative to --product-id)" } ], "columns": [ - "rank", - "key", + "product_id", "title", - "authors", - "venue", - "year", - "type", - "doi", - "pid", + "price", + "original_price", + "discount_rate", + "rating", + "review_count", + "seller", + "brand", + "rocket", + "delivery_promise", + "image_url", "url" ], "type": "js", - "modulePath": "dblp/author.js", - "sourceFile": "dblp/author.js" - }, - { - "site": "dblp", - "name": "paper", - "aliases": [ - "detail", - "view" - ], - "description": "Fetch a dblp record by canonical key (e.g. conf/nips/VaswaniSPUJGKP17)", - "access": "read", - "domain": "dblp.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "dblp record key (round-tripped from the `key` column of `dblp search`)" - } - ], - "columns": [ - "key", - "type", - "title", - "authors", - "venue", - "year", - "pages", - "doi", - "open_access_url", - "dblp_url" - ], - "type": "js", - "modulePath": "dblp/paper.js", - "sourceFile": "dblp/paper.js" + "modulePath": "coupang/product.js", + "sourceFile": "coupang/product.js", + "navigateBefore": "https://www.coupang.com" }, { - "site": "dblp", + "site": "coupang", "name": "search", - "description": "Search dblp computer-science bibliography by free-text query", + "description": "Search Coupang products with logged-in browser session", "access": "read", - "domain": "dblp.org", - "strategy": "public", - "browser": false, + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "query", "type": "str", "required": true, "positional": true, - "help": "Search keyword (title / author / venue, e.g. \"attention is all you need\")" + "help": "Search keyword" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Search result page number" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max results (1-100, single dblp page)" + "help": "Max results (max 50)" + }, + { + "name": "filter", + "type": "str", + "required": false, + "help": "Optional search filter (currently supports: rocket)" } ], "columns": [ "rank", - "key", + "product_id", "title", - "authors", - "venue", - "year", - "type", - "doi", + "price", + "unit_price", + "rating", + "review_count", + "rocket", + "delivery_type", + "delivery_promise", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "dblp/search.js", - "sourceFile": "dblp/search.js" + "modulePath": "coupang/search.js", + "sourceFile": "coupang/search.js", + "navigateBefore": "https://www.coupang.com" }, { - "site": "dblp", - "name": "venue", - "description": "Search dblp venue registry (conferences / journals) by name or acronym", + "site": "coupang", + "name": "whoami", + "description": "Show the current logged-in coupang account", "access": "read", - "domain": "dblp.org", - "strategy": "public", - "browser": false, + "domain": "coupang.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name" + ], + "type": "js", + "modulePath": "coupang/auth.js", + "sourceFile": "coupang/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "cursor", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", + "name": "text", "type": "str", "required": true, "positional": true, - "help": "Venue name or acronym (e.g. \"ICLR\", \"neural networks\")" + "help": "Prompt to send" }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 20, + "default": 30, "required": false, - "help": "Max venues (1-100, single dblp page)" + "help": "Max seconds to wait for response (default: 30)" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "cursor/ask.js", + "sourceFile": "cursor/ask.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "composer", + "description": "Send a prompt directly into Cursor Composer (Cmd+I shortcut)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Text to send into Composer" } ], "columns": [ - "rank", - "acronym", - "venue", - "type", - "url" + "Status", + "InjectedText" ], "type": "js", - "modulePath": "dblp/venue.js", - "sourceFile": "dblp/venue.js" + "modulePath": "cursor/composer.js", + "sourceFile": "cursor/composer.js", + "navigateBefore": true }, { - "site": "defillama", - "name": "protocol", - "description": "Single DefiLlama protocol details (current TVL, mcap, chains, twitter, github, description)", + "site": "cursor", + "name": "dump", + "description": "Dump the DOM and Accessibility tree of cursor for reverse-engineering", "access": "read", - "domain": "defillama.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "slug", - "type": "string", - "required": true, - "positional": true, - "help": "DefiLlama protocol slug (e.g. \"aave\", \"lido\")" - } - ], + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "slug", - "name", - "category", - "isParent", - "tvl", - "tvlAt", - "mcap", - "chains", - "twitter", - "github", - "audits", - "listedAt", - "description", - "website", - "url" + "action", + "files" ], "type": "js", - "modulePath": "defillama/protocol.js", - "sourceFile": "defillama/protocol.js" + "modulePath": "cursor/dump.js", + "sourceFile": "cursor/dump.js", + "navigateBefore": true }, { - "site": "defillama", - "name": "protocols", - "description": "Top DeFi protocols on DefiLlama by current TVL (slug, name, category, TVL, mcap, change_1d/7d, chains)", + "site": "cursor", + "name": "export", + "description": "Export the current cursor conversation to a Markdown file", "access": "read", - "domain": "defillama.com", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 30, + "name": "output", + "type": "str", "required": false, - "help": "Number of rows to return (1-500)" + "help": "Output file (default: /tmp/cursor-export.md)" } ], "columns": [ - "rank", - "slug", - "name", - "category", - "tvl", - "mcap", - "change_1d", - "change_7d", - "chains", - "listedAt", - "url" + "Status", + "File", + "Messages" ], "type": "js", - "modulePath": "defillama/protocols.js", - "sourceFile": "defillama/protocols.js" + "modulePath": "cursor/export.js", + "sourceFile": "cursor/export.js", + "navigateBefore": true }, { - "site": "devto", - "name": "latest", - "description": "Newest dev.to articles (firehose, all tags)", + "site": "cursor", + "name": "extract-code", + "description": "Extract multi-line code blocks from the current Cursor conversation", "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Articles per page (1-100)" - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number (1-based)" - } - ], + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "rank", - "id", - "title", - "author", - "tags", - "reactions", - "comments", - "published", - "url" + "Code" ], "type": "js", - "modulePath": "devto/latest.js", - "sourceFile": "devto/latest.js" + "modulePath": "cursor/extract-code.js", + "sourceFile": "cursor/extract-code.js", + "navigateBefore": true }, { - "site": "devto", - "name": "read", - "description": "Read a DEV.to article body by id", + "site": "cursor", + "name": "history", + "description": "List recent chat sessions from the Cursor sidebar", "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "DEV.to article id (numeric, e.g. 3605688)" - }, - { - "name": "max-length", - "type": "int", - "default": 20000, - "required": false, - "help": "Max characters of body to return (min 100)" - } - ], + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "id", - "title", - "author", - "reactions", - "reading_time", - "tags", - "published_at", - "body", - "url" + "Index", + "Title" ], "type": "js", - "modulePath": "devto/read.js", - "sourceFile": "devto/read.js" + "modulePath": "cursor/history.js", + "sourceFile": "cursor/history.js", + "navigateBefore": true }, { - "site": "devto", - "name": "tag", - "description": "Latest DEV.to articles for a specific tag", + "site": "cursor", + "name": "model", + "description": "Get or switch the currently active AI model in Cursor", "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "tag", + "name": "model-name", "type": "str", - "required": true, - "positional": true, - "help": "Tag name (e.g. javascript, python, webdev)" - }, - { - "name": "limit", - "type": "int", - "default": 20, "required": false, - "help": "Number of articles" + "positional": true, + "help": "The ID of the model to switch to (e.g. claude-3.5-sonnet)" } ], "columns": [ - "rank", - "id", - "title", - "author", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", - "url" + "Status", + "Model" ], "type": "js", - "modulePath": "devto/tag.js", - "sourceFile": "devto/tag.js" + "modulePath": "cursor/model.js", + "sourceFile": "cursor/model.js", + "navigateBefore": true }, { - "site": "devto", - "name": "top", - "description": "Top DEV.to articles of the day", - "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of articles" - } - ], + "site": "cursor", + "name": "new", + "description": "Start a new Cursor chat or Composer session", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "rank", - "id", - "title", - "author", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", - "url" + "Status" ], "type": "js", - "modulePath": "devto/top.js", - "sourceFile": "devto/top.js" + "modulePath": "cursor/new.js", + "sourceFile": "cursor/new.js", + "navigateBefore": true }, { - "site": "devto", - "name": "user", - "description": "Recent DEV.to articles from a specific user", + "site": "cursor", + "name": "read", + "description": "Read the current Cursor chat/composer conversation history", "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "DEV.to username (e.g. ben, thepracticaldev)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of articles" - } - ], + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "rank", - "id", - "title", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", - "url" + "Role", + "Text" ], "type": "js", - "modulePath": "devto/user.js", - "sourceFile": "devto/user.js" + "modulePath": "cursor/read.js", + "sourceFile": "cursor/read.js", + "navigateBefore": true }, { - "site": "dictionary", - "name": "examples", - "description": "Read real-world example sentences utilizing the word", + "site": "cursor", + "name": "screenshot", + "description": "Capture a snapshot of the current cursor window (DOM + Accessibility tree)", "access": "read", - "domain": "api.dictionaryapi.dev", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "word", - "type": "string", - "required": true, - "positional": true, - "help": "Word to get example sentences for" + "name": "output", + "type": "str", + "required": false, + "help": "Output file path (default: /tmp/cursor-snapshot.txt)" } ], "columns": [ - "word", - "example" + "Status", + "File" ], "type": "js", - "modulePath": "dictionary/examples.js", - "sourceFile": "dictionary/examples.js" - }, - { - "site": "dictionary", - "name": "search", - "description": "Search the Free Dictionary API for definitions, parts of speech, and pronunciations.", - "access": "read", - "domain": "api.dictionaryapi.dev", - "strategy": "public", - "browser": false, + "modulePath": "cursor/screenshot.js", + "sourceFile": "cursor/screenshot.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "send", + "description": "Send a prompt directly into Cursor Composer/Chat", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "word", - "type": "string", + "name": "text", + "type": "str", "required": true, "positional": true, - "help": "Word to define (e.g., serendipity)" + "help": "Text to send into Cursor" } ], "columns": [ - "word", - "phonetic", - "type", - "definition" - ], - "tags": [ - "search" + "Status", + "InjectedText" ], "type": "js", - "modulePath": "dictionary/search.js", - "sourceFile": "dictionary/search.js" + "modulePath": "cursor/send.js", + "sourceFile": "cursor/send.js", + "navigateBefore": true }, { - "site": "dictionary", - "name": "synonyms", - "description": "Find synonyms for a specific word", + "site": "cursor", + "name": "status", + "description": "Check active CDP connection to Cursor AI Editor", "access": "read", - "domain": "api.dictionaryapi.dev", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "word", - "type": "string", - "required": true, - "positional": true, - "help": "Word to find synonyms for (e.g., serendipity)" - } - ], + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "word", - "synonyms" + "Status", + "Url", + "Title" ], "type": "js", - "modulePath": "dictionary/synonyms.js", - "sourceFile": "dictionary/synonyms.js" + "modulePath": "cursor/status.js", + "sourceFile": "cursor/status.js", + "navigateBefore": true }, { "site": "discord-app", @@ -8306,79 +6351,6 @@ "navigateBefore": false, "siteSession": "persistent" }, - { - "site": "dockerhub", - "name": "image", - "description": "Fetch a Docker Hub repository's public metadata (stars, pulls, last updated, status)", - "access": "read", - "domain": "hub.docker.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "image", - "type": "str", - "required": true, - "positional": true, - "help": "Image name (e.g. \"nginx\", \"library/nginx\", \"bitnami/redis\")" - } - ], - "columns": [ - "image", - "official", - "stars", - "pulls", - "description", - "lastUpdated", - "lastModified", - "registered", - "status", - "url" - ], - "type": "js", - "modulePath": "dockerhub/image.js", - "sourceFile": "dockerhub/image.js" - }, - { - "site": "dockerhub", - "name": "search", - "description": "Search Docker Hub repositories by keyword", - "access": "read", - "domain": "hub.docker.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"nginx\", \"bitnami redis\")" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max repositories (1-100, single Docker Hub page)" - } - ], - "columns": [ - "rank", - "image", - "official", - "stars", - "pulls", - "description", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "dockerhub/search.js", - "sourceFile": "dockerhub/search.js" - }, { "site": "duckduckgo", "name": "search", @@ -8469,40 +6441,6 @@ "modulePath": "duckduckgo/suggest.js", "sourceFile": "duckduckgo/suggest.js" }, - { - "site": "endoflife", - "name": "product", - "description": "Release cycles + EOL / LTS / support dates for one product on endoflife.date", - "access": "read", - "domain": "endoflife.date", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "product", - "type": "string", - "required": true, - "positional": true, - "help": "endoflife.date product slug (e.g. \"nodejs\", \"python\", \"ubuntu\")" - } - ], - "columns": [ - "product", - "cycle", - "releaseDate", - "latest", - "latestReleaseDate", - "lts", - "support", - "eol", - "extendedSupport", - "eolStatus", - "url" - ], - "type": "js", - "modulePath": "endoflife/product.js", - "sourceFile": "endoflife/product.js" - }, { "site": "facebook", "name": "add-friend", @@ -8900,88 +6838,6 @@ "navigateBefore": false, "siteSession": "persistent" }, - { - "site": "flathub", - "name": "app", - "description": "Full Flathub appstream metadata for an app id (license, categories, latest release)", - "access": "read", - "domain": "flathub.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "appId", - "type": "str", - "required": true, - "positional": true, - "help": "AppStream id (e.g. \"org.mozilla.firefox\", \"org.gnome.Calculator\")" - } - ], - "columns": [ - "appId", - "name", - "summary", - "developer", - "license", - "isFreeLicense", - "isEol", - "categories", - "keywords", - "latestVersion", - "latestReleaseDate", - "homepage", - "bugtracker", - "donation", - "url" - ], - "type": "js", - "modulePath": "flathub/app.js", - "sourceFile": "flathub/app.js" - }, - { - "site": "flathub", - "name": "search", - "description": "Search Flathub apps by keyword", - "access": "read", - "domain": "flathub.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max apps (1-100)" - } - ], - "columns": [ - "rank", - "appId", - "name", - "summary", - "developer", - "license", - "isFreeLicense", - "mainCategories", - "installsLastMonth", - "updatedAt", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "flathub/search.js", - "sourceFile": "flathub/search.js" - }, { "site": "gemini", "name": "ask", @@ -9696,51 +7552,6 @@ "navigateBefore": false, "siteSession": "persistent" }, - { - "site": "github-trending", - "name": "repos", - "description": "GitHub Trending repositories (public, no login). Filter by --language and --since.", - "access": "read", - "domain": "github.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "since", - "type": "string", - "default": "daily", - "required": false, - "help": "Time range: daily / weekly / monthly" - }, - { - "name": "language", - "type": "string", - "default": "", - "required": false, - "help": "Filter by programming language slug, e.g. python, rust, \"c++\"" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Number of repositories to return (max 25)" - } - ], - "columns": [ - "rank", - "repo", - "description", - "language", - "stars", - "forks", - "starsSince", - "url" - ], - "type": "js", - "modulePath": "github-trending/repos.js", - "sourceFile": "github-trending/repos.js" - }, { "site": "google", "name": "images", @@ -10060,86 +7871,12 @@ "cited", "url" ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "google-scholar/search.js", - "sourceFile": "google-scholar/search.js" - }, - { - "site": "goproxy", - "name": "module", - "description": "Latest version + VCS origin metadata for a Go module on proxy.golang.org", - "access": "read", - "domain": "proxy.golang.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "module", - "type": "string", - "required": true, - "positional": true, - "help": "Go module path (e.g. \"github.com/gin-gonic/gin\", \"golang.org/x/net\")" - } - ], - "columns": [ - "module", - "version", - "publishedAt", - "vcs", - "repository", - "commit", - "ref", - "pkgGoDevUrl", - "url" - ], - "type": "js", - "modulePath": "goproxy/module.js", - "sourceFile": "goproxy/module.js" - }, - { - "site": "goproxy", - "name": "versions", - "description": "Published version tags for a Go module (newest first), optionally with publish times", - "access": "read", - "domain": "proxy.golang.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "module", - "type": "string", - "required": true, - "positional": true, - "help": "Go module path (e.g. \"github.com/gin-gonic/gin\")" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max rows to return (1-200)" - }, - { - "name": "with-time", - "type": "boolean", - "default": false, - "required": false, - "help": "Fetch each version's publish time (one extra request per row)" - } - ], - "columns": [ - "rank", - "module", - "version", - "publishedAt", - "url" + "tags": [ + "search" ], "type": "js", - "modulePath": "goproxy/versions.js", - "sourceFile": "goproxy/versions.js" + "modulePath": "google-scholar/search.js", + "sourceFile": "google-scholar/search.js" }, { "site": "grok", @@ -10471,501 +8208,190 @@ "site", "user_id", "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "grok/auth.js", - "sourceFile": "grok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "new", - "description": "Start a new conversation in Grok", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "grok/new.js", - "sourceFile": "grok/new.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "pin", - "description": "Pin a Grok conversation by ID", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" - } - ], - "columns": [ - "status", - "id" - ], - "type": "js", - "modulePath": "grok/pin.js", - "sourceFile": "grok/pin.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "read", - "description": "Read messages in the current Grok conversation", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" - } - ], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "grok/read.js", - "sourceFile": "grok/read.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "send", - "description": "Fire-and-forget: send a prompt to Grok without waiting for the reply", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send to Grok" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - } - ], - "columns": [ - "Status", - "Prompt" - ], - "type": "js", - "modulePath": "grok/send.js", - "sourceFile": "grok/send.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "status", - "description": "Check Grok page availability, login state, current session and model", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status", - "Login", - "Model", - "SessionId", - "Url" - ], - "type": "js", - "modulePath": "grok/status.js", - "sourceFile": "grok/status.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "unpin", - "description": "Unpin a Grok conversation by ID", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" - } - ], - "columns": [ - "status", - "id" - ], - "type": "js", - "modulePath": "grok/pin.js", - "sourceFile": "grok/pin.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "whoami", - "description": "Show the current logged-in grok account", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "grok/auth.js", - "sourceFile": "grok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "hackernews", - "name": "ask", - "description": "Hacker News Ask HN posts", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" - ], - "type": "js", - "modulePath": "hackernews/ask.js", - "sourceFile": "hackernews/ask.js" - }, - { - "site": "hackernews", - "name": "best", - "description": "Hacker News best stories", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" - ], - "type": "js", - "modulePath": "hackernews/best.js", - "sourceFile": "hackernews/best.js" - }, - { - "site": "hackernews", - "name": "jobs", - "description": "Hacker News job postings", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of job postings" - } - ], - "columns": [ - "rank", - "id", - "title", - "author", - "url" + "action", + "verify_command" ], "type": "js", - "modulePath": "hackernews/jobs.js", - "sourceFile": "hackernews/jobs.js" + "modulePath": "grok/auth.js", + "sourceFile": "grok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "hackernews", + "site": "grok", "name": "new", - "description": "Hacker News newest stories", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, + "description": "Start a new conversation in Grok", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "grok/new.js", + "sourceFile": "grok/new.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "pin", + "description": "Pin a Grok conversation by ID", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID or grok.com/c/ URL" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" + "status", + "id" ], "type": "js", - "modulePath": "hackernews/new.js", - "sourceFile": "hackernews/new.js" + "modulePath": "grok/pin.js", + "sourceFile": "grok/pin.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" }, { - "site": "hackernews", + "site": "grok", "name": "read", - "description": "Read a Hacker News story and its comment tree", + "description": "Read messages in the current Grok conversation", "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, + "domain": "grok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "HN item ID (e.g. 39847301)" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max top-level comments" - }, - { - "name": "depth", - "type": "int", - "default": 2, - "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" - }, - { - "name": "replies", - "type": "int", - "default": 5, - "required": false, - "help": "Max replies shown per comment at each level" - }, - { - "name": "max-length", - "type": "int", - "default": 2000, + "name": "markdown", + "type": "boolean", + "default": false, "required": false, - "help": "Max characters per comment body (min 100)" + "help": "Emit assistant replies as markdown" } ], "columns": [ - "type", - "author", - "score", - "text" + "Role", + "Text" ], "type": "js", - "modulePath": "hackernews/read.js", - "sourceFile": "hackernews/read.js" + "modulePath": "grok/read.js", + "sourceFile": "grok/read.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "hackernews", - "name": "search", - "description": "Search Hacker News stories", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, + "site": "grok", + "name": "send", + "description": "Fire-and-forget: send a prompt to Grok without waiting for the reply", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "prompt", "type": "str", "required": true, "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" + "help": "Prompt to send to Grok" }, { - "name": "sort", - "type": "str", - "default": "relevance", + "name": "new", + "type": "boolean", + "default": false, "required": false, - "help": "Sort by relevance or date", - "choices": [ - "relevance", - "date" - ] + "help": "Start a new chat before sending" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" - ], - "tags": [ - "search" + "Status", + "Prompt" ], "type": "js", - "modulePath": "hackernews/search.js", - "sourceFile": "hackernews/search.js" + "modulePath": "grok/send.js", + "sourceFile": "grok/send.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "hackernews", - "name": "show", - "description": "Hacker News Show HN posts", + "site": "grok", + "name": "status", + "description": "Check Grok page availability, login state, current session and model", "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" + "Status", + "Login", + "Model", + "SessionId", + "Url" ], "type": "js", - "modulePath": "hackernews/show.js", - "sourceFile": "hackernews/show.js" + "modulePath": "grok/status.js", + "sourceFile": "grok/status.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "hackernews", - "name": "top", - "description": "Hacker News top stories", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, + "site": "grok", + "name": "unpin", + "description": "Unpin a Grok conversation by ID", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID or grok.com/c/ URL" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" + "status", + "id" ], "type": "js", - "modulePath": "hackernews/top.js", - "sourceFile": "hackernews/top.js" + "modulePath": "grok/pin.js", + "sourceFile": "grok/pin.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" }, { - "site": "hackernews", - "name": "user", - "description": "Hacker News user profile", + "site": "grok", + "name": "whoami", + "description": "Show the current logged-in grok account", "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "HN username" - } - ], + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "username", - "karma", - "created", - "about" + "logged_in", + "site", + "user_id", + "name" ], "type": "js", - "modulePath": "hackernews/user.js", - "sourceFile": "hackernews/user.js" + "modulePath": "grok/auth.js", + "sourceFile": "grok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "hf", diff --git a/plugin-command-manifest.json b/plugin-command-manifest.json index fcf68785..464c5bc6 100644 --- a/plugin-command-manifest.json +++ b/plugin-command-manifest.json @@ -1,70 +1,2792 @@ [ { - "site": "bmwblog", - "name": "article", - "description": "Read a BMWBLOG article by URL or slug", + "site": "apple-podcasts", + "name": "episodes", + "description": "List recent episodes of an Apple Podcast (use ID from search)", "access": "read", - "domain": "www.bmwblog.com", "strategy": "public", "browser": false, "args": [ { - "name": "url-or-slug", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "BMWBLOG article URL or slug" + "help": "Podcast ID (collectionId from search output)" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Max episodes to show" } ], "columns": [ "title", - "date", + "duration", + "date" + ], + "type": "js", + "modulePath": "plugins/apple-podcasts/episodes.js", + "sourceFile": "plugins/apple-podcasts/episodes.js" + }, + { + "site": "apple-podcasts", + "name": "search", + "description": "Search Apple Podcasts", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results" + } + ], + "columns": [ + "id", + "title", "author", - "sections", - "excerpt", - "url", - "content" + "episodes", + "genre", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/bmwblog/article.js", - "sourceFile": "plugins/bmwblog/article.js" + "modulePath": "plugins/apple-podcasts/search.js", + "sourceFile": "plugins/apple-podcasts/search.js" }, { - "site": "bmwblog", - "name": "latest", - "description": "List the latest BMWBLOG articles", + "site": "apple-podcasts", + "name": "top", + "description": "Top podcasts chart on Apple Podcasts", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of podcasts (max 100)" + }, + { + "name": "country", + "type": "str", + "default": "us", + "required": false, + "help": "Country code (e.g. us, cn, gb, jp)" + } + ], + "columns": [ + "rank", + "title", + "author", + "id" + ], + "type": "js", + "modulePath": "plugins/apple-podcasts/top.js", + "sourceFile": "plugins/apple-podcasts/top.js" + }, + { + "site": "archive", + "name": "item", + "description": "Fetch metadata for a single Internet Archive item by identifier.", + "access": "read", + "domain": "archive.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "identifier", + "type": "str", + "required": true, + "positional": true, + "help": "Archive item identifier (e.g. \"open-syllabus\", \"FinalFantasy2_356\")." + } + ], + "columns": [ + "identifier", + "title", + "creator", + "date", + "mediatype", + "collection", + "description", + "file_count", + "url" + ], + "type": "js", + "modulePath": "plugins/archive/item.js", + "sourceFile": "plugins/archive/item.js" + }, + { + "site": "archive", + "name": "search", + "description": "Search Internet Archive items across books, movies, audio, software, and web.", + "access": "read", + "domain": "archive.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Full-text query (matches title, description, creator, subject)." + }, + { + "name": "mediatype", + "type": "string", + "required": false, + "help": "Restrict to mediatype: texts, movies, audio, software, image, web, data, collection" + }, + { + "name": "sort", + "type": "string", + "default": "downloads", + "required": false, + "help": "Sort key: downloads, date, addeddate, week, title" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max items (max 100; one API page)." + } + ], + "columns": [ + "rank", + "identifier", + "title", + "creator", + "date", + "mediatype", + "downloads", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/archive/search.js", + "sourceFile": "plugins/archive/search.js" + }, + { + "site": "archive", + "name": "snapshots", + "description": "List Wayback Machine snapshots over time for a URL via the CDX API.", + "access": "read", + "domain": "archive.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "URL to look up (with or without scheme)." + }, + { + "name": "from", + "type": "string", + "required": false, + "help": "Earliest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" + }, + { + "name": "to", + "type": "string", + "required": false, + "help": "Latest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max snapshots to return (max 1000)." + } + ], + "columns": [ + "timestamp", + "snapshot_url", + "status", + "mimetype", + "original_url" + ], + "type": "js", + "modulePath": "plugins/archive/snapshots.js", + "sourceFile": "plugins/archive/snapshots.js" + }, + { + "site": "archive", + "name": "wayback", + "description": "Look up the closest Wayback Machine snapshot for a URL.", + "access": "read", + "domain": "archive.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "URL to look up (with or without scheme)." + }, + { + "name": "timestamp", + "type": "string", + "required": false, + "help": "Target timestamp (YYYY[MM[DD[hh[mm[ss]]]]] or ISO date). Defaults to most recent snapshot." + } + ], + "columns": [ + "original_url", + "requested_timestamp", + "snapshot_timestamp", + "snapshot_url", + "status" + ], + "type": "js", + "modulePath": "plugins/archive/wayback.js", + "sourceFile": "plugins/archive/wayback.js" + }, + { + "site": "arxiv", + "name": "author", + "description": "List arXiv papers by a given author (newest first)", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "author", + "type": "str", + "required": true, + "positional": true, + "help": "Author name (e.g. \"Yoshua Bengio\" or \"Y Bengio\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max papers to return (max 50)" + } + ], + "columns": [ + "id", + "title", + "authors", + "published", + "primary_category", + "url" + ], + "type": "js", + "modulePath": "plugins/arxiv/author.js", + "sourceFile": "plugins/arxiv/author.js" + }, + { + "site": "arxiv", + "name": "paper", + "description": "Get arXiv paper details by ID", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "arXiv paper ID (e.g. 1706.03762)" + } + ], + "columns": [ + "id", + "title", + "authors", + "published", + "updated", + "primary_category", + "categories", + "abstract", + "comment", + "pdf", + "url" + ], + "type": "js", + "modulePath": "plugins/arxiv/paper.js", + "sourceFile": "plugins/arxiv/paper.js" + }, + { + "site": "arxiv", + "name": "recent", + "description": "List recent arXiv submissions in a category", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "category", + "type": "str", + "required": true, + "positional": true, + "help": "arXiv category (e.g. cs.CL, cs.LG, math.PR, q-bio.NC)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results (max 50)" + } + ], + "columns": [ + "id", + "title", + "authors", + "published", + "primary_category", + "url" + ], + "type": "js", + "modulePath": "plugins/arxiv/recent.js", + "sourceFile": "plugins/arxiv/recent.js" + }, + { + "site": "arxiv", + "name": "search", + "description": "Search arXiv papers", "access": "read", - "domain": "www.bmwblog.com", "strategy": "public", "browser": false, "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"attention is all you need\")" + }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Number of articles (1-50)" + "help": "Max results (max 25)" + } + ], + "columns": [ + "id", + "title", + "authors", + "published", + "primary_category", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/arxiv/search.js", + "sourceFile": "plugins/arxiv/search.js" + }, + { + "site": "bbc", + "name": "news", + "description": "BBC News headlines (RSS)", + "access": "read", + "domain": "www.bbc.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of headlines (max 50)" + } + ], + "columns": [ + "rank", + "title", + "description", + "url" + ], + "type": "js", + "modulePath": "plugins/bbc/news.js", + "sourceFile": "plugins/bbc/news.js" + }, + { + "site": "bbc", + "name": "topic", + "description": "BBC News headlines for a specific section (RSS feed)", + "access": "read", + "domain": "www.bbc.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "topic", + "type": "str", + "required": true, + "positional": true, + "help": "Section name (world / business / politics / health / education / science_and_environment / technology / entertainment_and_arts)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max headlines (1-50)" + } + ], + "columns": [ + "rank", + "title", + "description", + "pubDate", + "url" + ], + "type": "js", + "modulePath": "plugins/bbc/topic.js", + "sourceFile": "plugins/bbc/topic.js" + }, + { + "site": "binance", + "name": "asks", + "description": "Order book ask prices for a trading pair", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of price levels (5, 10, 20, 50, 100)" + } + ], + "columns": [ + "rank", + "ask_price", + "ask_qty" + ], + "type": "js", + "modulePath": "plugins/binance/asks.js", + "sourceFile": "plugins/binance/asks.js" + }, + { + "site": "binance", + "name": "depth", + "description": "Order book bid and ask prices for a trading pair", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of price levels (5, 10, 20, 50, 100)" + } + ], + "columns": [ + "rank", + "bid_price", + "bid_qty", + "ask_price", + "ask_qty" + ], + "type": "js", + "modulePath": "plugins/binance/depth.js", + "sourceFile": "plugins/binance/depth.js" + }, + { + "site": "binance", + "name": "gainers", + "description": "Top gaining trading pairs by 24h price change", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of trading pairs" + } + ], + "columns": [ + "rank", + "symbol", + "price", + "change_24h", + "volume" + ], + "type": "js", + "modulePath": "plugins/binance/gainers.js", + "sourceFile": "plugins/binance/gainers.js" + }, + { + "site": "binance", + "name": "klines", + "description": "Candlestick/kline data for a trading pair", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, + { + "name": "interval", + "type": "str", + "default": "1d", + "required": false, + "help": "Kline interval (1m, 5m, 15m, 1h, 4h, 1d, 1w, 1M)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of klines (max 1000)" + } + ], + "columns": [ + "open", + "high", + "low", + "close", + "volume" + ], + "type": "js", + "modulePath": "plugins/binance/klines.js", + "sourceFile": "plugins/binance/klines.js" + }, + { + "site": "binance", + "name": "losers", + "description": "Top losing trading pairs by 24h price change", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of trading pairs" + } + ], + "columns": [ + "rank", + "symbol", + "price", + "change_24h", + "volume" + ], + "type": "js", + "modulePath": "plugins/binance/losers.js", + "sourceFile": "plugins/binance/losers.js" + }, + { + "site": "binance", + "name": "pairs", + "description": "List active trading pairs on Binance", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of trading pairs" + } + ], + "columns": [ + "symbol", + "base", + "quote", + "status" + ], + "type": "js", + "modulePath": "plugins/binance/pairs.js", + "sourceFile": "plugins/binance/pairs.js" + }, + { + "site": "binance", + "name": "price", + "description": "Quick price check for a trading pair", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + } + ], + "columns": [ + "symbol", + "price", + "change", + "change_pct", + "high", + "low", + "volume", + "quote_volume", + "trades" + ], + "type": "js", + "modulePath": "plugins/binance/price.js", + "sourceFile": "plugins/binance/price.js" + }, + { + "site": "binance", + "name": "prices", + "description": "Latest prices for all trading pairs", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of prices" + } + ], + "columns": [ + "rank", + "symbol", + "price" + ], + "type": "js", + "modulePath": "plugins/binance/prices.js", + "sourceFile": "plugins/binance/prices.js" + }, + { + "site": "binance", + "name": "ticker", + "description": "24h ticker statistics for top trading pairs by volume", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of tickers" + } + ], + "columns": [ + "symbol", + "price", + "change_pct", + "high", + "low", + "volume", + "quote_vol", + "trades" + ], + "type": "js", + "modulePath": "plugins/binance/ticker.js", + "sourceFile": "plugins/binance/ticker.js" + }, + { + "site": "binance", + "name": "top", + "description": "Top trading pairs by 24h volume on Binance", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of trading pairs" + } + ], + "columns": [ + "rank", + "symbol", + "price", + "change_24h", + "high", + "low", + "volume" + ], + "type": "js", + "modulePath": "plugins/binance/top.js", + "sourceFile": "plugins/binance/top.js" + }, + { + "site": "binance", + "name": "trades", + "description": "Recent trades for a trading pair", + "access": "read", + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of trades (max 1000)" + } + ], + "columns": [ + "id", + "price", + "qty", + "quote_qty", + "buyer_maker" + ], + "type": "js", + "modulePath": "plugins/binance/trades.js", + "sourceFile": "plugins/binance/trades.js" + }, + { + "site": "bluesky", + "name": "feeds", + "description": "Popular Bluesky feed generators", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of feeds" + } + ], + "columns": [ + "rank", + "name", + "likes", + "creator", + "description" + ], + "type": "js", + "modulePath": "plugins/bluesky/feeds.js", + "sourceFile": "plugins/bluesky/feeds.js" + }, + { + "site": "bluesky", + "name": "followers", + "description": "List followers of a Bluesky user", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of followers" + } + ], + "columns": [ + "rank", + "handle", + "name", + "description" + ], + "type": "js", + "modulePath": "plugins/bluesky/followers.js", + "sourceFile": "plugins/bluesky/followers.js" + }, + { + "site": "bluesky", + "name": "following", + "description": "List accounts a Bluesky user is following", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of accounts" + } + ], + "columns": [ + "rank", + "handle", + "name", + "description" + ], + "type": "js", + "modulePath": "plugins/bluesky/following.js", + "sourceFile": "plugins/bluesky/following.js" + }, + { + "site": "bluesky", + "name": "profile", + "description": "Get Bluesky user profile info", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle (e.g. bsky.app, jay.bsky.team)" + } + ], + "columns": [ + "handle", + "name", + "followers", + "following", + "posts", + "description" + ], + "type": "js", + "modulePath": "plugins/bluesky/profile.js", + "sourceFile": "plugins/bluesky/profile.js" + }, + { + "site": "bluesky", + "name": "search", + "description": "Search Bluesky users", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "handle", + "name", + "followers", + "description" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/bluesky/search.js", + "sourceFile": "plugins/bluesky/search.js" + }, + { + "site": "bluesky", + "name": "starter-packs", + "description": "Get starter packs created by a Bluesky user", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of starter packs" + } + ], + "columns": [ + "rank", + "name", + "description", + "members", + "joins" + ], + "type": "js", + "modulePath": "plugins/bluesky/starter-packs.js", + "sourceFile": "plugins/bluesky/starter-packs.js" + }, + { + "site": "bluesky", + "name": "thread", + "description": "Get a Bluesky post thread with replies", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "uri", + "type": "str", + "required": true, + "positional": true, + "help": "Post AT URI (at://did:.../app.bsky.feed.post/...) or bsky.app URL" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of replies" + } + ], + "columns": [ + "author", + "text", + "likes", + "reposts", + "replies_count" + ], + "type": "js", + "modulePath": "plugins/bluesky/thread.js", + "sourceFile": "plugins/bluesky/thread.js" + }, + { + "site": "bluesky", + "name": "trending", + "description": "Trending topics on Bluesky", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of topics" + } + ], + "columns": [ + "rank", + "topic", + "link" + ], + "type": "js", + "modulePath": "plugins/bluesky/trending.js", + "sourceFile": "plugins/bluesky/trending.js" + }, + { + "site": "bluesky", + "name": "user", + "description": "Get recent posts from a Bluesky user", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle (e.g. bsky.app)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts" + } + ], + "columns": [ + "rank", + "uri", + "text", + "likes", + "reposts", + "replies" + ], + "type": "js", + "modulePath": "plugins/bluesky/user.js", + "sourceFile": "plugins/bluesky/user.js" + }, + { + "site": "bmwblog", + "name": "article", + "description": "Read a BMWBLOG article by URL or slug", + "access": "read", + "domain": "www.bmwblog.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "url-or-slug", + "type": "str", + "required": true, + "positional": true, + "help": "BMWBLOG article URL or slug" + } + ], + "columns": [ + "title", + "date", + "author", + "sections", + "excerpt", + "url", + "content" + ], + "type": "js", + "modulePath": "plugins/bmwblog/article.js", + "sourceFile": "plugins/bmwblog/article.js" + }, + { + "site": "bmwblog", + "name": "latest", + "description": "List the latest BMWBLOG articles", + "access": "read", + "domain": "www.bmwblog.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of articles (1-50)" + } + ], + "columns": [ + "rank", + "title", + "date", + "author", + "section", + "excerpt", + "url" + ], + "type": "js", + "modulePath": "plugins/bmwblog/latest.js", + "sourceFile": "plugins/bmwblog/latest.js" + }, + { + "site": "bmwblog", + "name": "search", + "description": "Search BMWBLOG articles", + "access": "read", + "domain": "www.bmwblog.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results (1-50)" + } + ], + "columns": [ + "rank", + "title", + "date", + "author", + "section", + "excerpt", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/bmwblog/search.js", + "sourceFile": "plugins/bmwblog/search.js" + }, + { + "site": "cincinnati", + "name": "export-postgraduate-courses", + "description": "Export University of Cincinnati graduate and professional programs from official public sources.", + "access": "read", + "example": "webcmd cincinnati export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.grad.uc.edu", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/cincinnati/export-postgraduate-courses.js", + "sourceFile": "plugins/cincinnati/export-postgraduate-courses.js" + }, + { + "site": "coingecko", + "name": "categories", + "description": "Crypto categories ranked by aggregated market cap", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "sort", + "type": "str", + "default": "market_cap_desc", + "required": false, + "help": "Sort order (market_cap_desc / market_cap_asc / name_desc / name_asc / market_cap_change_24h_desc / market_cap_change_24h_asc)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of categories (1-100; CoinGecko returns ~120 max)" + } + ], + "columns": [ + "rank", + "id", + "name", + "marketCap", + "volume24h", + "marketCapChange24hPct", + "top3Coins" + ], + "type": "js", + "modulePath": "plugins/coingecko/categories.js", + "sourceFile": "plugins/coingecko/categories.js" + }, + { + "site": "coingecko", + "name": "coin", + "description": "Fetch a single cryptocurrency's market data by CoinGecko id (e.g. bitcoin, ethereum).", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "CoinGecko coin id (lowercase, e.g. bitcoin / ethereum / solana)." + }, + { + "name": "currency", + "type": "string", + "default": "usd", + "required": false, + "help": "Quote currency (usd, cny, eur, jpy, ...)." + } + ], + "columns": [ + "id", + "symbol", + "name", + "rank", + "price", + "marketCap", + "volume24h", + "change24hPct", + "change7dPct", + "change30dPct", + "ath", + "athDate", + "atl", + "atlDate", + "circulatingSupply", + "totalSupply", + "maxSupply", + "genesisDate", + "homepage" + ], + "type": "js", + "modulePath": "plugins/coingecko/coin.js", + "sourceFile": "plugins/coingecko/coin.js" + }, + { + "site": "coingecko", + "name": "derivatives", + "description": "Top crypto derivative (perpetual / futures) markets by 24h volume", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max rows to return (1-500; CoinGecko returns one large page)." + }, + { + "name": "symbol", + "type": "string", + "required": false, + "help": "Optional symbol substring filter (e.g. \"BTC\", \"ETHUSDT\")." + } + ], + "columns": [ + "rank", + "market", + "symbol", + "indexId", + "contractType", + "price", + "change24hPct", + "fundingRate", + "openInterestUsd", + "volume24hUsd", + "expired" + ], + "type": "js", + "modulePath": "plugins/coingecko/derivatives.js", + "sourceFile": "plugins/coingecko/derivatives.js" + }, + { + "site": "coingecko", + "name": "exchanges", + "description": "Top crypto exchanges by 24h BTC trading volume", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of exchanges (1-250, CoinGecko per_page upper bound)" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number (1-based)" + } + ], + "columns": [ + "rank", + "id", + "name", + "trustScore", + "volume24hBtc", + "country", + "yearEstablished", + "url" + ], + "type": "js", + "modulePath": "plugins/coingecko/exchanges.js", + "sourceFile": "plugins/coingecko/exchanges.js" + }, + { + "site": "coingecko", + "name": "global", + "description": "Aggregate crypto market stats: total market cap, volume, dominance", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "currency", + "type": "string", + "default": "usd", + "required": false, + "help": "Quote currency for total market cap / volume (usd, cny, eur, jpy, ...)" + } + ], + "columns": [ + "currency", + "totalMarketCap", + "totalVolume24h", + "marketCapChange24hPct", + "btcDominancePct", + "ethDominancePct", + "activeCryptocurrencies", + "markets", + "ongoingIcos", + "updatedAt" + ], + "type": "js", + "modulePath": "plugins/coingecko/global.js", + "sourceFile": "plugins/coingecko/global.js" + }, + { + "site": "coingecko", + "name": "top", + "description": "Cryptocurrency quotes by market cap (default USD)", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "currency", + "type": "string", + "default": "usd", + "required": false, + "help": "quote currency (usd / cny / eur / jpy ...)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number to return (default 10, maximum 250)" + } + ], + "columns": [ + "rank", + "symbol", + "name", + "price", + "change24hPct", + "marketCap", + "volume24h", + "high24h", + "low24h" + ], + "type": "js", + "modulePath": "plugins/coingecko/top.js", + "sourceFile": "plugins/coingecko/top.js" + }, + { + "site": "coingecko", + "name": "trending", + "description": "Top trending cryptocurrencies on CoinGecko in the last 24h (search-volume based).", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [], + "columns": [ + "rank", + "id", + "symbol", + "name", + "marketCapRank", + "priceBtc", + "thumb" + ], + "type": "js", + "modulePath": "plugins/coingecko/trending.js", + "sourceFile": "plugins/coingecko/trending.js" + }, + { + "site": "concordia", + "name": "export-postgraduate-courses", + "description": "Export Concordia University Montreal postgraduate programs using official public sources.", + "access": "read", + "example": "webcmd concordia export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.concordia.ca", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/concordia/export-postgraduate-courses.js", + "sourceFile": "plugins/concordia/export-postgraduate-courses.js" + }, + { + "site": "crates", + "name": "crate", + "description": "Single crates.io crate metadata (latest version, downloads, license, repo)", + "access": "read", + "domain": "crates.io", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "crates.io crate name (e.g. \"serde\", \"tokio\")" + } + ], + "columns": [ + "name", + "latestVersion", + "description", + "downloads", + "recentDownloads", + "versions", + "license", + "homepage", + "documentation", + "repository", + "keywords", + "categories", + "created", + "updated", + "url" + ], + "type": "js", + "modulePath": "plugins/crates/crate.js", + "sourceFile": "plugins/crates/crate.js" + }, + { + "site": "crates", + "name": "search", + "description": "Search the public crates.io registry by keyword", + "access": "read", + "domain": "crates.io", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"serde\", \"async runtime\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + } + ], + "columns": [ + "rank", + "name", + "latestVersion", + "description", + "downloads", + "recentDownloads", + "repository", + "updated", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/crates/search.js", + "sourceFile": "plugins/crates/search.js" + }, + { + "site": "dblp", + "name": "author", + "description": "List dblp publications by a given author (newest first; resolves to top PID match)", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "author", + "type": "str", + "required": false, + "positional": true, + "help": "Author name (e.g. \"Yoshua Bengio\"). Optional when --pid is given." + }, + { + "name": "pid", + "type": "str", + "required": false, + "help": "Canonical dblp PID (e.g. \"56/953\"). Bypasses author search." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max publications (1-200)" + } + ], + "columns": [ + "rank", + "key", + "title", + "authors", + "venue", + "year", + "type", + "doi", + "pid", + "url" + ], + "type": "js", + "modulePath": "plugins/dblp/author.js", + "sourceFile": "plugins/dblp/author.js" + }, + { + "site": "dblp", + "name": "paper", + "aliases": [ + "detail", + "view" + ], + "description": "Fetch a dblp record by canonical key (e.g. conf/nips/VaswaniSPUJGKP17)", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "dblp record key (round-tripped from the `key` column of `dblp search`)" + } + ], + "columns": [ + "key", + "type", + "title", + "authors", + "venue", + "year", + "pages", + "doi", + "open_access_url", + "dblp_url" + ], + "type": "js", + "modulePath": "plugins/dblp/paper.js", + "sourceFile": "plugins/dblp/paper.js" + }, + { + "site": "dblp", + "name": "search", + "description": "Search dblp computer-science bibliography by free-text query", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (title / author / venue, e.g. \"attention is all you need\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100, single dblp page)" + } + ], + "columns": [ + "rank", + "key", + "title", + "authors", + "venue", + "year", + "type", + "doi", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/dblp/search.js", + "sourceFile": "plugins/dblp/search.js" + }, + { + "site": "dblp", + "name": "venue", + "description": "Search dblp venue registry (conferences / journals) by name or acronym", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Venue name or acronym (e.g. \"ICLR\", \"neural networks\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max venues (1-100, single dblp page)" + } + ], + "columns": [ + "rank", + "acronym", + "venue", + "type", + "url" + ], + "type": "js", + "modulePath": "plugins/dblp/venue.js", + "sourceFile": "plugins/dblp/venue.js" + }, + { + "site": "defillama", + "name": "protocol", + "description": "Single DefiLlama protocol details (current TVL, mcap, chains, twitter, github, description)", + "access": "read", + "domain": "defillama.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "slug", + "type": "string", + "required": true, + "positional": true, + "help": "DefiLlama protocol slug (e.g. \"aave\", \"lido\")" + } + ], + "columns": [ + "slug", + "name", + "category", + "isParent", + "tvl", + "tvlAt", + "mcap", + "chains", + "twitter", + "github", + "audits", + "listedAt", + "description", + "website", + "url" + ], + "type": "js", + "modulePath": "plugins/defillama/protocol.js", + "sourceFile": "plugins/defillama/protocol.js" + }, + { + "site": "defillama", + "name": "protocols", + "description": "Top DeFi protocols on DefiLlama by current TVL (slug, name, category, TVL, mcap, change_1d/7d, chains)", + "access": "read", + "domain": "defillama.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Number of rows to return (1-500)" + } + ], + "columns": [ + "rank", + "slug", + "name", + "category", + "tvl", + "mcap", + "change_1d", + "change_7d", + "chains", + "listedAt", + "url" + ], + "type": "js", + "modulePath": "plugins/defillama/protocols.js", + "sourceFile": "plugins/defillama/protocols.js" + }, + { + "site": "devto", + "name": "latest", + "description": "Newest dev.to articles (firehose, all tags)", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Articles per page (1-100)" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number (1-based)" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "tags", + "reactions", + "comments", + "published", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/latest.js", + "sourceFile": "plugins/devto/latest.js" + }, + { + "site": "devto", + "name": "read", + "description": "Read a DEV.to article body by id", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "DEV.to article id (numeric, e.g. 3605688)" + }, + { + "name": "max-length", + "type": "int", + "default": 20000, + "required": false, + "help": "Max characters of body to return (min 100)" + } + ], + "columns": [ + "id", + "title", + "author", + "reactions", + "reading_time", + "tags", + "published_at", + "body", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/read.js", + "sourceFile": "plugins/devto/read.js" + }, + { + "site": "devto", + "name": "tag", + "description": "Latest DEV.to articles for a specific tag", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "tag", + "type": "str", + "required": true, + "positional": true, + "help": "Tag name (e.g. javascript, python, webdev)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of articles" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/tag.js", + "sourceFile": "plugins/devto/tag.js" + }, + { + "site": "devto", + "name": "top", + "description": "Top DEV.to articles of the day", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of articles" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/top.js", + "sourceFile": "plugins/devto/top.js" + }, + { + "site": "devto", + "name": "user", + "description": "Recent DEV.to articles from a specific user", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "DEV.to username (e.g. ben, thepracticaldev)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of articles" + } + ], + "columns": [ + "rank", + "id", + "title", + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/user.js", + "sourceFile": "plugins/devto/user.js" + }, + { + "site": "dictionary", + "name": "examples", + "description": "Read real-world example sentences utilizing the word", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to get example sentences for" + } + ], + "columns": [ + "word", + "example" + ], + "type": "js", + "modulePath": "plugins/dictionary/examples.js", + "sourceFile": "plugins/dictionary/examples.js" + }, + { + "site": "dictionary", + "name": "search", + "description": "Search the Free Dictionary API for definitions, parts of speech, and pronunciations.", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to define (e.g., serendipity)" + } + ], + "columns": [ + "word", + "phonetic", + "type", + "definition" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/dictionary/search.js", + "sourceFile": "plugins/dictionary/search.js" + }, + { + "site": "dictionary", + "name": "synonyms", + "description": "Find synonyms for a specific word", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to find synonyms for (e.g., serendipity)" + } + ], + "columns": [ + "word", + "synonyms" + ], + "type": "js", + "modulePath": "plugins/dictionary/synonyms.js", + "sourceFile": "plugins/dictionary/synonyms.js" + }, + { + "site": "dockerhub", + "name": "image", + "description": "Fetch a Docker Hub repository's public metadata (stars, pulls, last updated, status)", + "access": "read", + "domain": "hub.docker.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image name (e.g. \"nginx\", \"library/nginx\", \"bitnami/redis\")" + } + ], + "columns": [ + "image", + "official", + "stars", + "pulls", + "description", + "lastUpdated", + "lastModified", + "registered", + "status", + "url" + ], + "type": "js", + "modulePath": "plugins/dockerhub/image.js", + "sourceFile": "plugins/dockerhub/image.js" + }, + { + "site": "dockerhub", + "name": "search", + "description": "Search Docker Hub repositories by keyword", + "access": "read", + "domain": "hub.docker.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"nginx\", \"bitnami redis\")" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max repositories (1-100, single Docker Hub page)" + } + ], + "columns": [ + "rank", + "image", + "official", + "stars", + "pulls", + "description", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/dockerhub/search.js", + "sourceFile": "plugins/dockerhub/search.js" + }, + { + "site": "endoflife", + "name": "product", + "description": "Release cycles + EOL / LTS / support dates for one product on endoflife.date", + "access": "read", + "domain": "endoflife.date", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "product", + "type": "string", + "required": true, + "positional": true, + "help": "endoflife.date product slug (e.g. \"nodejs\", \"python\", \"ubuntu\")" + } + ], + "columns": [ + "product", + "cycle", + "releaseDate", + "latest", + "latestReleaseDate", + "lts", + "support", + "eol", + "extendedSupport", + "eolStatus", + "url" + ], + "type": "js", + "modulePath": "plugins/endoflife/product.js", + "sourceFile": "plugins/endoflife/product.js" + }, + { + "site": "flathub", + "name": "app", + "description": "Full Flathub appstream metadata for an app id (license, categories, latest release)", + "access": "read", + "domain": "flathub.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "appId", + "type": "str", + "required": true, + "positional": true, + "help": "AppStream id (e.g. \"org.mozilla.firefox\", \"org.gnome.Calculator\")" + } + ], + "columns": [ + "appId", + "name", + "summary", + "developer", + "license", + "isFreeLicense", + "isEol", + "categories", + "keywords", + "latestVersion", + "latestReleaseDate", + "homepage", + "bugtracker", + "donation", + "url" + ], + "type": "js", + "modulePath": "plugins/flathub/app.js", + "sourceFile": "plugins/flathub/app.js" + }, + { + "site": "flathub", + "name": "search", + "description": "Search Flathub apps by keyword", + "access": "read", + "domain": "flathub.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max apps (1-100)" + } + ], + "columns": [ + "rank", + "appId", + "name", + "summary", + "developer", + "license", + "isFreeLicense", + "mainCategories", + "installsLastMonth", + "updatedAt", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/flathub/search.js", + "sourceFile": "plugins/flathub/search.js" + }, + { + "site": "github-trending", + "name": "repos", + "description": "GitHub Trending repositories (public, no login). Filter by --language and --since.", + "access": "read", + "domain": "github.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "since", + "type": "string", + "default": "daily", + "required": false, + "help": "Time range: daily / weekly / monthly" + }, + { + "name": "language", + "type": "string", + "default": "", + "required": false, + "help": "Filter by programming language slug, e.g. python, rust, \"c++\"" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Number of repositories to return (max 25)" + } + ], + "columns": [ + "rank", + "repo", + "description", + "language", + "stars", + "forks", + "starsSince", + "url" + ], + "type": "js", + "modulePath": "plugins/github-trending/repos.js", + "sourceFile": "plugins/github-trending/repos.js" + }, + { + "site": "goettingen", + "name": "export-postgraduate-courses", + "description": "Export University of Göttingen postgraduate programmes from the official A-Z API.", + "access": "read", + "example": "webcmd goettingen export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.uni-goettingen.de", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programmes after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/goettingen/export-postgraduate-courses.js", + "sourceFile": "plugins/goettingen/export-postgraduate-courses.js" + }, + { + "site": "goproxy", + "name": "module", + "description": "Latest version + VCS origin metadata for a Go module on proxy.golang.org", + "access": "read", + "domain": "proxy.golang.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "module", + "type": "string", + "required": true, + "positional": true, + "help": "Go module path (e.g. \"github.com/gin-gonic/gin\", \"golang.org/x/net\")" + } + ], + "columns": [ + "module", + "version", + "publishedAt", + "vcs", + "repository", + "commit", + "ref", + "pkgGoDevUrl", + "url" + ], + "type": "js", + "modulePath": "plugins/goproxy/module.js", + "sourceFile": "plugins/goproxy/module.js" + }, + { + "site": "goproxy", + "name": "versions", + "description": "Published version tags for a Go module (newest first), optionally with publish times", + "access": "read", + "domain": "proxy.golang.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "module", + "type": "string", + "required": true, + "positional": true, + "help": "Go module path (e.g. \"github.com/gin-gonic/gin\")" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max rows to return (1-200)" + }, + { + "name": "with-time", + "type": "boolean", + "default": false, + "required": false, + "help": "Fetch each version's publish time (one extra request per row)" + } + ], + "columns": [ + "rank", + "module", + "version", + "publishedAt", + "url" + ], + "type": "js", + "modulePath": "plugins/goproxy/versions.js", + "sourceFile": "plugins/goproxy/versions.js" + }, + { + "site": "hackernews", + "name": "ask", + "description": "Hacker News Ask HN posts", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/hackernews/ask.js", + "sourceFile": "plugins/hackernews/ask.js" + }, + { + "site": "hackernews", + "name": "best", + "description": "Hacker News best stories", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/hackernews/best.js", + "sourceFile": "plugins/hackernews/best.js" + }, + { + "site": "hackernews", + "name": "jobs", + "description": "Hacker News job postings", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of job postings" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "url" + ], + "type": "js", + "modulePath": "plugins/hackernews/jobs.js", + "sourceFile": "plugins/hackernews/jobs.js" + }, + { + "site": "hackernews", + "name": "new", + "description": "Hacker News newest stories", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/hackernews/new.js", + "sourceFile": "plugins/hackernews/new.js" + }, + { + "site": "hackernews", + "name": "read", + "description": "Read a Hacker News story and its comment tree", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "HN item ID (e.g. 39847301)" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max top-level comments" + }, + { + "name": "depth", + "type": "int", + "default": 2, + "required": false, + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + }, + { + "name": "replies", + "type": "int", + "default": 5, + "required": false, + "help": "Max replies shown per comment at each level" + }, + { + "name": "max-length", + "type": "int", + "default": 2000, + "required": false, + "help": "Max characters per comment body (min 100)" } ], "columns": [ - "rank", - "title", - "date", + "type", "author", - "section", - "excerpt", - "url" + "score", + "text" ], "type": "js", - "modulePath": "plugins/bmwblog/latest.js", - "sourceFile": "plugins/bmwblog/latest.js" + "modulePath": "plugins/hackernews/read.js", + "sourceFile": "plugins/hackernews/read.js" }, { - "site": "bmwblog", + "site": "hackernews", "name": "search", - "description": "Search BMWBLOG articles", + "description": "Search Hacker News stories", "access": "read", - "domain": "www.bmwblog.com", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ @@ -78,269 +2800,124 @@ { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results (1-50)" + "help": "Number of results" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance or date", + "choices": [ + "relevance", + "date" + ] } ], "columns": [ "rank", + "id", "title", - "date", + "score", "author", - "section", - "excerpt", + "comments", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/bmwblog/search.js", - "sourceFile": "plugins/bmwblog/search.js" + "modulePath": "plugins/hackernews/search.js", + "sourceFile": "plugins/hackernews/search.js" }, { - "site": "cincinnati", - "name": "export-postgraduate-courses", - "description": "Export University of Cincinnati graduate and professional programs from official public sources.", + "site": "hackernews", + "name": "show", + "description": "Hacker News Show HN posts", "access": "read", - "example": "webcmd cincinnati export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.grad.uc.edu", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", + "name": "limit", "type": "int", + "default": 20, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Number of stories" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/cincinnati/export-postgraduate-courses.js", - "sourceFile": "plugins/cincinnati/export-postgraduate-courses.js" + "modulePath": "plugins/hackernews/show.js", + "sourceFile": "plugins/hackernews/show.js" }, { - "site": "concordia", - "name": "export-postgraduate-courses", - "description": "Export Concordia University Montreal postgraduate programs using official public sources.", + "site": "hackernews", + "name": "top", + "description": "Hacker News top stories", "access": "read", - "example": "webcmd concordia export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.concordia.ca", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", + "name": "limit", "type": "int", + "default": 20, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Number of stories" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/concordia/export-postgraduate-courses.js", - "sourceFile": "plugins/concordia/export-postgraduate-courses.js" + "modulePath": "plugins/hackernews/top.js", + "sourceFile": "plugins/hackernews/top.js" }, { - "site": "goettingen", - "name": "export-postgraduate-courses", - "description": "Export University of Göttingen postgraduate programmes from the official A-Z API.", + "site": "hackernews", + "name": "user", + "description": "Hacker News user profile", "access": "read", - "example": "webcmd goettingen export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.uni-goettingen.de", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Positive maximum number of programmes after filtering and deduplication" + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "HN username" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "username", + "karma", + "created", + "about" ], "type": "js", - "modulePath": "plugins/goettingen/export-postgraduate-courses.js", - "sourceFile": "plugins/goettingen/export-postgraduate-courses.js" + "modulePath": "plugins/hackernews/user.js", + "sourceFile": "plugins/hackernews/user.js" }, { "site": "heidelberg", @@ -2327,6 +4904,9 @@ "description", "url" ], + "tags": [ + "search" + ], "type": "js", "modulePath": "plugins/techcrunch/search.js", "sourceFile": "plugins/techcrunch/search.js" diff --git a/plugins/apple-podcasts/README.md b/plugins/apple-podcasts/README.md new file mode 100644 index 00000000..b2d9c520 --- /dev/null +++ b/plugins/apple-podcasts/README.md @@ -0,0 +1,17 @@ +# webcmd-plugin-apple-podcasts + +Webcmd commands for apple-podcasts. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/apple-podcasts +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd apple-podcasts episodes` | List recent episodes of an Apple Podcast (use ID from search) | +| `webcmd apple-podcasts search` | Search Apple Podcasts | +| `webcmd apple-podcasts top` | Top podcasts chart on Apple Podcasts | diff --git a/clis/apple-podcasts/episodes.js b/plugins/apple-podcasts/episodes.js similarity index 100% rename from clis/apple-podcasts/episodes.js rename to plugins/apple-podcasts/episodes.js diff --git a/plugins/apple-podcasts/package.json b/plugins/apple-podcasts/package.json new file mode 100644 index 00000000..8972e683 --- /dev/null +++ b/plugins/apple-podcasts/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-apple-podcasts", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for apple-podcasts", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/apple-podcasts/search.js b/plugins/apple-podcasts/search.js similarity index 100% rename from clis/apple-podcasts/search.js rename to plugins/apple-podcasts/search.js diff --git a/clis/apple-podcasts/commands.test.js b/plugins/apple-podcasts/test/commands.test.js similarity index 99% rename from clis/apple-podcasts/commands.test.js rename to plugins/apple-podcasts/test/commands.test.js index 960713ea..34f5b408 100644 --- a/clis/apple-podcasts/commands.test.js +++ b/plugins/apple-podcasts/test/commands.test.js @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './search.js'; -import './top.js'; +import '../search.js'; +import '../top.js'; describe('apple-podcasts search command', () => { beforeEach(() => { vi.restoreAllMocks(); diff --git a/clis/apple-podcasts/utils.test.js b/plugins/apple-podcasts/test/utils.test.js similarity index 96% rename from clis/apple-podcasts/utils.test.js rename to plugins/apple-podcasts/test/utils.test.js index 66978456..8e7a10f7 100644 --- a/clis/apple-podcasts/utils.test.js +++ b/plugins/apple-podcasts/test/utils.test.js @@ -1,5 +1,5 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { formatDuration, formatDate, itunesFetch } from './utils.js'; +import { formatDuration, formatDate, itunesFetch } from '../utils.js'; describe('formatDuration', () => { it('formats typical duration in ms', () => { expect(formatDuration(3661000)).toBe('61:01'); diff --git a/clis/apple-podcasts/top.js b/plugins/apple-podcasts/top.js similarity index 100% rename from clis/apple-podcasts/top.js rename to plugins/apple-podcasts/top.js diff --git a/clis/apple-podcasts/utils.js b/plugins/apple-podcasts/utils.js similarity index 100% rename from clis/apple-podcasts/utils.js rename to plugins/apple-podcasts/utils.js diff --git a/plugins/apple-podcasts/webcmd-plugin.json b/plugins/apple-podcasts/webcmd-plugin.json new file mode 100644 index 00000000..ce335a80 --- /dev/null +++ b/plugins/apple-podcasts/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "apple-podcasts", + "version": "0.1.0", + "description": "Webcmd commands for apple-podcasts", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/archive/README.md b/plugins/archive/README.md new file mode 100644 index 00000000..771c72ea --- /dev/null +++ b/plugins/archive/README.md @@ -0,0 +1,18 @@ +# webcmd-plugin-archive + +Webcmd commands for archive. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/archive +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd archive item` | Fetch metadata for a single Internet Archive item by identifier. | +| `webcmd archive search` | Search Internet Archive items across books, movies, audio, software, and web. | +| `webcmd archive snapshots` | List Wayback Machine snapshots over time for a URL via the CDX API. | +| `webcmd archive wayback` | Look up the closest Wayback Machine snapshot for a URL. | diff --git a/clis/archive/item.js b/plugins/archive/item.js similarity index 100% rename from clis/archive/item.js rename to plugins/archive/item.js diff --git a/plugins/archive/package.json b/plugins/archive/package.json new file mode 100644 index 00000000..08d9d2d3 --- /dev/null +++ b/plugins/archive/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-archive", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for archive", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/archive/search.js b/plugins/archive/search.js similarity index 100% rename from clis/archive/search.js rename to plugins/archive/search.js diff --git a/clis/archive/snapshots.js b/plugins/archive/snapshots.js similarity index 100% rename from clis/archive/snapshots.js rename to plugins/archive/snapshots.js diff --git a/clis/archive/archive.test.js b/plugins/archive/test/archive.test.js similarity index 99% rename from clis/archive/archive.test.js rename to plugins/archive/test/archive.test.js index 26353897..c0d975e7 100644 --- a/clis/archive/archive.test.js +++ b/plugins/archive/test/archive.test.js @@ -1,10 +1,10 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './search.js'; -import './item.js'; -import './wayback.js'; -import './snapshots.js'; +import '../search.js'; +import '../item.js'; +import '../wayback.js'; +import '../snapshots.js'; function jsonResponse(body, status = 200) { return new Response(JSON.stringify(body), { diff --git a/clis/archive/wayback.js b/plugins/archive/wayback.js similarity index 100% rename from clis/archive/wayback.js rename to plugins/archive/wayback.js diff --git a/plugins/archive/webcmd-plugin.json b/plugins/archive/webcmd-plugin.json new file mode 100644 index 00000000..fcfaeff5 --- /dev/null +++ b/plugins/archive/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "archive", + "version": "0.1.0", + "description": "Webcmd commands for archive", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/arxiv/README.md b/plugins/arxiv/README.md new file mode 100644 index 00000000..14797907 --- /dev/null +++ b/plugins/arxiv/README.md @@ -0,0 +1,18 @@ +# webcmd-plugin-arxiv + +Webcmd commands for arxiv. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/arxiv +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd arxiv author` | List arXiv papers by a given author (newest first) | +| `webcmd arxiv paper` | Get arXiv paper details by ID | +| `webcmd arxiv recent` | List recent arXiv submissions in a category | +| `webcmd arxiv search` | Search arXiv papers | diff --git a/clis/arxiv/author.js b/plugins/arxiv/author.js similarity index 100% rename from clis/arxiv/author.js rename to plugins/arxiv/author.js diff --git a/plugins/arxiv/package.json b/plugins/arxiv/package.json new file mode 100644 index 00000000..1fdff537 --- /dev/null +++ b/plugins/arxiv/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-arxiv", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for arxiv", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/arxiv/paper.js b/plugins/arxiv/paper.js similarity index 100% rename from clis/arxiv/paper.js rename to plugins/arxiv/paper.js diff --git a/clis/arxiv/recent.js b/plugins/arxiv/recent.js similarity index 100% rename from clis/arxiv/recent.js rename to plugins/arxiv/recent.js diff --git a/clis/arxiv/search.js b/plugins/arxiv/search.js similarity index 100% rename from clis/arxiv/search.js rename to plugins/arxiv/search.js diff --git a/clis/arxiv/arxiv.test.js b/plugins/arxiv/test/arxiv.test.js similarity index 98% rename from clis/arxiv/arxiv.test.js rename to plugins/arxiv/test/arxiv.test.js index f82344ec..ff4f0c66 100644 --- a/clis/arxiv/arxiv.test.js +++ b/plugins/arxiv/test/arxiv.test.js @@ -1,9 +1,9 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { normalizeArxivCategory, normalizeArxivLimit, parseEntries } from './utils.js'; -import './paper.js'; -import './search.js'; -import './recent.js'; +import { normalizeArxivCategory, normalizeArxivLimit, parseEntries } from '../utils.js'; +import '../paper.js'; +import '../search.js'; +import '../recent.js'; const SAMPLE_ENTRY_XML = ` =0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/bbc/README.md b/plugins/bbc/README.md new file mode 100644 index 00000000..6abe7098 --- /dev/null +++ b/plugins/bbc/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-bbc + +Webcmd commands for bbc. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/bbc +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd bbc news` | BBC News headlines (RSS) | +| `webcmd bbc topic` | BBC News headlines for a specific section (RSS feed) | diff --git a/clis/bbc/news.js b/plugins/bbc/news.js similarity index 100% rename from clis/bbc/news.js rename to plugins/bbc/news.js diff --git a/plugins/bbc/package.json b/plugins/bbc/package.json new file mode 100644 index 00000000..0c52783a --- /dev/null +++ b/plugins/bbc/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-bbc", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for bbc", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/bbc/topic.js b/plugins/bbc/topic.js similarity index 100% rename from clis/bbc/topic.js rename to plugins/bbc/topic.js diff --git a/clis/bbc/utils.js b/plugins/bbc/utils.js similarity index 100% rename from clis/bbc/utils.js rename to plugins/bbc/utils.js diff --git a/plugins/bbc/webcmd-plugin.json b/plugins/bbc/webcmd-plugin.json new file mode 100644 index 00000000..3976f34c --- /dev/null +++ b/plugins/bbc/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "bbc", + "version": "0.1.0", + "description": "Webcmd commands for bbc", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/binance/README.md b/plugins/binance/README.md new file mode 100644 index 00000000..a07ff7e0 --- /dev/null +++ b/plugins/binance/README.md @@ -0,0 +1,25 @@ +# webcmd-plugin-binance + +Webcmd commands for binance. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/binance +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd binance asks` | Order book ask prices for a trading pair | +| `webcmd binance depth` | Order book bid and ask prices for a trading pair | +| `webcmd binance gainers` | Top gaining trading pairs by 24h price change | +| `webcmd binance klines` | Candlestick/kline data for a trading pair | +| `webcmd binance losers` | Top losing trading pairs by 24h price change | +| `webcmd binance pairs` | List active trading pairs on Binance | +| `webcmd binance price` | Quick price check for a trading pair | +| `webcmd binance prices` | Latest prices for all trading pairs | +| `webcmd binance ticker` | 24h ticker statistics for top trading pairs by volume | +| `webcmd binance top` | Top trading pairs by 24h volume on Binance | +| `webcmd binance trades` | Recent trades for a trading pair | diff --git a/clis/binance/asks.js b/plugins/binance/asks.js similarity index 100% rename from clis/binance/asks.js rename to plugins/binance/asks.js diff --git a/clis/binance/depth.js b/plugins/binance/depth.js similarity index 100% rename from clis/binance/depth.js rename to plugins/binance/depth.js diff --git a/clis/binance/gainers.js b/plugins/binance/gainers.js similarity index 100% rename from clis/binance/gainers.js rename to plugins/binance/gainers.js diff --git a/clis/binance/klines.js b/plugins/binance/klines.js similarity index 100% rename from clis/binance/klines.js rename to plugins/binance/klines.js diff --git a/clis/binance/losers.js b/plugins/binance/losers.js similarity index 100% rename from clis/binance/losers.js rename to plugins/binance/losers.js diff --git a/plugins/binance/package.json b/plugins/binance/package.json new file mode 100644 index 00000000..f3d324e5 --- /dev/null +++ b/plugins/binance/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-binance", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for binance", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/binance/pairs.js b/plugins/binance/pairs.js similarity index 100% rename from clis/binance/pairs.js rename to plugins/binance/pairs.js diff --git a/clis/binance/price.js b/plugins/binance/price.js similarity index 100% rename from clis/binance/price.js rename to plugins/binance/price.js diff --git a/clis/binance/prices.js b/plugins/binance/prices.js similarity index 100% rename from clis/binance/prices.js rename to plugins/binance/prices.js diff --git a/clis/binance/commands.test.js b/plugins/binance/test/commands.test.js similarity index 97% rename from clis/binance/commands.test.js rename to plugins/binance/test/commands.test.js index 17f8da55..eca74ede 100644 --- a/clis/binance/commands.test.js +++ b/plugins/binance/test/commands.test.js @@ -3,9 +3,9 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { executePipeline } from '@agentrhq/webcmd/pipeline'; // Import all binance adapters to register them -import './top.js'; -import './gainers.js'; -import './pairs.js'; +import '../top.js'; +import '../gainers.js'; +import '../pairs.js'; function loadPipeline(name) { const cmd = getRegistry().get(`binance/${name}`); diff --git a/clis/binance/ticker.js b/plugins/binance/ticker.js similarity index 100% rename from clis/binance/ticker.js rename to plugins/binance/ticker.js diff --git a/clis/binance/top.js b/plugins/binance/top.js similarity index 100% rename from clis/binance/top.js rename to plugins/binance/top.js diff --git a/clis/binance/trades.js b/plugins/binance/trades.js similarity index 100% rename from clis/binance/trades.js rename to plugins/binance/trades.js diff --git a/plugins/binance/webcmd-plugin.json b/plugins/binance/webcmd-plugin.json new file mode 100644 index 00000000..6b6ef765 --- /dev/null +++ b/plugins/binance/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "binance", + "version": "0.1.0", + "description": "Webcmd commands for binance", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/bluesky/README.md b/plugins/bluesky/README.md new file mode 100644 index 00000000..1eed7705 --- /dev/null +++ b/plugins/bluesky/README.md @@ -0,0 +1,23 @@ +# webcmd-plugin-bluesky + +Webcmd commands for bluesky. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/bluesky +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd bluesky feeds` | Popular Bluesky feed generators | +| `webcmd bluesky followers` | List followers of a Bluesky user | +| `webcmd bluesky following` | List accounts a Bluesky user is following | +| `webcmd bluesky profile` | Get Bluesky user profile info | +| `webcmd bluesky search` | Search Bluesky users | +| `webcmd bluesky starter-packs` | Get starter packs created by a Bluesky user | +| `webcmd bluesky thread` | Get a Bluesky post thread with replies | +| `webcmd bluesky trending` | Trending topics on Bluesky | +| `webcmd bluesky user` | Get recent posts from a Bluesky user | diff --git a/clis/bluesky/feeds.js b/plugins/bluesky/feeds.js similarity index 100% rename from clis/bluesky/feeds.js rename to plugins/bluesky/feeds.js diff --git a/clis/bluesky/followers.js b/plugins/bluesky/followers.js similarity index 100% rename from clis/bluesky/followers.js rename to plugins/bluesky/followers.js diff --git a/clis/bluesky/following.js b/plugins/bluesky/following.js similarity index 100% rename from clis/bluesky/following.js rename to plugins/bluesky/following.js diff --git a/plugins/bluesky/package.json b/plugins/bluesky/package.json new file mode 100644 index 00000000..923a54a7 --- /dev/null +++ b/plugins/bluesky/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-bluesky", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for bluesky", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/bluesky/profile.js b/plugins/bluesky/profile.js similarity index 100% rename from clis/bluesky/profile.js rename to plugins/bluesky/profile.js diff --git a/clis/bluesky/search.js b/plugins/bluesky/search.js similarity index 100% rename from clis/bluesky/search.js rename to plugins/bluesky/search.js diff --git a/clis/bluesky/starter-packs.js b/plugins/bluesky/starter-packs.js similarity index 100% rename from clis/bluesky/starter-packs.js rename to plugins/bluesky/starter-packs.js diff --git a/clis/bluesky/thread.js b/plugins/bluesky/thread.js similarity index 100% rename from clis/bluesky/thread.js rename to plugins/bluesky/thread.js diff --git a/clis/bluesky/trending.js b/plugins/bluesky/trending.js similarity index 100% rename from clis/bluesky/trending.js rename to plugins/bluesky/trending.js diff --git a/clis/bluesky/user.js b/plugins/bluesky/user.js similarity index 100% rename from clis/bluesky/user.js rename to plugins/bluesky/user.js diff --git a/plugins/bluesky/webcmd-plugin.json b/plugins/bluesky/webcmd-plugin.json new file mode 100644 index 00000000..cc1bcfcb --- /dev/null +++ b/plugins/bluesky/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "bluesky", + "version": "0.1.0", + "description": "Webcmd commands for bluesky", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/coingecko/README.md b/plugins/coingecko/README.md new file mode 100644 index 00000000..c22f4dcb --- /dev/null +++ b/plugins/coingecko/README.md @@ -0,0 +1,21 @@ +# webcmd-plugin-coingecko + +Webcmd commands for coingecko. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/coingecko +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd coingecko categories` | Crypto categories ranked by aggregated market cap | +| `webcmd coingecko coin` | Fetch a single cryptocurrency's market data by CoinGecko id (e.g. bitcoin, ethereum). | +| `webcmd coingecko derivatives` | Top crypto derivative (perpetual / futures) markets by 24h volume | +| `webcmd coingecko exchanges` | Top crypto exchanges by 24h BTC trading volume | +| `webcmd coingecko global` | Aggregate crypto market stats: total market cap, volume, dominance | +| `webcmd coingecko top` | Cryptocurrency quotes by market cap (default USD) | +| `webcmd coingecko trending` | Top trending cryptocurrencies on CoinGecko in the last 24h (search-volume based). | diff --git a/clis/coingecko/categories.js b/plugins/coingecko/categories.js similarity index 100% rename from clis/coingecko/categories.js rename to plugins/coingecko/categories.js diff --git a/clis/coingecko/coin.js b/plugins/coingecko/coin.js similarity index 100% rename from clis/coingecko/coin.js rename to plugins/coingecko/coin.js diff --git a/clis/coingecko/derivatives.js b/plugins/coingecko/derivatives.js similarity index 100% rename from clis/coingecko/derivatives.js rename to plugins/coingecko/derivatives.js diff --git a/clis/coingecko/exchanges.js b/plugins/coingecko/exchanges.js similarity index 100% rename from clis/coingecko/exchanges.js rename to plugins/coingecko/exchanges.js diff --git a/clis/coingecko/global.js b/plugins/coingecko/global.js similarity index 100% rename from clis/coingecko/global.js rename to plugins/coingecko/global.js diff --git a/plugins/coingecko/package.json b/plugins/coingecko/package.json new file mode 100644 index 00000000..4e6f944f --- /dev/null +++ b/plugins/coingecko/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-coingecko", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for coingecko", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/coingecko/coingecko.test.js b/plugins/coingecko/test/coingecko.test.js similarity index 98% rename from clis/coingecko/coingecko.test.js rename to plugins/coingecko/test/coingecko.test.js index 511c8148..f9505516 100644 --- a/clis/coingecko/coingecko.test.js +++ b/plugins/coingecko/test/coingecko.test.js @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './coin.js'; -import './trending.js'; +import '../coin.js'; +import '../trending.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/coingecko/top.js b/plugins/coingecko/top.js similarity index 100% rename from clis/coingecko/top.js rename to plugins/coingecko/top.js diff --git a/clis/coingecko/trending.js b/plugins/coingecko/trending.js similarity index 100% rename from clis/coingecko/trending.js rename to plugins/coingecko/trending.js diff --git a/plugins/coingecko/webcmd-plugin.json b/plugins/coingecko/webcmd-plugin.json new file mode 100644 index 00000000..26920440 --- /dev/null +++ b/plugins/coingecko/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "coingecko", + "version": "0.1.0", + "description": "Webcmd commands for coingecko", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/crates/README.md b/plugins/crates/README.md new file mode 100644 index 00000000..600ebadb --- /dev/null +++ b/plugins/crates/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-crates + +Webcmd commands for crates. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/crates +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd crates crate` | Single crates.io crate metadata (latest version, downloads, license, repo) | +| `webcmd crates search` | Search the public crates.io registry by keyword | diff --git a/clis/crates/crate.js b/plugins/crates/crate.js similarity index 100% rename from clis/crates/crate.js rename to plugins/crates/crate.js diff --git a/plugins/crates/package.json b/plugins/crates/package.json new file mode 100644 index 00000000..44a9684b --- /dev/null +++ b/plugins/crates/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-crates", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for crates", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/crates/search.js b/plugins/crates/search.js similarity index 100% rename from clis/crates/search.js rename to plugins/crates/search.js diff --git a/clis/crates/utils.js b/plugins/crates/utils.js similarity index 100% rename from clis/crates/utils.js rename to plugins/crates/utils.js diff --git a/plugins/crates/webcmd-plugin.json b/plugins/crates/webcmd-plugin.json new file mode 100644 index 00000000..90d97b65 --- /dev/null +++ b/plugins/crates/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "crates", + "version": "0.1.0", + "description": "Webcmd commands for crates", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/dblp/README.md b/plugins/dblp/README.md new file mode 100644 index 00000000..0c5eebe5 --- /dev/null +++ b/plugins/dblp/README.md @@ -0,0 +1,18 @@ +# webcmd-plugin-dblp + +Webcmd commands for dblp. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/dblp +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd dblp author` | List dblp publications by a given author (newest first; resolves to top PID match) | +| `webcmd dblp paper` | Fetch a dblp record by canonical key (e.g. conf/nips/VaswaniSPUJGKP17) | +| `webcmd dblp search` | Search dblp computer-science bibliography by free-text query | +| `webcmd dblp venue` | Search dblp venue registry (conferences / journals) by name or acronym | diff --git a/clis/dblp/author.js b/plugins/dblp/author.js similarity index 100% rename from clis/dblp/author.js rename to plugins/dblp/author.js diff --git a/plugins/dblp/package.json b/plugins/dblp/package.json new file mode 100644 index 00000000..753a4eb4 --- /dev/null +++ b/plugins/dblp/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-dblp", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for dblp", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/dblp/paper.js b/plugins/dblp/paper.js similarity index 100% rename from clis/dblp/paper.js rename to plugins/dblp/paper.js diff --git a/clis/dblp/search.js b/plugins/dblp/search.js similarity index 100% rename from clis/dblp/search.js rename to plugins/dblp/search.js diff --git a/clis/dblp/dblp.test.js b/plugins/dblp/test/dblp.test.js similarity index 99% rename from clis/dblp/dblp.test.js rename to plugins/dblp/test/dblp.test.js index 244de201..c2378571 100644 --- a/clis/dblp/dblp.test.js +++ b/plugins/dblp/test/dblp.test.js @@ -17,9 +17,9 @@ import { requireQuery, requireRecordKey, searchHitToRow, -} from './utils.js'; -import './search.js'; -import './paper.js'; +} from '../utils.js'; +import '../search.js'; +import '../paper.js'; const SEARCH_HIT = { '@score': '9', diff --git a/clis/dblp/utils.js b/plugins/dblp/utils.js similarity index 100% rename from clis/dblp/utils.js rename to plugins/dblp/utils.js diff --git a/clis/dblp/venue.js b/plugins/dblp/venue.js similarity index 100% rename from clis/dblp/venue.js rename to plugins/dblp/venue.js diff --git a/plugins/dblp/webcmd-plugin.json b/plugins/dblp/webcmd-plugin.json new file mode 100644 index 00000000..928f0674 --- /dev/null +++ b/plugins/dblp/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "dblp", + "version": "0.1.0", + "description": "Webcmd commands for dblp", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/defillama/README.md b/plugins/defillama/README.md new file mode 100644 index 00000000..2b96fb3a --- /dev/null +++ b/plugins/defillama/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-defillama + +Webcmd commands for defillama. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/defillama +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd defillama protocol` | Single DefiLlama protocol details (current TVL, mcap, chains, twitter, github, description) | +| `webcmd defillama protocols` | Top DeFi protocols on DefiLlama by current TVL (slug, name, category, TVL, mcap, change_1d/7d, chains) | diff --git a/plugins/defillama/package.json b/plugins/defillama/package.json new file mode 100644 index 00000000..58d2beac --- /dev/null +++ b/plugins/defillama/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-defillama", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for defillama", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/defillama/protocol.js b/plugins/defillama/protocol.js similarity index 100% rename from clis/defillama/protocol.js rename to plugins/defillama/protocol.js diff --git a/clis/defillama/protocols.js b/plugins/defillama/protocols.js similarity index 100% rename from clis/defillama/protocols.js rename to plugins/defillama/protocols.js diff --git a/clis/defillama/defillama.test.js b/plugins/defillama/test/defillama.test.js similarity index 98% rename from clis/defillama/defillama.test.js rename to plugins/defillama/test/defillama.test.js index a7d038da..28e2c624 100644 --- a/clis/defillama/defillama.test.js +++ b/plugins/defillama/test/defillama.test.js @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './protocols.js'; -import './protocol.js'; +import '../protocols.js'; +import '../protocol.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/defillama/utils.js b/plugins/defillama/utils.js similarity index 100% rename from clis/defillama/utils.js rename to plugins/defillama/utils.js diff --git a/plugins/defillama/webcmd-plugin.json b/plugins/defillama/webcmd-plugin.json new file mode 100644 index 00000000..2c5007a7 --- /dev/null +++ b/plugins/defillama/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "defillama", + "version": "0.1.0", + "description": "Webcmd commands for defillama", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/devto/README.md b/plugins/devto/README.md new file mode 100644 index 00000000..465947cb --- /dev/null +++ b/plugins/devto/README.md @@ -0,0 +1,19 @@ +# webcmd-plugin-devto + +Webcmd commands for devto. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/devto +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd devto latest` | Newest dev.to articles (firehose, all tags) | +| `webcmd devto read` | Read a DEV.to article body by id | +| `webcmd devto tag` | Latest DEV.to articles for a specific tag | +| `webcmd devto top` | Top DEV.to articles of the day | +| `webcmd devto user` | Recent DEV.to articles from a specific user | diff --git a/clis/devto/latest.js b/plugins/devto/latest.js similarity index 100% rename from clis/devto/latest.js rename to plugins/devto/latest.js diff --git a/plugins/devto/package.json b/plugins/devto/package.json new file mode 100644 index 00000000..eed8049b --- /dev/null +++ b/plugins/devto/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-devto", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for devto", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/devto/read.js b/plugins/devto/read.js similarity index 100% rename from clis/devto/read.js rename to plugins/devto/read.js diff --git a/clis/devto/tag.js b/plugins/devto/tag.js similarity index 100% rename from clis/devto/tag.js rename to plugins/devto/tag.js diff --git a/clis/devto/devto.test.js b/plugins/devto/test/devto.test.js similarity index 99% rename from clis/devto/devto.test.js rename to plugins/devto/test/devto.test.js index fe9c852e..02e71289 100644 --- a/clis/devto/devto.test.js +++ b/plugins/devto/test/devto.test.js @@ -1,10 +1,10 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './top.js'; -import './tag.js'; -import './user.js'; -import './read.js'; +import '../top.js'; +import '../tag.js'; +import '../user.js'; +import '../read.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/devto/top.js b/plugins/devto/top.js similarity index 100% rename from clis/devto/top.js rename to plugins/devto/top.js diff --git a/clis/devto/user.js b/plugins/devto/user.js similarity index 100% rename from clis/devto/user.js rename to plugins/devto/user.js diff --git a/plugins/devto/webcmd-plugin.json b/plugins/devto/webcmd-plugin.json new file mode 100644 index 00000000..c15d6854 --- /dev/null +++ b/plugins/devto/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "devto", + "version": "0.1.0", + "description": "Webcmd commands for devto", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/dictionary/README.md b/plugins/dictionary/README.md new file mode 100644 index 00000000..0906aa98 --- /dev/null +++ b/plugins/dictionary/README.md @@ -0,0 +1,17 @@ +# webcmd-plugin-dictionary + +Webcmd commands for dictionary. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/dictionary +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd dictionary examples` | Read real-world example sentences utilizing the word | +| `webcmd dictionary search` | Search the Free Dictionary API for definitions, parts of speech, and pronunciations. | +| `webcmd dictionary synonyms` | Find synonyms for a specific word | diff --git a/clis/dictionary/examples.js b/plugins/dictionary/examples.js similarity index 100% rename from clis/dictionary/examples.js rename to plugins/dictionary/examples.js diff --git a/plugins/dictionary/package.json b/plugins/dictionary/package.json new file mode 100644 index 00000000..024cf89c --- /dev/null +++ b/plugins/dictionary/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-dictionary", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for dictionary", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/dictionary/search.js b/plugins/dictionary/search.js similarity index 100% rename from clis/dictionary/search.js rename to plugins/dictionary/search.js diff --git a/clis/dictionary/synonyms.js b/plugins/dictionary/synonyms.js similarity index 100% rename from clis/dictionary/synonyms.js rename to plugins/dictionary/synonyms.js diff --git a/plugins/dictionary/webcmd-plugin.json b/plugins/dictionary/webcmd-plugin.json new file mode 100644 index 00000000..4b68e1d0 --- /dev/null +++ b/plugins/dictionary/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "dictionary", + "version": "0.1.0", + "description": "Webcmd commands for dictionary", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/dockerhub/README.md b/plugins/dockerhub/README.md new file mode 100644 index 00000000..8fab128c --- /dev/null +++ b/plugins/dockerhub/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-dockerhub + +Webcmd commands for dockerhub. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/dockerhub +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd dockerhub image` | Fetch a Docker Hub repository's public metadata (stars, pulls, last updated, status) | +| `webcmd dockerhub search` | Search Docker Hub repositories by keyword | diff --git a/clis/dockerhub/image.js b/plugins/dockerhub/image.js similarity index 100% rename from clis/dockerhub/image.js rename to plugins/dockerhub/image.js diff --git a/plugins/dockerhub/package.json b/plugins/dockerhub/package.json new file mode 100644 index 00000000..79d57c1a --- /dev/null +++ b/plugins/dockerhub/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-dockerhub", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for dockerhub", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/dockerhub/search.js b/plugins/dockerhub/search.js similarity index 100% rename from clis/dockerhub/search.js rename to plugins/dockerhub/search.js diff --git a/clis/dockerhub/utils.js b/plugins/dockerhub/utils.js similarity index 100% rename from clis/dockerhub/utils.js rename to plugins/dockerhub/utils.js diff --git a/plugins/dockerhub/webcmd-plugin.json b/plugins/dockerhub/webcmd-plugin.json new file mode 100644 index 00000000..8f6fe160 --- /dev/null +++ b/plugins/dockerhub/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "dockerhub", + "version": "0.1.0", + "description": "Webcmd commands for dockerhub", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/endoflife/README.md b/plugins/endoflife/README.md new file mode 100644 index 00000000..96dc3e64 --- /dev/null +++ b/plugins/endoflife/README.md @@ -0,0 +1,15 @@ +# webcmd-plugin-endoflife + +Webcmd commands for endoflife. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/endoflife +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd endoflife product` | Release cycles + EOL / LTS / support dates for one product on endoflife.date | diff --git a/plugins/endoflife/package.json b/plugins/endoflife/package.json new file mode 100644 index 00000000..75f77dde --- /dev/null +++ b/plugins/endoflife/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-endoflife", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for endoflife", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/endoflife/product.js b/plugins/endoflife/product.js similarity index 100% rename from clis/endoflife/product.js rename to plugins/endoflife/product.js diff --git a/clis/endoflife/endoflife.test.js b/plugins/endoflife/test/endoflife.test.js similarity index 99% rename from clis/endoflife/endoflife.test.js rename to plugins/endoflife/test/endoflife.test.js index 14738587..3648ec16 100644 --- a/clis/endoflife/endoflife.test.js +++ b/plugins/endoflife/test/endoflife.test.js @@ -1,7 +1,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './product.js'; +import '../product.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/endoflife/utils.js b/plugins/endoflife/utils.js similarity index 100% rename from clis/endoflife/utils.js rename to plugins/endoflife/utils.js diff --git a/plugins/endoflife/webcmd-plugin.json b/plugins/endoflife/webcmd-plugin.json new file mode 100644 index 00000000..53ac5384 --- /dev/null +++ b/plugins/endoflife/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "endoflife", + "version": "0.1.0", + "description": "Webcmd commands for endoflife", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/flathub/README.md b/plugins/flathub/README.md new file mode 100644 index 00000000..0ec252d1 --- /dev/null +++ b/plugins/flathub/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-flathub + +Webcmd commands for flathub. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/flathub +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd flathub app` | Full Flathub appstream metadata for an app id (license, categories, latest release) | +| `webcmd flathub search` | Search Flathub apps by keyword | diff --git a/clis/flathub/app.js b/plugins/flathub/app.js similarity index 100% rename from clis/flathub/app.js rename to plugins/flathub/app.js diff --git a/plugins/flathub/package.json b/plugins/flathub/package.json new file mode 100644 index 00000000..43dc53b2 --- /dev/null +++ b/plugins/flathub/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-flathub", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for flathub", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/flathub/search.js b/plugins/flathub/search.js similarity index 100% rename from clis/flathub/search.js rename to plugins/flathub/search.js diff --git a/clis/flathub/flathub.test.js b/plugins/flathub/test/flathub.test.js similarity index 99% rename from clis/flathub/flathub.test.js rename to plugins/flathub/test/flathub.test.js index 77cd762f..d89d7dd9 100644 --- a/clis/flathub/flathub.test.js +++ b/plugins/flathub/test/flathub.test.js @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './search.js'; -import './app.js'; +import '../search.js'; +import '../app.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/flathub/utils.js b/plugins/flathub/utils.js similarity index 100% rename from clis/flathub/utils.js rename to plugins/flathub/utils.js diff --git a/plugins/flathub/webcmd-plugin.json b/plugins/flathub/webcmd-plugin.json new file mode 100644 index 00000000..35e7a073 --- /dev/null +++ b/plugins/flathub/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "flathub", + "version": "0.1.0", + "description": "Webcmd commands for flathub", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/github-trending/README.md b/plugins/github-trending/README.md new file mode 100644 index 00000000..24305623 --- /dev/null +++ b/plugins/github-trending/README.md @@ -0,0 +1,15 @@ +# webcmd-plugin-github-trending + +Webcmd commands for github-trending. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/github-trending +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd github-trending repos` | GitHub Trending repositories (public, no login). Filter by --language and --since. | diff --git a/plugins/github-trending/package.json b/plugins/github-trending/package.json new file mode 100644 index 00000000..54981a4c --- /dev/null +++ b/plugins/github-trending/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-github-trending", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for github-trending", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/github-trending/repos.js b/plugins/github-trending/repos.js similarity index 100% rename from clis/github-trending/repos.js rename to plugins/github-trending/repos.js diff --git a/clis/github-trending/repos.test.js b/plugins/github-trending/test/repos.test.js similarity index 99% rename from clis/github-trending/repos.test.js rename to plugins/github-trending/test/repos.test.js index c1f351b5..237f5878 100644 --- a/clis/github-trending/repos.test.js +++ b/plugins/github-trending/test/repos.test.js @@ -1,7 +1,7 @@ import { getRegistry } from '@agentrhq/webcmd/registry'; import { CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import './repos.js'; +import '../repos.js'; function loadCommand() { const cmd = getRegistry().get('github-trending/repos'); diff --git a/plugins/github-trending/webcmd-plugin.json b/plugins/github-trending/webcmd-plugin.json new file mode 100644 index 00000000..955c4851 --- /dev/null +++ b/plugins/github-trending/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "github-trending", + "version": "0.1.0", + "description": "Webcmd commands for github-trending", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/goproxy/README.md b/plugins/goproxy/README.md new file mode 100644 index 00000000..c9a859b7 --- /dev/null +++ b/plugins/goproxy/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-goproxy + +Webcmd commands for goproxy. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/goproxy +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd goproxy module` | Latest version + VCS origin metadata for a Go module on proxy.golang.org | +| `webcmd goproxy versions` | Published version tags for a Go module (newest first), optionally with publish times | diff --git a/clis/goproxy/module.js b/plugins/goproxy/module.js similarity index 100% rename from clis/goproxy/module.js rename to plugins/goproxy/module.js diff --git a/plugins/goproxy/package.json b/plugins/goproxy/package.json new file mode 100644 index 00000000..1892470e --- /dev/null +++ b/plugins/goproxy/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-goproxy", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for goproxy", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/goproxy/goproxy.test.js b/plugins/goproxy/test/goproxy.test.js similarity index 98% rename from clis/goproxy/goproxy.test.js rename to plugins/goproxy/test/goproxy.test.js index 50cd594e..124c3762 100644 --- a/clis/goproxy/goproxy.test.js +++ b/plugins/goproxy/test/goproxy.test.js @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './module.js'; -import './versions.js'; +import '../module.js'; +import '../versions.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/goproxy/utils.js b/plugins/goproxy/utils.js similarity index 100% rename from clis/goproxy/utils.js rename to plugins/goproxy/utils.js diff --git a/clis/goproxy/versions.js b/plugins/goproxy/versions.js similarity index 100% rename from clis/goproxy/versions.js rename to plugins/goproxy/versions.js diff --git a/plugins/goproxy/webcmd-plugin.json b/plugins/goproxy/webcmd-plugin.json new file mode 100644 index 00000000..63a5e609 --- /dev/null +++ b/plugins/goproxy/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "goproxy", + "version": "0.1.0", + "description": "Webcmd commands for goproxy", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/hackernews/README.md b/plugins/hackernews/README.md new file mode 100644 index 00000000..47727023 --- /dev/null +++ b/plugins/hackernews/README.md @@ -0,0 +1,23 @@ +# webcmd-plugin-hackernews + +Webcmd commands for hackernews. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/hackernews +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd hackernews ask` | Hacker News Ask HN posts | +| `webcmd hackernews best` | Hacker News best stories | +| `webcmd hackernews jobs` | Hacker News job postings | +| `webcmd hackernews new` | Hacker News newest stories | +| `webcmd hackernews read` | Read a Hacker News story and its comment tree | +| `webcmd hackernews search` | Search Hacker News stories | +| `webcmd hackernews show` | Hacker News Show HN posts | +| `webcmd hackernews top` | Hacker News top stories | +| `webcmd hackernews user` | Hacker News user profile | diff --git a/clis/hackernews/ask.js b/plugins/hackernews/ask.js similarity index 100% rename from clis/hackernews/ask.js rename to plugins/hackernews/ask.js diff --git a/clis/hackernews/best.js b/plugins/hackernews/best.js similarity index 100% rename from clis/hackernews/best.js rename to plugins/hackernews/best.js diff --git a/clis/hackernews/jobs.js b/plugins/hackernews/jobs.js similarity index 100% rename from clis/hackernews/jobs.js rename to plugins/hackernews/jobs.js diff --git a/clis/hackernews/new.js b/plugins/hackernews/new.js similarity index 100% rename from clis/hackernews/new.js rename to plugins/hackernews/new.js diff --git a/plugins/hackernews/package.json b/plugins/hackernews/package.json new file mode 100644 index 00000000..a887356e --- /dev/null +++ b/plugins/hackernews/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-hackernews", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for hackernews", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/hackernews/read.js b/plugins/hackernews/read.js similarity index 100% rename from clis/hackernews/read.js rename to plugins/hackernews/read.js diff --git a/clis/hackernews/search.js b/plugins/hackernews/search.js similarity index 100% rename from clis/hackernews/search.js rename to plugins/hackernews/search.js diff --git a/clis/hackernews/show.js b/plugins/hackernews/show.js similarity index 100% rename from clis/hackernews/show.js rename to plugins/hackernews/show.js diff --git a/clis/hackernews/hackernews.test.js b/plugins/hackernews/test/hackernews.test.js similarity index 96% rename from clis/hackernews/hackernews.test.js rename to plugins/hackernews/test/hackernews.test.js index 3400660f..dbd6169b 100644 --- a/clis/hackernews/hackernews.test.js +++ b/plugins/hackernews/test/hackernews.test.js @@ -2,14 +2,14 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, EmptyResultError } from '@agentrhq/webcmd/errors'; import { executePipeline } from '@agentrhq/webcmd/pipeline'; -import './top.js'; -import './best.js'; -import './ask.js'; -import './new.js'; -import './show.js'; -import './jobs.js'; -import './search.js'; -import './read.js'; +import '../top.js'; +import '../best.js'; +import '../ask.js'; +import '../new.js'; +import '../show.js'; +import '../jobs.js'; +import '../search.js'; +import '../read.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/hackernews/top.js b/plugins/hackernews/top.js similarity index 100% rename from clis/hackernews/top.js rename to plugins/hackernews/top.js diff --git a/clis/hackernews/user.js b/plugins/hackernews/user.js similarity index 100% rename from clis/hackernews/user.js rename to plugins/hackernews/user.js diff --git a/plugins/hackernews/webcmd-plugin.json b/plugins/hackernews/webcmd-plugin.json new file mode 100644 index 00000000..352b6a28 --- /dev/null +++ b/plugins/hackernews/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "hackernews", + "version": "0.1.0", + "description": "Webcmd commands for hackernews", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/scripts/silent-column-drop-baseline.json b/scripts/silent-column-drop-baseline.json index daa415de..9b54bb72 100644 --- a/scripts/silent-column-drop-baseline.json +++ b/scripts/silent-column-drop-baseline.json @@ -118,35 +118,35 @@ }, { "command": "binance/depth", - "file": "clis/binance/depth.js", + "file": "plugins/binance/depth.js", "missing": [ "select" ] }, { "command": "binance/gainers", - "file": "clis/binance/gainers.js", + "file": "plugins/binance/gainers.js", "missing": [ "sort_change" ] }, { "command": "binance/losers", - "file": "clis/binance/losers.js", + "file": "plugins/binance/losers.js", "missing": [ "sort_change" ] }, { "command": "binance/ticker", - "file": "clis/binance/ticker.js", + "file": "plugins/binance/ticker.js", "missing": [ "sort_volume" ] }, { "command": "binance/top", - "file": "clis/binance/top.js", + "file": "plugins/binance/top.js", "missing": [ "sort_volume" ] diff --git a/scripts/typed-error-lint-baseline.json b/scripts/typed-error-lint-baseline.json index 04a01cd2..ed3234d3 100644 --- a/scripts/typed-error-lint-baseline.json +++ b/scripts/typed-error-lint-baseline.json @@ -2,7 +2,7 @@ { "rule": "silent-clamp", "command": "apple-podcasts/episodes", - "file": "clis/apple-podcasts/episodes.js", + "file": "plugins/apple-podcasts/episodes.js", "line": 17, "text": "const limit = Math.max(1, Math.min(Number(args.limit), 200));", "occurrence": 0 @@ -10,7 +10,7 @@ { "rule": "silent-clamp", "command": "apple-podcasts/search", - "file": "clis/apple-podcasts/search.js", + "file": "plugins/apple-podcasts/search.js", "line": 19, "text": "const limit = Math.max(1, Math.min(Number(args.limit), 25));", "occurrence": 0 @@ -18,7 +18,7 @@ { "rule": "silent-clamp", "command": "apple-podcasts/top", - "file": "clis/apple-podcasts/top.js", + "file": "plugins/apple-podcasts/top.js", "line": 19, "text": "const limit = Math.max(1, Math.min(Number(args.limit), 100));", "occurrence": 0 @@ -26,7 +26,7 @@ { "rule": "silent-clamp", "command": "bbc/news", - "file": "clis/bbc/news.js", + "file": "plugins/bbc/news.js", "line": 17, "text": "const count = Math.min(kwargs.limit || 20, 50);", "occurrence": 0 @@ -74,7 +74,7 @@ { "rule": "silent-clamp", "command": "hackernews/ask", - "file": "clis/hackernews/ask.js", + "file": "plugins/hackernews/ask.js", "line": 16, "text": "{ limit: '${{ Math.min((args.limit ? args.limit : 20) + 10, 50) }}' },", "occurrence": 0 @@ -82,7 +82,7 @@ { "rule": "silent-clamp", "command": "hackernews/best", - "file": "clis/hackernews/best.js", + "file": "plugins/hackernews/best.js", "line": 16, "text": "{ limit: '${{ Math.min((args.limit ? args.limit : 20) + 10, 50) }}' },", "occurrence": 0 @@ -90,7 +90,7 @@ { "rule": "silent-clamp", "command": "hackernews/jobs", - "file": "clis/hackernews/jobs.js", + "file": "plugins/hackernews/jobs.js", "line": 16, "text": "{ limit: '${{ Math.min((args.limit ? args.limit : 20) + 10, 50) }}' },", "occurrence": 0 @@ -98,7 +98,7 @@ { "rule": "silent-clamp", "command": "hackernews/new", - "file": "clis/hackernews/new.js", + "file": "plugins/hackernews/new.js", "line": 16, "text": "{ limit: '${{ Math.min((args.limit ? args.limit : 20) + 10, 50) }}' },", "occurrence": 0 @@ -106,7 +106,7 @@ { "rule": "silent-clamp", "command": "hackernews/show", - "file": "clis/hackernews/show.js", + "file": "plugins/hackernews/show.js", "line": 16, "text": "{ limit: '${{ Math.min((args.limit ? args.limit : 20) + 10, 50) }}' },", "occurrence": 0 @@ -114,7 +114,7 @@ { "rule": "silent-clamp", "command": "hackernews/top", - "file": "clis/hackernews/top.js", + "file": "plugins/hackernews/top.js", "line": 16, "text": "{ limit: '${{ Math.min((args.limit ? args.limit : 20) + 10, 50) }}' },", "occurrence": 0 diff --git a/webcmd-plugin.json b/webcmd-plugin.json index 15961a11..072089d2 100644 --- a/webcmd-plugin.json +++ b/webcmd-plugin.json @@ -4,6 +4,66 @@ "description": "Webcmd plugin collection", "webcmd": ">=0.2.0", "plugins": { + "apple-podcasts": { + "path": "plugins/apple-podcasts", + "version": "0.1.0", + "description": "Webcmd commands for apple-podcasts", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "archive": { + "path": "plugins/archive", + "version": "0.1.0", + "description": "Webcmd commands for archive", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "arxiv": { + "path": "plugins/arxiv", + "version": "0.1.0", + "description": "Webcmd commands for arxiv", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "bbc": { + "path": "plugins/bbc", + "version": "0.1.0", + "description": "Webcmd commands for bbc", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "binance": { + "path": "plugins/binance", + "version": "0.1.0", + "description": "Webcmd commands for binance", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "bluesky": { + "path": "plugins/bluesky", + "version": "0.1.0", + "description": "Webcmd commands for bluesky", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "bmwblog": { "path": "plugins/bmwblog", "version": "0.1.0", @@ -24,6 +84,16 @@ "handle": "agentrhq" } }, + "coingecko": { + "path": "plugins/coingecko", + "version": "0.1.0", + "description": "Webcmd commands for coingecko", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "concordia": { "path": "plugins/concordia", "version": "0.1.0", @@ -34,6 +104,96 @@ "handle": "agentrhq" } }, + "crates": { + "path": "plugins/crates", + "version": "0.1.0", + "description": "Webcmd commands for crates", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "dblp": { + "path": "plugins/dblp", + "version": "0.1.0", + "description": "Webcmd commands for dblp", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "defillama": { + "path": "plugins/defillama", + "version": "0.1.0", + "description": "Webcmd commands for defillama", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "devto": { + "path": "plugins/devto", + "version": "0.1.0", + "description": "Webcmd commands for devto", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "dictionary": { + "path": "plugins/dictionary", + "version": "0.1.0", + "description": "Webcmd commands for dictionary", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "dockerhub": { + "path": "plugins/dockerhub", + "version": "0.1.0", + "description": "Webcmd commands for dockerhub", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "endoflife": { + "path": "plugins/endoflife", + "version": "0.1.0", + "description": "Webcmd commands for endoflife", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "flathub": { + "path": "plugins/flathub", + "version": "0.1.0", + "description": "Webcmd commands for flathub", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "github-trending": { + "path": "plugins/github-trending", + "version": "0.1.0", + "description": "Webcmd commands for github-trending", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "goettingen": { "path": "plugins/goettingen", "version": "0.1.0", @@ -44,6 +204,26 @@ "handle": "agentrhq" } }, + "goproxy": { + "path": "plugins/goproxy", + "version": "0.1.0", + "description": "Webcmd commands for goproxy", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "hackernews": { + "path": "plugins/hackernews", + "version": "0.1.0", + "description": "Webcmd commands for hackernews", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "heidelberg": { "path": "plugins/heidelberg", "version": "0.1.0", From 8d7f28bc84ad95acf78e9387dbe1a766b5173dfb Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 16:28:11 +0530 Subject: [PATCH 10/39] refactor: migrate package and Jira adapters to plugins --- cli-manifest.json | 3662 ++++------------ plugin-command-manifest.json | 3832 ++++++++++++----- plugins/homebrew/README.md | 17 + {clis => plugins}/homebrew/cask.js | 0 {clis => plugins}/homebrew/formula.js | 0 plugins/homebrew/package.json | 9 + {clis => plugins}/homebrew/popular.js | 0 {clis => plugins}/homebrew/utils.js | 0 plugins/homebrew/webcmd-plugin.json | 10 + plugins/jira/README.md | 19 + plugins/jira/atlassian.js | 342 ++ {clis => plugins}/jira/attachments.js | 2 +- {clis => plugins}/jira/comments.js | 0 {clis => plugins}/jira/issue.js | 0 {clis => plugins}/jira/links.js | 2 +- plugins/jira/package.json | 9 + {clis => plugins}/jira/search.js | 2 +- {clis => plugins}/jira/shared.js | 2 +- plugins/jira/test/atlassian.test.js | 117 + .../jira/test}/commands.test.js | 12 +- plugins/jira/webcmd-plugin.json | 10 + plugins/lesswrong/README.md | 29 + {clis => plugins}/lesswrong/_helpers.js | 0 {clis => plugins}/lesswrong/comments.js | 0 {clis => plugins}/lesswrong/curated.js | 0 {clis => plugins}/lesswrong/frontpage.js | 0 {clis => plugins}/lesswrong/new.js | 0 plugins/lesswrong/package.json | 9 + {clis => plugins}/lesswrong/read.js | 0 {clis => plugins}/lesswrong/sequences.js | 0 {clis => plugins}/lesswrong/shortform.js | 0 {clis => plugins}/lesswrong/tag.js | 0 {clis => plugins}/lesswrong/tags.js | 0 .../lesswrong/test}/frontpage.test.js | 6 +- {clis => plugins}/lesswrong/top-month.js | 0 {clis => plugins}/lesswrong/top-week.js | 0 {clis => plugins}/lesswrong/top-year.js | 0 {clis => plugins}/lesswrong/top.js | 0 {clis => plugins}/lesswrong/user-posts.js | 0 {clis => plugins}/lesswrong/user.js | 0 plugins/lesswrong/webcmd-plugin.json | 10 + plugins/lichess/README.md | 16 + plugins/lichess/package.json | 9 + .../lichess/test}/lichess.test.js | 4 +- {clis => plugins}/lichess/top.js | 0 {clis => plugins}/lichess/user.js | 0 {clis => plugins}/lichess/utils.js | 0 plugins/lichess/webcmd-plugin.json | 10 + plugins/lobsters/README.md | 20 + {clis => plugins}/lobsters/active.js | 0 {clis => plugins}/lobsters/domain.js | 0 {clis => plugins}/lobsters/hot.js | 0 {clis => plugins}/lobsters/newest.js | 0 plugins/lobsters/package.json | 9 + {clis => plugins}/lobsters/read.js | 0 {clis => plugins}/lobsters/tag.js | 0 .../lobsters/test}/lobsters.test.js | 10 +- plugins/lobsters/webcmd-plugin.json | 10 + plugins/maven/README.md | 16 + {clis => plugins}/maven/artifact.js | 0 plugins/maven/package.json | 9 + {clis => plugins}/maven/search.js | 0 {clis => plugins}/maven/utils.js | 0 plugins/maven/webcmd-plugin.json | 10 + plugins/mdn/README.md | 15 + plugins/mdn/package.json | 9 + {clis => plugins}/mdn/search.js | 0 plugins/mdn/webcmd-plugin.json | 10 + plugins/npm/README.md | 17 + {clis => plugins}/npm/downloads.js | 0 {clis => plugins}/npm/package.js | 0 plugins/npm/package.json | 9 + {clis => plugins}/npm/search.js | 0 {clis => plugins}/npm/utils.js | 0 plugins/npm/webcmd-plugin.json | 10 + plugins/nuget/README.md | 16 + {clis => plugins}/nuget/package.js | 0 plugins/nuget/package.json | 9 + {clis => plugins}/nuget/search.js | 0 .../nuget/test}/nuget.test.js | 4 +- {clis => plugins}/nuget/utils.js | 0 plugins/nuget/webcmd-plugin.json | 10 + plugins/nvd/README.md | 15 + {clis => plugins}/nvd/cve.js | 0 plugins/nvd/package.json | 9 + plugins/nvd/webcmd-plugin.json | 10 + plugins/oeis/README.md | 16 + plugins/oeis/package.json | 9 + {clis => plugins}/oeis/search.js | 0 {clis => plugins}/oeis/sequence.js | 0 {clis/oeis => plugins/oeis/test}/oeis.test.js | 4 +- {clis => plugins}/oeis/utils.js | 0 plugins/oeis/webcmd-plugin.json | 10 + plugins/openalex/README.md | 16 + plugins/openalex/package.json | 9 + {clis => plugins}/openalex/search.js | 0 {clis => plugins}/openalex/utils.js | 0 plugins/openalex/webcmd-plugin.json | 10 + {clis => plugins}/openalex/work.js | 0 plugins/openfda/README.md | 16 + {clis => plugins}/openfda/drug-label.js | 0 {clis => plugins}/openfda/food-recall.js | 0 plugins/openfda/package.json | 9 + .../openfda/test}/openfda.test.js | 4 +- {clis => plugins}/openfda/utils.js | 0 plugins/openfda/webcmd-plugin.json | 10 + plugins/openreview/README.md | 19 + {clis => plugins}/openreview/author.js | 0 plugins/openreview/package.json | 9 + {clis => plugins}/openreview/paper.js | 0 {clis => plugins}/openreview/reviews.js | 0 {clis => plugins}/openreview/search.js | 0 .../openreview/test}/openreview.test.js | 12 +- {clis => plugins}/openreview/utils.js | 0 {clis => plugins}/openreview/venue.js | 0 plugins/openreview/webcmd-plugin.json | 10 + scripts/plugin-local-runtime-loader.mjs | 25 + src/build-plugin-command-manifest.test.ts | 54 + src/build-plugin-command-manifest.ts | 14 +- webcmd-plugin.json | 140 + 120 files changed, 4980 insertions(+), 3783 deletions(-) create mode 100644 plugins/homebrew/README.md rename {clis => plugins}/homebrew/cask.js (100%) rename {clis => plugins}/homebrew/formula.js (100%) create mode 100644 plugins/homebrew/package.json rename {clis => plugins}/homebrew/popular.js (100%) rename {clis => plugins}/homebrew/utils.js (100%) create mode 100644 plugins/homebrew/webcmd-plugin.json create mode 100644 plugins/jira/README.md create mode 100644 plugins/jira/atlassian.js rename {clis => plugins}/jira/attachments.js (94%) rename {clis => plugins}/jira/comments.js (100%) rename {clis => plugins}/jira/issue.js (100%) rename {clis => plugins}/jira/links.js (93%) create mode 100644 plugins/jira/package.json rename {clis => plugins}/jira/search.js (97%) rename {clis => plugins}/jira/shared.js (99%) create mode 100644 plugins/jira/test/atlassian.test.js rename {clis/jira => plugins/jira/test}/commands.test.js (98%) create mode 100644 plugins/jira/webcmd-plugin.json create mode 100644 plugins/lesswrong/README.md rename {clis => plugins}/lesswrong/_helpers.js (100%) rename {clis => plugins}/lesswrong/comments.js (100%) rename {clis => plugins}/lesswrong/curated.js (100%) rename {clis => plugins}/lesswrong/frontpage.js (100%) rename {clis => plugins}/lesswrong/new.js (100%) create mode 100644 plugins/lesswrong/package.json rename {clis => plugins}/lesswrong/read.js (100%) rename {clis => plugins}/lesswrong/sequences.js (100%) rename {clis => plugins}/lesswrong/shortform.js (100%) rename {clis => plugins}/lesswrong/tag.js (100%) rename {clis => plugins}/lesswrong/tags.js (100%) rename {clis/lesswrong => plugins/lesswrong/test}/frontpage.test.js (92%) rename {clis => plugins}/lesswrong/top-month.js (100%) rename {clis => plugins}/lesswrong/top-week.js (100%) rename {clis => plugins}/lesswrong/top-year.js (100%) rename {clis => plugins}/lesswrong/top.js (100%) rename {clis => plugins}/lesswrong/user-posts.js (100%) rename {clis => plugins}/lesswrong/user.js (100%) create mode 100644 plugins/lesswrong/webcmd-plugin.json create mode 100644 plugins/lichess/README.md create mode 100644 plugins/lichess/package.json rename {clis/lichess => plugins/lichess/test}/lichess.test.js (98%) rename {clis => plugins}/lichess/top.js (100%) rename {clis => plugins}/lichess/user.js (100%) rename {clis => plugins}/lichess/utils.js (100%) create mode 100644 plugins/lichess/webcmd-plugin.json create mode 100644 plugins/lobsters/README.md rename {clis => plugins}/lobsters/active.js (100%) rename {clis => plugins}/lobsters/domain.js (100%) rename {clis => plugins}/lobsters/hot.js (100%) rename {clis => plugins}/lobsters/newest.js (100%) create mode 100644 plugins/lobsters/package.json rename {clis => plugins}/lobsters/read.js (100%) rename {clis => plugins}/lobsters/tag.js (100%) rename {clis/lobsters => plugins/lobsters/test}/lobsters.test.js (98%) create mode 100644 plugins/lobsters/webcmd-plugin.json create mode 100644 plugins/maven/README.md rename {clis => plugins}/maven/artifact.js (100%) create mode 100644 plugins/maven/package.json rename {clis => plugins}/maven/search.js (100%) rename {clis => plugins}/maven/utils.js (100%) create mode 100644 plugins/maven/webcmd-plugin.json create mode 100644 plugins/mdn/README.md create mode 100644 plugins/mdn/package.json rename {clis => plugins}/mdn/search.js (100%) create mode 100644 plugins/mdn/webcmd-plugin.json create mode 100644 plugins/npm/README.md rename {clis => plugins}/npm/downloads.js (100%) rename {clis => plugins}/npm/package.js (100%) create mode 100644 plugins/npm/package.json rename {clis => plugins}/npm/search.js (100%) rename {clis => plugins}/npm/utils.js (100%) create mode 100644 plugins/npm/webcmd-plugin.json create mode 100644 plugins/nuget/README.md rename {clis => plugins}/nuget/package.js (100%) create mode 100644 plugins/nuget/package.json rename {clis => plugins}/nuget/search.js (100%) rename {clis/nuget => plugins/nuget/test}/nuget.test.js (99%) rename {clis => plugins}/nuget/utils.js (100%) create mode 100644 plugins/nuget/webcmd-plugin.json create mode 100644 plugins/nvd/README.md rename {clis => plugins}/nvd/cve.js (100%) create mode 100644 plugins/nvd/package.json create mode 100644 plugins/nvd/webcmd-plugin.json create mode 100644 plugins/oeis/README.md create mode 100644 plugins/oeis/package.json rename {clis => plugins}/oeis/search.js (100%) rename {clis => plugins}/oeis/sequence.js (100%) rename {clis/oeis => plugins/oeis/test}/oeis.test.js (98%) rename {clis => plugins}/oeis/utils.js (100%) create mode 100644 plugins/oeis/webcmd-plugin.json create mode 100644 plugins/openalex/README.md create mode 100644 plugins/openalex/package.json rename {clis => plugins}/openalex/search.js (100%) rename {clis => plugins}/openalex/utils.js (100%) create mode 100644 plugins/openalex/webcmd-plugin.json rename {clis => plugins}/openalex/work.js (100%) create mode 100644 plugins/openfda/README.md rename {clis => plugins}/openfda/drug-label.js (100%) rename {clis => plugins}/openfda/food-recall.js (100%) create mode 100644 plugins/openfda/package.json rename {clis/openfda => plugins/openfda/test}/openfda.test.js (98%) rename {clis => plugins}/openfda/utils.js (100%) create mode 100644 plugins/openfda/webcmd-plugin.json create mode 100644 plugins/openreview/README.md rename {clis => plugins}/openreview/author.js (100%) create mode 100644 plugins/openreview/package.json rename {clis => plugins}/openreview/paper.js (100%) rename {clis => plugins}/openreview/reviews.js (100%) rename {clis => plugins}/openreview/search.js (100%) rename {clis/openreview => plugins/openreview/test}/openreview.test.js (99%) rename {clis => plugins}/openreview/utils.js (100%) rename {clis => plugins}/openreview/venue.js (100%) create mode 100644 plugins/openreview/webcmd-plugin.json create mode 100644 scripts/plugin-local-runtime-loader.mjs diff --git a/cli-manifest.json b/cli-manifest.json index 8ef3ec5c..c59779f4 100644 --- a/cli-manifest.json +++ b/cli-manifest.json @@ -8670,117 +8670,6 @@ "navigateBefore": false, "siteSession": "persistent" }, - { - "site": "homebrew", - "name": "cask", - "description": "Fetch a Homebrew cask's metadata (version, homepage, deprecation, download URL)", - "access": "read", - "domain": "formulae.brew.sh", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "token", - "type": "str", - "required": true, - "positional": true, - "help": "Cask token (e.g. \"firefox\", \"visual-studio-code\", \"google-chrome\")" - } - ], - "columns": [ - "cask", - "tap", - "name", - "version", - "description", - "homepage", - "deprecated", - "disabled", - "download", - "url" - ], - "type": "js", - "modulePath": "homebrew/cask.js", - "sourceFile": "homebrew/cask.js" - }, - { - "site": "homebrew", - "name": "formula", - "description": "Fetch a Homebrew formula's metadata (version, license, deps, deprecation, source)", - "access": "read", - "domain": "formulae.brew.sh", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Formula name (e.g. \"wget\", \"gcc@13\", \"imagemagick\")" - } - ], - "columns": [ - "formula", - "tap", - "version", - "license", - "description", - "homepage", - "dependencies", - "deprecated", - "disabled", - "source", - "url" - ], - "type": "js", - "modulePath": "homebrew/formula.js", - "sourceFile": "homebrew/formula.js" - }, - { - "site": "homebrew", - "name": "popular", - "description": "List most-installed Homebrew formulae or casks (Homebrew's analytics ranking)", - "access": "read", - "domain": "formulae.brew.sh", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "type", - "type": "str", - "default": "formula", - "required": false, - "help": "Package type (formula / cask)" - }, - { - "name": "window", - "type": "str", - "default": "30d", - "required": false, - "help": "Time window (30d / 90d / 365d)" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max rows (1-500)" - } - ], - "columns": [ - "rank", - "token", - "type", - "installs", - "percent", - "window", - "url" - ], - "type": "js", - "modulePath": "homebrew/popular.js", - "sourceFile": "homebrew/popular.js" - }, { "site": "imdb", "name": "person", @@ -9858,3058 +9747,1323 @@ "siteSession": "persistent" }, { - "site": "jira", - "name": "attachments", - "description": "Jira issue attachment metadata", + "site": "linkedin-learning", + "name": "course", + "description": "Get LinkedIn Learning course detail by slug or course URL", "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "key", - "type": "str", + "name": "slug", + "type": "string", "required": true, "positional": true, - "help": "Jira issue key, e.g. PROJ-123" + "help": "Course slug (e.g. agentic-ai-build-your-first-agentic-ai-system) or full /learning/ URL" } ], "columns": [ - "id", - "filename", - "mimeType", - "size", + "title", + "slug", + "description", + "difficulty", + "duration_sec", + "videos_count", + "rating", + "rating_count", + "released", "url" ], "type": "js", - "modulePath": "jira/attachments.js", - "sourceFile": "jira/attachments.js" + "modulePath": "linkedin-learning/course.js", + "sourceFile": "linkedin-learning/course.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "jira", - "name": "comments", - "description": "Jira issue comments as Markdown", + "site": "linkedin-learning", + "name": "login", + "description": "Open linkedin-learning login", + "access": "write", + "domain": "linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "public_id", + "plain_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "linkedin-learning/auth.js", + "sourceFile": "linkedin-learning/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "linkedin-learning", + "name": "search", + "description": "Search LinkedIn Learning courses, videos, and learning paths by keyword", "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "key", - "type": "str", + "name": "keywords", + "type": "string", "required": true, "positional": true, - "help": "Jira issue key, e.g. PROJ-123" + "help": "Search keywords, e.g. \"AI agent\"" }, { "name": "limit", "type": "int", - "default": 50, + "default": 10, "required": false, - "help": "Max comments to return (1-100)" + "help": "Maximum results to return (1-50)" } ], "columns": [ - "id", - "author", - "created", - "updated", - "markdown" + "rank", + "type", + "title", + "instructor", + "difficulty", + "duration_sec", + "rating", + "rating_count", + "viewers", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "jira/comments.js", - "sourceFile": "jira/comments.js" + "modulePath": "linkedin-learning/search.js", + "sourceFile": "linkedin-learning/search.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "jira", - "name": "issue", - "description": "Jira issue detail normalized for agents (description, comments, attachments, links)", + "site": "linkedin-learning", + "name": "trending", + "description": "Browse LinkedIn Learning recommended courses across personalized carousels", "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Jira issue key, e.g. PROJ-123" - }, - { - "name": "comments-limit", + "name": "limit", "type": "int", - "default": 100, + "default": 10, "required": false, - "help": "Max comments to include (1-100)" + "help": "Maximum results to return (1-50)" } ], "columns": [ - "key", - "summary", - "issueType", - "status", - "priority", - "assignee", - "updated", + "rank", + "group", + "type", + "title", + "difficulty", + "viewers", "url" ], "type": "js", - "modulePath": "jira/issue.js", - "sourceFile": "jira/issue.js" + "modulePath": "linkedin-learning/trending.js", + "sourceFile": "linkedin-learning/trending.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "jira", - "name": "links", - "description": "Jira issue links", + "site": "linkedin-learning", + "name": "whoami", + "description": "Show the current logged-in linkedin-learning account", "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Jira issue key, e.g. PROJ-123" - } - ], + "domain": "linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "key", - "type", - "direction" + "logged_in", + "site", + "public_id", + "plain_id", + "name" ], "type": "js", - "modulePath": "jira/links.js", - "sourceFile": "jira/links.js" + "modulePath": "linkedin-learning/auth.js", + "sourceFile": "linkedin-learning/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "jira", - "name": "search", - "description": "Search Jira issues with JQL", + "site": "manus", + "name": "connectors", + "description": "List available Manus connectors (integrations).", "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "domain": "manus.im", + "strategy": "cookie", + "browser": true, "args": [ - { - "name": "jql", - "type": "str", - "required": true, - "positional": true, - "help": "JQL query, e.g. \"project = PROJ order by updated desc\"" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 50, "required": false, - "help": "Max issues to return (1-100)" + "help": "Max connectors to return" } ], "columns": [ - "key", - "summary", - "issueType", - "status", - "priority", - "assignee", - "updated", - "url" - ], - "tags": [ - "search" + "UID", + "Name", + "Brief" ], "type": "js", - "modulePath": "jira/search.js", - "sourceFile": "jira/search.js" + "modulePath": "manus/connectors.js", + "sourceFile": "manus/connectors.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "lesswrong", - "name": "comments", - "description": "Top comments on a post", + "site": "manus", + "name": "credits", + "description": "Show Manus credit balance and refresh details.", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "url-or-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post URL or LessWrong post ID" - }, - { - "name": "limit", - "type": "int", - "default": 5, - "required": false, - "help": "Number of comments" - } - ], + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "score", - "author", - "text" + "Field", + "Value" ], "type": "js", - "modulePath": "lesswrong/comments.js", - "sourceFile": "lesswrong/comments.js" + "modulePath": "manus/credits.js", + "sourceFile": "manus/credits.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "lesswrong", - "name": "curated", - "description": "Curated editor's picks", + "site": "manus", + "name": "list", + "description": "List Manus sessions (tasks).", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "manus.im", + "strategy": "cookie", + "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results" + "help": "Max sessions to return" + }, + { + "name": "archived", + "type": "bool", + "default": false, + "required": false, + "help": "Include archived sessions" } ], "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" + "id", + "Title", + "Status", + "Last Message", + "Last Updated", + "Credits" ], "type": "js", - "modulePath": "lesswrong/curated.js", - "sourceFile": "lesswrong/curated.js" + "modulePath": "manus/list.js", + "sourceFile": "manus/list.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "lesswrong", - "name": "frontpage", - "description": "Algorithmic frontpage", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], + "site": "manus", + "name": "login", + "description": "Open manus login", + "access": "write", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" ], "type": "js", - "modulePath": "lesswrong/frontpage.js", - "sourceFile": "lesswrong/frontpage.js" + "modulePath": "manus/auth.js", + "sourceFile": "manus/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "lesswrong", - "name": "new", - "description": "Latest posts", + "site": "manus", + "name": "read", + "description": "Show details for a specific Manus session.", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "manus.im", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" + "name": "uid", + "type": "str", + "required": true, + "positional": true, + "help": "Session UID" } ], "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" + "Field", + "Value" ], "type": "js", - "modulePath": "lesswrong/new.js", - "sourceFile": "lesswrong/new.js" + "modulePath": "manus/read.js", + "sourceFile": "manus/read.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "lesswrong", - "name": "read", - "description": "Read full post by URL or ID", + "site": "manus", + "name": "skills", + "description": "List Manus skills (user-added and system).", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "url-or-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post URL or LessWrong post ID" - } - ], + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "title", - "author", - "karma", - "comments", - "tags", - "content", - "url" + "ID", + "Name", + "Description", + "Source" ], "type": "js", - "modulePath": "lesswrong/read.js", - "sourceFile": "lesswrong/read.js" + "modulePath": "manus/skills.js", + "sourceFile": "manus/skills.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "lesswrong", - "name": "sequences", - "description": "List post collections", + "site": "manus", + "name": "status", + "description": "Show current Manus user profile and credit summary.", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Field", + "Value" ], + "type": "js", + "modulePath": "manus/status.js", + "sourceFile": "manus/status.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "whoami", + "description": "Show the current logged-in manus account", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "title", - "author" + "logged_in", + "site", + "user_id", + "name" ], "type": "js", - "modulePath": "lesswrong/sequences.js", - "sourceFile": "lesswrong/sequences.js" + "modulePath": "manus/auth.js", + "sourceFile": "manus/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "lesswrong", - "name": "shortform", - "description": "Quick takes / shortform posts", + "site": "medium", + "name": "feed", + "description": "Medium popular posts Feed", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "medium.com", + "strategy": "cookie", + "browser": true, "args": [ + { + "name": "topic", + "type": "str", + "default": "", + "required": false, + "help": "Topic (for example technology, programming, ai)" + }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results" + "help": "Number of posts to return" } ], "columns": [ "rank", "title", "author", - "karma", - "comments", - "url" + "date", + "readTime", + "claps" ], "type": "js", - "modulePath": "lesswrong/shortform.js", - "sourceFile": "lesswrong/shortform.js" + "modulePath": "medium/feed.js", + "sourceFile": "medium/feed.js", + "navigateBefore": "https://medium.com" }, { - "site": "lesswrong", - "name": "tag", - "description": "Posts by tag", + "site": "medium", + "name": "search", + "description": "Search Medium posts", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "medium.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "tag", - "type": "string", + "name": "keyword", + "type": "str", "required": true, "positional": true, - "help": "Tag slug or name" + "help": "Search keyword" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results" + "help": "Number of posts to return" } ], "columns": [ "rank", "title", "author", - "karma", - "comments", - "url" - ], - "type": "js", - "modulePath": "lesswrong/tag.js", - "sourceFile": "lesswrong/tag.js" - }, - { - "site": "lesswrong", - "name": "tags", - "description": "List popular tags", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } + "date", + "readTime", + "claps", + "url" ], - "columns": [ - "rank", - "name", - "posts" + "tags": [ + "search" ], "type": "js", - "modulePath": "lesswrong/tags.js", - "sourceFile": "lesswrong/tags.js" + "modulePath": "medium/search.js", + "sourceFile": "medium/search.js", + "navigateBefore": "https://medium.com" }, { - "site": "lesswrong", - "name": "top", - "description": "Top all-time", + "site": "medium", + "name": "tag", + "description": "Latest Medium articles tagged with a given keyword (RSS feed)", "access": "read", - "domain": "www.lesswrong.com", + "domain": "medium.com", "strategy": "public", "browser": false, "args": [ + { + "name": "tag", + "type": "str", + "required": true, + "positional": true, + "help": "Lowercase tag slug (e.g. \"programming\", \"machine-learning\")" + }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results" + "help": "Max articles (1-25 — single RSS page)" } ], "columns": [ "rank", "title", "author", - "karma", - "comments", + "description", + "categories", + "published", "url" ], "type": "js", - "modulePath": "lesswrong/top.js", - "sourceFile": "lesswrong/top.js" + "modulePath": "medium/tag.js", + "sourceFile": "medium/tag.js" }, { - "site": "lesswrong", - "name": "top-month", - "description": "Top this month", + "site": "medium", + "name": "user", + "description": "Get Medium user posts", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "medium.com", + "strategy": "cookie", + "browser": true, "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Medium username(for example @username or username)" + }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results" + "help": "Number of posts to return" } ], "columns": [ "rank", "title", - "author", - "karma", - "comments", + "date", + "readTime", + "claps", "url" ], "type": "js", - "modulePath": "lesswrong/top-month.js", - "sourceFile": "lesswrong/top-month.js" + "modulePath": "medium/user.js", + "sourceFile": "medium/user.js", + "navigateBefore": "https://medium.com" }, { - "site": "lesswrong", - "name": "top-week", - "description": "Top this week", + "site": "mercury", + "name": "check-login", + "description": "Open Mercury reimbursements and report whether the active browser profile is logged in", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], + "example": "webcmd --profile mercury check-login -f json", + "domain": "app.mercury.com", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" + "status", + "loggedIn", + "url", + "hasSubmitExpense", + "hasReimbursements", + "title" ], "type": "js", - "modulePath": "lesswrong/top-week.js", - "sourceFile": "lesswrong/top-week.js" + "modulePath": "mercury/check-login.js", + "sourceFile": "mercury/check-login.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "lesswrong", - "name": "top-year", - "description": "Top this year", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "site": "mercury", + "name": "reimbursement-draft", + "description": "Create a Mercury reimbursement draft from a local receipt, correct OCR fields, and stop at Review", + "access": "write", + "example": "webcmd --profile mercury reimbursement-draft --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", + "domain": "app.mercury.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 10, + "name": "receipt", + "type": "str", + "required": true, + "help": "Local receipt/proof file path", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp", + "application/pdf" + ], + "maxBytes": 26214400 + } + }, + { + "name": "amount", + "type": "str", + "required": true, + "help": "Original-currency amount, e.g. 140.00" + }, + { + "name": "currency", + "type": "str", + "default": "CNY", "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" - ], - "type": "js", - "modulePath": "lesswrong/top-year.js", - "sourceFile": "lesswrong/top-year.js" - }, - { - "site": "lesswrong", - "name": "user", - "description": "User profile", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ + "help": "Original currency code" + }, { - "name": "username", - "type": "string", + "name": "date", + "type": "str", "required": true, - "positional": true, - "help": "LessWrong username or slug" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "lesswrong/user.js", - "sourceFile": "lesswrong/user.js" - }, - { - "site": "lesswrong", - "name": "user-posts", - "description": "List a user's posts", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ + "help": "Expense date as YYYY-MM-DD" + }, { - "name": "username", - "type": "string", + "name": "merchant", + "type": "str", "required": true, - "positional": true, - "help": "LessWrong username or slug" + "help": "Merchant shown on the reimbursement" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "category", + "type": "str", + "default": "Marketing & Advertising", "required": false, - "help": "Number of results" + "help": "Mercury expense category" + }, + { + "name": "notes", + "type": "str", + "required": true, + "help": "Business purpose / reimbursement notes" + }, + { + "name": "ocr-wait-seconds", + "type": "str", + "default": "8", + "required": false, + "help": "Seconds to wait after receipt upload before correcting OCR-overwritten fields" + }, + { + "name": "close-after-review", + "type": "boolean", + "default": false, + "required": false, + "help": "Close the Review dialog after verification; final Submit is still never clicked" } ], "columns": [ - "rank", - "title", - "karma", - "comments", - "date", - "url" + "status", + "url", + "receipt", + "uploaded", + "fieldsTouched", + "reviewReady", + "submitBlocked", + "warnings" ], "type": "js", - "modulePath": "lesswrong/user-posts.js", - "sourceFile": "lesswrong/user-posts.js" + "modulePath": "mercury/reimbursement-draft.js", + "sourceFile": "mercury/reimbursement-draft.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "lichess", - "name": "top", - "description": "Top-N Lichess leaderboard for a perf type (bullet/blitz/rapid/classical/...)", + "site": "mercury", + "name": "reimbursement-plan", + "description": "Validate Mercury reimbursement inputs and print the draft plan without opening a browser", "access": "read", - "domain": "lichess.org", - "strategy": "public", + "example": "webcmd mercury reimbursement-plan --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", + "strategy": "local", "browser": false, "args": [ { - "name": "perf", + "name": "receipt", "type": "str", "required": true, - "positional": true, - "help": "Perf type (bullet, blitz, rapid, classical, ultraBullet, chess960, ...)" + "help": "Local receipt/proof file path" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "amount", + "type": "str", + "required": true, + "help": "Original-currency amount, e.g. 140.00" + }, + { + "name": "currency", + "type": "str", + "default": "CNY", + "required": false, + "help": "Original currency code" + }, + { + "name": "date", + "type": "str", + "required": true, + "help": "Expense date as YYYY-MM-DD" + }, + { + "name": "merchant", + "type": "str", + "required": true, + "help": "Merchant shown on the reimbursement" + }, + { + "name": "category", + "type": "str", + "default": "Marketing & Advertising", + "required": false, + "help": "Mercury expense category" + }, + { + "name": "notes", + "type": "str", + "required": true, + "help": "Business purpose / reimbursement notes" + }, + { + "name": "ocr-wait-seconds", + "type": "str", + "default": "8", + "required": false, + "help": "Seconds the draft command waits after receipt upload before correcting OCR fields" + }, + { + "name": "close-after-review", + "type": "boolean", + "default": false, "required": false, - "help": "Top-N rows (1-200)" + "help": "For draft command: close the Review dialog after verification" } ], "columns": [ - "rank", - "username", - "id", - "title", - "rating", - "progress", - "patron", - "url" + "status", + "receipt", + "amount", + "currency", + "date", + "merchant", + "category", + "notes", + "safety" ], "type": "js", - "modulePath": "lichess/top.js", - "sourceFile": "lichess/top.js" + "modulePath": "mercury/reimbursement-plan.js", + "sourceFile": "mercury/reimbursement-plan.js" }, { - "site": "lichess", - "name": "user", - "description": "Fetch a Lichess player profile by username (rating, perfs, counts)", - "access": "read", - "domain": "lichess.org", - "strategy": "public", - "browser": false, + "site": "notebooklm", + "name": "add-source", + "description": "Add a URL, text, or local file source to an existing NotebookLM notebook", + "access": "write", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "username", + "name": "notebook", "type": "str", "required": true, "positional": true, - "help": "Lichess username (case-insensitive)" + "help": "Notebook id from `notebooklm list` or full notebook URL" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Source URL to add (http/https). Pass exactly one of --url, --content, --file." + }, + { + "name": "content", + "type": "str", + "required": false, + "help": "Raw text content to add as a Text source (max 10 MB)." + }, + { + "name": "file", + "type": "str", + "required": false, + "help": "Local file path to upload as a source (max 52428800 bytes; pdf / txt / md / html / docx / etc.). Uses Google Drive's 3-step resumable upload protocol." + }, + { + "name": "title", + "type": "str", + "required": false, + "help": "Title for the text source (default \"Text Source\"). Ignored for --url and --file." + }, + { + "name": "mime-type", + "type": "str", + "required": false, + "help": "Override the auto-detected MIME type when --file is given." + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually add the remote source to the NotebookLM notebook" } ], "columns": [ - "username", - "id", - "title", - "patron", - "online", - "tosViolation", - "createdAt", - "seenAt", - "gamesAll", - "gamesWin", - "gamesLoss", - "gamesDraw", - "topPerfName", - "topPerfRating", - "topPerfGames", - "fideRating", - "country", - "bio", - "url" + "notebook_id", + "source_id", + "kind", + "identifier", + "notebook_url" ], "type": "js", - "modulePath": "lichess/user.js", - "sourceFile": "lichess/user.js" + "modulePath": "notebooklm/add-source.js", + "sourceFile": "notebooklm/add-source.js", + "navigateBefore": false }, { - "site": "linkedin-learning", - "name": "course", - "description": "Get LinkedIn Learning course detail by slug or course URL", - "access": "read", - "domain": "www.linkedin.com", + "site": "notebooklm", + "name": "create", + "description": "Create a new NotebookLM notebook with the given title", + "access": "write", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "slug", - "type": "string", + "name": "title", + "type": "str", "required": true, "positional": true, - "help": "Course slug (e.g. agentic-ai-build-your-first-agentic-ai-system) or full /learning/ URL" + "help": "Notebook title (1-200 chars)" + }, + { + "name": "emoji", + "type": "str", + "required": false, + "help": "Notebook emoji icon (default 📒)" + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually create the remote NotebookLM notebook" } ], "columns": [ + "id", "title", - "slug", - "description", - "difficulty", - "duration_sec", - "videos_count", - "rating", - "rating_count", - "released", + "emoji", "url" ], "type": "js", - "modulePath": "linkedin-learning/course.js", - "sourceFile": "linkedin-learning/course.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "notebooklm/create.js", + "sourceFile": "notebooklm/create.js", + "navigateBefore": false }, { - "site": "linkedin-learning", - "name": "login", - "description": "Open linkedin-learning login", - "access": "write", - "domain": "linkedin.com", + "site": "notebooklm", + "name": "current", + "description": "Show metadata for the currently opened NotebookLM notebook tab", + "access": "read", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "status", - "logged_in", - "site", - "public_id", - "plain_id", - "name", - "action", - "verify_command" + "id", + "title", + "url", + "source" ], "type": "js", - "modulePath": "linkedin-learning/auth.js", - "sourceFile": "linkedin-learning/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/current.js", + "sourceFile": "notebooklm/current.js", + "navigateBefore": false }, { - "site": "linkedin-learning", - "name": "search", - "description": "Search LinkedIn Learning courses, videos, and learning paths by keyword", - "access": "read", - "domain": "www.linkedin.com", + "site": "notebooklm", + "name": "generate-audio", + "description": "Trigger an Audio Overview (Deep Dive podcast) generation for a NotebookLM notebook, using all of its sources", + "access": "write", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "keywords", - "type": "string", + "name": "notebook", + "type": "str", "required": true, "positional": true, - "help": "Search keywords, e.g. \"AI agent\"" + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "execute", + "type": "boolean", "required": false, - "help": "Maximum results to return (1-50)" + "help": "Actually trigger remote NotebookLM audio generation" } ], "columns": [ - "rank", - "type", - "title", - "instructor", - "difficulty", - "duration_sec", - "rating", - "rating_count", - "viewers", - "url" - ], - "tags": [ - "search" + "notebook_id", + "audio_id", + "source_count", + "status", + "notebook_url" ], "type": "js", - "modulePath": "linkedin-learning/search.js", - "sourceFile": "linkedin-learning/search.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "notebooklm/generate-audio.js", + "sourceFile": "notebooklm/generate-audio.js", + "navigateBefore": false }, { - "site": "linkedin-learning", - "name": "trending", - "description": "Browse LinkedIn Learning recommended courses across personalized carousels", - "access": "read", - "domain": "www.linkedin.com", + "site": "notebooklm", + "name": "generate-slides", + "description": "Trigger a Slide Deck (AI presentation) generation for a NotebookLM notebook, using all of its sources", + "access": "write", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 10, + "name": "notebook", + "type": "str", + "required": true, + "positional": true, + "help": "Notebook id from `notebooklm list` or full notebook URL" + }, + { + "name": "length", + "type": "str", "required": false, - "help": "Maximum results to return (1-50)" + "help": "Slide deck length: 1=Short, 3=Default (default 3)" + }, + { + "name": "language", + "type": "str", + "required": false, + "help": "Language code (default en)" + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually trigger remote NotebookLM slide deck generation" } ], "columns": [ - "rank", - "group", - "type", - "title", - "difficulty", - "viewers", - "url" + "notebook_id", + "slides_id", + "source_count", + "status", + "notebook_url" ], "type": "js", - "modulePath": "linkedin-learning/trending.js", - "sourceFile": "linkedin-learning/trending.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "notebooklm/generate-slides.js", + "sourceFile": "notebooklm/generate-slides.js", + "navigateBefore": false }, { - "site": "linkedin-learning", - "name": "whoami", - "description": "Show the current logged-in linkedin-learning account", + "site": "notebooklm", + "name": "get", + "aliases": [ + "metadata" + ], + "description": "Get rich metadata for the currently opened NotebookLM notebook", "access": "read", - "domain": "linkedin.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "public_id", - "plain_id", - "name" + "id", + "title", + "emoji", + "source_count", + "created_at", + "updated_at", + "url", + "source" ], "type": "js", - "modulePath": "linkedin-learning/auth.js", - "sourceFile": "linkedin-learning/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/get.js", + "sourceFile": "notebooklm/get.js", + "navigateBefore": false }, { - "site": "lobsters", - "name": "active", - "description": "Lobste.rs most active discussions", + "site": "notebooklm", + "name": "history", + "description": "List NotebookLM conversation history threads in the current notebook", "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", + "thread_id", + "item_count", + "preview", + "source", + "notebook_id", "url" ], "type": "js", - "modulePath": "lobsters/active.js", - "sourceFile": "lobsters/active.js" + "modulePath": "notebooklm/history.js", + "sourceFile": "notebooklm/history.js", + "navigateBefore": false }, { - "site": "lobsters", - "name": "domain", - "description": "Lobste.rs stories submitted from a specific domain", + "site": "notebooklm", + "name": "list", + "description": "List NotebookLM notebooks via in-page batchexecute RPC in the current logged-in session", "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "domain", - "type": "str", - "required": true, - "positional": true, - "help": "Source domain (e.g. github.com, arxiv.org, blog.cloudflare.com)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories (1-25 — single page)" - } - ], + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "id", "title", - "score", - "author", - "comments", + "id", + "is_owner", "created_at", - "tags", - "submission_url", - "comments_url" + "source", + "url" ], "type": "js", - "modulePath": "lobsters/domain.js", - "sourceFile": "lobsters/domain.js" + "modulePath": "notebooklm/list.js", + "sourceFile": "notebooklm/list.js", + "navigateBefore": false }, { - "site": "lobsters", - "name": "hot", - "description": "Lobste.rs hottest stories", - "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], + "site": "notebooklm", + "name": "login", + "description": "Open notebooklm login", + "access": "write", + "domain": "google.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "url" + "status", + "logged_in", + "site", + "name", + "authuser", + "action", + "verify_command" ], "type": "js", - "modulePath": "lobsters/hot.js", - "sourceFile": "lobsters/hot.js" + "modulePath": "notebooklm/auth.js", + "sourceFile": "notebooklm/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "lobsters", - "name": "newest", - "description": "Lobste.rs newest stories", - "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } + "site": "notebooklm", + "name": "note-list", + "aliases": [ + "notes-list" ], + "description": "List saved notes from the Studio panel of the current NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "id", "title", - "score", - "author", - "comments", "created_at", - "tags", + "source", "url" ], "type": "js", - "modulePath": "lobsters/newest.js", - "sourceFile": "lobsters/newest.js" + "modulePath": "notebooklm/note-list.js", + "sourceFile": "notebooklm/note-list.js", + "navigateBefore": false }, { - "site": "lobsters", - "name": "read", - "description": "Read a Lobste.rs story and its comment tree", + "site": "notebooklm", + "name": "notes-get", + "description": "Get one note from the current NotebookLM notebook by title from the visible note editor", "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "note", "type": "str", "required": true, "positional": true, - "help": "Lobste.rs short_id (e.g. 6cmh6h)" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max top-level comments" - }, - { - "name": "depth", - "type": "int", - "default": 2, - "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" - }, - { - "name": "replies", - "type": "int", - "default": 5, - "required": false, - "help": "Max replies shown per comment at each level" - }, - { - "name": "max-length", - "type": "int", - "default": 2000, - "required": false, - "help": "Max characters per comment body (min 100)" - } - ], - "columns": [ - "type", - "author", - "score", - "text" - ], - "type": "js", - "modulePath": "lobsters/read.js", - "sourceFile": "lobsters/read.js" - }, - { - "site": "lobsters", - "name": "tag", - "description": "Lobste.rs stories by tag", - "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "tag", - "type": "str", - "required": true, - "positional": true, - "help": "Tag name (e.g. programming, rust, security, ai)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "url" - ], - "type": "js", - "modulePath": "lobsters/tag.js", - "sourceFile": "lobsters/tag.js" - }, - { - "site": "manus", - "name": "connectors", - "description": "List available Manus connectors (integrations).", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max connectors to return" - } - ], - "columns": [ - "UID", - "Name", - "Brief" - ], - "type": "js", - "modulePath": "manus/connectors.js", - "sourceFile": "manus/connectors.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "credits", - "description": "Show Manus credit balance and refresh details.", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "manus/credits.js", - "sourceFile": "manus/credits.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "list", - "description": "List Manus sessions (tasks).", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max sessions to return" - }, - { - "name": "archived", - "type": "bool", - "default": false, - "required": false, - "help": "Include archived sessions" - } - ], - "columns": [ - "id", - "Title", - "Status", - "Last Message", - "Last Updated", - "Credits" - ], - "type": "js", - "modulePath": "manus/list.js", - "sourceFile": "manus/list.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "login", - "description": "Open manus login", - "access": "write", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "manus/auth.js", - "sourceFile": "manus/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "read", - "description": "Show details for a specific Manus session.", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "uid", - "type": "str", - "required": true, - "positional": true, - "help": "Session UID" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "manus/read.js", - "sourceFile": "manus/read.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "skills", - "description": "List Manus skills (user-added and system).", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "ID", - "Name", - "Description", - "Source" - ], - "type": "js", - "modulePath": "manus/skills.js", - "sourceFile": "manus/skills.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "status", - "description": "Show current Manus user profile and credit summary.", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "manus/status.js", - "sourceFile": "manus/status.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "whoami", - "description": "Show the current logged-in manus account", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "manus/auth.js", - "sourceFile": "manus/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "maven", - "name": "artifact", - "description": "Fetch a Maven Central artifact's version history (groupId:artifactId[:version])", - "access": "read", - "domain": "search.maven.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "coordinate", - "type": "str", - "required": true, - "positional": true, - "help": "Maven coord \"groupId:artifactId\" or \"groupId:artifactId:version\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max versions (1-200, ignored when version is pinned)" - } - ], - "columns": [ - "groupId", - "artifactId", - "version", - "packaging", - "publishedAt", - "tags", - "url" - ], - "type": "js", - "modulePath": "maven/artifact.js", - "sourceFile": "maven/artifact.js" - }, - { - "site": "maven", - "name": "search", - "description": "Search Maven Central by keyword (artifact name, groupId, tag)", - "access": "read", - "domain": "search.maven.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"jackson\", \"guava\", \"ai.koog\")" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max artifacts (1-200)" - } - ], - "columns": [ - "rank", - "coordinate", - "groupId", - "artifactId", - "latestVersion", - "packaging", - "versions", - "lastPublished", - "repository", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "maven/search.js", - "sourceFile": "maven/search.js" - }, - { - "site": "mdn", - "name": "search", - "description": "Search MDN Web Docs by keyword", - "access": "read", - "domain": "developer.mozilla.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"fetch\", \"flexbox\", \"Array.prototype.map\")" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max results (1-50)" - }, - { - "name": "locale", - "type": "str", - "default": "en-US", - "required": false, - "help": "Doc locale (en-US default; de / es / fr / ja / ko / pt-BR / ru / zh-CN / zh-TW)" - } - ], - "columns": [ - "rank", - "title", - "slug", - "locale", - "summary", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "mdn/search.js", - "sourceFile": "mdn/search.js" - }, - { - "site": "medium", - "name": "feed", - "description": "Medium popular posts Feed", - "access": "read", - "domain": "medium.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "topic", - "type": "str", - "default": "", - "required": false, - "help": "Topic (for example technology, programming, ai)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" - } - ], - "columns": [ - "rank", - "title", - "author", - "date", - "readTime", - "claps" - ], - "type": "js", - "modulePath": "medium/feed.js", - "sourceFile": "medium/feed.js", - "navigateBefore": "https://medium.com" - }, - { - "site": "medium", - "name": "search", - "description": "Search Medium posts", - "access": "read", - "domain": "medium.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" - } - ], - "columns": [ - "rank", - "title", - "author", - "date", - "readTime", - "claps", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "medium/search.js", - "sourceFile": "medium/search.js", - "navigateBefore": "https://medium.com" - }, - { - "site": "medium", - "name": "tag", - "description": "Latest Medium articles tagged with a given keyword (RSS feed)", - "access": "read", - "domain": "medium.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "tag", - "type": "str", - "required": true, - "positional": true, - "help": "Lowercase tag slug (e.g. \"programming\", \"machine-learning\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max articles (1-25 — single RSS page)" - } - ], - "columns": [ - "rank", - "title", - "author", - "description", - "categories", - "published", - "url" - ], - "type": "js", - "modulePath": "medium/tag.js", - "sourceFile": "medium/tag.js" - }, - { - "site": "medium", - "name": "user", - "description": "Get Medium user posts", - "access": "read", - "domain": "medium.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Medium username(for example @username or username)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" - } - ], - "columns": [ - "rank", - "title", - "date", - "readTime", - "claps", - "url" - ], - "type": "js", - "modulePath": "medium/user.js", - "sourceFile": "medium/user.js", - "navigateBefore": "https://medium.com" - }, - { - "site": "mercury", - "name": "check-login", - "description": "Open Mercury reimbursements and report whether the active browser profile is logged in", - "access": "read", - "example": "webcmd --profile mercury check-login -f json", - "domain": "app.mercury.com", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "status", - "loggedIn", - "url", - "hasSubmitExpense", - "hasReimbursements", - "title" - ], - "type": "js", - "modulePath": "mercury/check-login.js", - "sourceFile": "mercury/check-login.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "mercury", - "name": "reimbursement-draft", - "description": "Create a Mercury reimbursement draft from a local receipt, correct OCR fields, and stop at Review", - "access": "write", - "example": "webcmd --profile mercury reimbursement-draft --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", - "domain": "app.mercury.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "receipt", - "type": "str", - "required": true, - "help": "Local receipt/proof file path", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp", - "application/pdf" - ], - "maxBytes": 26214400 - } - }, - { - "name": "amount", - "type": "str", - "required": true, - "help": "Original-currency amount, e.g. 140.00" - }, - { - "name": "currency", - "type": "str", - "default": "CNY", - "required": false, - "help": "Original currency code" - }, - { - "name": "date", - "type": "str", - "required": true, - "help": "Expense date as YYYY-MM-DD" - }, - { - "name": "merchant", - "type": "str", - "required": true, - "help": "Merchant shown on the reimbursement" - }, - { - "name": "category", - "type": "str", - "default": "Marketing & Advertising", - "required": false, - "help": "Mercury expense category" - }, - { - "name": "notes", - "type": "str", - "required": true, - "help": "Business purpose / reimbursement notes" - }, - { - "name": "ocr-wait-seconds", - "type": "str", - "default": "8", - "required": false, - "help": "Seconds to wait after receipt upload before correcting OCR-overwritten fields" - }, - { - "name": "close-after-review", - "type": "boolean", - "default": false, - "required": false, - "help": "Close the Review dialog after verification; final Submit is still never clicked" - } - ], - "columns": [ - "status", - "url", - "receipt", - "uploaded", - "fieldsTouched", - "reviewReady", - "submitBlocked", - "warnings" - ], - "type": "js", - "modulePath": "mercury/reimbursement-draft.js", - "sourceFile": "mercury/reimbursement-draft.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "mercury", - "name": "reimbursement-plan", - "description": "Validate Mercury reimbursement inputs and print the draft plan without opening a browser", - "access": "read", - "example": "webcmd mercury reimbursement-plan --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "receipt", - "type": "str", - "required": true, - "help": "Local receipt/proof file path" - }, - { - "name": "amount", - "type": "str", - "required": true, - "help": "Original-currency amount, e.g. 140.00" - }, - { - "name": "currency", - "type": "str", - "default": "CNY", - "required": false, - "help": "Original currency code" - }, - { - "name": "date", - "type": "str", - "required": true, - "help": "Expense date as YYYY-MM-DD" - }, - { - "name": "merchant", - "type": "str", - "required": true, - "help": "Merchant shown on the reimbursement" - }, - { - "name": "category", - "type": "str", - "default": "Marketing & Advertising", - "required": false, - "help": "Mercury expense category" - }, - { - "name": "notes", - "type": "str", - "required": true, - "help": "Business purpose / reimbursement notes" - }, - { - "name": "ocr-wait-seconds", - "type": "str", - "default": "8", - "required": false, - "help": "Seconds the draft command waits after receipt upload before correcting OCR fields" - }, - { - "name": "close-after-review", - "type": "boolean", - "default": false, - "required": false, - "help": "For draft command: close the Review dialog after verification" - } - ], - "columns": [ - "status", - "receipt", - "amount", - "currency", - "date", - "merchant", - "category", - "notes", - "safety" - ], - "type": "js", - "modulePath": "mercury/reimbursement-plan.js", - "sourceFile": "mercury/reimbursement-plan.js" - }, - { - "site": "notebooklm", - "name": "add-source", - "description": "Add a URL, text, or local file source to an existing NotebookLM notebook", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Source URL to add (http/https). Pass exactly one of --url, --content, --file." - }, - { - "name": "content", - "type": "str", - "required": false, - "help": "Raw text content to add as a Text source (max 10 MB)." - }, - { - "name": "file", - "type": "str", - "required": false, - "help": "Local file path to upload as a source (max 52428800 bytes; pdf / txt / md / html / docx / etc.). Uses Google Drive's 3-step resumable upload protocol." - }, - { - "name": "title", - "type": "str", - "required": false, - "help": "Title for the text source (default \"Text Source\"). Ignored for --url and --file." - }, - { - "name": "mime-type", - "type": "str", - "required": false, - "help": "Override the auto-detected MIME type when --file is given." - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually add the remote source to the NotebookLM notebook" - } - ], - "columns": [ - "notebook_id", - "source_id", - "kind", - "identifier", - "notebook_url" - ], - "type": "js", - "modulePath": "notebooklm/add-source.js", - "sourceFile": "notebooklm/add-source.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "create", - "description": "Create a new NotebookLM notebook with the given title", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "title", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook title (1-200 chars)" - }, - { - "name": "emoji", - "type": "str", - "required": false, - "help": "Notebook emoji icon (default 📒)" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually create the remote NotebookLM notebook" - } - ], - "columns": [ - "id", - "title", - "emoji", - "url" - ], - "type": "js", - "modulePath": "notebooklm/create.js", - "sourceFile": "notebooklm/create.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "current", - "description": "Show metadata for the currently opened NotebookLM notebook tab", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", - "title", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/current.js", - "sourceFile": "notebooklm/current.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "generate-audio", - "description": "Trigger an Audio Overview (Deep Dive podcast) generation for a NotebookLM notebook, using all of its sources", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually trigger remote NotebookLM audio generation" - } - ], - "columns": [ - "notebook_id", - "audio_id", - "source_count", - "status", - "notebook_url" - ], - "type": "js", - "modulePath": "notebooklm/generate-audio.js", - "sourceFile": "notebooklm/generate-audio.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "generate-slides", - "description": "Trigger a Slide Deck (AI presentation) generation for a NotebookLM notebook, using all of its sources", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "length", - "type": "str", - "required": false, - "help": "Slide deck length: 1=Short, 3=Default (default 3)" - }, - { - "name": "language", - "type": "str", - "required": false, - "help": "Language code (default en)" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually trigger remote NotebookLM slide deck generation" - } - ], - "columns": [ - "notebook_id", - "slides_id", - "source_count", - "status", - "notebook_url" - ], - "type": "js", - "modulePath": "notebooklm/generate-slides.js", - "sourceFile": "notebooklm/generate-slides.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "get", - "aliases": [ - "metadata" - ], - "description": "Get rich metadata for the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", - "title", - "emoji", - "source_count", - "created_at", - "updated_at", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/get.js", - "sourceFile": "notebooklm/get.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "history", - "description": "List NotebookLM conversation history threads in the current notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "thread_id", - "item_count", - "preview", - "source", - "notebook_id", - "url" - ], - "type": "js", - "modulePath": "notebooklm/history.js", - "sourceFile": "notebooklm/history.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "list", - "description": "List NotebookLM notebooks via in-page batchexecute RPC in the current logged-in session", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "id", - "is_owner", - "created_at", - "source", - "url" - ], - "type": "js", - "modulePath": "notebooklm/list.js", - "sourceFile": "notebooklm/list.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "login", - "description": "Open notebooklm login", - "access": "write", - "domain": "google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "name", - "authuser", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "notebooklm/auth.js", - "sourceFile": "notebooklm/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "notebooklm", - "name": "note-list", - "aliases": [ - "notes-list" - ], - "description": "List saved notes from the Studio panel of the current NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "created_at", - "source", - "url" - ], - "type": "js", - "modulePath": "notebooklm/note-list.js", - "sourceFile": "notebooklm/note-list.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "notes-get", - "description": "Get one note from the current NotebookLM notebook by title from the visible note editor", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "note", - "type": "str", - "required": true, - "positional": true, - "help": "Note title or id from the current notebook" - } - ], - "columns": [ - "title", - "content", - "source", - "url" - ], - "type": "js", - "modulePath": "notebooklm/notes-get.js", - "sourceFile": "notebooklm/notes-get.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "open", - "aliases": [ - "select" - ], - "description": "Open one NotebookLM notebook in the adapter session by id or URL", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from list output, or a full NotebookLM notebook URL" - } - ], - "columns": [ - "id", - "title", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/open.js", - "sourceFile": "notebooklm/open.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-fulltext", - "description": "Get the extracted fulltext for one source in the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "source", - "type": "str", - "required": true, - "positional": true, - "help": "Source id or title from the current notebook" - } - ], - "columns": [ - "title", - "kind", - "char_count", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/source-fulltext.js", - "sourceFile": "notebooklm/source-fulltext.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-get", - "description": "Get one source from the currently opened NotebookLM notebook by id or title", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "source", - "type": "str", - "required": true, - "positional": true, - "help": "Source id or title from the current notebook" - } - ], - "columns": [ - "title", - "id", - "type", - "size", - "created_at", - "updated_at", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/source-get.js", - "sourceFile": "notebooklm/source-get.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-guide", - "description": "Get the guide summary and keywords for one source in the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "source", - "type": "str", - "required": true, - "positional": true, - "help": "Source id or title from the current notebook" - } - ], - "columns": [ - "source_id", - "notebook_id", - "title", - "type", - "summary", - "keywords", - "source" - ], - "type": "js", - "modulePath": "notebooklm/source-guide.js", - "sourceFile": "notebooklm/source-guide.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-list", - "description": "List sources for the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "id", - "type", - "size", - "created_at", - "updated_at", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/source-list.js", - "sourceFile": "notebooklm/source-list.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "status", - "description": "Check NotebookLM page availability and login state in the current Chrome session", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "login", - "page", - "url", - "title", - "notebooks" - ], - "type": "js", - "modulePath": "notebooklm/status.js", - "sourceFile": "notebooklm/status.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "summary", - "description": "Get the summary block from the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "summary", - "source", - "url" - ], - "type": "js", - "modulePath": "notebooklm/summary.js", - "sourceFile": "notebooklm/summary.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "whoami", - "description": "Show the current logged-in notebooklm account", - "access": "read", - "domain": "google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name", - "authuser" - ], - "type": "js", - "modulePath": "notebooklm/auth.js", - "sourceFile": "notebooklm/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "notebooklm", - "name": "write-note", - "description": "Create a Studio note in an existing NotebookLM notebook with the given title and Markdown content", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "title", - "type": "str", - "required": true, - "help": "Note title (1-200 chars)" - }, - { - "name": "content", - "type": "str", - "required": true, - "help": "Note body as Markdown" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually create the remote NotebookLM note" - } - ], - "columns": [ - "notebook_id", - "note_id", - "title", - "notebook_url" - ], - "type": "js", - "modulePath": "notebooklm/write-note.js", - "sourceFile": "notebooklm/write-note.js", - "navigateBefore": false - }, - { - "site": "npm", - "name": "downloads", - "description": "Daily download counts for an npm package over a window", - "access": "read", - "domain": "api.npmjs.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "npm package name (e.g. \"react\", \"@vercel/og\")" - }, - { - "name": "period", - "type": "str", - "default": "last-week", - "required": false, - "help": "last-day / last-week / last-month / last-year, or YYYY-MM-DD:YYYY-MM-DD" - } - ], - "columns": [ - "rank", - "package", - "day", - "downloads" - ], - "type": "js", - "modulePath": "npm/downloads.js", - "sourceFile": "npm/downloads.js" - }, - { - "site": "npm", - "name": "package", - "description": "Single npm package metadata (latest version, license, homepage, repository). Use `npm downloads` for stats.", - "access": "read", - "domain": "registry.npmjs.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "npm package name (e.g. \"react\", \"@vercel/og\")" - } - ], - "columns": [ - "name", - "latestVersion", - "description", - "license", - "homepage", - "repository", - "bugs", - "maintainers", - "keywords", - "created", - "modified", - "url" - ], - "type": "js", - "modulePath": "npm/package.js", - "sourceFile": "npm/package.js" - }, - { - "site": "npm", - "name": "search", - "description": "Search the public npm registry by keyword", - "access": "read", - "domain": "registry.npmjs.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"react\", \"graphql client\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-250)" - } - ], - "columns": [ - "rank", - "name", - "version", - "description", - "weeklyDownloads", - "dependents", - "license", - "publisher", - "updated", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "npm/search.js", - "sourceFile": "npm/search.js" - }, - { - "site": "nuget", - "name": "package", - "description": "Full NuGet package version history (catalogEntry per release)", - "access": "read", - "domain": "api.nuget.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "NuGet package id (e.g. \"Newtonsoft.Json\", case-insensitive)" - } - ], - "columns": [ - "rank", - "id", - "version", - "title", - "authors", - "tags", - "language", - "licenseExpression", - "projectUrl", - "published", - "listed", - "url" - ], - "type": "js", - "modulePath": "nuget/package.js", - "sourceFile": "nuget/package.js" - }, - { - "site": "nuget", - "name": "search", - "description": "Search NuGet packages by keyword", - "access": "read", - "domain": "api.nuget.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max packages (1-1000)" - }, - { - "name": "prerelease", - "type": "boolean", - "default": false, - "required": false, - "help": "Include prerelease versions" - } - ], - "columns": [ - "rank", - "id", - "version", - "title", - "description", - "authors", - "tags", - "totalDownloads", - "verified", - "projectUrl", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "nuget/search.js", - "sourceFile": "nuget/search.js" - }, - { - "site": "nvd", - "name": "cve", - "description": "NIST NVD CVE detail (description, CVSS, CWE, KEV flag)", - "access": "read", - "domain": "services.nvd.nist.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "CVE identifier (e.g. \"CVE-2021-44228\")" - } - ], - "columns": [ - "id", - "published", - "lastModified", - "vulnStatus", - "baseScore", - "severity", - "attackVector", - "cwe", - "kevAdded", - "description", - "url" - ], - "type": "js", - "modulePath": "nvd/cve.js", - "sourceFile": "nvd/cve.js" - }, - { - "site": "oeis", - "name": "search", - "description": "Search OEIS sequences by keyword or numeric pattern", - "access": "read", - "domain": "oeis.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword or comma-separated terms (e.g. \"fibonacci\", \"1,1,2,3,5,8\")" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max sequences (1-100)" - } - ], - "columns": [ - "rank", - "id", - "name", - "keywords", - "preview", - "author", - "created", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "oeis/search.js", - "sourceFile": "oeis/search.js" - }, - { - "site": "oeis", - "name": "sequence", - "description": "Full OEIS sequence detail by A-number (terms, name, keywords, formula counts)", - "access": "read", - "domain": "oeis.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "OEIS sequence id (e.g. \"A000045\" for Fibonacci)" - } - ], - "columns": [ - "id", - "name", - "keywords", - "preview", - "termCount", - "offset", - "author", - "created", - "revision", - "commentCount", - "formulaCount", - "referenceCount", - "xrefCount", - "linkCount", - "url" - ], - "type": "js", - "modulePath": "oeis/sequence.js", - "sourceFile": "oeis/sequence.js" - }, - { - "site": "openalex", - "name": "search", - "description": "Search OpenAlex Works (papers, books, preprints) by keyword", - "access": "read", - "domain": "api.openalex.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search text (e.g. \"transformers\", \"open access scholarly\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max works (1-200, single OpenAlex page)" - } - ], - "columns": [ - "rank", - "id", - "title", - "year", - "citations", - "firstAuthor", - "venue", - "openAccess", - "type", - "doi", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "openalex/search.js", - "sourceFile": "openalex/search.js" - }, - { - "site": "openalex", - "name": "work", - "description": "Fetch a single OpenAlex Work (paper / preprint / book) — metadata + abstract", - "access": "read", - "domain": "api.openalex.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "OpenAlex Work id (\"W2741809807\"), DOI (\"10.7717/peerj.4375\"), or full URL" - } - ], - "columns": [ - "id", - "title", - "type", - "year", - "date", - "language", - "authors", - "venue", - "citations", - "openAccess", - "openAccessUrl", - "referencedCount", - "doi", - "abstract", - "url" - ], - "type": "js", - "modulePath": "openalex/work.js", - "sourceFile": "openalex/work.js" - }, - { - "site": "openfda", - "name": "drug-label", - "description": "Search FDA-approved drug labels (brand or generic name)", - "access": "read", - "domain": "fda.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Brand or generic drug name (e.g. \"aspirin\", \"lisinopril\")" - }, - { - "name": "limit", - "type": "int", - "default": 5, - "required": false, - "help": "Max rows (1-25, default 5; openFDA caps anonymous tier at 25/page)" - } - ], - "columns": [ - "rank", - "id", - "brandName", - "genericName", - "manufacturer", - "productType", - "route", - "productNdc", - "pharmClass", - "purpose", - "indications", - "warnings", - "dosage", - "effectiveTime" - ], - "type": "js", - "modulePath": "openfda/drug-label.js", - "sourceFile": "openfda/drug-label.js" - }, - { - "site": "openfda", - "name": "food-recall", - "description": "FDA food recall and enforcement actions (most recent first)", - "access": "read", - "domain": "fda.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": false, - "help": "Free-text Lucene query (e.g. \"salmonella\", \"listeria\"); default: all recent recalls" - }, - { - "name": "status", - "type": "str", - "required": false, - "help": "Filter by status: \"Ongoing\", \"Completed\", \"Terminated\"" - }, - { - "name": "classification", - "type": "str", - "required": false, - "help": "Filter by class: \"Class I\" (most serious), \"Class II\", \"Class III\"" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max rows (1-100, default 10; openFDA caps anonymous tier at 100/page)" + "help": "Note title or id from the current notebook" } ], - "columns": [ - "rank", - "recallNumber", - "status", - "classification", - "voluntary", - "recallingFirm", - "city", - "state", - "country", - "productDescription", - "reasonForRecall", - "productQuantity", - "distributionPattern", - "reportDate", - "recallInitiationDate", - "terminationDate" + "columns": [ + "title", + "content", + "source", + "url" ], "type": "js", - "modulePath": "openfda/food-recall.js", - "sourceFile": "openfda/food-recall.js" + "modulePath": "notebooklm/notes-get.js", + "sourceFile": "notebooklm/notes-get.js", + "navigateBefore": false }, { - "site": "openreview", - "name": "author", - "description": "List OpenReview submissions by an author profile id (newest first)", + "site": "notebooklm", + "name": "open", + "aliases": [ + "select" + ], + "description": "Open one NotebookLM notebook in the adapter session by id or URL", "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "profile", + "name": "notebook", "type": "str", "required": true, "positional": true, - "help": "OpenReview profile id (e.g. \"~Yoshua_Bengio1\"). Find it on the author profile URL on openreview.net." - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max submissions (1-1000)" + "help": "Notebook id from list output, or a full NotebookLM notebook URL" } ], "columns": [ - "rank", "id", "title", - "authors", - "venue", - "pdate", - "url" + "url", + "source" ], "type": "js", - "modulePath": "openreview/author.js", - "sourceFile": "openreview/author.js" + "modulePath": "notebooklm/open.js", + "sourceFile": "notebooklm/open.js", + "navigateBefore": false }, { - "site": "openreview", - "name": "paper", - "description": "Show full metadata for a single OpenReview paper", + "site": "notebooklm", + "name": "source-fulltext", + "description": "Get the extracted fulltext for one source in the currently opened NotebookLM notebook", "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "source", "type": "str", "required": true, "positional": true, - "help": "OpenReview note id (e.g. \"5sRnsubyAK\")" + "help": "Source id or title from the current notebook" } ], "columns": [ - "id", "title", - "authors", - "keywords", - "venue", - "venueid", - "primary_area", - "abstract", - "pdate", - "pdf", - "url" + "kind", + "char_count", + "url", + "source" ], "type": "js", - "modulePath": "openreview/paper.js", - "sourceFile": "openreview/paper.js" + "modulePath": "notebooklm/source-fulltext.js", + "sourceFile": "notebooklm/source-fulltext.js", + "navigateBefore": false }, { - "site": "openreview", - "name": "reviews", - "description": "Show full review thread (paper + reviews + decisions) for an OpenReview forum", + "site": "notebooklm", + "name": "source-get", + "description": "Get one source from the currently opened NotebookLM notebook by id or title", "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "forum", + "name": "source", "type": "str", "required": true, "positional": true, - "help": "OpenReview forum id (same as paper id)" - }, - { - "name": "max-length", - "type": "int", - "default": 4000, - "required": false, - "help": "Per-row text truncation (min 200)" + "help": "Source id or title from the current notebook" } ], "columns": [ + "title", + "id", "type", - "author", - "rating", - "confidence", - "text" + "size", + "created_at", + "updated_at", + "url", + "source" ], "type": "js", - "modulePath": "openreview/reviews.js", - "sourceFile": "openreview/reviews.js" + "modulePath": "notebooklm/source-get.js", + "sourceFile": "notebooklm/source-get.js", + "navigateBefore": false }, { - "site": "openreview", - "name": "search", - "description": "Search OpenReview papers by free-text query", + "site": "notebooklm", + "name": "source-guide", + "description": "Get the guide summary and keywords for one source in the currently opened NotebookLM notebook", "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "source", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"diffusion model\")" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max results (max 50)" + "help": "Source id or title from the current notebook" } ], "columns": [ - "rank", + "source_id", + "notebook_id", + "title", + "type", + "summary", + "keywords", + "source" + ], + "type": "js", + "modulePath": "notebooklm/source-guide.js", + "sourceFile": "notebooklm/source-guide.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "source-list", + "description": "List sources for the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "title", "id", + "type", + "size", + "created_at", + "updated_at", + "url", + "source" + ], + "type": "js", + "modulePath": "notebooklm/source-list.js", + "sourceFile": "notebooklm/source-list.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "status", + "description": "Check NotebookLM page availability and login state in the current Chrome session", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "login", + "page", + "url", "title", - "authors", - "venue", - "pdate", - "url" + "notebooks" ], - "tags": [ - "search" + "type": "js", + "modulePath": "notebooklm/status.js", + "sourceFile": "notebooklm/status.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "summary", + "description": "Get the summary block from the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "title", + "summary", + "source", + "url" ], "type": "js", - "modulePath": "openreview/search.js", - "sourceFile": "openreview/search.js" + "modulePath": "notebooklm/summary.js", + "sourceFile": "notebooklm/summary.js", + "navigateBefore": false }, { - "site": "openreview", - "name": "venue", - "description": "List papers at an OpenReview venue (e.g. \"ICLR 2024 oral\" or full invitation id)", + "site": "notebooklm", + "name": "whoami", + "description": "Show the current logged-in notebooklm account", "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, + "domain": "google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name", + "authuser" + ], + "type": "js", + "modulePath": "notebooklm/auth.js", + "sourceFile": "notebooklm/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "notebooklm", + "name": "write-note", + "description": "Create a Studio note in an existing NotebookLM notebook with the given title and Markdown content", + "access": "write", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "venue", + "name": "notebook", "type": "str", "required": true, "positional": true, - "help": "Venue name (\"ICLR 2024 oral\") or invitation (\"ICLR.cc/2025/Conference/-/Submission\")" + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max results (max 200)" + "name": "title", + "type": "str", + "required": true, + "help": "Note title (1-200 chars)" }, { - "name": "offset", - "type": "int", - "default": 0, + "name": "content", + "type": "str", + "required": true, + "help": "Note body as Markdown" + }, + { + "name": "execute", + "type": "boolean", "required": false, - "help": "Pagination offset" + "help": "Actually create the remote NotebookLM note" } ], "columns": [ - "rank", - "id", + "notebook_id", + "note_id", "title", - "authors", - "keywords", - "primary_area", - "pdate", - "pdf", - "url" + "notebook_url" ], "type": "js", - "modulePath": "openreview/venue.js", - "sourceFile": "openreview/venue.js" + "modulePath": "notebooklm/write-note.js", + "sourceFile": "notebooklm/write-note.js", + "navigateBefore": false }, { "site": "osv", diff --git a/plugin-command-manifest.json b/plugin-command-manifest.json index 464c5bc6..8d7b4b48 100644 --- a/plugin-command-manifest.json +++ b/plugin-command-manifest.json @@ -3083,6 +3083,117 @@ "modulePath": "plugins/hft/export-postgraduate-courses.js", "sourceFile": "plugins/hft/export-postgraduate-courses.js" }, + { + "site": "homebrew", + "name": "cask", + "description": "Fetch a Homebrew cask's metadata (version, homepage, deprecation, download URL)", + "access": "read", + "domain": "formulae.brew.sh", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "token", + "type": "str", + "required": true, + "positional": true, + "help": "Cask token (e.g. \"firefox\", \"visual-studio-code\", \"google-chrome\")" + } + ], + "columns": [ + "cask", + "tap", + "name", + "version", + "description", + "homepage", + "deprecated", + "disabled", + "download", + "url" + ], + "type": "js", + "modulePath": "plugins/homebrew/cask.js", + "sourceFile": "plugins/homebrew/cask.js" + }, + { + "site": "homebrew", + "name": "formula", + "description": "Fetch a Homebrew formula's metadata (version, license, deps, deprecation, source)", + "access": "read", + "domain": "formulae.brew.sh", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Formula name (e.g. \"wget\", \"gcc@13\", \"imagemagick\")" + } + ], + "columns": [ + "formula", + "tap", + "version", + "license", + "description", + "homepage", + "dependencies", + "deprecated", + "disabled", + "source", + "url" + ], + "type": "js", + "modulePath": "plugins/homebrew/formula.js", + "sourceFile": "plugins/homebrew/formula.js" + }, + { + "site": "homebrew", + "name": "popular", + "description": "List most-installed Homebrew formulae or casks (Homebrew's analytics ranking)", + "access": "read", + "domain": "formulae.brew.sh", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "type", + "type": "str", + "default": "formula", + "required": false, + "help": "Package type (formula / cask)" + }, + { + "name": "window", + "type": "str", + "default": "30d", + "required": false, + "help": "Time window (30d / 90d / 365d)" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max rows (1-500)" + } + ], + "columns": [ + "rank", + "token", + "type", + "installs", + "percent", + "window", + "url" + ], + "type": "js", + "modulePath": "plugins/homebrew/popular.js", + "sourceFile": "plugins/homebrew/popular.js" + }, { "site": "iit", "name": "export-postgraduate-courses", @@ -3248,1066 +3359,936 @@ "sourceFile": "plugins/jhu/export-postgraduate-courses.js" }, { - "site": "linkedin", - "name": "company", - "description": "Read a LinkedIn company page: industry, size, HQ, founded, website, followers, and about text", + "site": "jira", + "name": "attachments", + "description": "Jira issue attachment metadata", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "company", - "type": "string", + "name": "key", + "type": "str", "required": true, "positional": true, - "help": "Company universal name, /company/ path, or full URL" + "help": "Jira issue key, e.g. PROJ-123" } ], "columns": [ - "name", - "industry", + "id", + "filename", + "mimeType", "size", - "headquarters", - "founded", - "website", - "specialties", - "followers", - "about", "url" ], "type": "js", - "modulePath": "plugins/linkedin/company.js", - "sourceFile": "plugins/linkedin/company.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/jira/attachments.js", + "sourceFile": "plugins/jira/attachments.js" }, { - "site": "linkedin", - "name": "connect", - "description": "Fail-closed LinkedIn connection request sender that verifies the exact profile before optionally sending a note", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, + "site": "jira", + "name": "comments", + "description": "Jira issue comments as Markdown", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "profile-url", - "type": "string", + "name": "key", + "type": "str", "required": true, "positional": true, - "help": "Exact LinkedIn profile URL to open and verify" - }, - { - "name": "expected-name", - "type": "string", - "required": true, - "help": "Expected visible profile name" + "help": "Jira issue key, e.g. PROJ-123" }, { - "name": "note", - "type": "string", - "default": "", + "name": "limit", + "type": "int", + "default": 50, "required": false, - "help": "Optional connection note, max 300 chars" + "help": "Max comments to return (1-100)" + } + ], + "columns": [ + "id", + "author", + "created", + "updated", + "markdown" + ], + "type": "js", + "modulePath": "plugins/jira/comments.js", + "sourceFile": "plugins/jira/comments.js" + }, + { + "site": "jira", + "name": "issue", + "description": "Jira issue detail normalized for agents (description, comments, attachments, links)", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "Jira issue key, e.g. PROJ-123" }, { - "name": "send", - "type": "bool", - "default": false, + "name": "comments-limit", + "type": "int", + "default": 100, "required": false, - "help": "Actually click Send. Default is dry-run verification only." + "help": "Max comments to include (1-100)" } ], "columns": [ + "key", + "summary", + "issueType", "status", - "recipient", - "reason", - "profile_url", - "note_chars", - "connectable", - "delivery_verified", - "matched_invitation_name", - "matched_invitation_url", - "actualValue", - "blockReason", - "expectedValue", - "observedUrl", - "safety" + "priority", + "assignee", + "updated", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/connect.js", - "sourceFile": "plugins/linkedin/connect.js", - "navigateBefore": true + "modulePath": "plugins/jira/issue.js", + "sourceFile": "plugins/jira/issue.js" }, { - "site": "linkedin", - "name": "connections", - "description": "List your LinkedIn first-degree connections with names, headlines, and profile URLs", + "site": "jira", + "name": "links", + "description": "Jira issue links", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of connections to return (max 500)" + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "Jira issue key, e.g. PROJ-123" } ], "columns": [ - "rank", - "name", - "occupation", - "public_id", - "connected_at", - "url" + "key", + "type", + "direction" ], "type": "js", - "modulePath": "plugins/linkedin/connections.js", - "sourceFile": "plugins/linkedin/connections.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/jira/links.js", + "sourceFile": "plugins/jira/links.js" }, { - "site": "linkedin", - "name": "inbox", - "description": "List LinkedIn messaging inbox conversations and unread messages", + "site": "jira", + "name": "search", + "description": "Search Jira issues with JQL", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 40, - "required": false, - "help": "Maximum conversations to return (1-100)" + "name": "jql", + "type": "str", + "required": true, + "positional": true, + "help": "JQL query, e.g. \"project = PROJ order by updated desc\"" }, { - "name": "unread-only", - "type": "bool", - "default": false, + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Return only conversations with unread messages" + "help": "Max issues to return (1-100)" } ], "columns": [ - "rank", - "thread_url", - "thread_id", - "person_name", - "last_message_preview", - "unread", - "counterparty_type", - "category", - "timestamp" + "key", + "summary", + "issueType", + "status", + "priority", + "assignee", + "updated", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/linkedin/inbox.js", - "sourceFile": "plugins/linkedin/inbox.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/jira/search.js", + "sourceFile": "plugins/jira/search.js" }, { - "site": "linkedin", - "name": "job-detail", - "description": "Read one LinkedIn job page with description, apply URL, workplace type, applicants, and company metadata", + "site": "lesswrong", + "name": "comments", + "description": "Top comments on a post", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "job-url", + "name": "url-or-id", "type": "string", "required": true, "positional": true, - "help": "Exact LinkedIn job URL, e.g. https://www.linkedin.com/jobs/view/123/" + "help": "Post URL or LessWrong post ID" + }, + { + "name": "limit", + "type": "int", + "default": 5, + "required": false, + "help": "Number of comments" } ], "columns": [ - "title", - "company", - "location", - "workplace_type", - "job_type", - "applicants", - "listed", - "apply_url", - "company_url", - "url", - "description" + "rank", + "score", + "author", + "text" ], "type": "js", - "modulePath": "plugins/linkedin/job-detail.js", - "sourceFile": "plugins/linkedin/job-detail.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/lesswrong/comments.js", + "sourceFile": "plugins/lesswrong/comments.js" }, { - "site": "linkedin", - "name": "jobs-preferences", - "description": "Read visible LinkedIn Jobs preferences and alert settings without changing them", + "site": "lesswrong", + "name": "curated", + "description": "Curated editor's picks", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "open_to_work", - "job_titles", - "locations", - "job_alerts", - "preferences_url", - "alerts_url", - "raw_preferences" + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } ], - "type": "js", - "modulePath": "plugins/linkedin/jobs-preferences.js", - "sourceFile": "plugins/linkedin/jobs-preferences.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "login", - "description": "Open linkedin login", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], "columns": [ - "status", - "logged_in", - "site", - "public_id", - "plain_id", - "name", - "action", - "verify_command" + "rank", + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/auth.js", - "sourceFile": "plugins/linkedin/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/lesswrong/curated.js", + "sourceFile": "plugins/lesswrong/curated.js" }, { - "site": "linkedin", - "name": "people-search", - "description": "Search standard LinkedIn (not Sales Navigator) for people by keyword. Each invocation consumes against LinkedIn's monthly Commercial Use Limit on people search; throttle accordingly.", + "site": "lesswrong", + "name": "frontpage", + "description": "Algorithmic frontpage", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ - { - "name": "keywords", - "type": "string", - "required": true, - "positional": true, - "help": "People search keywords, e.g. \"site reliability engineer berlin\"" - }, { "name": "limit", "type": "int", - "default": 5, + "default": 10, "required": false, - "help": "Maximum people to return (1-10); each query counts toward LinkedIn's monthly CUL" + "help": "Number of results" } ], "columns": [ "rank", - "name", - "headline", - "location", - "profile_url" - ], - "tags": [ - "search" + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/people-search.js", - "sourceFile": "plugins/linkedin/people-search.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/lesswrong/frontpage.js", + "sourceFile": "plugins/lesswrong/frontpage.js" }, { - "site": "linkedin", - "name": "post-analytics", - "description": "Summarize raw visible LinkedIn post counters without custom scoring or classification", + "site": "lesswrong", + "name": "new", + "description": "Latest posts", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - }, { "name": "limit", "type": "int", - "default": 30, + "default": 10, "required": false, - "help": "Maximum posts to summarize (1-100)" + "help": "Number of results" } ], "columns": [ - "posts_analyzed", - "total_reactions", - "total_comments", - "total_reposts", - "total_impressions", - "posts_with_media", - "posts_with_urls", - "latest_posted_at", - "latest_reactions", - "latest_comments", - "latest_reposts", - "latest_impressions", - "latest_url" + "rank", + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/post-analytics.js", - "sourceFile": "plugins/linkedin/post-analytics.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/lesswrong/new.js", + "sourceFile": "plugins/lesswrong/new.js" }, { - "site": "linkedin", - "name": "post-comments", - "description": "List unique commenters and reply authors from one exact LinkedIn post URL", + "site": "lesswrong", + "name": "read", + "description": "Read full post by URL or ID", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "post-url", + "name": "url-or-id", "type": "string", "required": true, "positional": true, - "help": "Exact LinkedIn post URL" - }, + "help": "Post URL or LessWrong post ID" + } + ], + "columns": [ + "title", + "author", + "karma", + "comments", + "tags", + "content", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/read.js", + "sourceFile": "plugins/lesswrong/read.js" + }, + { + "site": "lesswrong", + "name": "sequences", + "description": "List post collections", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ { "name": "limit", "type": "int", + "default": 10, "required": false, - "help": "Maximum unique commenters to return; omit to fetch all" + "help": "Number of results" } ], "columns": [ "rank", - "name", - "headline", - "profile_url", - "comment_count", - "sample_comment", - "commented_at", - "source_post" + "title", + "author" ], "type": "js", - "modulePath": "plugins/linkedin/post-comments.js", - "sourceFile": "plugins/linkedin/post-comments.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/lesswrong/sequences.js", + "sourceFile": "plugins/lesswrong/sequences.js" }, { - "site": "linkedin", - "name": "posts", - "description": "Export visible posts from a LinkedIn profile activity page with engagement metrics", + "site": "lesswrong", + "name": "shortform", + "description": "Quick takes / shortform posts", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Maximum posts to return (1-100)" + "help": "Number of results" } ], "columns": [ "rank", + "title", "author", - "posted_at", - "body", - "reactions", + "karma", "comments", - "reposts", - "impressions", - "media", - "media_urls", - "url", - "raw_text" + "url" ], "type": "js", - "modulePath": "plugins/linkedin/posts.js", - "sourceFile": "plugins/linkedin/posts.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/lesswrong/shortform.js", + "sourceFile": "plugins/lesswrong/shortform.js" }, { - "site": "linkedin", - "name": "profile-analytics", - "description": "Read visible LinkedIn profile dashboard metrics such as profile views, post impressions, and search appearances", + "site": "lesswrong", + "name": "tag", + "description": "Posts by tag", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "profile-url", + "name": "tag", "type": "string", + "required": true, + "positional": true, + "help": "Tag slug or name" + }, + { + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "Number of results" } ], "columns": [ - "profile_url", - "profile_views", - "post_impressions", - "search_appearances", - "followers", - "connections", - "raw_analytics" - ], - "type": "js", - "modulePath": "plugins/linkedin/profile-analytics.js", - "sourceFile": "plugins/linkedin/profile-analytics.js", - "navigateBefore": "https://www.linkedin.com" + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/tag.js", + "sourceFile": "plugins/lesswrong/tag.js" }, { - "site": "linkedin", - "name": "profile-experience", - "description": "Read visible LinkedIn profile experience entries with titles, dates, locations, skills, media, and URLs", + "site": "lesswrong", + "name": "tags", + "description": "List popular tags", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "profile-url", - "type": "string", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "Number of results" } ], "columns": [ "rank", - "total_count", - "title", - "employment_type", - "company", - "date_range", - "start_date", - "end_date", - "location", - "location_type", - "description", - "skills", - "media", - "urls", - "skill_url", - "media_url", - "profile_url", - "raw_text" + "name", + "posts" ], "type": "js", - "modulePath": "plugins/linkedin/profile-experience.js", - "sourceFile": "plugins/linkedin/profile-experience.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/lesswrong/tags.js", + "sourceFile": "plugins/lesswrong/tags.js" }, { - "site": "linkedin", - "name": "profile-projects", - "description": "Read visible LinkedIn profile projects with descriptions, dates, skills, media, and URLs", + "site": "lesswrong", + "name": "top", + "description": "Top all-time", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "profile-url", - "type": "string", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "Number of results" } ], "columns": [ "rank", "title", - "date_range", - "associated_with", - "description", - "skills", - "media", - "urls", - "profile_url", - "raw_text" + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/profile-projects.js", - "sourceFile": "plugins/linkedin/profile-projects.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/lesswrong/top.js", + "sourceFile": "plugins/lesswrong/top.js" }, { - "site": "linkedin", - "name": "profile-read", - "description": "Read visible LinkedIn profile sections: headline, About, experience, education, services, and featured sections", + "site": "lesswrong", + "name": "top-month", + "description": "Top this month", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "profile-url", - "type": "string", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "Number of results" } ], "columns": [ - "profile_url", - "name", - "headline", - "location", - "about", - "about_character_count", - "about_skills", - "experience", - "education", - "services", - "featured" + "rank", + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/profile-read.js", - "sourceFile": "plugins/linkedin/profile-read.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/lesswrong/top-month.js", + "sourceFile": "plugins/lesswrong/top-month.js" }, { - "site": "linkedin", - "name": "safe-send", - "description": "Fail-closed LinkedIn message sender that verifies exact thread, recipient, and latest message before filling/sending", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, + "site": "lesswrong", + "name": "top-week", + "description": "Top this week", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "thread-url", - "type": "str", - "required": true, - "help": "Exact LinkedIn messaging thread URL to open and verify" - }, - { - "name": "expected-name", - "type": "str", - "required": true, - "help": "Expected visible recipient name in the active thread header" - }, - { - "name": "message", - "type": "str", - "required": true, - "help": "Message body to send or dry-run" - }, - { - "name": "expected-last-text", - "type": "str", - "required": false, - "help": "Substring expected in the currently visible latest conversation context" - }, - { - "name": "expected-last-hash", - "type": "str", - "required": false, - "help": "SHA-256 hash of expected latest visible message text" - }, - { - "name": "send", - "type": "bool", - "default": false, - "required": false, - "help": "Actually click Send. Default is dry-run verification only." - }, - { - "name": "screenshot", - "type": "bool", - "default": false, + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Capture a screenshot during verification" + "help": "Number of results" } ], "columns": [ - "status", - "recipient", - "reason", - "thread_url", - "message_chars", - "screenshot" + "rank", + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/safe-send.js", - "sourceFile": "plugins/linkedin/safe-send.js", - "navigateBefore": true + "modulePath": "plugins/lesswrong/top-week.js", + "sourceFile": "plugins/lesswrong/top-week.js" }, { - "site": "linkedin", - "name": "salesnav-inbox", - "description": "List LinkedIn Sales Navigator message conversations with API pagination", + "site": "lesswrong", + "name": "top-year", + "description": "Top this year", "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ { "name": "limit", - "type": "number", - "default": 40, - "required": false, - "help": "Maximum conversations to return (1-500)" - }, - { - "name": "max-pages", - "type": "number", - "default": 30, - "required": false, - "help": "Maximum Sales Navigator API pages to fetch" - }, - { - "name": "unread-only", - "type": "bool", - "default": false, + "type": "int", + "default": 10, "required": false, - "help": "Return only unread conversations" + "help": "Number of results" } ], "columns": [ "rank", - "thread_id", - "thread_url", - "person_name", - "last_message_snippet", - "last_activity_time", - "unread", - "unread_count", - "total_message_count", - "archived", - "participants", - "next_page_starts_at" + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-inbox.js", - "sourceFile": "plugins/linkedin/salesnav-inbox.js", - "navigateBefore": true + "modulePath": "plugins/lesswrong/top-year.js", + "sourceFile": "plugins/lesswrong/top-year.js" }, { - "site": "linkedin", - "name": "salesnav-message", - "description": "Send or dry-run a LinkedIn Sales Navigator InMail to a lead using the Sales Navigator messaging API", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, + "site": "lesswrong", + "name": "user", + "description": "User profile", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "recipient", + "name": "username", "type": "string", "required": true, "positional": true, - "help": "Sales Navigator lead URL, LinkedIn /in/ URL from salesnav-search, or urn:li:fs_salesProfile:(...)" - }, + "help": "LessWrong username or slug" + } + ], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "plugins/lesswrong/user.js", + "sourceFile": "plugins/lesswrong/user.js" + }, + { + "site": "lesswrong", + "name": "user-posts", + "description": "List a user's posts", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ { - "name": "subject", + "name": "username", "type": "string", "required": true, - "help": "InMail subject" - }, - { - "name": "body", - "type": "string", - "required": true, - "help": "InMail body" - }, - { - "name": "send", - "type": "bool", - "default": false, - "required": false, - "help": "Actually send the InMail. Default is dry-run validation only." + "positional": true, + "help": "LessWrong username or slug" }, { - "name": "copy-to-crm", - "type": "bool", - "default": false, + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Set Sales Navigator copyToCrm on the message request" + "help": "Number of results" } ], "columns": [ - "status", - "recipient", + "rank", "title", - "company", - "credits_remaining", - "credits_before", - "credits_after", - "sent_in_salesnav", - "message_chars", - "subject_chars", - "recipient_urn", - "degree", - "inmail_restriction", - "open_link" + "karma", + "comments", + "date", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-message.js", - "sourceFile": "plugins/linkedin/salesnav-message.js", - "navigateBefore": true + "modulePath": "plugins/lesswrong/user-posts.js", + "sourceFile": "plugins/lesswrong/user-posts.js" }, { - "site": "linkedin", - "name": "salesnav-search", - "description": "Search LinkedIn Sales Navigator for people leads by keyword", + "site": "lichess", + "name": "top", + "description": "Top-N Lichess leaderboard for a perf type (bullet/blitz/rapid/classical/...)", "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, + "domain": "lichess.org", + "strategy": "public", + "browser": false, "args": [ { - "name": "keywords", - "type": "string", + "name": "perf", + "type": "str", "required": true, "positional": true, - "help": "People search keywords, e.g. \"quality manager food manufacturing\"" + "help": "Perf type (bullet, blitz, rapid, classical, ultraBullet, chess960, ...)" }, { "name": "limit", - "type": "number", - "default": 25, + "type": "int", + "default": 10, "required": false, - "help": "Maximum leads to return (1-500, fetched 25 per request)" + "help": "Top-N rows (1-200)" } ], "columns": [ "rank", - "name", + "username", + "id", "title", - "company", - "location", - "degree", - "profile_url", - "lead_url", - "recipient_urn" + "rating", + "progress", + "patron", + "url" ], - "tags": [ - "search" + "type": "js", + "modulePath": "plugins/lichess/top.js", + "sourceFile": "plugins/lichess/top.js" + }, + { + "site": "lichess", + "name": "user", + "description": "Fetch a Lichess player profile by username (rating, perfs, counts)", + "access": "read", + "domain": "lichess.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Lichess username (case-insensitive)" + } + ], + "columns": [ + "username", + "id", + "title", + "patron", + "online", + "tosViolation", + "createdAt", + "seenAt", + "gamesAll", + "gamesWin", + "gamesLoss", + "gamesDraw", + "topPerfName", + "topPerfRating", + "topPerfGames", + "fideRating", + "country", + "bio", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-search.js", - "sourceFile": "plugins/linkedin/salesnav-search.js", - "navigateBefore": true + "modulePath": "plugins/lichess/user.js", + "sourceFile": "plugins/lichess/user.js" }, { "site": "linkedin", - "name": "salesnav-thread", - "description": "Return full Sales Navigator message history for a thread id, Sales Navigator inbox URL, lead URL, recipient urn, or exact recipient name", + "name": "company", + "description": "Read a LinkedIn company page: industry, size, HQ, founded, website, followers, and about text", "access": "read", "domain": "www.linkedin.com", - "strategy": "ui", + "strategy": "cookie", "browser": true, "args": [ { - "name": "thread-or-recipient", + "name": "company", "type": "string", "required": true, "positional": true, - "help": "Sales Navigator inbox URL/thread id, Sales Navigator lead URL, recipient urn, or exact participant name" - }, - { - "name": "limit", - "type": "number", - "default": 200, - "required": false, - "help": "Maximum messages to return (1-500)" - }, - { - "name": "max-pages", - "type": "number", - "default": 30, - "required": false, - "help": "Maximum inbox pages to scan when resolving a recipient" + "help": "Company universal name, /company/ path, or full URL" } ], "columns": [ - "index", - "thread_id", - "thread_url", - "sender", - "text", - "timestamp", - "subject", - "message_id", - "sender_urn", - "delivered_at", - "type", - "total_message_count" + "name", + "industry", + "size", + "headquarters", + "founded", + "website", + "specialties", + "followers", + "about", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-thread.js", - "sourceFile": "plugins/linkedin/salesnav-thread.js", - "navigateBefore": true + "modulePath": "plugins/linkedin/company.js", + "sourceFile": "plugins/linkedin/company.js", + "navigateBefore": "https://www.linkedin.com" }, { "site": "linkedin", - "name": "search", - "description": "Search LinkedIn jobs", - "access": "read", + "name": "connect", + "description": "Fail-closed LinkedIn connection request sender that verifies the exact profile before optionally sending a note", + "access": "write", "domain": "www.linkedin.com", - "strategy": "cookie", + "strategy": "ui", "browser": true, "args": [ { - "name": "query", + "name": "profile-url", "type": "string", "required": true, "positional": true, - "help": "Job search keywords" + "help": "Exact LinkedIn profile URL to open and verify" }, { - "name": "location", + "name": "expected-name", "type": "string", - "required": false, - "help": "Location text such as San Francisco Bay Area" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of jobs to return (max 100)" + "required": true, + "help": "Expected visible profile name" }, { - "name": "start", - "type": "int", - "default": 0, + "name": "note", + "type": "string", + "default": "", "required": false, - "help": "Result offset for pagination" + "help": "Optional connection note, max 300 chars" }, { - "name": "details", + "name": "send", "type": "bool", "default": false, "required": false, - "help": "Include full job description and apply URL (slower)" - }, - { - "name": "company", - "type": "string", - "required": false, - "help": "Comma-separated company names or LinkedIn company IDs" - }, - { - "name": "experience-level", - "type": "string", - "required": false, - "help": "Comma-separated: internship, entry, associate, mid-senior, director, executive" - }, - { - "name": "job-type", - "type": "string", - "required": false, - "help": "Comma-separated: full-time, part-time, contract, temporary, volunteer, internship, other" - }, - { - "name": "date-posted", - "type": "string", - "required": false, - "help": "One of: any, month, week, 24h" - }, - { - "name": "remote", - "type": "string", - "required": false, - "help": "Comma-separated: on-site, hybrid, remote" + "help": "Actually click Send. Default is dry-run verification only." } ], "columns": [ - "rank", - "title", - "company", - "location", - "listed", - "salary", - "url" - ], - "tags": [ - "search" + "status", + "recipient", + "reason", + "profile_url", + "note_chars", + "connectable", + "delivery_verified", + "matched_invitation_name", + "matched_invitation_url", + "actualValue", + "blockReason", + "expectedValue", + "observedUrl", + "safety" ], "type": "js", - "modulePath": "plugins/linkedin/search.js", - "sourceFile": "plugins/linkedin/search.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/linkedin/connect.js", + "sourceFile": "plugins/linkedin/connect.js", + "navigateBefore": true }, { "site": "linkedin", - "name": "sent-invitations", - "description": "List pending LinkedIn sent invitations for CRM reconciliation", + "name": "connections", + "description": "List your LinkedIn first-degree connections with names, headlines, and profile URLs", "access": "read", "domain": "www.linkedin.com", - "strategy": "ui", + "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "rank", + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of connections to return (max 500)" + } + ], + "columns": [ + "rank", "name", - "profile_url", - "invited_date_text" + "occupation", + "public_id", + "connected_at", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/sent-invitations.js", - "sourceFile": "plugins/linkedin/sent-invitations.js", - "navigateBefore": true + "modulePath": "plugins/linkedin/connections.js", + "sourceFile": "plugins/linkedin/connections.js", + "navigateBefore": "https://www.linkedin.com" }, { "site": "linkedin", - "name": "services-read", - "description": "Read LinkedIn Services page details including services, overview, availability, pricing, and media titles/descriptions", + "name": "inbox", + "description": "List LinkedIn messaging inbox conversations and unread messages", "access": "read", "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "profile-url", - "type": "string", + "name": "limit", + "type": "int", + "default": 40, "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "Maximum conversations to return (1-100)" }, { - "name": "services-url", - "type": "string", + "name": "unread-only", + "type": "bool", + "default": false, "required": false, - "help": "LinkedIn /services/page// URL. If omitted, it is discovered from the profile." + "help": "Return only conversations with unread messages" } ], "columns": [ - "service_url", - "page_title", - "overview", - "availability", - "work_locations", - "pricing", - "services_provided", - "services_count", - "media", - "media_count", - "messages", - "reviews_visibility" + "rank", + "thread_url", + "thread_id", + "person_name", + "last_message_preview", + "unread", + "counterparty_type", + "category", + "timestamp" ], "type": "js", - "modulePath": "plugins/linkedin/services-read.js", - "sourceFile": "plugins/linkedin/services-read.js", + "modulePath": "plugins/linkedin/inbox.js", + "sourceFile": "plugins/linkedin/inbox.js", "navigateBefore": "https://www.linkedin.com" }, { "site": "linkedin", - "name": "thread-snapshot", - "description": "Load a LinkedIn messaging thread, scroll for available history, and return a full context snapshot", + "name": "job-detail", + "description": "Read one LinkedIn job page with description, apply URL, workplace type, applicants, and company metadata", "access": "read", "domain": "www.linkedin.com", - "strategy": "ui", + "strategy": "cookie", "browser": true, "args": [ { - "name": "thread-url", - "type": "str", + "name": "job-url", + "type": "string", "required": true, - "help": "Exact LinkedIn messaging thread URL to open and snapshot" - }, - { - "name": "max-scrolls", - "type": "number", - "default": 30, - "required": false, - "help": "Maximum upward scroll attempts to load older messages" - }, - { - "name": "json", - "type": "bool", - "default": false, - "required": false, - "help": "Return only JSON snapshot string in the snapshot_json field" + "positional": true, + "help": "Exact LinkedIn job URL, e.g. https://www.linkedin.com/jobs/view/123/" } ], "columns": [ - "thread_url", - "recipient", - "message_count", - "latest_text", - "snapshot_json" + "title", + "company", + "location", + "workplace_type", + "job_type", + "applicants", + "listed", + "apply_url", + "company_url", + "url", + "description" ], "type": "js", - "modulePath": "plugins/linkedin/thread-snapshot.js", - "sourceFile": "plugins/linkedin/thread-snapshot.js", - "navigateBefore": true + "modulePath": "plugins/linkedin/job-detail.js", + "sourceFile": "plugins/linkedin/job-detail.js", + "navigateBefore": "https://www.linkedin.com" }, { "site": "linkedin", - "name": "timeline", - "description": "Read LinkedIn home timeline posts", + "name": "jobs-preferences", + "description": "Read visible LinkedIn Jobs preferences and alert settings without changing them", "access": "read", "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return (max 100)" - } - ], + "args": [], "columns": [ - "rank", - "author", - "author_url", - "headline", - "text", - "posted_at", - "reactions", - "comments", - "url" + "open_to_work", + "job_titles", + "locations", + "job_alerts", + "preferences_url", + "alerts_url", + "raw_preferences" ], "type": "js", - "modulePath": "plugins/linkedin/timeline.js", - "sourceFile": "plugins/linkedin/timeline.js", + "modulePath": "plugins/linkedin/jobs-preferences.js", + "sourceFile": "plugins/linkedin/jobs-preferences.js", "navigateBefore": "https://www.linkedin.com" }, { "site": "linkedin", - "name": "whoami", - "description": "Show the current logged-in linkedin account", - "access": "read", + "name": "login", + "description": "Open linkedin login", + "access": "write", "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ + "status", "logged_in", "site", "public_id", "plain_id", - "name" + "name", + "action", + "verify_command" ], "type": "js", "modulePath": "plugins/linkedin/auth.js", @@ -4316,393 +4297,2258 @@ "siteSession": "persistent" }, { - "site": "luma", - "name": "create-event", - "description": "Create a free single-session Luma event", - "access": "write", - "domain": "luma.com", - "strategy": "ui", + "site": "linkedin", + "name": "people-search", + "description": "Search standard LinkedIn (not Sales Navigator) for people by keyword. Each invocation consumes against LinkedIn's monthly Commercial Use Limit on people search; throttle accordingly.", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "name", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "start", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "end", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "timezone", - "type": "str", + "name": "keywords", + "type": "string", "required": true, - "help": "" - }, - { - "name": "calendar", - "type": "str", - "required": false, - "help": "" - }, - { - "name": "description", - "type": "str", - "required": false, - "help": "" - }, - { - "name": "location", - "type": "str", - "required": false, - "help": "" - }, - { - "name": "virtual-url", - "type": "str", - "required": false, - "help": "" - }, - { - "name": "visibility", - "type": "str", - "default": "public", - "required": false, - "help": "", - "choices": [ - "public", - "private", - "members-only" - ] + "positional": true, + "help": "People search keywords, e.g. \"site reliability engineer berlin\"" }, { - "name": "capacity", + "name": "limit", "type": "int", + "default": 5, "required": false, - "help": "" - }, - { - "name": "require-approval", - "type": "boolean", - "default": false, - "required": false, - "help": "" - }, - { - "name": "confirm", - "type": "boolean", - "default": false, - "required": false, - "help": "" + "help": "Maximum people to return (1-10); each query counts toward LinkedIn's monthly CUL" } ], "columns": [ - "eventId", + "rank", "name", - "startsAt", - "endsAt", - "timezone", - "visibility", - "requireApproval", - "capacity", - "eventUrl", - "manageUrl" + "headline", + "location", + "profile_url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/luma/create-event.js", - "sourceFile": "plugins/luma/create-event.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "modulePath": "plugins/linkedin/people-search.js", + "sourceFile": "plugins/linkedin/people-search.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "luma", - "name": "events", - "description": "List upcoming or past Luma events managed by the logged-in account", + "site": "linkedin", + "name": "post-analytics", + "description": "Summarize raw visible LinkedIn post counters without custom scoring or classification", "access": "read", - "example": "webcmd luma events --period future --limit 25 -f json", - "domain": "luma.com", + "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "period", - "type": "str", - "default": "future", + "name": "profile-url", + "type": "string", "required": false, - "help": "List future or past events", - "choices": [ - "future", - "past" - ] + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." }, { "name": "limit", "type": "int", - "default": 25, + "default": 30, "required": false, - "help": "Maximum number of events to request" + "help": "Maximum posts to summarize (1-100)" } ], "columns": [ - "eventId", + "posts_analyzed", + "total_reactions", + "total_comments", + "total_reposts", + "total_impressions", + "posts_with_media", + "posts_with_urls", + "latest_posted_at", + "latest_reactions", + "latest_comments", + "latest_reposts", + "latest_impressions", + "latest_url" + ], + "type": "js", + "modulePath": "plugins/linkedin/post-analytics.js", + "sourceFile": "plugins/linkedin/post-analytics.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "post-comments", + "description": "List unique commenters and reply authors from one exact LinkedIn post URL", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "post-url", + "type": "string", + "required": true, + "positional": true, + "help": "Exact LinkedIn post URL" + }, + { + "name": "limit", + "type": "int", + "required": false, + "help": "Maximum unique commenters to return; omit to fetch all" + } + ], + "columns": [ + "rank", "name", - "startsAt", - "endsAt", - "timezone", - "guestCount", - "requireApproval", - "managerLevel", + "headline", + "profile_url", + "comment_count", + "sample_comment", + "commented_at", + "source_post" + ], + "type": "js", + "modulePath": "plugins/linkedin/post-comments.js", + "sourceFile": "plugins/linkedin/post-comments.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "posts", + "description": "Export visible posts from a LinkedIn profile activity page with engagement metrics", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum posts to return (1-100)" + } + ], + "columns": [ + "rank", + "author", + "posted_at", + "body", + "reactions", + "comments", + "reposts", + "impressions", + "media", + "media_urls", + "url", + "raw_text" + ], + "type": "js", + "modulePath": "plugins/linkedin/posts.js", + "sourceFile": "plugins/linkedin/posts.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "profile-analytics", + "description": "Read visible LinkedIn profile dashboard metrics such as profile views, post impressions, and search appearances", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], + "columns": [ + "profile_url", + "profile_views", + "post_impressions", + "search_appearances", + "followers", + "connections", + "raw_analytics" + ], + "type": "js", + "modulePath": "plugins/linkedin/profile-analytics.js", + "sourceFile": "plugins/linkedin/profile-analytics.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "profile-experience", + "description": "Read visible LinkedIn profile experience entries with titles, dates, locations, skills, media, and URLs", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], + "columns": [ + "rank", + "total_count", + "title", + "employment_type", + "company", + "date_range", + "start_date", + "end_date", "location", - "manageUrl", - "eventUrl" + "location_type", + "description", + "skills", + "media", + "urls", + "skill_url", + "media_url", + "profile_url", + "raw_text" ], "type": "js", - "modulePath": "plugins/luma/events.js", - "sourceFile": "plugins/luma/events.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/linkedin/profile-experience.js", + "sourceFile": "plugins/linkedin/profile-experience.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "luma", - "name": "guests", - "description": "List guests and all custom registration answers for a managed Luma event", + "site": "linkedin", + "name": "profile-projects", + "description": "Read visible LinkedIn profile projects with descriptions, dates, skills, media, and URLs", "access": "read", - "example": "webcmd luma guests evt-abc --status pending_approval --limit 100 -f json", - "domain": "luma.com", + "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "eventId", + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], + "columns": [ + "rank", + "title", + "date_range", + "associated_with", + "description", + "skills", + "media", + "urls", + "profile_url", + "raw_text" + ], + "type": "js", + "modulePath": "plugins/linkedin/profile-projects.js", + "sourceFile": "plugins/linkedin/profile-projects.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "profile-read", + "description": "Read visible LinkedIn profile sections: headline, About, experience, education, services, and featured sections", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], + "columns": [ + "profile_url", + "name", + "headline", + "location", + "about", + "about_character_count", + "about_skills", + "experience", + "education", + "services", + "featured" + ], + "type": "js", + "modulePath": "plugins/linkedin/profile-read.js", + "sourceFile": "plugins/linkedin/profile-read.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "safe-send", + "description": "Fail-closed LinkedIn message sender that verifies exact thread, recipient, and latest message before filling/sending", + "access": "write", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "thread-url", + "type": "str", + "required": true, + "help": "Exact LinkedIn messaging thread URL to open and verify" + }, + { + "name": "expected-name", + "type": "str", + "required": true, + "help": "Expected visible recipient name in the active thread header" + }, + { + "name": "message", + "type": "str", + "required": true, + "help": "Message body to send or dry-run" + }, + { + "name": "expected-last-text", + "type": "str", + "required": false, + "help": "Substring expected in the currently visible latest conversation context" + }, + { + "name": "expected-last-hash", + "type": "str", + "required": false, + "help": "SHA-256 hash of expected latest visible message text" + }, + { + "name": "send", + "type": "bool", + "default": false, + "required": false, + "help": "Actually click Send. Default is dry-run verification only." + }, + { + "name": "screenshot", + "type": "bool", + "default": false, + "required": false, + "help": "Capture a screenshot during verification" + } + ], + "columns": [ + "status", + "recipient", + "reason", + "thread_url", + "message_chars", + "screenshot" + ], + "type": "js", + "modulePath": "plugins/linkedin/safe-send.js", + "sourceFile": "plugins/linkedin/safe-send.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "salesnav-inbox", + "description": "List LinkedIn Sales Navigator message conversations with API pagination", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "limit", + "type": "number", + "default": 40, + "required": false, + "help": "Maximum conversations to return (1-500)" + }, + { + "name": "max-pages", + "type": "number", + "default": 30, + "required": false, + "help": "Maximum Sales Navigator API pages to fetch" + }, + { + "name": "unread-only", + "type": "bool", + "default": false, + "required": false, + "help": "Return only unread conversations" + } + ], + "columns": [ + "rank", + "thread_id", + "thread_url", + "person_name", + "last_message_snippet", + "last_activity_time", + "unread", + "unread_count", + "total_message_count", + "archived", + "participants", + "next_page_starts_at" + ], + "type": "js", + "modulePath": "plugins/linkedin/salesnav-inbox.js", + "sourceFile": "plugins/linkedin/salesnav-inbox.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "salesnav-message", + "description": "Send or dry-run a LinkedIn Sales Navigator InMail to a lead using the Sales Navigator messaging API", + "access": "write", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "recipient", + "type": "string", + "required": true, + "positional": true, + "help": "Sales Navigator lead URL, LinkedIn /in/ URL from salesnav-search, or urn:li:fs_salesProfile:(...)" + }, + { + "name": "subject", + "type": "string", + "required": true, + "help": "InMail subject" + }, + { + "name": "body", + "type": "string", + "required": true, + "help": "InMail body" + }, + { + "name": "send", + "type": "bool", + "default": false, + "required": false, + "help": "Actually send the InMail. Default is dry-run validation only." + }, + { + "name": "copy-to-crm", + "type": "bool", + "default": false, + "required": false, + "help": "Set Sales Navigator copyToCrm on the message request" + } + ], + "columns": [ + "status", + "recipient", + "title", + "company", + "credits_remaining", + "credits_before", + "credits_after", + "sent_in_salesnav", + "message_chars", + "subject_chars", + "recipient_urn", + "degree", + "inmail_restriction", + "open_link" + ], + "type": "js", + "modulePath": "plugins/linkedin/salesnav-message.js", + "sourceFile": "plugins/linkedin/salesnav-message.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "salesnav-search", + "description": "Search LinkedIn Sales Navigator for people leads by keyword", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "keywords", + "type": "string", + "required": true, + "positional": true, + "help": "People search keywords, e.g. \"quality manager food manufacturing\"" + }, + { + "name": "limit", + "type": "number", + "default": 25, + "required": false, + "help": "Maximum leads to return (1-500, fetched 25 per request)" + } + ], + "columns": [ + "rank", + "name", + "title", + "company", + "location", + "degree", + "profile_url", + "lead_url", + "recipient_urn" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/linkedin/salesnav-search.js", + "sourceFile": "plugins/linkedin/salesnav-search.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "salesnav-thread", + "description": "Return full Sales Navigator message history for a thread id, Sales Navigator inbox URL, lead URL, recipient urn, or exact recipient name", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "thread-or-recipient", + "type": "string", + "required": true, + "positional": true, + "help": "Sales Navigator inbox URL/thread id, Sales Navigator lead URL, recipient urn, or exact participant name" + }, + { + "name": "limit", + "type": "number", + "default": 200, + "required": false, + "help": "Maximum messages to return (1-500)" + }, + { + "name": "max-pages", + "type": "number", + "default": 30, + "required": false, + "help": "Maximum inbox pages to scan when resolving a recipient" + } + ], + "columns": [ + "index", + "thread_id", + "thread_url", + "sender", + "text", + "timestamp", + "subject", + "message_id", + "sender_urn", + "delivered_at", + "type", + "total_message_count" + ], + "type": "js", + "modulePath": "plugins/linkedin/salesnav-thread.js", + "sourceFile": "plugins/linkedin/salesnav-thread.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "search", + "description": "Search LinkedIn jobs", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "string", + "required": true, + "positional": true, + "help": "Job search keywords" + }, + { + "name": "location", + "type": "string", + "required": false, + "help": "Location text such as San Francisco Bay Area" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of jobs to return (max 100)" + }, + { + "name": "start", + "type": "int", + "default": 0, + "required": false, + "help": "Result offset for pagination" + }, + { + "name": "details", + "type": "bool", + "default": false, + "required": false, + "help": "Include full job description and apply URL (slower)" + }, + { + "name": "company", + "type": "string", + "required": false, + "help": "Comma-separated company names or LinkedIn company IDs" + }, + { + "name": "experience-level", + "type": "string", + "required": false, + "help": "Comma-separated: internship, entry, associate, mid-senior, director, executive" + }, + { + "name": "job-type", + "type": "string", + "required": false, + "help": "Comma-separated: full-time, part-time, contract, temporary, volunteer, internship, other" + }, + { + "name": "date-posted", + "type": "string", + "required": false, + "help": "One of: any, month, week, 24h" + }, + { + "name": "remote", + "type": "string", + "required": false, + "help": "Comma-separated: on-site, hybrid, remote" + } + ], + "columns": [ + "rank", + "title", + "company", + "location", + "listed", + "salary", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/linkedin/search.js", + "sourceFile": "plugins/linkedin/search.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "sent-invitations", + "description": "List pending LinkedIn sent invitations for CRM reconciliation", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "rank", + "name", + "profile_url", + "invited_date_text" + ], + "type": "js", + "modulePath": "plugins/linkedin/sent-invitations.js", + "sourceFile": "plugins/linkedin/sent-invitations.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "services-read", + "description": "Read LinkedIn Services page details including services, overview, availability, pricing, and media titles/descriptions", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + }, + { + "name": "services-url", + "type": "string", + "required": false, + "help": "LinkedIn /services/page// URL. If omitted, it is discovered from the profile." + } + ], + "columns": [ + "service_url", + "page_title", + "overview", + "availability", + "work_locations", + "pricing", + "services_provided", + "services_count", + "media", + "media_count", + "messages", + "reviews_visibility" + ], + "type": "js", + "modulePath": "plugins/linkedin/services-read.js", + "sourceFile": "plugins/linkedin/services-read.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "thread-snapshot", + "description": "Load a LinkedIn messaging thread, scroll for available history, and return a full context snapshot", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "thread-url", + "type": "str", + "required": true, + "help": "Exact LinkedIn messaging thread URL to open and snapshot" + }, + { + "name": "max-scrolls", + "type": "number", + "default": 30, + "required": false, + "help": "Maximum upward scroll attempts to load older messages" + }, + { + "name": "json", + "type": "bool", + "default": false, + "required": false, + "help": "Return only JSON snapshot string in the snapshot_json field" + } + ], + "columns": [ + "thread_url", + "recipient", + "message_count", + "latest_text", + "snapshot_json" + ], + "type": "js", + "modulePath": "plugins/linkedin/thread-snapshot.js", + "sourceFile": "plugins/linkedin/thread-snapshot.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "timeline", + "description": "Read LinkedIn home timeline posts", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return (max 100)" + } + ], + "columns": [ + "rank", + "author", + "author_url", + "headline", + "text", + "posted_at", + "reactions", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/linkedin/timeline.js", + "sourceFile": "plugins/linkedin/timeline.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "whoami", + "description": "Show the current logged-in linkedin account", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "public_id", + "plain_id", + "name" + ], + "type": "js", + "modulePath": "plugins/linkedin/auth.js", + "sourceFile": "plugins/linkedin/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "lobsters", + "name": "active", + "description": "Lobste.rs most active discussions", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/lobsters/active.js", + "sourceFile": "plugins/lobsters/active.js" + }, + { + "site": "lobsters", + "name": "domain", + "description": "Lobste.rs stories submitted from a specific domain", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "domain", + "type": "str", + "required": true, + "positional": true, + "help": "Source domain (e.g. github.com, arxiv.org, blog.cloudflare.com)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories (1-25 — single page)" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "submission_url", + "comments_url" + ], + "type": "js", + "modulePath": "plugins/lobsters/domain.js", + "sourceFile": "plugins/lobsters/domain.js" + }, + { + "site": "lobsters", + "name": "hot", + "description": "Lobste.rs hottest stories", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/lobsters/hot.js", + "sourceFile": "plugins/lobsters/hot.js" + }, + { + "site": "lobsters", + "name": "newest", + "description": "Lobste.rs newest stories", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/lobsters/newest.js", + "sourceFile": "plugins/lobsters/newest.js" + }, + { + "site": "lobsters", + "name": "read", + "description": "Read a Lobste.rs story and its comment tree", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Lobste.rs short_id (e.g. 6cmh6h)" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max top-level comments" + }, + { + "name": "depth", + "type": "int", + "default": 2, + "required": false, + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + }, + { + "name": "replies", + "type": "int", + "default": 5, + "required": false, + "help": "Max replies shown per comment at each level" + }, + { + "name": "max-length", + "type": "int", + "default": 2000, + "required": false, + "help": "Max characters per comment body (min 100)" + } + ], + "columns": [ + "type", + "author", + "score", + "text" + ], + "type": "js", + "modulePath": "plugins/lobsters/read.js", + "sourceFile": "plugins/lobsters/read.js" + }, + { + "site": "lobsters", + "name": "tag", + "description": "Lobste.rs stories by tag", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "tag", + "type": "str", + "required": true, + "positional": true, + "help": "Tag name (e.g. programming, rust, security, ai)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/lobsters/tag.js", + "sourceFile": "plugins/lobsters/tag.js" + }, + { + "site": "luma", + "name": "create-event", + "description": "Create a free single-session Luma event", + "access": "write", + "domain": "luma.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "start", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "end", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "timezone", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "calendar", + "type": "str", + "required": false, + "help": "" + }, + { + "name": "description", + "type": "str", + "required": false, + "help": "" + }, + { + "name": "location", + "type": "str", + "required": false, + "help": "" + }, + { + "name": "virtual-url", + "type": "str", + "required": false, + "help": "" + }, + { + "name": "visibility", + "type": "str", + "default": "public", + "required": false, + "help": "", + "choices": [ + "public", + "private", + "members-only" + ] + }, + { + "name": "capacity", + "type": "int", + "required": false, + "help": "" + }, + { + "name": "require-approval", + "type": "boolean", + "default": false, + "required": false, + "help": "" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "" + } + ], + "columns": [ + "eventId", + "name", + "startsAt", + "endsAt", + "timezone", + "visibility", + "requireApproval", + "capacity", + "eventUrl", + "manageUrl" + ], + "type": "js", + "modulePath": "plugins/luma/create-event.js", + "sourceFile": "plugins/luma/create-event.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true + }, + { + "site": "luma", + "name": "events", + "description": "List upcoming or past Luma events managed by the logged-in account", + "access": "read", + "example": "webcmd luma events --period future --limit 25 -f json", + "domain": "luma.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "period", + "type": "str", + "default": "future", + "required": false, + "help": "List future or past events", + "choices": [ + "future", + "past" + ] + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Maximum number of events to request" + } + ], + "columns": [ + "eventId", + "name", + "startsAt", + "endsAt", + "timezone", + "guestCount", + "requireApproval", + "managerLevel", + "location", + "manageUrl", + "eventUrl" + ], + "type": "js", + "modulePath": "plugins/luma/events.js", + "sourceFile": "plugins/luma/events.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "luma", + "name": "guests", + "description": "List guests and all custom registration answers for a managed Luma event", + "access": "read", + "example": "webcmd luma guests evt-abc --status pending_approval --limit 100 -f json", + "domain": "luma.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "eventId", + "type": "str", + "required": true, + "positional": true, + "help": "Luma event ID returned by webcmd luma events" + }, + { + "name": "status", + "type": "str", + "default": "all", + "required": false, + "help": "Filter by guest approval status", + "choices": [ + "all", + "approved", + "pending_approval", + "declined", + "waitlist", + "invited" + ] + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Maximum matching guests to return" + }, + { + "name": "query", + "type": "str", + "default": "", + "required": false, + "help": "Search text passed to Luma guest search" + } + ], + "columns": [ + "eventId", + "guestId", + "userId", + "name", + "email", + "phone", + "status", + "registeredAt", + "profiles", + "answers" + ], + "type": "js", + "modulePath": "plugins/luma/guests.js", + "sourceFile": "plugins/luma/guests.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "luma", + "name": "login", + "description": "Open Luma sign in", + "access": "write", + "domain": "luma.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "email", + "url", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/luma/auth.js", + "sourceFile": "plugins/luma/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "luma", + "name": "set-registration-questions", + "description": "Append or replace custom registration questions on a managed Luma event", + "access": "write", + "domain": "luma.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "eventId", + "type": "str", + "required": true, + "positional": true, + "help": "" + }, + { + "name": "questions-file", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "mode", + "type": "str", + "required": true, + "help": "", + "choices": [ + "append", + "replace" + ] + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "" + } + ], + "columns": [ + "eventId", + "mode", + "previousCount", + "questionCount", + "questions", + "registrationUrl" + ], + "type": "js", + "modulePath": "plugins/luma/set-registration-questions.js", + "sourceFile": "plugins/luma/set-registration-questions.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true + }, + { + "site": "luma", + "name": "update-guest-status", + "description": "Approve or decline a pending Luma guest after explicit confirmation", + "access": "write", + "example": "webcmd luma update-guest-status evt-abc gst-abc --status approved --confirm true -f json", + "domain": "luma.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "eventId", + "type": "str", + "required": true, + "positional": true, + "help": "Luma event ID returned by webcmd luma events" + }, + { + "name": "guestId", + "type": "str", + "required": true, + "positional": true, + "help": "Luma guest ID returned by webcmd luma guests" + }, + { + "name": "status", + "type": "str", + "required": true, + "help": "New guest status", + "choices": [ + "approved", + "declined" + ] + }, + { + "name": "suppress-email", + "type": "boolean", + "default": false, + "required": false, + "help": "Set true to prevent Luma from emailing the guest" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "Required. Set --confirm true to change the real guest status" + } + ], + "columns": [ + "eventId", + "guestId", + "name", + "email", + "previousStatus", + "status", + "emailSuppressed" + ], + "type": "js", + "modulePath": "plugins/luma/update-guest-status.js", + "sourceFile": "plugins/luma/update-guest-status.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true + }, + { + "site": "luma", + "name": "whoami", + "description": "Show the current logged-in Luma account", + "access": "read", + "domain": "luma.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name", + "email", + "url" + ], + "type": "js", + "modulePath": "plugins/luma/auth.js", + "sourceFile": "plugins/luma/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "maven", + "name": "artifact", + "description": "Fetch a Maven Central artifact's version history (groupId:artifactId[:version])", + "access": "read", + "domain": "search.maven.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "coordinate", + "type": "str", + "required": true, + "positional": true, + "help": "Maven coord \"groupId:artifactId\" or \"groupId:artifactId:version\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max versions (1-200, ignored when version is pinned)" + } + ], + "columns": [ + "groupId", + "artifactId", + "version", + "packaging", + "publishedAt", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/maven/artifact.js", + "sourceFile": "plugins/maven/artifact.js" + }, + { + "site": "maven", + "name": "search", + "description": "Search Maven Central by keyword (artifact name, groupId, tag)", + "access": "read", + "domain": "search.maven.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"jackson\", \"guava\", \"ai.koog\")" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max artifacts (1-200)" + } + ], + "columns": [ + "rank", + "coordinate", + "groupId", + "artifactId", + "latestVersion", + "packaging", + "versions", + "lastPublished", + "repository", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/maven/search.js", + "sourceFile": "plugins/maven/search.js" + }, + { + "site": "mdn", + "name": "search", + "description": "Search MDN Web Docs by keyword", + "access": "read", + "domain": "developer.mozilla.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"fetch\", \"flexbox\", \"Array.prototype.map\")" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results (1-50)" + }, + { + "name": "locale", + "type": "str", + "default": "en-US", + "required": false, + "help": "Doc locale (en-US default; de / es / fr / ja / ko / pt-BR / ru / zh-CN / zh-TW)" + } + ], + "columns": [ + "rank", + "title", + "slug", + "locale", + "summary", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/mdn/search.js", + "sourceFile": "plugins/mdn/search.js" + }, + { + "site": "npm", + "name": "downloads", + "description": "Daily download counts for an npm package over a window", + "access": "read", + "domain": "api.npmjs.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "npm package name (e.g. \"react\", \"@vercel/og\")" + }, + { + "name": "period", + "type": "str", + "default": "last-week", + "required": false, + "help": "last-day / last-week / last-month / last-year, or YYYY-MM-DD:YYYY-MM-DD" + } + ], + "columns": [ + "rank", + "package", + "day", + "downloads" + ], + "type": "js", + "modulePath": "plugins/npm/downloads.js", + "sourceFile": "plugins/npm/downloads.js" + }, + { + "site": "npm", + "name": "package", + "description": "Single npm package metadata (latest version, license, homepage, repository). Use `npm downloads` for stats.", + "access": "read", + "domain": "registry.npmjs.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "npm package name (e.g. \"react\", \"@vercel/og\")" + } + ], + "columns": [ + "name", + "latestVersion", + "description", + "license", + "homepage", + "repository", + "bugs", + "maintainers", + "keywords", + "created", + "modified", + "url" + ], + "type": "js", + "modulePath": "plugins/npm/package.js", + "sourceFile": "plugins/npm/package.js" + }, + { + "site": "npm", + "name": "search", + "description": "Search the public npm registry by keyword", + "access": "read", + "domain": "registry.npmjs.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"react\", \"graphql client\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-250)" + } + ], + "columns": [ + "rank", + "name", + "version", + "description", + "weeklyDownloads", + "dependents", + "license", + "publisher", + "updated", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/npm/search.js", + "sourceFile": "plugins/npm/search.js" + }, + { + "site": "nuget", + "name": "package", + "description": "Full NuGet package version history (catalogEntry per release)", + "access": "read", + "domain": "api.nuget.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "NuGet package id (e.g. \"Newtonsoft.Json\", case-insensitive)" + } + ], + "columns": [ + "rank", + "id", + "version", + "title", + "authors", + "tags", + "language", + "licenseExpression", + "projectUrl", + "published", + "listed", + "url" + ], + "type": "js", + "modulePath": "plugins/nuget/package.js", + "sourceFile": "plugins/nuget/package.js" + }, + { + "site": "nuget", + "name": "search", + "description": "Search NuGet packages by keyword", + "access": "read", + "domain": "api.nuget.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max packages (1-1000)" + }, + { + "name": "prerelease", + "type": "boolean", + "default": false, + "required": false, + "help": "Include prerelease versions" + } + ], + "columns": [ + "rank", + "id", + "version", + "title", + "description", + "authors", + "tags", + "totalDownloads", + "verified", + "projectUrl", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/nuget/search.js", + "sourceFile": "plugins/nuget/search.js" + }, + { + "site": "nvd", + "name": "cve", + "description": "NIST NVD CVE detail (description, CVSS, CWE, KEV flag)", + "access": "read", + "domain": "services.nvd.nist.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "CVE identifier (e.g. \"CVE-2021-44228\")" + } + ], + "columns": [ + "id", + "published", + "lastModified", + "vulnStatus", + "baseScore", + "severity", + "attackVector", + "cwe", + "kevAdded", + "description", + "url" + ], + "type": "js", + "modulePath": "plugins/nvd/cve.js", + "sourceFile": "plugins/nvd/cve.js" + }, + { + "site": "oeis", + "name": "search", + "description": "Search OEIS sequences by keyword or numeric pattern", + "access": "read", + "domain": "oeis.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword or comma-separated terms (e.g. \"fibonacci\", \"1,1,2,3,5,8\")" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max sequences (1-100)" + } + ], + "columns": [ + "rank", + "id", + "name", + "keywords", + "preview", + "author", + "created", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/oeis/search.js", + "sourceFile": "plugins/oeis/search.js" + }, + { + "site": "oeis", + "name": "sequence", + "description": "Full OEIS sequence detail by A-number (terms, name, keywords, formula counts)", + "access": "read", + "domain": "oeis.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "OEIS sequence id (e.g. \"A000045\" for Fibonacci)" + } + ], + "columns": [ + "id", + "name", + "keywords", + "preview", + "termCount", + "offset", + "author", + "created", + "revision", + "commentCount", + "formulaCount", + "referenceCount", + "xrefCount", + "linkCount", + "url" + ], + "type": "js", + "modulePath": "plugins/oeis/sequence.js", + "sourceFile": "plugins/oeis/sequence.js" + }, + { + "site": "openalex", + "name": "search", + "description": "Search OpenAlex Works (papers, books, preprints) by keyword", + "access": "read", + "domain": "api.openalex.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search text (e.g. \"transformers\", \"open access scholarly\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max works (1-200, single OpenAlex page)" + } + ], + "columns": [ + "rank", + "id", + "title", + "year", + "citations", + "firstAuthor", + "venue", + "openAccess", + "type", + "doi", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/openalex/search.js", + "sourceFile": "plugins/openalex/search.js" + }, + { + "site": "openalex", + "name": "work", + "description": "Fetch a single OpenAlex Work (paper / preprint / book) — metadata + abstract", + "access": "read", + "domain": "api.openalex.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "OpenAlex Work id (\"W2741809807\"), DOI (\"10.7717/peerj.4375\"), or full URL" + } + ], + "columns": [ + "id", + "title", + "type", + "year", + "date", + "language", + "authors", + "venue", + "citations", + "openAccess", + "openAccessUrl", + "referencedCount", + "doi", + "abstract", + "url" + ], + "type": "js", + "modulePath": "plugins/openalex/work.js", + "sourceFile": "plugins/openalex/work.js" + }, + { + "site": "openfda", + "name": "drug-label", + "description": "Search FDA-approved drug labels (brand or generic name)", + "access": "read", + "domain": "fda.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Brand or generic drug name (e.g. \"aspirin\", \"lisinopril\")" + }, + { + "name": "limit", + "type": "int", + "default": 5, + "required": false, + "help": "Max rows (1-25, default 5; openFDA caps anonymous tier at 25/page)" + } + ], + "columns": [ + "rank", + "id", + "brandName", + "genericName", + "manufacturer", + "productType", + "route", + "productNdc", + "pharmClass", + "purpose", + "indications", + "warnings", + "dosage", + "effectiveTime" + ], + "type": "js", + "modulePath": "plugins/openfda/drug-label.js", + "sourceFile": "plugins/openfda/drug-label.js" + }, + { + "site": "openfda", + "name": "food-recall", + "description": "FDA food recall and enforcement actions (most recent first)", + "access": "read", + "domain": "fda.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", "type": "str", - "required": true, - "positional": true, - "help": "Luma event ID returned by webcmd luma events" + "required": false, + "help": "Free-text Lucene query (e.g. \"salmonella\", \"listeria\"); default: all recent recalls" }, { "name": "status", "type": "str", - "default": "all", "required": false, - "help": "Filter by guest approval status", - "choices": [ - "all", - "approved", - "pending_approval", - "declined", - "waitlist", - "invited" - ] + "help": "Filter by status: \"Ongoing\", \"Completed\", \"Terminated\"" }, { - "name": "limit", - "type": "int", - "default": 100, + "name": "classification", + "type": "str", "required": false, - "help": "Maximum matching guests to return" + "help": "Filter by class: \"Class I\" (most serious), \"Class II\", \"Class III\"" }, { - "name": "query", - "type": "str", - "default": "", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Search text passed to Luma guest search" + "help": "Max rows (1-100, default 10; openFDA caps anonymous tier at 100/page)" } ], "columns": [ - "eventId", - "guestId", - "userId", - "name", - "email", - "phone", + "rank", + "recallNumber", "status", - "registeredAt", - "profiles", - "answers" + "classification", + "voluntary", + "recallingFirm", + "city", + "state", + "country", + "productDescription", + "reasonForRecall", + "productQuantity", + "distributionPattern", + "reportDate", + "recallInitiationDate", + "terminationDate" ], "type": "js", - "modulePath": "plugins/luma/guests.js", - "sourceFile": "plugins/luma/guests.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/openfda/food-recall.js", + "sourceFile": "plugins/openfda/food-recall.js" }, { - "site": "luma", - "name": "login", - "description": "Open Luma sign in", - "access": "write", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, - "args": [], + "site": "openreview", + "name": "author", + "description": "List OpenReview submissions by an author profile id (newest first)", + "access": "read", + "domain": "openreview.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "profile", + "type": "str", + "required": true, + "positional": true, + "help": "OpenReview profile id (e.g. \"~Yoshua_Bengio1\"). Find it on the author profile URL on openreview.net." + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max submissions (1-1000)" + } + ], "columns": [ - "status", - "logged_in", - "site", - "name", - "email", - "url", - "action", - "verify_command" + "rank", + "id", + "title", + "authors", + "venue", + "pdate", + "url" ], "type": "js", - "modulePath": "plugins/luma/auth.js", - "sourceFile": "plugins/luma/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/openreview/author.js", + "sourceFile": "plugins/openreview/author.js" }, { - "site": "luma", - "name": "set-registration-questions", - "description": "Append or replace custom registration questions on a managed Luma event", - "access": "write", - "domain": "luma.com", - "strategy": "ui", - "browser": true, + "site": "openreview", + "name": "paper", + "description": "Show full metadata for a single OpenReview paper", + "access": "read", + "domain": "openreview.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "eventId", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "" - }, - { - "name": "questions-file", - "type": "str", - "required": true, - "help": "" - }, + "help": "OpenReview note id (e.g. \"5sRnsubyAK\")" + } + ], + "columns": [ + "id", + "title", + "authors", + "keywords", + "venue", + "venueid", + "primary_area", + "abstract", + "pdate", + "pdf", + "url" + ], + "type": "js", + "modulePath": "plugins/openreview/paper.js", + "sourceFile": "plugins/openreview/paper.js" + }, + { + "site": "openreview", + "name": "reviews", + "description": "Show full review thread (paper + reviews + decisions) for an OpenReview forum", + "access": "read", + "domain": "openreview.net", + "strategy": "public", + "browser": false, + "args": [ { - "name": "mode", + "name": "forum", "type": "str", "required": true, - "help": "", - "choices": [ - "append", - "replace" - ] + "positional": true, + "help": "OpenReview forum id (same as paper id)" }, { - "name": "confirm", - "type": "boolean", - "default": false, + "name": "max-length", + "type": "int", + "default": 4000, "required": false, - "help": "" + "help": "Per-row text truncation (min 200)" } ], "columns": [ - "eventId", - "mode", - "previousCount", - "questionCount", - "questions", - "registrationUrl" + "type", + "author", + "rating", + "confidence", + "text" ], "type": "js", - "modulePath": "plugins/luma/set-registration-questions.js", - "sourceFile": "plugins/luma/set-registration-questions.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "modulePath": "plugins/openreview/reviews.js", + "sourceFile": "plugins/openreview/reviews.js" }, { - "site": "luma", - "name": "update-guest-status", - "description": "Approve or decline a pending Luma guest after explicit confirmation", - "access": "write", - "example": "webcmd luma update-guest-status evt-abc gst-abc --status approved --confirm true -f json", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, + "site": "openreview", + "name": "search", + "description": "Search OpenReview papers by free-text query", + "access": "read", + "domain": "openreview.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "eventId", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Luma event ID returned by webcmd luma events" + "help": "Search keyword (e.g. \"diffusion model\")" }, { - "name": "guestId", - "type": "str", - "required": true, - "positional": true, - "help": "Luma guest ID returned by webcmd luma guests" - }, + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max results (max 50)" + } + ], + "columns": [ + "rank", + "id", + "title", + "authors", + "venue", + "pdate", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/openreview/search.js", + "sourceFile": "plugins/openreview/search.js" + }, + { + "site": "openreview", + "name": "venue", + "description": "List papers at an OpenReview venue (e.g. \"ICLR 2024 oral\" or full invitation id)", + "access": "read", + "domain": "openreview.net", + "strategy": "public", + "browser": false, + "args": [ { - "name": "status", + "name": "venue", "type": "str", "required": true, - "help": "New guest status", - "choices": [ - "approved", - "declined" - ] + "positional": true, + "help": "Venue name (\"ICLR 2024 oral\") or invitation (\"ICLR.cc/2025/Conference/-/Submission\")" }, { - "name": "suppress-email", - "type": "boolean", - "default": false, + "name": "limit", + "type": "int", + "default": 25, "required": false, - "help": "Set true to prevent Luma from emailing the guest" + "help": "Max results (max 200)" }, { - "name": "confirm", - "type": "boolean", - "default": false, + "name": "offset", + "type": "int", + "default": 0, "required": false, - "help": "Required. Set --confirm true to change the real guest status" + "help": "Pagination offset" } ], "columns": [ - "eventId", - "guestId", - "name", - "email", - "previousStatus", - "status", - "emailSuppressed" - ], - "type": "js", - "modulePath": "plugins/luma/update-guest-status.js", - "sourceFile": "plugins/luma/update-guest-status.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true - }, - { - "site": "luma", - "name": "whoami", - "description": "Show the current logged-in Luma account", - "access": "read", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name", - "email", + "rank", + "id", + "title", + "authors", + "keywords", + "primary_area", + "pdate", + "pdf", "url" ], "type": "js", - "modulePath": "plugins/luma/auth.js", - "sourceFile": "plugins/luma/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/openreview/venue.js", + "sourceFile": "plugins/openreview/venue.js" }, { "site": "pypi", diff --git a/plugins/homebrew/README.md b/plugins/homebrew/README.md new file mode 100644 index 00000000..87446015 --- /dev/null +++ b/plugins/homebrew/README.md @@ -0,0 +1,17 @@ +# webcmd-plugin-homebrew + +Webcmd commands for homebrew. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/homebrew +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd homebrew cask` | Fetch a Homebrew cask's metadata (version, homepage, deprecation, download URL) | +| `webcmd homebrew formula` | Fetch a Homebrew formula's metadata (version, license, deps, deprecation, source) | +| `webcmd homebrew popular` | List most-installed Homebrew formulae or casks (Homebrew's analytics ranking) | diff --git a/clis/homebrew/cask.js b/plugins/homebrew/cask.js similarity index 100% rename from clis/homebrew/cask.js rename to plugins/homebrew/cask.js diff --git a/clis/homebrew/formula.js b/plugins/homebrew/formula.js similarity index 100% rename from clis/homebrew/formula.js rename to plugins/homebrew/formula.js diff --git a/plugins/homebrew/package.json b/plugins/homebrew/package.json new file mode 100644 index 00000000..08fdd8e2 --- /dev/null +++ b/plugins/homebrew/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-homebrew", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for homebrew", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/homebrew/popular.js b/plugins/homebrew/popular.js similarity index 100% rename from clis/homebrew/popular.js rename to plugins/homebrew/popular.js diff --git a/clis/homebrew/utils.js b/plugins/homebrew/utils.js similarity index 100% rename from clis/homebrew/utils.js rename to plugins/homebrew/utils.js diff --git a/plugins/homebrew/webcmd-plugin.json b/plugins/homebrew/webcmd-plugin.json new file mode 100644 index 00000000..7af1dbc8 --- /dev/null +++ b/plugins/homebrew/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "homebrew", + "version": "0.1.0", + "description": "Webcmd commands for homebrew", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/jira/README.md b/plugins/jira/README.md new file mode 100644 index 00000000..1c9747f3 --- /dev/null +++ b/plugins/jira/README.md @@ -0,0 +1,19 @@ +# webcmd-plugin-jira + +Webcmd commands for jira. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/jira +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd jira attachments` | Jira issue attachment metadata | +| `webcmd jira comments` | Jira issue comments as Markdown | +| `webcmd jira issue` | Jira issue detail normalized for agents (description, comments, attachments, links) | +| `webcmd jira links` | Jira issue links | +| `webcmd jira search` | Search Jira issues with JQL | diff --git a/plugins/jira/atlassian.js b/plugins/jira/atlassian.js new file mode 100644 index 00000000..e33a4900 --- /dev/null +++ b/plugins/jira/atlassian.js @@ -0,0 +1,342 @@ +import { htmlToMarkdown as coreHtmlToMarkdown } from '@agentrhq/webcmd/utils'; +import { + ArgumentError, + AuthRequiredError, + CommandExecutionError, + ConfigError, + EmptyResultError, +} from '@agentrhq/webcmd/errors'; + +const USER_AGENT = 'webcmd-atlassian-adapter (+https://github.com/agentrhq/webcmd)'; +const DEPLOYMENTS = new Set(['cloud', 'datacenter', 'auto']); + +function firstEnv(names) { + for (const name of names) { + const value = process.env[name]?.trim(); + if (value) return value; + } + return ''; +} + +function normalizeBaseUrl(value, label) { + const raw = String(value ?? '').trim(); + if (!raw) { + throw new ConfigError(`Missing ${label}`, `Set ${label}, for example https://example.atlassian.net`); + } + let parsed; + try { + parsed = new URL(raw); + } catch { + throw new ConfigError(`Invalid ${label}: ${raw}`, 'Use an absolute http(s) URL.'); + } + if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') { + throw new ConfigError(`Invalid ${label}: ${raw}`, 'Use an http(s) URL.'); + } + parsed.hash = ''; + parsed.search = ''; + return parsed.toString().replace(/\/+$/, ''); +} + +function parseDeployment(raw, baseUrl) { + const value = String(raw || 'auto').trim().toLowerCase(); + if (!DEPLOYMENTS.has(value)) { + throw new ConfigError('Invalid ATLASSIAN_DEPLOYMENT', 'Expected one of: cloud, datacenter, auto.'); + } + if (value !== 'auto') return value; + const host = new URL(baseUrl).hostname; + return host === 'atlassian.net' || host.endsWith('.atlassian.net') ? 'cloud' : 'datacenter'; +} + +function basicAuth(user, token) { + return `Basic ${Buffer.from(`${user}:${token}`, 'utf8').toString('base64')}`; +} + +function resolveAuthHeaders(deployment, productLabel) { + const bearer = firstEnv(['ATLASSIAN_BEARER_TOKEN', 'ATLASSIAN_OAUTH_TOKEN']); + if (bearer) return { Authorization: `Bearer ${bearer}` }; + + const pat = firstEnv(['ATLASSIAN_PAT', `${productLabel.toUpperCase()}_PAT`]); + if (deployment === 'datacenter' && pat) return { Authorization: `Bearer ${pat}` }; + + const prefix = productLabel.toUpperCase(); + const email = firstEnv(['ATLASSIAN_EMAIL', 'ATLASSIAN_USERNAME', `${prefix}_EMAIL`, `${prefix}_USERNAME`]); + const token = firstEnv(['ATLASSIAN_API_TOKEN', 'ATLASSIAN_PASSWORD', `${prefix}_API_TOKEN`, `${prefix}_PASSWORD`]); + if (email && token) return { Authorization: basicAuth(email, token) }; + + if (deployment === 'cloud') { + throw new ConfigError( + 'Missing Atlassian Cloud credentials', + 'Set ATLASSIAN_EMAIL and ATLASSIAN_API_TOKEN, or set ATLASSIAN_BEARER_TOKEN for OAuth.', + ); + } + throw new ConfigError( + 'Missing Atlassian Data Center credentials', + 'Set ATLASSIAN_PAT, ATLASSIAN_BEARER_TOKEN, or ATLASSIAN_USERNAME plus ATLASSIAN_PASSWORD.', + ); +} + +export function getJiraConfig() { + const baseUrl = normalizeBaseUrl(firstEnv(['ATLASSIAN_JIRA_BASE_URL', 'JIRA_BASE_URL']), 'ATLASSIAN_JIRA_BASE_URL'); + const deployment = parseDeployment(process.env.ATLASSIAN_DEPLOYMENT, baseUrl); + return { + product: 'jira', + baseUrl, + deployment, + authHeaders: resolveAuthHeaders(deployment, 'jira'), + }; +} + +function joinUrl(baseUrl, apiPath) { + if (/^https?:\/\//i.test(apiPath)) return apiPath; + const path = apiPath.startsWith('/') ? apiPath : `/${apiPath}`; + return `${baseUrl}${path}`; +} + +function summarizeApiError(parsed, fallback) { + if (parsed && typeof parsed === 'object') { + const messages = []; + if (Array.isArray(parsed.errorMessages)) messages.push(...parsed.errorMessages.filter(Boolean)); + if (typeof parsed.message === 'string') messages.push(parsed.message); + if (typeof parsed.error === 'string') messages.push(parsed.error); + if (typeof parsed.reason === 'string') messages.push(parsed.reason); + if (parsed.errors && typeof parsed.errors === 'object') { + for (const [key, value] of Object.entries(parsed.errors)) messages.push(`${key}: ${String(value)}`); + } + if (messages.length) return messages.join(' \u00b7 '); + } + if (typeof parsed === 'string' && parsed.trim()) return parsed.trim().slice(0, 300); + return fallback; +} + +async function parseResponseBody(resp, label) { + let text; + try { + text = await resp.text(); + } catch (err) { + throw new CommandExecutionError( + `${label} response body could not be read: ${err?.message ?? err}`, + 'Check whether the Atlassian instance, proxy, or network interrupted the response.', + ); + } + if (!text) return null; + try { + return JSON.parse(text); + } catch { + return text; + } +} + +export async function atlassianRequest(config, apiPath, options = {}) { + const method = (options.method ?? 'GET').toUpperCase(); + const label = options.label ?? `${config.product} ${method} ${apiPath}`; + const headers = { + 'user-agent': USER_AGENT, + accept: 'application/json', + ...config.authHeaders, + ...(options.headers ?? {}), + }; + let body; + if (options.body !== undefined) { + headers['content-type'] = headers['content-type'] ?? 'application/json'; + body = typeof options.body === 'string' ? options.body : JSON.stringify(options.body); + } + + let resp; + const url = joinUrl(config.baseUrl, apiPath); + try { + resp = await fetch(url, { method, headers, body }); + } catch (err) { + throw new CommandExecutionError( + `${label} request failed: ${err?.message ?? err}`, + 'Check the Atlassian base URL, VPN/network access, and proxy settings.', + ); + } + + const parsed = await parseResponseBody(resp, label); + if (resp.status === 401) { + throw new AuthRequiredError( + config.baseUrl, + `${label} returned HTTP 401`, + 'Check Atlassian credentials and whether this instance accepts the configured auth method.', + ); + } + if (resp.status === 403) { + throw new AuthRequiredError( + config.baseUrl, + `${label} returned HTTP 403: ${summarizeApiError(parsed, 'forbidden')}`, + 'The authenticated user lacks permission for this Jira issue.', + ); + } + if (resp.status === 404) throw new EmptyResultError(label, `Atlassian returned 404 for ${url}.`); + if (resp.status === 409) { + throw new CommandExecutionError( + `${label} returned HTTP 409: ${summarizeApiError(parsed, 'version conflict')}`, + 'Reload the current Jira issue and retry.', + ); + } + if (resp.status === 429) { + throw new CommandExecutionError(`${label} returned HTTP 429 (rate limited)`, 'Wait and retry with a smaller limit.'); + } + if (!resp.ok) { + throw new CommandExecutionError(`${label} returned HTTP ${resp.status}: ${summarizeApiError(parsed, resp.statusText)}`); + } + if (typeof parsed === 'string') { + throw new CommandExecutionError( + `${label} returned a non-JSON response`, + 'Expected Atlassian REST API JSON. Check the base URL and whether an HTML login, SSO, or proxy page was returned.', + ); + } + return parsed; +} + +export function queryString(params) { + const qs = new URLSearchParams(); + for (const [key, value] of Object.entries(params)) { + if (value === undefined || value === null || value === '') continue; + if (Array.isArray(value)) { + for (const item of value) qs.append(key, String(item)); + } else { + qs.set(key, String(value)); + } + } + const value = qs.toString(); + return value ? `?${value}` : ''; +} + +export function requireString(value, label) { + const string = String(value ?? '').trim(); + if (!string) throw new ArgumentError(`${label} is required`); + return string; +} + +export function requirePayloadObject(value, label) { + if (!value || typeof value !== 'object' || Array.isArray(value)) { + throw new CommandExecutionError(`${label} returned an unexpected payload shape; expected an object.`); + } + return value; +} + +export function requirePayloadArray(value, label) { + if (!Array.isArray(value)) { + throw new CommandExecutionError(`${label} returned an unexpected payload shape; expected an array.`); + } + return value; +} + +export function requirePayloadString(value, field, label) { + if (typeof value !== 'string' && typeof value !== 'number') { + throw new CommandExecutionError(`${label} did not include a stable ${field}.`); + } + const string = String(value).trim(); + if (!string) throw new CommandExecutionError(`${label} did not include a stable ${field}.`); + return string; +} + +export function requireNonEmptyRows(rows, label, hint) { + if (!rows.length) throw new EmptyResultError(label, hint); + return rows; +} + +export function parseLimit(value, defaultValue = 20, maxValue = 100, label = 'limit') { + const raw = value ?? defaultValue; + const parsed = typeof raw === 'number' ? raw : Number(raw); + if (!Number.isInteger(parsed) || parsed <= 0) throw new ArgumentError(`${label} must be a positive integer`); + if (parsed > maxValue) throw new ArgumentError(`${label} must be <= ${maxValue}`); + return parsed; +} + +export function htmlToMarkdown(html) { + return coreHtmlToMarkdown(String(html ?? '')); +} + +function applyAdfMarks(text, marks = []) { + let out = text; + for (const mark of marks) { + const type = mark?.type; + if (type === 'link' && mark.attrs?.href) out = `[${out}](${mark.attrs.href})`; + else if (type === 'strong') out = `**${out}**`; + else if (type === 'em') out = `_${out}_`; + else if (type === 'code') out = `\`${out}\``; + else if (type === 'strike') out = `~~${out}~~`; + } + return out; +} + +function renderAdfNode(node, depth = 0) { + if (!node || typeof node !== 'object') return ''; + const content = Array.isArray(node.content) ? node.content : []; + const renderChildren = (separator = '') => content.map((child) => renderAdfNode(child, depth)).filter(Boolean).join(separator); + switch (node.type) { + case 'doc': + return content.map((child) => renderAdfNode(child, depth)).filter(Boolean).join('\n\n').trim(); + case 'paragraph': + return renderChildren(''); + case 'text': + return applyAdfMarks(String(node.text ?? ''), Array.isArray(node.marks) ? node.marks : []); + case 'hardBreak': + return '\n'; + case 'heading': + return `${'#'.repeat(Math.max(1, Math.min(6, Number(node.attrs?.level ?? 2))))} ${renderChildren('')}`; + case 'bulletList': + return content.map((child) => renderAdfListItem(child, depth, '-')).join('\n'); + case 'orderedList': + return content.map((child, index) => renderAdfListItem(child, depth, `${index + 1}.`)).join('\n'); + case 'listItem': + return renderChildren('\n'); + case 'codeBlock': + return `\`\`\`\n${renderChildren('')}\n\`\`\``; + case 'blockquote': + return renderChildren('\n').split('\n').map((line) => `> ${line}`).join('\n'); + case 'rule': + return '---'; + case 'table': + return renderAdfTable(content); + case 'tableRow': + return content.map((cell) => escapeMarkdownTableCell(renderAdfNode(cell, depth))).join(' | '); + case 'tableHeader': + case 'tableCell': + return renderChildren(' ').replace(/\s+/g, ' ').trim(); + case 'mention': + return node.attrs?.text ? String(node.attrs.text) : ''; + case 'emoji': + return String(node.attrs?.shortName ?? node.attrs?.text ?? ''); + case 'inlineCard': + return node.attrs?.url ? String(node.attrs.url) : ''; + default: + return renderChildren(''); + } +} + +function renderAdfListItem(node, depth, marker) { + const indent = ' '.repeat(depth); + const body = renderAdfNode(node, depth + 1).trim(); + const [first, ...rest] = body.split('\n'); + return `${indent}${marker} ${first ?? ''}${rest.length ? `\n${rest.map((line) => `${indent} ${line}`).join('\n')}` : ''}`; +} + +function escapeMarkdownTableCell(value) { + return String(value ?? '').replace(/\|/g, '\\|').replace(/\n+/g, '
').trim(); +} + +function renderAdfTable(rows) { + const matrix = rows + .map((row) => { + const cells = Array.isArray(row?.content) ? row.content : []; + return cells.map((cell) => escapeMarkdownTableCell(renderAdfNode(cell))); + }) + .filter((row) => row.length > 0); + if (!matrix.length) return ''; + const columnCount = Math.max(...matrix.map((row) => row.length)); + const normalize = (row) => Array.from({ length: columnCount }, (_value, index) => row[index] ?? '').join(' | '); + return [ + normalize(matrix[0]), + Array.from({ length: columnCount }, () => '---').join(' | '), + ...matrix.slice(1).map(normalize), + ].join('\n'); +} + +export function adfToMarkdown(value) { + if (!value) return ''; + if (typeof value === 'string') return value.trim(); + return renderAdfNode(value).trim(); +} diff --git a/clis/jira/attachments.js b/plugins/jira/attachments.js similarity index 94% rename from clis/jira/attachments.js rename to plugins/jira/attachments.js index 4df0d52e..ab983fe4 100644 --- a/clis/jira/attachments.js +++ b/plugins/jira/attachments.js @@ -1,6 +1,6 @@ import { cli, Strategy } from '@agentrhq/webcmd/registry'; import { fetchIssue, jiraConfig, jiraRowsOrEmpty, normalizeAttachment, requireIssueKey } from './shared.js'; -import { requirePayloadArray } from '../_atlassian/shared.js'; +import { requirePayloadArray } from './atlassian.js'; cli({ site: 'jira', diff --git a/clis/jira/comments.js b/plugins/jira/comments.js similarity index 100% rename from clis/jira/comments.js rename to plugins/jira/comments.js diff --git a/clis/jira/issue.js b/plugins/jira/issue.js similarity index 100% rename from clis/jira/issue.js rename to plugins/jira/issue.js diff --git a/clis/jira/links.js b/plugins/jira/links.js similarity index 93% rename from clis/jira/links.js rename to plugins/jira/links.js index 4c7e6f39..05d05aaf 100644 --- a/clis/jira/links.js +++ b/plugins/jira/links.js @@ -1,6 +1,6 @@ import { cli, Strategy } from '@agentrhq/webcmd/registry'; import { fetchIssue, jiraConfig, jiraRowsOrEmpty, normalizeIssueLink, requireIssueKey } from './shared.js'; -import { requirePayloadArray } from '../_atlassian/shared.js'; +import { requirePayloadArray } from './atlassian.js'; cli({ site: 'jira', diff --git a/plugins/jira/package.json b/plugins/jira/package.json new file mode 100644 index 00000000..a86b72df --- /dev/null +++ b/plugins/jira/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-jira", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for jira", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/jira/search.js b/plugins/jira/search.js similarity index 97% rename from clis/jira/search.js rename to plugins/jira/search.js index 45e24d03..bc332cb5 100644 --- a/clis/jira/search.js +++ b/plugins/jira/search.js @@ -1,6 +1,6 @@ import { cli, Strategy } from '@agentrhq/webcmd/registry'; import { jiraConfig, issueSummaryRow, jiraRowsOrEmpty, parseJiraLimit } from './shared.js'; -import { atlassianRequest, requirePayloadArray, requirePayloadObject, requireString } from '../_atlassian/shared.js'; +import { atlassianRequest, requirePayloadArray, requirePayloadObject, requireString } from './atlassian.js'; function searchPath(config) { return config.deployment === 'cloud' ? '/rest/api/3/search/jql' : '/rest/api/2/search'; diff --git a/clis/jira/shared.js b/plugins/jira/shared.js similarity index 99% rename from clis/jira/shared.js rename to plugins/jira/shared.js index 06383473..f4a399bd 100644 --- a/clis/jira/shared.js +++ b/plugins/jira/shared.js @@ -10,7 +10,7 @@ import { requirePayloadObject, requirePayloadString, requireString, -} from '../_atlassian/shared.js'; +} from './atlassian.js'; import { ArgumentError } from '@agentrhq/webcmd/errors'; const DEFAULT_ISSUE_FIELDS = [ diff --git a/plugins/jira/test/atlassian.test.js b/plugins/jira/test/atlassian.test.js new file mode 100644 index 00000000..1e385307 --- /dev/null +++ b/plugins/jira/test/atlassian.test.js @@ -0,0 +1,117 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { CommandExecutionError } from '@agentrhq/webcmd/errors'; +import { + adfToMarkdown, + atlassianRequest, + getJiraConfig, + htmlToMarkdown, +} from '../atlassian.js'; + +const ENV_KEYS = [ + 'ATLASSIAN_BEARER_TOKEN', + 'ATLASSIAN_DEPLOYMENT', + 'ATLASSIAN_EMAIL', + 'ATLASSIAN_API_TOKEN', + 'ATLASSIAN_OAUTH_TOKEN', + 'ATLASSIAN_PAT', + 'ATLASSIAN_PASSWORD', + 'ATLASSIAN_USERNAME', + 'ATLASSIAN_JIRA_BASE_URL', + 'JIRA_API_TOKEN', + 'JIRA_BASE_URL', + 'JIRA_EMAIL', + 'JIRA_PASSWORD', + 'JIRA_PAT', + 'JIRA_USERNAME', +]; + +function clearEnv() { + for (const key of ENV_KEYS) delete process.env[key]; +} + +afterEach(() => { + clearEnv(); + vi.unstubAllGlobals(); +}); + +describe('jira atlassian helpers', () => { + it('builds Jira Cloud and Data Center authentication', () => { + process.env.ATLASSIAN_JIRA_BASE_URL = 'https://team.atlassian.net'; + process.env.ATLASSIAN_EMAIL = 'bot@example.com'; + process.env.ATLASSIAN_API_TOKEN = 'secret'; + expect(getJiraConfig()).toMatchObject({ + baseUrl: 'https://team.atlassian.net', + deployment: 'cloud', + authHeaders: { Authorization: `Basic ${Buffer.from('bot@example.com:secret').toString('base64')}` }, + }); + + clearEnv(); + process.env.ATLASSIAN_JIRA_BASE_URL = 'https://jira.example.com'; + process.env.ATLASSIAN_DEPLOYMENT = 'datacenter'; + process.env.ATLASSIAN_PAT = 'pat-123'; + expect(getJiraConfig()).toMatchObject({ + baseUrl: 'https://jira.example.com', + deployment: 'datacenter', + authHeaders: { Authorization: 'Bearer pat-123' }, + }); + }); + + it('converts Jira ADF and rendered HTML to Markdown', () => { + const markdown = adfToMarkdown({ + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { type: 'text', text: 'Broken ', marks: [{ type: 'strong' }] }, + { type: 'text', text: 'checkout', marks: [{ type: 'link', attrs: { href: 'https://example.com' } }] }, + ], + }, + { + type: 'bulletList', + content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'retry payment' }] }] }], + }, + ], + }); + expect(markdown).toContain('**Broken **'); + expect(markdown).toContain('[checkout](https://example.com)'); + expect(markdown).toContain('- retry payment'); + expect(adfToMarkdown({ + type: 'doc', + content: [{ + type: 'table', + content: [ + { type: 'tableRow', content: [{ type: 'tableHeader', content: [{ type: 'text', text: 'Notes' }] }] }, + { type: 'tableRow', content: [{ type: 'tableCell', content: [{ type: 'text', text: 'a | b' }] }] }, + ], + }], + })).toContain('a \\| b'); + expect(htmlToMarkdown('

Fixed
Ready

')).toContain('**Fixed**'); + }); + + it('sends JSON requests and preserves typed failures', async () => { + const config = { + product: 'jira', + baseUrl: 'https://jira.example.com', + deployment: 'datacenter', + authHeaders: { Authorization: 'Bearer token' }, + }; + const fetchMock = vi.fn(async () => new Response(JSON.stringify({ ok: true }), { status: 200 })); + vi.stubGlobal('fetch', fetchMock); + await expect(atlassianRequest(config, '/rest/api/2/myself', { label: 'jira myself' })).resolves.toEqual({ ok: true }); + expect(fetchMock.mock.calls[0][0]).toBe('https://jira.example.com/rest/api/2/myself'); + expect(fetchMock.mock.calls[0][1].headers.Authorization).toBe('Bearer token'); + + vi.stubGlobal('fetch', vi.fn(async () => new Response(JSON.stringify({ message: 'bad token' }), { status: 401 }))); + await expect(atlassianRequest(config, '/rest/api/2/myself', { label: 'jira myself' })) + .rejects.toMatchObject({ code: 'AUTH_REQUIRED' }); + + vi.stubGlobal('fetch', vi.fn(async () => new Response(JSON.stringify({ message: 'slow down' }), { status: 429 }))); + await expect(atlassianRequest(config, '/rest/api/2/myself', { label: 'jira myself' })) + .rejects.toMatchObject({ code: 'COMMAND_EXEC' }); + + vi.stubGlobal('fetch', vi.fn(async () => new Response('login', { status: 200 }))); + await expect(atlassianRequest(config, '/rest/api/2/myself', { label: 'jira myself' })) + .rejects.toBeInstanceOf(CommandExecutionError); + }); +}); diff --git a/clis/jira/commands.test.js b/plugins/jira/test/commands.test.js similarity index 98% rename from clis/jira/commands.test.js rename to plugins/jira/test/commands.test.js index 42ab56df..34ead20b 100644 --- a/clis/jira/commands.test.js +++ b/plugins/jira/test/commands.test.js @@ -1,12 +1,12 @@ import { describe, expect, it, afterEach, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { __test__ as jiraSharedTest } from './shared.js'; -import './issue.js'; -import './search.js'; -import './comments.js'; -import './attachments.js'; -import './links.js'; +import { __test__ as jiraSharedTest } from '../shared.js'; +import '../issue.js'; +import '../search.js'; +import '../comments.js'; +import '../attachments.js'; +import '../links.js'; const ENV_KEYS = [ 'ATLASSIAN_JIRA_BASE_URL', diff --git a/plugins/jira/webcmd-plugin.json b/plugins/jira/webcmd-plugin.json new file mode 100644 index 00000000..0e7064ff --- /dev/null +++ b/plugins/jira/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "jira", + "version": "0.1.0", + "description": "Webcmd commands for jira", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/lesswrong/README.md b/plugins/lesswrong/README.md new file mode 100644 index 00000000..634cee21 --- /dev/null +++ b/plugins/lesswrong/README.md @@ -0,0 +1,29 @@ +# webcmd-plugin-lesswrong + +Webcmd commands for lesswrong. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/lesswrong +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd lesswrong comments` | Top comments on a post | +| `webcmd lesswrong curated` | Curated editor's picks | +| `webcmd lesswrong frontpage` | Algorithmic frontpage | +| `webcmd lesswrong new` | Latest posts | +| `webcmd lesswrong read` | Read full post by URL or ID | +| `webcmd lesswrong sequences` | List post collections | +| `webcmd lesswrong shortform` | Quick takes / shortform posts | +| `webcmd lesswrong tag` | Posts by tag | +| `webcmd lesswrong tags` | List popular tags | +| `webcmd lesswrong top` | Top all-time | +| `webcmd lesswrong top-month` | Top this month | +| `webcmd lesswrong top-week` | Top this week | +| `webcmd lesswrong top-year` | Top this year | +| `webcmd lesswrong user` | User profile | +| `webcmd lesswrong user-posts` | List a user's posts | diff --git a/clis/lesswrong/_helpers.js b/plugins/lesswrong/_helpers.js similarity index 100% rename from clis/lesswrong/_helpers.js rename to plugins/lesswrong/_helpers.js diff --git a/clis/lesswrong/comments.js b/plugins/lesswrong/comments.js similarity index 100% rename from clis/lesswrong/comments.js rename to plugins/lesswrong/comments.js diff --git a/clis/lesswrong/curated.js b/plugins/lesswrong/curated.js similarity index 100% rename from clis/lesswrong/curated.js rename to plugins/lesswrong/curated.js diff --git a/clis/lesswrong/frontpage.js b/plugins/lesswrong/frontpage.js similarity index 100% rename from clis/lesswrong/frontpage.js rename to plugins/lesswrong/frontpage.js diff --git a/clis/lesswrong/new.js b/plugins/lesswrong/new.js similarity index 100% rename from clis/lesswrong/new.js rename to plugins/lesswrong/new.js diff --git a/plugins/lesswrong/package.json b/plugins/lesswrong/package.json new file mode 100644 index 00000000..16916178 --- /dev/null +++ b/plugins/lesswrong/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-lesswrong", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for lesswrong", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/lesswrong/read.js b/plugins/lesswrong/read.js similarity index 100% rename from clis/lesswrong/read.js rename to plugins/lesswrong/read.js diff --git a/clis/lesswrong/sequences.js b/plugins/lesswrong/sequences.js similarity index 100% rename from clis/lesswrong/sequences.js rename to plugins/lesswrong/sequences.js diff --git a/clis/lesswrong/shortform.js b/plugins/lesswrong/shortform.js similarity index 100% rename from clis/lesswrong/shortform.js rename to plugins/lesswrong/shortform.js diff --git a/clis/lesswrong/tag.js b/plugins/lesswrong/tag.js similarity index 100% rename from clis/lesswrong/tag.js rename to plugins/lesswrong/tag.js diff --git a/clis/lesswrong/tags.js b/plugins/lesswrong/tags.js similarity index 100% rename from clis/lesswrong/tags.js rename to plugins/lesswrong/tags.js diff --git a/clis/lesswrong/frontpage.test.js b/plugins/lesswrong/test/frontpage.test.js similarity index 92% rename from clis/lesswrong/frontpage.test.js rename to plugins/lesswrong/test/frontpage.test.js index d742363a..3eb09762 100644 --- a/clis/lesswrong/frontpage.test.js +++ b/plugins/lesswrong/test/frontpage.test.js @@ -2,12 +2,12 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; const { gqlRequestMock } = vi.hoisted(() => ({ gqlRequestMock: vi.fn() })); -vi.mock('./_helpers.js', async () => { - const actual = await vi.importActual('./_helpers.js'); +vi.mock('../_helpers.js', async () => { + const actual = await vi.importActual('../_helpers.js'); return { ...actual, gqlRequest: gqlRequestMock }; }); -import './frontpage.js'; +import '../frontpage.js'; describe('lesswrong frontpage', () => { beforeEach(() => { diff --git a/clis/lesswrong/top-month.js b/plugins/lesswrong/top-month.js similarity index 100% rename from clis/lesswrong/top-month.js rename to plugins/lesswrong/top-month.js diff --git a/clis/lesswrong/top-week.js b/plugins/lesswrong/top-week.js similarity index 100% rename from clis/lesswrong/top-week.js rename to plugins/lesswrong/top-week.js diff --git a/clis/lesswrong/top-year.js b/plugins/lesswrong/top-year.js similarity index 100% rename from clis/lesswrong/top-year.js rename to plugins/lesswrong/top-year.js diff --git a/clis/lesswrong/top.js b/plugins/lesswrong/top.js similarity index 100% rename from clis/lesswrong/top.js rename to plugins/lesswrong/top.js diff --git a/clis/lesswrong/user-posts.js b/plugins/lesswrong/user-posts.js similarity index 100% rename from clis/lesswrong/user-posts.js rename to plugins/lesswrong/user-posts.js diff --git a/clis/lesswrong/user.js b/plugins/lesswrong/user.js similarity index 100% rename from clis/lesswrong/user.js rename to plugins/lesswrong/user.js diff --git a/plugins/lesswrong/webcmd-plugin.json b/plugins/lesswrong/webcmd-plugin.json new file mode 100644 index 00000000..07615531 --- /dev/null +++ b/plugins/lesswrong/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "lesswrong", + "version": "0.1.0", + "description": "Webcmd commands for lesswrong", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/lichess/README.md b/plugins/lichess/README.md new file mode 100644 index 00000000..5193b792 --- /dev/null +++ b/plugins/lichess/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-lichess + +Webcmd commands for lichess. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/lichess +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd lichess top` | Top-N Lichess leaderboard for a perf type (bullet/blitz/rapid/classical/...) | +| `webcmd lichess user` | Fetch a Lichess player profile by username (rating, perfs, counts) | diff --git a/plugins/lichess/package.json b/plugins/lichess/package.json new file mode 100644 index 00000000..02ea271b --- /dev/null +++ b/plugins/lichess/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-lichess", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for lichess", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/lichess/lichess.test.js b/plugins/lichess/test/lichess.test.js similarity index 98% rename from clis/lichess/lichess.test.js rename to plugins/lichess/test/lichess.test.js index af00cdfe..a913fd19 100644 --- a/clis/lichess/lichess.test.js +++ b/plugins/lichess/test/lichess.test.js @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './user.js'; -import './top.js'; +import '../user.js'; +import '../top.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/lichess/top.js b/plugins/lichess/top.js similarity index 100% rename from clis/lichess/top.js rename to plugins/lichess/top.js diff --git a/clis/lichess/user.js b/plugins/lichess/user.js similarity index 100% rename from clis/lichess/user.js rename to plugins/lichess/user.js diff --git a/clis/lichess/utils.js b/plugins/lichess/utils.js similarity index 100% rename from clis/lichess/utils.js rename to plugins/lichess/utils.js diff --git a/plugins/lichess/webcmd-plugin.json b/plugins/lichess/webcmd-plugin.json new file mode 100644 index 00000000..22e91e58 --- /dev/null +++ b/plugins/lichess/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "lichess", + "version": "0.1.0", + "description": "Webcmd commands for lichess", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/lobsters/README.md b/plugins/lobsters/README.md new file mode 100644 index 00000000..cc9bb02a --- /dev/null +++ b/plugins/lobsters/README.md @@ -0,0 +1,20 @@ +# webcmd-plugin-lobsters + +Webcmd commands for lobsters. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/lobsters +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd lobsters active` | Lobste.rs most active discussions | +| `webcmd lobsters domain` | Lobste.rs stories submitted from a specific domain | +| `webcmd lobsters hot` | Lobste.rs hottest stories | +| `webcmd lobsters newest` | Lobste.rs newest stories | +| `webcmd lobsters read` | Read a Lobste.rs story and its comment tree | +| `webcmd lobsters tag` | Lobste.rs stories by tag | diff --git a/clis/lobsters/active.js b/plugins/lobsters/active.js similarity index 100% rename from clis/lobsters/active.js rename to plugins/lobsters/active.js diff --git a/clis/lobsters/domain.js b/plugins/lobsters/domain.js similarity index 100% rename from clis/lobsters/domain.js rename to plugins/lobsters/domain.js diff --git a/clis/lobsters/hot.js b/plugins/lobsters/hot.js similarity index 100% rename from clis/lobsters/hot.js rename to plugins/lobsters/hot.js diff --git a/clis/lobsters/newest.js b/plugins/lobsters/newest.js similarity index 100% rename from clis/lobsters/newest.js rename to plugins/lobsters/newest.js diff --git a/plugins/lobsters/package.json b/plugins/lobsters/package.json new file mode 100644 index 00000000..594cfa0c --- /dev/null +++ b/plugins/lobsters/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-lobsters", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for lobsters", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/lobsters/read.js b/plugins/lobsters/read.js similarity index 100% rename from clis/lobsters/read.js rename to plugins/lobsters/read.js diff --git a/clis/lobsters/tag.js b/plugins/lobsters/tag.js similarity index 100% rename from clis/lobsters/tag.js rename to plugins/lobsters/tag.js diff --git a/clis/lobsters/lobsters.test.js b/plugins/lobsters/test/lobsters.test.js similarity index 98% rename from clis/lobsters/lobsters.test.js rename to plugins/lobsters/test/lobsters.test.js index 67ce7383..6d5f38e8 100644 --- a/clis/lobsters/lobsters.test.js +++ b/plugins/lobsters/test/lobsters.test.js @@ -1,11 +1,11 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './hot.js'; -import './active.js'; -import './newest.js'; -import './tag.js'; -import './read.js'; +import '../hot.js'; +import '../active.js'; +import '../newest.js'; +import '../tag.js'; +import '../read.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/plugins/lobsters/webcmd-plugin.json b/plugins/lobsters/webcmd-plugin.json new file mode 100644 index 00000000..807f9446 --- /dev/null +++ b/plugins/lobsters/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "lobsters", + "version": "0.1.0", + "description": "Webcmd commands for lobsters", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/maven/README.md b/plugins/maven/README.md new file mode 100644 index 00000000..6d5b8880 --- /dev/null +++ b/plugins/maven/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-maven + +Webcmd commands for maven. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/maven +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd maven artifact` | Fetch a Maven Central artifact's version history (groupId:artifactId[:version]) | +| `webcmd maven search` | Search Maven Central by keyword (artifact name, groupId, tag) | diff --git a/clis/maven/artifact.js b/plugins/maven/artifact.js similarity index 100% rename from clis/maven/artifact.js rename to plugins/maven/artifact.js diff --git a/plugins/maven/package.json b/plugins/maven/package.json new file mode 100644 index 00000000..9dbb871b --- /dev/null +++ b/plugins/maven/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-maven", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for maven", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/maven/search.js b/plugins/maven/search.js similarity index 100% rename from clis/maven/search.js rename to plugins/maven/search.js diff --git a/clis/maven/utils.js b/plugins/maven/utils.js similarity index 100% rename from clis/maven/utils.js rename to plugins/maven/utils.js diff --git a/plugins/maven/webcmd-plugin.json b/plugins/maven/webcmd-plugin.json new file mode 100644 index 00000000..cac95fdc --- /dev/null +++ b/plugins/maven/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "maven", + "version": "0.1.0", + "description": "Webcmd commands for maven", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/mdn/README.md b/plugins/mdn/README.md new file mode 100644 index 00000000..18432a38 --- /dev/null +++ b/plugins/mdn/README.md @@ -0,0 +1,15 @@ +# webcmd-plugin-mdn + +Webcmd commands for mdn. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/mdn +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd mdn search` | Search MDN Web Docs by keyword | diff --git a/plugins/mdn/package.json b/plugins/mdn/package.json new file mode 100644 index 00000000..3db96f0f --- /dev/null +++ b/plugins/mdn/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-mdn", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for mdn", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/mdn/search.js b/plugins/mdn/search.js similarity index 100% rename from clis/mdn/search.js rename to plugins/mdn/search.js diff --git a/plugins/mdn/webcmd-plugin.json b/plugins/mdn/webcmd-plugin.json new file mode 100644 index 00000000..dedc3b19 --- /dev/null +++ b/plugins/mdn/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "mdn", + "version": "0.1.0", + "description": "Webcmd commands for mdn", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/npm/README.md b/plugins/npm/README.md new file mode 100644 index 00000000..db79befd --- /dev/null +++ b/plugins/npm/README.md @@ -0,0 +1,17 @@ +# webcmd-plugin-npm + +Webcmd commands for npm. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/npm +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd npm downloads` | Daily download counts for an npm package over a window | +| `webcmd npm package` | Single npm package metadata (latest version, license, homepage, repository). Use `npm downloads` for stats. | +| `webcmd npm search` | Search the public npm registry by keyword | diff --git a/clis/npm/downloads.js b/plugins/npm/downloads.js similarity index 100% rename from clis/npm/downloads.js rename to plugins/npm/downloads.js diff --git a/clis/npm/package.js b/plugins/npm/package.js similarity index 100% rename from clis/npm/package.js rename to plugins/npm/package.js diff --git a/plugins/npm/package.json b/plugins/npm/package.json new file mode 100644 index 00000000..70514fe0 --- /dev/null +++ b/plugins/npm/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-npm", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for npm", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/npm/search.js b/plugins/npm/search.js similarity index 100% rename from clis/npm/search.js rename to plugins/npm/search.js diff --git a/clis/npm/utils.js b/plugins/npm/utils.js similarity index 100% rename from clis/npm/utils.js rename to plugins/npm/utils.js diff --git a/plugins/npm/webcmd-plugin.json b/plugins/npm/webcmd-plugin.json new file mode 100644 index 00000000..770fabb9 --- /dev/null +++ b/plugins/npm/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "npm", + "version": "0.1.0", + "description": "Webcmd commands for npm", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/nuget/README.md b/plugins/nuget/README.md new file mode 100644 index 00000000..b39b2b49 --- /dev/null +++ b/plugins/nuget/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-nuget + +Webcmd commands for nuget. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/nuget +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd nuget package` | Full NuGet package version history (catalogEntry per release) | +| `webcmd nuget search` | Search NuGet packages by keyword | diff --git a/clis/nuget/package.js b/plugins/nuget/package.js similarity index 100% rename from clis/nuget/package.js rename to plugins/nuget/package.js diff --git a/plugins/nuget/package.json b/plugins/nuget/package.json new file mode 100644 index 00000000..c0218e0c --- /dev/null +++ b/plugins/nuget/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-nuget", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for nuget", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/nuget/search.js b/plugins/nuget/search.js similarity index 100% rename from clis/nuget/search.js rename to plugins/nuget/search.js diff --git a/clis/nuget/nuget.test.js b/plugins/nuget/test/nuget.test.js similarity index 99% rename from clis/nuget/nuget.test.js rename to plugins/nuget/test/nuget.test.js index c8cbfd41..f4f6bd63 100644 --- a/clis/nuget/nuget.test.js +++ b/plugins/nuget/test/nuget.test.js @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './search.js'; -import './package.js'; +import '../search.js'; +import '../package.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/nuget/utils.js b/plugins/nuget/utils.js similarity index 100% rename from clis/nuget/utils.js rename to plugins/nuget/utils.js diff --git a/plugins/nuget/webcmd-plugin.json b/plugins/nuget/webcmd-plugin.json new file mode 100644 index 00000000..d41f90ca --- /dev/null +++ b/plugins/nuget/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "nuget", + "version": "0.1.0", + "description": "Webcmd commands for nuget", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/nvd/README.md b/plugins/nvd/README.md new file mode 100644 index 00000000..9055a120 --- /dev/null +++ b/plugins/nvd/README.md @@ -0,0 +1,15 @@ +# webcmd-plugin-nvd + +Webcmd commands for nvd. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/nvd +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd nvd cve` | NIST NVD CVE detail (description, CVSS, CWE, KEV flag) | diff --git a/clis/nvd/cve.js b/plugins/nvd/cve.js similarity index 100% rename from clis/nvd/cve.js rename to plugins/nvd/cve.js diff --git a/plugins/nvd/package.json b/plugins/nvd/package.json new file mode 100644 index 00000000..cb1ac8cc --- /dev/null +++ b/plugins/nvd/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-nvd", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for nvd", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/plugins/nvd/webcmd-plugin.json b/plugins/nvd/webcmd-plugin.json new file mode 100644 index 00000000..9cf8aa11 --- /dev/null +++ b/plugins/nvd/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "nvd", + "version": "0.1.0", + "description": "Webcmd commands for nvd", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/oeis/README.md b/plugins/oeis/README.md new file mode 100644 index 00000000..01fbd8e1 --- /dev/null +++ b/plugins/oeis/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-oeis + +Webcmd commands for oeis. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/oeis +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd oeis search` | Search OEIS sequences by keyword or numeric pattern | +| `webcmd oeis sequence` | Full OEIS sequence detail by A-number (terms, name, keywords, formula counts) | diff --git a/plugins/oeis/package.json b/plugins/oeis/package.json new file mode 100644 index 00000000..e6084f70 --- /dev/null +++ b/plugins/oeis/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-oeis", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for oeis", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/oeis/search.js b/plugins/oeis/search.js similarity index 100% rename from clis/oeis/search.js rename to plugins/oeis/search.js diff --git a/clis/oeis/sequence.js b/plugins/oeis/sequence.js similarity index 100% rename from clis/oeis/sequence.js rename to plugins/oeis/sequence.js diff --git a/clis/oeis/oeis.test.js b/plugins/oeis/test/oeis.test.js similarity index 98% rename from clis/oeis/oeis.test.js rename to plugins/oeis/test/oeis.test.js index 3136741c..d6c63f17 100644 --- a/clis/oeis/oeis.test.js +++ b/plugins/oeis/test/oeis.test.js @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './search.js'; -import './sequence.js'; +import '../search.js'; +import '../sequence.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/oeis/utils.js b/plugins/oeis/utils.js similarity index 100% rename from clis/oeis/utils.js rename to plugins/oeis/utils.js diff --git a/plugins/oeis/webcmd-plugin.json b/plugins/oeis/webcmd-plugin.json new file mode 100644 index 00000000..e836bfff --- /dev/null +++ b/plugins/oeis/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "oeis", + "version": "0.1.0", + "description": "Webcmd commands for oeis", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/openalex/README.md b/plugins/openalex/README.md new file mode 100644 index 00000000..42e9d9ae --- /dev/null +++ b/plugins/openalex/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-openalex + +Webcmd commands for openalex. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/openalex +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd openalex search` | Search OpenAlex Works (papers, books, preprints) by keyword | +| `webcmd openalex work` | Fetch a single OpenAlex Work (paper / preprint / book) — metadata + abstract | diff --git a/plugins/openalex/package.json b/plugins/openalex/package.json new file mode 100644 index 00000000..6afc41df --- /dev/null +++ b/plugins/openalex/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-openalex", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for openalex", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/openalex/search.js b/plugins/openalex/search.js similarity index 100% rename from clis/openalex/search.js rename to plugins/openalex/search.js diff --git a/clis/openalex/utils.js b/plugins/openalex/utils.js similarity index 100% rename from clis/openalex/utils.js rename to plugins/openalex/utils.js diff --git a/plugins/openalex/webcmd-plugin.json b/plugins/openalex/webcmd-plugin.json new file mode 100644 index 00000000..ff953e9e --- /dev/null +++ b/plugins/openalex/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "openalex", + "version": "0.1.0", + "description": "Webcmd commands for openalex", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/clis/openalex/work.js b/plugins/openalex/work.js similarity index 100% rename from clis/openalex/work.js rename to plugins/openalex/work.js diff --git a/plugins/openfda/README.md b/plugins/openfda/README.md new file mode 100644 index 00000000..a2e6d0b8 --- /dev/null +++ b/plugins/openfda/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-openfda + +Webcmd commands for openfda. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/openfda +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd openfda drug-label` | Search FDA-approved drug labels (brand or generic name) | +| `webcmd openfda food-recall` | FDA food recall and enforcement actions (most recent first) | diff --git a/clis/openfda/drug-label.js b/plugins/openfda/drug-label.js similarity index 100% rename from clis/openfda/drug-label.js rename to plugins/openfda/drug-label.js diff --git a/clis/openfda/food-recall.js b/plugins/openfda/food-recall.js similarity index 100% rename from clis/openfda/food-recall.js rename to plugins/openfda/food-recall.js diff --git a/plugins/openfda/package.json b/plugins/openfda/package.json new file mode 100644 index 00000000..373eeb5b --- /dev/null +++ b/plugins/openfda/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-openfda", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for openfda", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/openfda/openfda.test.js b/plugins/openfda/test/openfda.test.js similarity index 98% rename from clis/openfda/openfda.test.js rename to plugins/openfda/test/openfda.test.js index 50a82409..22f512e8 100644 --- a/clis/openfda/openfda.test.js +++ b/plugins/openfda/test/openfda.test.js @@ -1,8 +1,8 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, EmptyResultError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './drug-label.js'; -import './food-recall.js'; +import '../drug-label.js'; +import '../food-recall.js'; const origFetch = global.fetch; afterEach(() => { global.fetch = origFetch; }); diff --git a/clis/openfda/utils.js b/plugins/openfda/utils.js similarity index 100% rename from clis/openfda/utils.js rename to plugins/openfda/utils.js diff --git a/plugins/openfda/webcmd-plugin.json b/plugins/openfda/webcmd-plugin.json new file mode 100644 index 00000000..34261831 --- /dev/null +++ b/plugins/openfda/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "openfda", + "version": "0.1.0", + "description": "Webcmd commands for openfda", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/openreview/README.md b/plugins/openreview/README.md new file mode 100644 index 00000000..b2e048da --- /dev/null +++ b/plugins/openreview/README.md @@ -0,0 +1,19 @@ +# webcmd-plugin-openreview + +Webcmd commands for openreview. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/openreview +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd openreview author` | List OpenReview submissions by an author profile id (newest first) | +| `webcmd openreview paper` | Show full metadata for a single OpenReview paper | +| `webcmd openreview reviews` | Show full review thread (paper + reviews + decisions) for an OpenReview forum | +| `webcmd openreview search` | Search OpenReview papers by free-text query | +| `webcmd openreview venue` | List papers at an OpenReview venue (e.g. "ICLR 2024 oral" or full invitation id) | diff --git a/clis/openreview/author.js b/plugins/openreview/author.js similarity index 100% rename from clis/openreview/author.js rename to plugins/openreview/author.js diff --git a/plugins/openreview/package.json b/plugins/openreview/package.json new file mode 100644 index 00000000..ba5240b1 --- /dev/null +++ b/plugins/openreview/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-openreview", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for openreview", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/openreview/paper.js b/plugins/openreview/paper.js similarity index 100% rename from clis/openreview/paper.js rename to plugins/openreview/paper.js diff --git a/clis/openreview/reviews.js b/plugins/openreview/reviews.js similarity index 100% rename from clis/openreview/reviews.js rename to plugins/openreview/reviews.js diff --git a/clis/openreview/search.js b/plugins/openreview/search.js similarity index 100% rename from clis/openreview/search.js rename to plugins/openreview/search.js diff --git a/clis/openreview/openreview.test.js b/plugins/openreview/test/openreview.test.js similarity index 99% rename from clis/openreview/openreview.test.js rename to plugins/openreview/test/openreview.test.js index 311400e5..14d64ab7 100644 --- a/clis/openreview/openreview.test.js +++ b/plugins/openreview/test/openreview.test.js @@ -9,12 +9,12 @@ import { requireForumId, requireNonNegativeInt, requireProfileId, -} from './utils.js'; -import './search.js'; -import './venue.js'; -import './paper.js'; -import './reviews.js'; -import './author.js'; +} from '../utils.js'; +import '../search.js'; +import '../venue.js'; +import '../paper.js'; +import '../reviews.js'; +import '../author.js'; const SAMPLE_NOTE = { id: 'abc123XYZ_', diff --git a/clis/openreview/utils.js b/plugins/openreview/utils.js similarity index 100% rename from clis/openreview/utils.js rename to plugins/openreview/utils.js diff --git a/clis/openreview/venue.js b/plugins/openreview/venue.js similarity index 100% rename from clis/openreview/venue.js rename to plugins/openreview/venue.js diff --git a/plugins/openreview/webcmd-plugin.json b/plugins/openreview/webcmd-plugin.json new file mode 100644 index 00000000..3268b4dd --- /dev/null +++ b/plugins/openreview/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "openreview", + "version": "0.1.0", + "description": "Webcmd commands for openreview", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/scripts/plugin-local-runtime-loader.mjs b/scripts/plugin-local-runtime-loader.mjs new file mode 100644 index 00000000..b9cb4f62 --- /dev/null +++ b/scripts/plugin-local-runtime-loader.mjs @@ -0,0 +1,25 @@ +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { pathToFileURL } from 'node:url'; + +let packageName; +let packageRoot; +let packageExports; + +export function initialize(data) { + packageRoot = data.packageRoot; + const manifest = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8')); + packageName = manifest.name; + packageExports = manifest.exports; +} + +export function resolve(specifier, context, nextResolve) { + if (specifier === packageName || specifier.startsWith(`${packageName}/`)) { + const key = specifier === packageName ? '.' : `.${specifier.slice(packageName.length)}`; + const target = packageExports[key]; + if (typeof target === 'string' && target.startsWith('./')) { + return { url: pathToFileURL(path.join(packageRoot, target)).href, shortCircuit: true }; + } + } + return nextResolve(specifier, context); +} diff --git a/src/build-plugin-command-manifest.test.ts b/src/build-plugin-command-manifest.test.ts index d335f7b9..aaedaa69 100644 --- a/src/build-plugin-command-manifest.test.ts +++ b/src/build-plugin-command-manifest.test.ts @@ -1,3 +1,4 @@ +import { execFileSync } from 'node:child_process'; import * as fs from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; @@ -97,4 +98,57 @@ describe('plugin command manifest', () => { 'alpha/search executable metadata differs from frozen core manifest', ]); }); + + it('scans plugin packages against the local Webcmd runtime', async () => { + const root = fixture({ + 'alpha/package.json': '{"name":"webcmd-plugin-alpha","type":"module"}', + 'alpha/search.js': ` + import { cli, Strategy } from '@agentrhq/webcmd/registry'; + cli({ + site: 'alpha', + name: 'search', + tags: ['search'], + access: 'read', + description: 'Search alpha', + strategy: Strategy.PUBLIC, + browser: false, + args: [], + columns: ['id'], + func: async () => [], + }); + `, + }); + const stalePackage = path.join(root, 'node_modules', '@agentrhq', 'webcmd'); + fs.mkdirSync(path.join(stalePackage, 'dist'), { recursive: true }); + fs.writeFileSync(path.join(stalePackage, 'package.json'), JSON.stringify({ + name: '@agentrhq/webcmd', + version: '0.0.0-stale', + type: 'module', + exports: { './registry': './dist/registry.js' }, + })); + fs.writeFileSync(path.join(stalePackage, 'dist', 'registry.js'), ` + const registry = globalThis.__webcmd_registry__ ??= new Map(); + export const Strategy = { PUBLIC: 'public' }; + export function cli(options) { + const command = { ...options }; + delete command.tags; + registry.set(command.site + '/' + command.name, command); + } + `); + const moduleHref = pathToFileURL(path.resolve('src/build-plugin-command-manifest.ts')).href; + const stdout = execFileSync(process.execPath, [ + '--import', 'tsx', + '--input-type=module', + '--eval', + ` + import { scanPluginCommandModules } from ${JSON.stringify(moduleHref)}; + const entries = await scanPluginCommandModules(${JSON.stringify(path.join(root, 'plugins'))}); + process.stdout.write(JSON.stringify(entries)); + `, + ], { encoding: 'utf8' }); + const entries = JSON.parse(stdout) as ManifestEntry[]; + + expect(entries).toHaveLength(1); + expect(entries[0]?.tags).toEqual(['search']); + }); }); diff --git a/src/build-plugin-command-manifest.ts b/src/build-plugin-command-manifest.ts index fae0cfbe..8e87d889 100644 --- a/src/build-plugin-command-manifest.ts +++ b/src/build-plugin-command-manifest.ts @@ -1,12 +1,19 @@ #!/usr/bin/env node import * as fs from 'node:fs'; +import { register } from 'node:module'; import * as path from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { fileURLToPath, pathToFileURL } from 'node:url'; import { loadManifestEntries } from './build-manifest.js'; import type { ManifestEntry } from './manifest-types.js'; import { findPackageRoot } from './package-paths.js'; +const localPackageRoot = findPackageRoot(fileURLToPath(import.meta.url)); +register(pathToFileURL(path.join(localPackageRoot, 'scripts/plugin-local-runtime-loader.mjs')), { + parentURL: import.meta.url, + data: { packageRoot: localPackageRoot }, +}); + const EXECUTABLE_FIELDS = [ 'aliases', 'access', 'domain', 'strategy', 'browser', 'args', 'columns', 'tags', 'keywords', 'defaultFormat', 'pipeline', 'navigateBefore', 'siteSession', 'freshPage', @@ -74,9 +81,8 @@ export function serializePluginCommandManifest(entries: readonly ManifestEntry[] } async function main(): Promise { - const packageRoot = findPackageRoot(fileURLToPath(import.meta.url)); - const entries = await scanPluginCommandModules(path.join(packageRoot, 'plugins')); - const output = path.join(packageRoot, 'plugin-command-manifest.json'); + const entries = await scanPluginCommandModules(path.join(localPackageRoot, 'plugins')); + const output = path.join(localPackageRoot, 'plugin-command-manifest.json'); fs.writeFileSync(output, serializePluginCommandManifest(entries)); process.stderr.write(`✅ Plugin command manifest compiled: ${entries.length} entries → ${output}\n`); } diff --git a/webcmd-plugin.json b/webcmd-plugin.json index 072089d2..c741e6e0 100644 --- a/webcmd-plugin.json +++ b/webcmd-plugin.json @@ -244,6 +244,16 @@ "handle": "agentrhq" } }, + "homebrew": { + "path": "plugins/homebrew", + "version": "0.1.0", + "description": "Webcmd commands for homebrew", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "iit": { "path": "plugins/iit", "version": "0.1.0", @@ -264,6 +274,36 @@ "handle": "agentrhq" } }, + "jira": { + "path": "plugins/jira", + "version": "0.1.0", + "description": "Webcmd commands for jira", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "lesswrong": { + "path": "plugins/lesswrong", + "version": "0.1.0", + "description": "Webcmd commands for lesswrong", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "lichess": { + "path": "plugins/lichess", + "version": "0.1.0", + "description": "Webcmd commands for lichess", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "linkedin": { "path": "plugins/linkedin", "version": "0.1.0", @@ -274,6 +314,16 @@ "handle": "agentrhq" } }, + "lobsters": { + "path": "plugins/lobsters", + "version": "0.1.0", + "description": "Webcmd commands for lobsters", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "luma": { "path": "plugins/luma", "version": "0.1.0", @@ -284,6 +334,96 @@ "handle": "agentrhq" } }, + "maven": { + "path": "plugins/maven", + "version": "0.1.0", + "description": "Webcmd commands for maven", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "mdn": { + "path": "plugins/mdn", + "version": "0.1.0", + "description": "Webcmd commands for mdn", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "npm": { + "path": "plugins/npm", + "version": "0.1.0", + "description": "Webcmd commands for npm", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "nuget": { + "path": "plugins/nuget", + "version": "0.1.0", + "description": "Webcmd commands for nuget", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "nvd": { + "path": "plugins/nvd", + "version": "0.1.0", + "description": "Webcmd commands for nvd", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "oeis": { + "path": "plugins/oeis", + "version": "0.1.0", + "description": "Webcmd commands for oeis", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "openalex": { + "path": "plugins/openalex", + "version": "0.1.0", + "description": "Webcmd commands for openalex", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "openfda": { + "path": "plugins/openfda", + "version": "0.1.0", + "description": "Webcmd commands for openfda", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "openreview": { + "path": "plugins/openreview", + "version": "0.1.0", + "description": "Webcmd commands for openreview", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "pypi": { "path": "plugins/pypi", "version": "0.1.0", From ab64ca82785db9d9d4b450c2e9da3c008937ace0 Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 16:48:50 +0530 Subject: [PATCH 11/39] fix: make plugin manifest resolution fail closed --- package-lock.json | 2 +- package.json | 2 +- scripts/plugin-local-runtime-loader.mjs | 1 + src/build-plugin-command-manifest.test.ts | 68 +++++++++++++++++++---- 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index d54cb40f..3fddcdb1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,7 @@ "vitest": "^4.1.0" }, "engines": { - "node": ">=20.0.0" + "node": ">=20.6.0" } }, "node_modules/@asamuzakjp/css-color": { diff --git a/package.json b/package.json index 1c9822d9..b1e10fd4 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.5.3", "description": "Turn websites, browser sessions, desktop apps, and local tools into deterministic CLI surfaces for humans and AI agents.", "engines": { - "node": ">=20.0.0" + "node": ">=20.6.0" }, "type": "module", "main": "dist/src/main.js", diff --git a/scripts/plugin-local-runtime-loader.mjs b/scripts/plugin-local-runtime-loader.mjs index b9cb4f62..554f1745 100644 --- a/scripts/plugin-local-runtime-loader.mjs +++ b/scripts/plugin-local-runtime-loader.mjs @@ -20,6 +20,7 @@ export function resolve(specifier, context, nextResolve) { if (typeof target === 'string' && target.startsWith('./')) { return { url: pathToFileURL(path.join(packageRoot, target)).href, shortCircuit: true }; } + throw new Error(`${packageName} does not export ${key}`); } return nextResolve(specifier, context); } diff --git a/src/build-plugin-command-manifest.test.ts b/src/build-plugin-command-manifest.test.ts index aaedaa69..60545010 100644 --- a/src/build-plugin-command-manifest.test.ts +++ b/src/build-plugin-command-manifest.test.ts @@ -46,6 +46,20 @@ function command(site: string, name: string, overrides: Record }; } +function scanInChild(pluginsDir: string): string { + const moduleHref = pathToFileURL(path.resolve('src/build-plugin-command-manifest.ts')).href; + return execFileSync(process.execPath, [ + '--import', 'tsx', + '--input-type=module', + '--eval', + ` + import { scanPluginCommandModules } from ${JSON.stringify(moduleHref)}; + const entries = await scanPluginCommandModules(${JSON.stringify(pluginsDir)}); + process.stdout.write(JSON.stringify(entries)); + `, + ], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }); +} + describe('plugin command manifest', () => { it('scans flat command modules and emits deterministic plugin source paths', async () => { const root = fixture({ @@ -135,20 +149,52 @@ describe('plugin command manifest', () => { registry.set(command.site + '/' + command.name, command); } `); - const moduleHref = pathToFileURL(path.resolve('src/build-plugin-command-manifest.ts')).href; - const stdout = execFileSync(process.execPath, [ - '--import', 'tsx', - '--input-type=module', - '--eval', - ` - import { scanPluginCommandModules } from ${JSON.stringify(moduleHref)}; - const entries = await scanPluginCommandModules(${JSON.stringify(path.join(root, 'plugins'))}); - process.stdout.write(JSON.stringify(entries)); - `, - ], { encoding: 'utf8' }); + const stdout = scanInChild(path.join(root, 'plugins')); const entries = JSON.parse(stdout) as ManifestEntry[]; expect(entries).toHaveLength(1); expect(entries[0]?.tags).toEqual(['search']); }); + + it('rejects Webcmd subpaths missing from the local package instead of loading a stale install', () => { + const root = fixture({ + 'alpha/package.json': '{"name":"webcmd-plugin-alpha","type":"module"}', + 'alpha/search.js': ` + import { cli, Strategy } from '@agentrhq/webcmd/registry'; + import { staleDescription } from '@agentrhq/webcmd/stale-only'; + cli({ + site: 'alpha', + name: 'search', + access: 'read', + description: staleDescription, + strategy: Strategy.PUBLIC, + browser: false, + args: [], + columns: ['id'], + func: async () => [], + }); + `, + }); + const stalePackage = path.join(root, 'node_modules', '@agentrhq', 'webcmd'); + fs.mkdirSync(path.join(stalePackage, 'dist'), { recursive: true }); + fs.writeFileSync(path.join(stalePackage, 'package.json'), JSON.stringify({ + name: '@agentrhq/webcmd', + version: '0.0.0-stale', + type: 'module', + exports: { './stale-only': './dist/stale-only.js' }, + })); + fs.writeFileSync( + path.join(stalePackage, 'dist', 'stale-only.js'), + "export const staleDescription = 'loaded from stale install';\n", + ); + + expect(() => scanInChild(path.join(root, 'plugins'))) + .toThrow(/@agentrhq\/webcmd does not export \.\/stale-only/); + }); + + it('declares the minimum Node version required by module.register', () => { + const manifest = JSON.parse(fs.readFileSync('package.json', 'utf8')) as { engines?: { node?: string } }; + + expect(manifest.engines?.node).toBe('>=20.6.0'); + }); }); From d36432a086895888e2b78463625de5fc7d4a519f Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 16:52:48 +0530 Subject: [PATCH 12/39] refactor: migrate reference adapters to plugins --- cli-manifest.json | 2517 ++--------------- plugin-command-manifest.json | 2291 ++++++++++++++- plugins/osv/README.md | 16 + plugins/osv/package.json | 9 + {clis => plugins}/osv/query.js | 0 {clis/osv => plugins/osv/test}/osv.test.js | 4 +- {clis => plugins}/osv/utils.js | 0 {clis => plugins}/osv/vulnerability.js | 0 plugins/osv/webcmd-plugin.json | 10 + plugins/packagist/README.md | 16 + {clis => plugins}/packagist/package.js | 0 plugins/packagist/package.json | 9 + {clis => plugins}/packagist/search.js | 0 {clis => plugins}/packagist/utils.js | 0 plugins/packagist/webcmd-plugin.json | 10 + plugins/pubmed/README.md | 23 + {clis => plugins}/pubmed/article.js | 0 {clis => plugins}/pubmed/author.js | 0 {clis => plugins}/pubmed/citations.js | 0 {clis => plugins}/pubmed/clinical-trial.js | 0 {clis => plugins}/pubmed/journal.js | 0 {clis => plugins}/pubmed/mesh.js | 0 plugins/pubmed/package.json | 9 + {clis => plugins}/pubmed/related.js | 0 {clis => plugins}/pubmed/review.js | 0 {clis => plugins}/pubmed/search.js | 0 .../pubmed/test}/pubmed.test.js | 20 +- {clis => plugins}/pubmed/utils.js | 0 plugins/pubmed/webcmd-plugin.json | 10 + plugins/rest-countries/README.md | 16 + {clis => plugins}/rest-countries/country.js | 0 plugins/rest-countries/package.json | 9 + {clis => plugins}/rest-countries/region.js | 0 .../test}/rest-countries.test.js | 4 +- {clis => plugins}/rest-countries/utils.js | 0 plugins/rest-countries/webcmd-plugin.json | 10 + plugins/rfc/README.md | 15 + plugins/rfc/package.json | 9 + {clis => plugins}/rfc/rfc.js | 0 {clis/rfc => plugins/rfc/test}/rfc.test.js | 2 +- {clis => plugins}/rfc/utils.js | 0 plugins/rfc/webcmd-plugin.json | 10 + plugins/rubygems/README.md | 16 + {clis => plugins}/rubygems/gem.js | 0 plugins/rubygems/package.json | 9 + {clis => plugins}/rubygems/search.js | 0 {clis => plugins}/rubygems/utils.js | 0 plugins/rubygems/webcmd-plugin.json | 10 + plugins/semanticscholar/README.md | 18 + .../semanticscholar/citations.js | 0 plugins/semanticscholar/package.json | 9 + {clis => plugins}/semanticscholar/paper.js | 0 .../semanticscholar/recommendations.js | 0 {clis => plugins}/semanticscholar/search.js | 0 .../test}/semanticscholar.test.js | 8 +- {clis => plugins}/semanticscholar/utils.js | 0 plugins/semanticscholar/webcmd-plugin.json | 10 + plugins/stackoverflow/README.md | 22 + {clis => plugins}/stackoverflow/bounties.js | 0 {clis => plugins}/stackoverflow/hot.js | 0 plugins/stackoverflow/package.json | 9 + {clis => plugins}/stackoverflow/read.js | 0 {clis => plugins}/stackoverflow/related.js | 0 {clis => plugins}/stackoverflow/search.js | 0 {clis => plugins}/stackoverflow/tag.js | 0 .../stackoverflow/test}/stackoverflow.test.js | 14 +- {clis => plugins}/stackoverflow/unanswered.js | 0 {clis => plugins}/stackoverflow/user.js | 0 {clis => plugins}/stackoverflow/utils.js | 0 plugins/stackoverflow/webcmd-plugin.json | 10 + plugins/steam/README.md | 17 + {clis => plugins}/steam/app.js | 0 plugins/steam/package.json | 9 + {clis => plugins}/steam/search.js | 0 .../steam/test}/steam.test.js | 6 +- {clis => plugins}/steam/top-sellers.js | 0 {clis => plugins}/steam/utils.js | 0 plugins/steam/webcmd-plugin.json | 10 + plugins/tvmaze/README.md | 16 + plugins/tvmaze/package.json | 9 + {clis => plugins}/tvmaze/search.js | 0 {clis => plugins}/tvmaze/show.js | 0 .../tvmaze/test}/tvmaze.test.js | 4 +- {clis => plugins}/tvmaze/utils.js | 0 plugins/tvmaze/webcmd-plugin.json | 10 + plugins/wikidata/README.md | 16 + {clis => plugins}/wikidata/entity.js | 0 plugins/wikidata/package.json | 9 + {clis => plugins}/wikidata/search.js | 0 .../wikidata/test}/wikidata.test.js | 4 +- {clis => plugins}/wikidata/utils.js | 0 plugins/wikidata/webcmd-plugin.json | 10 + plugins/wikipedia/README.md | 19 + plugins/wikipedia/package.json | 9 + {clis => plugins}/wikipedia/page.js | 0 {clis => plugins}/wikipedia/random.js | 0 {clis => plugins}/wikipedia/search.js | 0 {clis => plugins}/wikipedia/summary.js | 0 .../wikipedia/test}/trending.test.js | 6 +- {clis => plugins}/wikipedia/trending.js | 0 {clis => plugins}/wikipedia/utils.js | 0 plugins/wikipedia/webcmd-plugin.json | 10 + plugins/wttr/README.md | 16 + {clis => plugins}/wttr/current.js | 0 {clis => plugins}/wttr/forecast.js | 0 plugins/wttr/package.json | 9 + {clis/wttr => plugins/wttr/test}/wttr.test.js | 4 +- {clis => plugins}/wttr/utils.js | 0 plugins/wttr/webcmd-plugin.json | 10 + scripts/typed-error-lint-baseline.json | 6 +- webcmd-plugin.json | 130 + 111 files changed, 3048 insertions(+), 2445 deletions(-) create mode 100644 plugins/osv/README.md create mode 100644 plugins/osv/package.json rename {clis => plugins}/osv/query.js (100%) rename {clis/osv => plugins/osv/test}/osv.test.js (98%) rename {clis => plugins}/osv/utils.js (100%) rename {clis => plugins}/osv/vulnerability.js (100%) create mode 100644 plugins/osv/webcmd-plugin.json create mode 100644 plugins/packagist/README.md rename {clis => plugins}/packagist/package.js (100%) create mode 100644 plugins/packagist/package.json rename {clis => plugins}/packagist/search.js (100%) rename {clis => plugins}/packagist/utils.js (100%) create mode 100644 plugins/packagist/webcmd-plugin.json create mode 100644 plugins/pubmed/README.md rename {clis => plugins}/pubmed/article.js (100%) rename {clis => plugins}/pubmed/author.js (100%) rename {clis => plugins}/pubmed/citations.js (100%) rename {clis => plugins}/pubmed/clinical-trial.js (100%) rename {clis => plugins}/pubmed/journal.js (100%) rename {clis => plugins}/pubmed/mesh.js (100%) create mode 100644 plugins/pubmed/package.json rename {clis => plugins}/pubmed/related.js (100%) rename {clis => plugins}/pubmed/review.js (100%) rename {clis => plugins}/pubmed/search.js (100%) rename {clis/pubmed => plugins/pubmed/test}/pubmed.test.js (99%) rename {clis => plugins}/pubmed/utils.js (100%) create mode 100644 plugins/pubmed/webcmd-plugin.json create mode 100644 plugins/rest-countries/README.md rename {clis => plugins}/rest-countries/country.js (100%) create mode 100644 plugins/rest-countries/package.json rename {clis => plugins}/rest-countries/region.js (100%) rename {clis/rest-countries => plugins/rest-countries/test}/rest-countries.test.js (98%) rename {clis => plugins}/rest-countries/utils.js (100%) create mode 100644 plugins/rest-countries/webcmd-plugin.json create mode 100644 plugins/rfc/README.md create mode 100644 plugins/rfc/package.json rename {clis => plugins}/rfc/rfc.js (100%) rename {clis/rfc => plugins/rfc/test}/rfc.test.js (99%) rename {clis => plugins}/rfc/utils.js (100%) create mode 100644 plugins/rfc/webcmd-plugin.json create mode 100644 plugins/rubygems/README.md rename {clis => plugins}/rubygems/gem.js (100%) create mode 100644 plugins/rubygems/package.json rename {clis => plugins}/rubygems/search.js (100%) rename {clis => plugins}/rubygems/utils.js (100%) create mode 100644 plugins/rubygems/webcmd-plugin.json create mode 100644 plugins/semanticscholar/README.md rename {clis => plugins}/semanticscholar/citations.js (100%) create mode 100644 plugins/semanticscholar/package.json rename {clis => plugins}/semanticscholar/paper.js (100%) rename {clis => plugins}/semanticscholar/recommendations.js (100%) rename {clis => plugins}/semanticscholar/search.js (100%) rename {clis/semanticscholar => plugins/semanticscholar/test}/semanticscholar.test.js (99%) rename {clis => plugins}/semanticscholar/utils.js (100%) create mode 100644 plugins/semanticscholar/webcmd-plugin.json create mode 100644 plugins/stackoverflow/README.md rename {clis => plugins}/stackoverflow/bounties.js (100%) rename {clis => plugins}/stackoverflow/hot.js (100%) create mode 100644 plugins/stackoverflow/package.json rename {clis => plugins}/stackoverflow/read.js (100%) rename {clis => plugins}/stackoverflow/related.js (100%) rename {clis => plugins}/stackoverflow/search.js (100%) rename {clis => plugins}/stackoverflow/tag.js (100%) rename {clis/stackoverflow => plugins/stackoverflow/test}/stackoverflow.test.js (99%) rename {clis => plugins}/stackoverflow/unanswered.js (100%) rename {clis => plugins}/stackoverflow/user.js (100%) rename {clis => plugins}/stackoverflow/utils.js (100%) create mode 100644 plugins/stackoverflow/webcmd-plugin.json create mode 100644 plugins/steam/README.md rename {clis => plugins}/steam/app.js (100%) create mode 100644 plugins/steam/package.json rename {clis => plugins}/steam/search.js (100%) rename {clis/steam => plugins/steam/test}/steam.test.js (94%) rename {clis => plugins}/steam/top-sellers.js (100%) rename {clis => plugins}/steam/utils.js (100%) create mode 100644 plugins/steam/webcmd-plugin.json create mode 100644 plugins/tvmaze/README.md create mode 100644 plugins/tvmaze/package.json rename {clis => plugins}/tvmaze/search.js (100%) rename {clis => plugins}/tvmaze/show.js (100%) rename {clis/tvmaze => plugins/tvmaze/test}/tvmaze.test.js (99%) rename {clis => plugins}/tvmaze/utils.js (100%) create mode 100644 plugins/tvmaze/webcmd-plugin.json create mode 100644 plugins/wikidata/README.md rename {clis => plugins}/wikidata/entity.js (100%) create mode 100644 plugins/wikidata/package.json rename {clis => plugins}/wikidata/search.js (100%) rename {clis/wikidata => plugins/wikidata/test}/wikidata.test.js (98%) rename {clis => plugins}/wikidata/utils.js (100%) create mode 100644 plugins/wikidata/webcmd-plugin.json create mode 100644 plugins/wikipedia/README.md create mode 100644 plugins/wikipedia/package.json rename {clis => plugins}/wikipedia/page.js (100%) rename {clis => plugins}/wikipedia/random.js (100%) rename {clis => plugins}/wikipedia/search.js (100%) rename {clis => plugins}/wikipedia/summary.js (100%) rename {clis/wikipedia => plugins/wikipedia/test}/trending.test.js (94%) rename {clis => plugins}/wikipedia/trending.js (100%) rename {clis => plugins}/wikipedia/utils.js (100%) create mode 100644 plugins/wikipedia/webcmd-plugin.json create mode 100644 plugins/wttr/README.md rename {clis => plugins}/wttr/current.js (100%) rename {clis => plugins}/wttr/forecast.js (100%) create mode 100644 plugins/wttr/package.json rename {clis/wttr => plugins/wttr/test}/wttr.test.js (98%) rename {clis => plugins}/wttr/utils.js (100%) create mode 100644 plugins/wttr/webcmd-plugin.json diff --git a/cli-manifest.json b/cli-manifest.json index c59779f4..df0a4b6b 100644 --- a/cli-manifest.json +++ b/cli-manifest.json @@ -11065,168 +11065,6 @@ "sourceFile": "notebooklm/write-note.js", "navigateBefore": false }, - { - "site": "osv", - "name": "query", - "description": "OSV.dev vulnerabilities affecting a package (optionally pinned to a version)", - "access": "read", - "domain": "osv.dev", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "package", - "type": "string", - "required": true, - "positional": true, - "help": "Package name (e.g. \"lodash\", \"django\")" - }, - { - "name": "ecosystem", - "type": "string", - "required": true, - "help": "OSV ecosystem (npm / PyPI / Go / Maven / NuGet / RubyGems / crates.io / Packagist / ...)" - }, - { - "name": "version", - "type": "string", - "required": false, - "help": "Pin to a specific version (e.g. \"4.17.20\"); omit for all known vulns" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max rows to return (1-200)" - } - ], - "columns": [ - "rank", - "id", - "summary", - "severity", - "aliases", - "published", - "modified", - "affectedPackages", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "osv/query.js", - "sourceFile": "osv/query.js" - }, - { - "site": "osv", - "name": "vulnerability", - "description": "Single OSV.dev vulnerability detail (severity, affected packages, CVE/GHSA aliases)", - "access": "read", - "domain": "osv.dev", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "OSV vulnerability id (e.g. \"GHSA-29mw-wpgm-hmr9\", \"CVE-2020-28500\")" - } - ], - "columns": [ - "id", - "summary", - "severity", - "aliases", - "published", - "modified", - "affectedPackages", - "cwes", - "referenceCount", - "url" - ], - "type": "js", - "modulePath": "osv/vulnerability.js", - "sourceFile": "osv/vulnerability.js" - }, - { - "site": "packagist", - "name": "package", - "description": "Fetch a Packagist package's metadata (version, downloads, license, repo, GitHub stars)", - "access": "read", - "domain": "packagist.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Composer package \"/\" (e.g. \"symfony/console\", \"monolog/monolog\")" - } - ], - "columns": [ - "package", - "version", - "releasedAt", - "license", - "description", - "repository", - "githubStars", - "favers", - "downloads", - "monthlyDownloads", - "dailyDownloads", - "url" - ], - "type": "js", - "modulePath": "packagist/package.js", - "sourceFile": "packagist/package.js" - }, - { - "site": "packagist", - "name": "search", - "description": "Search Packagist (PHP / Composer) packages by keyword", - "access": "read", - "domain": "packagist.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"symfony\", \"laravel http\")" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max packages (1-100, single Packagist page)" - } - ], - "columns": [ - "rank", - "package", - "description", - "downloads", - "favers", - "repository", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "packagist/search.js", - "sourceFile": "packagist/search.js" - }, { "site": "paperreview", "name": "feedback", @@ -12260,62 +12098,46 @@ "sourceFile": "producthunt/today.js" }, { - "site": "pubmed", - "name": "article", - "aliases": [ - "paper", - "read" - ], - "description": "Get detailed information for a PubMed article by PMID", + "site": "pypi", + "name": "downloads", + "description": "PyPI download stats for a package (recent totals or full daily history)", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", + "domain": "pypistats.org", "strategy": "public", "browser": false, "args": [ { - "name": "pmid", + "name": "name", "type": "str", "required": true, "positional": true, - "help": "PubMed ID, e.g. 37780221" + "help": "PyPI package name (e.g. \"requests\", \"pandas\")" }, { - "name": "full-abstract", - "type": "boolean", - "default": false, + "name": "period", + "type": "str", + "default": "recent", "required": false, - "help": "Do not truncate the abstract in table output" + "help": "recent (default — 1 row, last day/week/month) or overall (1 row per day)" } ], "columns": [ - "pmid", - "title", - "authors", - "journal", - "year", + "rank", + "package", + "period", "date", - "article_type", - "language", - "doi", - "pmc", - "affiliations", - "grants", - "mesh_terms", - "keywords", - "abstract", - "url" + "downloads" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "pubmed/article.js", - "sourceFile": "pubmed/article.js" + "modulePath": "pypi/downloads.js", + "sourceFile": "pypi/downloads.js" }, { - "site": "pubmed", - "name": "author", - "description": "Search PubMed articles by author name and optional affiliation", + "site": "pypi", + "name": "package", + "description": "Single PyPI package metadata (latest version, license, homepage, classifiers)", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", + "domain": "pypi.org", "strategy": "public", "browser": false, "args": [ @@ -12324,634 +12146,52 @@ "type": "str", "required": true, "positional": true, - "help": "Author name, e.g. \"Smith J\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "affiliation", - "type": "str", - "required": false, - "help": "Filter by author affiliation" - }, - { - "name": "position", - "type": "str", - "default": "any", - "required": false, - "help": "Author position: any, first, or last", - "choices": [ - "any", - "first", - "last" - ] - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, - { - "name": "sort", - "type": "str", - "default": "date", - "required": false, - "help": "Sort by date or relevance", - "choices": [ - "date", - "relevance" - ] + "help": "PyPI package name (e.g. \"requests\", \"pandas\")" } ], "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", + "name", + "latestVersion", + "summary", + "author", + "license", + "homepage", + "repository", + "requiresPython", + "keywords", + "releases", + "firstReleased", + "lastReleased", "url" ], "type": "js", - "modulePath": "pubmed/author.js", - "sourceFile": "pubmed/author.js" + "modulePath": "pypi/package.js", + "sourceFile": "pypi/package.js" }, { - "site": "pubmed", - "name": "citations", - "description": "Get PubMed citation relationships for an article", + "site": "qoder", + "name": "account", + "description": "Click the account button (username) in the Qoder sidebar and return the visible account dropdown items.", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "pmid", - "type": "str", - "required": true, - "positional": true, - "help": "PubMed ID, e.g. 37780221" - }, - { - "name": "direction", + "name": "username", "type": "str", - "default": "citedby", - "required": false, - "help": "citedby or references", - "choices": [ - "citedby", - "references" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, "required": false, - "help": "Max results (1-100)" + "help": "Username text shown in the sidebar (default: tries common short labels)" } ], "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" + "Field", + "Value" ], "type": "js", - "modulePath": "pubmed/citations.js", - "sourceFile": "pubmed/citations.js" - }, - { - "site": "pubmed", - "name": "clinical-trial", - "description": "Search PubMed clinical trials with a trial-study preset", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Clinical topic query, e.g. \"breast cancer\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, - { - "name": "free-full-text", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include free full text articles" - }, - { - "name": "sort", - "type": "str", - "default": "date", - "required": false, - "help": "Sort by date or relevance", - "choices": [ - "date", - "relevance" - ] - } - ], - "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" - ], - "type": "js", - "modulePath": "pubmed/clinical-trial.js", - "sourceFile": "pubmed/clinical-trial.js" - }, - { - "site": "pubmed", - "name": "journal", - "description": "Search PubMed articles by journal name", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "journal", - "type": "str", - "required": true, - "positional": true, - "help": "Journal name, e.g. \"Nature\" or \"The Lancet\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, - { - "name": "sort", - "type": "str", - "default": "relevance", - "required": false, - "help": "Sort by relevance or date", - "choices": [ - "relevance", - "date" - ] - } - ], - "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" - ], - "type": "js", - "modulePath": "pubmed/journal.js", - "sourceFile": "pubmed/journal.js" - }, - { - "site": "pubmed", - "name": "mesh", - "description": "Search PubMed articles by MeSH term", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "term", - "type": "str", - "required": true, - "positional": true, - "help": "MeSH term, e.g. \"Neoplasms\" or \"Machine Learning\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "major", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include articles where this is a major MeSH topic" - }, - { - "name": "sort", - "type": "str", - "default": "relevance", - "required": false, - "help": "Sort by relevance or date", - "choices": [ - "relevance", - "date" - ] - } - ], - "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" - ], - "type": "js", - "modulePath": "pubmed/mesh.js", - "sourceFile": "pubmed/mesh.js" - }, - { - "site": "pubmed", - "name": "related", - "description": "Find articles related to a PubMed article", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "pmid", - "type": "str", - "required": true, - "positional": true, - "help": "PubMed ID, e.g. 37780221" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "score", - "type": "boolean", - "default": false, - "required": false, - "help": "Show similarity scores when available" - } - ], - "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "score", - "doi", - "url" - ], - "type": "js", - "modulePath": "pubmed/related.js", - "sourceFile": "pubmed/related.js" - }, - { - "site": "pubmed", - "name": "review", - "description": "Search PubMed review articles with a review preset", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Review topic query, e.g. \"immunotherapy\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, - { - "name": "has-abstract", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include articles with abstracts" - }, - { - "name": "sort", - "type": "str", - "default": "date", - "required": false, - "help": "Sort by date or relevance", - "choices": [ - "date", - "relevance" - ] - } - ], - "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" - ], - "type": "js", - "modulePath": "pubmed/review.js", - "sourceFile": "pubmed/review.js" - }, - { - "site": "pubmed", - "name": "search", - "description": "Search PubMed articles with advanced filters", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query, e.g. \"machine learning cancer\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "author", - "type": "str", - "required": false, - "help": "Filter by author name" - }, - { - "name": "journal", - "type": "str", - "required": false, - "help": "Filter by journal name" - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, - { - "name": "article-type", - "type": "str", - "required": false, - "help": "Filter by publication type, e.g. Review or Clinical Trial" - }, - { - "name": "has-abstract", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include articles with abstracts" - }, - { - "name": "free-full-text", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include free full text articles" - }, - { - "name": "humans-only", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include human studies" - }, - { - "name": "english-only", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include English articles" - }, - { - "name": "sort", - "type": "str", - "default": "relevance", - "required": false, - "help": "Sort by relevance, date, author, or journal", - "choices": [ - "relevance", - "date", - "author", - "journal" - ] - } - ], - "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "pubmed/search.js", - "sourceFile": "pubmed/search.js" - }, - { - "site": "pypi", - "name": "downloads", - "description": "PyPI download stats for a package (recent totals or full daily history)", - "access": "read", - "domain": "pypistats.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "PyPI package name (e.g. \"requests\", \"pandas\")" - }, - { - "name": "period", - "type": "str", - "default": "recent", - "required": false, - "help": "recent (default — 1 row, last day/week/month) or overall (1 row per day)" - } - ], - "columns": [ - "rank", - "package", - "period", - "date", - "downloads" - ], - "type": "js", - "modulePath": "pypi/downloads.js", - "sourceFile": "pypi/downloads.js" - }, - { - "site": "pypi", - "name": "package", - "description": "Single PyPI package metadata (latest version, license, homepage, classifiers)", - "access": "read", - "domain": "pypi.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "PyPI package name (e.g. \"requests\", \"pandas\")" - } - ], - "columns": [ - "name", - "latestVersion", - "summary", - "author", - "license", - "homepage", - "repository", - "requiresPython", - "keywords", - "releases", - "firstReleased", - "lastReleased", - "url" - ], - "type": "js", - "modulePath": "pypi/package.js", - "sourceFile": "pypi/package.js" - }, - { - "site": "qoder", - "name": "account", - "description": "Click the account button (username) in the Qoder sidebar and return the visible account dropdown items.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": false, - "help": "Username text shown in the sidebar (default: tries common short labels)" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { "site": "qoder", @@ -14103,108 +13343,6 @@ "sourceFile": "reddit/whoami.js", "navigateBefore": "https://reddit.com" }, - { - "site": "rest-countries", - "name": "country", - "description": "Look up countries by name (common / official, substring match)", - "access": "read", - "domain": "restcountries.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Country name (e.g. \"japan\", \"united kingdom\")" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max rows (1-250)" - } - ], - "columns": [ - "rank", - "commonName", - "officialName", - "cca2", - "cca3", - "ccn3", - "capital", - "region", - "subregion", - "population", - "area", - "languages", - "currencies", - "latitude", - "longitude", - "timezones", - "independent", - "unMember", - "landlocked", - "flag", - "url" - ], - "type": "js", - "modulePath": "rest-countries/country.js", - "sourceFile": "rest-countries/country.js" - }, - { - "site": "rest-countries", - "name": "region", - "description": "List countries in a region (africa / americas / asia / europe / oceania / antarctic)", - "access": "read", - "domain": "restcountries.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "region", - "type": "str", - "required": true, - "positional": true, - "help": "Region name (case-insensitive)" - }, - { - "name": "limit", - "type": "int", - "default": 250, - "required": false, - "help": "Max rows (1-250)" - } - ], - "columns": [ - "rank", - "commonName", - "officialName", - "cca2", - "cca3", - "ccn3", - "capital", - "region", - "subregion", - "population", - "area", - "languages", - "currencies", - "latitude", - "longitude", - "timezones", - "independent", - "unMember", - "landlocked", - "flag", - "url" - ], - "type": "js", - "modulePath": "rest-countries/region.js", - "sourceFile": "rest-countries/region.js" - }, { "site": "reuters", "name": "article-detail", @@ -14310,287 +13448,19 @@ "access": "read", "domain": "reuters.com", "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "subscribed" - ], - "type": "js", - "modulePath": "reuters/auth.js", - "sourceFile": "reuters/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "rfc", - "name": "rfc", - "description": "Single IETF RFC metadata (title, abstract, working group, authors, std level)", - "access": "read", - "domain": "datatracker.ietf.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "number", - "type": "int", - "required": true, - "positional": true, - "help": "RFC number (e.g. 9000, 791, 2616)" - } - ], - "columns": [ - "rfc", - "title", - "state", - "stdLevel", - "group", - "groupType", - "pages", - "published", - "authors", - "abstract", - "rfcEditorUrl", - "url" - ], - "type": "js", - "modulePath": "rfc/rfc.js", - "sourceFile": "rfc/rfc.js" - }, - { - "site": "rubygems", - "name": "gem", - "description": "Fetch a RubyGems.org gem's metadata (version, downloads, license, links)", - "access": "read", - "domain": "rubygems.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Gem name (e.g. \"rails\", \"sidekiq\")" - } - ], - "columns": [ - "gem", - "version", - "releasedAt", - "downloads", - "versionDownloads", - "license", - "authors", - "homepage", - "source", - "bugs", - "info", - "url" - ], - "type": "js", - "modulePath": "rubygems/gem.js", - "sourceFile": "rubygems/gem.js" - }, - { - "site": "rubygems", - "name": "search", - "description": "Search RubyGems.org gems by keyword", - "access": "read", - "domain": "rubygems.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"rails\", \"redis\")" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max gems (1-100, single RubyGems page)" - } - ], - "columns": [ - "rank", - "gem", - "version", - "downloads", - "license", - "authors", - "info", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "rubygems/search.js", - "sourceFile": "rubygems/search.js" - }, - { - "site": "semanticscholar", - "name": "citations", - "description": "List papers that cite a Semantic Scholar paper (paginated)", - "access": "read", - "domain": "api.semanticscholar.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max citing papers (1-1000, single Semantic Scholar page)" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Page offset (0-based)" - } - ], - "columns": [ - "rank", - "paperId", - "doi", - "title", - "year", - "firstAuthor", - "citationCount", - "url" - ], - "type": "js", - "modulePath": "semanticscholar/citations.js", - "sourceFile": "semanticscholar/citations.js" - }, - { - "site": "semanticscholar", - "name": "paper", - "description": "Semantic Scholar paper detail (citation graph + AI tldr) by paperId, DOI, or arXiv id", - "access": "read", - "domain": "api.semanticscholar.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id (e.g. \"ARXIV:1706.03762\", \"PMID:12345\")" - } - ], - "columns": [ - "paperId", - "doi", - "title", - "year", - "firstAuthor", - "citationCount", - "influentialCitationCount", - "referenceCount", - "tldr", - "url" - ], - "type": "js", - "modulePath": "semanticscholar/paper.js", - "sourceFile": "semanticscholar/paper.js" - }, - { - "site": "semanticscholar", - "name": "recommendations", - "description": "Semantic Scholar AI-curated related papers for a paperId, DOI, or arXiv id", - "access": "read", - "domain": "api.semanticscholar.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max recommendations (1-500)" - } - ], - "columns": [ - "rank", - "paperId", - "doi", - "title", - "year", - "firstAuthor", - "citationCount", - "url" - ], - "type": "js", - "modulePath": "semanticscholar/recommendations.js", - "sourceFile": "semanticscholar/recommendations.js" - }, - { - "site": "semanticscholar", - "name": "search", - "description": "Search Semantic Scholar papers by free text", - "access": "read", - "domain": "api.semanticscholar.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search text (e.g. \"attention is all you need\", \"diffusion model\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max papers (1-100, single Semantic Scholar page)" - } - ], - "columns": [ - "rank", - "paperId", - "doi", - "title", - "year", - "firstAuthor", - "citationCount", - "url" - ], - "tags": [ - "search" + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "subscribed" ], "type": "js", - "modulePath": "semanticscholar/search.js", - "sourceFile": "semanticscholar/search.js" + "modulePath": "reuters/auth.js", + "sourceFile": "reuters/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "slock", @@ -16177,766 +15047,311 @@ "help": "Thread channel UUID (from thread-list / message-read)" }, { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "threadChannelId", - "result" - ], - "type": "js", - "modulePath": "slock/thread-unfollow.js", - "sourceFile": "slock/thread-unfollow.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "unread-summary", - "description": "Global unread counts across every server you belong to.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "serverId", - "slug", - "name", - "unreadCount" - ], - "type": "js", - "modulePath": "slock/unread-summary.js", - "sourceFile": "slock/unread-summary.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "whoami", - "description": "Show the current logged-in slock account", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "id", - "name", - "email" - ], - "type": "js", - "modulePath": "slock/whoami.js", - "sourceFile": "slock/whoami.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "spotify", - "name": "auth", - "description": "Authenticate with Spotify (OAuth — run once)", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "next", - "description": "Skip to next track", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "pause", - "description": "Pause playback", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "play", - "description": "Resume playback or search and play a track/artist", - "access": "write", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "default": "", - "required": false, - "positional": true, - "help": "Track or artist to play (optional)" - } - ], - "columns": [ - "track", - "artist", - "status" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "prev", - "description": "Skip to previous track", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "queue", - "description": "Add a track to the playback queue", - "access": "write", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Track to add to queue" - } - ], - "columns": [ - "track", - "artist", - "status" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "repeat", - "description": "Set repeat mode (off / track / context)", - "access": "write", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "mode", - "type": "str", - "default": "context", - "required": false, - "positional": true, - "help": "off / track / context", - "choices": [ - "off", - "track", - "context" - ] - } - ], - "columns": [ - "repeat" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "search", - "description": "Search for tracks", - "access": "read", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results (default: 10)" - } - ], - "columns": [ - "track", - "artist", - "album", - "uri" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "shuffle", - "description": "Toggle shuffle on/off", - "access": "write", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "state", + "name": "server", "type": "str", - "default": "on", "required": false, - "positional": true, - "help": "on or off", - "choices": [ - "on", - "off" - ] + "help": "Override active server" } ], "columns": [ - "shuffle" + "threadChannelId", + "result" ], "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" + "modulePath": "slock/thread-unfollow.js", + "sourceFile": "slock/thread-unfollow.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "spotify", - "name": "status", - "description": "Show current playback status", + "site": "slock", + "name": "unread-summary", + "description": "Global unread counts across every server you belong to.", "access": "read", - "strategy": "local", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [], "columns": [ - "track", - "artist", - "album", - "status", - "progress" + "serverId", + "slug", + "name", + "unreadCount" ], "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" + "modulePath": "slock/unread-summary.js", + "sourceFile": "slock/unread-summary.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "whoami", + "description": "Show the current logged-in slock account", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "id", + "name", + "email" + ], + "type": "js", + "modulePath": "slock/whoami.js", + "sourceFile": "slock/whoami.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "spotify", - "name": "volume", - "description": "Set playback volume (0-100)", + "name": "auth", + "description": "Authenticate with Spotify (OAuth — run once)", "access": "write", "strategy": "local", "browser": false, - "args": [ - { - "name": "level", - "type": "int", - "default": 50, - "required": true, - "positional": true, - "help": "Volume 0–100" - } - ], + "args": [], "columns": [ - "volume" + "status" ], "type": "js", "modulePath": "spotify/spotify.js", "sourceFile": "spotify/spotify.js" }, { - "site": "stackoverflow", - "name": "bounties", - "description": "Active bounties on Stack Overflow", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", + "site": "spotify", + "name": "next", + "description": "Skip to next track", + "access": "write", + "strategy": "local", "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max number of results" - } - ], + "args": [], "columns": [ - "rank", - "id", - "bounty", - "title", - "score", - "answers", - "views", - "is_answered", - "tags", - "author", - "creation_date", - "url" + "status" ], "type": "js", - "modulePath": "stackoverflow/bounties.js", - "sourceFile": "stackoverflow/bounties.js" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "stackoverflow", - "name": "hot", - "description": "Hot Stack Overflow questions", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", + "site": "spotify", + "name": "pause", + "description": "Pause playback", + "access": "write", + "strategy": "local", "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max number of results" - } - ], + "args": [], "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "is_answered", - "tags", - "author", - "creation_date", - "url" + "status" ], "type": "js", - "modulePath": "stackoverflow/hot.js", - "sourceFile": "stackoverflow/hot.js" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "stackoverflow", - "name": "read", - "description": "Read a Stack Overflow question with answers and comments", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", + "site": "spotify", + "name": "play", + "description": "Resume playback or search and play a track/artist", + "access": "write", + "strategy": "local", "browser": false, "args": [ { - "name": "id", + "name": "query", "type": "str", - "required": true, - "positional": true, - "help": "Stack Overflow question id (numeric, e.g. 79935770)" - }, - { - "name": "answers-limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max answers to include (1-100; accepted answer always included first)" - }, - { - "name": "comments-limit", - "type": "int", - "default": 5, - "required": false, - "help": "Max comments per question/answer (1-100)" - }, - { - "name": "max-length", - "type": "int", - "default": 4000, + "default": "", "required": false, - "help": "Max characters per body / answer / comment (min 100)" - } - ], - "columns": [ - "type", - "author", - "score", - "accepted", - "text" - ], - "type": "js", - "modulePath": "stackoverflow/read.js", - "sourceFile": "stackoverflow/read.js" - }, - { - "site": "stackoverflow", - "name": "related", - "description": "List Stack Overflow questions related to a given question id.", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "string", - "required": true, "positional": true, - "help": "Stack Overflow question id (numeric, e.g. 79935770)." - }, - { - "name": "sort", - "type": "string", - "default": "rank", - "required": false, - "help": "Sort key: rank, activity, votes, creation (rank = SO relevance default)." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max related questions (1-100)." + "help": "Track or artist to play (optional)" } ], "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "isAnswered", - "tags", - "author", - "createdAt", - "lastActivityAt", - "url" + "track", + "artist", + "status" ], "type": "js", - "modulePath": "stackoverflow/related.js", - "sourceFile": "stackoverflow/related.js" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "stackoverflow", - "name": "search", - "description": "Search Stack Overflow questions", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", + "site": "spotify", + "name": "prev", + "description": "Skip to previous track", + "access": "write", + "strategy": "local", "browser": false, - "args": [ - { - "name": "query", - "type": "string", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max number of results" - } - ], + "args": [], "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "is_answered", - "tags", - "author", - "creation_date", - "url" - ], - "tags": [ - "search" + "status" ], "type": "js", - "modulePath": "stackoverflow/search.js", - "sourceFile": "stackoverflow/search.js" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "stackoverflow", - "name": "tag", - "description": "List Stack Overflow questions tagged with a given tag (most active first).", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", + "site": "spotify", + "name": "queue", + "description": "Add a track to the playback queue", + "access": "write", + "strategy": "local", "browser": false, "args": [ { - "name": "tag", - "type": "string", + "name": "query", + "type": "str", "required": true, "positional": true, - "help": "Tag slug (e.g. python, rust, typescript)." - }, - { - "name": "sort", - "type": "string", - "default": "activity", - "required": false, - "help": "Sort key: activity, votes, creation, hot, week, month" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max questions to return (max 100)." + "help": "Track to add to queue" } ], "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "isAnswered", - "tags", - "author", - "createdAt", - "lastActivityAt", - "url" + "track", + "artist", + "status" ], "type": "js", - "modulePath": "stackoverflow/tag.js", - "sourceFile": "stackoverflow/tag.js" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "stackoverflow", - "name": "unanswered", - "description": "Top voted unanswered questions on Stack Overflow", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", + "site": "spotify", + "name": "repeat", + "description": "Set repeat mode (off / track / context)", + "access": "write", + "strategy": "local", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 10, + "name": "mode", + "type": "str", + "default": "context", "required": false, - "help": "Max number of results" + "positional": true, + "help": "off / track / context", + "choices": [ + "off", + "track", + "context" + ] } ], "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "tags", - "author", - "creation_date", - "url" + "repeat" ], "type": "js", - "modulePath": "stackoverflow/unanswered.js", - "sourceFile": "stackoverflow/unanswered.js" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "stackoverflow", - "name": "user", - "description": "Find Stack Overflow users by display name (highest reputation first).", + "site": "spotify", + "name": "search", + "description": "Search for tracks", "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", + "strategy": "local", "browser": false, "args": [ { - "name": "name", - "type": "string", + "name": "query", + "type": "str", "required": true, "positional": true, - "help": "Display name (or substring) to search." + "help": "Search query" }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Max users to return (max 100)." + "help": "Number of results (default: 10)" } ], "columns": [ - "userId", - "displayName", - "reputation", - "goldBadges", - "silverBadges", - "bronzeBadges", - "location", - "createdAt", - "lastAccessAt", - "url" + "track", + "artist", + "album", + "uri" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "stackoverflow/user.js", - "sourceFile": "stackoverflow/user.js" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "steam", - "name": "app", - "description": "Steam storefront detail for a single app id", - "access": "read", - "domain": "store.steampowered.com", - "strategy": "public", + "site": "spotify", + "name": "shuffle", + "description": "Toggle shuffle on/off", + "access": "write", + "strategy": "local", "browser": false, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Numeric Steam app id (e.g. \"620\" for Portal 2)" - }, - { - "name": "currency", + "name": "state", "type": "str", - "default": "us", + "default": "on", "required": false, - "help": "Storefront country code (e.g. us / cn / jp / de)" + "positional": true, + "help": "on or off", + "choices": [ + "on", + "off" + ] } ], "columns": [ - "id", - "name", - "type", - "isFree", - "releaseDate", - "developers", - "publishers", - "price", - "currency", - "metacritic", - "recommendations", - "genres", - "categories", - "shortDescription", - "website", - "url" + "shuffle" ], "type": "js", - "modulePath": "steam/app.js", - "sourceFile": "steam/app.js" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "steam", - "name": "search", - "description": "Search the Steam storefront by name keyword", + "site": "spotify", + "name": "status", + "description": "Show current playback status", "access": "read", - "domain": "store.steampowered.com", - "strategy": "public", + "strategy": "local", "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"portal\", \"stardew\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-50)" - }, - { - "name": "currency", - "type": "str", - "default": "us", - "required": false, - "help": "Storefront country code (e.g. us / cn / jp / de)" - } - ], + "args": [], "columns": [ - "rank", - "id", - "name", - "price", - "currency", - "metascore", - "platforms", - "url" - ], - "tags": [ - "search" + "track", + "artist", + "album", + "status", + "progress" ], "type": "js", - "modulePath": "steam/search.js", - "sourceFile": "steam/search.js" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "steam", - "name": "top-sellers", - "description": "Steam top selling games", - "access": "read", - "domain": "store.steampowered.com", - "strategy": "public", + "site": "spotify", + "name": "volume", + "description": "Set playback volume (0-100)", + "access": "write", + "strategy": "local", "browser": false, "args": [ { - "name": "limit", + "name": "level", "type": "int", - "default": 10, - "required": false, - "help": "Number of games" + "default": 50, + "required": true, + "positional": true, + "help": "Volume 0–100" } ], "columns": [ - "rank", - "name", - "price", - "discount", - "url" + "volume" ], "type": "js", - "modulePath": "steam/top-sellers.js", - "sourceFile": "steam/top-sellers.js" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { "site": "substack", @@ -19218,108 +17633,19 @@ "help": "Number of vehicles (1-50)" } ], - "columns": [ - "rank", - "type", - "passengers", - "luggage", - "price", - "currency", - "url" - ], - "type": "js", - "modulePath": "trip/transfer.js", - "sourceFile": "trip/transfer.js", - "navigateBefore": false - }, - { - "site": "tvmaze", - "name": "search", - "description": "TVmaze TV show search by title (returns id, name, network, premiered/ended, rating)", - "access": "read", - "domain": "tvmaze.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "string", - "required": true, - "positional": true, - "help": "TV show title or fragment to search for" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max rows to return (1-50)" - } - ], - "columns": [ - "rank", - "id", - "name", - "type", - "language", - "genres", - "status", - "premiered", - "ended", - "network", - "rating", - "matchScore", - "summary", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "tvmaze/search.js", - "sourceFile": "tvmaze/search.js" - }, - { - "site": "tvmaze", - "name": "show", - "description": "Single TVmaze TV show detail by id (network, schedule, rating, IMDB/TheTVDB cross-refs)", - "access": "read", - "domain": "tvmaze.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "int", - "required": true, - "positional": true, - "help": "TVmaze show id (positive integer)" - } - ], - "columns": [ - "id", - "name", - "type", - "language", - "genres", - "status", - "premiered", - "ended", - "runtime", - "averageRuntime", - "network", - "country", - "schedule", - "rating", - "imdb", - "thetvdb", - "officialSite", - "summary", + "columns": [ + "rank", + "type", + "passengers", + "luggage", + "price", + "currency", "url" ], "type": "js", - "modulePath": "tvmaze/show.js", - "sourceFile": "tvmaze/show.js" + "modulePath": "trip/transfer.js", + "sourceFile": "trip/transfer.js", + "navigateBefore": false }, { "site": "twitter", @@ -21412,357 +19738,6 @@ "sourceFile": "web/fetch-browser.js", "navigateBefore": false }, - { - "site": "wikidata", - "name": "entity", - "description": "Fetch a Wikidata entity by Q/P/L id (label, description, aliases, claim summary)", - "access": "read", - "domain": "www.wikidata.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Entity id (e.g. Q937 = Albert Einstein, P31 = instance of)" - }, - { - "name": "language", - "type": "str", - "default": "en", - "required": false, - "help": "Display language (ISO 639, falls back to English when missing)" - } - ], - "columns": [ - "qid", - "type", - "label", - "description", - "aliases", - "claimPropertyCount", - "sitelinkCount", - "enwikiTitle", - "modified", - "url" - ], - "type": "js", - "modulePath": "wikidata/entity.js", - "sourceFile": "wikidata/entity.js" - }, - { - "site": "wikidata", - "name": "search", - "description": "Search Wikidata items by keyword (returns Q-IDs)", - "access": "read", - "domain": "www.wikidata.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (label / alias)" - }, - { - "name": "language", - "type": "str", - "default": "en", - "required": false, - "help": "Search & display language (ISO 639, e.g. en, fr, zh)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max items (1-50)" - } - ], - "columns": [ - "rank", - "qid", - "label", - "description", - "matchType", - "matchText", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "wikidata/search.js", - "sourceFile": "wikidata/search.js" - }, - { - "site": "wikipedia", - "name": "page", - "description": "Full plain-text extract of a Wikipedia article (optional paragraph cap).", - "access": "read", - "domain": "wikipedia.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "title", - "type": "string", - "required": true, - "positional": true, - "help": "Article title (e.g. \"Transformer (machine learning model)\")" - }, - { - "name": "lang", - "type": "string", - "default": "en", - "required": false, - "help": "Language code (en, zh, ja, de, ...)." - }, - { - "name": "paragraphs", - "type": "int", - "default": 0, - "required": false, - "help": "Cap to first N paragraphs (0 = full article)." - } - ], - "columns": [ - "title", - "description", - "pageId", - "paragraphs", - "extract", - "url" - ], - "type": "js", - "modulePath": "wikipedia/page.js", - "sourceFile": "wikipedia/page.js" - }, - { - "site": "wikipedia", - "name": "random", - "description": "Get a random Wikipedia article", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language code (e.g. en, zh, ja)" - } - ], - "columns": [ - "title", - "description", - "extract", - "url" - ], - "type": "js", - "modulePath": "wikipedia/random.js", - "sourceFile": "wikipedia/random.js" - }, - { - "site": "wikipedia", - "name": "search", - "description": "Search Wikipedia articles", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max results" - }, - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language code (e.g. en, zh, ja)" - } - ], - "columns": [ - "title", - "snippet", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "wikipedia/search.js", - "sourceFile": "wikipedia/search.js" - }, - { - "site": "wikipedia", - "name": "summary", - "description": "Get Wikipedia article summary", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "title", - "type": "str", - "required": true, - "positional": true, - "help": "Article title (e.g. \"Transformer (machine learning model)\")" - }, - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language code (e.g. en, zh, ja)" - } - ], - "columns": [ - "title", - "description", - "extract", - "url" - ], - "type": "js", - "modulePath": "wikipedia/summary.js", - "sourceFile": "wikipedia/summary.js" - }, - { - "site": "wikipedia", - "name": "trending", - "description": "Most-read Wikipedia articles (yesterday)", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max results" - }, - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language code (e.g. en, zh, ja)" - } - ], - "columns": [ - "rank", - "title", - "description", - "views" - ], - "type": "js", - "modulePath": "wikipedia/trending.js", - "sourceFile": "wikipedia/trending.js" - }, - { - "site": "wttr", - "name": "current", - "description": "Current weather conditions for a location (city, lat,lon, or airport code)", - "access": "read", - "domain": "wttr.in", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "location", - "type": "str", - "required": true, - "positional": true, - "help": "City name, \"lat,lon\", airport ICAO code, or \"@domain\"" - } - ], - "columns": [ - "location", - "region", - "country", - "latitude", - "longitude", - "observedAt", - "tempC", - "tempF", - "feelsLikeC", - "feelsLikeF", - "description", - "humidity", - "cloudCover", - "pressure", - "precipMm", - "visibilityKm", - "uvIndex", - "windKmph", - "windDirection", - "windDirectionDegree" - ], - "type": "js", - "modulePath": "wttr/current.js", - "sourceFile": "wttr/current.js" - }, - { - "site": "wttr", - "name": "forecast", - "description": "Multi-day weather forecast (up to 3 days, wttr.in free tier max)", - "access": "read", - "domain": "wttr.in", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "location", - "type": "str", - "required": true, - "positional": true, - "help": "City name, \"lat,lon\", airport ICAO code, or \"@domain\"" - }, - { - "name": "days", - "type": "int", - "default": 3, - "required": false, - "help": "Max forecast days (1-3, wttr.in caps the response at 3 days)" - } - ], - "columns": [ - "rank", - "date", - "minTempC", - "maxTempC", - "avgTempC", - "minTempF", - "maxTempF", - "avgTempF", - "sunHour", - "totalSnowCm", - "uvIndex", - "description", - "sunrise", - "sunset" - ], - "type": "js", - "modulePath": "wttr/forecast.js", - "sourceFile": "wttr/forecast.js" - }, { "site": "yahoo", "name": "search", diff --git a/plugin-command-manifest.json b/plugin-command-manifest.json index 8d7b4b48..c97c93d4 100644 --- a/plugin-command-manifest.json +++ b/plugin-command-manifest.json @@ -6551,246 +6551,1920 @@ "sourceFile": "plugins/openreview/venue.js" }, { - "site": "pypi", - "name": "package", - "description": "Inspect public PyPI package metadata", + "site": "osv", + "name": "query", + "description": "OSV.dev vulnerabilities affecting a package (optionally pinned to a version)", "access": "read", - "domain": "pypi.org", + "domain": "osv.dev", "strategy": "public", "browser": false, "args": [ { - "name": "name", + "name": "package", "type": "string", "required": true, "positional": true, - "help": "Python package name, for example django" + "help": "Package name (e.g. \"lodash\", \"django\")" + }, + { + "name": "ecosystem", + "type": "string", + "required": true, + "help": "OSV ecosystem (npm / PyPI / Go / Maven / NuGet / RubyGems / crates.io / Packagist / ...)" + }, + { + "name": "version", + "type": "string", + "required": false, + "help": "Pin to a specific version (e.g. \"4.17.20\"); omit for all known vulns" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max rows to return (1-200)" } ], "columns": [ - "name", - "version", + "rank", + "id", "summary", - "author", - "license", - "requiresPython", - "uploadedAt", - "projectUrl", - "homepage", - "repository" + "severity", + "aliases", + "published", + "modified", + "affectedPackages", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/pypi/package.js", - "sourceFile": "plugins/pypi/package.js" + "modulePath": "plugins/osv/query.js", + "sourceFile": "plugins/osv/query.js" }, { - "site": "pypi", - "name": "releases", - "description": "List recent public PyPI package releases", + "site": "osv", + "name": "vulnerability", + "description": "Single OSV.dev vulnerability detail (severity, affected packages, CVE/GHSA aliases)", "access": "read", - "domain": "pypi.org", + "domain": "osv.dev", "strategy": "public", "browser": false, "args": [ { - "name": "name", + "name": "id", "type": "string", "required": true, "positional": true, - "help": "Python package name, for example django" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum releases to return (1-50)" + "help": "OSV vulnerability id (e.g. \"GHSA-29mw-wpgm-hmr9\", \"CVE-2020-28500\")" } ], "columns": [ - "version", - "uploadedAt", - "fileCount", - "pythonVersions", - "yanked", + "id", + "summary", + "severity", + "aliases", + "published", + "modified", + "affectedPackages", + "cwes", + "referenceCount", "url" ], "type": "js", - "modulePath": "plugins/pypi/releases.js", - "sourceFile": "plugins/pypi/releases.js" + "modulePath": "plugins/osv/vulnerability.js", + "sourceFile": "plugins/osv/vulnerability.js" }, { - "site": "skyscanner", - "name": "flights", - "description": "Skyscanner visible round-trip flight results from a warmed browser session", + "site": "packagist", + "name": "package", + "description": "Fetch a Packagist package's metadata (version, downloads, license, repo, GitHub stars)", "access": "read", - "domain": "www.skyscanner.com", - "strategy": "ui", - "browser": true, + "domain": "packagist.org", + "strategy": "public", + "browser": false, "args": [ { - "name": "origin", + "name": "name", "type": "str", "required": true, "positional": true, - "help": "Skyscanner origin route code, for example nyca" - }, + "help": "Composer package \"/\" (e.g. \"symfony/console\", \"monolog/monolog\")" + } + ], + "columns": [ + "package", + "version", + "releasedAt", + "license", + "description", + "repository", + "githubStars", + "favers", + "downloads", + "monthlyDownloads", + "dailyDownloads", + "url" + ], + "type": "js", + "modulePath": "plugins/packagist/package.js", + "sourceFile": "plugins/packagist/package.js" + }, + { + "site": "packagist", + "name": "search", + "description": "Search Packagist (PHP / Composer) packages by keyword", + "access": "read", + "domain": "packagist.org", + "strategy": "public", + "browser": false, + "args": [ { - "name": "destination", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Skyscanner destination route code, for example lond" - }, - { - "name": "depart-date", - "type": "str", - "required": true, - "help": "Outbound date as YYYY-MM-DD" - }, - { - "name": "return-date", - "type": "str", - "required": true, - "help": "Return date as YYYY-MM-DD" + "help": "Search keyword (e.g. \"symfony\", \"laravel http\")" }, { "name": "limit", "type": "int", - "default": 10, + "default": 30, "required": false, - "help": "Maximum flight rows to return (1-30)" + "help": "Max packages (1-100, single Packagist page)" } ], "columns": [ "rank", - "priceText", - "airlines", - "outboundTime", - "outboundRoute", - "outboundDuration", - "outboundStops", - "returnTime", - "returnRoute", - "returnDuration", - "returnStops", + "package", + "description", + "downloads", + "favers", + "repository", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/skyscanner/flights.js", - "sourceFile": "plugins/skyscanner/flights.js", - "navigateBefore": false + "modulePath": "plugins/packagist/search.js", + "sourceFile": "plugins/packagist/search.js" }, { - "site": "techcrunch", + "site": "pubmed", "name": "article", - "description": "Read a TechCrunch article from its URL", + "aliases": [ + "paper", + "read" + ], + "description": "Get detailed information for a PubMed article by PMID", "access": "read", - "domain": "techcrunch.com", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "url", - "type": "string", + "name": "pmid", + "type": "str", "required": true, "positional": true, - "help": "TechCrunch article URL" + "help": "PubMed ID, e.g. 37780221" + }, + { + "name": "full-abstract", + "type": "boolean", + "default": false, + "required": false, + "help": "Do not truncate the abstract in table output" } ], "columns": [ + "pmid", "title", - "author", - "publishedAt", - "categories", - "description", - "content", + "authors", + "journal", + "year", + "date", + "article_type", + "language", + "doi", + "pmc", + "affiliations", + "grants", + "mesh_terms", + "keywords", + "abstract", "url" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/techcrunch/article.js", - "sourceFile": "plugins/techcrunch/article.js" + "modulePath": "plugins/pubmed/article.js", + "sourceFile": "plugins/pubmed/article.js" }, { - "site": "techcrunch", - "name": "search", - "description": "Search TechCrunch stories or list the latest stories", + "site": "pubmed", + "name": "author", + "description": "Search PubMed articles by author name and optional affiliation", "access": "read", - "domain": "techcrunch.com", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "query", - "type": "string", - "required": false, + "name": "name", + "type": "str", + "required": true, "positional": true, - "help": "Words to search for" + "help": "Author name, e.g. \"Smith J\"" }, { - "name": "latest", - "type": "boolean", - "default": false, + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "List the latest stories instead of searching" + "help": "Max results (1-100)" }, { - "name": "limit", + "name": "affiliation", + "type": "str", + "required": false, + "help": "Filter by author affiliation" + }, + { + "name": "position", + "type": "str", + "default": "any", + "required": false, + "help": "Author position: any, first, or last", + "choices": [ + "any", + "first", + "last" + ] + }, + { + "name": "year-from", "type": "int", - "default": 20, "required": false, - "help": "Maximum stories to return (1-50)" + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "sort", + "type": "str", + "default": "date", + "required": false, + "help": "Sort by date or relevance", + "choices": [ + "date", + "relevance" + ] } ], "columns": [ "rank", + "pmid", "title", - "author", - "publishedAt", - "description", + "authors", + "journal", + "year", + "article_type", + "doi", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/techcrunch/search.js", - "sourceFile": "plugins/techcrunch/search.js" + "modulePath": "plugins/pubmed/author.js", + "sourceFile": "plugins/pubmed/author.js" }, { - "site": "ualberta", - "name": "export-postgraduate-courses", - "description": "Export University of Alberta postgraduate programs from the official graduate-program catalogue.", + "site": "pubmed", + "name": "citations", + "description": "Get PubMed citation relationships for an article", "access": "read", - "example": "webcmd ualberta export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.ualberta.ca", - "strategy": "ui", - "browser": true, + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", + "name": "pmid", + "type": "str", + "required": true, + "positional": true, + "help": "PubMed ID, e.g. 37780221" + }, + { + "name": "direction", + "type": "str", + "default": "citedby", "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "help": "citedby or references", + "choices": [ + "citedby", + "references" + ] }, { - "name": "count", + "name": "limit", "type": "int", + "default": 20, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Max results (1-100)" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" + ], + "type": "js", + "modulePath": "plugins/pubmed/citations.js", + "sourceFile": "plugins/pubmed/citations.js" + }, + { + "site": "pubmed", + "name": "clinical-trial", + "description": "Search PubMed clinical trials with a trial-study preset", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Clinical topic query, e.g. \"breast cancer\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "free-full-text", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include free full text articles" + }, + { + "name": "sort", + "type": "str", + "default": "date", + "required": false, + "help": "Sort by date or relevance", + "choices": [ + "date", + "relevance" + ] + } + ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" + ], + "type": "js", + "modulePath": "plugins/pubmed/clinical-trial.js", + "sourceFile": "plugins/pubmed/clinical-trial.js" + }, + { + "site": "pubmed", + "name": "journal", + "description": "Search PubMed articles by journal name", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "journal", + "type": "str", + "required": true, + "positional": true, + "help": "Journal name, e.g. \"Nature\" or \"The Lancet\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance or date", + "choices": [ + "relevance", + "date" + ] + } + ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" + ], + "type": "js", + "modulePath": "plugins/pubmed/journal.js", + "sourceFile": "plugins/pubmed/journal.js" + }, + { + "site": "pubmed", + "name": "mesh", + "description": "Search PubMed articles by MeSH term", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "term", + "type": "str", + "required": true, + "positional": true, + "help": "MeSH term, e.g. \"Neoplasms\" or \"Machine Learning\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "major", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include articles where this is a major MeSH topic" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance or date", + "choices": [ + "relevance", + "date" + ] + } + ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" + ], + "type": "js", + "modulePath": "plugins/pubmed/mesh.js", + "sourceFile": "plugins/pubmed/mesh.js" + }, + { + "site": "pubmed", + "name": "related", + "description": "Find articles related to a PubMed article", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "pmid", + "type": "str", + "required": true, + "positional": true, + "help": "PubMed ID, e.g. 37780221" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "score", + "type": "boolean", + "default": false, + "required": false, + "help": "Show similarity scores when available" + } + ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "score", + "doi", + "url" + ], + "type": "js", + "modulePath": "plugins/pubmed/related.js", + "sourceFile": "plugins/pubmed/related.js" + }, + { + "site": "pubmed", + "name": "review", + "description": "Search PubMed review articles with a review preset", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Review topic query, e.g. \"immunotherapy\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "has-abstract", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include articles with abstracts" + }, + { + "name": "sort", + "type": "str", + "default": "date", + "required": false, + "help": "Sort by date or relevance", + "choices": [ + "date", + "relevance" + ] + } + ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" + ], + "type": "js", + "modulePath": "plugins/pubmed/review.js", + "sourceFile": "plugins/pubmed/review.js" + }, + { + "site": "pubmed", + "name": "search", + "description": "Search PubMed articles with advanced filters", + "access": "read", + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query, e.g. \"machine learning cancer\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "author", + "type": "str", + "required": false, + "help": "Filter by author name" + }, + { + "name": "journal", + "type": "str", + "required": false, + "help": "Filter by journal name" + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "article-type", + "type": "str", + "required": false, + "help": "Filter by publication type, e.g. Review or Clinical Trial" + }, + { + "name": "has-abstract", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include articles with abstracts" + }, + { + "name": "free-full-text", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include free full text articles" + }, + { + "name": "humans-only", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include human studies" + }, + { + "name": "english-only", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include English articles" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance, date, author, or journal", + "choices": [ + "relevance", + "date", + "author", + "journal" + ] + } + ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/pubmed/search.js", + "sourceFile": "plugins/pubmed/search.js" + }, + { + "site": "pypi", + "name": "package", + "description": "Inspect public PyPI package metadata", + "access": "read", + "domain": "pypi.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "string", + "required": true, + "positional": true, + "help": "Python package name, for example django" + } + ], + "columns": [ + "name", + "version", + "summary", + "author", + "license", + "requiresPython", + "uploadedAt", + "projectUrl", + "homepage", + "repository" + ], + "type": "js", + "modulePath": "plugins/pypi/package.js", + "sourceFile": "plugins/pypi/package.js" + }, + { + "site": "pypi", + "name": "releases", + "description": "List recent public PyPI package releases", + "access": "read", + "domain": "pypi.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "string", + "required": true, + "positional": true, + "help": "Python package name, for example django" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum releases to return (1-50)" + } + ], + "columns": [ + "version", + "uploadedAt", + "fileCount", + "pythonVersions", + "yanked", + "url" + ], + "type": "js", + "modulePath": "plugins/pypi/releases.js", + "sourceFile": "plugins/pypi/releases.js" + }, + { + "site": "rest-countries", + "name": "country", + "description": "Look up countries by name (common / official, substring match)", + "access": "read", + "domain": "restcountries.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Country name (e.g. \"japan\", \"united kingdom\")" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max rows (1-250)" + } + ], + "columns": [ + "rank", + "commonName", + "officialName", + "cca2", + "cca3", + "ccn3", + "capital", + "region", + "subregion", + "population", + "area", + "languages", + "currencies", + "latitude", + "longitude", + "timezones", + "independent", + "unMember", + "landlocked", + "flag", + "url" + ], + "type": "js", + "modulePath": "plugins/rest-countries/country.js", + "sourceFile": "plugins/rest-countries/country.js" + }, + { + "site": "rest-countries", + "name": "region", + "description": "List countries in a region (africa / americas / asia / europe / oceania / antarctic)", + "access": "read", + "domain": "restcountries.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "region", + "type": "str", + "required": true, + "positional": true, + "help": "Region name (case-insensitive)" + }, + { + "name": "limit", + "type": "int", + "default": 250, + "required": false, + "help": "Max rows (1-250)" + } + ], + "columns": [ + "rank", + "commonName", + "officialName", + "cca2", + "cca3", + "ccn3", + "capital", + "region", + "subregion", + "population", + "area", + "languages", + "currencies", + "latitude", + "longitude", + "timezones", + "independent", + "unMember", + "landlocked", + "flag", + "url" + ], + "type": "js", + "modulePath": "plugins/rest-countries/region.js", + "sourceFile": "plugins/rest-countries/region.js" + }, + { + "site": "rfc", + "name": "rfc", + "description": "Single IETF RFC metadata (title, abstract, working group, authors, std level)", + "access": "read", + "domain": "datatracker.ietf.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "number", + "type": "int", + "required": true, + "positional": true, + "help": "RFC number (e.g. 9000, 791, 2616)" + } + ], + "columns": [ + "rfc", + "title", + "state", + "stdLevel", + "group", + "groupType", + "pages", + "published", + "authors", + "abstract", + "rfcEditorUrl", + "url" + ], + "type": "js", + "modulePath": "plugins/rfc/rfc.js", + "sourceFile": "plugins/rfc/rfc.js" + }, + { + "site": "rubygems", + "name": "gem", + "description": "Fetch a RubyGems.org gem's metadata (version, downloads, license, links)", + "access": "read", + "domain": "rubygems.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Gem name (e.g. \"rails\", \"sidekiq\")" + } + ], + "columns": [ + "gem", + "version", + "releasedAt", + "downloads", + "versionDownloads", + "license", + "authors", + "homepage", + "source", + "bugs", + "info", + "url" + ], + "type": "js", + "modulePath": "plugins/rubygems/gem.js", + "sourceFile": "plugins/rubygems/gem.js" + }, + { + "site": "rubygems", + "name": "search", + "description": "Search RubyGems.org gems by keyword", + "access": "read", + "domain": "rubygems.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"rails\", \"redis\")" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max gems (1-100, single RubyGems page)" + } + ], + "columns": [ + "rank", + "gem", + "version", + "downloads", + "license", + "authors", + "info", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/rubygems/search.js", + "sourceFile": "plugins/rubygems/search.js" + }, + { + "site": "semanticscholar", + "name": "citations", + "description": "List papers that cite a Semantic Scholar paper (paginated)", + "access": "read", + "domain": "api.semanticscholar.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max citing papers (1-1000, single Semantic Scholar page)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Page offset (0-based)" + } + ], + "columns": [ + "rank", + "paperId", + "doi", + "title", + "year", + "firstAuthor", + "citationCount", + "url" + ], + "type": "js", + "modulePath": "plugins/semanticscholar/citations.js", + "sourceFile": "plugins/semanticscholar/citations.js" + }, + { + "site": "semanticscholar", + "name": "paper", + "description": "Semantic Scholar paper detail (citation graph + AI tldr) by paperId, DOI, or arXiv id", + "access": "read", + "domain": "api.semanticscholar.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id (e.g. \"ARXIV:1706.03762\", \"PMID:12345\")" + } + ], + "columns": [ + "paperId", + "doi", + "title", + "year", + "firstAuthor", + "citationCount", + "influentialCitationCount", + "referenceCount", + "tldr", + "url" + ], + "type": "js", + "modulePath": "plugins/semanticscholar/paper.js", + "sourceFile": "plugins/semanticscholar/paper.js" + }, + { + "site": "semanticscholar", + "name": "recommendations", + "description": "Semantic Scholar AI-curated related papers for a paperId, DOI, or arXiv id", + "access": "read", + "domain": "api.semanticscholar.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max recommendations (1-500)" + } + ], + "columns": [ + "rank", + "paperId", + "doi", + "title", + "year", + "firstAuthor", + "citationCount", + "url" + ], + "type": "js", + "modulePath": "plugins/semanticscholar/recommendations.js", + "sourceFile": "plugins/semanticscholar/recommendations.js" + }, + { + "site": "semanticscholar", + "name": "search", + "description": "Search Semantic Scholar papers by free text", + "access": "read", + "domain": "api.semanticscholar.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search text (e.g. \"attention is all you need\", \"diffusion model\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max papers (1-100, single Semantic Scholar page)" + } + ], + "columns": [ + "rank", + "paperId", + "doi", + "title", + "year", + "firstAuthor", + "citationCount", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/semanticscholar/search.js", + "sourceFile": "plugins/semanticscholar/search.js" + }, + { + "site": "skyscanner", + "name": "flights", + "description": "Skyscanner visible round-trip flight results from a warmed browser session", + "access": "read", + "domain": "www.skyscanner.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "origin", + "type": "str", + "required": true, + "positional": true, + "help": "Skyscanner origin route code, for example nyca" + }, + { + "name": "destination", + "type": "str", + "required": true, + "positional": true, + "help": "Skyscanner destination route code, for example lond" + }, + { + "name": "depart-date", + "type": "str", + "required": true, + "help": "Outbound date as YYYY-MM-DD" + }, + { + "name": "return-date", + "type": "str", + "required": true, + "help": "Return date as YYYY-MM-DD" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum flight rows to return (1-30)" + } + ], + "columns": [ + "rank", + "priceText", + "airlines", + "outboundTime", + "outboundRoute", + "outboundDuration", + "outboundStops", + "returnTime", + "returnRoute", + "returnDuration", + "returnStops", + "url" + ], + "type": "js", + "modulePath": "plugins/skyscanner/flights.js", + "sourceFile": "plugins/skyscanner/flights.js", + "navigateBefore": false + }, + { + "site": "stackoverflow", + "name": "bounties", + "description": "Active bounties on Stack Overflow", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max number of results" + } + ], + "columns": [ + "rank", + "id", + "bounty", + "title", + "score", + "answers", + "views", + "is_answered", + "tags", + "author", + "creation_date", + "url" + ], + "type": "js", + "modulePath": "plugins/stackoverflow/bounties.js", + "sourceFile": "plugins/stackoverflow/bounties.js" + }, + { + "site": "stackoverflow", + "name": "hot", + "description": "Hot Stack Overflow questions", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max number of results" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "answers", + "views", + "is_answered", + "tags", + "author", + "creation_date", + "url" + ], + "type": "js", + "modulePath": "plugins/stackoverflow/hot.js", + "sourceFile": "plugins/stackoverflow/hot.js" + }, + { + "site": "stackoverflow", + "name": "read", + "description": "Read a Stack Overflow question with answers and comments", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Stack Overflow question id (numeric, e.g. 79935770)" + }, + { + "name": "answers-limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max answers to include (1-100; accepted answer always included first)" + }, + { + "name": "comments-limit", + "type": "int", + "default": 5, + "required": false, + "help": "Max comments per question/answer (1-100)" + }, + { + "name": "max-length", + "type": "int", + "default": 4000, + "required": false, + "help": "Max characters per body / answer / comment (min 100)" + } + ], + "columns": [ + "type", + "author", + "score", + "accepted", + "text" + ], + "type": "js", + "modulePath": "plugins/stackoverflow/read.js", + "sourceFile": "plugins/stackoverflow/read.js" + }, + { + "site": "stackoverflow", + "name": "related", + "description": "List Stack Overflow questions related to a given question id.", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Stack Overflow question id (numeric, e.g. 79935770)." + }, + { + "name": "sort", + "type": "string", + "default": "rank", + "required": false, + "help": "Sort key: rank, activity, votes, creation (rank = SO relevance default)." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max related questions (1-100)." + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "answers", + "views", + "isAnswered", + "tags", + "author", + "createdAt", + "lastActivityAt", + "url" + ], + "type": "js", + "modulePath": "plugins/stackoverflow/related.js", + "sourceFile": "plugins/stackoverflow/related.js" + }, + { + "site": "stackoverflow", + "name": "search", + "description": "Search Stack Overflow questions", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "string", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max number of results" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "answers", + "views", + "is_answered", + "tags", + "author", + "creation_date", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/stackoverflow/search.js", + "sourceFile": "plugins/stackoverflow/search.js" + }, + { + "site": "stackoverflow", + "name": "tag", + "description": "List Stack Overflow questions tagged with a given tag (most active first).", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "tag", + "type": "string", + "required": true, + "positional": true, + "help": "Tag slug (e.g. python, rust, typescript)." + }, + { + "name": "sort", + "type": "string", + "default": "activity", + "required": false, + "help": "Sort key: activity, votes, creation, hot, week, month" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max questions to return (max 100)." + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "answers", + "views", + "isAnswered", + "tags", + "author", + "createdAt", + "lastActivityAt", + "url" + ], + "type": "js", + "modulePath": "plugins/stackoverflow/tag.js", + "sourceFile": "plugins/stackoverflow/tag.js" + }, + { + "site": "stackoverflow", + "name": "unanswered", + "description": "Top voted unanswered questions on Stack Overflow", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max number of results" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "answers", + "views", + "tags", + "author", + "creation_date", + "url" + ], + "type": "js", + "modulePath": "plugins/stackoverflow/unanswered.js", + "sourceFile": "plugins/stackoverflow/unanswered.js" + }, + { + "site": "stackoverflow", + "name": "user", + "description": "Find Stack Overflow users by display name (highest reputation first).", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "string", + "required": true, + "positional": true, + "help": "Display name (or substring) to search." + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max users to return (max 100)." + } + ], + "columns": [ + "userId", + "displayName", + "reputation", + "goldBadges", + "silverBadges", + "bronzeBadges", + "location", + "createdAt", + "lastAccessAt", + "url" + ], + "type": "js", + "modulePath": "plugins/stackoverflow/user.js", + "sourceFile": "plugins/stackoverflow/user.js" + }, + { + "site": "steam", + "name": "app", + "description": "Steam storefront detail for a single app id", + "access": "read", + "domain": "store.steampowered.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Numeric Steam app id (e.g. \"620\" for Portal 2)" + }, + { + "name": "currency", + "type": "str", + "default": "us", + "required": false, + "help": "Storefront country code (e.g. us / cn / jp / de)" + } + ], + "columns": [ + "id", + "name", + "type", + "isFree", + "releaseDate", + "developers", + "publishers", + "price", + "currency", + "metacritic", + "recommendations", + "genres", + "categories", + "shortDescription", + "website", + "url" + ], + "type": "js", + "modulePath": "plugins/steam/app.js", + "sourceFile": "plugins/steam/app.js" + }, + { + "site": "steam", + "name": "search", + "description": "Search the Steam storefront by name keyword", + "access": "read", + "domain": "store.steampowered.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"portal\", \"stardew\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-50)" + }, + { + "name": "currency", + "type": "str", + "default": "us", + "required": false, + "help": "Storefront country code (e.g. us / cn / jp / de)" + } + ], + "columns": [ + "rank", + "id", + "name", + "price", + "currency", + "metascore", + "platforms", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/steam/search.js", + "sourceFile": "plugins/steam/search.js" + }, + { + "site": "steam", + "name": "top-sellers", + "description": "Steam top selling games", + "access": "read", + "domain": "store.steampowered.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of games" + } + ], + "columns": [ + "rank", + "name", + "price", + "discount", + "url" + ], + "type": "js", + "modulePath": "plugins/steam/top-sellers.js", + "sourceFile": "plugins/steam/top-sellers.js" + }, + { + "site": "techcrunch", + "name": "article", + "description": "Read a TechCrunch article from its URL", + "access": "read", + "domain": "techcrunch.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "TechCrunch article URL" + } + ], + "columns": [ + "title", + "author", + "publishedAt", + "categories", + "description", + "content", + "url" + ], + "type": "js", + "modulePath": "plugins/techcrunch/article.js", + "sourceFile": "plugins/techcrunch/article.js" + }, + { + "site": "techcrunch", + "name": "search", + "description": "Search TechCrunch stories or list the latest stories", + "access": "read", + "domain": "techcrunch.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "string", + "required": false, + "positional": true, + "help": "Words to search for" + }, + { + "name": "latest", + "type": "boolean", + "default": false, + "required": false, + "help": "List the latest stories instead of searching" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum stories to return (1-50)" + } + ], + "columns": [ + "rank", + "title", + "author", + "publishedAt", + "description", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/techcrunch/search.js", + "sourceFile": "plugins/techcrunch/search.js" + }, + { + "site": "tvmaze", + "name": "search", + "description": "TVmaze TV show search by title (returns id, name, network, premiered/ended, rating)", + "access": "read", + "domain": "tvmaze.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "string", + "required": true, + "positional": true, + "help": "TV show title or fragment to search for" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max rows to return (1-50)" + } + ], + "columns": [ + "rank", + "id", + "name", + "type", + "language", + "genres", + "status", + "premiered", + "ended", + "network", + "rating", + "matchScore", + "summary", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/tvmaze/search.js", + "sourceFile": "plugins/tvmaze/search.js" + }, + { + "site": "tvmaze", + "name": "show", + "description": "Single TVmaze TV show detail by id (network, schedule, rating, IMDB/TheTVDB cross-refs)", + "access": "read", + "domain": "tvmaze.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "int", + "required": true, + "positional": true, + "help": "TVmaze show id (positive integer)" + } + ], + "columns": [ + "id", + "name", + "type", + "language", + "genres", + "status", + "premiered", + "ended", + "runtime", + "averageRuntime", + "network", + "country", + "schedule", + "rating", + "imdb", + "thetvdb", + "officialSite", + "summary", + "url" + ], + "type": "js", + "modulePath": "plugins/tvmaze/show.js", + "sourceFile": "plugins/tvmaze/show.js" + }, + { + "site": "ualberta", + "name": "export-postgraduate-courses", + "description": "Export University of Alberta postgraduate programs from the official graduate-program catalogue.", + "access": "read", + "example": "webcmd ualberta export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.ualberta.ca", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", "Study option", "Program Type", "Partner", @@ -6840,6 +8514,357 @@ "sourceFile": "plugins/ualberta/export-postgraduate-courses.js", "navigateBefore": false }, + { + "site": "wikidata", + "name": "entity", + "description": "Fetch a Wikidata entity by Q/P/L id (label, description, aliases, claim summary)", + "access": "read", + "domain": "www.wikidata.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Entity id (e.g. Q937 = Albert Einstein, P31 = instance of)" + }, + { + "name": "language", + "type": "str", + "default": "en", + "required": false, + "help": "Display language (ISO 639, falls back to English when missing)" + } + ], + "columns": [ + "qid", + "type", + "label", + "description", + "aliases", + "claimPropertyCount", + "sitelinkCount", + "enwikiTitle", + "modified", + "url" + ], + "type": "js", + "modulePath": "plugins/wikidata/entity.js", + "sourceFile": "plugins/wikidata/entity.js" + }, + { + "site": "wikidata", + "name": "search", + "description": "Search Wikidata items by keyword (returns Q-IDs)", + "access": "read", + "domain": "www.wikidata.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (label / alias)" + }, + { + "name": "language", + "type": "str", + "default": "en", + "required": false, + "help": "Search & display language (ISO 639, e.g. en, fr, zh)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max items (1-50)" + } + ], + "columns": [ + "rank", + "qid", + "label", + "description", + "matchType", + "matchText", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/wikidata/search.js", + "sourceFile": "plugins/wikidata/search.js" + }, + { + "site": "wikipedia", + "name": "page", + "description": "Full plain-text extract of a Wikipedia article (optional paragraph cap).", + "access": "read", + "domain": "wikipedia.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "title", + "type": "string", + "required": true, + "positional": true, + "help": "Article title (e.g. \"Transformer (machine learning model)\")" + }, + { + "name": "lang", + "type": "string", + "default": "en", + "required": false, + "help": "Language code (en, zh, ja, de, ...)." + }, + { + "name": "paragraphs", + "type": "int", + "default": 0, + "required": false, + "help": "Cap to first N paragraphs (0 = full article)." + } + ], + "columns": [ + "title", + "description", + "pageId", + "paragraphs", + "extract", + "url" + ], + "type": "js", + "modulePath": "plugins/wikipedia/page.js", + "sourceFile": "plugins/wikipedia/page.js" + }, + { + "site": "wikipedia", + "name": "random", + "description": "Get a random Wikipedia article", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language code (e.g. en, zh, ja)" + } + ], + "columns": [ + "title", + "description", + "extract", + "url" + ], + "type": "js", + "modulePath": "plugins/wikipedia/random.js", + "sourceFile": "plugins/wikipedia/random.js" + }, + { + "site": "wikipedia", + "name": "search", + "description": "Search Wikipedia articles", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language code (e.g. en, zh, ja)" + } + ], + "columns": [ + "title", + "snippet", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/wikipedia/search.js", + "sourceFile": "plugins/wikipedia/search.js" + }, + { + "site": "wikipedia", + "name": "summary", + "description": "Get Wikipedia article summary", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "title", + "type": "str", + "required": true, + "positional": true, + "help": "Article title (e.g. \"Transformer (machine learning model)\")" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language code (e.g. en, zh, ja)" + } + ], + "columns": [ + "title", + "description", + "extract", + "url" + ], + "type": "js", + "modulePath": "plugins/wikipedia/summary.js", + "sourceFile": "plugins/wikipedia/summary.js" + }, + { + "site": "wikipedia", + "name": "trending", + "description": "Most-read Wikipedia articles (yesterday)", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language code (e.g. en, zh, ja)" + } + ], + "columns": [ + "rank", + "title", + "description", + "views" + ], + "type": "js", + "modulePath": "plugins/wikipedia/trending.js", + "sourceFile": "plugins/wikipedia/trending.js" + }, + { + "site": "wttr", + "name": "current", + "description": "Current weather conditions for a location (city, lat,lon, or airport code)", + "access": "read", + "domain": "wttr.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "location", + "type": "str", + "required": true, + "positional": true, + "help": "City name, \"lat,lon\", airport ICAO code, or \"@domain\"" + } + ], + "columns": [ + "location", + "region", + "country", + "latitude", + "longitude", + "observedAt", + "tempC", + "tempF", + "feelsLikeC", + "feelsLikeF", + "description", + "humidity", + "cloudCover", + "pressure", + "precipMm", + "visibilityKm", + "uvIndex", + "windKmph", + "windDirection", + "windDirectionDegree" + ], + "type": "js", + "modulePath": "plugins/wttr/current.js", + "sourceFile": "plugins/wttr/current.js" + }, + { + "site": "wttr", + "name": "forecast", + "description": "Multi-day weather forecast (up to 3 days, wttr.in free tier max)", + "access": "read", + "domain": "wttr.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "location", + "type": "str", + "required": true, + "positional": true, + "help": "City name, \"lat,lon\", airport ICAO code, or \"@domain\"" + }, + { + "name": "days", + "type": "int", + "default": 3, + "required": false, + "help": "Max forecast days (1-3, wttr.in caps the response at 3 days)" + } + ], + "columns": [ + "rank", + "date", + "minTempC", + "maxTempC", + "avgTempC", + "minTempF", + "maxTempF", + "avgTempF", + "sunHour", + "totalSnowCm", + "uvIndex", + "description", + "sunrise", + "sunset" + ], + "type": "js", + "modulePath": "plugins/wttr/forecast.js", + "sourceFile": "plugins/wttr/forecast.js" + }, { "site": "yale", "name": "export-postgraduate-courses", diff --git a/plugins/osv/README.md b/plugins/osv/README.md new file mode 100644 index 00000000..2c04eee3 --- /dev/null +++ b/plugins/osv/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-osv + +Webcmd commands for osv. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/osv +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd osv query` | OSV.dev vulnerabilities affecting a package (optionally pinned to a version) | +| `webcmd osv vulnerability` | Single OSV.dev vulnerability detail (severity, affected packages, CVE/GHSA aliases) | diff --git a/plugins/osv/package.json b/plugins/osv/package.json new file mode 100644 index 00000000..90611cf8 --- /dev/null +++ b/plugins/osv/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-osv", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for osv", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/osv/query.js b/plugins/osv/query.js similarity index 100% rename from clis/osv/query.js rename to plugins/osv/query.js diff --git a/clis/osv/osv.test.js b/plugins/osv/test/osv.test.js similarity index 98% rename from clis/osv/osv.test.js rename to plugins/osv/test/osv.test.js index b737c5a7..fd0f15d7 100644 --- a/clis/osv/osv.test.js +++ b/plugins/osv/test/osv.test.js @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './vulnerability.js'; -import './query.js'; +import '../vulnerability.js'; +import '../query.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/osv/utils.js b/plugins/osv/utils.js similarity index 100% rename from clis/osv/utils.js rename to plugins/osv/utils.js diff --git a/clis/osv/vulnerability.js b/plugins/osv/vulnerability.js similarity index 100% rename from clis/osv/vulnerability.js rename to plugins/osv/vulnerability.js diff --git a/plugins/osv/webcmd-plugin.json b/plugins/osv/webcmd-plugin.json new file mode 100644 index 00000000..1cd9bba6 --- /dev/null +++ b/plugins/osv/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "osv", + "version": "0.1.0", + "description": "Webcmd commands for osv", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/packagist/README.md b/plugins/packagist/README.md new file mode 100644 index 00000000..100cc0f2 --- /dev/null +++ b/plugins/packagist/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-packagist + +Webcmd commands for packagist. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/packagist +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd packagist package` | Fetch a Packagist package's metadata (version, downloads, license, repo, GitHub stars) | +| `webcmd packagist search` | Search Packagist (PHP / Composer) packages by keyword | diff --git a/clis/packagist/package.js b/plugins/packagist/package.js similarity index 100% rename from clis/packagist/package.js rename to plugins/packagist/package.js diff --git a/plugins/packagist/package.json b/plugins/packagist/package.json new file mode 100644 index 00000000..ee49518b --- /dev/null +++ b/plugins/packagist/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-packagist", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for packagist", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/packagist/search.js b/plugins/packagist/search.js similarity index 100% rename from clis/packagist/search.js rename to plugins/packagist/search.js diff --git a/clis/packagist/utils.js b/plugins/packagist/utils.js similarity index 100% rename from clis/packagist/utils.js rename to plugins/packagist/utils.js diff --git a/plugins/packagist/webcmd-plugin.json b/plugins/packagist/webcmd-plugin.json new file mode 100644 index 00000000..9609f212 --- /dev/null +++ b/plugins/packagist/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "packagist", + "version": "0.1.0", + "description": "Webcmd commands for packagist", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/pubmed/README.md b/plugins/pubmed/README.md new file mode 100644 index 00000000..07dafd87 --- /dev/null +++ b/plugins/pubmed/README.md @@ -0,0 +1,23 @@ +# webcmd-plugin-pubmed + +Webcmd commands for pubmed. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/pubmed +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd pubmed article` | Get detailed information for a PubMed article by PMID | +| `webcmd pubmed author` | Search PubMed articles by author name and optional affiliation | +| `webcmd pubmed citations` | Get PubMed citation relationships for an article | +| `webcmd pubmed clinical-trial` | Search PubMed clinical trials with a trial-study preset | +| `webcmd pubmed journal` | Search PubMed articles by journal name | +| `webcmd pubmed mesh` | Search PubMed articles by MeSH term | +| `webcmd pubmed related` | Find articles related to a PubMed article | +| `webcmd pubmed review` | Search PubMed review articles with a review preset | +| `webcmd pubmed search` | Search PubMed articles with advanced filters | diff --git a/clis/pubmed/article.js b/plugins/pubmed/article.js similarity index 100% rename from clis/pubmed/article.js rename to plugins/pubmed/article.js diff --git a/clis/pubmed/author.js b/plugins/pubmed/author.js similarity index 100% rename from clis/pubmed/author.js rename to plugins/pubmed/author.js diff --git a/clis/pubmed/citations.js b/plugins/pubmed/citations.js similarity index 100% rename from clis/pubmed/citations.js rename to plugins/pubmed/citations.js diff --git a/clis/pubmed/clinical-trial.js b/plugins/pubmed/clinical-trial.js similarity index 100% rename from clis/pubmed/clinical-trial.js rename to plugins/pubmed/clinical-trial.js diff --git a/clis/pubmed/journal.js b/plugins/pubmed/journal.js similarity index 100% rename from clis/pubmed/journal.js rename to plugins/pubmed/journal.js diff --git a/clis/pubmed/mesh.js b/plugins/pubmed/mesh.js similarity index 100% rename from clis/pubmed/mesh.js rename to plugins/pubmed/mesh.js diff --git a/plugins/pubmed/package.json b/plugins/pubmed/package.json new file mode 100644 index 00000000..eaf139d7 --- /dev/null +++ b/plugins/pubmed/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-pubmed", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for pubmed", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/pubmed/related.js b/plugins/pubmed/related.js similarity index 100% rename from clis/pubmed/related.js rename to plugins/pubmed/related.js diff --git a/clis/pubmed/review.js b/plugins/pubmed/review.js similarity index 100% rename from clis/pubmed/review.js rename to plugins/pubmed/review.js diff --git a/clis/pubmed/search.js b/plugins/pubmed/search.js similarity index 100% rename from clis/pubmed/search.js rename to plugins/pubmed/search.js diff --git a/clis/pubmed/pubmed.test.js b/plugins/pubmed/test/pubmed.test.js similarity index 99% rename from clis/pubmed/pubmed.test.js rename to plugins/pubmed/test/pubmed.test.js index 9fe006fa..e798f418 100644 --- a/clis/pubmed/pubmed.test.js +++ b/plugins/pubmed/test/pubmed.test.js @@ -10,16 +10,16 @@ import { parseArticleXml, requireBoundedInt, requirePmid, -} from './utils.js'; -import './search.js'; -import './article.js'; -import './author.js'; -import './citations.js'; -import './related.js'; -import './clinical-trial.js'; -import './review.js'; -import './mesh.js'; -import './journal.js'; +} from '../utils.js'; +import '../search.js'; +import '../article.js'; +import '../author.js'; +import '../citations.js'; +import '../related.js'; +import '../clinical-trial.js'; +import '../review.js'; +import '../mesh.js'; +import '../journal.js'; const SUMMARY_RESULT = { result: { diff --git a/clis/pubmed/utils.js b/plugins/pubmed/utils.js similarity index 100% rename from clis/pubmed/utils.js rename to plugins/pubmed/utils.js diff --git a/plugins/pubmed/webcmd-plugin.json b/plugins/pubmed/webcmd-plugin.json new file mode 100644 index 00000000..32e67167 --- /dev/null +++ b/plugins/pubmed/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "pubmed", + "version": "0.1.0", + "description": "Webcmd commands for pubmed", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/rest-countries/README.md b/plugins/rest-countries/README.md new file mode 100644 index 00000000..37e83a70 --- /dev/null +++ b/plugins/rest-countries/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-rest-countries + +Webcmd commands for rest-countries. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/rest-countries +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd rest-countries country` | Look up countries by name (common / official, substring match) | +| `webcmd rest-countries region` | List countries in a region (africa / americas / asia / europe / oceania / antarctic) | diff --git a/clis/rest-countries/country.js b/plugins/rest-countries/country.js similarity index 100% rename from clis/rest-countries/country.js rename to plugins/rest-countries/country.js diff --git a/plugins/rest-countries/package.json b/plugins/rest-countries/package.json new file mode 100644 index 00000000..0808e18f --- /dev/null +++ b/plugins/rest-countries/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-rest-countries", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for rest-countries", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/rest-countries/region.js b/plugins/rest-countries/region.js similarity index 100% rename from clis/rest-countries/region.js rename to plugins/rest-countries/region.js diff --git a/clis/rest-countries/rest-countries.test.js b/plugins/rest-countries/test/rest-countries.test.js similarity index 98% rename from clis/rest-countries/rest-countries.test.js rename to plugins/rest-countries/test/rest-countries.test.js index 6967a770..7145e695 100644 --- a/clis/rest-countries/rest-countries.test.js +++ b/plugins/rest-countries/test/rest-countries.test.js @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './country.js'; -import './region.js'; +import '../country.js'; +import '../region.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/rest-countries/utils.js b/plugins/rest-countries/utils.js similarity index 100% rename from clis/rest-countries/utils.js rename to plugins/rest-countries/utils.js diff --git a/plugins/rest-countries/webcmd-plugin.json b/plugins/rest-countries/webcmd-plugin.json new file mode 100644 index 00000000..08078ba2 --- /dev/null +++ b/plugins/rest-countries/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "rest-countries", + "version": "0.1.0", + "description": "Webcmd commands for rest-countries", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/rfc/README.md b/plugins/rfc/README.md new file mode 100644 index 00000000..0f66be4e --- /dev/null +++ b/plugins/rfc/README.md @@ -0,0 +1,15 @@ +# webcmd-plugin-rfc + +Webcmd commands for rfc. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/rfc +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd rfc rfc` | Single IETF RFC metadata (title, abstract, working group, authors, std level) | diff --git a/plugins/rfc/package.json b/plugins/rfc/package.json new file mode 100644 index 00000000..9c638834 --- /dev/null +++ b/plugins/rfc/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-rfc", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for rfc", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/rfc/rfc.js b/plugins/rfc/rfc.js similarity index 100% rename from clis/rfc/rfc.js rename to plugins/rfc/rfc.js diff --git a/clis/rfc/rfc.test.js b/plugins/rfc/test/rfc.test.js similarity index 99% rename from clis/rfc/rfc.test.js rename to plugins/rfc/test/rfc.test.js index ac3f2484..d59f1e02 100644 --- a/clis/rfc/rfc.test.js +++ b/plugins/rfc/test/rfc.test.js @@ -1,7 +1,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './rfc.js'; +import '../rfc.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/rfc/utils.js b/plugins/rfc/utils.js similarity index 100% rename from clis/rfc/utils.js rename to plugins/rfc/utils.js diff --git a/plugins/rfc/webcmd-plugin.json b/plugins/rfc/webcmd-plugin.json new file mode 100644 index 00000000..9c156097 --- /dev/null +++ b/plugins/rfc/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "rfc", + "version": "0.1.0", + "description": "Webcmd commands for rfc", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/rubygems/README.md b/plugins/rubygems/README.md new file mode 100644 index 00000000..3eb20d5b --- /dev/null +++ b/plugins/rubygems/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-rubygems + +Webcmd commands for rubygems. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/rubygems +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd rubygems gem` | Fetch a RubyGems.org gem's metadata (version, downloads, license, links) | +| `webcmd rubygems search` | Search RubyGems.org gems by keyword | diff --git a/clis/rubygems/gem.js b/plugins/rubygems/gem.js similarity index 100% rename from clis/rubygems/gem.js rename to plugins/rubygems/gem.js diff --git a/plugins/rubygems/package.json b/plugins/rubygems/package.json new file mode 100644 index 00000000..251bfb69 --- /dev/null +++ b/plugins/rubygems/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-rubygems", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for rubygems", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/rubygems/search.js b/plugins/rubygems/search.js similarity index 100% rename from clis/rubygems/search.js rename to plugins/rubygems/search.js diff --git a/clis/rubygems/utils.js b/plugins/rubygems/utils.js similarity index 100% rename from clis/rubygems/utils.js rename to plugins/rubygems/utils.js diff --git a/plugins/rubygems/webcmd-plugin.json b/plugins/rubygems/webcmd-plugin.json new file mode 100644 index 00000000..f09eba6e --- /dev/null +++ b/plugins/rubygems/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "rubygems", + "version": "0.1.0", + "description": "Webcmd commands for rubygems", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/semanticscholar/README.md b/plugins/semanticscholar/README.md new file mode 100644 index 00000000..faa6adfb --- /dev/null +++ b/plugins/semanticscholar/README.md @@ -0,0 +1,18 @@ +# webcmd-plugin-semanticscholar + +Webcmd commands for semanticscholar. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/semanticscholar +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd semanticscholar citations` | List papers that cite a Semantic Scholar paper (paginated) | +| `webcmd semanticscholar paper` | Semantic Scholar paper detail (citation graph + AI tldr) by paperId, DOI, or arXiv id | +| `webcmd semanticscholar recommendations` | Semantic Scholar AI-curated related papers for a paperId, DOI, or arXiv id | +| `webcmd semanticscholar search` | Search Semantic Scholar papers by free text | diff --git a/clis/semanticscholar/citations.js b/plugins/semanticscholar/citations.js similarity index 100% rename from clis/semanticscholar/citations.js rename to plugins/semanticscholar/citations.js diff --git a/plugins/semanticscholar/package.json b/plugins/semanticscholar/package.json new file mode 100644 index 00000000..bcd57b1f --- /dev/null +++ b/plugins/semanticscholar/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-semanticscholar", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for semanticscholar", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/semanticscholar/paper.js b/plugins/semanticscholar/paper.js similarity index 100% rename from clis/semanticscholar/paper.js rename to plugins/semanticscholar/paper.js diff --git a/clis/semanticscholar/recommendations.js b/plugins/semanticscholar/recommendations.js similarity index 100% rename from clis/semanticscholar/recommendations.js rename to plugins/semanticscholar/recommendations.js diff --git a/clis/semanticscholar/search.js b/plugins/semanticscholar/search.js similarity index 100% rename from clis/semanticscholar/search.js rename to plugins/semanticscholar/search.js diff --git a/clis/semanticscholar/semanticscholar.test.js b/plugins/semanticscholar/test/semanticscholar.test.js similarity index 99% rename from clis/semanticscholar/semanticscholar.test.js rename to plugins/semanticscholar/test/semanticscholar.test.js index 706faf13..71876aae 100644 --- a/clis/semanticscholar/semanticscholar.test.js +++ b/plugins/semanticscholar/test/semanticscholar.test.js @@ -1,10 +1,10 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './paper.js'; -import './citations.js'; -import './recommendations.js'; -import './search.js'; +import '../paper.js'; +import '../citations.js'; +import '../recommendations.js'; +import '../search.js'; function jsonResponse(body, status = 200) { return new Response(JSON.stringify(body), { diff --git a/clis/semanticscholar/utils.js b/plugins/semanticscholar/utils.js similarity index 100% rename from clis/semanticscholar/utils.js rename to plugins/semanticscholar/utils.js diff --git a/plugins/semanticscholar/webcmd-plugin.json b/plugins/semanticscholar/webcmd-plugin.json new file mode 100644 index 00000000..91587b09 --- /dev/null +++ b/plugins/semanticscholar/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "semanticscholar", + "version": "0.1.0", + "description": "Webcmd commands for semanticscholar", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/stackoverflow/README.md b/plugins/stackoverflow/README.md new file mode 100644 index 00000000..9f1192fd --- /dev/null +++ b/plugins/stackoverflow/README.md @@ -0,0 +1,22 @@ +# webcmd-plugin-stackoverflow + +Webcmd commands for stackoverflow. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/stackoverflow +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd stackoverflow bounties` | Active bounties on Stack Overflow | +| `webcmd stackoverflow hot` | Hot Stack Overflow questions | +| `webcmd stackoverflow read` | Read a Stack Overflow question with answers and comments | +| `webcmd stackoverflow related` | List Stack Overflow questions related to a given question id. | +| `webcmd stackoverflow search` | Search Stack Overflow questions | +| `webcmd stackoverflow tag` | List Stack Overflow questions tagged with a given tag (most active first). | +| `webcmd stackoverflow unanswered` | Top voted unanswered questions on Stack Overflow | +| `webcmd stackoverflow user` | Find Stack Overflow users by display name (highest reputation first). | diff --git a/clis/stackoverflow/bounties.js b/plugins/stackoverflow/bounties.js similarity index 100% rename from clis/stackoverflow/bounties.js rename to plugins/stackoverflow/bounties.js diff --git a/clis/stackoverflow/hot.js b/plugins/stackoverflow/hot.js similarity index 100% rename from clis/stackoverflow/hot.js rename to plugins/stackoverflow/hot.js diff --git a/plugins/stackoverflow/package.json b/plugins/stackoverflow/package.json new file mode 100644 index 00000000..49890f32 --- /dev/null +++ b/plugins/stackoverflow/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-stackoverflow", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for stackoverflow", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/stackoverflow/read.js b/plugins/stackoverflow/read.js similarity index 100% rename from clis/stackoverflow/read.js rename to plugins/stackoverflow/read.js diff --git a/clis/stackoverflow/related.js b/plugins/stackoverflow/related.js similarity index 100% rename from clis/stackoverflow/related.js rename to plugins/stackoverflow/related.js diff --git a/clis/stackoverflow/search.js b/plugins/stackoverflow/search.js similarity index 100% rename from clis/stackoverflow/search.js rename to plugins/stackoverflow/search.js diff --git a/clis/stackoverflow/tag.js b/plugins/stackoverflow/tag.js similarity index 100% rename from clis/stackoverflow/tag.js rename to plugins/stackoverflow/tag.js diff --git a/clis/stackoverflow/stackoverflow.test.js b/plugins/stackoverflow/test/stackoverflow.test.js similarity index 99% rename from clis/stackoverflow/stackoverflow.test.js rename to plugins/stackoverflow/test/stackoverflow.test.js index 9f42978c..2955d727 100644 --- a/clis/stackoverflow/stackoverflow.test.js +++ b/plugins/stackoverflow/test/stackoverflow.test.js @@ -1,13 +1,13 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './hot.js'; -import './search.js'; -import './unanswered.js'; -import './bounties.js'; -import './read.js'; -import './tag.js'; -import './user.js'; +import '../hot.js'; +import '../search.js'; +import '../unanswered.js'; +import '../bounties.js'; +import '../read.js'; +import '../tag.js'; +import '../user.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/stackoverflow/unanswered.js b/plugins/stackoverflow/unanswered.js similarity index 100% rename from clis/stackoverflow/unanswered.js rename to plugins/stackoverflow/unanswered.js diff --git a/clis/stackoverflow/user.js b/plugins/stackoverflow/user.js similarity index 100% rename from clis/stackoverflow/user.js rename to plugins/stackoverflow/user.js diff --git a/clis/stackoverflow/utils.js b/plugins/stackoverflow/utils.js similarity index 100% rename from clis/stackoverflow/utils.js rename to plugins/stackoverflow/utils.js diff --git a/plugins/stackoverflow/webcmd-plugin.json b/plugins/stackoverflow/webcmd-plugin.json new file mode 100644 index 00000000..5a92fd74 --- /dev/null +++ b/plugins/stackoverflow/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "stackoverflow", + "version": "0.1.0", + "description": "Webcmd commands for stackoverflow", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/steam/README.md b/plugins/steam/README.md new file mode 100644 index 00000000..5fe3252c --- /dev/null +++ b/plugins/steam/README.md @@ -0,0 +1,17 @@ +# webcmd-plugin-steam + +Webcmd commands for steam. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/steam +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd steam app` | Steam storefront detail for a single app id | +| `webcmd steam search` | Search the Steam storefront by name keyword | +| `webcmd steam top-sellers` | Steam top selling games | diff --git a/clis/steam/app.js b/plugins/steam/app.js similarity index 100% rename from clis/steam/app.js rename to plugins/steam/app.js diff --git a/plugins/steam/package.json b/plugins/steam/package.json new file mode 100644 index 00000000..c3f1f220 --- /dev/null +++ b/plugins/steam/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-steam", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for steam", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/steam/search.js b/plugins/steam/search.js similarity index 100% rename from clis/steam/search.js rename to plugins/steam/search.js diff --git a/clis/steam/steam.test.js b/plugins/steam/test/steam.test.js similarity index 94% rename from clis/steam/steam.test.js rename to plugins/steam/test/steam.test.js index f9593467..1c5d8b74 100644 --- a/clis/steam/steam.test.js +++ b/plugins/steam/test/steam.test.js @@ -1,9 +1,9 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError } from '@agentrhq/webcmd/errors'; -import { decodeHtmlEntities, requireCountryCode } from './utils.js'; -import './search.js'; -import './app.js'; +import { decodeHtmlEntities, requireCountryCode } from '../utils.js'; +import '../search.js'; +import '../app.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/steam/top-sellers.js b/plugins/steam/top-sellers.js similarity index 100% rename from clis/steam/top-sellers.js rename to plugins/steam/top-sellers.js diff --git a/clis/steam/utils.js b/plugins/steam/utils.js similarity index 100% rename from clis/steam/utils.js rename to plugins/steam/utils.js diff --git a/plugins/steam/webcmd-plugin.json b/plugins/steam/webcmd-plugin.json new file mode 100644 index 00000000..2f89676f --- /dev/null +++ b/plugins/steam/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "steam", + "version": "0.1.0", + "description": "Webcmd commands for steam", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/tvmaze/README.md b/plugins/tvmaze/README.md new file mode 100644 index 00000000..0ad824fd --- /dev/null +++ b/plugins/tvmaze/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-tvmaze + +Webcmd commands for tvmaze. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/tvmaze +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd tvmaze search` | TVmaze TV show search by title (returns id, name, network, premiered/ended, rating) | +| `webcmd tvmaze show` | Single TVmaze TV show detail by id (network, schedule, rating, IMDB/TheTVDB cross-refs) | diff --git a/plugins/tvmaze/package.json b/plugins/tvmaze/package.json new file mode 100644 index 00000000..8e209d88 --- /dev/null +++ b/plugins/tvmaze/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-tvmaze", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for tvmaze", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/tvmaze/search.js b/plugins/tvmaze/search.js similarity index 100% rename from clis/tvmaze/search.js rename to plugins/tvmaze/search.js diff --git a/clis/tvmaze/show.js b/plugins/tvmaze/show.js similarity index 100% rename from clis/tvmaze/show.js rename to plugins/tvmaze/show.js diff --git a/clis/tvmaze/tvmaze.test.js b/plugins/tvmaze/test/tvmaze.test.js similarity index 99% rename from clis/tvmaze/tvmaze.test.js rename to plugins/tvmaze/test/tvmaze.test.js index 6c148ebe..e68e49be 100644 --- a/clis/tvmaze/tvmaze.test.js +++ b/plugins/tvmaze/test/tvmaze.test.js @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './search.js'; -import './show.js'; +import '../search.js'; +import '../show.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/tvmaze/utils.js b/plugins/tvmaze/utils.js similarity index 100% rename from clis/tvmaze/utils.js rename to plugins/tvmaze/utils.js diff --git a/plugins/tvmaze/webcmd-plugin.json b/plugins/tvmaze/webcmd-plugin.json new file mode 100644 index 00000000..08722a28 --- /dev/null +++ b/plugins/tvmaze/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "tvmaze", + "version": "0.1.0", + "description": "Webcmd commands for tvmaze", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/wikidata/README.md b/plugins/wikidata/README.md new file mode 100644 index 00000000..0f8a8ea4 --- /dev/null +++ b/plugins/wikidata/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-wikidata + +Webcmd commands for wikidata. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/wikidata +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd wikidata entity` | Fetch a Wikidata entity by Q/P/L id (label, description, aliases, claim summary) | +| `webcmd wikidata search` | Search Wikidata items by keyword (returns Q-IDs) | diff --git a/clis/wikidata/entity.js b/plugins/wikidata/entity.js similarity index 100% rename from clis/wikidata/entity.js rename to plugins/wikidata/entity.js diff --git a/plugins/wikidata/package.json b/plugins/wikidata/package.json new file mode 100644 index 00000000..f79e8ade --- /dev/null +++ b/plugins/wikidata/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-wikidata", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for wikidata", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/wikidata/search.js b/plugins/wikidata/search.js similarity index 100% rename from clis/wikidata/search.js rename to plugins/wikidata/search.js diff --git a/clis/wikidata/wikidata.test.js b/plugins/wikidata/test/wikidata.test.js similarity index 98% rename from clis/wikidata/wikidata.test.js rename to plugins/wikidata/test/wikidata.test.js index 461e2442..6f136483 100644 --- a/clis/wikidata/wikidata.test.js +++ b/plugins/wikidata/test/wikidata.test.js @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './search.js'; -import './entity.js'; +import '../search.js'; +import '../entity.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/wikidata/utils.js b/plugins/wikidata/utils.js similarity index 100% rename from clis/wikidata/utils.js rename to plugins/wikidata/utils.js diff --git a/plugins/wikidata/webcmd-plugin.json b/plugins/wikidata/webcmd-plugin.json new file mode 100644 index 00000000..6f26e148 --- /dev/null +++ b/plugins/wikidata/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "wikidata", + "version": "0.1.0", + "description": "Webcmd commands for wikidata", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/wikipedia/README.md b/plugins/wikipedia/README.md new file mode 100644 index 00000000..02b4a212 --- /dev/null +++ b/plugins/wikipedia/README.md @@ -0,0 +1,19 @@ +# webcmd-plugin-wikipedia + +Webcmd commands for wikipedia. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/wikipedia +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd wikipedia page` | Full plain-text extract of a Wikipedia article (optional paragraph cap). | +| `webcmd wikipedia random` | Get a random Wikipedia article | +| `webcmd wikipedia search` | Search Wikipedia articles | +| `webcmd wikipedia summary` | Get Wikipedia article summary | +| `webcmd wikipedia trending` | Most-read Wikipedia articles (yesterday) | diff --git a/plugins/wikipedia/package.json b/plugins/wikipedia/package.json new file mode 100644 index 00000000..96663cb2 --- /dev/null +++ b/plugins/wikipedia/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-wikipedia", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for wikipedia", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/wikipedia/page.js b/plugins/wikipedia/page.js similarity index 100% rename from clis/wikipedia/page.js rename to plugins/wikipedia/page.js diff --git a/clis/wikipedia/random.js b/plugins/wikipedia/random.js similarity index 100% rename from clis/wikipedia/random.js rename to plugins/wikipedia/random.js diff --git a/clis/wikipedia/search.js b/plugins/wikipedia/search.js similarity index 100% rename from clis/wikipedia/search.js rename to plugins/wikipedia/search.js diff --git a/clis/wikipedia/summary.js b/plugins/wikipedia/summary.js similarity index 100% rename from clis/wikipedia/summary.js rename to plugins/wikipedia/summary.js diff --git a/clis/wikipedia/trending.test.js b/plugins/wikipedia/test/trending.test.js similarity index 94% rename from clis/wikipedia/trending.test.js rename to plugins/wikipedia/test/trending.test.js index e61dbf23..fd556720 100644 --- a/clis/wikipedia/trending.test.js +++ b/plugins/wikipedia/test/trending.test.js @@ -2,12 +2,12 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; const { wikiFetchMock } = vi.hoisted(() => ({ wikiFetchMock: vi.fn() })); -vi.mock('./utils.js', async () => { - const actual = await vi.importActual('./utils.js'); +vi.mock('../utils.js', async () => { + const actual = await vi.importActual('../utils.js'); return { ...actual, wikiFetch: wikiFetchMock }; }); -import './trending.js'; +import '../trending.js'; describe('wikipedia trending', () => { beforeEach(() => { diff --git a/clis/wikipedia/trending.js b/plugins/wikipedia/trending.js similarity index 100% rename from clis/wikipedia/trending.js rename to plugins/wikipedia/trending.js diff --git a/clis/wikipedia/utils.js b/plugins/wikipedia/utils.js similarity index 100% rename from clis/wikipedia/utils.js rename to plugins/wikipedia/utils.js diff --git a/plugins/wikipedia/webcmd-plugin.json b/plugins/wikipedia/webcmd-plugin.json new file mode 100644 index 00000000..ccd8264b --- /dev/null +++ b/plugins/wikipedia/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "wikipedia", + "version": "0.1.0", + "description": "Webcmd commands for wikipedia", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/wttr/README.md b/plugins/wttr/README.md new file mode 100644 index 00000000..4ed83852 --- /dev/null +++ b/plugins/wttr/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-wttr + +Webcmd commands for wttr. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/wttr +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd wttr current` | Current weather conditions for a location (city, lat,lon, or airport code) | +| `webcmd wttr forecast` | Multi-day weather forecast (up to 3 days, wttr.in free tier max) | diff --git a/clis/wttr/current.js b/plugins/wttr/current.js similarity index 100% rename from clis/wttr/current.js rename to plugins/wttr/current.js diff --git a/clis/wttr/forecast.js b/plugins/wttr/forecast.js similarity index 100% rename from clis/wttr/forecast.js rename to plugins/wttr/forecast.js diff --git a/plugins/wttr/package.json b/plugins/wttr/package.json new file mode 100644 index 00000000..0a99bfb4 --- /dev/null +++ b/plugins/wttr/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-wttr", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for wttr", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/wttr/wttr.test.js b/plugins/wttr/test/wttr.test.js similarity index 98% rename from clis/wttr/wttr.test.js rename to plugins/wttr/test/wttr.test.js index 41ca7df0..4cc0132c 100644 --- a/clis/wttr/wttr.test.js +++ b/plugins/wttr/test/wttr.test.js @@ -1,8 +1,8 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './current.js'; -import './forecast.js'; +import '../current.js'; +import '../forecast.js'; const origFetch = global.fetch; afterEach(() => { global.fetch = origFetch; }); diff --git a/clis/wttr/utils.js b/plugins/wttr/utils.js similarity index 100% rename from clis/wttr/utils.js rename to plugins/wttr/utils.js diff --git a/plugins/wttr/webcmd-plugin.json b/plugins/wttr/webcmd-plugin.json new file mode 100644 index 00000000..8793ee39 --- /dev/null +++ b/plugins/wttr/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "wttr", + "version": "0.1.0", + "description": "Webcmd commands for wttr", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/scripts/typed-error-lint-baseline.json b/scripts/typed-error-lint-baseline.json index ed3234d3..c54bd7cf 100644 --- a/scripts/typed-error-lint-baseline.json +++ b/scripts/typed-error-lint-baseline.json @@ -306,7 +306,7 @@ { "rule": "silent-clamp", "command": "stackoverflow/read", - "file": "clis/stackoverflow/read.js", + "file": "plugins/stackoverflow/read.js", "line": 125, "text": "const pageSize = Math.min(SE_MAX_PAGE_SIZE, answers.length * commentsLimit);", "occurrence": 0 @@ -378,7 +378,7 @@ { "rule": "silent-clamp", "command": "wikipedia/search", - "file": "clis/wikipedia/search.js", + "file": "plugins/wikipedia/search.js", "line": 19, "text": "const limit = Math.max(1, Math.min(Number(args.limit), 50));", "occurrence": 0 @@ -386,7 +386,7 @@ { "rule": "silent-clamp", "command": "wikipedia/trending", - "file": "clis/wikipedia/trending.js", + "file": "plugins/wikipedia/trending.js", "line": 18, "text": "const limit = Math.max(1, Math.min(Number(args.limit), 50));", "occurrence": 0 diff --git a/webcmd-plugin.json b/webcmd-plugin.json index c741e6e0..dcec0f9a 100644 --- a/webcmd-plugin.json +++ b/webcmd-plugin.json @@ -424,6 +424,36 @@ "handle": "agentrhq" } }, + "osv": { + "path": "plugins/osv", + "version": "0.1.0", + "description": "Webcmd commands for osv", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "packagist": { + "path": "plugins/packagist", + "version": "0.1.0", + "description": "Webcmd commands for packagist", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "pubmed": { + "path": "plugins/pubmed", + "version": "0.1.0", + "description": "Webcmd commands for pubmed", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "pypi": { "path": "plugins/pypi", "version": "0.1.0", @@ -434,6 +464,46 @@ "handle": "yoldaolmak" } }, + "rest-countries": { + "path": "plugins/rest-countries", + "version": "0.1.0", + "description": "Webcmd commands for rest-countries", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "rfc": { + "path": "plugins/rfc", + "version": "0.1.0", + "description": "Webcmd commands for rfc", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "rubygems": { + "path": "plugins/rubygems", + "version": "0.1.0", + "description": "Webcmd commands for rubygems", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "semanticscholar": { + "path": "plugins/semanticscholar", + "version": "0.1.0", + "description": "Webcmd commands for semanticscholar", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "skyscanner": { "path": "plugins/skyscanner", "version": "0.1.0", @@ -444,6 +514,26 @@ "handle": "rishabhraj36" } }, + "stackoverflow": { + "path": "plugins/stackoverflow", + "version": "0.1.0", + "description": "Webcmd commands for stackoverflow", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "steam": { + "path": "plugins/steam", + "version": "0.1.0", + "description": "Webcmd commands for steam", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "techcrunch": { "path": "plugins/techcrunch", "version": "0.1.0", @@ -454,6 +544,16 @@ "handle": "agentrhq" } }, + "tvmaze": { + "path": "plugins/tvmaze", + "version": "0.1.0", + "description": "Webcmd commands for tvmaze", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "ualberta": { "path": "plugins/ualberta", "version": "0.1.0", @@ -464,6 +564,36 @@ "handle": "agentrhq" } }, + "wikidata": { + "path": "plugins/wikidata", + "version": "0.1.0", + "description": "Webcmd commands for wikidata", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "wikipedia": { + "path": "plugins/wikipedia", + "version": "0.1.0", + "description": "Webcmd commands for wikipedia", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "wttr": { + "path": "plugins/wttr", + "version": "0.1.0", + "description": "Webcmd commands for wttr", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "yale": { "path": "plugins/yale", "version": "0.1.0", From f2b520e6583cde5b4d023f08f9c5945cfaecb183 Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 17:02:12 +0530 Subject: [PATCH 13/39] refactor: consolidate PyPI commands in plugin --- README.md | 2 +- cli-manifest.json | 71 --------------- clis/pypi/package.js | 79 ----------------- clis/pypi/utils.js | 55 ------------ plugin-command-manifest.json | 54 ++++++++++-- plugins/pypi/README.md | 7 +- {clis => plugins}/pypi/downloads.js | 18 ++-- plugins/pypi/lib/api.js | 129 ---------------------------- plugins/pypi/package.js | 69 +++++++++++++-- plugins/pypi/package.json | 2 +- plugins/pypi/releases.js | 2 +- plugins/pypi/test/pypi.test.js | 81 +++++++++++------ plugins/pypi/utils.js | 97 +++++++++++++++++++++ plugins/pypi/webcmd-plugin.json | 2 +- webcmd-plugin.json | 2 +- 15 files changed, 275 insertions(+), 395 deletions(-) delete mode 100644 clis/pypi/package.js delete mode 100644 clis/pypi/utils.js rename {clis => plugins}/pypi/downloads.js (84%) delete mode 100644 plugins/pypi/lib/api.js create mode 100644 plugins/pypi/utils.js diff --git a/README.md b/README.md index f67647bc..57e30797 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ Webcmd Cloud can run supported commands and browser sessions on hosted infrastru | Plugin | Description | Author | | --- | --- | --- | -| [`pypi`](./plugins/pypi/) | Inspect public Python package metadata and releases from PyPI | [Kemal Kaya](https://github.com/yoldaolmak) | +| [`pypi`](./plugins/pypi/) | Inspect public Python package metadata, downloads, and releases from PyPI | [Kemal Kaya](https://github.com/yoldaolmak) | | [`skyscanner`](./plugins/skyscanner/) | Skyscanner flight search commands for Webcmd | [Rishabh](https://github.com/rishabhraj36) | diff --git a/cli-manifest.json b/cli-manifest.json index df0a4b6b..8c6f143c 100644 --- a/cli-manifest.json +++ b/cli-manifest.json @@ -12097,77 +12097,6 @@ "modulePath": "producthunt/today.js", "sourceFile": "producthunt/today.js" }, - { - "site": "pypi", - "name": "downloads", - "description": "PyPI download stats for a package (recent totals or full daily history)", - "access": "read", - "domain": "pypistats.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "PyPI package name (e.g. \"requests\", \"pandas\")" - }, - { - "name": "period", - "type": "str", - "default": "recent", - "required": false, - "help": "recent (default — 1 row, last day/week/month) or overall (1 row per day)" - } - ], - "columns": [ - "rank", - "package", - "period", - "date", - "downloads" - ], - "type": "js", - "modulePath": "pypi/downloads.js", - "sourceFile": "pypi/downloads.js" - }, - { - "site": "pypi", - "name": "package", - "description": "Single PyPI package metadata (latest version, license, homepage, classifiers)", - "access": "read", - "domain": "pypi.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "PyPI package name (e.g. \"requests\", \"pandas\")" - } - ], - "columns": [ - "name", - "latestVersion", - "summary", - "author", - "license", - "homepage", - "repository", - "requiresPython", - "keywords", - "releases", - "firstReleased", - "lastReleased", - "url" - ], - "type": "js", - "modulePath": "pypi/package.js", - "sourceFile": "pypi/package.js" - }, { "site": "qoder", "name": "account", diff --git a/clis/pypi/package.js b/clis/pypi/package.js deleted file mode 100644 index 8b9412aa..00000000 --- a/clis/pypi/package.js +++ /dev/null @@ -1,79 +0,0 @@ -// pypi package — fetch a single PyPI package's metadata. -// -// Hits `https://pypi.org/pypi//json`. Returns the most agent-useful -// projection: name, latest version, summary, author, license, homepage, -// project URLs, requires-python, last-modified time. Download stats are -// intentionally separate (see `pypi downloads`). -import { cli, Strategy } from '@agentrhq/webcmd/registry'; -import { EmptyResultError } from '@agentrhq/webcmd/errors'; -import { PYPI_BASE, pypiFetch, requirePackageName } from './utils.js'; - -function pickHomepage(info) { - if (info.home_page) return String(info.home_page); - const proj = info.project_urls; - if (proj && typeof proj === 'object') { - return String(proj.Homepage || proj.homepage || proj.Documentation || proj.Source || proj['Source Code'] || ''); - } - return ''; -} - -function pickRepository(info) { - const proj = info.project_urls; - if (proj && typeof proj === 'object') { - return String(proj.Source || proj['Source Code'] || proj.Repository || proj.repository || ''); - } - return ''; -} - -cli({ - site: 'pypi', - name: 'package', - access: 'read', - description: 'Single PyPI package metadata (latest version, license, homepage, classifiers)', - domain: 'pypi.org', - strategy: Strategy.PUBLIC, - browser: false, - args: [ - { name: 'name', positional: true, required: true, help: 'PyPI package name (e.g. "requests", "pandas")' }, - ], - columns: [ - 'name', 'latestVersion', 'summary', 'author', 'license', 'homepage', 'repository', - 'requiresPython', 'keywords', 'releases', 'firstReleased', 'lastReleased', 'url', - ], - func: async (args) => { - const name = requirePackageName(args.name); - const body = await pypiFetch(`${PYPI_BASE}/pypi/${encodeURIComponent(name)}/json`, `pypi package ${name}`); - const info = body?.info; - if (!info || !info.name) { - throw new EmptyResultError('pypi package', `PyPI returned no metadata for "${name}".`); - } - const releases = body?.releases ?? {}; - const releaseVersions = Object.keys(releases).filter((v) => Array.isArray(releases[v]) && releases[v].length > 0); - // earliest / latest release timestamps from the upload_time fields - let firstReleased = ''; - let lastReleased = ''; - for (const v of releaseVersions) { - for (const file of releases[v]) { - const t = String(file?.upload_time ?? '').slice(0, 10); - if (!t) continue; - if (!firstReleased || t < firstReleased) firstReleased = t; - if (!lastReleased || t > lastReleased) lastReleased = t; - } - } - return [{ - name: String(info.name), - latestVersion: String(info.version ?? ''), - summary: String(info.summary ?? ''), - author: String(info.author ?? info.author_email ?? ''), - license: String(info.license_expression ?? info.license ?? ''), - homepage: pickHomepage(info), - repository: pickRepository(info), - requiresPython: String(info.requires_python ?? ''), - keywords: String(info.keywords ?? ''), - releases: releaseVersions.length, - firstReleased, - lastReleased, - url: String(info.package_url ?? `${PYPI_BASE}/project/${info.name}/`), - }]; - }, -}); diff --git a/clis/pypi/utils.js b/clis/pypi/utils.js deleted file mode 100644 index 4d740786..00000000 --- a/clis/pypi/utils.js +++ /dev/null @@ -1,55 +0,0 @@ -// Shared helpers for the pypi adapters that hit the PyPI public JSON API -// (pypi.org/pypi//json) and pypistats.org for download stats. -import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; - -export const PYPI_BASE = 'https://pypi.org'; -export const PYPISTATS_BASE = 'https://pypistats.org'; -const UA = 'webcmd-pypi-adapter (+https://github.com/agentrhq/webcmd)'; - -// PEP 508 / PEP 426 normalized name: letters, digits, "._-", with leading-letter rule relaxed by PyPI. -const PKG_NAME = /^[A-Za-z0-9]([A-Za-z0-9._-]*[A-Za-z0-9])?$/; - -export function requirePackageName(value) { - const s = String(value ?? '').trim(); - if (!s) throw new ArgumentError('pypi package name is required (e.g. "requests", "pandas")'); - if (!PKG_NAME.test(s)) { - throw new ArgumentError( - `pypi package name "${value}" is not a valid distribution name`, - 'PyPI accepts ASCII letters / digits / "._-" with no leading or trailing separator.', - ); - } - return s; -} - -export async function pypiFetch(url, label) { - let resp; - try { - resp = await fetch(url, { headers: { 'user-agent': UA, accept: 'application/json' } }); - } - catch (err) { - throw new CommandExecutionError( - `${label} request failed: ${err?.message ?? err}`, - 'Check that pypi.org / pypistats.org are reachable from this network.', - ); - } - if (resp.status === 404) { - throw new EmptyResultError(label, `PyPI returned 404 for ${url}.`); - } - if (resp.status === 429) { - throw new CommandExecutionError( - `${label} returned HTTP 429 (rate limited)`, - 'PyPI throttles unauthenticated bursts; wait a few seconds and retry.', - ); - } - if (!resp.ok) { - throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`); - } - let body; - try { - body = await resp.json(); - } - catch (err) { - throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`); - } - return body; -} diff --git a/plugin-command-manifest.json b/plugin-command-manifest.json index c97c93d4..b5d8f4d5 100644 --- a/plugin-command-manifest.json +++ b/plugin-command-manifest.json @@ -7310,10 +7310,45 @@ "modulePath": "plugins/pubmed/search.js", "sourceFile": "plugins/pubmed/search.js" }, + { + "site": "pypi", + "name": "downloads", + "description": "PyPI download stats for a package (recent totals or full daily history)", + "access": "read", + "domain": "pypistats.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "PyPI package name (e.g. \"requests\", \"pandas\")" + }, + { + "name": "period", + "type": "str", + "default": "recent", + "required": false, + "help": "recent (default — 1 row, last day/week/month) or overall (1 row per day)" + } + ], + "columns": [ + "rank", + "package", + "period", + "date", + "downloads" + ], + "type": "js", + "modulePath": "plugins/pypi/downloads.js", + "sourceFile": "plugins/pypi/downloads.js" + }, { "site": "pypi", "name": "package", - "description": "Inspect public PyPI package metadata", + "description": "Single PyPI package metadata (latest version, license, homepage, classifiers)", "access": "read", "domain": "pypi.org", "strategy": "public", @@ -7321,23 +7356,26 @@ "args": [ { "name": "name", - "type": "string", + "type": "str", "required": true, "positional": true, - "help": "Python package name, for example django" + "help": "PyPI package name (e.g. \"requests\", \"pandas\")" } ], "columns": [ "name", - "version", + "latestVersion", "summary", "author", "license", - "requiresPython", - "uploadedAt", - "projectUrl", "homepage", - "repository" + "repository", + "requiresPython", + "keywords", + "releases", + "firstReleased", + "lastReleased", + "url" ], "type": "js", "modulePath": "plugins/pypi/package.js", diff --git a/plugins/pypi/README.md b/plugins/pypi/README.md index d4fbb0d5..ef7b40ee 100644 --- a/plugins/pypi/README.md +++ b/plugins/pypi/README.md @@ -1,7 +1,7 @@ # webcmd-plugin-pypi -Inspect public Python package metadata and releases from PyPI. No login or API -key is required. +Inspect public Python package metadata, downloads, and releases. No login or +API key is required. ## Install @@ -14,12 +14,15 @@ webcmd plugin install github:agentrhq/webcmd/plugins/pypi | Command | Description | | --- | --- | | `webcmd pypi package ` | Show current project metadata for a package | +| `webcmd pypi downloads ` | Show recent or daily download counts for a package | | `webcmd pypi releases ` | List recent release files for a package | ## Examples ```bash webcmd pypi package django +webcmd pypi downloads django +webcmd pypi downloads django --period overall webcmd pypi releases pictovap --limit 5 ``` diff --git a/clis/pypi/downloads.js b/plugins/pypi/downloads.js similarity index 84% rename from clis/pypi/downloads.js rename to plugins/pypi/downloads.js index ef5d1c56..6c5cc957 100644 --- a/clis/pypi/downloads.js +++ b/plugins/pypi/downloads.js @@ -1,10 +1,4 @@ -// pypi downloads — fetch download counts for a single PyPI package via -// pypistats.org's public JSON API. -// -// Default endpoint is `/api/packages//recent` which returns last-day / -// last-week / last-month totals as a single row. Pass `--period overall` to -// hit `/api/packages//overall` for the full daily history (one row per -// day). +// pypi downloads — fetch package download counts from pypistats.org. import { cli, Strategy } from '@agentrhq/webcmd/registry'; import { ArgumentError, EmptyResultError } from '@agentrhq/webcmd/errors'; import { PYPISTATS_BASE, pypiFetch, requirePackageName } from './utils.js'; @@ -12,14 +6,14 @@ import { PYPISTATS_BASE, pypiFetch, requirePackageName } from './utils.js'; const PERIODS = new Set(['recent', 'overall']); function requirePeriod(value) { - const s = String(value ?? 'recent').trim().toLowerCase(); - if (!PERIODS.has(s)) { + const period = String(value ?? 'recent').trim().toLowerCase(); + if (!PERIODS.has(period)) { throw new ArgumentError( `pypi downloads period "${value}" is invalid`, 'Allowed values: recent (default — last day/week/month totals) or overall (full daily history).', ); } - return s; + return period; } cli({ @@ -55,8 +49,8 @@ cli({ if (!days.length) { throw new EmptyResultError('pypi downloads', `pypistats has no overall download history for "${name}".`); } - return days.map((row, i) => ({ - rank: i + 1, + return days.map((row, index) => ({ + rank: index + 1, package: String(body.package ?? name), period: 'daily', date: String(row.date ?? ''), diff --git a/plugins/pypi/lib/api.js b/plugins/pypi/lib/api.js deleted file mode 100644 index 7a67810f..00000000 --- a/plugins/pypi/lib/api.js +++ /dev/null @@ -1,129 +0,0 @@ -import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; - -const PYPI_BASE_URL = 'https://pypi.org/pypi'; -const PACKAGE_URL_BASE = 'https://pypi.org/project'; -const MAX_RELEASE_LIMIT = 50; - -export function normalizePackageName(raw) { - const name = String(raw ?? '').trim(); - if (!name) { - throw new ArgumentError('package name is required'); - } - if (name.length > 214 || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name)) { - throw new ArgumentError('package name must contain only letters, numbers, dots, underscores, and hyphens'); - } - return name; -} - -export function parseLimit(raw, fallback = 10) { - const value = raw === undefined || raw === null || raw === '' ? fallback : Number(raw); - if (!Number.isInteger(value) || value < 1 || value > MAX_RELEASE_LIMIT) { - throw new ArgumentError(`--limit must be an integer between 1 and ${MAX_RELEASE_LIMIT}`); - } - return value; -} - -export async function fetchPackageJson(name, request = fetch) { - const packageName = normalizePackageName(name); - const url = `${PYPI_BASE_URL}/${encodeURIComponent(packageName)}/json`; - - let response; - try { - response = await request(url, { - headers: { - Accept: 'application/json', - 'User-Agent': 'webcmd/0.4 (+https://github.com/agentrhq/webcmd)', - }, - }); - } catch (error) { - throw new CommandExecutionError(`PyPI request failed: ${error.message}`); - } - - if (response.status === 404) { - throw new EmptyResultError('pypi package', `PyPI has no project named "${packageName}".`); - } - if (!response.ok) { - throw new CommandExecutionError(`PyPI request failed with HTTP ${response.status}`); - } - - let payload; - try { - payload = await response.json(); - } catch (error) { - throw new CommandExecutionError(`PyPI returned malformed JSON: ${error.message}`); - } - if (!payload || typeof payload !== 'object' || !payload.info) { - throw new CommandExecutionError('PyPI returned an unexpected response.'); - } - return payload; -} - -function projectUrl(info, label) { - const urls = info?.project_urls; - if (!urls || typeof urls !== 'object') return null; - const match = Object.entries(urls).find(([name]) => name.toLowerCase() === label); - return match ? match[1] : null; -} - -function releaseFiles(payload, version) { - const releases = payload?.releases; - const files = releases && typeof releases === 'object' ? releases[version] : []; - return Array.isArray(files) ? files : []; -} - -function latestUploadTime(files) { - return files - .map(file => file?.upload_time_iso_8601 || file?.upload_time || null) - .filter(Boolean) - .sort() - .at(-1) || null; -} - -export function summarizePackage(payload) { - const info = payload.info || {}; - const name = String(info.name || '').trim(); - const version = String(info.version || '').trim(); - if (!name || !version) { - throw new CommandExecutionError('PyPI package metadata is missing a name or version.'); - } - - const files = releaseFiles(payload, version); - return [{ - name, - version, - summary: info.summary || null, - author: info.author || null, - license: info.license || null, - requiresPython: info.requires_python || null, - uploadedAt: latestUploadTime(files), - projectUrl: `${PACKAGE_URL_BASE}/${encodeURIComponent(name)}/`, - homepage: projectUrl(info, 'homepage') || info.home_page || null, - repository: projectUrl(info, 'repository') || projectUrl(info, 'source') || null, - }]; -} - -export function summarizeReleases(payload, limit) { - const info = payload.info || {}; - const name = String(info.name || '').trim(); - const releases = payload.releases && typeof payload.releases === 'object' ? payload.releases : {}; - const rows = Object.entries(releases) - .map(([version, files]) => { - const releaseFiles = Array.isArray(files) ? files : []; - return { - version, - uploadedAt: latestUploadTime(releaseFiles), - fileCount: releaseFiles.length, - pythonVersions: [...new Set(releaseFiles.map(file => file?.python_version).filter(Boolean))].join(', ') || null, - yanked: releaseFiles.length > 0 && releaseFiles.every(file => file?.yanked === true), - url: `${PACKAGE_URL_BASE}/${encodeURIComponent(name)}/${encodeURIComponent(version)}/`, - }; - }) - .filter(row => row.uploadedAt) - .sort((a, b) => String(b.uploadedAt).localeCompare(String(a.uploadedAt))) - .slice(0, limit); - - if (!rows.length) { - throw new EmptyResultError('pypi releases', `PyPI returned no release files for "${name}".`); - } - return rows; -} diff --git a/plugins/pypi/package.js b/plugins/pypi/package.js index 19198ba1..95b6605d 100644 --- a/plugins/pypi/package.js +++ b/plugins/pypi/package.js @@ -1,23 +1,76 @@ +// pypi package — fetch a single PyPI package's metadata. +// +// Hits `https://pypi.org/pypi//json`. Returns the most agent-useful +// projection. Download stats are intentionally separate (see `pypi downloads`). import { cli, Strategy } from '@agentrhq/webcmd/registry'; +import { EmptyResultError } from '@agentrhq/webcmd/errors'; +import { PYPI_BASE, pypiFetch, requirePackageName } from './utils.js'; -import { fetchPackageJson, summarizePackage } from './lib/api.js'; +function pickHomepage(info) { + if (info.home_page) return String(info.home_page); + const proj = info.project_urls; + if (proj && typeof proj === 'object') { + return String(proj.Homepage || proj.homepage || proj.Documentation || proj.Source || proj['Source Code'] || ''); + } + return ''; +} -export async function packagePyPI(args, request = fetch) { - const payload = await fetchPackageJson(args.name, request); - return summarizePackage(payload); +function pickRepository(info) { + const proj = info.project_urls; + if (proj && typeof proj === 'object') { + return String(proj.Source || proj['Source Code'] || proj.Repository || proj.repository || ''); + } + return ''; } cli({ site: 'pypi', name: 'package', access: 'read', - description: 'Inspect public PyPI package metadata', + description: 'Single PyPI package metadata (latest version, license, homepage, classifiers)', domain: 'pypi.org', strategy: Strategy.PUBLIC, browser: false, args: [ - { name: 'name', positional: true, required: true, type: 'string', help: 'Python package name, for example django' }, + { name: 'name', positional: true, required: true, help: 'PyPI package name (e.g. "requests", "pandas")' }, + ], + columns: [ + 'name', 'latestVersion', 'summary', 'author', 'license', 'homepage', 'repository', + 'requiresPython', 'keywords', 'releases', 'firstReleased', 'lastReleased', 'url', ], - columns: ['name', 'version', 'summary', 'author', 'license', 'requiresPython', 'uploadedAt', 'projectUrl', 'homepage', 'repository'], - func: args => packagePyPI(args), + func: async (args) => { + const name = requirePackageName(args.name); + const body = await pypiFetch(`${PYPI_BASE}/pypi/${encodeURIComponent(name)}/json`, `pypi package ${name}`); + const info = body?.info; + if (!info || !info.name) { + throw new EmptyResultError('pypi package', `PyPI returned no metadata for "${name}".`); + } + const releases = body?.releases ?? {}; + const releaseVersions = Object.keys(releases).filter(v => Array.isArray(releases[v]) && releases[v].length > 0); + let firstReleased = ''; + let lastReleased = ''; + for (const version of releaseVersions) { + for (const file of releases[version]) { + const time = String(file?.upload_time ?? '').slice(0, 10); + if (!time) continue; + if (!firstReleased || time < firstReleased) firstReleased = time; + if (!lastReleased || time > lastReleased) lastReleased = time; + } + } + return [{ + name: String(info.name), + latestVersion: String(info.version ?? ''), + summary: String(info.summary ?? ''), + author: String(info.author ?? info.author_email ?? ''), + license: String(info.license_expression ?? info.license ?? ''), + homepage: pickHomepage(info), + repository: pickRepository(info), + requiresPython: String(info.requires_python ?? ''), + keywords: String(info.keywords ?? ''), + releases: releaseVersions.length, + firstReleased, + lastReleased, + url: String(info.package_url ?? `${PYPI_BASE}/project/${info.name}/`), + }]; + }, }); diff --git a/plugins/pypi/package.json b/plugins/pypi/package.json index 05b397c7..18baf460 100644 --- a/plugins/pypi/package.json +++ b/plugins/pypi/package.json @@ -2,7 +2,7 @@ "name": "webcmd-plugin-pypi", "version": "0.1.0", "type": "module", - "description": "Inspect public Python package metadata and releases from PyPI", + "description": "Inspect public Python package metadata, downloads, and releases from PyPI", "peerDependencies": { "@agentrhq/webcmd": ">=0.2.0" } diff --git a/plugins/pypi/releases.js b/plugins/pypi/releases.js index 41077a0d..4e746472 100644 --- a/plugins/pypi/releases.js +++ b/plugins/pypi/releases.js @@ -1,6 +1,6 @@ import { cli, Strategy } from '@agentrhq/webcmd/registry'; -import { fetchPackageJson, parseLimit, summarizeReleases } from './lib/api.js'; +import { fetchPackageJson, parseLimit, summarizeReleases } from './utils.js'; export async function releasesPyPI(args, request = fetch) { const payload = await fetchPackageJson(args.name, request); diff --git a/plugins/pypi/test/pypi.test.js b/plugins/pypi/test/pypi.test.js index 5fb3340a..63ec8aff 100644 --- a/plugins/pypi/test/pypi.test.js +++ b/plugins/pypi/test/pypi.test.js @@ -28,10 +28,11 @@ afterAll(() => { } }); -const [{ getRegistry }, { packagePyPI }, { releasesPyPI }] = await Promise.all([ - import('@agentrhq/webcmd/registry'), - import('../package.js'), +const { getRegistry } = await import('@agentrhq/webcmd/registry'); +const [{ releasesPyPI }] = await Promise.all([ import('../releases.js'), + import('../package.js'), + import('../downloads.js'), ]); const payload = { @@ -42,6 +43,8 @@ const payload = { author: 'Kemal Kaya', license: 'MIT', requires_python: '>=3.10', + keywords: 'images,publishing', + package_url: 'https://pypi.org/project/pictovap/', home_page: 'https://github.com/yoldaolmak/Pictovap', project_urls: { Homepage: 'https://github.com/yoldaolmak/Pictovap', @@ -52,11 +55,13 @@ const payload = { '0.7.14': [ { upload_time_iso_8601: '2026-07-26T06:12:00.000Z', + upload_time: '2026-07-26T06:12:00', python_version: 'py3', yanked: false, }, { upload_time_iso_8601: '2026-07-26T06:13:00.000Z', + upload_time: '2026-07-26T06:13:00', python_version: 'source', yanked: false, }, @@ -64,6 +69,7 @@ const payload = { '0.7.13': [ { upload_time_iso_8601: '2026-07-26T05:22:00.000Z', + upload_time: '2026-07-26T05:22:00', python_version: 'py3', yanked: false, }, @@ -86,23 +92,31 @@ function fakeRequest(responsePayload = payload, { ok = true, status = 200 } = {} test('package returns public PyPI project metadata', async () => { const request = fakeRequest(); + const originalFetch = globalThis.fetch; + try { + globalThis.fetch = request; + const rows = await getRegistry().get('pypi/package').func({ name: 'pictovap' }, false); - const rows = await packagePyPI({ name: 'pictovap' }, request); - - assert.deepEqual(rows, [{ - name: 'pictovap', - version: '0.7.14', - summary: 'Visual finishing engine for publishers', - author: 'Kemal Kaya', - license: 'MIT', - requiresPython: '>=3.10', - uploadedAt: '2026-07-26T06:13:00.000Z', - projectUrl: 'https://pypi.org/project/pictovap/', - homepage: 'https://github.com/yoldaolmak/Pictovap', - repository: 'https://github.com/yoldaolmak/Pictovap', - }]); - assert.equal(request.calls[0].url, 'https://pypi.org/pypi/pictovap/json'); - assert.match(request.calls[0].options.headers['User-Agent'], /^webcmd\//); + assert.deepEqual(rows, [{ + name: 'pictovap', + latestVersion: '0.7.14', + summary: 'Visual finishing engine for publishers', + author: 'Kemal Kaya', + license: 'MIT', + homepage: 'https://github.com/yoldaolmak/Pictovap', + repository: 'https://github.com/yoldaolmak/Pictovap', + requiresPython: '>=3.10', + keywords: 'images,publishing', + releases: 2, + firstReleased: '2026-07-26', + lastReleased: '2026-07-26', + url: 'https://pypi.org/project/pictovap/', + }]); + assert.equal(request.calls[0].url, 'https://pypi.org/pypi/pictovap/json'); + assert.match(request.calls[0].options.headers['user-agent'], /webcmd/); + } finally { + globalThis.fetch = originalFetch; + } }); test('releases returns recent release rows newest first', async () => { @@ -130,7 +144,7 @@ test('releases returns recent release rows newest first', async () => { test('rejects invalid package names and limits', async () => { await assert.rejects( - () => packagePyPI({ name: '../secret' }, fakeRequest()), + () => getRegistry().get('pypi/package').func({ name: '../secret' }, false), /package name/, ); await assert.rejects( @@ -140,26 +154,41 @@ test('rejects invalid package names and limits', async () => { }); test('reports missing packages as empty results', async () => { - await assert.rejects( - () => packagePyPI({ name: 'missing-package' }, fakeRequest({}, { ok: false, status: 404 })), - error => error.code === 'EMPTY_RESULT' - && /no project named/.test(error.hint), - ); + const originalFetch = globalThis.fetch; + try { + globalThis.fetch = fakeRequest({}, { ok: false, status: 404 }); + await assert.rejects( + () => getRegistry().get('pypi/package').func({ name: 'missing-package' }, false), + error => error.code === 'EMPTY_RESULT' && /returned 404/.test(error.hint), + ); + } finally { + globalThis.fetch = originalFetch; + } }); test('registered handlers do not require a browser', async () => { const originalFetch = globalThis.fetch; try { - globalThis.fetch = fakeRequest(); + globalThis.fetch = async url => String(url).includes('pypistats.org') + ? { ok: true, status: 200, json: async () => ({ package: 'pictovap', data: { last_day: 1, last_week: 7, last_month: 30 } }) } + : { ok: true, status: 200, json: async () => payload }; const registry = getRegistry(); const packageCommand = registry.get('pypi/package'); + const downloadsCommand = registry.get('pypi/downloads'); const releasesCommand = registry.get('pypi/releases'); assert.ok(packageCommand?.func); + assert.ok(downloadsCommand?.func); assert.ok(releasesCommand?.func); assert.equal(packageCommand.browser, false); + assert.equal(downloadsCommand.browser, false); assert.equal(releasesCommand.browser, false); await packageCommand.func({ name: 'pictovap' }, false); + assert.deepEqual(await downloadsCommand.func({ name: 'pictovap', period: 'recent' }, false), [ + { rank: 1, package: 'pictovap', period: 'last_day', date: '', downloads: 1 }, + { rank: 2, package: 'pictovap', period: 'last_week', date: '', downloads: 7 }, + { rank: 3, package: 'pictovap', period: 'last_month', date: '', downloads: 30 }, + ]); await releasesCommand.func({ name: 'pictovap', limit: 1 }, false); } finally { globalThis.fetch = originalFetch; diff --git a/plugins/pypi/utils.js b/plugins/pypi/utils.js new file mode 100644 index 00000000..dac8e7fd --- /dev/null +++ b/plugins/pypi/utils.js @@ -0,0 +1,97 @@ +import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; + +export const PYPI_BASE = 'https://pypi.org'; +export const PYPISTATS_BASE = 'https://pypistats.org'; +const PACKAGE_URL_BASE = `${PYPI_BASE}/project`; +const MAX_RELEASE_LIMIT = 50; +const USER_AGENT = 'webcmd-pypi-adapter (+https://github.com/agentrhq/webcmd)'; +const PACKAGE_NAME = /^[A-Za-z0-9]([A-Za-z0-9._-]*[A-Za-z0-9])?$/; + +export function requirePackageName(value) { + const name = String(value ?? '').trim(); + if (!name) throw new ArgumentError('pypi package name is required (e.g. "requests", "pandas")'); + if (name.length > 214 || !PACKAGE_NAME.test(name)) { + throw new ArgumentError( + `pypi package name "${value}" is not a valid distribution name`, + 'PyPI accepts ASCII letters / digits / "._-" with no leading or trailing separator.', + ); + } + return name; +} + +export async function pypiFetch(url, label, request = fetch) { + let response; + try { + response = await request(url, { headers: { 'user-agent': USER_AGENT, accept: 'application/json' } }); + } catch (error) { + throw new CommandExecutionError( + `${label} request failed: ${error?.message ?? error}`, + 'Check that pypi.org / pypistats.org are reachable from this network.', + ); + } + if (response.status === 404) { + throw new EmptyResultError(label, `PyPI returned 404 for ${url}.`); + } + if (response.status === 429) { + throw new CommandExecutionError( + `${label} returned HTTP 429 (rate limited)`, + 'PyPI throttles unauthenticated bursts; wait a few seconds and retry.', + ); + } + if (!response.ok) throw new CommandExecutionError(`${label} returned HTTP ${response.status}`); + try { + return await response.json(); + } catch (error) { + throw new CommandExecutionError(`${label} returned malformed JSON: ${error?.message ?? error}`); + } +} + +export function parseLimit(raw, fallback = 10) { + const value = raw === undefined || raw === null || raw === '' ? fallback : Number(raw); + if (!Number.isInteger(value) || value < 1 || value > MAX_RELEASE_LIMIT) { + throw new ArgumentError(`--limit must be an integer between 1 and ${MAX_RELEASE_LIMIT}`); + } + return value; +} + +function latestUploadTime(files) { + return files + .map(file => file?.upload_time_iso_8601 || file?.upload_time || null) + .filter(Boolean) + .sort() + .at(-1) || null; +} + +export function summarizeReleases(payload, limit) { + const name = String(payload.info?.name || '').trim(); + const releases = payload.releases && typeof payload.releases === 'object' ? payload.releases : {}; + const rows = Object.entries(releases) + .map(([version, files]) => { + const versionFiles = Array.isArray(files) ? files : []; + return { + version, + uploadedAt: latestUploadTime(versionFiles), + fileCount: versionFiles.length, + pythonVersions: [...new Set(versionFiles.map(file => file?.python_version).filter(Boolean))].join(', ') || null, + yanked: versionFiles.length > 0 && versionFiles.every(file => file?.yanked === true), + url: `${PACKAGE_URL_BASE}/${encodeURIComponent(name)}/${encodeURIComponent(version)}/`, + }; + }) + .filter(row => row.uploadedAt) + .sort((a, b) => String(b.uploadedAt).localeCompare(String(a.uploadedAt))) + .slice(0, limit); + + if (!rows.length) { + throw new EmptyResultError('pypi releases', `PyPI returned no release files for "${name}".`); + } + return rows; +} + +export async function fetchPackageJson(name, request = fetch) { + const packageName = requirePackageName(name); + const payload = await pypiFetch(`${PYPI_BASE}/pypi/${encodeURIComponent(packageName)}/json`, `pypi package ${packageName}`, request); + if (!payload || typeof payload !== 'object' || !payload.info) { + throw new CommandExecutionError('PyPI returned an unexpected response.'); + } + return payload; +} diff --git a/plugins/pypi/webcmd-plugin.json b/plugins/pypi/webcmd-plugin.json index 607f4b0d..36d8dd2b 100644 --- a/plugins/pypi/webcmd-plugin.json +++ b/plugins/pypi/webcmd-plugin.json @@ -1,7 +1,7 @@ { "name": "pypi", "version": "0.1.0", - "description": "Inspect public Python package metadata and releases from PyPI", + "description": "Inspect public Python package metadata, downloads, and releases from PyPI", "webcmd": ">=0.2.0", "author": { "name": "Kemal Kaya", diff --git a/webcmd-plugin.json b/webcmd-plugin.json index dcec0f9a..572cc411 100644 --- a/webcmd-plugin.json +++ b/webcmd-plugin.json @@ -457,7 +457,7 @@ "pypi": { "path": "plugins/pypi", "version": "0.1.0", - "description": "Inspect public Python package metadata and releases from PyPI", + "description": "Inspect public Python package metadata, downloads, and releases from PyPI", "webcmd": ">=0.2.0", "author": { "name": "Kemal Kaya", From ea26cb9468dfd0b0d010c7e7da01e5fdadf096f8 Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 17:10:48 +0530 Subject: [PATCH 14/39] refactor: migrate search and desktop adapters to plugins --- cli-manifest.json | 15371 +++++++--------- clis/codex/dump.js | 2 - clis/cursor/dump.js | 2 - clis/cursor/screenshot.js | 2 - plugin-command-manifest.json | 10707 ++++++----- plugins/brave/README.md | 15 + plugins/brave/package.json | 9 + {clis => plugins}/brave/search.js | 2 +- .../brave/test}/search.test.js | 2 +- plugins/brave/webcmd-plugin.json | 10 + plugins/chatwise/README.md | 23 + {clis => plugins}/chatwise/ask.js | 0 {clis => plugins}/chatwise/export.js | 0 {clis => plugins}/chatwise/history.js | 0 {clis => plugins}/chatwise/model.js | 0 {clis => plugins}/chatwise/new.js | 2 +- plugins/chatwise/package.json | 9 + {clis => plugins}/chatwise/read.js | 0 {clis => plugins}/chatwise/screenshot.js | 2 +- {clis => plugins}/chatwise/send.js | 0 {clis => plugins}/chatwise/status.js | 2 +- .../chatwise/test}/composer.test.js | 4 +- {clis => plugins}/chatwise/utils.js | 0 plugins/chatwise/webcmd-plugin.json | 10 + plugins/codex/README.md | 30 + {clis => plugins}/codex/_actions.js | 0 {clis => plugins}/codex/archive.js | 0 {clis => plugins}/codex/ask.js | 0 plugins/codex/dump.js | 2 + {clis => plugins}/codex/export.js | 0 {clis => plugins}/codex/extract-diff.js | 0 {clis => plugins}/codex/history.js | 0 {clis => plugins}/codex/model.js | 0 {clis => plugins}/codex/new.js | 2 +- plugins/codex/package.json | 9 + {clis => plugins}/codex/pin.js | 0 {clis => plugins}/codex/projects.js | 0 {clis => plugins}/codex/read.js | 0 {clis => plugins}/codex/rename.js | 0 {clis => plugins}/codex/screenshot.js | 2 +- {clis => plugins}/codex/send.js | 0 {clis => plugins}/codex/sidebar.js | 0 {clis => plugins}/codex/status.js | 2 +- .../codex/test}/sidebar.test.js | 12 +- plugins/codex/webcmd-plugin.json | 10 + plugins/cursor/README.md | 26 + {clis => plugins}/cursor/ask.js | 0 {clis => plugins}/cursor/composer.js | 0 plugins/cursor/dump.js | 2 + {clis => plugins}/cursor/export.js | 0 {clis => plugins}/cursor/extract-code.js | 0 {clis => plugins}/cursor/history.js | 0 {clis => plugins}/cursor/model.js | 0 {clis => plugins}/cursor/new.js | 2 +- plugins/cursor/package.json | 9 + {clis => plugins}/cursor/read.js | 0 plugins/cursor/screenshot.js | 2 + {clis => plugins}/cursor/send.js | 0 {clis => plugins}/cursor/status.js | 2 +- plugins/cursor/webcmd-plugin.json | 10 + plugins/duckduckgo/README.md | 16 + plugins/duckduckgo/package.json | 9 + {clis => plugins}/duckduckgo/search.js | 2 +- {clis => plugins}/duckduckgo/suggest.js | 2 +- .../duckduckgo/test}/search.test.js | 2 +- .../duckduckgo/test}/suggest.test.js | 2 +- plugins/duckduckgo/webcmd-plugin.json | 10 + plugins/google-scholar/README.md | 17 + {clis => plugins}/google-scholar/cite.js | 2 +- plugins/google-scholar/package.json | 9 + {clis => plugins}/google-scholar/profile.js | 2 +- {clis => plugins}/google-scholar/search.js | 2 +- .../google-scholar/test}/cite.test.js | 2 +- .../google-scholar/test}/profile.test.js | 2 +- .../google-scholar/test}/search.test.js | 2 +- plugins/google-scholar/webcmd-plugin.json | 10 + plugins/google/README.md | 19 + {clis => plugins}/google/images.js | 2 +- {clis => plugins}/google/news.js | 0 plugins/google/package.json | 9 + {clis => plugins}/google/search.js | 0 {clis => plugins}/google/suggest.js | 0 .../google/test}/images.test.js | 2 +- .../google/test}/utils.test.js | 2 +- {clis => plugins}/google/trends.js | 0 {clis => plugins}/google/utils.js | 0 plugins/google/webcmd-plugin.json | 10 + plugins/trae-solo/README.md | 39 + {clis => plugins}/trae-solo/_actions.js | 0 {clis => plugins}/trae-solo/_fs.js | 0 {clis => plugins}/trae-solo/_state.js | 0 {clis => plugins}/trae-solo/automation.js | 0 {clis => plugins}/trae-solo/history.js | 0 {clis => plugins}/trae-solo/mode.js | 0 {clis => plugins}/trae-solo/model.js | 0 plugins/trae-solo/package.json | 9 + .../trae-solo/renderer-storage.js | 0 {clis => plugins}/trae-solo/settings.js | 0 {clis => plugins}/trae-solo/skill-fs.js | 0 {clis => plugins}/trae-solo/skill.js | 0 {clis => plugins}/trae-solo/state-fs.js | 0 {clis => plugins}/trae-solo/status.js | 2 +- {clis => plugins}/trae-solo/task-fs.js | 0 .../trae-solo/test}/trae-solo.test.js | 4 +- {clis => plugins}/trae-solo/user-rules.js | 0 plugins/trae-solo/webcmd-plugin.json | 10 + {clis => plugins}/trae-solo/workspaces-fs.js | 0 plugins/yahoo/README.md | 15 + plugins/yahoo/package.json | 9 + {clis => plugins}/yahoo/search.js | 2 +- .../yahoo/test}/search.test.js | 2 +- plugins/yahoo/webcmd-plugin.json | 10 + scripts/typed-error-lint-baseline.json | 6 +- src/hosted/availability.test.ts | 6 +- webcmd-plugin.json | 90 + 115 files changed, 13548 insertions(+), 13087 deletions(-) delete mode 100644 clis/codex/dump.js delete mode 100644 clis/cursor/dump.js delete mode 100644 clis/cursor/screenshot.js create mode 100644 plugins/brave/README.md create mode 100644 plugins/brave/package.json rename {clis => plugins}/brave/search.js (98%) rename {clis/brave => plugins/brave/test}/search.test.js (98%) create mode 100644 plugins/brave/webcmd-plugin.json create mode 100644 plugins/chatwise/README.md rename {clis => plugins}/chatwise/ask.js (100%) rename {clis => plugins}/chatwise/export.js (100%) rename {clis => plugins}/chatwise/history.js (100%) rename {clis => plugins}/chatwise/model.js (100%) rename {clis => plugins}/chatwise/new.js (54%) create mode 100644 plugins/chatwise/package.json rename {clis => plugins}/chatwise/read.js (100%) rename {clis => plugins}/chatwise/screenshot.js (52%) rename {clis => plugins}/chatwise/send.js (100%) rename {clis => plugins}/chatwise/status.js (53%) rename {clis/chatwise => plugins/chatwise/test}/composer.test.js (99%) rename {clis => plugins}/chatwise/utils.js (100%) create mode 100644 plugins/chatwise/webcmd-plugin.json create mode 100644 plugins/codex/README.md rename {clis => plugins}/codex/_actions.js (100%) rename {clis => plugins}/codex/archive.js (100%) rename {clis => plugins}/codex/ask.js (100%) create mode 100644 plugins/codex/dump.js rename {clis => plugins}/codex/export.js (100%) rename {clis => plugins}/codex/extract-diff.js (100%) rename {clis => plugins}/codex/history.js (100%) rename {clis => plugins}/codex/model.js (100%) rename {clis => plugins}/codex/new.js (52%) create mode 100644 plugins/codex/package.json rename {clis => plugins}/codex/pin.js (100%) rename {clis => plugins}/codex/projects.js (100%) rename {clis => plugins}/codex/read.js (100%) rename {clis => plugins}/codex/rename.js (100%) rename {clis => plugins}/codex/screenshot.js (50%) rename {clis => plugins}/codex/send.js (100%) rename {clis => plugins}/codex/sidebar.js (100%) rename {clis => plugins}/codex/status.js (52%) rename {clis/codex => plugins/codex/test}/sidebar.test.js (98%) create mode 100644 plugins/codex/webcmd-plugin.json create mode 100644 plugins/cursor/README.md rename {clis => plugins}/cursor/ask.js (100%) rename {clis => plugins}/cursor/composer.js (100%) create mode 100644 plugins/cursor/dump.js rename {clis => plugins}/cursor/export.js (100%) rename {clis => plugins}/cursor/extract-code.js (100%) rename {clis => plugins}/cursor/history.js (100%) rename {clis => plugins}/cursor/model.js (100%) rename {clis => plugins}/cursor/new.js (54%) create mode 100644 plugins/cursor/package.json rename {clis => plugins}/cursor/read.js (100%) create mode 100644 plugins/cursor/screenshot.js rename {clis => plugins}/cursor/send.js (100%) rename {clis => plugins}/cursor/status.js (53%) create mode 100644 plugins/cursor/webcmd-plugin.json create mode 100644 plugins/duckduckgo/README.md create mode 100644 plugins/duckduckgo/package.json rename {clis => plugins}/duckduckgo/search.js (99%) rename {clis => plugins}/duckduckgo/suggest.js (94%) rename {clis/duckduckgo => plugins/duckduckgo/test}/search.test.js (98%) rename {clis/duckduckgo => plugins/duckduckgo/test}/suggest.test.js (97%) create mode 100644 plugins/duckduckgo/webcmd-plugin.json create mode 100644 plugins/google-scholar/README.md rename {clis => plugins}/google-scholar/cite.js (97%) create mode 100644 plugins/google-scholar/package.json rename {clis => plugins}/google-scholar/profile.js (97%) rename {clis => plugins}/google-scholar/search.js (97%) rename {clis/google-scholar => plugins/google-scholar/test}/cite.test.js (99%) rename {clis/google-scholar => plugins/google-scholar/test}/profile.test.js (98%) rename {clis/google-scholar => plugins/google-scholar/test}/search.test.js (99%) create mode 100644 plugins/google-scholar/webcmd-plugin.json create mode 100644 plugins/google/README.md rename {clis => plugins}/google/images.js (99%) rename {clis => plugins}/google/news.js (100%) create mode 100644 plugins/google/package.json rename {clis => plugins}/google/search.js (100%) rename {clis => plugins}/google/suggest.js (100%) rename {clis/google => plugins/google/test}/images.test.js (99%) rename {clis/google => plugins/google/test}/utils.test.js (98%) rename {clis => plugins}/google/trends.js (100%) rename {clis => plugins}/google/utils.js (100%) create mode 100644 plugins/google/webcmd-plugin.json create mode 100644 plugins/trae-solo/README.md rename {clis => plugins}/trae-solo/_actions.js (100%) rename {clis => plugins}/trae-solo/_fs.js (100%) rename {clis => plugins}/trae-solo/_state.js (100%) rename {clis => plugins}/trae-solo/automation.js (100%) rename {clis => plugins}/trae-solo/history.js (100%) rename {clis => plugins}/trae-solo/mode.js (100%) rename {clis => plugins}/trae-solo/model.js (100%) create mode 100644 plugins/trae-solo/package.json rename {clis => plugins}/trae-solo/renderer-storage.js (100%) rename {clis => plugins}/trae-solo/settings.js (100%) rename {clis => plugins}/trae-solo/skill-fs.js (100%) rename {clis => plugins}/trae-solo/skill.js (100%) rename {clis => plugins}/trae-solo/state-fs.js (100%) rename {clis => plugins}/trae-solo/status.js (54%) rename {clis => plugins}/trae-solo/task-fs.js (100%) rename {clis/trae-solo => plugins/trae-solo/test}/trae-solo.test.js (96%) rename {clis => plugins}/trae-solo/user-rules.js (100%) create mode 100644 plugins/trae-solo/webcmd-plugin.json rename {clis => plugins}/trae-solo/workspaces-fs.js (100%) create mode 100644 plugins/yahoo/README.md create mode 100644 plugins/yahoo/package.json rename {clis => plugins}/yahoo/search.js (98%) rename {clis/yahoo => plugins/yahoo/test}/search.test.js (98%) create mode 100644 plugins/yahoo/webcmd-plugin.json diff --git a/cli-manifest.json b/cli-manifest.json index 8c6f143c..d838e3fc 100644 --- a/cli-manifest.json +++ b/cli-manifest.json @@ -2798,50 +2798,6 @@ "modulePath": "booking/search.js", "sourceFile": "booking/search.js" }, - { - "site": "brave", - "name": "search", - "description": "Search Brave Search", - "access": "read", - "domain": "search.brave.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results per page (max 18)" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Page offset (0, 1, 2...). Brave returns ~18 results per page" - } - ], - "columns": [ - "rank", - "title", - "url", - "snippet" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "brave/search.js", - "sourceFile": "brave/search.js" - }, { "site": "chatgpt", "name": "ask", @@ -3638,213 +3594,6 @@ "modulePath": "chatgpt-app/status.js", "sourceFile": "chatgpt-app/status.js" }, - { - "site": "chatwise", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 30, - "required": false, - "help": "Max seconds to wait (default: 30)" - } - ], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "chatwise/ask.js", - "sourceFile": "chatwise/ask.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "export", - "description": "Export the current ChatWise conversation to a Markdown file", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "output", - "type": "str", - "required": false, - "help": "Output file (default: /tmp/chatwise-export.md)" - } - ], - "columns": [ - "Status", - "File", - "Messages" - ], - "type": "js", - "modulePath": "chatwise/export.js", - "sourceFile": "chatwise/export.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "history", - "description": "List conversation history in ChatWise sidebar", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Title" - ], - "type": "js", - "modulePath": "chatwise/history.js", - "sourceFile": "chatwise/history.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "model", - "description": "Get or switch the active AI model in ChatWise", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "model-name", - "type": "str", - "required": false, - "positional": true, - "help": "Model to switch to (e.g. gpt-4, claude-3)" - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "chatwise/model.js", - "sourceFile": "chatwise/model.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "new", - "description": "Start a new ChatWise conversation session", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "chatwise/new.js", - "sourceFile": "chatwise/new.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "read", - "description": "Read the current ChatWise conversation history", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Content" - ], - "type": "js", - "modulePath": "chatwise/read.js", - "sourceFile": "chatwise/read.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "screenshot", - "description": "Capture a snapshot of the current ChatWise window (DOM + Accessibility tree)", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "output", - "type": "str", - "required": false, - "help": "Output file path (default: /tmp/chatwise-snapshot.txt)" - } - ], - "columns": [ - "Status", - "File" - ], - "type": "js", - "modulePath": "chatwise/screenshot.js", - "sourceFile": "chatwise/screenshot.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "send", - "description": "Send a message to the active ChatWise conversation", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Message to send" - } - ], - "columns": [ - "Status", - "InjectedText" - ], - "type": "js", - "modulePath": "chatwise/send.js", - "sourceFile": "chatwise/send.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "status", - "description": "Check active CDP connection to ChatWise Desktop", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" - ], - "type": "js", - "modulePath": "chatwise/status.js", - "sourceFile": "chatwise/status.js", - "navigateBefore": true - }, { "site": "chess", "name": "analyze", @@ -4262,2768 +4011,2600 @@ "siteSession": "persistent" }, { - "site": "codex", - "name": "archive", - "description": "Archive (Codex's term for delete) the selected conversation via the Chat actions header menu. No confirmation in UI — pass --yes to actually archive.", + "site": "confluence", + "name": "create", + "description": "Create a Confluence page from Markdown or storage XHTML", "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "yes", - "type": "boolean", - "default": false, - "required": false, - "help": "Actually archive (default: dry-run preview)" + "name": "space", + "type": "string", + "required": true, + "help": "Cloud space id, or Data Center space key" }, { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" + "name": "title", + "type": "string", + "required": true, + "help": "Page title" }, { - "name": "conversation", - "type": "str", + "name": "file", + "type": "string", + "required": true, + "help": "Markdown file path" + }, + { + "name": "parent", + "type": "string", "required": false, - "help": "Conversation title to select within --project" + "help": "Optional parent page id" }, { - "name": "index", - "type": "str", + "name": "representation", + "type": "string", + "default": "markdown", "required": false, - "help": "1-based conversation index within --project" + "help": "Input file format", + "choices": [ + "markdown", + "storage" + ] }, { - "name": "thread-id", - "type": "str", + "name": "execute", + "type": "boolean", "required": false, - "help": "Exact Codex thread id to select" + "help": "Actually create the remote page" } ], "columns": [ "status", - "thread_id", - "project", - "conversation" + "id", + "title", + "spaceId", + "spaceKey", + "version", + "url" ], "type": "js", - "modulePath": "codex/archive.js", - "sourceFile": "codex/archive.js", - "navigateBefore": true + "modulePath": "confluence/create.js", + "sourceFile": "confluence/create.js" }, { - "site": "codex", - "name": "ask", - "description": "Send a prompt to the current or selected Codex conversation and wait for the AI response", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "site": "confluence", + "name": "page", + "description": "Confluence page by id with storage and Markdown body", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "text", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 60, - "required": false, - "help": "Max seconds to wait for response (default: 60)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" + "help": "Confluence page id" } ], "columns": [ - "Role", - "Project", - "Conversation", - "Text" - ], - "type": "js", - "modulePath": "codex/ask.js", - "sourceFile": "codex/ask.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "dump", - "description": "Dump the DOM and Accessibility tree of codex for reverse-engineering", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "action", - "files" + "id", + "title", + "status", + "spaceId", + "spaceKey", + "version", + "url" ], "type": "js", - "modulePath": "codex/dump.js", - "sourceFile": "codex/dump.js", - "navigateBefore": true + "modulePath": "confluence/page.js", + "sourceFile": "confluence/page.js" }, { - "site": "codex", - "name": "export", - "description": "Export the current Codex conversation to a Markdown file", + "site": "confluence", + "name": "search", + "description": "Search Confluence content with CQL", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "output", + "name": "cql", "type": "str", + "required": true, + "positional": true, + "help": "CQL query, e.g. \"type = page and title ~ \\\"RCA\\\"\"" + }, + { + "name": "space", + "type": "string", + "required": false, + "help": "Limit search to a Confluence space key" + }, + { + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Output file (default: /tmp/codex-export.md)" + "help": "Max results to return (1-100)" } ], "columns": [ - "Status", - "File", - "Messages" + "id", + "title", + "type", + "spaceKey", + "status", + "lastModified", + "url" ], - "type": "js", - "modulePath": "codex/export.js", - "sourceFile": "codex/export.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "extract-diff", - "description": "Extract visual code review diff patches from Codex", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "File", - "Diff" + "tags": [ + "search" ], "type": "js", - "modulePath": "codex/extract-diff.js", - "sourceFile": "codex/extract-diff.js", - "navigateBefore": true + "modulePath": "confluence/search.js", + "sourceFile": "confluence/search.js" }, { - "site": "codex", - "name": "history", - "description": "List visible Codex conversation threads grouped by project", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "site": "confluence", + "name": "update", + "description": "Update a Confluence page body from Markdown or storage XHTML", + "access": "write", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "project", + "name": "id", "type": "str", + "required": true, + "positional": true, + "help": "Confluence page id" + }, + { + "name": "file", + "type": "string", + "required": true, + "help": "Markdown file path" + }, + { + "name": "title", + "type": "string", "required": false, - "help": "Filter by project label or path" + "help": "Optional replacement title; defaults to current title" }, { - "name": "limit", - "type": "str", + "name": "version-message", + "type": "string", + "required": false, + "help": "Confluence version message" + }, + { + "name": "representation", + "type": "string", + "default": "markdown", + "required": false, + "help": "Input file format", + "choices": [ + "markdown", + "storage" + ] + }, + { + "name": "execute", + "type": "boolean", "required": false, - "help": "Max conversations per project" + "help": "Actually update the remote page" } ], "columns": [ - "Project", - "Index", - "Title", - "Updated", - "Active" + "status", + "id", + "title", + "spaceId", + "spaceKey", + "version", + "url" ], "type": "js", - "modulePath": "codex/history.js", - "sourceFile": "codex/history.js", - "navigateBefore": true + "modulePath": "confluence/update.js", + "sourceFile": "confluence/update.js" }, { - "site": "codex", - "name": "model", - "description": "Read, list, or switch the active model / reasoning level in Codex Desktop. The composer toolbar button toggles a menu that mixes model variants (GPT-5.5, Speed) with reasoning levels (Low/Medium/High/Extra High).", + "site": "coupang", + "name": "add-to-cart", + "description": "Add a Coupang product to cart using logged-in browser session", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "www.coupang.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "name", + "name": "product-id", "type": "str", "required": false, "positional": true, - "help": "Substring (case-insensitive) of a model / reasoning level to switch to. Omit to read current." + "help": "Coupang product ID" }, { - "name": "list", - "type": "boolean", - "default": false, + "name": "url", + "type": "str", "required": false, - "help": "List all menu options (does not switch)" + "help": "Canonical product URL" } ], "columns": [ - "Status", - "Model" + "ok", + "product_id", + "url", + "message" ], "type": "js", - "modulePath": "codex/model.js", - "sourceFile": "codex/model.js", - "navigateBefore": true + "modulePath": "coupang/add-to-cart.js", + "sourceFile": "coupang/add-to-cart.js", + "navigateBefore": "https://www.coupang.com" }, { - "site": "codex", - "name": "new", - "description": "Start a new Codex conversation session", + "site": "coupang", + "name": "login", + "description": "Open coupang login", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "coupang.com", + "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Status" + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" ], "type": "js", - "modulePath": "codex/new.js", - "sourceFile": "codex/new.js", - "navigateBefore": true + "modulePath": "coupang/auth.js", + "sourceFile": "coupang/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "codex", - "name": "pin", - "description": "Pin the selected Codex conversation via the Chat actions header menu.", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "site": "coupang", + "name": "product", + "description": "Read full product detail (price, rating, seller, delivery) for a Coupang product", + "access": "read", + "domain": "www.coupang.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", + "name": "product-id", "type": "str", "required": false, - "help": "1-based conversation index within --project" + "positional": true, + "help": "Coupang product ID (digits only)" }, { - "name": "thread-id", + "name": "url", "type": "str", "required": false, - "help": "Exact Codex thread id to select" + "help": "Canonical Coupang product URL (alternative to --product-id)" } ], "columns": [ - "status", - "thread_id", - "project", - "conversation" + "product_id", + "title", + "price", + "original_price", + "discount_rate", + "rating", + "review_count", + "seller", + "brand", + "rocket", + "delivery_promise", + "image_url", + "url" ], "type": "js", - "modulePath": "codex/pin.js", - "sourceFile": "codex/pin.js", - "navigateBefore": true + "modulePath": "coupang/product.js", + "sourceFile": "coupang/product.js", + "navigateBefore": "https://www.coupang.com" }, { - "site": "codex", - "name": "projects", - "description": "List Codex projects and visible conversations from the sidebar", + "site": "coupang", + "name": "search", + "description": "Search Coupang products with logged-in browser session", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.coupang.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "project", + "name": "query", "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "page", + "type": "int", + "default": 1, "required": false, - "help": "Filter by project label or path" + "help": "Search result page number" }, { "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (max 50)" + }, + { + "name": "filter", "type": "str", "required": false, - "help": "Max conversations per project" + "help": "Optional search filter (currently supports: rocket)" } ], "columns": [ - "Project", - "Index", - "Title", - "Updated", - "Active" + "rank", + "product_id", + "title", + "price", + "unit_price", + "rating", + "review_count", + "rocket", + "delivery_type", + "delivery_promise", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "codex/projects.js", - "sourceFile": "codex/projects.js", - "navigateBefore": true + "modulePath": "coupang/search.js", + "sourceFile": "coupang/search.js", + "navigateBefore": "https://www.coupang.com" }, { - "site": "codex", - "name": "read", - "description": "Read the contents of the current or selected Codex conversation thread", + "site": "coupang", + "name": "whoami", + "description": "Show the current logged-in coupang account", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "coupang.com", + "strategy": "cookie", "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], + "args": [], "columns": [ - "Project", - "Conversation", - "Content" + "logged_in", + "site", + "name" ], "type": "js", - "modulePath": "codex/read.js", - "sourceFile": "codex/read.js", - "navigateBefore": true + "modulePath": "coupang/auth.js", + "sourceFile": "coupang/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "codex", - "name": "rename", - "description": "Rename the selected Codex conversation. Opens the Chat actions menu → \"Rename chat\", then types the new title.", - "access": "write", + "site": "discord-app", + "name": "channels", + "description": "List channels in the current Discord server", + "access": "read", "domain": "localhost", "strategy": "ui", "browser": true, - "args": [ - { - "name": "title", - "type": "str", - "required": true, - "positional": true, - "help": "New title (single line, no newlines)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], + "args": [], "columns": [ - "status", - "title", - "thread_id", - "project" + "Index", + "Channel", + "Type", + "guild_id", + "channel_id", + "url" ], "type": "js", - "modulePath": "codex/rename.js", - "sourceFile": "codex/rename.js", + "modulePath": "discord-app/channels.js", + "sourceFile": "discord-app/channels.js", "navigateBefore": true }, { - "site": "codex", - "name": "screenshot", - "description": "Capture a snapshot of the current Codex window (DOM + Accessibility tree)", - "access": "read", + "site": "discord-app", + "name": "delete", + "description": "Delete a message by its ID in the active Discord channel", + "access": "write", "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "output", - "type": "str", - "required": false, - "help": "Output file path (default: /tmp/codex-snapshot.txt)" + "name": "message_id", + "type": "string", + "required": true, + "positional": true, + "help": "The ID of the message to delete (visible via Developer Mode or the read command)" } ], "columns": [ - "Status", - "File" + "status", + "message" ], "type": "js", - "modulePath": "codex/screenshot.js", - "sourceFile": "codex/screenshot.js", + "modulePath": "discord-app/delete.js", + "sourceFile": "discord-app/delete.js", "navigateBefore": true }, { - "site": "codex", - "name": "send", - "description": "Send text/commands to the current or selected Codex AI composer", - "access": "write", + "site": "discord-app", + "name": "goto", + "description": "Open a Discord channel by id/name/url without sending messages", + "access": "read", "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Text, command (e.g. /review), or skill (e.g. $imagegen)" - }, - { - "name": "project", + "name": "guild", "type": "str", "required": false, - "help": "Project label or path to select before running the command" + "help": "Guild/server id or visible name" }, { - "name": "conversation", + "name": "channel", "type": "str", "required": false, - "help": "Conversation title to select within --project" + "help": "Channel id or visible name" }, { - "name": "index", + "name": "url", "type": "str", "required": false, - "help": "1-based conversation index within --project" + "help": "Discord channel URL" }, { - "name": "thread-id", + "name": "timeout", "type": "str", + "default": "8", "required": false, - "help": "Exact Codex thread id to select" + "help": "Seconds to wait for Discord to show the route (default: 8)" } ], "columns": [ "Status", - "Project", - "Conversation", - "InjectedText" + "guild_id", + "channel_id", + "url" ], "type": "js", - "modulePath": "codex/send.js", - "sourceFile": "codex/send.js", + "modulePath": "discord-app/goto.js", + "sourceFile": "discord-app/goto.js", "navigateBefore": true }, { - "site": "codex", - "name": "status", - "description": "Check active CDP connection to OpenAI Codex App", + "site": "discord-app", + "name": "members", + "description": "List online members in the current Discord channel", "access": "read", "domain": "localhost", "strategy": "ui", "browser": true, "args": [], "columns": [ - "Status", - "Url", - "Title" + "Index", + "Name", + "Status" ], "type": "js", - "modulePath": "codex/status.js", - "sourceFile": "codex/status.js", + "modulePath": "discord-app/members.js", + "sourceFile": "discord-app/members.js", "navigateBefore": true }, { - "site": "codex", - "name": "unpin", - "description": "Unpin the selected Codex conversation via the Chat actions header menu.", - "access": "write", + "site": "discord-app", + "name": "read", + "description": "Read recent messages from the active or targeted Discord channel", + "access": "read", "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "project", + "name": "count", "type": "str", + "default": "20", "required": false, - "help": "Project label or path to select before running the command" + "help": "Number of messages to read (default: 20)" }, { - "name": "conversation", + "name": "guild", "type": "str", "required": false, - "help": "Conversation title to select within --project" + "help": "Guild/server id or visible name for targeted reads" }, { - "name": "index", + "name": "channel", "type": "str", "required": false, - "help": "1-based conversation index within --project" + "help": "Channel id or visible name for targeted reads" }, { - "name": "thread-id", + "name": "url", "type": "str", "required": false, - "help": "Exact Codex thread id to select" + "help": "Discord channel URL to open before reading" } ], "columns": [ - "status", - "thread_id", - "project", - "conversation" + "Author", + "Time", + "Message", + "channel_id", + "message_id" ], "type": "js", - "modulePath": "codex/pin.js", - "sourceFile": "codex/pin.js", + "modulePath": "discord-app/read.js", + "sourceFile": "discord-app/read.js", "navigateBefore": true }, { - "site": "confluence", - "name": "create", - "description": "Create a Confluence page from Markdown or storage XHTML", - "access": "write", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "site": "discord-app", + "name": "search", + "description": "Search messages in the current Discord server/channel (Cmd+F)", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "space", - "type": "string", + "name": "query", + "type": "str", "required": true, - "help": "Cloud space id, or Data Center space key" - }, - { - "name": "title", - "type": "string", - "required": true, - "help": "Page title" - }, - { - "name": "file", - "type": "string", - "required": true, - "help": "Markdown file path" - }, - { - "name": "parent", - "type": "string", - "required": false, - "help": "Optional parent page id" - }, - { - "name": "representation", - "type": "string", - "default": "markdown", - "required": false, - "help": "Input file format", - "choices": [ - "markdown", - "storage" - ] - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually create the remote page" + "positional": true, + "help": "Search query" } ], "columns": [ - "status", - "id", - "title", - "spaceId", - "spaceKey", - "version", - "url" + "Index", + "Author", + "Message" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "confluence/create.js", - "sourceFile": "confluence/create.js" + "modulePath": "discord-app/search.js", + "sourceFile": "discord-app/search.js", + "navigateBefore": true }, { - "site": "confluence", - "name": "page", - "description": "Confluence page by id with storage and Markdown body", - "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "site": "discord-app", + "name": "send", + "description": "Send a message in the active Discord channel", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "id", + "name": "text", "type": "str", "required": true, "positional": true, - "help": "Confluence page id" + "help": "Message to send" } ], "columns": [ - "id", - "title", - "status", - "spaceId", - "spaceKey", - "version", - "url" + "Status" ], "type": "js", - "modulePath": "confluence/page.js", - "sourceFile": "confluence/page.js" + "modulePath": "discord-app/send.js", + "sourceFile": "discord-app/send.js", + "navigateBefore": true }, { - "site": "confluence", - "name": "search", - "description": "Search Confluence content with CQL", + "site": "discord-app", + "name": "servers", + "description": "List all Discord servers (guilds) in the sidebar", "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "cql", - "type": "str", - "required": true, - "positional": true, - "help": "CQL query, e.g. \"type = page and title ~ \\\"RCA\\\"\"" - }, - { - "name": "space", - "type": "string", - "required": false, - "help": "Limit search to a Confluence space key" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results to return (1-100)" - } - ], + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "id", - "title", - "type", - "spaceKey", - "status", - "lastModified", + "Index", + "Server", + "guild_id", "url" ], - "tags": [ - "search" + "type": "js", + "modulePath": "discord-app/servers.js", + "sourceFile": "discord-app/servers.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "status", + "description": "Check active CDP connection to Discord Desktop", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" ], "type": "js", - "modulePath": "confluence/search.js", - "sourceFile": "confluence/search.js" + "modulePath": "discord-app/status.js", + "sourceFile": "discord-app/status.js", + "navigateBefore": true }, { - "site": "confluence", - "name": "update", - "description": "Update a Confluence page body from Markdown or storage XHTML", - "access": "write", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "site": "discord-app", + "name": "thread-read", + "description": "Read recent messages from a Discord thread/post by id or URL", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "id", + "name": "thread", "type": "str", - "required": true, - "positional": true, - "help": "Confluence page id" - }, - { - "name": "file", - "type": "string", - "required": true, - "help": "Markdown file path" + "required": false, + "help": "Thread/post id, or a full Discord thread/post URL" }, { - "name": "title", - "type": "string", + "name": "count", + "type": "str", + "default": "20", "required": false, - "help": "Optional replacement title; defaults to current title" + "help": "Number of messages to read (default: 20)" }, { - "name": "version-message", - "type": "string", + "name": "guild", + "type": "str", "required": false, - "help": "Confluence version message" + "help": "Parent guild/server id or visible name" }, { - "name": "representation", - "type": "string", - "default": "markdown", + "name": "channel", + "type": "str", "required": false, - "help": "Input file format", - "choices": [ - "markdown", - "storage" - ] + "help": "Parent forum/channel id or visible name" }, { - "name": "execute", - "type": "boolean", + "name": "url", + "type": "str", "required": false, - "help": "Actually update the remote page" + "help": "Discord thread/post URL" } ], "columns": [ - "status", - "id", - "title", - "spaceId", - "spaceKey", - "version", - "url" + "Author", + "Time", + "Message", + "channel_id", + "message_id" ], "type": "js", - "modulePath": "confluence/update.js", - "sourceFile": "confluence/update.js" + "modulePath": "discord-app/thread-read.js", + "sourceFile": "discord-app/thread-read.js", + "navigateBefore": true }, { - "site": "coupang", - "name": "add-to-cart", - "description": "Add a Coupang product to cart using logged-in browser session", - "access": "write", - "domain": "www.coupang.com", - "strategy": "cookie", + "site": "discord-app", + "name": "threads", + "description": "List visible Discord forum/thread posts in the active or targeted channel", + "access": "read", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "product-id", + "name": "limit", "type": "str", + "default": "30", "required": false, - "positional": true, - "help": "Coupang product ID" + "help": "Maximum thread/post cards to return (default: 30)" }, { - "name": "url", + "name": "guild", "type": "str", "required": false, - "help": "Canonical product URL" - } - ], - "columns": [ - "ok", - "product_id", - "url", - "message" - ], - "type": "js", - "modulePath": "coupang/add-to-cart.js", - "sourceFile": "coupang/add-to-cart.js", - "navigateBefore": "https://www.coupang.com" - }, - { - "site": "coupang", - "name": "login", - "description": "Open coupang login", - "access": "write", - "domain": "coupang.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "coupang/auth.js", - "sourceFile": "coupang/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "coupang", - "name": "product", - "description": "Read full product detail (price, rating, seller, delivery) for a Coupang product", - "access": "read", - "domain": "www.coupang.com", - "strategy": "cookie", - "browser": true, - "args": [ + "help": "Guild/server id or visible name for targeted thread listing" + }, { - "name": "product-id", + "name": "channel", "type": "str", "required": false, - "positional": true, - "help": "Coupang product ID (digits only)" + "help": "Forum/channel id or visible name for targeted thread listing" }, { "name": "url", "type": "str", "required": false, - "help": "Canonical Coupang product URL (alternative to --product-id)" + "help": "Discord forum/channel URL to open before listing threads" } ], "columns": [ - "product_id", - "title", - "price", - "original_price", - "discount_rate", - "rating", - "review_count", - "seller", - "brand", - "rocket", - "delivery_promise", - "image_url", + "Index", + "Thread", + "Author", + "Updated", + "Preview", + "guild_id", + "channel_id", + "thread_id", "url" ], "type": "js", - "modulePath": "coupang/product.js", - "sourceFile": "coupang/product.js", - "navigateBefore": "https://www.coupang.com" + "modulePath": "discord-app/threads.js", + "sourceFile": "discord-app/threads.js", + "navigateBefore": true }, { - "site": "coupang", - "name": "search", - "description": "Search Coupang products with logged-in browser session", - "access": "read", - "domain": "www.coupang.com", + "site": "district", + "name": "checkout", + "description": "Select District movie seats and open the UPI QR payment scanner", + "access": "write", + "domain": "www.district.in", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", + "name": "show", "type": "str", "required": true, "positional": true, - "help": "Search keyword" + "help": "District seat-layout URL or showId from district showtimes" }, { - "name": "page", - "type": "int", - "default": 1, + "name": "seats", + "type": "str", + "required": true, + "help": "Comma-separated seat labels to select, e.g. I22,I21" + }, + { + "name": "format-id", + "type": "str", "required": false, - "help": "Search result page number" + "help": "District formatId from showtimes; required when show is a showId" }, { - "name": "limit", + "name": "content-id", + "type": "str", + "required": false, + "help": "District content id; required when show is a showId" + }, + { + "name": "timeout", "type": "int", - "default": 20, + "default": 45, "required": false, - "help": "Max results (max 50)" + "help": "Maximum seconds to wait for selection, review page, and payment handoff" }, { - "name": "filter", + "name": "payment", "type": "str", + "default": "upi-qr", "required": false, - "help": "Optional search filter (currently supports: rocket)" + "help": "Payment handoff target: upi-qr opens the scanner; review stops on order review" } ], "columns": [ - "rank", - "product_id", - "title", - "price", - "unit_price", - "rating", - "review_count", - "rocket", - "delivery_type", - "delivery_promise", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "coupang/search.js", - "sourceFile": "coupang/search.js", - "navigateBefore": "https://www.coupang.com" - }, - { - "site": "coupang", - "name": "whoami", - "description": "Show the current logged-in coupang account", - "access": "read", - "domain": "coupang.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name" + "status", + "movie", + "cinema", + "date", + "time", + "seats", + "ticketCount", + "orderAmount", + "bookingCharge", + "total", + "paymentMethod", + "paymentState", + "upiQrVisible", + "paymentAmount", + "paymentUrl", + "showId" ], "type": "js", - "modulePath": "coupang/auth.js", - "sourceFile": "coupang/auth.js", + "modulePath": "district/checkout.js", + "sourceFile": "district/checkout.js", "navigateBefore": false, - "siteSession": "persistent" + "siteSession": "persistent", + "freshPage": true }, { - "site": "cursor", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "site": "district", + "name": "listings", + "aliases": [ + "ls" + ], + "description": "List public District by Zomato movies, events, and nearby going-out cards", + "access": "read", + "domain": "www.district.in", + "strategy": "public", + "browser": false, "args": [ { - "name": "text", + "name": "input", "type": "str", - "required": true, + "default": "home", + "required": false, "positional": true, - "help": "Prompt to send" + "help": "home, movies, events, a district.in URL, or a District path" }, { - "name": "timeout", + "name": "limit", "type": "int", - "default": 30, + "default": 20, "required": false, - "help": "Max seconds to wait for response (default: 30)" + "help": "Maximum rows to return (1-100)" } ], "columns": [ - "Role", - "Text" + "rank", + "title", + "category", + "date", + "venue", + "price", + "url" ], "type": "js", - "modulePath": "cursor/ask.js", - "sourceFile": "cursor/ask.js", - "navigateBefore": true + "modulePath": "district/listings.js", + "sourceFile": "district/listings.js" }, { - "site": "cursor", - "name": "composer", - "description": "Send a prompt directly into Cursor Composer (Cmd+I shortcut)", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "site": "district", + "name": "locations", + "aliases": [ + "location-search" + ], + "description": "Search District-supported cities, areas, malls, and places for booking filters", + "access": "read", + "domain": "www.district.in", + "strategy": "public", + "browser": false, "args": [ { - "name": "text", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Text to send into Composer" + "help": "City, area, mall, or locality, for example \"bangalore\" or \"indiranagar\"" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum location rows to return (1-50)" } ], "columns": [ - "Status", - "InjectedText" + "rank", + "name", + "kind", + "city", + "state", + "cityKey", + "cityId", + "placeId", + "lat", + "lng", + "distanceKm", + "source" ], "type": "js", - "modulePath": "cursor/composer.js", - "sourceFile": "cursor/composer.js", - "navigateBefore": true + "modulePath": "district/locations.js", + "sourceFile": "district/locations.js" }, { - "site": "cursor", - "name": "dump", - "description": "Dump the DOM and Accessibility tree of cursor for reverse-engineering", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "district", + "name": "login", + "description": "Open district login", + "access": "write", + "domain": "www.district.in", + "strategy": "cookie", "browser": true, "args": [], "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "phone_number", + "email", "action", - "files" + "verify_command" ], "type": "js", - "modulePath": "cursor/dump.js", - "sourceFile": "cursor/dump.js", - "navigateBefore": true + "modulePath": "district/auth.js", + "sourceFile": "district/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "cursor", - "name": "export", - "description": "Export the current cursor conversation to a Markdown file", + "site": "district", + "name": "search", + "aliases": [ + "s" + ], + "description": "Search District by Zomato across movies, events, dining, stores, activities, and play", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "domain": "www.district.in", + "strategy": "public", + "browser": false, "args": [ { - "name": "output", + "name": "query", "type": "str", + "required": true, + "positional": true, + "help": "Search query, for example \"hamlet\" or \"arijit\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Output file (default: /tmp/cursor-export.md)" - } - ], - "columns": [ - "Status", - "File", - "Messages" - ], - "type": "js", - "modulePath": "cursor/export.js", - "sourceFile": "cursor/export.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "extract-code", - "description": "Extract multi-line code blocks from the current Cursor conversation", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Code" - ], - "type": "js", - "modulePath": "cursor/extract-code.js", - "sourceFile": "cursor/extract-code.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "history", - "description": "List recent chat sessions from the Cursor sidebar", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Title" - ], - "type": "js", - "modulePath": "cursor/history.js", - "sourceFile": "cursor/history.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "model", - "description": "Get or switch the currently active AI model in Cursor", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Maximum rows to return (1-100)" + }, { - "name": "model-name", + "name": "tab", "type": "str", + "default": "all", "required": false, - "positional": true, - "help": "The ID of the model to switch to (e.g. claude-3.5-sonnet)" + "help": "Search tab: all, dining, events, movies, stores, activities, or play" } ], "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "cursor/model.js", - "sourceFile": "cursor/model.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "new", - "description": "Start a new Cursor chat or Composer session", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" + "rank", + "title", + "category", + "date", + "venue", + "price", + "url" ], - "type": "js", - "modulePath": "cursor/new.js", - "sourceFile": "cursor/new.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "read", - "description": "Read the current Cursor chat/composer conversation history", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Role", - "Text" + "tags": [ + "search" ], "type": "js", - "modulePath": "cursor/read.js", - "sourceFile": "cursor/read.js", - "navigateBefore": true + "modulePath": "district/search.js", + "sourceFile": "district/search.js" }, { - "site": "cursor", - "name": "screenshot", - "description": "Capture a snapshot of the current cursor window (DOM + Accessibility tree)", + "site": "district", + "name": "seats", + "description": "List available seats for a District movie showtime", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.district.in", + "strategy": "cookie", "browser": true, "args": [ { - "name": "output", + "name": "show", + "type": "str", + "required": true, + "positional": true, + "help": "District seat-layout URL or showId from district showtimes" + }, + { + "name": "format-id", + "type": "str", + "required": false, + "help": "District formatId from showtimes; required when show is a showId" + }, + { + "name": "content-id", + "type": "str", + "required": false, + "help": "District content id; required when show is a showId" + }, + { + "name": "class", + "type": "str", + "required": false, + "help": "Optional seat class filter, e.g. premium, premium xl, or recliner" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Number of seats to choose (1-10); without count, seats are listed normally" + }, + { + "name": "together", "type": "str", "required": false, - "help": "Output file path (default: /tmp/cursor-snapshot.txt)" + "help": "Require selected seats to be adjacent when count is provided" + }, + { + "name": "max-price", + "type": "float", + "required": false, + "help": "Maximum price per seat" + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Maximum seats to return (1-300)" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Maximum seconds to wait for the seat map to render" } ], "columns": [ - "Status", - "File" + "rank", + "seat", + "row", + "number", + "column", + "seatClass", + "price", + "status", + "flags", + "showId", + "formatId", + "url" ], "type": "js", - "modulePath": "cursor/screenshot.js", - "sourceFile": "cursor/screenshot.js", - "navigateBefore": true + "modulePath": "district/seats.js", + "sourceFile": "district/seats.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "cursor", - "name": "send", - "description": "Send a prompt directly into Cursor Composer/Chat", + "site": "district", + "name": "set-location", + "aliases": [ + "setlocation" + ], + "description": "Set the District browser session location for movie booking filters", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "www.district.in", + "strategy": "cookie", "browser": true, "args": [ { - "name": "text", + "name": "location", "type": "str", "required": true, "positional": true, - "help": "Text to send into Cursor" + "help": "City, area, mall, or locality, for example \"Bangalore\" or \"Indiranagar\"" + }, + { + "name": "rank", + "type": "int", + "default": 1, + "required": false, + "help": "Pick the Nth District location result (1-20), default: 1" + }, + { + "name": "timeout", + "type": "int", + "default": 45, + "required": false, + "help": "Maximum seconds to wait for the picker and location change" } ], "columns": [ - "Status", - "InjectedText" + "status", + "name", + "city", + "state", + "cityKey", + "cityId", + "placeId", + "subzoneId", + "lat", + "lng", + "availableTabs", + "source" ], "type": "js", - "modulePath": "cursor/send.js", - "sourceFile": "cursor/send.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "status", - "description": "Check active CDP connection to Cursor AI Editor", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" - ], - "type": "js", - "modulePath": "cursor/status.js", - "sourceFile": "cursor/status.js", - "navigateBefore": true + "modulePath": "district/set-location.js", + "sourceFile": "district/set-location.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "channels", - "description": "List channels in the current Discord server", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Channel", - "Type", - "guild_id", - "channel_id", - "url" + "site": "district", + "name": "showtimes", + "aliases": [ + "shows" ], - "type": "js", - "modulePath": "discord-app/channels.js", - "sourceFile": "discord-app/channels.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "delete", - "description": "Delete a message by its ID in the active Discord channel", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "description": "List District movie showtimes with location, time, cinema, language, price, and format filters", + "access": "read", + "domain": "www.district.in", + "strategy": "cookie", "browser": true, "args": [ { - "name": "message_id", - "type": "string", + "name": "movie", + "type": "str", "required": true, "positional": true, - "help": "The ID of the message to delete (visible via Developer Mode or the read command)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "discord-app/delete.js", - "sourceFile": "discord-app/delete.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "goto", - "description": "Open a Discord channel by id/name/url without sending messages", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Movie name or District movie URL" + }, { - "name": "guild", + "name": "date", "type": "str", "required": false, - "help": "Guild/server id or visible name" + "help": "Show date in YYYY-MM-DD format; defaults to District selected date" }, { - "name": "channel", + "name": "city", "type": "str", "required": false, - "help": "Channel id or visible name" + "help": "District city name/key, for example Bangalore or Bengaluru" }, { - "name": "url", + "name": "near", "type": "str", "required": false, - "help": "Discord channel URL" + "help": "Area, mall, or locality to search near, for example Indiranagar" }, { - "name": "timeout", + "name": "city-key", "type": "str", - "default": "8", "required": false, - "help": "Seconds to wait for Discord to show the route (default: 8)" - } - ], - "columns": [ - "Status", - "guild_id", - "channel_id", - "url" - ], - "type": "js", - "modulePath": "discord-app/goto.js", - "sourceFile": "discord-app/goto.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "members", - "description": "List online members in the current Discord channel", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Name", - "Status" - ], - "type": "js", - "modulePath": "discord-app/members.js", - "sourceFile": "discord-app/members.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "read", - "description": "Read recent messages from the active or targeted Discord channel", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Legacy District city key override, for example bengaluru" + }, { - "name": "count", + "name": "after", "type": "str", - "default": "20", "required": false, - "help": "Number of messages to read (default: 20)" + "help": "Only shows at or after HH:MM, 24-hour time" }, { - "name": "guild", + "name": "before", "type": "str", "required": false, - "help": "Guild/server id or visible name for targeted reads" + "help": "Only shows at or before HH:MM, 24-hour time" }, { - "name": "channel", + "name": "cinema", "type": "str", "required": false, - "help": "Channel id or visible name for targeted reads" + "help": "Filter cinema/theatre name, for example PVR, INOX, Orion" }, { - "name": "url", + "name": "language", "type": "str", "required": false, - "help": "Discord channel URL to open before reading" + "help": "Filter movie language, for example English, Hindi, Kannada" + }, + { + "name": "max-price", + "type": "float", + "required": false, + "help": "Only shows with at least one ticket class at or below this price" + }, + { + "name": "quality", + "type": "str", + "required": false, + "help": "Generic format/quality filter, for example 2D, 3D, IMAX, IMAX 3D, 4DX" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Maximum showtime rows to return (1-200)" } ], "columns": [ - "Author", - "Time", - "Message", - "channel_id", - "message_id" + "rank", + "movie", + "language", + "date", + "time", + "cinema", + "format", + "priceRange", + "available", + "showId", + "formatId", + "url" ], "type": "js", - "modulePath": "discord-app/read.js", - "sourceFile": "discord-app/read.js", - "navigateBefore": true + "modulePath": "district/showtimes.js", + "sourceFile": "district/showtimes.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "search", - "description": "Search messages in the current Discord server/channel (Cmd+F)", + "site": "district", + "name": "whoami", + "description": "Show the current logged-in district account", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.district.in", + "strategy": "cookie", "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - } - ], + "args": [], "columns": [ - "Index", - "Author", - "Message" - ], - "tags": [ - "search" + "logged_in", + "site", + "user_id", + "name", + "phone_number", + "email" ], "type": "js", - "modulePath": "discord-app/search.js", - "sourceFile": "discord-app/search.js", - "navigateBefore": true + "modulePath": "district/auth.js", + "sourceFile": "district/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "send", - "description": "Send a message in the active Discord channel", + "site": "facebook", + "name": "add-friend", + "description": "Send a friend request on Facebook", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "www.facebook.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "text", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Message to send" + "help": "Facebook username or profile URL" } ], "columns": [ - "Status" + "status", + "username" ], "type": "js", - "modulePath": "discord-app/send.js", - "sourceFile": "discord-app/send.js", - "navigateBefore": true + "modulePath": "facebook/add-friend.js", + "sourceFile": "facebook/add-friend.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "discord-app", - "name": "servers", - "description": "List all Discord servers (guilds) in the sidebar", + "site": "facebook", + "name": "events", + "description": "Browse Facebook event categories", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.facebook.com", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Number of categories" + } + ], "columns": [ - "Index", - "Server", - "guild_id", - "url" + "index", + "name" ], "type": "js", - "modulePath": "discord-app/servers.js", - "sourceFile": "discord-app/servers.js", - "navigateBefore": true + "modulePath": "facebook/events.js", + "sourceFile": "facebook/events.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "discord-app", - "name": "status", - "description": "Check active CDP connection to Discord Desktop", + "site": "facebook", + "name": "feed", + "description": "Get your Facebook news feed", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.facebook.com", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of posts" + } + ], "columns": [ - "Status", - "Url", - "Title" + "index", + "author", + "content", + "likes", + "comments", + "shares" ], "type": "js", - "modulePath": "discord-app/status.js", - "sourceFile": "discord-app/status.js", - "navigateBefore": true + "modulePath": "facebook/feed.js", + "sourceFile": "facebook/feed.js", + "navigateBefore": false }, { - "site": "discord-app", - "name": "thread-read", - "description": "Read recent messages from a Discord thread/post by id or URL", + "site": "facebook", + "name": "friends", + "description": "Get Facebook friend suggestions", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.facebook.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "thread", - "type": "str", - "required": false, - "help": "Thread/post id, or a full Discord thread/post URL" - }, - { - "name": "count", - "type": "str", - "default": "20", - "required": false, - "help": "Number of messages to read (default: 20)" - }, - { - "name": "guild", - "type": "str", - "required": false, - "help": "Parent guild/server id or visible name" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Parent forum/channel id or visible name" - }, - { - "name": "url", - "type": "str", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Discord thread/post URL" + "help": "Number of friend suggestions" } ], "columns": [ - "Author", - "Time", - "Message", - "channel_id", - "message_id" + "index", + "name", + "mutual" ], "type": "js", - "modulePath": "discord-app/thread-read.js", - "sourceFile": "discord-app/thread-read.js", - "navigateBefore": true + "modulePath": "facebook/friends.js", + "sourceFile": "facebook/friends.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "discord-app", - "name": "threads", - "description": "List visible Discord forum/thread posts in the active or targeted channel", + "site": "facebook", + "name": "groups", + "description": "List your Facebook groups", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.facebook.com", + "strategy": "cookie", "browser": true, "args": [ { "name": "limit", - "type": "str", - "default": "30", - "required": false, - "help": "Maximum thread/post cards to return (default: 30)" - }, - { - "name": "guild", - "type": "str", - "required": false, - "help": "Guild/server id or visible name for targeted thread listing" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Forum/channel id or visible name for targeted thread listing" - }, - { - "name": "url", - "type": "str", + "type": "int", + "default": 20, "required": false, - "help": "Discord forum/channel URL to open before listing threads" + "help": "Number of groups" } ], "columns": [ - "Index", - "Thread", - "Author", - "Updated", - "Preview", - "guild_id", - "channel_id", - "thread_id", + "index", + "name", + "last_post", "url" ], "type": "js", - "modulePath": "discord-app/threads.js", - "sourceFile": "discord-app/threads.js", - "navigateBefore": true + "modulePath": "facebook/groups.js", + "sourceFile": "facebook/groups.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "district", - "name": "checkout", - "description": "Select District movie seats and open the UPI QR payment scanner", + "site": "facebook", + "name": "join-group", + "description": "Join a Facebook group", "access": "write", - "domain": "www.district.in", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "show", + "name": "group", "type": "str", "required": true, "positional": true, - "help": "District seat-layout URL or showId from district showtimes" - }, - { - "name": "seats", - "type": "str", - "required": true, - "help": "Comma-separated seat labels to select, e.g. I22,I21" - }, - { - "name": "format-id", - "type": "str", - "required": false, - "help": "District formatId from showtimes; required when show is a showId" - }, - { - "name": "content-id", - "type": "str", - "required": false, - "help": "District content id; required when show is a showId" - }, - { - "name": "timeout", - "type": "int", - "default": 45, - "required": false, - "help": "Maximum seconds to wait for selection, review page, and payment handoff" - }, - { - "name": "payment", - "type": "str", - "default": "upi-qr", - "required": false, - "help": "Payment handoff target: upi-qr opens the scanner; review stops on order review" + "help": "Group ID or URL path (e.g. '1876150192925481' or group name)" } ], "columns": [ "status", - "movie", - "cinema", - "date", - "time", - "seats", - "ticketCount", - "orderAmount", - "bookingCharge", - "total", - "paymentMethod", - "paymentState", - "upiQrVisible", - "paymentAmount", - "paymentUrl", - "showId" + "group" ], "type": "js", - "modulePath": "district/checkout.js", - "sourceFile": "district/checkout.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "modulePath": "facebook/join-group.js", + "sourceFile": "facebook/join-group.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "district", - "name": "listings", - "aliases": [ - "ls" - ], - "description": "List public District by Zomato movies, events, and nearby going-out cards", - "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, + "site": "facebook", + "name": "login", + "description": "Open facebook login", + "access": "write", + "domain": "facebook.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "vanity", + "profile_url", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "facebook/auth.js", + "sourceFile": "facebook/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "facebook", + "name": "marketplace-inbox", + "description": "List recent Facebook Marketplace buyer/seller conversations", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "input", - "type": "str", - "default": "home", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "positional": true, - "help": "home, movies, events, a district.in URL, or a District path" - }, + "help": "Number of conversations to return" + } + ], + "columns": [ + "index", + "buyer", + "listing", + "snippet", + "time", + "unread" + ], + "type": "js", + "modulePath": "facebook/marketplace-inbox.js", + "sourceFile": "facebook/marketplace-inbox.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "marketplace-listings", + "description": "List your Facebook Marketplace seller listings", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Maximum rows to return (1-100)" + "help": "Number of listings to return" } ], "columns": [ - "rank", + "index", "title", - "category", - "date", - "venue", "price", - "url" + "status", + "listed", + "clicks", + "actions" ], "type": "js", - "modulePath": "district/listings.js", - "sourceFile": "district/listings.js" + "modulePath": "facebook/marketplace-listings.js", + "sourceFile": "facebook/marketplace-listings.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "district", - "name": "locations", - "aliases": [ - "location-search" - ], - "description": "Search District-supported cities, areas, malls, and places for booking filters", + "site": "facebook", + "name": "memories", + "description": "Get your Facebook memories (On This Day)", "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "City, area, mall, or locality, for example \"bangalore\" or \"indiranagar\"" - }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Maximum location rows to return (1-50)" + "help": "Number of memories" } ], "columns": [ - "rank", - "name", - "kind", - "city", - "state", - "cityKey", - "cityId", - "placeId", - "lat", - "lng", - "distanceKm", - "source" + "index", + "source", + "content", + "time" ], "type": "js", - "modulePath": "district/locations.js", - "sourceFile": "district/locations.js" + "modulePath": "facebook/memories.js", + "sourceFile": "facebook/memories.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "district", - "name": "login", - "description": "Open district login", - "access": "write", - "domain": "www.district.in", + "site": "facebook", + "name": "notifications", + "description": "Get recent Facebook notifications (includes unread / time / url / notif_id / notif_type columns)", + "access": "read", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Number of notifications (1-100)" + } + ], + "columns": [ + "index", + "unread", + "text", + "time", + "url", + "notif_id", + "notif_type" + ], + "type": "js", + "modulePath": "facebook/notifications.js", + "sourceFile": "facebook/notifications.js", + "navigateBefore": false + }, + { + "site": "facebook", + "name": "profile", + "description": "Get Facebook user/page profile info", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Facebook username or page name" + } + ], "columns": [ - "status", - "logged_in", - "site", - "user_id", "name", - "phone_number", - "email", - "action", - "verify_command" + "username", + "friends", + "followers", + "url" ], "type": "js", - "modulePath": "district/auth.js", - "sourceFile": "district/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "facebook/profile.js", + "sourceFile": "facebook/profile.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "district", + "site": "facebook", "name": "search", - "aliases": [ - "s" - ], - "description": "Search District by Zomato across movies, events, dining, stores, activities, and play", + "description": "Search Facebook for people, pages, or posts", "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "query", "type": "str", "required": true, "positional": true, - "help": "Search query, for example \"hamlet\" or \"arijit\"" + "help": "Search query" }, { "name": "limit", "type": "int", - "default": 20, - "required": false, - "help": "Maximum rows to return (1-100)" - }, - { - "name": "tab", - "type": "str", - "default": "all", + "default": 10, "required": false, - "help": "Search tab: all, dining, events, movies, stores, activities, or play" + "help": "Number of results" } ], "columns": [ - "rank", + "index", "title", - "category", - "date", - "venue", - "price", + "text", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "district/search.js", - "sourceFile": "district/search.js" + "modulePath": "facebook/search.js", + "sourceFile": "facebook/search.js", + "navigateBefore": false }, { - "site": "district", - "name": "seats", - "description": "List available seats for a District movie showtime", + "site": "facebook", + "name": "whoami", + "description": "Show the current logged-in facebook account", "access": "read", - "domain": "www.district.in", + "domain": "facebook.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "show", - "type": "str", - "required": true, - "positional": true, - "help": "District seat-layout URL or showId from district showtimes" - }, - { - "name": "format-id", - "type": "str", - "required": false, - "help": "District formatId from showtimes; required when show is a showId" - }, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "vanity", + "profile_url" + ], + "type": "js", + "modulePath": "facebook/auth.js", + "sourceFile": "facebook/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "ask", + "description": "Send a prompt to Gemini and return only the assistant response", + "access": "write", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "content-id", + "name": "prompt", "type": "str", - "required": false, - "help": "District content id; required when show is a showId" + "required": true, + "positional": true, + "help": "Prompt to send" }, { - "name": "class", - "type": "str", + "name": "model", + "type": "string", "required": false, - "help": "Optional seat class filter, e.g. premium, premium xl, or recliner" + "help": "Gemini model to use (e.g. \"2.5-flash\"). Use \"webcmd gemini models\" to list available values." }, { - "name": "count", + "name": "timeout", "type": "int", + "default": 60, "required": false, - "help": "Number of seats to choose (1-10); without count, seats are listed normally" + "help": "Max seconds to wait (default: 60)" }, { - "name": "together", + "name": "new", "type": "str", + "default": "false", "required": false, - "help": "Require selected seats to be adjacent when count is provided" - }, - { - "name": "max-price", - "type": "float", - "required": false, - "help": "Maximum price per seat" - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Maximum seats to return (1-300)" + "help": "Start a new chat first (true/false, default: false)" }, { - "name": "timeout", - "type": "int", - "default": 30, + "name": "thinking", + "type": "str", + "default": null, "required": false, - "help": "Maximum seconds to wait for the seat map to render" + "help": "Thinking level: standard or extended (omitted = leave unchanged)" } ], "columns": [ - "rank", - "seat", - "row", - "number", - "column", - "seatClass", - "price", - "status", - "flags", - "showId", - "formatId", - "url" + "response" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "district/seats.js", - "sourceFile": "district/seats.js", + "modulePath": "gemini/ask.js", + "sourceFile": "gemini/ask.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "district", - "name": "set-location", - "aliases": [ - "setlocation" - ], - "description": "Set the District browser session location for movie booking filters", + "site": "gemini", + "name": "deep-research", + "description": "Start a Gemini Deep Research run and confirm it", "access": "write", - "domain": "www.district.in", + "domain": "gemini.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "location", + "name": "prompt", "type": "str", "required": true, "positional": true, - "help": "City, area, mall, or locality, for example \"Bangalore\" or \"Indiranagar\"" + "help": "Prompt to send" }, { - "name": "rank", + "name": "timeout", "type": "int", - "default": 1, + "default": 180, "required": false, - "help": "Pick the Nth District location result (1-20), default: 1" + "help": "Max seconds for the overall command (default: 180; confirm-wait clamps internally to 6-20s)" }, { - "name": "timeout", - "type": "int", - "default": 45, + "name": "tool", + "type": "str", "required": false, - "help": "Maximum seconds to wait for the picker and location change" + "help": "Override tool label (default: Deep Research)" + }, + { + "name": "confirm", + "type": "str", + "required": false, + "help": "Override confirm button label (default: Start research)" } ], "columns": [ "status", - "name", - "city", - "state", - "cityKey", - "cityId", - "placeId", - "subzoneId", - "lat", - "lng", - "availableTabs", - "source" + "url" + ], + "tags": [ + "search" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "district/set-location.js", - "sourceFile": "district/set-location.js", + "modulePath": "gemini/deep-research.js", + "sourceFile": "gemini/deep-research.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "district", - "name": "showtimes", - "aliases": [ - "shows" - ], - "description": "List District movie showtimes with location, time, cinema, language, price, and format filters", + "site": "gemini", + "name": "deep-research-result", + "description": "Export Deep Research report URL from a Gemini conversation", "access": "read", - "domain": "www.district.in", + "domain": "gemini.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "movie", - "type": "str", - "required": true, - "positional": true, - "help": "Movie name or District movie URL" - }, - { - "name": "date", - "type": "str", - "required": false, - "help": "Show date in YYYY-MM-DD format; defaults to District selected date" - }, - { - "name": "city", - "type": "str", - "required": false, - "help": "District city name/key, for example Bangalore or Bengaluru" - }, - { - "name": "near", - "type": "str", - "required": false, - "help": "Area, mall, or locality to search near, for example Indiranagar" - }, - { - "name": "city-key", - "type": "str", - "required": false, - "help": "Legacy District city key override, for example bengaluru" - }, - { - "name": "after", - "type": "str", - "required": false, - "help": "Only shows at or after HH:MM, 24-hour time" - }, - { - "name": "before", - "type": "str", - "required": false, - "help": "Only shows at or before HH:MM, 24-hour time" - }, - { - "name": "cinema", - "type": "str", - "required": false, - "help": "Filter cinema/theatre name, for example PVR, INOX, Orion" - }, - { - "name": "language", + "name": "query", "type": "str", "required": false, - "help": "Filter movie language, for example English, Hindi, Kannada" - }, - { - "name": "max-price", - "type": "float", - "required": false, - "help": "Only shows with at least one ticket class at or below this price" + "positional": true, + "help": "Conversation title or URL (optional; defaults to latest conversation)" }, { - "name": "quality", + "name": "match", "type": "str", + "default": "contains", "required": false, - "help": "Generic format/quality filter, for example 2D, 3D, IMAX, IMAX 3D, 4DX" + "help": "Match mode", + "choices": [ + "contains", + "exact" + ] }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 50, + "default": 120, "required": false, - "help": "Maximum showtime rows to return (1-200)" + "help": "Max seconds to wait for Docs export (default: 120)" } ], "columns": [ - "rank", - "movie", - "language", - "date", - "time", - "cinema", - "format", - "priceRange", - "available", - "showId", - "formatId", - "url" + "response" ], + "tags": [ + "search" + ], + "defaultFormat": "plain", "type": "js", - "modulePath": "district/showtimes.js", - "sourceFile": "district/showtimes.js", + "modulePath": "gemini/deep-research-result.js", + "sourceFile": "gemini/deep-research-result.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "district", - "name": "whoami", - "description": "Show the current logged-in district account", + "site": "gemini", + "name": "detail", + "description": "Open a Gemini web conversation by id, URL, or sidebar title and read its turns", "access": "read", - "domain": "www.district.in", + "domain": "gemini.google.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Conversation id, /app/ URL, or sidebar title" + } + ], "columns": [ - "logged_in", - "site", - "user_id", - "name", - "phone_number", - "email" + "Index", + "Role", + "Text" ], "type": "js", - "modulePath": "district/auth.js", - "sourceFile": "district/auth.js", + "modulePath": "gemini/detail.js", + "sourceFile": "gemini/detail.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "duckduckgo", - "name": "search", - "description": "Search DuckDuckGo", + "site": "gemini", + "name": "history", + "description": "List visible Gemini web conversation history from the sidebar", "access": "read", - "domain": "html.duckduckgo.com", - "strategy": "public", + "domain": "gemini.google.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "keyword", + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max conversations to show" + } + ], + "columns": [ + "Index", + "Id", + "Title", + "Url" + ], + "type": "js", + "modulePath": "gemini/history.js", + "sourceFile": "gemini/history.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "image", + "description": "Generate images with Gemini web and save them locally", + "access": "write", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "Image prompt to send to Gemini" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "rt", + "type": "str", + "default": "1:1", "required": false, - "help": "Number of results per page (1-10). For multi-page, use --offset" + "help": "Ratio shorthand for aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3)" }, { - "name": "offset", - "type": "int", - "default": 0, + "name": "st", + "type": "str", + "default": "", "required": false, - "help": "Result offset for pagination (0, 10, 20...). Uses XHR POST internally" + "help": "Style shorthand, e.g. anime, icon, watercolor" }, { - "name": "region", + "name": "op", "type": "str", + "default": "~/tmp/gemini-images", "required": false, - "help": "Region code (e.g. jp-jp, us-en, cn-zh). Default: all regions" + "help": "Output directory shorthand" }, { - "name": "time", - "type": "str", + "name": "sd", + "type": "boolean", + "default": false, "required": false, - "help": "Time range: d (day), w (week), m (month), y (year)" - } - ], - "columns": [ - "rank", - "title", - "url", - "snippet", - "displayUrl", - "icon", - "resultType" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "duckduckgo/search.js", - "sourceFile": "duckduckgo/search.js" - }, - { - "site": "duckduckgo", - "name": "suggest", - "description": "DuckDuckGo search suggestions", - "access": "read", - "domain": "duckduckgo.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search query prefix" + "help": "Skip download shorthand; only show Gemini page link" }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 8, + "default": 240, "required": false, - "help": "Max number of suggestions" + "help": "Max seconds for the overall command (default: 240)" } ], "columns": [ - "phrase" + "status", + "file", + "link" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "duckduckgo/suggest.js", - "sourceFile": "duckduckgo/suggest.js" + "modulePath": "gemini/image.js", + "sourceFile": "gemini/image.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "facebook", - "name": "add-friend", - "description": "Send a friend request on Facebook", + "site": "gemini", + "name": "login", + "description": "Open gemini login", "access": "write", - "domain": "www.facebook.com", + "domain": "gemini.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Facebook username or profile URL" - } - ], + "args": [], "columns": [ "status", - "username" + "logged_in", + "site", + "name", + "action", + "verify_command" ], "type": "js", - "modulePath": "facebook/add-friend.js", - "sourceFile": "facebook/add-friend.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "gemini/auth.js", + "sourceFile": "gemini/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "facebook", - "name": "events", - "description": "Browse Facebook event categories", + "site": "gemini", + "name": "models", + "description": "List available Gemini models from the web UI", "access": "read", - "domain": "www.facebook.com", + "domain": "gemini.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Number of categories" - } - ], + "args": [], "columns": [ - "index", - "name" + "model", + "thinkingValues" ], "type": "js", - "modulePath": "facebook/events.js", - "sourceFile": "facebook/events.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "gemini/models.js", + "sourceFile": "gemini/models.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "facebook", - "name": "feed", - "description": "Get your Facebook news feed", + "site": "gemini", + "name": "new", + "description": "Start a new conversation in Gemini web chat", "access": "read", - "domain": "www.facebook.com", + "domain": "gemini.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of posts" - } - ], + "args": [], "columns": [ - "index", - "author", - "content", - "likes", - "comments", - "shares" + "Status", + "Action" ], "type": "js", - "modulePath": "facebook/feed.js", - "sourceFile": "facebook/feed.js", - "navigateBefore": false + "modulePath": "gemini/new.js", + "sourceFile": "gemini/new.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "facebook", - "name": "friends", - "description": "Get Facebook friend suggestions", + "site": "gemini", + "name": "read", + "description": "Read the turns visible in the current Gemini web conversation", "access": "read", - "domain": "www.facebook.com", + "domain": "gemini.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of friend suggestions" - } - ], + "args": [], "columns": [ - "index", - "name", - "mutual" + "Index", + "Role", + "Text" ], "type": "js", - "modulePath": "facebook/friends.js", - "sourceFile": "facebook/friends.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "gemini/read.js", + "sourceFile": "gemini/read.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "facebook", - "name": "groups", - "description": "List your Facebook groups", + "site": "gemini", + "name": "status", + "description": "Check Gemini web page availability and login state", "access": "read", - "domain": "www.facebook.com", + "domain": "gemini.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of groups" - } - ], - "columns": [ - "index", - "name", - "last_post", - "url" - ], - "type": "js", - "modulePath": "facebook/groups.js", - "sourceFile": "facebook/groups.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "join-group", - "description": "Join a Facebook group", - "access": "write", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "group", - "type": "str", - "required": true, - "positional": true, - "help": "Group ID or URL path (e.g. '1876150192925481' or group name)" - } - ], + "args": [], "columns": [ - "status", - "group" + "Status", + "Login", + "Url" ], "type": "js", - "modulePath": "facebook/join-group.js", - "sourceFile": "facebook/join-group.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "gemini/status.js", + "sourceFile": "gemini/status.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "facebook", - "name": "login", - "description": "Open facebook login", - "access": "write", - "domain": "facebook.com", + "site": "gemini", + "name": "whoami", + "description": "Show the current logged-in gemini account", + "access": "read", + "domain": "gemini.google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "status", "logged_in", "site", - "user_id", - "vanity", - "profile_url", - "action", - "verify_command" + "name" ], "type": "js", - "modulePath": "facebook/auth.js", - "sourceFile": "facebook/auth.js", + "modulePath": "gemini/auth.js", + "sourceFile": "gemini/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "facebook", - "name": "marketplace-inbox", - "description": "List recent Facebook Marketplace buyer/seller conversations", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", + "site": "geogebra", + "name": "add-circle", + "description": "Create a circle by center+radius or center+point", + "access": "write", + "example": "webcmd geogebra add-circle --center A --radius 3", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "center", + "type": "str", + "required": true, + "help": "Center point label (e.g. A)" + }, + { + "name": "radius", + "type": "str", "required": false, - "help": "Number of conversations to return" + "help": "Radius value (number) or a point label on the circle" + }, + { + "name": "point", + "type": "str", + "required": false, + "help": "Alternative: a point label on the circle (use instead of --radius for Circle(center,point))" } ], "columns": [ - "index", - "buyer", - "listing", - "snippet", - "time", - "unread" + "label", + "center", + "radius" ], "type": "js", - "modulePath": "facebook/marketplace-inbox.js", - "sourceFile": "facebook/marketplace-inbox.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "geogebra/add-circle.js", + "sourceFile": "geogebra/add-circle.js", + "navigateBefore": false }, { - "site": "facebook", - "name": "marketplace-listings", - "description": "List your Facebook Marketplace seller listings", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", + "site": "geogebra", + "name": "add-line", + "description": "Create a line through two points or a segment between two points", + "access": "write", + "example": "webcmd geogebra add-line --points A,B --type segment", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "points", + "type": "str", + "required": true, + "help": "Two point labels separated by comma (e.g. \"A,B\")" + }, + { + "name": "type", + "type": "str", + "default": "line", "required": false, - "help": "Number of listings to return" + "help": "Type: line, segment, or ray (default: line)", + "choices": [ + "line", + "segment", + "ray" + ] } ], "columns": [ - "index", - "title", - "price", - "status", - "listed", - "clicks", - "actions" + "label", + "type", + "points" ], "type": "js", - "modulePath": "facebook/marketplace-listings.js", - "sourceFile": "facebook/marketplace-listings.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "geogebra/add-line.js", + "sourceFile": "geogebra/add-line.js", + "navigateBefore": false }, { - "site": "facebook", - "name": "memories", - "description": "Get your Facebook memories (On This Day)", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", + "site": "geogebra", + "name": "add-point", + "description": "Create a point with given label and coordinates", + "access": "write", + "example": "webcmd geogebra add-point --name A --coords 1,2", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of memories" + "name": "name", + "type": "str", + "required": true, + "help": "Point label (e.g. A, B, P1)" + }, + { + "name": "coords", + "type": "str", + "required": true, + "help": "Coordinates as x,y (e.g. \"1,2\")" } ], "columns": [ - "index", - "source", - "content", - "time" + "name", + "x", + "y" ], "type": "js", - "modulePath": "facebook/memories.js", - "sourceFile": "facebook/memories.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "geogebra/add-point.js", + "sourceFile": "geogebra/add-point.js", + "navigateBefore": false }, { - "site": "facebook", - "name": "notifications", - "description": "Get recent Facebook notifications (includes unread / time / url / notif_id / notif_type columns)", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", + "site": "geogebra", + "name": "add-polygon", + "description": "Create a polygon from a list of point labels", + "access": "write", + "example": "webcmd geogebra add-polygon --points A,B,C", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Number of notifications (1-100)" + "name": "points", + "type": "str", + "required": true, + "help": "Comma-separated point labels (e.g. \"A,B,C\" or \"A,B,C,D\")" } ], "columns": [ - "index", - "unread", - "text", - "time", - "url", - "notif_id", - "notif_type" + "label", + "vertices" ], "type": "js", - "modulePath": "facebook/notifications.js", - "sourceFile": "facebook/notifications.js", + "modulePath": "geogebra/add-polygon.js", + "sourceFile": "geogebra/add-polygon.js", "navigateBefore": false }, { - "site": "facebook", - "name": "profile", - "description": "Get Facebook user/page profile info", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", + "site": "geogebra", + "name": "eval", + "description": "Execute one or more GeoGebra command strings (semicolon-separated)", + "access": "write", + "example": "webcmd geogebra eval \"A=(0,0);B=(4,0);c=Circle(A,B);d=Circle(B,A);C=Intersect(c,d,1);Polygon(A,B,C)\"", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, "args": [ { - "name": "username", + "name": "command", "type": "str", "required": true, "positional": true, - "help": "Facebook username or page name" + "help": "GeoGebra command string (use ; to chain multiple commands)" } ], "columns": [ - "name", - "username", - "friends", - "followers", - "url" + "command", + "result" ], "type": "js", - "modulePath": "facebook/profile.js", - "sourceFile": "facebook/profile.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "geogebra/eval.js", + "sourceFile": "geogebra/eval.js", + "navigateBefore": false }, { - "site": "facebook", - "name": "search", - "description": "Search Facebook for people, pages, or posts", + "site": "geogebra", + "name": "hexagon", + "description": "Draw a regular hexagon centered at the origin", + "access": "write", + "example": "webcmd geogebra hexagon --size 3", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "size", + "type": "str", + "default": "2", + "required": false, + "help": "Radius of the hexagon (default: 2)" + } + ], + "columns": [ + "step", + "result" + ], + "type": "js", + "modulePath": "geogebra/hexagon.js", + "sourceFile": "geogebra/hexagon.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "info", + "description": "Get detailed properties of a GeoGebra object", "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", + "example": "webcmd geogebra info --name A", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, "args": [ { - "name": "query", + "name": "name", "type": "str", "required": true, - "positional": true, - "help": "Search query" - }, + "help": "Object label (e.g. A, c1, poly1)" + } + ], + "columns": [ + "property", + "value" + ], + "type": "js", + "modulePath": "geogebra/info.js", + "sourceFile": "geogebra/info.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "list", + "description": "List all geometric objects on the GeoGebra canvas", + "access": "read", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ { - "name": "limit", - "type": "int", - "default": 10, + "name": "type", + "type": "str", "required": false, - "help": "Number of results" + "help": "Filter by object type (e.g. \"point\", \"line\", \"circle\")" } ], "columns": [ - "index", - "title", - "text", - "url" + "name", + "type", + "value", + "visible" ], - "tags": [ - "search" + "type": "js", + "modulePath": "geogebra/list.js", + "sourceFile": "geogebra/list.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "triangle", + "description": "Draw an equilateral triangle from a horizontal base segment", + "access": "write", + "example": "webcmd geogebra triangle --size 4", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "size", + "type": "str", + "default": "2", + "required": false, + "help": "Side length of the triangle (default: 2)" + } + ], + "columns": [ + "step", + "result" ], "type": "js", - "modulePath": "facebook/search.js", - "sourceFile": "facebook/search.js", + "modulePath": "geogebra/triangle.js", + "sourceFile": "geogebra/triangle.js", "navigateBefore": false }, { - "site": "facebook", + "site": "github", + "name": "login", + "description": "Open github login", + "access": "write", + "domain": "github.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "id", + "username", + "name", + "url", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "github/auth.js", + "sourceFile": "github/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "github", "name": "whoami", - "description": "Show the current logged-in facebook account", + "description": "Show the current logged-in github account", "access": "read", - "domain": "facebook.com", + "domain": "github.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ "logged_in", "site", - "user_id", - "vanity", - "profile_url" + "id", + "username", + "name", + "url" ], "type": "js", - "modulePath": "facebook/auth.js", - "sourceFile": "facebook/auth.js", + "modulePath": "github/auth.js", + "sourceFile": "github/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", + "site": "grok", "name": "ask", - "description": "Send a prompt to Gemini and return only the assistant response", + "description": "Send a message to Grok and get response", "access": "write", - "domain": "gemini.google.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { "name": "prompt", - "type": "str", + "type": "string", "required": true, "positional": true, - "help": "Prompt to send" - }, - { - "name": "model", - "type": "string", - "required": false, - "help": "Gemini model to use (e.g. \"2.5-flash\"). Use \"webcmd gemini models\" to list available values." + "help": "Prompt to send to Grok" }, { "name": "timeout", "type": "int", - "default": 60, + "default": 120, "required": false, - "help": "Max seconds to wait (default: 60)" + "help": "Max seconds to wait for response (default: 120)" }, { "name": "new", - "type": "str", - "default": "false", - "required": false, - "help": "Start a new chat first (true/false, default: false)" - }, - { - "name": "thinking", - "type": "str", - "default": null, + "type": "boolean", + "default": false, "required": false, - "help": "Thinking level: standard or extended (omitted = leave unchanged)" + "help": "Start a new chat before sending (default: false)" } ], "columns": [ "response" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "gemini/ask.js", - "sourceFile": "gemini/ask.js", - "navigateBefore": false, + "modulePath": "grok/ask.js", + "sourceFile": "grok/ask.js", + "navigateBefore": "https://grok.com", "siteSession": "persistent" }, { - "site": "gemini", - "name": "deep-research", - "description": "Start a Gemini Deep Research run and confirm it", + "site": "grok", + "name": "delete", + "description": "Delete a Grok conversation by ID. Grok takes effect immediately with no confirmation dialog — require --yes to actually delete.", "access": "write", - "domain": "gemini.google.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", - "type": "str", + "name": "id", + "type": "string", "required": true, "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 180, - "required": false, - "help": "Max seconds for the overall command (default: 180; confirm-wait clamps internally to 6-20s)" - }, - { - "name": "tool", - "type": "str", - "required": false, - "help": "Override tool label (default: Deep Research)" + "help": "Conversation UUID or grok.com/c/ URL" }, { - "name": "confirm", - "type": "str", + "name": "yes", + "type": "boolean", + "default": false, "required": false, - "help": "Override confirm button label (default: Start research)" + "help": "Actually delete (default is a dry-run preview)" } ], "columns": [ "status", - "url" - ], - "tags": [ - "search" + "id" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "gemini/deep-research.js", - "sourceFile": "gemini/deep-research.js", - "navigateBefore": false, + "modulePath": "grok/delete.js", + "sourceFile": "grok/delete.js", + "navigateBefore": "https://grok.com", "siteSession": "persistent" }, { - "site": "gemini", - "name": "deep-research-result", - "description": "Export Deep Research report URL from a Gemini conversation", + "site": "grok", + "name": "detail", + "description": "Open a Grok conversation by ID and read its messages", "access": "read", - "domain": "gemini.google.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", + "name": "id", "type": "str", - "required": false, + "required": true, "positional": true, - "help": "Conversation title or URL (optional; defaults to latest conversation)" + "help": "Session ID (UUID) or full https://grok.com/c/ URL" }, { - "name": "match", - "type": "str", - "default": "contains", + "name": "markdown", + "type": "boolean", + "default": false, "required": false, - "help": "Match mode", - "choices": [ - "contains", - "exact" - ] + "help": "Emit assistant replies as markdown" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "grok/detail.js", + "sourceFile": "grok/detail.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "export", + "description": "Export all visible Grok conversation history metadata", + "access": "read", + "example": "webcmd grok export -f yaml", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 0, + "required": false, + "help": "Max conversations to export; 0 means all loaded history" }, { - "name": "timeout", + "name": "maxScrolls", "type": "int", - "default": 120, + "default": 80, "required": false, - "help": "Max seconds to wait for Docs export (default: 120)" + "help": "Max history-list scroll rounds when limit is 0 (max 500)" } ], "columns": [ - "response" - ], - "tags": [ - "search" + "index", + "id", + "title", + "date", + "url" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "gemini/deep-research-result.js", - "sourceFile": "gemini/deep-research-result.js", + "modulePath": "grok/export.js", + "sourceFile": "grok/export.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", - "name": "detail", - "description": "Open a Gemini web conversation by id, URL, or sidebar title and read its turns", + "site": "grok", + "name": "export-all", + "description": "Export Grok conversation history and each conversation transcript", "access": "read", - "domain": "gemini.google.com", + "example": "webcmd grok export-all --limit 5 -f json", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Conversation id, /app/ URL, or sidebar title" + "name": "limit", + "type": "int", + "default": 0, + "required": false, + "help": "Max conversations to export; 0 means all loaded history" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Skip this many conversations before exporting" + }, + { + "name": "manifestPath", + "type": "string", + "default": "", + "required": false, + "help": "Optional grok/export JSON manifest path; skips history dialog and visits listed /c pages directly" + }, + { + "name": "maxScrolls", + "type": "int", + "default": 80, + "required": false, + "help": "Max history-list scroll rounds when limit is 0 (max 500)" + }, + { + "name": "pageScrolls", + "type": "int", + "default": 30, + "required": false, + "help": "Max per-conversation scroll-to-bottom rounds (max 200)" + }, + { + "name": "pageTimeoutMs", + "type": "int", + "default": 30000, + "required": false, + "help": "Max wait for each conversation page to show messages" + }, + { + "name": "delayMinMs", + "type": "int", + "default": 0, + "required": false, + "help": "Minimum polite delay after a conversation page loads" + }, + { + "name": "delayMaxMs", + "type": "int", + "default": 5000, + "required": false, + "help": "Maximum polite delay after a conversation page loads" } ], "columns": [ - "Index", - "Role", - "Text" + "index", + "id", + "title", + "date", + "url", + "status", + "messageCount", + "error", + "messagesJson" ], "type": "js", - "modulePath": "gemini/detail.js", - "sourceFile": "gemini/detail.js", + "modulePath": "grok/export-all.js", + "sourceFile": "grok/export-all.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", + "site": "grok", "name": "history", - "description": "List visible Gemini web conversation history from the sidebar", + "description": "List recent Grok conversations from the sidebar (requires login)", "access": "read", - "domain": "gemini.google.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ @@ -7032,91 +6613,83 @@ "type": "int", "default": 20, "required": false, - "help": "Max conversations to show" + "help": "Max conversations to show (default 20, max 100)" } ], "columns": [ "Index", - "Id", "Title", "Url" ], "type": "js", - "modulePath": "gemini/history.js", - "sourceFile": "gemini/history.js", + "modulePath": "grok/history.js", + "sourceFile": "grok/history.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", + "site": "grok", "name": "image", - "description": "Generate images with Gemini web and save them locally", + "description": "Generate images on grok.com and return image URLs", "access": "write", - "domain": "gemini.google.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { "name": "prompt", - "type": "str", + "type": "string", "required": true, "positional": true, - "help": "Image prompt to send to Gemini" - }, - { - "name": "rt", - "type": "str", - "default": "1:1", - "required": false, - "help": "Ratio shorthand for aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3)" - }, - { - "name": "st", - "type": "str", - "default": "", - "required": false, - "help": "Style shorthand, e.g. anime, icon, watercolor" + "help": "Image generation prompt" }, { - "name": "op", - "type": "str", - "default": "~/tmp/gemini-images", + "name": "timeout", + "type": "int", + "default": 240, "required": false, - "help": "Output directory shorthand" + "help": "Max seconds to wait for the image (default: 240)" }, { - "name": "sd", + "name": "new", "type": "boolean", "default": false, "required": false, - "help": "Skip download shorthand; only show Gemini page link" + "help": "Start a new chat before sending (default: false)" }, { - "name": "timeout", + "name": "count", "type": "int", - "default": 240, + "default": 1, "required": false, - "help": "Max seconds for the overall command (default: 240)" + "help": "Minimum images to wait for before returning (default: 1)" + }, + { + "name": "out", + "type": "string", + "default": "", + "required": false, + "help": "Directory to save downloaded images (uses browser session to bypass auth)" } ], "columns": [ - "status", - "file", - "link" + "url", + "width", + "height", + "path" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "gemini/image.js", - "sourceFile": "gemini/image.js", - "navigateBefore": false, + "modulePath": "grok/image.js", + "sourceFile": "grok/image.js", + "navigateBefore": "https://grok.com", "siteSession": "persistent" }, { - "site": "gemini", + "site": "grok", "name": "login", - "description": "Open gemini login", + "description": "Open grok login", "access": "write", - "domain": "gemini.google.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [], @@ -7124,545 +6697,549 @@ "status", "logged_in", "site", + "user_id", "name", "action", "verify_command" ], "type": "js", - "modulePath": "gemini/auth.js", - "sourceFile": "gemini/auth.js", + "modulePath": "grok/auth.js", + "sourceFile": "grok/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", - "name": "models", - "description": "List available Gemini models from the web UI", - "access": "read", - "domain": "gemini.google.com", + "site": "grok", + "name": "new", + "description": "Start a new conversation in Grok", + "access": "write", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "model", - "thinkingValues" + "Status" ], "type": "js", - "modulePath": "gemini/models.js", - "sourceFile": "gemini/models.js", + "modulePath": "grok/new.js", + "sourceFile": "grok/new.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", - "name": "new", - "description": "Start a new conversation in Gemini web chat", - "access": "read", - "domain": "gemini.google.com", + "site": "grok", + "name": "pin", + "description": "Pin a Grok conversation by ID", + "access": "write", + "domain": "grok.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID or grok.com/c/ URL" + } + ], "columns": [ - "Status", - "Action" + "status", + "id" ], "type": "js", - "modulePath": "gemini/new.js", - "sourceFile": "gemini/new.js", - "navigateBefore": false, + "modulePath": "grok/pin.js", + "sourceFile": "grok/pin.js", + "navigateBefore": "https://grok.com", "siteSession": "persistent" }, { - "site": "gemini", + "site": "grok", "name": "read", - "description": "Read the turns visible in the current Gemini web conversation", + "description": "Read messages in the current Grok conversation", "access": "read", - "domain": "gemini.google.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" + } + ], "columns": [ - "Index", "Role", "Text" ], "type": "js", - "modulePath": "gemini/read.js", - "sourceFile": "gemini/read.js", + "modulePath": "grok/read.js", + "sourceFile": "grok/read.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", + "site": "grok", + "name": "send", + "description": "Fire-and-forget: send a prompt to Grok without waiting for the reply", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send to Grok" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" + } + ], + "columns": [ + "Status", + "Prompt" + ], + "type": "js", + "modulePath": "grok/send.js", + "sourceFile": "grok/send.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", "name": "status", - "description": "Check Gemini web page availability and login state", + "description": "Check Grok page availability, login state, current session and model", "access": "read", - "domain": "gemini.google.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ "Status", "Login", + "Model", + "SessionId", "Url" ], "type": "js", - "modulePath": "gemini/status.js", - "sourceFile": "gemini/status.js", + "modulePath": "grok/status.js", + "sourceFile": "grok/status.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", + "site": "grok", + "name": "unpin", + "description": "Unpin a Grok conversation by ID", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID or grok.com/c/ URL" + } + ], + "columns": [ + "status", + "id" + ], + "type": "js", + "modulePath": "grok/pin.js", + "sourceFile": "grok/pin.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" + }, + { + "site": "grok", "name": "whoami", - "description": "Show the current logged-in gemini account", + "description": "Show the current logged-in grok account", "access": "read", - "domain": "gemini.google.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ "logged_in", "site", + "user_id", "name" ], "type": "js", - "modulePath": "gemini/auth.js", - "sourceFile": "gemini/auth.js", + "modulePath": "grok/auth.js", + "sourceFile": "grok/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "geogebra", - "name": "add-circle", - "description": "Create a circle by center+radius or center+point", - "access": "write", - "example": "webcmd geogebra add-circle --center A --radius 3", - "domain": "www.geogebra.org", + "site": "hf", + "name": "datasets", + "description": "Top Hugging Face datasets (downloads / likes / trending / freshness).", + "access": "read", + "domain": "huggingface.co", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "center", - "type": "str", - "required": true, - "help": "Center point label (e.g. A)" + "name": "sort", + "type": "string", + "default": "downloads", + "required": false, + "help": "Sort key: downloads, likes, trending, created_at, last_modified" }, { - "name": "radius", - "type": "str", + "name": "search", + "type": "string", "required": false, - "help": "Radius value (number) or a point label on the circle" + "help": "Optional name/owner substring filter." }, { - "name": "point", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Alternative: a point label on the circle (use instead of --radius for Circle(center,point))" + "help": "Max datasets (max 100; one API page)." } ], "columns": [ - "label", - "center", - "radius" + "rank", + "id", + "author", + "downloads", + "likes", + "tags", + "lastModified", + "url" ], "type": "js", - "modulePath": "geogebra/add-circle.js", - "sourceFile": "geogebra/add-circle.js", - "navigateBefore": false + "modulePath": "hf/datasets.js", + "sourceFile": "hf/datasets.js" }, { - "site": "geogebra", - "name": "add-line", - "description": "Create a line through two points or a segment between two points", + "site": "hf", + "name": "login", + "description": "Open hf login", "access": "write", - "example": "webcmd geogebra add-line --points A,B --type segment", - "domain": "www.geogebra.org", - "strategy": "public", + "domain": "huggingface.co", + "strategy": "cookie", "browser": true, - "args": [ - { - "name": "points", - "type": "str", - "required": true, - "help": "Two point labels separated by comma (e.g. \"A,B\")" - }, - { - "name": "type", - "type": "str", - "default": "line", - "required": false, - "help": "Type: line, segment, or ray (default: line)", - "choices": [ - "line", - "segment", - "ray" - ] - } - ], + "args": [], "columns": [ - "label", + "status", + "logged_in", + "site", + "username", + "fullname", "type", - "points" + "action", + "verify_command" ], "type": "js", - "modulePath": "geogebra/add-line.js", - "sourceFile": "geogebra/add-line.js", - "navigateBefore": false + "modulePath": "hf/auth.js", + "sourceFile": "hf/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "geogebra", - "name": "add-point", - "description": "Create a point with given label and coordinates", - "access": "write", - "example": "webcmd geogebra add-point --name A --coords 1,2", - "domain": "www.geogebra.org", + "site": "hf", + "name": "models", + "description": "Top Hugging Face models (downloads / likes / trending / freshness).", + "access": "read", + "domain": "huggingface.co", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "name", - "type": "str", - "required": true, - "help": "Point label (e.g. A, B, P1)" - }, + "name": "sort", + "type": "string", + "default": "downloads", + "required": false, + "help": "Sort key: downloads, likes, trending, created_at, last_modified" + }, { - "name": "coords", - "type": "str", - "required": true, - "help": "Coordinates as x,y (e.g. \"1,2\")" + "name": "search", + "type": "string", + "required": false, + "help": "Optional name/owner substring filter (e.g. \"llama\", \"mistralai/\")" + }, + { + "name": "pipeline", + "type": "string", + "required": false, + "help": "Filter by pipeline tag (e.g. text-generation, image-classification)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max models (max 100; one API page)." } ], "columns": [ - "name", - "x", - "y" + "rank", + "id", + "author", + "pipelineTag", + "downloads", + "likes", + "tags", + "lastModified", + "url" ], "type": "js", - "modulePath": "geogebra/add-point.js", - "sourceFile": "geogebra/add-point.js", - "navigateBefore": false + "modulePath": "hf/models.js", + "sourceFile": "hf/models.js" }, { - "site": "geogebra", - "name": "add-polygon", - "description": "Create a polygon from a list of point labels", - "access": "write", - "example": "webcmd geogebra add-polygon --points A,B,C", - "domain": "www.geogebra.org", + "site": "hf", + "name": "paper", + "description": "Hugging Face paper detail by arXiv id (full title / summary / authors / AI keywords)", + "access": "read", + "domain": "huggingface.co", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "points", + "name": "id", "type": "str", "required": true, - "help": "Comma-separated point labels (e.g. \"A,B,C\" or \"A,B,C,D\")" + "positional": true, + "help": "arXiv id (e.g. \"1706.03762\") — same value HF uses to mirror the paper" } ], "columns": [ - "label", - "vertices" + "id", + "title", + "authors", + "publishedAt", + "upvotes", + "aiKeywords", + "summary", + "aiSummary", + "url" ], "type": "js", - "modulePath": "geogebra/add-polygon.js", - "sourceFile": "geogebra/add-polygon.js", - "navigateBefore": false + "modulePath": "hf/paper.js", + "sourceFile": "hf/paper.js" }, { - "site": "geogebra", - "name": "eval", - "description": "Execute one or more GeoGebra command strings (semicolon-separated)", - "access": "write", - "example": "webcmd geogebra eval \"A=(0,0);B=(4,0);c=Circle(A,B);d=Circle(B,A);C=Intersect(c,d,1);Polygon(A,B,C)\"", - "domain": "www.geogebra.org", + "site": "hf", + "name": "spaces", + "description": "Top Hugging Face Spaces (likes / created_at / last_modified).", + "access": "read", + "domain": "huggingface.co", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "command", - "type": "str", - "required": true, - "positional": true, - "help": "GeoGebra command string (use ; to chain multiple commands)" + "name": "sort", + "type": "string", + "default": "likes", + "required": false, + "help": "Sort key: likes, created_at, last_modified" + }, + { + "name": "search", + "type": "string", + "required": false, + "help": "Optional name/owner substring filter (e.g. \"stability\", \"openai/\")" + }, + { + "name": "sdk", + "type": "string", + "required": false, + "help": "Filter by Space SDK: gradio / streamlit / docker / static" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max spaces (max 100; one API page)." } ], "columns": [ - "command", - "result" + "rank", + "id", + "author", + "sdk", + "likes", + "tags", + "lastModified", + "url" ], "type": "js", - "modulePath": "geogebra/eval.js", - "sourceFile": "geogebra/eval.js", - "navigateBefore": false + "modulePath": "hf/spaces.js", + "sourceFile": "hf/spaces.js" }, { - "site": "geogebra", - "name": "hexagon", - "description": "Draw a regular hexagon centered at the origin", - "access": "write", - "example": "webcmd geogebra hexagon --size 3", - "domain": "www.geogebra.org", + "site": "hf", + "name": "top", + "description": "Top upvoted Hugging Face papers", + "access": "read", + "domain": "huggingface.co", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "size", + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of papers" + }, + { + "name": "all", + "type": "bool", + "default": false, + "required": false, + "help": "Return all papers (ignore limit)" + }, + { + "name": "date", "type": "str", - "default": "2", "required": false, - "help": "Radius of the hexagon (default: 2)" + "help": "Date (YYYY-MM-DD), defaults to most recent" + }, + { + "name": "period", + "type": "str", + "default": "daily", + "required": false, + "help": "Time period: daily, weekly, or monthly", + "choices": [ + "daily", + "weekly", + "monthly" + ] } ], "columns": [ - "step", - "result" + "rank", + "id", + "title", + "upvotes", + "authors" ], "type": "js", - "modulePath": "geogebra/hexagon.js", - "sourceFile": "geogebra/hexagon.js", - "navigateBefore": false + "modulePath": "hf/top.js", + "sourceFile": "hf/top.js" }, { - "site": "geogebra", - "name": "info", - "description": "Get detailed properties of a GeoGebra object", + "site": "hf", + "name": "whoami", + "description": "Show the current logged-in hf account", "access": "read", - "example": "webcmd geogebra info --name A", - "domain": "www.geogebra.org", - "strategy": "public", + "domain": "huggingface.co", + "strategy": "cookie", "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "help": "Object label (e.g. A, c1, poly1)" - } - ], + "args": [], "columns": [ - "property", - "value" + "logged_in", + "site", + "username", + "fullname", + "type" ], "type": "js", - "modulePath": "geogebra/info.js", - "sourceFile": "geogebra/info.js", - "navigateBefore": false + "modulePath": "hf/auth.js", + "sourceFile": "hf/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "geogebra", - "name": "list", - "description": "List all geometric objects on the GeoGebra canvas", + "site": "imdb", + "name": "person", + "description": "Get actor or director info", "access": "read", - "domain": "www.geogebra.org", + "domain": "www.imdb.com", "strategy": "public", "browser": true, "args": [ { - "name": "type", + "name": "id", "type": "str", + "required": true, + "positional": true, + "help": "IMDb person ID (nm0634240) or URL" + }, + { + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Filter by object type (e.g. \"point\", \"line\", \"circle\")" + "help": "Max filmography entries" } ], "columns": [ - "name", - "type", - "value", - "visible" + "field", + "value" ], "type": "js", - "modulePath": "geogebra/list.js", - "sourceFile": "geogebra/list.js", - "navigateBefore": false + "modulePath": "imdb/person.js", + "sourceFile": "imdb/person.js" }, { - "site": "geogebra", - "name": "triangle", - "description": "Draw an equilateral triangle from a horizontal base segment", - "access": "write", - "example": "webcmd geogebra triangle --size 4", - "domain": "www.geogebra.org", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "size", - "type": "str", - "default": "2", - "required": false, - "help": "Side length of the triangle (default: 2)" - } - ], - "columns": [ - "step", - "result" - ], - "type": "js", - "modulePath": "geogebra/triangle.js", - "sourceFile": "geogebra/triangle.js", - "navigateBefore": false - }, - { - "site": "github", - "name": "login", - "description": "Open github login", - "access": "write", - "domain": "github.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "id", - "username", - "name", - "url", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "github/auth.js", - "sourceFile": "github/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "github", - "name": "whoami", - "description": "Show the current logged-in github account", - "access": "read", - "domain": "github.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "id", - "username", - "name", - "url" - ], - "type": "js", - "modulePath": "github/auth.js", - "sourceFile": "github/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "google", - "name": "images", - "description": "Search Google Images for photos and image results", + "site": "imdb", + "name": "reviews", + "description": "Get user reviews for a movie or TV show", "access": "read", - "domain": "google.com", + "domain": "www.imdb.com", "strategy": "public", "browser": true, "args": [ { - "name": "keyword", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Image search query" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of image results (1-100)" - }, - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language short code (e.g. en, zh)" - }, - { - "name": "resolve", - "type": "bool", - "default": true, - "required": false, - "help": "Click image previews to resolve original imgurl values" - } - ], - "columns": [ - "rank", - "title", - "imageUrl", - "thumbnailUrl", - "sourceUrl", - "source", - "width", - "height" - ], - "type": "js", - "modulePath": "google/images.js", - "sourceFile": "google/images.js", - "navigateBefore": false - }, - { - "site": "google", - "name": "news", - "description": "Get Google News headlines", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "keyword", - "type": "str", - "required": false, - "positional": true, - "help": "Search query (omit for top stories)" + "help": "IMDb title ID (tt1375666) or URL" }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Number of results" - }, - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language short code (e.g. en, zh)" - }, - { - "name": "region", - "type": "str", - "default": "US", - "required": false, - "help": "Region code (e.g. US, CN)" + "help": "Number of reviews" } ], "columns": [ + "rank", "title", - "source", + "rating", + "author", "date", - "url" + "text" ], "type": "js", - "modulePath": "google/news.js", - "sourceFile": "google/news.js" + "modulePath": "imdb/reviews.js", + "sourceFile": "imdb/reviews.js" }, { - "site": "google", + "site": "imdb", "name": "search", - "description": "Search Google", + "description": "Search IMDb for movies, TV shows, and people", "access": "read", - "domain": "google.com", + "domain": "www.imdb.com", "strategy": "public", "browser": true, "args": [ { - "name": "keyword", + "name": "query", "type": "str", "required": true, "positional": true, @@ -7671,76 +7248,60 @@ { "name": "limit", "type": "int", - "default": 10, - "required": false, - "help": "Number of results (1-100)" - }, - { - "name": "lang", - "type": "str", - "default": "en", + "default": 20, "required": false, - "help": "Language short code (e.g. en, zh)" + "help": "Number of results" } ], "columns": [ - "type", + "rank", + "id", "title", - "url", - "snippet" + "year", + "type", + "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "google/search.js", - "sourceFile": "google/search.js" + "modulePath": "imdb/search.js", + "sourceFile": "imdb/search.js" }, { - "site": "google", - "name": "suggest", - "description": "Get Google search suggestions", + "site": "imdb", + "name": "title", + "description": "Get movie or TV show details", "access": "read", + "domain": "www.imdb.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "keyword", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Search query" - }, - { - "name": "lang", - "type": "str", - "default": "zh-CN", - "required": false, - "help": "Language code" + "help": "IMDb title ID (tt1375666) or URL" } ], "columns": [ - "suggestion" + "field", + "value" ], "type": "js", - "modulePath": "google/suggest.js", - "sourceFile": "google/suggest.js" + "modulePath": "imdb/title.js", + "sourceFile": "imdb/title.js" }, { - "site": "google", - "name": "trends", - "description": "Get Google Trends daily trending searches", + "site": "imdb", + "name": "top", + "description": "IMDb Top 250 Movies", "access": "read", + "domain": "www.imdb.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ - { - "name": "region", - "type": "str", - "default": "US", - "required": false, - "help": "Region code (e.g. US, CN, JP)" - }, { "name": "limit", "type": "int", @@ -7750,101 +7311,88 @@ } ], "columns": [ + "rank", "title", - "traffic", - "date" + "rating", + "votes", + "genre", + "url" ], "type": "js", - "modulePath": "google/trends.js", - "sourceFile": "google/trends.js" + "modulePath": "imdb/top.js", + "sourceFile": "imdb/top.js" }, { - "site": "google-scholar", - "name": "cite", - "description": "Get citation for a Google Scholar paper", + "site": "imdb", + "name": "trending", + "description": "IMDb Most Popular Movies", "access": "read", - "domain": "scholar.google.com", + "domain": "www.imdb.com", "strategy": "public", "browser": true, "args": [ { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Paper title to search for" - }, - { - "name": "style", - "type": "str", - "default": "bibtex", - "required": false, - "help": "Citation format", - "choices": [ - "bibtex", - "endnote", - "refman", - "refworks" - ] - }, - { - "name": "index", + "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Which search result to cite (1-based)" + "help": "Number of results" } ], "columns": [ + "rank", "title", - "format", - "citation" + "rating", + "genre", + "url" ], "type": "js", - "modulePath": "google-scholar/cite.js", - "sourceFile": "google-scholar/cite.js" + "modulePath": "imdb/trending.js", + "sourceFile": "imdb/trending.js" }, { - "site": "google-scholar", - "name": "profile", - "description": "View a Google Scholar author profile", + "site": "indeed", + "name": "job", + "aliases": [ + "detail", + "view" + ], + "description": "Read the full Indeed job posting by jk (job key)", "access": "read", - "domain": "scholar.google.com", - "strategy": "public", + "domain": "www.indeed.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "author", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Author name or Scholar user ID (e.g. JicYPdAAAAAJ)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max papers to show (max 20)" + "help": "Job key (16-char hex from `indeed search`, e.g. \"dccc07ac5a6a3683\")" } ], "columns": [ - "rank", + "id", "title", - "cited", - "year" - ], - "type": "js", - "modulePath": "google-scholar/profile.js", - "sourceFile": "google-scholar/profile.js" - }, - { - "site": "google-scholar", - "name": "search", - "description": "Google Scholar scholar search", + "company", + "location", + "salary", + "job_type", + "description", + "url" + ], + "type": "js", + "modulePath": "indeed/job.js", + "sourceFile": "indeed/job.js", + "navigateBefore": false + }, + { + "site": "indeed", + "name": "search", + "description": "Indeed keyword job search (rendered DOM via browser session, US site)", "access": "read", - "domain": "scholar.google.com", - "strategy": "public", + "domain": "www.indeed.com", + "strategy": "cookie", "browser": true, "args": [ { @@ -7852,353 +7400,358 @@ "type": "str", "required": true, "positional": true, - "help": "Search keyword" + "help": "Job keyword (title / skill / company)" + }, + { + "name": "location", + "type": "string", + "default": "", + "required": false, + "help": "Location filter (e.g. \"remote\", \"New York, NY\", \"San Francisco\")" + }, + { + "name": "fromage", + "type": "string", + "default": "", + "required": false, + "help": "Recency filter, days back: 1 / 3 / 7 / 14" + }, + { + "name": "sort", + "type": "string", + "default": "relevance", + "required": false, + "help": "Sort order: relevance | date" + }, + { + "name": "start", + "type": "int", + "default": 0, + "required": false, + "help": "Pagination offset (multiple of 10, 0-based)" }, { "name": "limit", "type": "int", - "default": 10, + "default": 15, "required": false, - "help": "Number of results to return (max 20)" + "help": "Max rows to return (1-25, capped at one page)" } ], "columns": [ "rank", + "id", "title", - "authors", - "source", - "year", - "cited", + "company", + "location", + "salary", + "tags", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "google-scholar/search.js", - "sourceFile": "google-scholar/search.js" + "modulePath": "indeed/search.js", + "sourceFile": "indeed/search.js", + "navigateBefore": false }, { - "site": "grok", - "name": "ask", - "description": "Send a message to Grok and get response", + "site": "instagram", + "name": "collection-create", + "description": "Create a new Instagram saved-posts collection (folder)", "access": "write", - "domain": "grok.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", - "type": "string", + "name": "name", + "type": "str", "required": true, "positional": true, - "help": "Prompt to send to Grok" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait for response (default: 120)" - }, + "help": "Name of the collection to create" + } + ], + "columns": [ + "status", + "collectionId", + "collectionName", + "mediaCount" + ], + "type": "js", + "modulePath": "instagram/collection-create.js", + "sourceFile": "instagram/collection-create.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "collection-delete", + "description": "Delete an Instagram saved-posts collection (folder) by name or id", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending (default: false)" + "name": "target", + "type": "str", + "required": true, + "positional": true, + "help": "Collection name (case-insensitive) or numeric collection_id" } ], "columns": [ - "response" + "status", + "collectionId", + "collectionName" ], "type": "js", - "modulePath": "grok/ask.js", - "sourceFile": "grok/ask.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" + "modulePath": "instagram/collection-delete.js", + "sourceFile": "instagram/collection-delete.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", - "name": "delete", - "description": "Delete a Grok conversation by ID. Grok takes effect immediately with no confirmation dialog — require --yes to actually delete.", + "site": "instagram", + "name": "comment", + "description": "Comment on an Instagram post", "access": "write", - "domain": "grok.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", - "type": "string", + "name": "username", + "type": "str", "required": true, "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" + "help": "Username of the post author" }, { - "name": "yes", - "type": "boolean", - "default": false, + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Comment text" + }, + { + "name": "index", + "type": "int", + "default": 1, "required": false, - "help": "Actually delete (default is a dry-run preview)" + "help": "Post index (1 = most recent)" } ], "columns": [ "status", - "id" + "user", + "text" ], "type": "js", - "modulePath": "grok/delete.js", - "sourceFile": "grok/delete.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" + "modulePath": "instagram/comment.js", + "sourceFile": "instagram/comment.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", - "name": "detail", - "description": "Open a Grok conversation by ID and read its messages", + "site": "instagram", + "name": "download", + "description": "Download images and videos from Instagram posts and reels", "access": "read", - "domain": "grok.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Session ID (UUID) or full https://grok.com/c/ URL" + "help": "Instagram post / reel / tv URL" }, { - "name": "markdown", - "type": "boolean", - "default": false, + "name": "path", + "type": "str", + "default": "~/Downloads/Instagram", "required": false, - "help": "Emit assistant replies as markdown" + "help": "Download directory" } ], - "columns": [ - "Role", - "Text" - ], "type": "js", - "modulePath": "grok/detail.js", - "sourceFile": "grok/detail.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/download.js", + "sourceFile": "instagram/download.js", + "navigateBefore": false }, { - "site": "grok", - "name": "export", - "description": "Export all visible Grok conversation history metadata", + "site": "instagram", + "name": "explore", + "description": "Instagram explore/discover trending posts", "access": "read", - "example": "webcmd grok export -f yaml", - "domain": "grok.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 0, - "required": false, - "help": "Max conversations to export; 0 means all loaded history" - }, - { - "name": "maxScrolls", - "type": "int", - "default": 80, + "default": 20, "required": false, - "help": "Max history-list scroll rounds when limit is 0 (max 500)" + "help": "Number of posts" } ], "columns": [ - "index", - "id", - "title", - "date", - "url" + "rank", + "user", + "caption", + "likes", + "comments", + "type" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "grok/export.js", - "sourceFile": "grok/export.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/explore.js", + "sourceFile": "instagram/explore.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", - "name": "export-all", - "description": "Export Grok conversation history and each conversation transcript", - "access": "read", - "example": "webcmd grok export-all --limit 5 -f json", - "domain": "grok.com", + "site": "instagram", + "name": "follow", + "description": "Follow an Instagram user", + "access": "write", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 0, - "required": false, - "help": "Max conversations to export; 0 means all loaded history" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Skip this many conversations before exporting" - }, - { - "name": "manifestPath", - "type": "string", - "default": "", - "required": false, - "help": "Optional grok/export JSON manifest path; skips history dialog and visits listed /c pages directly" - }, - { - "name": "maxScrolls", - "type": "int", - "default": 80, - "required": false, - "help": "Max history-list scroll rounds when limit is 0 (max 500)" - }, - { - "name": "pageScrolls", - "type": "int", - "default": 30, - "required": false, - "help": "Max per-conversation scroll-to-bottom rounds (max 200)" - }, - { - "name": "pageTimeoutMs", - "type": "int", - "default": 30000, - "required": false, - "help": "Max wait for each conversation page to show messages" - }, - { - "name": "delayMinMs", - "type": "int", - "default": 0, - "required": false, - "help": "Minimum polite delay after a conversation page loads" - }, - { - "name": "delayMaxMs", - "type": "int", - "default": 5000, - "required": false, - "help": "Maximum polite delay after a conversation page loads" + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram username to follow" } ], "columns": [ - "index", - "id", - "title", - "date", - "url", "status", - "messageCount", - "error", - "messagesJson" + "username" ], "type": "js", - "modulePath": "grok/export-all.js", - "sourceFile": "grok/export-all.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/follow.js", + "sourceFile": "instagram/follow.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", - "name": "history", - "description": "List recent Grok conversations from the sidebar (requires login)", + "site": "instagram", + "name": "followers", + "description": "List followers of an Instagram user", "access": "read", - "domain": "grok.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram username" + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max conversations to show (default 20, max 100)" + "help": "Number of followers" } ], "columns": [ - "Index", - "Title", - "Url" + "rank", + "username", + "name", + "verified", + "private" ], "type": "js", - "modulePath": "grok/history.js", - "sourceFile": "grok/history.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/followers.js", + "sourceFile": "instagram/followers.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", - "name": "image", - "description": "Generate images on grok.com and return image URLs", - "access": "write", - "domain": "grok.com", + "site": "instagram", + "name": "following", + "description": "List accounts an Instagram user is following", + "access": "read", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", - "type": "string", + "name": "username", + "type": "str", "required": true, "positional": true, - "help": "Image generation prompt" + "help": "Instagram username" }, { - "name": "timeout", + "name": "limit", "type": "int", - "default": 240, + "default": 20, "required": false, - "help": "Max seconds to wait for the image (default: 240)" - }, + "help": "Number of accounts" + } + ], + "columns": [ + "rank", + "username", + "name", + "verified", + "private" + ], + "type": "js", + "modulePath": "instagram/following.js", + "sourceFile": "instagram/following.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "like", + "description": "Like an Instagram post", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending (default: false)" + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Username of the post author" }, { - "name": "count", + "name": "index", "type": "int", "default": 1, "required": false, - "help": "Minimum images to wait for before returning (default: 1)" - }, - { - "name": "out", - "type": "string", - "default": "", - "required": false, - "help": "Directory to save downloaded images (uses browser session to bypass auth)" + "help": "Post index (1 = most recent)" } ], "columns": [ - "url", - "width", - "height", - "path" + "status", + "user", + "post" ], "type": "js", - "modulePath": "grok/image.js", - "sourceFile": "grok/image.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" + "modulePath": "instagram/like.js", + "sourceFile": "instagram/like.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", + "site": "instagram", "name": "login", - "description": "Open grok login", + "description": "Open instagram login", "access": "write", - "domain": "grok.com", + "domain": "instagram.com", "strategy": "cookie", "browser": true, "args": [], @@ -8207,1026 +7760,957 @@ "logged_in", "site", "user_id", - "name", + "username", + "full_name", "action", "verify_command" ], "type": "js", - "modulePath": "grok/auth.js", - "sourceFile": "grok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "new", - "description": "Start a new conversation in Grok", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "grok/new.js", - "sourceFile": "grok/new.js", + "modulePath": "instagram/auth.js", + "sourceFile": "instagram/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "grok", - "name": "pin", - "description": "Pin a Grok conversation by ID", + "site": "instagram", + "name": "note", + "description": "Publish a text Instagram note", "access": "write", - "domain": "grok.com", - "strategy": "cookie", + "domain": "www.instagram.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "id", - "type": "string", + "name": "content", + "type": "str", "required": true, "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" + "help": "Note text (max 60 characters)" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds for the overall command (default: 120)" } ], "columns": [ "status", - "id" + "detail", + "noteId" ], "type": "js", - "modulePath": "grok/pin.js", - "sourceFile": "grok/pin.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" + "modulePath": "instagram/note.js", + "sourceFile": "instagram/note.js", + "navigateBefore": true }, { - "site": "grok", - "name": "read", - "description": "Read messages in the current Grok conversation", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "markdown", - "type": "boolean", - "default": false, + "site": "instagram", + "name": "post", + "description": "Post an Instagram feed image or mixed-media carousel", + "access": "write", + "domain": "www.instagram.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "media", + "type": "str", "required": false, - "help": "Emit assistant replies as markdown" + "valueRequired": true, + "help": "Comma-separated media paths (images/videos, up to 10)", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "image/jpeg", + "image/png", + "image/webp", + "video/mp4" + ], + "maxBytes": 262144000 + } + }, + { + "name": "content", + "type": "str", + "required": false, + "positional": true, + "help": "Caption text" + }, + { + "name": "timeout", + "type": "int", + "default": 300, + "required": false, + "help": "Max seconds for the overall command (default: 300)" } ], "columns": [ - "Role", - "Text" + "status", + "detail", + "url" ], "type": "js", - "modulePath": "grok/read.js", - "sourceFile": "grok/read.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/post.js", + "sourceFile": "instagram/post.js", + "navigateBefore": true }, { - "site": "grok", - "name": "send", - "description": "Fire-and-forget: send a prompt to Grok without waiting for the reply", - "access": "write", - "domain": "grok.com", + "site": "instagram", + "name": "profile", + "description": "Get Instagram user profile info", + "access": "read", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Prompt to send to Grok" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" + "help": "Instagram username" } ], "columns": [ - "Status", - "Prompt" + "username", + "name", + "followers", + "following", + "posts", + "verified", + "bio" ], "type": "js", - "modulePath": "grok/send.js", - "sourceFile": "grok/send.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/profile.js", + "sourceFile": "instagram/profile.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", - "name": "status", - "description": "Check Grok page availability, login state, current session and model", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", + "site": "instagram", + "name": "reel", + "description": "Post an Instagram reel video", + "access": "write", + "domain": "www.instagram.com", + "strategy": "ui", "browser": true, - "args": [], + "args": [ + { + "name": "video", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Path to a single .mp4 video file", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "video/mp4" + ], + "maxBytes": 262144000 + } + }, + { + "name": "content", + "type": "str", + "required": false, + "positional": true, + "help": "Caption text" + }, + { + "name": "timeout", + "type": "int", + "default": 600, + "required": false, + "help": "Max seconds for the overall command (default: 600)" + } + ], "columns": [ - "Status", - "Login", - "Model", - "SessionId", - "Url" + "status", + "detail", + "url" ], "type": "js", - "modulePath": "grok/status.js", - "sourceFile": "grok/status.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/reel.js", + "sourceFile": "instagram/reel.js", + "navigateBefore": true }, { - "site": "grok", - "name": "unpin", - "description": "Unpin a Grok conversation by ID", + "site": "instagram", + "name": "save", + "description": "Save (bookmark) an Instagram post", "access": "write", - "domain": "grok.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", - "type": "string", + "name": "username", + "type": "str", "required": true, "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" + "help": "Username of the post author" + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Post index (1 = most recent)" } ], "columns": [ "status", - "id" + "user", + "post" ], "type": "js", - "modulePath": "grok/pin.js", - "sourceFile": "grok/pin.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" + "modulePath": "instagram/save.js", + "sourceFile": "instagram/save.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", - "name": "whoami", - "description": "Show the current logged-in grok account", + "site": "instagram", + "name": "saved", + "description": "Get your saved Instagram posts (optionally from a specific collection)", "access": "read", - "domain": "grok.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of saved posts" + }, + { + "name": "collection", + "type": "str", + "required": false, + "help": "Collection name (case-insensitive). Omit for the default \"All posts\" feed." + } + ], "columns": [ - "logged_in", - "site", - "user_id", - "name" + "index", + "user", + "caption", + "likes", + "comments", + "type" ], "type": "js", - "modulePath": "grok/auth.js", - "sourceFile": "grok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/saved.js", + "sourceFile": "instagram/saved.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "hf", - "name": "datasets", - "description": "Top Hugging Face datasets (downloads / likes / trending / freshness).", + "site": "instagram", + "name": "search", + "description": "Search Instagram users", "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "sort", - "type": "string", - "default": "downloads", - "required": false, - "help": "Sort key: downloads, likes, trending, created_at, last_modified" - }, - { - "name": "search", - "type": "string", - "required": false, - "help": "Optional name/owner substring filter." + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Max datasets (max 100; one API page)." + "help": "Number of results" } ], "columns": [ "rank", - "id", - "author", - "downloads", - "likes", - "tags", - "lastModified", + "username", + "name", + "verified", + "private", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "hf/datasets.js", - "sourceFile": "hf/datasets.js" + "modulePath": "instagram/search.js", + "sourceFile": "instagram/search.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "hf", - "name": "login", - "description": "Open hf login", + "site": "instagram", + "name": "story", + "description": "Post a single Instagram story image or video", "access": "write", - "domain": "huggingface.co", - "strategy": "cookie", + "domain": "www.instagram.com", + "strategy": "ui", "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "username", - "fullname", - "type", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "hf/auth.js", - "sourceFile": "hf/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "hf", - "name": "models", - "description": "Top Hugging Face models (downloads / likes / trending / freshness).", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, "args": [ { - "name": "sort", - "type": "string", - "default": "downloads", - "required": false, - "help": "Sort key: downloads, likes, trending, created_at, last_modified" - }, - { - "name": "search", - "type": "string", - "required": false, - "help": "Optional name/owner substring filter (e.g. \"llama\", \"mistralai/\")" - }, - { - "name": "pipeline", - "type": "string", + "name": "media", + "type": "str", "required": false, - "help": "Filter by pipeline tag (e.g. text-generation, image-classification)" + "valueRequired": true, + "help": "Path to a single story image or video file" }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 20, + "default": 300, "required": false, - "help": "Max models (max 100; one API page)." + "help": "Max seconds for the overall command (default: 300)" } ], "columns": [ - "rank", - "id", - "author", - "pipelineTag", - "downloads", - "likes", - "tags", - "lastModified", + "status", + "detail", "url" ], "type": "js", - "modulePath": "hf/models.js", - "sourceFile": "hf/models.js" + "modulePath": "instagram/story.js", + "sourceFile": "instagram/story.js", + "navigateBefore": true }, { - "site": "hf", - "name": "paper", - "description": "Hugging Face paper detail by arXiv id (full title / summary / authors / AI keywords)", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, + "site": "instagram", + "name": "unfollow", + "description": "Unfollow an Instagram user", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "arXiv id (e.g. \"1706.03762\") — same value HF uses to mirror the paper" + "help": "Instagram username to unfollow" } ], "columns": [ - "id", - "title", - "authors", - "publishedAt", - "upvotes", - "aiKeywords", - "summary", - "aiSummary", - "url" + "status", + "username" ], "type": "js", - "modulePath": "hf/paper.js", - "sourceFile": "hf/paper.js" + "modulePath": "instagram/unfollow.js", + "sourceFile": "instagram/unfollow.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "hf", - "name": "spaces", - "description": "Top Hugging Face Spaces (likes / created_at / last_modified).", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, + "site": "instagram", + "name": "unlike", + "description": "Unlike an Instagram post", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "sort", - "type": "string", - "default": "likes", - "required": false, - "help": "Sort key: likes, created_at, last_modified" - }, - { - "name": "search", - "type": "string", - "required": false, - "help": "Optional name/owner substring filter (e.g. \"stability\", \"openai/\")" - }, - { - "name": "sdk", - "type": "string", - "required": false, - "help": "Filter by Space SDK: gradio / streamlit / docker / static" + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Username of the post author" }, { - "name": "limit", + "name": "index", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Max spaces (max 100; one API page)." + "help": "Post index (1 = most recent)" } ], "columns": [ - "rank", - "id", - "author", - "sdk", - "likes", - "tags", - "lastModified", - "url" + "status", + "user", + "post" ], "type": "js", - "modulePath": "hf/spaces.js", - "sourceFile": "hf/spaces.js" + "modulePath": "instagram/unlike.js", + "sourceFile": "instagram/unlike.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "hf", - "name": "top", - "description": "Top upvoted Hugging Face papers", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, + "site": "instagram", + "name": "unsave", + "description": "Unsave (remove bookmark) an Instagram post", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of papers" - }, - { - "name": "all", - "type": "bool", - "default": false, - "required": false, - "help": "Return all papers (ignore limit)" - }, - { - "name": "date", + "name": "username", "type": "str", - "required": false, - "help": "Date (YYYY-MM-DD), defaults to most recent" + "required": true, + "positional": true, + "help": "Username of the post author" }, { - "name": "period", - "type": "str", - "default": "daily", + "name": "index", + "type": "int", + "default": 1, "required": false, - "help": "Time period: daily, weekly, or monthly", - "choices": [ - "daily", - "weekly", - "monthly" - ] + "help": "Post index (1 = most recent)" } ], "columns": [ - "rank", - "id", - "title", - "upvotes", - "authors" + "status", + "user", + "post" ], "type": "js", - "modulePath": "hf/top.js", - "sourceFile": "hf/top.js" + "modulePath": "instagram/unsave.js", + "sourceFile": "instagram/unsave.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "hf", - "name": "whoami", - "description": "Show the current logged-in hf account", + "site": "instagram", + "name": "user", + "description": "Get recent posts from an Instagram user", "access": "read", - "domain": "huggingface.co", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "username", - "fullname", - "type" - ], - "type": "js", - "modulePath": "hf/auth.js", - "sourceFile": "hf/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "imdb", - "name": "person", - "description": "Get actor or director info", - "access": "read", - "domain": "www.imdb.com", - "strategy": "public", - "browser": true, "args": [ { - "name": "id", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "IMDb person ID (nm0634240) or URL" + "help": "Instagram username" }, { "name": "limit", "type": "int", - "default": 10, + "default": 12, "required": false, - "help": "Max filmography entries" + "help": "Number of posts" } ], "columns": [ - "field", - "value" + "index", + "caption", + "likes", + "comments", + "type", + "date" ], "type": "js", - "modulePath": "imdb/person.js", - "sourceFile": "imdb/person.js" + "modulePath": "instagram/user.js", + "sourceFile": "instagram/user.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "imdb", - "name": "reviews", - "description": "Get user reviews for a movie or TV show", + "site": "instagram", + "name": "whoami", + "description": "Show the current logged-in instagram account", "access": "read", - "domain": "www.imdb.com", - "strategy": "public", + "domain": "instagram.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "username", + "full_name" + ], + "type": "js", + "modulePath": "instagram/auth.js", + "sourceFile": "instagram/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "linkedin-learning", + "name": "course", + "description": "Get LinkedIn Learning course detail by slug or course URL", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "id", - "type": "str", + "name": "slug", + "type": "string", "required": true, "positional": true, - "help": "IMDb title ID (tt1375666) or URL" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of reviews" + "help": "Course slug (e.g. agentic-ai-build-your-first-agentic-ai-system) or full /learning/ URL" } ], "columns": [ - "rank", "title", + "slug", + "description", + "difficulty", + "duration_sec", + "videos_count", "rating", - "author", - "date", - "text" + "rating_count", + "released", + "url" ], "type": "js", - "modulePath": "imdb/reviews.js", - "sourceFile": "imdb/reviews.js" + "modulePath": "linkedin-learning/course.js", + "sourceFile": "linkedin-learning/course.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "imdb", + "site": "linkedin-learning", + "name": "login", + "description": "Open linkedin-learning login", + "access": "write", + "domain": "linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "public_id", + "plain_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "linkedin-learning/auth.js", + "sourceFile": "linkedin-learning/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "linkedin-learning", "name": "search", - "description": "Search IMDb for movies, TV shows, and people", + "description": "Search LinkedIn Learning courses, videos, and learning paths by keyword", "access": "read", - "domain": "www.imdb.com", - "strategy": "public", + "domain": "www.linkedin.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "query", - "type": "str", + "name": "keywords", + "type": "string", "required": true, "positional": true, - "help": "Search query" + "help": "Search keywords, e.g. \"AI agent\"" }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Number of results" + "help": "Maximum results to return (1-50)" } ], "columns": [ "rank", - "id", - "title", - "year", "type", + "title", + "instructor", + "difficulty", + "duration_sec", + "rating", + "rating_count", + "viewers", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "imdb/search.js", - "sourceFile": "imdb/search.js" + "modulePath": "linkedin-learning/search.js", + "sourceFile": "linkedin-learning/search.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "imdb", - "name": "title", - "description": "Get movie or TV show details", + "site": "linkedin-learning", + "name": "trending", + "description": "Browse LinkedIn Learning recommended courses across personalized carousels", "access": "read", - "domain": "www.imdb.com", - "strategy": "public", + "domain": "www.linkedin.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "IMDb title ID (tt1375666) or URL" + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum results to return (1-50)" } ], "columns": [ - "field", - "value" + "rank", + "group", + "type", + "title", + "difficulty", + "viewers", + "url" ], "type": "js", - "modulePath": "imdb/title.js", - "sourceFile": "imdb/title.js" + "modulePath": "linkedin-learning/trending.js", + "sourceFile": "linkedin-learning/trending.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "imdb", - "name": "top", - "description": "IMDb Top 250 Movies", + "site": "linkedin-learning", + "name": "whoami", + "description": "Show the current logged-in linkedin-learning account", "access": "read", - "domain": "www.imdb.com", - "strategy": "public", + "domain": "linkedin.com", + "strategy": "cookie", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], + "args": [], "columns": [ - "rank", - "title", - "rating", - "votes", - "genre", - "url" + "logged_in", + "site", + "public_id", + "plain_id", + "name" ], "type": "js", - "modulePath": "imdb/top.js", - "sourceFile": "imdb/top.js" + "modulePath": "linkedin-learning/auth.js", + "sourceFile": "linkedin-learning/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "imdb", - "name": "trending", - "description": "IMDb Most Popular Movies", + "site": "manus", + "name": "connectors", + "description": "List available Manus connectors (integrations).", "access": "read", - "domain": "www.imdb.com", - "strategy": "public", + "domain": "manus.im", + "strategy": "cookie", "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 50, "required": false, - "help": "Number of results" + "help": "Max connectors to return" } ], "columns": [ - "rank", - "title", - "rating", - "genre", - "url" + "UID", + "Name", + "Brief" ], "type": "js", - "modulePath": "imdb/trending.js", - "sourceFile": "imdb/trending.js" + "modulePath": "manus/connectors.js", + "sourceFile": "manus/connectors.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "indeed", - "name": "job", - "aliases": [ - "detail", - "view" - ], - "description": "Read the full Indeed job posting by jk (job key)", + "site": "manus", + "name": "credits", + "description": "Show Manus credit balance and refresh details.", "access": "read", - "domain": "www.indeed.com", + "domain": "manus.im", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Job key (16-char hex from `indeed search`, e.g. \"dccc07ac5a6a3683\")" - } - ], + "args": [], "columns": [ - "id", - "title", - "company", - "location", - "salary", - "job_type", - "description", - "url" + "Field", + "Value" ], "type": "js", - "modulePath": "indeed/job.js", - "sourceFile": "indeed/job.js", - "navigateBefore": false + "modulePath": "manus/credits.js", + "sourceFile": "manus/credits.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "indeed", - "name": "search", - "description": "Indeed keyword job search (rendered DOM via browser session, US site)", + "site": "manus", + "name": "list", + "description": "List Manus sessions (tasks).", "access": "read", - "domain": "www.indeed.com", + "domain": "manus.im", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Job keyword (title / skill / company)" - }, - { - "name": "location", - "type": "string", - "default": "", - "required": false, - "help": "Location filter (e.g. \"remote\", \"New York, NY\", \"San Francisco\")" - }, - { - "name": "fromage", - "type": "string", - "default": "", - "required": false, - "help": "Recency filter, days back: 1 / 3 / 7 / 14" - }, - { - "name": "sort", - "type": "string", - "default": "relevance", - "required": false, - "help": "Sort order: relevance | date" - }, - { - "name": "start", + "name": "limit", "type": "int", - "default": 0, + "default": 20, "required": false, - "help": "Pagination offset (multiple of 10, 0-based)" + "help": "Max sessions to return" }, { - "name": "limit", - "type": "int", - "default": 15, + "name": "archived", + "type": "bool", + "default": false, "required": false, - "help": "Max rows to return (1-25, capped at one page)" + "help": "Include archived sessions" } ], "columns": [ - "rank", "id", - "title", - "company", - "location", - "salary", - "tags", - "url" - ], - "tags": [ - "search" + "Title", + "Status", + "Last Message", + "Last Updated", + "Credits" ], "type": "js", - "modulePath": "indeed/search.js", - "sourceFile": "indeed/search.js", - "navigateBefore": false + "modulePath": "manus/list.js", + "sourceFile": "manus/list.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "instagram", - "name": "collection-create", - "description": "Create a new Instagram saved-posts collection (folder)", + "site": "manus", + "name": "login", + "description": "Open manus login", "access": "write", - "domain": "www.instagram.com", + "domain": "manus.im", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Name of the collection to create" - } - ], + "args": [], "columns": [ "status", - "collectionId", - "collectionName", - "mediaCount" + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" ], "type": "js", - "modulePath": "instagram/collection-create.js", - "sourceFile": "instagram/collection-create.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "manus/auth.js", + "sourceFile": "manus/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "instagram", - "name": "collection-delete", - "description": "Delete an Instagram saved-posts collection (folder) by name or id", - "access": "write", - "domain": "www.instagram.com", + "site": "manus", + "name": "read", + "description": "Show details for a specific Manus session.", + "access": "read", + "domain": "manus.im", "strategy": "cookie", "browser": true, "args": [ { - "name": "target", + "name": "uid", "type": "str", "required": true, "positional": true, - "help": "Collection name (case-insensitive) or numeric collection_id" + "help": "Session UID" } ], "columns": [ - "status", - "collectionId", - "collectionName" + "Field", + "Value" ], "type": "js", - "modulePath": "instagram/collection-delete.js", - "sourceFile": "instagram/collection-delete.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "manus/read.js", + "sourceFile": "manus/read.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "instagram", - "name": "comment", - "description": "Comment on an Instagram post", - "access": "write", - "domain": "www.instagram.com", + "site": "manus", + "name": "skills", + "description": "List Manus skills (user-added and system).", + "access": "read", + "domain": "manus.im", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Comment text" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], + "args": [], "columns": [ - "status", - "user", - "text" + "ID", + "Name", + "Description", + "Source" ], "type": "js", - "modulePath": "instagram/comment.js", - "sourceFile": "instagram/comment.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "manus/skills.js", + "sourceFile": "manus/skills.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "instagram", - "name": "download", - "description": "Download images and videos from Instagram posts and reels", + "site": "manus", + "name": "status", + "description": "Show current Manus user profile and credit summary.", "access": "read", - "domain": "www.instagram.com", + "domain": "manus.im", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram post / reel / tv URL" - }, - { - "name": "path", - "type": "str", - "default": "~/Downloads/Instagram", - "required": false, - "help": "Download directory" - } + "args": [], + "columns": [ + "Field", + "Value" ], "type": "js", - "modulePath": "instagram/download.js", - "sourceFile": "instagram/download.js", - "navigateBefore": false - }, - { - "site": "instagram", - "name": "explore", - "description": "Instagram explore/discover trending posts", + "modulePath": "manus/status.js", + "sourceFile": "manus/status.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "whoami", + "description": "Show the current logged-in manus account", "access": "read", - "domain": "www.instagram.com", + "domain": "manus.im", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts" - } - ], + "args": [], "columns": [ - "rank", - "user", - "caption", - "likes", - "comments", - "type" - ], - "tags": [ - "search" + "logged_in", + "site", + "user_id", + "name" ], "type": "js", - "modulePath": "instagram/explore.js", - "sourceFile": "instagram/explore.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "manus/auth.js", + "sourceFile": "manus/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "instagram", - "name": "follow", - "description": "Follow an Instagram user", - "access": "write", - "domain": "www.instagram.com", + "site": "medium", + "name": "feed", + "description": "Medium popular posts Feed", + "access": "read", + "domain": "medium.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "topic", "type": "str", - "required": true, - "positional": true, - "help": "Instagram username to follow" + "default": "", + "required": false, + "help": "Topic (for example technology, programming, ai)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" } ], "columns": [ - "status", - "username" + "rank", + "title", + "author", + "date", + "readTime", + "claps" ], "type": "js", - "modulePath": "instagram/follow.js", - "sourceFile": "instagram/follow.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "medium/feed.js", + "sourceFile": "medium/feed.js", + "navigateBefore": "https://medium.com" }, { - "site": "instagram", - "name": "followers", - "description": "List followers of an Instagram user", + "site": "medium", + "name": "search", + "description": "Search Medium posts", "access": "read", - "domain": "www.instagram.com", + "domain": "medium.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "keyword", "type": "str", "required": true, "positional": true, - "help": "Instagram username" + "help": "Search keyword" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of followers" + "help": "Number of posts to return" } ], "columns": [ "rank", - "username", - "name", - "verified", - "private" + "title", + "author", + "date", + "readTime", + "claps", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "instagram/followers.js", - "sourceFile": "instagram/followers.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "medium/search.js", + "sourceFile": "medium/search.js", + "navigateBefore": "https://medium.com" }, { - "site": "instagram", - "name": "following", - "description": "List accounts an Instagram user is following", + "site": "medium", + "name": "tag", + "description": "Latest Medium articles tagged with a given keyword (RSS feed)", "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, + "domain": "medium.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "username", + "name": "tag", "type": "str", "required": true, "positional": true, - "help": "Instagram username" + "help": "Lowercase tag slug (e.g. \"programming\", \"machine-learning\")" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of accounts" + "help": "Max articles (1-25 — single RSS page)" } ], "columns": [ "rank", - "username", - "name", - "verified", - "private" + "title", + "author", + "description", + "categories", + "published", + "url" ], "type": "js", - "modulePath": "instagram/following.js", - "sourceFile": "instagram/following.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "medium/tag.js", + "sourceFile": "medium/tag.js" }, { - "site": "instagram", - "name": "like", - "description": "Like an Instagram post", - "access": "write", - "domain": "www.instagram.com", + "site": "medium", + "name": "user", + "description": "Get Medium user posts", + "access": "read", + "domain": "medium.com", "strategy": "cookie", "browser": true, "args": [ @@ -9235,557 +8719,518 @@ "type": "str", "required": true, "positional": true, - "help": "Username of the post author" + "help": "Medium username(for example @username or username)" }, { - "name": "index", + "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Post index (1 = most recent)" + "help": "Number of posts to return" } ], "columns": [ - "status", - "user", - "post" + "rank", + "title", + "date", + "readTime", + "claps", + "url" ], "type": "js", - "modulePath": "instagram/like.js", - "sourceFile": "instagram/like.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "medium/user.js", + "sourceFile": "medium/user.js", + "navigateBefore": "https://medium.com" }, { - "site": "instagram", - "name": "login", - "description": "Open instagram login", - "access": "write", - "domain": "instagram.com", - "strategy": "cookie", + "site": "mercury", + "name": "check-login", + "description": "Open Mercury reimbursements and report whether the active browser profile is logged in", + "access": "read", + "example": "webcmd --profile mercury check-login -f json", + "domain": "app.mercury.com", + "strategy": "ui", "browser": true, "args": [], "columns": [ "status", - "logged_in", - "site", - "user_id", - "username", - "full_name", - "action", - "verify_command" + "loggedIn", + "url", + "hasSubmitExpense", + "hasReimbursements", + "title" ], "type": "js", - "modulePath": "instagram/auth.js", - "sourceFile": "instagram/auth.js", + "modulePath": "mercury/check-login.js", + "sourceFile": "mercury/check-login.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "instagram", - "name": "note", - "description": "Publish a text Instagram note", + "site": "mercury", + "name": "reimbursement-draft", + "description": "Create a Mercury reimbursement draft from a local receipt, correct OCR fields, and stop at Review", "access": "write", - "domain": "www.instagram.com", + "example": "webcmd --profile mercury reimbursement-draft --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", + "domain": "app.mercury.com", "strategy": "ui", "browser": true, "args": [ { - "name": "content", + "name": "receipt", "type": "str", "required": true, - "positional": true, - "help": "Note text (max 60 characters)" + "help": "Local receipt/proof file path", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp", + "application/pdf" + ], + "maxBytes": 26214400 + } }, { - "name": "timeout", - "type": "int", - "default": 120, + "name": "amount", + "type": "str", + "required": true, + "help": "Original-currency amount, e.g. 140.00" + }, + { + "name": "currency", + "type": "str", + "default": "CNY", "required": false, - "help": "Max seconds for the overall command (default: 120)" - } - ], - "columns": [ - "status", - "detail", - "noteId" - ], - "type": "js", - "modulePath": "instagram/note.js", - "sourceFile": "instagram/note.js", - "navigateBefore": true - }, - { - "site": "instagram", - "name": "post", - "description": "Post an Instagram feed image or mixed-media carousel", - "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Original currency code" + }, { - "name": "media", + "name": "date", + "type": "str", + "required": true, + "help": "Expense date as YYYY-MM-DD" + }, + { + "name": "merchant", + "type": "str", + "required": true, + "help": "Merchant shown on the reimbursement" + }, + { + "name": "category", "type": "str", + "default": "Marketing & Advertising", "required": false, - "valueRequired": true, - "help": "Comma-separated media paths (images/videos, up to 10)", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "image/jpeg", - "image/png", - "image/webp", - "video/mp4" - ], - "maxBytes": 262144000 - } + "help": "Mercury expense category" }, { - "name": "content", + "name": "notes", + "type": "str", + "required": true, + "help": "Business purpose / reimbursement notes" + }, + { + "name": "ocr-wait-seconds", "type": "str", + "default": "8", "required": false, - "positional": true, - "help": "Caption text" + "help": "Seconds to wait after receipt upload before correcting OCR-overwritten fields" }, { - "name": "timeout", - "type": "int", - "default": 300, + "name": "close-after-review", + "type": "boolean", + "default": false, "required": false, - "help": "Max seconds for the overall command (default: 300)" + "help": "Close the Review dialog after verification; final Submit is still never clicked" } ], "columns": [ "status", - "detail", - "url" + "url", + "receipt", + "uploaded", + "fieldsTouched", + "reviewReady", + "submitBlocked", + "warnings" ], "type": "js", - "modulePath": "instagram/post.js", - "sourceFile": "instagram/post.js", - "navigateBefore": true + "modulePath": "mercury/reimbursement-draft.js", + "sourceFile": "mercury/reimbursement-draft.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "instagram", - "name": "profile", - "description": "Get Instagram user profile info", + "site": "mercury", + "name": "reimbursement-plan", + "description": "Validate Mercury reimbursement inputs and print the draft plan without opening a browser", "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, + "example": "webcmd mercury reimbursement-plan --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", + "strategy": "local", + "browser": false, "args": [ { - "name": "username", + "name": "receipt", "type": "str", "required": true, - "positional": true, - "help": "Instagram username" - } - ], - "columns": [ - "username", - "name", - "followers", - "following", - "posts", - "verified", - "bio" - ], - "type": "js", - "modulePath": "instagram/profile.js", - "sourceFile": "instagram/profile.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "reel", - "description": "Post an Instagram reel video", - "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Local receipt/proof file path" + }, { - "name": "video", + "name": "amount", + "type": "str", + "required": true, + "help": "Original-currency amount, e.g. 140.00" + }, + { + "name": "currency", "type": "str", + "default": "CNY", "required": false, - "valueRequired": true, - "help": "Path to a single .mp4 video file", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "video/mp4" - ], - "maxBytes": 262144000 - } + "help": "Original currency code" }, { - "name": "content", + "name": "date", + "type": "str", + "required": true, + "help": "Expense date as YYYY-MM-DD" + }, + { + "name": "merchant", + "type": "str", + "required": true, + "help": "Merchant shown on the reimbursement" + }, + { + "name": "category", "type": "str", + "default": "Marketing & Advertising", "required": false, - "positional": true, - "help": "Caption text" + "help": "Mercury expense category" }, { - "name": "timeout", - "type": "int", - "default": 600, + "name": "notes", + "type": "str", + "required": true, + "help": "Business purpose / reimbursement notes" + }, + { + "name": "ocr-wait-seconds", + "type": "str", + "default": "8", "required": false, - "help": "Max seconds for the overall command (default: 600)" + "help": "Seconds the draft command waits after receipt upload before correcting OCR fields" + }, + { + "name": "close-after-review", + "type": "boolean", + "default": false, + "required": false, + "help": "For draft command: close the Review dialog after verification" } ], "columns": [ "status", - "detail", - "url" + "receipt", + "amount", + "currency", + "date", + "merchant", + "category", + "notes", + "safety" ], "type": "js", - "modulePath": "instagram/reel.js", - "sourceFile": "instagram/reel.js", - "navigateBefore": true + "modulePath": "mercury/reimbursement-plan.js", + "sourceFile": "mercury/reimbursement-plan.js" }, { - "site": "instagram", - "name": "save", - "description": "Save (bookmark) an Instagram post", + "site": "notebooklm", + "name": "add-source", + "description": "Add a URL, text, or local file source to an existing NotebookLM notebook", "access": "write", - "domain": "www.instagram.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "notebook", "type": "str", "required": true, "positional": true, - "help": "Username of the post author" + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "index", - "type": "int", - "default": 1, + "name": "url", + "type": "str", "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "post" - ], - "type": "js", - "modulePath": "instagram/save.js", - "sourceFile": "instagram/save.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "saved", - "description": "Get your saved Instagram posts (optionally from a specific collection)", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ + "help": "Source URL to add (http/https). Pass exactly one of --url, --content, --file." + }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "content", + "type": "str", "required": false, - "help": "Number of saved posts" + "help": "Raw text content to add as a Text source (max 10 MB)." }, { - "name": "collection", + "name": "file", "type": "str", "required": false, - "help": "Collection name (case-insensitive). Omit for the default \"All posts\" feed." - } - ], - "columns": [ - "index", - "user", - "caption", - "likes", - "comments", - "type" - ], - "type": "js", - "modulePath": "instagram/saved.js", - "sourceFile": "instagram/saved.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "search", - "description": "Search Instagram users", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ + "help": "Local file path to upload as a source (max 52428800 bytes; pdf / txt / md / html / docx / etc.). Uses Google Drive's 3-step resumable upload protocol." + }, { - "name": "query", + "name": "title", "type": "str", - "required": true, - "positional": true, - "help": "Search query" + "required": false, + "help": "Title for the text source (default \"Text Source\"). Ignored for --url and --file." }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "mime-type", + "type": "str", "required": false, - "help": "Number of results" + "help": "Override the auto-detected MIME type when --file is given." + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually add the remote source to the NotebookLM notebook" } ], "columns": [ - "rank", - "username", - "name", - "verified", - "private", - "url" - ], - "tags": [ - "search" + "notebook_id", + "source_id", + "kind", + "identifier", + "notebook_url" ], "type": "js", - "modulePath": "instagram/search.js", - "sourceFile": "instagram/search.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "notebooklm/add-source.js", + "sourceFile": "notebooklm/add-source.js", + "navigateBefore": false }, { - "site": "instagram", - "name": "story", - "description": "Post a single Instagram story image or video", + "site": "notebooklm", + "name": "create", + "description": "Create a new NotebookLM notebook with the given title", "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", + "domain": "notebooklm.google.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "media", + "name": "title", + "type": "str", + "required": true, + "positional": true, + "help": "Notebook title (1-200 chars)" + }, + { + "name": "emoji", "type": "str", "required": false, - "valueRequired": true, - "help": "Path to a single story image or video file" + "help": "Notebook emoji icon (default 📒)" }, { - "name": "timeout", - "type": "int", - "default": 300, + "name": "execute", + "type": "boolean", "required": false, - "help": "Max seconds for the overall command (default: 300)" + "help": "Actually create the remote NotebookLM notebook" } ], "columns": [ - "status", - "detail", + "id", + "title", + "emoji", "url" ], "type": "js", - "modulePath": "instagram/story.js", - "sourceFile": "instagram/story.js", - "navigateBefore": true + "modulePath": "notebooklm/create.js", + "sourceFile": "notebooklm/create.js", + "navigateBefore": false }, { - "site": "instagram", - "name": "unfollow", - "description": "Unfollow an Instagram user", - "access": "write", - "domain": "www.instagram.com", + "site": "notebooklm", + "name": "current", + "description": "Show metadata for the currently opened NotebookLM notebook tab", + "access": "read", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username to unfollow" - } - ], + "args": [], "columns": [ - "status", - "username" + "id", + "title", + "url", + "source" ], "type": "js", - "modulePath": "instagram/unfollow.js", - "sourceFile": "instagram/unfollow.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "notebooklm/current.js", + "sourceFile": "notebooklm/current.js", + "navigateBefore": false }, { - "site": "instagram", - "name": "unlike", - "description": "Unlike an Instagram post", + "site": "notebooklm", + "name": "generate-audio", + "description": "Trigger an Audio Overview (Deep Dive podcast) generation for a NotebookLM notebook, using all of its sources", "access": "write", - "domain": "www.instagram.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "notebook", "type": "str", "required": true, "positional": true, - "help": "Username of the post author" + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "index", - "type": "int", - "default": 1, + "name": "execute", + "type": "boolean", "required": false, - "help": "Post index (1 = most recent)" + "help": "Actually trigger remote NotebookLM audio generation" } ], "columns": [ + "notebook_id", + "audio_id", + "source_count", "status", - "user", - "post" + "notebook_url" ], "type": "js", - "modulePath": "instagram/unlike.js", - "sourceFile": "instagram/unlike.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "notebooklm/generate-audio.js", + "sourceFile": "notebooklm/generate-audio.js", + "navigateBefore": false }, { - "site": "instagram", - "name": "unsave", - "description": "Unsave (remove bookmark) an Instagram post", + "site": "notebooklm", + "name": "generate-slides", + "description": "Trigger a Slide Deck (AI presentation) generation for a NotebookLM notebook, using all of its sources", "access": "write", - "domain": "www.instagram.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "notebook", "type": "str", "required": true, "positional": true, - "help": "Username of the post author" + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "index", - "type": "int", - "default": 1, + "name": "length", + "type": "str", "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "post" - ], - "type": "js", - "modulePath": "instagram/unsave.js", - "sourceFile": "instagram/unsave.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "user", - "description": "Get recent posts from an Instagram user", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ + "help": "Slide deck length: 1=Short, 3=Default (default 3)" + }, { - "name": "username", + "name": "language", "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" + "required": false, + "help": "Language code (default en)" }, { - "name": "limit", - "type": "int", - "default": 12, + "name": "execute", + "type": "boolean", "required": false, - "help": "Number of posts" + "help": "Actually trigger remote NotebookLM slide deck generation" } ], "columns": [ - "index", - "caption", - "likes", - "comments", - "type", - "date" + "notebook_id", + "slides_id", + "source_count", + "status", + "notebook_url" ], "type": "js", - "modulePath": "instagram/user.js", - "sourceFile": "instagram/user.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "notebooklm/generate-slides.js", + "sourceFile": "notebooklm/generate-slides.js", + "navigateBefore": false }, { - "site": "instagram", - "name": "whoami", - "description": "Show the current logged-in instagram account", + "site": "notebooklm", + "name": "get", + "aliases": [ + "metadata" + ], + "description": "Get rich metadata for the currently opened NotebookLM notebook", "access": "read", - "domain": "instagram.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "user_id", - "username", - "full_name" - ], + "id", + "title", + "emoji", + "source_count", + "created_at", + "updated_at", + "url", + "source" + ], "type": "js", - "modulePath": "instagram/auth.js", - "sourceFile": "instagram/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/get.js", + "sourceFile": "notebooklm/get.js", + "navigateBefore": false }, { - "site": "linkedin-learning", - "name": "course", - "description": "Get LinkedIn Learning course detail by slug or course URL", + "site": "notebooklm", + "name": "history", + "description": "List NotebookLM conversation history threads in the current notebook", "access": "read", - "domain": "www.linkedin.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "slug", - "type": "string", - "required": true, - "positional": true, - "help": "Course slug (e.g. agentic-ai-build-your-first-agentic-ai-system) or full /learning/ URL" - } + "args": [], + "columns": [ + "thread_id", + "item_count", + "preview", + "source", + "notebook_id", + "url" ], + "type": "js", + "modulePath": "notebooklm/history.js", + "sourceFile": "notebooklm/history.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "list", + "description": "List NotebookLM notebooks via in-page batchexecute RPC in the current logged-in session", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ "title", - "slug", - "description", - "difficulty", - "duration_sec", - "videos_count", - "rating", - "rating_count", - "released", + "id", + "is_owner", + "created_at", + "source", "url" ], "type": "js", - "modulePath": "linkedin-learning/course.js", - "sourceFile": "linkedin-learning/course.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "notebooklm/list.js", + "sourceFile": "notebooklm/list.js", + "navigateBefore": false }, { - "site": "linkedin-learning", + "site": "notebooklm", "name": "login", - "description": "Open linkedin-learning login", + "description": "Open notebooklm login", "access": "write", - "domain": "linkedin.com", + "domain": "google.com", "strategy": "cookie", "browser": true, "args": [], @@ -9793,3412 +9238,1797 @@ "status", "logged_in", "site", - "public_id", - "plain_id", "name", + "authuser", "action", "verify_command" ], "type": "js", - "modulePath": "linkedin-learning/auth.js", - "sourceFile": "linkedin-learning/auth.js", + "modulePath": "notebooklm/auth.js", + "sourceFile": "notebooklm/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "linkedin-learning", - "name": "search", - "description": "Search LinkedIn Learning courses, videos, and learning paths by keyword", + "site": "notebooklm", + "name": "note-list", + "aliases": [ + "notes-list" + ], + "description": "List saved notes from the Studio panel of the current NotebookLM notebook", "access": "read", - "domain": "www.linkedin.com", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "title", + "created_at", + "source", + "url" + ], + "type": "js", + "modulePath": "notebooklm/note-list.js", + "sourceFile": "notebooklm/note-list.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "notes-get", + "description": "Get one note from the current NotebookLM notebook by title from the visible note editor", + "access": "read", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "keywords", - "type": "string", + "name": "note", + "type": "str", "required": true, "positional": true, - "help": "Search keywords, e.g. \"AI agent\"" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum results to return (1-50)" + "help": "Note title or id from the current notebook" } ], "columns": [ - "rank", - "type", "title", - "instructor", - "difficulty", - "duration_sec", - "rating", - "rating_count", - "viewers", + "content", + "source", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "linkedin-learning/search.js", - "sourceFile": "linkedin-learning/search.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "notebooklm/notes-get.js", + "sourceFile": "notebooklm/notes-get.js", + "navigateBefore": false }, { - "site": "linkedin-learning", - "name": "trending", - "description": "Browse LinkedIn Learning recommended courses across personalized carousels", + "site": "notebooklm", + "name": "open", + "aliases": [ + "select" + ], + "description": "Open one NotebookLM notebook in the adapter session by id or URL", "access": "read", - "domain": "www.linkedin.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum results to return (1-50)" + "name": "notebook", + "type": "str", + "required": true, + "positional": true, + "help": "Notebook id from list output, or a full NotebookLM notebook URL" } ], "columns": [ - "rank", - "group", - "type", + "id", "title", - "difficulty", - "viewers", - "url" + "url", + "source" ], "type": "js", - "modulePath": "linkedin-learning/trending.js", - "sourceFile": "linkedin-learning/trending.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "notebooklm/open.js", + "sourceFile": "notebooklm/open.js", + "navigateBefore": false }, { - "site": "linkedin-learning", - "name": "whoami", - "description": "Show the current logged-in linkedin-learning account", + "site": "notebooklm", + "name": "source-fulltext", + "description": "Get the extracted fulltext for one source in the currently opened NotebookLM notebook", "access": "read", - "domain": "linkedin.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "source", + "type": "str", + "required": true, + "positional": true, + "help": "Source id or title from the current notebook" + } + ], "columns": [ - "logged_in", - "site", - "public_id", - "plain_id", - "name" + "title", + "kind", + "char_count", + "url", + "source" ], "type": "js", - "modulePath": "linkedin-learning/auth.js", - "sourceFile": "linkedin-learning/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/source-fulltext.js", + "sourceFile": "notebooklm/source-fulltext.js", + "navigateBefore": false }, { - "site": "manus", - "name": "connectors", - "description": "List available Manus connectors (integrations).", + "site": "notebooklm", + "name": "source-get", + "description": "Get one source from the currently opened NotebookLM notebook by id or title", "access": "read", - "domain": "manus.im", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max connectors to return" + "name": "source", + "type": "str", + "required": true, + "positional": true, + "help": "Source id or title from the current notebook" } ], "columns": [ - "UID", - "Name", - "Brief" + "title", + "id", + "type", + "size", + "created_at", + "updated_at", + "url", + "source" ], "type": "js", - "modulePath": "manus/connectors.js", - "sourceFile": "manus/connectors.js", - "navigateBefore": true, - "siteSession": "persistent" + "modulePath": "notebooklm/source-get.js", + "sourceFile": "notebooklm/source-get.js", + "navigateBefore": false }, { - "site": "manus", - "name": "credits", - "description": "Show Manus credit balance and refresh details.", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "manus/credits.js", - "sourceFile": "manus/credits.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "list", - "description": "List Manus sessions (tasks).", + "site": "notebooklm", + "name": "source-guide", + "description": "Get the guide summary and keywords for one source in the currently opened NotebookLM notebook", "access": "read", - "domain": "manus.im", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max sessions to return" - }, - { - "name": "archived", - "type": "bool", - "default": false, - "required": false, - "help": "Include archived sessions" + "name": "source", + "type": "str", + "required": true, + "positional": true, + "help": "Source id or title from the current notebook" } ], "columns": [ - "id", - "Title", - "Status", - "Last Message", - "Last Updated", - "Credits" - ], - "type": "js", - "modulePath": "manus/list.js", - "sourceFile": "manus/list.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "login", - "description": "Open manus login", - "access": "write", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" + "source_id", + "notebook_id", + "title", + "type", + "summary", + "keywords", + "source" ], "type": "js", - "modulePath": "manus/auth.js", - "sourceFile": "manus/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/source-guide.js", + "sourceFile": "notebooklm/source-guide.js", + "navigateBefore": false }, { - "site": "manus", - "name": "read", - "description": "Show details for a specific Manus session.", + "site": "notebooklm", + "name": "source-list", + "description": "List sources for the currently opened NotebookLM notebook", "access": "read", - "domain": "manus.im", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "uid", - "type": "str", - "required": true, - "positional": true, - "help": "Session UID" - } - ], + "args": [], "columns": [ - "Field", - "Value" + "title", + "id", + "type", + "size", + "created_at", + "updated_at", + "url", + "source" ], "type": "js", - "modulePath": "manus/read.js", - "sourceFile": "manus/read.js", - "navigateBefore": true, - "siteSession": "persistent" + "modulePath": "notebooklm/source-list.js", + "sourceFile": "notebooklm/source-list.js", + "navigateBefore": false }, { - "site": "manus", - "name": "skills", - "description": "List Manus skills (user-added and system).", + "site": "notebooklm", + "name": "status", + "description": "Check NotebookLM page availability and login state in the current Chrome session", "access": "read", - "domain": "manus.im", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "ID", - "Name", - "Description", - "Source" + "status", + "login", + "page", + "url", + "title", + "notebooks" ], "type": "js", - "modulePath": "manus/skills.js", - "sourceFile": "manus/skills.js", - "navigateBefore": true, - "siteSession": "persistent" + "modulePath": "notebooklm/status.js", + "sourceFile": "notebooklm/status.js", + "navigateBefore": false }, { - "site": "manus", - "name": "status", - "description": "Show current Manus user profile and credit summary.", + "site": "notebooklm", + "name": "summary", + "description": "Get the summary block from the currently opened NotebookLM notebook", "access": "read", - "domain": "manus.im", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Field", - "Value" + "title", + "summary", + "source", + "url" ], "type": "js", - "modulePath": "manus/status.js", - "sourceFile": "manus/status.js", - "navigateBefore": true, - "siteSession": "persistent" + "modulePath": "notebooklm/summary.js", + "sourceFile": "notebooklm/summary.js", + "navigateBefore": false }, { - "site": "manus", + "site": "notebooklm", "name": "whoami", - "description": "Show the current logged-in manus account", + "description": "Show the current logged-in notebooklm account", "access": "read", - "domain": "manus.im", + "domain": "google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ "logged_in", "site", - "user_id", - "name" + "name", + "authuser" ], "type": "js", - "modulePath": "manus/auth.js", - "sourceFile": "manus/auth.js", + "modulePath": "notebooklm/auth.js", + "sourceFile": "notebooklm/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "medium", - "name": "feed", - "description": "Medium popular posts Feed", - "access": "read", - "domain": "medium.com", + "site": "notebooklm", + "name": "write-note", + "description": "Create a Studio note in an existing NotebookLM notebook with the given title and Markdown content", + "access": "write", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "topic", + "name": "notebook", "type": "str", - "default": "", - "required": false, - "help": "Topic (for example technology, programming, ai)" + "required": true, + "positional": true, + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "title", + "type": "str", + "required": true, + "help": "Note title (1-200 chars)" + }, + { + "name": "content", + "type": "str", + "required": true, + "help": "Note body as Markdown" + }, + { + "name": "execute", + "type": "boolean", "required": false, - "help": "Number of posts to return" + "help": "Actually create the remote NotebookLM note" } ], "columns": [ - "rank", + "notebook_id", + "note_id", "title", - "author", - "date", - "readTime", - "claps" + "notebook_url" ], "type": "js", - "modulePath": "medium/feed.js", - "sourceFile": "medium/feed.js", - "navigateBefore": "https://medium.com" + "modulePath": "notebooklm/write-note.js", + "sourceFile": "notebooklm/write-note.js", + "navigateBefore": false }, { - "site": "medium", - "name": "search", - "description": "Search Medium posts", - "access": "read", - "domain": "medium.com", - "strategy": "cookie", - "browser": true, + "site": "paperreview", + "name": "feedback", + "description": "Submit feedback for a paperreview.ai review token", + "access": "write", + "domain": "paperreview.ai", + "strategy": "public", + "browser": false, "args": [ { - "name": "keyword", + "name": "token", "type": "str", "required": true, "positional": true, - "help": "Search keyword" + "help": "Review token returned by paperreview.ai" }, { - "name": "limit", + "name": "helpfulness", "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" - } - ], - "columns": [ - "rank", - "title", - "author", - "date", - "readTime", - "claps", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "medium/search.js", - "sourceFile": "medium/search.js", - "navigateBefore": "https://medium.com" - }, - { - "site": "medium", - "name": "tag", - "description": "Latest Medium articles tagged with a given keyword (RSS feed)", - "access": "read", - "domain": "medium.com", - "strategy": "public", - "browser": false, - "args": [ + "required": true, + "help": "Helpfulness score from 1 to 5" + }, { - "name": "tag", + "name": "critical-error", "type": "str", "required": true, - "positional": true, - "help": "Lowercase tag slug (e.g. \"programming\", \"machine-learning\")" + "help": "Whether the review contains a critical error", + "choices": [ + "yes", + "no" + ] }, { - "name": "limit", + "name": "actionable-suggestions", + "type": "str", + "required": true, + "help": "Whether the review contains actionable suggestions", + "choices": [ + "yes", + "no" + ] + }, + { + "name": "additional-comments", + "type": "str", + "required": false, + "help": "Optional free-text feedback" + }, + { + "name": "timeout", "type": "int", - "default": 20, + "default": 30, "required": false, - "help": "Max articles (1-25 — single RSS page)" + "help": "Max seconds for the overall command (default: 30)" } ], "columns": [ - "rank", - "title", - "author", - "description", - "categories", - "published", - "url" + "status", + "token", + "helpfulness", + "critical_error", + "actionable_suggestions", + "message" ], "type": "js", - "modulePath": "medium/tag.js", - "sourceFile": "medium/tag.js" + "modulePath": "paperreview/feedback.js", + "sourceFile": "paperreview/feedback.js" }, { - "site": "medium", - "name": "user", - "description": "Get Medium user posts", + "site": "paperreview", + "name": "review", + "description": "Fetch a paperreview.ai review by token", "access": "read", - "domain": "medium.com", - "strategy": "cookie", - "browser": true, + "domain": "paperreview.ai", + "strategy": "public", + "browser": false, "args": [ { - "name": "username", + "name": "token", "type": "str", "required": true, "positional": true, - "help": "Medium username(for example @username or username)" + "help": "Review token returned by paperreview.ai" }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 20, + "default": 30, "required": false, - "help": "Number of posts to return" + "help": "Max seconds for the overall command (default: 30)" } ], - "columns": [ - "rank", - "title", - "date", - "readTime", - "claps", - "url" - ], - "type": "js", - "modulePath": "medium/user.js", - "sourceFile": "medium/user.js", - "navigateBefore": "https://medium.com" - }, - { - "site": "mercury", - "name": "check-login", - "description": "Open Mercury reimbursements and report whether the active browser profile is logged in", - "access": "read", - "example": "webcmd --profile mercury check-login -f json", - "domain": "app.mercury.com", - "strategy": "ui", - "browser": true, - "args": [], "columns": [ "status", - "loggedIn", - "url", - "hasSubmitExpense", - "hasReimbursements", - "title" + "title", + "venue", + "numerical_score", + "has_feedback", + "review_url" ], "type": "js", - "modulePath": "mercury/check-login.js", - "sourceFile": "mercury/check-login.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "paperreview/review.js", + "sourceFile": "paperreview/review.js" }, { - "site": "mercury", - "name": "reimbursement-draft", - "description": "Create a Mercury reimbursement draft from a local receipt, correct OCR fields, and stop at Review", + "site": "paperreview", + "name": "submit", + "description": "Submit a PDF to paperreview.ai for review", "access": "write", - "example": "webcmd --profile mercury reimbursement-draft --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", - "domain": "app.mercury.com", - "strategy": "ui", - "browser": true, + "domain": "paperreview.ai", + "strategy": "public", + "browser": false, "args": [ { - "name": "receipt", + "name": "pdf", "type": "str", "required": true, - "help": "Local receipt/proof file path", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp", - "application/pdf" - ], - "maxBytes": 26214400 - } + "positional": true, + "help": "Path to the paper PDF" }, { - "name": "amount", + "name": "email", "type": "str", "required": true, - "help": "Original-currency amount, e.g. 140.00" + "help": "Email address for the submission" }, { - "name": "currency", + "name": "venue", "type": "str", - "default": "CNY", "required": false, - "help": "Original currency code" - }, - { - "name": "date", - "type": "str", - "required": true, - "help": "Expense date as YYYY-MM-DD" - }, - { - "name": "merchant", - "type": "str", - "required": true, - "help": "Merchant shown on the reimbursement" + "help": "Optional target venue such as ICLR or NeurIPS" }, { - "name": "category", - "type": "str", - "default": "Marketing & Advertising", + "name": "dry-run", + "type": "bool", + "default": false, "required": false, - "help": "Mercury expense category" - }, - { - "name": "notes", - "type": "str", - "required": true, - "help": "Business purpose / reimbursement notes" + "help": "Validate the input and stop before remote submission" }, { - "name": "ocr-wait-seconds", - "type": "str", - "default": "8", + "name": "prepare-only", + "type": "bool", + "default": false, "required": false, - "help": "Seconds to wait after receipt upload before correcting OCR-overwritten fields" + "help": "Request an upload slot but stop before uploading the PDF" }, { - "name": "close-after-review", - "type": "boolean", - "default": false, + "name": "timeout", + "type": "int", + "default": 120, "required": false, - "help": "Close the Review dialog after verification; final Submit is still never clicked" + "help": "Max seconds for the overall command (default: 120)" } ], "columns": [ "status", - "url", - "receipt", - "uploaded", - "fieldsTouched", - "reviewReady", - "submitBlocked", - "warnings" + "file", + "email", + "venue", + "token", + "review_url", + "message" ], "type": "js", - "modulePath": "mercury/reimbursement-draft.js", - "sourceFile": "mercury/reimbursement-draft.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "paperreview/submit.js", + "sourceFile": "paperreview/submit.js" }, { - "site": "mercury", - "name": "reimbursement-plan", - "description": "Validate Mercury reimbursement inputs and print the draft plan without opening a browser", + "site": "pixiv", + "name": "detail", + "description": "View illustration details (tags, stats, URLs)", "access": "read", - "example": "webcmd mercury reimbursement-plan --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", - "strategy": "local", - "browser": false, + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "receipt", + "name": "id", "type": "str", "required": true, - "help": "Local receipt/proof file path" - }, - { - "name": "amount", - "type": "str", - "required": true, - "help": "Original-currency amount, e.g. 140.00" - }, - { - "name": "currency", - "type": "str", - "default": "CNY", - "required": false, - "help": "Original currency code" - }, - { - "name": "date", - "type": "str", - "required": true, - "help": "Expense date as YYYY-MM-DD" - }, - { - "name": "merchant", - "type": "str", - "required": true, - "help": "Merchant shown on the reimbursement" - }, - { - "name": "category", - "type": "str", - "default": "Marketing & Advertising", - "required": false, - "help": "Mercury expense category" - }, - { - "name": "notes", - "type": "str", - "required": true, - "help": "Business purpose / reimbursement notes" - }, - { - "name": "ocr-wait-seconds", - "type": "str", - "default": "8", - "required": false, - "help": "Seconds the draft command waits after receipt upload before correcting OCR fields" - }, - { - "name": "close-after-review", - "type": "boolean", - "default": false, - "required": false, - "help": "For draft command: close the Review dialog after verification" + "positional": true, + "help": "Illustration ID" } ], "columns": [ - "status", - "receipt", - "amount", - "currency", - "date", - "merchant", - "category", - "notes", - "safety" + "illust_id", + "title", + "author", + "type", + "pages", + "bookmarks", + "likes", + "views", + "tags", + "created", + "url" ], "type": "js", - "modulePath": "mercury/reimbursement-plan.js", - "sourceFile": "mercury/reimbursement-plan.js" + "modulePath": "pixiv/detail.js", + "sourceFile": "pixiv/detail.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "notebooklm", - "name": "add-source", - "description": "Add a URL, text, or local file source to an existing NotebookLM notebook", - "access": "write", - "domain": "notebooklm.google.com", + "site": "pixiv", + "name": "download", + "description": "Download illustration images from Pixiv", + "access": "read", + "domain": "www.pixiv.net", "strategy": "cookie", "browser": true, "args": [ { - "name": "notebook", + "name": "illust-id", "type": "str", "required": true, "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Source URL to add (http/https). Pass exactly one of --url, --content, --file." - }, - { - "name": "content", - "type": "str", - "required": false, - "help": "Raw text content to add as a Text source (max 10 MB)." - }, - { - "name": "file", - "type": "str", - "required": false, - "help": "Local file path to upload as a source (max 52428800 bytes; pdf / txt / md / html / docx / etc.). Uses Google Drive's 3-step resumable upload protocol." - }, - { - "name": "title", - "type": "str", - "required": false, - "help": "Title for the text source (default \"Text Source\"). Ignored for --url and --file." + "help": "Illustration ID" }, { - "name": "mime-type", + "name": "output", "type": "str", + "default": "./pixiv-downloads", "required": false, - "help": "Override the auto-detected MIME type when --file is given." - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually add the remote source to the NotebookLM notebook" + "help": "Output directory" } ], "columns": [ - "notebook_id", - "source_id", - "kind", - "identifier", - "notebook_url" + "index", + "type", + "status", + "size" ], "type": "js", - "modulePath": "notebooklm/add-source.js", - "sourceFile": "notebooklm/add-source.js", - "navigateBefore": false + "modulePath": "pixiv/download.js", + "sourceFile": "pixiv/download.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "notebooklm", - "name": "create", - "description": "Create a new NotebookLM notebook with the given title", - "access": "write", - "domain": "notebooklm.google.com", + "site": "pixiv", + "name": "illusts", + "description": "List a Pixiv artist's illustrations", + "access": "read", + "domain": "www.pixiv.net", "strategy": "cookie", "browser": true, "args": [ { - "name": "title", + "name": "user-id", "type": "str", "required": true, "positional": true, - "help": "Notebook title (1-200 chars)" - }, - { - "name": "emoji", - "type": "str", - "required": false, - "help": "Notebook emoji icon (default 📒)" + "help": "Pixiv user ID" }, { - "name": "execute", - "type": "boolean", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Actually create the remote NotebookLM notebook" + "help": "Number of results" } ], "columns": [ - "id", + "rank", "title", - "emoji", + "illust_id", + "pages", + "bookmarks", + "tags", + "created", "url" ], "type": "js", - "modulePath": "notebooklm/create.js", - "sourceFile": "notebooklm/create.js", - "navigateBefore": false + "modulePath": "pixiv/illusts.js", + "sourceFile": "pixiv/illusts.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "notebooklm", - "name": "current", - "description": "Show metadata for the currently opened NotebookLM notebook tab", - "access": "read", - "domain": "notebooklm.google.com", + "site": "pixiv", + "name": "login", + "description": "Open pixiv login", + "access": "write", + "domain": "pixiv.net", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "id", - "title", - "url", - "source" + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" ], "type": "js", - "modulePath": "notebooklm/current.js", - "sourceFile": "notebooklm/current.js", - "navigateBefore": false + "modulePath": "pixiv/auth.js", + "sourceFile": "pixiv/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "notebooklm", - "name": "generate-audio", - "description": "Trigger an Audio Overview (Deep Dive podcast) generation for a NotebookLM notebook, using all of its sources", - "access": "write", - "domain": "notebooklm.google.com", + "site": "pixiv", + "name": "ranking", + "description": "Pixiv illustration rankings (daily/weekly/monthly)", + "access": "read", + "domain": "www.pixiv.net", "strategy": "cookie", "browser": true, "args": [ { - "name": "notebook", + "name": "mode", "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" + "default": "daily", + "required": false, + "help": "Ranking mode", + "choices": [ + "daily", + "weekly", + "monthly", + "rookie", + "original", + "male", + "female", + "daily_r18", + "weekly_r18" + ] }, { - "name": "execute", - "type": "boolean", + "name": "page", + "type": "int", + "default": 1, "required": false, - "help": "Actually trigger remote NotebookLM audio generation" + "help": "Page number" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" } ], "columns": [ - "notebook_id", - "audio_id", - "source_count", - "status", - "notebook_url" + "rank", + "title", + "author", + "user_id", + "illust_id", + "pages", + "bookmarks", + "url" ], "type": "js", - "modulePath": "notebooklm/generate-audio.js", - "sourceFile": "notebooklm/generate-audio.js", - "navigateBefore": false + "modulePath": "pixiv/ranking.js", + "sourceFile": "pixiv/ranking.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "notebooklm", - "name": "generate-slides", - "description": "Trigger a Slide Deck (AI presentation) generation for a NotebookLM notebook, using all of its sources", - "access": "write", - "domain": "notebooklm.google.com", + "site": "pixiv", + "name": "search", + "description": "Search Pixiv illustrations by keyword", + "access": "read", + "domain": "www.pixiv.net", "strategy": "cookie", "browser": true, "args": [ { - "name": "notebook", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" + "help": "Search keyword or tag" }, { - "name": "length", + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + }, + { + "name": "order", "type": "str", + "default": "date_d", "required": false, - "help": "Slide deck length: 1=Short, 3=Default (default 3)" + "help": "Sort order", + "choices": [ + "date_d", + "date", + "popular_d", + "popular_male_d", + "popular_female_d" + ] }, { - "name": "language", + "name": "mode", "type": "str", + "default": "all", "required": false, - "help": "Language code (default en)" + "help": "Search mode", + "choices": [ + "all", + "safe", + "r18" + ] }, { - "name": "execute", - "type": "boolean", + "name": "page", + "type": "int", + "default": 1, "required": false, - "help": "Actually trigger remote NotebookLM slide deck generation" + "help": "Page number" } ], "columns": [ - "notebook_id", - "slides_id", - "source_count", - "status", - "notebook_url" - ], - "type": "js", - "modulePath": "notebooklm/generate-slides.js", - "sourceFile": "notebooklm/generate-slides.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "get", - "aliases": [ - "metadata" - ], - "description": "Get rich metadata for the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", + "rank", "title", - "emoji", - "source_count", - "created_at", - "updated_at", - "url", - "source" + "author", + "user_id", + "illust_id", + "pages", + "bookmarks", + "tags", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "notebooklm/get.js", - "sourceFile": "notebooklm/get.js", - "navigateBefore": false + "modulePath": "pixiv/search.js", + "sourceFile": "pixiv/search.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "notebooklm", - "name": "history", - "description": "List NotebookLM conversation history threads in the current notebook", + "site": "pixiv", + "name": "user", + "description": "View Pixiv artist profile", "access": "read", - "domain": "notebooklm.google.com", + "domain": "www.pixiv.net", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "uid", + "type": "str", + "required": true, + "positional": true, + "help": "Pixiv user ID" + } + ], "columns": [ - "thread_id", - "item_count", - "preview", - "source", - "notebook_id", + "user_id", + "name", + "premium", + "following", + "illusts", + "manga", + "novels", + "comment", "url" ], "type": "js", - "modulePath": "notebooklm/history.js", - "sourceFile": "notebooklm/history.js", - "navigateBefore": false + "modulePath": "pixiv/user.js", + "sourceFile": "pixiv/user.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "notebooklm", - "name": "list", - "description": "List NotebookLM notebooks via in-page batchexecute RPC in the current logged-in session", + "site": "pixiv", + "name": "whoami", + "description": "Show the current logged-in pixiv account", "access": "read", - "domain": "notebooklm.google.com", + "domain": "pixiv.net", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "title", - "id", - "is_owner", - "created_at", - "source", - "url" + "logged_in", + "site", + "user_id", + "name" ], "type": "js", - "modulePath": "notebooklm/list.js", - "sourceFile": "notebooklm/list.js", - "navigateBefore": false + "modulePath": "pixiv/auth.js", + "sourceFile": "pixiv/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "notebooklm", - "name": "login", - "description": "Open notebooklm login", - "access": "write", - "domain": "google.com", + "site": "practo", + "name": "appointment", + "description": "Show logged-in Practo Drive appointment details", + "access": "read", + "domain": "drive.practo.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "appointment_id", + "type": "str", + "required": true, + "positional": true, + "help": "Appointment id from `practo appointments`" + } + ], "columns": [ + "appointment_id", "status", - "logged_in", - "site", - "name", - "authuser", - "action", - "verify_command" + "summary" ], "type": "js", - "modulePath": "notebooklm/auth.js", - "sourceFile": "notebooklm/auth.js", + "modulePath": "practo/appointment.js", + "sourceFile": "practo/appointment.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "notebooklm", - "name": "note-list", - "aliases": [ - "notes-list" - ], - "description": "List saved notes from the Studio panel of the current NotebookLM notebook", + "site": "practo", + "name": "appointments", + "description": "List logged-in Practo Drive appointments", "access": "read", - "domain": "notebooklm.google.com", + "domain": "drive.practo.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "title", - "created_at", - "source", - "url" + "appointment_id", + "doctor", + "practice", + "time", + "status" ], "type": "js", - "modulePath": "notebooklm/note-list.js", - "sourceFile": "notebooklm/note-list.js", - "navigateBefore": false + "modulePath": "practo/appointments.js", + "sourceFile": "practo/appointments.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "notebooklm", - "name": "notes-get", - "description": "Get one note from the current NotebookLM notebook by title from the visible note editor", - "access": "read", - "domain": "notebooklm.google.com", + "site": "practo", + "name": "book-confirm", + "description": "Confirm a Practo clinic visit booking after explicit confirmation", + "access": "write", + "domain": "www.practo.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "note", + "name": "practice_doctor_id", "type": "str", "required": true, "positional": true, - "help": "Note title or id from the current notebook" - } - ], - "columns": [ - "title", - "content", - "source", + "help": "Practo practice_doctor_id" + }, + { + "name": "time", + "type": "str", + "required": true, + "help": "Slot time YYYY-MM-DD HH:mm:ss" + }, + { + "name": "profile-url", + "type": "str", + "required": false, + "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "Required. Set --confirm true to create the appointment." + } + ], + "columns": [ + "status", + "practice_doctor_id", + "time", "url" ], "type": "js", - "modulePath": "notebooklm/notes-get.js", - "sourceFile": "notebooklm/notes-get.js", - "navigateBefore": false + "modulePath": "practo/book-confirm.js", + "sourceFile": "practo/book-confirm.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "notebooklm", - "name": "open", - "aliases": [ - "select" - ], - "description": "Open one NotebookLM notebook in the adapter session by id or URL", + "site": "practo", + "name": "book-preview", + "description": "Preview Practo booking details for a selected slot without confirming", "access": "read", - "domain": "notebooklm.google.com", + "domain": "www.practo.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "notebook", + "name": "practice_doctor_id", "type": "str", "required": true, "positional": true, - "help": "Notebook id from list output, or a full NotebookLM notebook URL" + "help": "Practo practice_doctor_id" + }, + { + "name": "time", + "type": "str", + "required": true, + "help": "Slot time YYYY-MM-DD HH:mm:ss" + }, + { + "name": "profile-url", + "type": "str", + "required": false, + "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" } ], "columns": [ - "id", - "title", - "url", - "source" + "practice_doctor_id", + "time", + "amount", + "prepaid", + "payment_mode", + "requires_payment", + "confirm_button", + "booking_url" ], "type": "js", - "modulePath": "notebooklm/open.js", - "sourceFile": "notebooklm/open.js", - "navigateBefore": false + "modulePath": "practo/book-preview.js", + "sourceFile": "practo/book-preview.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "notebooklm", - "name": "source-fulltext", - "description": "Get the extracted fulltext for one source in the currently opened NotebookLM notebook", + "site": "practo", + "name": "booking-link", + "description": "Build a Practo booking URL for a selected slot without confirming it", "access": "read", - "domain": "notebooklm.google.com", + "domain": "www.practo.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "source", + "name": "practice_doctor_id", "type": "str", "required": true, "positional": true, - "help": "Source id or title from the current notebook" + "help": "Practo practice_doctor_id" + }, + { + "name": "time", + "type": "str", + "required": true, + "help": "Slot time YYYY-MM-DD HH:mm:ss" + }, + { + "name": "profile-url", + "type": "str", + "required": false, + "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" } ], "columns": [ - "title", - "kind", - "char_count", - "url", - "source" + "practice_doctor_id", + "time", + "booking_url" ], "type": "js", - "modulePath": "notebooklm/source-fulltext.js", - "sourceFile": "notebooklm/source-fulltext.js", + "modulePath": "practo/booking-link.js", + "sourceFile": "practo/booking-link.js", "navigateBefore": false }, { - "site": "notebooklm", - "name": "source-get", - "description": "Get one source from the currently opened NotebookLM notebook by id or title", - "access": "read", - "domain": "notebooklm.google.com", + "site": "practo", + "name": "cancel", + "description": "Cancel a logged-in Practo Drive appointment after explicit confirmation", + "access": "write", + "domain": "drive.practo.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "source", + "name": "appointment_id", "type": "str", "required": true, "positional": true, - "help": "Source id or title from the current notebook" + "help": "Appointment id from `practo appointments`" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "Required. Set --confirm true to cancel the appointment." } ], "columns": [ - "title", - "id", - "type", - "size", - "created_at", - "updated_at", - "url", - "source" + "status", + "appointment_id" ], "type": "js", - "modulePath": "notebooklm/source-get.js", - "sourceFile": "notebooklm/source-get.js", - "navigateBefore": false + "modulePath": "practo/cancel.js", + "sourceFile": "practo/cancel.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "notebooklm", - "name": "source-guide", - "description": "Get the guide summary and keywords for one source in the currently opened NotebookLM notebook", + "site": "practo", + "name": "contact", + "description": "Get Practo virtual contact number for a practice_doctor_id", "access": "read", - "domain": "notebooklm.google.com", + "domain": "www.practo.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "source", + "name": "practice_doctor_id", "type": "str", "required": true, "positional": true, - "help": "Source id or title from the current notebook" + "help": "Practo practice_doctor_id from search results" } ], "columns": [ - "source_id", - "notebook_id", - "title", - "type", - "summary", - "keywords", - "source" + "practice_doctor_id", + "phone", + "raw" ], "type": "js", - "modulePath": "notebooklm/source-guide.js", - "sourceFile": "notebooklm/source-guide.js", + "modulePath": "practo/contact.js", + "sourceFile": "practo/contact.js", "navigateBefore": false }, { - "site": "notebooklm", - "name": "source-list", - "description": "List sources for the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", + "site": "practo", + "name": "login", + "description": "Open practo login", + "access": "write", + "domain": "www.practo.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "title", - "id", - "type", - "size", - "created_at", - "updated_at", - "url", - "source" + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" ], "type": "js", - "modulePath": "notebooklm/source-list.js", - "sourceFile": "notebooklm/source-list.js", - "navigateBefore": false + "modulePath": "practo/login.js", + "sourceFile": "practo/login.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "notebooklm", - "name": "status", - "description": "Check NotebookLM page availability and login state in the current Chrome session", + "site": "practo", + "name": "profile", + "description": "Read public details from a Practo doctor profile URL", "access": "read", - "domain": "notebooklm.google.com", + "domain": "www.practo.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "Practo doctor profile URL" + } + ], "columns": [ - "status", - "login", - "page", - "url", - "title", - "notebooks" + "name", + "specialty", + "experience", + "fee", + "profile_url" ], "type": "js", - "modulePath": "notebooklm/status.js", - "sourceFile": "notebooklm/status.js", + "modulePath": "practo/profile.js", + "sourceFile": "practo/profile.js", "navigateBefore": false }, { - "site": "notebooklm", - "name": "summary", - "description": "Get the summary block from the currently opened NotebookLM notebook", + "site": "practo", + "name": "search", + "description": "Search Practo doctors by specialty, city, and optional locality", "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "summary", - "source", - "url" - ], - "type": "js", - "modulePath": "notebooklm/summary.js", - "sourceFile": "notebooklm/summary.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "whoami", - "description": "Show the current logged-in notebooklm account", - "access": "read", - "domain": "google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name", - "authuser" - ], - "type": "js", - "modulePath": "notebooklm/auth.js", - "sourceFile": "notebooklm/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "notebooklm", - "name": "write-note", - "description": "Create a Studio note in an existing NotebookLM notebook with the given title and Markdown content", - "access": "write", - "domain": "notebooklm.google.com", + "domain": "www.practo.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "notebook", + "name": "specialty", "type": "str", "required": true, "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" + "help": "Doctor specialty, e.g. orthopedist or dermatologist" }, { - "name": "title", + "name": "city", "type": "str", - "required": true, - "help": "Note title (1-200 chars)" + "default": "bangalore", + "required": false, + "help": "City, e.g. bangalore" }, { - "name": "content", + "name": "locality", "type": "str", - "required": true, - "help": "Note body as Markdown" + "required": false, + "help": "Optional locality, e.g. indiranagar" }, { - "name": "execute", - "type": "boolean", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Actually create the remote NotebookLM note" + "help": "Max doctors to return (1-25)" } ], "columns": [ - "notebook_id", - "note_id", - "title", - "notebook_url" + "rank", + "practice_doctor_id", + "doctor_id", + "practice_id", + "name", + "specialty", + "experience_years", + "locality", + "clinic", + "fee", + "next_available", + "profile_url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "notebooklm/write-note.js", - "sourceFile": "notebooklm/write-note.js", + "modulePath": "practo/search.js", + "sourceFile": "practo/search.js", "navigateBefore": false }, { - "site": "paperreview", - "name": "feedback", - "description": "Submit feedback for a paperreview.ai review token", - "access": "write", - "domain": "paperreview.ai", - "strategy": "public", - "browser": false, + "site": "practo", + "name": "slots", + "description": "List available Practo appointment slots for a practice_doctor_id", + "access": "read", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "token", + "name": "practice_doctor_id", "type": "str", "required": true, "positional": true, - "help": "Review token returned by paperreview.ai" - }, - { - "name": "helpfulness", - "type": "int", - "required": true, - "help": "Helpfulness score from 1 to 5" - }, - { - "name": "critical-error", - "type": "str", - "required": true, - "help": "Whether the review contains a critical error", - "choices": [ - "yes", - "no" - ] - }, - { - "name": "actionable-suggestions", - "type": "str", - "required": true, - "help": "Whether the review contains actionable suggestions", - "choices": [ - "yes", - "no" - ] - }, - { - "name": "additional-comments", - "type": "str", - "required": false, - "help": "Optional free-text feedback" + "help": "Practo practice_doctor_id from search results" }, { - "name": "timeout", + "name": "limit", "type": "int", - "default": 30, + "default": 20, "required": false, - "help": "Max seconds for the overall command (default: 30)" + "help": "Max slots to return (1-25)" } ], "columns": [ - "status", - "token", - "helpfulness", - "critical_error", - "actionable_suggestions", - "message" + "practice_doctor_id", + "time", + "available", + "amount", + "prepaid", + "appointment_token" ], "type": "js", - "modulePath": "paperreview/feedback.js", - "sourceFile": "paperreview/feedback.js" + "modulePath": "practo/slots.js", + "sourceFile": "practo/slots.js", + "navigateBefore": false }, { - "site": "paperreview", - "name": "review", - "description": "Fetch a paperreview.ai review by token", + "site": "practo", + "name": "whoami", + "aliases": [ + "auth-status" + ], + "description": "Show the current logged-in practo account", "access": "read", - "domain": "paperreview.ai", - "strategy": "public", - "browser": false, + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name" + ], + "type": "js", + "modulePath": "practo/login.js", + "sourceFile": "practo/login.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "producthunt", + "name": "browse", + "description": "Best products in a Product Hunt category", + "access": "read", + "domain": "www.producthunt.com", + "strategy": "intercept", + "browser": true, "args": [ { - "name": "token", - "type": "str", + "name": "category", + "type": "string", "required": true, "positional": true, - "help": "Review token returned by paperreview.ai" + "help": "Category slug, e.g. vibe-coding, ai-agents, developer-tools" }, { - "name": "timeout", + "name": "limit", "type": "int", - "default": 30, + "default": 20, "required": false, - "help": "Max seconds for the overall command (default: 30)" + "help": "Number of results (max 50)" } ], "columns": [ - "status", - "title", - "venue", - "numerical_score", - "has_feedback", - "review_url" + "rank", + "name", + "tagline", + "reviews", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "paperreview/review.js", - "sourceFile": "paperreview/review.js" + "modulePath": "producthunt/browse.js", + "sourceFile": "producthunt/browse.js", + "navigateBefore": true }, { - "site": "paperreview", - "name": "submit", - "description": "Submit a PDF to paperreview.ai for review", - "access": "write", - "domain": "paperreview.ai", - "strategy": "public", - "browser": false, + "site": "producthunt", + "name": "hot", + "description": "Today's top Product Hunt launches with vote counts", + "access": "read", + "domain": "www.producthunt.com", + "strategy": "intercept", + "browser": true, "args": [ { - "name": "pdf", - "type": "str", - "required": true, - "positional": true, - "help": "Path to the paper PDF" - }, - { - "name": "email", - "type": "str", - "required": true, - "help": "Email address for the submission" - }, - { - "name": "venue", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Optional target venue such as ICLR or NeurIPS" - }, - { - "name": "dry-run", - "type": "bool", - "default": false, - "required": false, - "help": "Validate the input and stop before remote submission" - }, - { - "name": "prepare-only", - "type": "bool", - "default": false, - "required": false, - "help": "Request an upload slot but stop before uploading the PDF" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds for the overall command (default: 120)" + "help": "Number of results (max 50)" } ], "columns": [ - "status", - "file", - "email", - "venue", - "token", - "review_url", - "message" + "rank", + "name", + "votes", + "url" ], "type": "js", - "modulePath": "paperreview/submit.js", - "sourceFile": "paperreview/submit.js" + "modulePath": "producthunt/hot.js", + "sourceFile": "producthunt/hot.js", + "navigateBefore": true }, { - "site": "pixiv", - "name": "detail", - "description": "View illustration details (tags, stats, URLs)", + "site": "producthunt", + "name": "posts", + "description": "Latest Product Hunt launches (optional category filter)", "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", - "browser": true, + "domain": "www.producthunt.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Illustration ID" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results (max 50)" + }, + { + "name": "category", + "type": "string", + "default": "", + "required": false, + "help": "Category filter: ai-agents, ai-coding-agents, ai-code-editors, ai-chatbots, ai-workflow-automation, vibe-coding, developer-tools, productivity, design-creative, marketing-sales, no-code-platforms, llms, finance, social-community, engineering-development" } ], "columns": [ - "illust_id", - "title", + "rank", + "name", + "tagline", "author", - "type", - "pages", - "bookmarks", - "likes", - "views", - "tags", - "created", + "date", "url" ], "type": "js", - "modulePath": "pixiv/detail.js", - "sourceFile": "pixiv/detail.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "producthunt/posts.js", + "sourceFile": "producthunt/posts.js" }, { - "site": "pixiv", - "name": "download", - "description": "Download illustration images from Pixiv", + "site": "producthunt", + "name": "today", + "description": "Today's Product Hunt launches (most recent day in feed)", "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", - "browser": true, + "domain": "www.producthunt.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "illust-id", - "type": "str", - "required": true, - "positional": true, - "help": "Illustration ID" - }, - { - "name": "output", - "type": "str", - "default": "./pixiv-downloads", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Output directory" + "help": "Max results" } ], "columns": [ - "index", - "type", - "status", - "size" + "rank", + "name", + "tagline", + "author", + "url" ], "type": "js", - "modulePath": "pixiv/download.js", - "sourceFile": "pixiv/download.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "producthunt/today.js", + "sourceFile": "producthunt/today.js" }, { - "site": "pixiv", - "name": "illusts", - "description": "List a Pixiv artist's illustrations", + "site": "qoder", + "name": "account", + "description": "Click the account button (username) in the Qoder sidebar and return the visible account dropdown items.", "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "user-id", + "name": "username", "type": "str", - "required": true, - "positional": true, - "help": "Pixiv user ID" - }, - { - "name": "limit", - "type": "int", - "default": 20, "required": false, - "help": "Number of results" + "help": "Username text shown in the sidebar (default: tries common short labels)" } ], "columns": [ - "rank", - "title", - "illust_id", - "pages", - "bookmarks", - "tags", - "created", - "url" + "Field", + "Value" ], "type": "js", - "modulePath": "pixiv/illusts.js", - "sourceFile": "pixiv/illusts.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "pixiv", - "name": "login", - "description": "Open pixiv login", + "site": "qoder", + "name": "add-workspace", + "description": "Click \"Add Workspace\" — opens the folder picker. Note: this opens a system file-picker dialog that Qoder controls; the actual folder selection must be done in the UI by the user.", "access": "write", - "domain": "pixiv.net", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [], "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" + "Status" ], "type": "js", - "modulePath": "pixiv/auth.js", - "sourceFile": "pixiv/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "pixiv", - "name": "ranking", - "description": "Pixiv illustration rankings (daily/weekly/monthly)", - "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", + "site": "qoder", + "name": "ask", + "description": "Send a prompt to Qoder and wait up to --timeout seconds for the reply (best-effort: polls for the chat turn count to grow + stabilize).", + "access": "write", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "mode", + "name": "text", "type": "str", - "default": "daily", - "required": false, - "help": "Ranking mode", - "choices": [ - "daily", - "weekly", - "monthly", - "rookie", - "original", - "male", - "female", - "daily_r18", - "weekly_r18" - ] - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number" + "required": true, + "positional": true, + "help": "Prompt text" }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 20, + "default": 120, "required": false, - "help": "Number of results" + "help": "Max seconds to wait" } ], "columns": [ - "rank", - "title", - "author", - "user_id", - "illust_id", - "pages", - "bookmarks", - "url" + "Role", + "Text", + "WaitedSeconds" ], "type": "js", - "modulePath": "pixiv/ranking.js", - "sourceFile": "pixiv/ranking.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "qoder/quest.js", + "sourceFile": "qoder/quest.js", + "navigateBefore": true }, { - "site": "pixiv", - "name": "search", - "description": "Search Pixiv illustrations by keyword", + "site": "qoder", + "name": "credits", + "description": "Click \"Credits Usage\" and return the credits-usage display text.", "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "history", + "description": "List Quests visible in the Qoder sidebar. Returns title + visible metadata.", + "access": "read", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword or tag" - }, { "name": "limit", "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - }, - { - "name": "order", - "type": "str", - "default": "date_d", - "required": false, - "help": "Sort order", - "choices": [ - "date_d", - "date", - "popular_d", - "popular_male_d", - "popular_female_d" - ] - }, - { - "name": "mode", - "type": "str", - "default": "all", - "required": false, - "help": "Search mode", - "choices": [ - "all", - "safe", - "r18" - ] - }, - { - "name": "page", - "type": "int", - "default": 1, + "default": 50, "required": false, - "help": "Page number" + "help": "" } ], "columns": [ - "rank", - "title", - "author", - "user_id", - "illust_id", - "pages", - "bookmarks", - "tags", - "url" - ], - "tags": [ - "search" + "Index", + "Title" ], "type": "js", - "modulePath": "pixiv/search.js", - "sourceFile": "pixiv/search.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "qoder/history.js", + "sourceFile": "qoder/history.js", + "navigateBefore": true }, { - "site": "pixiv", - "name": "user", - "description": "View Pixiv artist profile", - "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", + "site": "qoder", + "name": "knowledge", + "description": "Open the Knowledge view (Qoder's personal/team knowledge base).", + "access": "write", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "uid", - "type": "str", - "required": true, - "positional": true, - "help": "Pixiv user ID" - } - ], + "args": [], "columns": [ - "user_id", - "name", - "premium", - "following", - "illusts", - "manga", - "novels", - "comment", - "url" + "Status" ], "type": "js", - "modulePath": "pixiv/user.js", - "sourceFile": "pixiv/user.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "pixiv", - "name": "whoami", - "description": "Show the current logged-in pixiv account", - "access": "read", - "domain": "pixiv.net", - "strategy": "cookie", + "site": "qoder", + "name": "marketplace", + "description": "Open the Qoder Marketplace.", + "access": "write", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "user_id", - "name" + "Status" ], "type": "js", - "modulePath": "pixiv/auth.js", - "sourceFile": "pixiv/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "practo", - "name": "appointment", - "description": "Show logged-in Practo Drive appointment details", + "site": "qoder", + "name": "more-actions", + "description": "Click the \"More Actions\" button and list its menu items.", "access": "read", - "domain": "drive.practo.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "appointment_id", - "type": "str", - "required": true, - "positional": true, - "help": "Appointment id from `practo appointments`" - } - ], + "args": [], "columns": [ - "appointment_id", - "status", - "summary" + "Index", + "Item" ], "type": "js", - "modulePath": "practo/appointment.js", - "sourceFile": "practo/appointment.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "practo", - "name": "appointments", - "description": "List logged-in Practo Drive appointments", - "access": "read", - "domain": "drive.practo.com", - "strategy": "cookie", + "site": "qoder", + "name": "new", + "description": "Start a new Qoder Quest (conversation). Clicks the \"New Quest\" button in the sidebar (or its ⌘N variant).", + "access": "write", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [], "columns": [ - "appointment_id", - "doctor", - "practice", - "time", - "status" + "Status" ], "type": "js", - "modulePath": "practo/appointments.js", - "sourceFile": "practo/appointments.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "qoder/quest.js", + "sourceFile": "qoder/quest.js", + "navigateBefore": true }, { - "site": "practo", - "name": "book-confirm", - "description": "Confirm a Practo clinic visit booking after explicit confirmation", + "site": "qoder", + "name": "open-editor", + "description": "Click \"Open Editor\" — opens the current draft in a full editor pane.", "access": "write", - "domain": "www.practo.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "practice_doctor_id", - "type": "str", - "required": true, - "positional": true, - "help": "Practo practice_doctor_id" - }, - { - "name": "time", - "type": "str", - "required": true, - "help": "Slot time YYYY-MM-DD HH:mm:ss" - }, - { - "name": "profile-url", - "type": "str", - "required": false, - "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" - }, - { - "name": "confirm", - "type": "boolean", - "default": false, - "required": false, - "help": "Required. Set --confirm true to create the appointment." - } - ], + "args": [], "columns": [ - "status", - "practice_doctor_id", - "time", - "url" + "Status" ], "type": "js", - "modulePath": "practo/book-confirm.js", - "sourceFile": "practo/book-confirm.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "qoder/composer.js", + "sourceFile": "qoder/composer.js", + "navigateBefore": true }, { - "site": "practo", - "name": "book-preview", - "description": "Preview Practo booking details for a selected slot without confirming", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "practice_doctor_id", - "type": "str", - "required": true, - "positional": true, - "help": "Practo practice_doctor_id" - }, - { - "name": "time", - "type": "str", - "required": true, - "help": "Slot time YYYY-MM-DD HH:mm:ss" - }, - { - "name": "profile-url", - "type": "str", - "required": false, - "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" - } - ], - "columns": [ - "practice_doctor_id", - "time", - "amount", - "prepaid", - "payment_mode", - "requires_payment", - "confirm_button", - "booking_url" - ], - "type": "js", - "modulePath": "practo/book-preview.js", - "sourceFile": "practo/book-preview.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "practo", - "name": "booking-link", - "description": "Build a Practo booking URL for a selected slot without confirming it", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "practice_doctor_id", - "type": "str", - "required": true, - "positional": true, - "help": "Practo practice_doctor_id" - }, - { - "name": "time", - "type": "str", - "required": true, - "help": "Slot time YYYY-MM-DD HH:mm:ss" - }, - { - "name": "profile-url", - "type": "str", - "required": false, - "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" - } - ], - "columns": [ - "practice_doctor_id", - "time", - "booking_url" - ], - "type": "js", - "modulePath": "practo/booking-link.js", - "sourceFile": "practo/booking-link.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "cancel", - "description": "Cancel a logged-in Practo Drive appointment after explicit confirmation", + "site": "qoder", + "name": "open-panel", + "description": "Open / close the Qoder bottom panel (Output / Terminal / Debug Console). ⌥⌘B equivalent.", "access": "write", - "domain": "drive.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "appointment_id", - "type": "str", - "required": true, - "positional": true, - "help": "Appointment id from `practo appointments`" - }, - { - "name": "confirm", - "type": "boolean", - "default": false, - "required": false, - "help": "Required. Set --confirm true to cancel the appointment." - } - ], - "columns": [ - "status", - "appointment_id" - ], - "type": "js", - "modulePath": "practo/cancel.js", - "sourceFile": "practo/cancel.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "practo", - "name": "contact", - "description": "Get Practo virtual contact number for a practice_doctor_id", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "practice_doctor_id", - "type": "str", - "required": true, - "positional": true, - "help": "Practo practice_doctor_id from search results" - } - ], + "args": [], "columns": [ - "practice_doctor_id", - "phone", - "raw" + "Status" ], "type": "js", - "modulePath": "practo/contact.js", - "sourceFile": "practo/contact.js", - "navigateBefore": false + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "practo", - "name": "login", - "description": "Open practo login", + "site": "qoder", + "name": "prompt-enhance", + "description": "Click \"Prompt Enhance\" — Qoder rewrites the current composer draft for better LLM consumption.", "access": "write", - "domain": "www.practo.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [], "columns": [ - "status", - "logged_in", - "site", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "practo/login.js", - "sourceFile": "practo/login.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "practo", - "name": "profile", - "description": "Read public details from a Practo doctor profile URL", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "Practo doctor profile URL" - } - ], - "columns": [ - "name", - "specialty", - "experience", - "fee", - "profile_url" + "Status" ], "type": "js", - "modulePath": "practo/profile.js", - "sourceFile": "practo/profile.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "search", - "description": "Search Practo doctors by specialty, city, and optional locality", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "specialty", - "type": "str", - "required": true, - "positional": true, - "help": "Doctor specialty, e.g. orthopedist or dermatologist" - }, - { - "name": "city", - "type": "str", - "default": "bangalore", - "required": false, - "help": "City, e.g. bangalore" - }, - { - "name": "locality", - "type": "str", - "required": false, - "help": "Optional locality, e.g. indiranagar" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max doctors to return (1-25)" - } - ], - "columns": [ - "rank", - "practice_doctor_id", - "doctor_id", - "practice_id", - "name", - "specialty", - "experience_years", - "locality", - "clinic", - "fee", - "next_available", - "profile_url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "practo/search.js", - "sourceFile": "practo/search.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "slots", - "description": "List available Practo appointment slots for a practice_doctor_id", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "practice_doctor_id", - "type": "str", - "required": true, - "positional": true, - "help": "Practo practice_doctor_id from search results" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max slots to return (1-25)" - } - ], - "columns": [ - "practice_doctor_id", - "time", - "available", - "amount", - "prepaid", - "appointment_token" - ], - "type": "js", - "modulePath": "practo/slots.js", - "sourceFile": "practo/slots.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "whoami", - "aliases": [ - "auth-status" - ], - "description": "Show the current logged-in practo account", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name" - ], - "type": "js", - "modulePath": "practo/login.js", - "sourceFile": "practo/login.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "producthunt", - "name": "browse", - "description": "Best products in a Product Hunt category", - "access": "read", - "domain": "www.producthunt.com", - "strategy": "intercept", - "browser": true, - "args": [ - { - "name": "category", - "type": "string", - "required": true, - "positional": true, - "help": "Category slug, e.g. vibe-coding, ai-agents, developer-tools" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results (max 50)" - } - ], - "columns": [ - "rank", - "name", - "tagline", - "reviews", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "producthunt/browse.js", - "sourceFile": "producthunt/browse.js", - "navigateBefore": true - }, - { - "site": "producthunt", - "name": "hot", - "description": "Today's top Product Hunt launches with vote counts", - "access": "read", - "domain": "www.producthunt.com", - "strategy": "intercept", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results (max 50)" - } - ], - "columns": [ - "rank", - "name", - "votes", - "url" - ], - "type": "js", - "modulePath": "producthunt/hot.js", - "sourceFile": "producthunt/hot.js", - "navigateBefore": true - }, - { - "site": "producthunt", - "name": "posts", - "description": "Latest Product Hunt launches (optional category filter)", - "access": "read", - "domain": "www.producthunt.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results (max 50)" - }, - { - "name": "category", - "type": "string", - "default": "", - "required": false, - "help": "Category filter: ai-agents, ai-coding-agents, ai-code-editors, ai-chatbots, ai-workflow-automation, vibe-coding, developer-tools, productivity, design-creative, marketing-sales, no-code-platforms, llms, finance, social-community, engineering-development" - } - ], - "columns": [ - "rank", - "name", - "tagline", - "author", - "date", - "url" - ], - "type": "js", - "modulePath": "producthunt/posts.js", - "sourceFile": "producthunt/posts.js" - }, - { - "site": "producthunt", - "name": "today", - "description": "Today's Product Hunt launches (most recent day in feed)", - "access": "read", - "domain": "www.producthunt.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results" - } - ], - "columns": [ - "rank", - "name", - "tagline", - "author", - "url" - ], - "type": "js", - "modulePath": "producthunt/today.js", - "sourceFile": "producthunt/today.js" - }, - { - "site": "qoder", - "name": "account", - "description": "Click the account button (username) in the Qoder sidebar and return the visible account dropdown items.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": false, - "help": "Username text shown in the sidebar (default: tries common short labels)" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "add-workspace", - "description": "Click \"Add Workspace\" — opens the folder picker. Note: this opens a system file-picker dialog that Qoder controls; the actual folder selection must be done in the UI by the user.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true + "modulePath": "qoder/composer.js", + "sourceFile": "qoder/composer.js", + "navigateBefore": true }, { "site": "qoder", - "name": "ask", - "description": "Send a prompt to Qoder and wait up to --timeout seconds for the reply (best-effort: polls for the chat turn count to grow + stabilize).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt text" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait" - } - ], - "columns": [ - "Role", - "Text", - "WaitedSeconds" - ], - "type": "js", - "modulePath": "qoder/quest.js", - "sourceFile": "qoder/quest.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "credits", - "description": "Click \"Credits Usage\" and return the credits-usage display text.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "history", - "description": "List Quests visible in the Qoder sidebar. Returns title + visible metadata.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Title" - ], - "type": "js", - "modulePath": "qoder/history.js", - "sourceFile": "qoder/history.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "knowledge", - "description": "Open the Knowledge view (Qoder's personal/team knowledge base).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "marketplace", - "description": "Open the Qoder Marketplace.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "more-actions", - "description": "Click the \"More Actions\" button and list its menu items.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Item" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "new", - "description": "Start a new Qoder Quest (conversation). Clicks the \"New Quest\" button in the sidebar (or its ⌘N variant).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/quest.js", - "sourceFile": "qoder/quest.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "open-editor", - "description": "Click \"Open Editor\" — opens the current draft in a full editor pane.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/composer.js", - "sourceFile": "qoder/composer.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "open-panel", - "description": "Open / close the Qoder bottom panel (Output / Terminal / Debug Console). ⌥⌘B equivalent.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "prompt-enhance", - "description": "Click \"Prompt Enhance\" — Qoder rewrites the current composer draft for better LLM consumption.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/composer.js", - "sourceFile": "qoder/composer.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "read", - "description": "Read messages in the current Qoder Quest. Returns role + text for each visible turn.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Role", - "Text" - ], - "type": "js", - "modulePath": "qoder/read.js", - "sourceFile": "qoder/read.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "search", - "description": "Open Qoder Search palette (⌘P), type a query, return matched options.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search text" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Item" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "send", - "description": "Type text into the Qoder composer and click \"Send message\" (fire-and-forget).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Text to send" - } - ], - "columns": [ - "Status", - "Length" - ], - "type": "js", - "modulePath": "qoder/quest.js", - "sourceFile": "qoder/quest.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "settings", - "description": "Click the Settings button in the Qoder sidebar.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "sidebar-toggle", - "description": "Collapse / Expand the Qoder Quest List sidebar (⌘B).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "status", - "description": "Check Qoder CDP connection and report the current renderer URL + title.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" - ], - "type": "js", - "modulePath": "qoder/status.js", - "sourceFile": "qoder/status.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "view-all", - "description": "Click \"View all\" to show all Quests.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "reddit", - "name": "comment", - "description": "Post a comment on a Reddit post", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" - }, - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "Comment text" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "reddit/comment.js", - "sourceFile": "reddit/comment.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "frontpage", - "description": "Reddit Frontpage / r/all", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "author", - "upvotes", - "comments", - "url", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/frontpage.js", - "sourceFile": "reddit/frontpage.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "home", - "description": "Reddit personalized home feed (Best, requires login)", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Number of posts (1–100)" - } - ], - "columns": [ - "rank", - "title", - "subreddit", - "score", - "comments", - "postId", - "author", - "url", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/home.js", - "sourceFile": "reddit/home.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "hot", - "description": "Reddit hot posts", - "access": "read", - "domain": "www.reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "subreddit", - "type": "str", - "default": "", - "required": false, - "help": "Subreddit name (e.g. programming). Empty for frontpage" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts" - } - ], - "columns": [ - "rank", - "title", - "subreddit", - "score", - "comments", - "postId", - "author", - "url", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/hot.js", - "sourceFile": "reddit/hot.js", - "navigateBefore": "https://www.reddit.com" - }, - { - "site": "reddit", - "name": "login", - "description": "Open reddit login", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "username", - "id", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "reddit/auth.js", - "sourceFile": "reddit/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "reddit", - "name": "popular", - "description": "Reddit Popular posts (/r/popular)", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "" - } - ], - "columns": [ - "rank", - "id", - "title", - "subreddit", - "score", - "comments", - "author", - "url", - "created_utc", - "selftext", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/popular.js", - "sourceFile": "reddit/popular.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "read", - "description": "Read a Reddit post and its comments", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "str", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or full URL" - }, - { - "name": "sort", - "type": "str", - "default": "best", - "required": false, - "help": "Comment sort: best, top, new, controversial, old, qa" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Number of top-level comments" - }, - { - "name": "depth", - "type": "int", - "default": 2, - "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" - }, - { - "name": "replies", - "type": "int", - "default": 5, - "required": false, - "help": "Max replies shown per comment at each level (sorted by score)" - }, - { - "name": "max-length", - "type": "int", - "default": 2000, - "required": false, - "help": "Max characters per comment body (min 100)" - }, - { - "name": "expand-more", - "type": "bool", - "default": false, - "required": false, - "help": "Follow Reddit \"more comments\" stubs by calling /api/morechildren.json" - }, - { - "name": "expand-rounds", - "type": "int", - "default": 2, - "required": false, - "help": "Max expansion passes when --expand-more is on (1–5; each round can fan out new \"more\" stubs)" - } - ], - "columns": [ - "type", - "author", - "score", - "text", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/read.js", - "sourceFile": "reddit/read.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "reply", - "description": "Reply to a Reddit comment", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "comment-id", - "type": "string", - "required": true, - "positional": true, - "help": "Comment ID (e.g. okf3s7u) or fullname (t1_xxx)" - }, - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "Reply text" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "reddit/reply.js", - "sourceFile": "reddit/reply.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "save", - "description": "Save or unsave a Reddit post", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" - }, - { - "name": "undo", - "type": "boolean", - "default": false, - "required": false, - "help": "Unsave instead of save" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "reddit/save.js", - "sourceFile": "reddit/save.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "saved", - "description": "Browse your saved Reddit posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "score", - "comments", - "url" - ], - "type": "js", - "modulePath": "reddit/saved.js", - "sourceFile": "reddit/saved.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "search", - "description": "Search Reddit Posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit search query" - }, - { - "name": "subreddit", - "type": "string", - "default": "", - "required": false, - "help": "Search within a specific subreddit" - }, - { - "name": "sort", - "type": "string", - "default": "relevance", - "required": false, - "help": "Sort order: relevance, hot, top, new, comments" - }, - { - "name": "time", - "type": "string", - "default": "all", - "required": false, - "help": "Time filter: hour, day, week, month, year, all" - }, + "name": "read", + "description": "Read messages in the current Qoder Quest. Returns role + text for each visible turn.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ { "name": "limit", "type": "int", - "default": 15, + "default": 30, "required": false, "help": "" } ], "columns": [ - "id", - "title", - "subreddit", - "author", - "score", - "comments", - "url", - "created_utc", - "selftext", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "tags": [ - "search" + "Index", + "Role", + "Text" ], "type": "js", - "modulePath": "reddit/search.js", - "sourceFile": "reddit/search.js", - "navigateBefore": "https://reddit.com" + "modulePath": "qoder/read.js", + "sourceFile": "qoder/read.js", + "navigateBefore": true }, { - "site": "reddit", - "name": "subreddit", - "description": "Get posts from a specific Subreddit", + "site": "qoder", + "name": "search", + "description": "Open Qoder Search palette (⌘P), type a query, return matched options.", "access": "read", - "domain": "reddit.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "name", - "type": "string", + "name": "query", + "type": "str", "required": true, "positional": true, - "help": "Subreddit name (no `r/` prefix; e.g. `python`)" - }, - { - "name": "sort", - "type": "string", - "default": "hot", - "required": false, - "help": "Sorting method: hot, new, top, rising, controversial" - }, - { - "name": "time", - "type": "string", - "default": "all", - "required": false, - "help": "Time filter for top/controversial: hour, day, week, month, year, all" + "help": "Search text" }, { "name": "limit", "type": "int", - "default": 15, + "default": 20, "required": false, "help": "" } ], "columns": [ - "id", - "title", - "subreddit", - "author", - "upvotes", - "comments", - "url", - "created_utc", - "selftext", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" + "Index", + "Item" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "reddit/subreddit.js", - "sourceFile": "reddit/subreddit.js", - "navigateBefore": "https://reddit.com" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "reddit", - "name": "subreddit-info", - "description": "Show metadata for a Reddit subreddit (subscribers, description, created date, NSFW)", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", + "site": "qoder", + "name": "send", + "description": "Type text into the Qoder composer and click \"Send message\" (fire-and-forget).", + "access": "write", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "name", - "type": "string", + "name": "text", + "type": "str", "required": true, "positional": true, - "help": "Subreddit name (no `r/` prefix needed)" + "help": "Text to send" } ], "columns": [ - "field", - "value" + "Status", + "Length" ], "type": "js", - "modulePath": "reddit/subreddit-info.js", - "sourceFile": "reddit/subreddit-info.js", - "navigateBefore": "https://reddit.com" + "modulePath": "qoder/quest.js", + "sourceFile": "qoder/quest.js", + "navigateBefore": true }, { - "site": "reddit", - "name": "subscribe", - "description": "Subscribe or unsubscribe to a subreddit", + "site": "qoder", + "name": "settings", + "description": "Click the Settings button in the Qoder sidebar.", "access": "write", - "domain": "reddit.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "subreddit", - "type": "string", - "required": true, - "positional": true, - "help": "Subreddit name (e.g. python)" - }, - { - "name": "undo", - "type": "boolean", - "default": false, - "required": false, - "help": "Unsubscribe instead of subscribe" - } - ], + "args": [], "columns": [ - "status", - "message" + "Status" ], "type": "js", - "modulePath": "reddit/subscribe.js", - "sourceFile": "reddit/subscribe.js", - "navigateBefore": "https://reddit.com" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "reddit", - "name": "subscribed", - "description": "List subreddits you are subscribed to", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", + "site": "qoder", + "name": "sidebar-toggle", + "description": "Collapse / Expand the Qoder Quest List sidebar (⌘B).", + "access": "write", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Max subreddits to return (1-1000, auto-paginates)" - } - ], + "args": [], "columns": [ - "id", - "subreddit", - "title", - "subscribers", - "description", - "url" + "Status" ], "type": "js", - "modulePath": "reddit/subscribed.js", - "sourceFile": "reddit/subscribed.js", - "navigateBefore": "https://reddit.com" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "reddit", - "name": "upvote", - "description": "Upvote or downvote a Reddit post", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", + "site": "qoder", + "name": "status", + "description": "Check Qoder CDP connection and report the current renderer URL + title.", + "access": "read", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "post-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" - }, - { - "name": "direction", - "type": "string", - "default": "up", - "required": false, - "help": "Vote direction: up, down, none" - } - ], + "args": [], "columns": [ - "status", - "message" + "Status", + "Url", + "Title" ], "type": "js", - "modulePath": "reddit/upvote.js", - "sourceFile": "reddit/upvote.js", - "navigateBefore": "https://reddit.com" + "modulePath": "qoder/status.js", + "sourceFile": "qoder/status.js", + "navigateBefore": true }, { - "site": "reddit", - "name": "upvoted", - "description": "Browse your upvoted Reddit posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", + "site": "qoder", + "name": "view-all", + "description": "Click \"View all\" to show all Quests.", + "access": "write", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "score", - "comments", - "url" + "args": [], + "columns": [ + "Status" ], "type": "js", - "modulePath": "reddit/upvoted.js", - "sourceFile": "reddit/upvoted.js", - "navigateBefore": "https://reddit.com" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { "site": "reddit", - "name": "user", - "description": "View a Reddit user profile", - "access": "read", + "name": "comment", + "description": "Post a comment on a Reddit post", + "access": "write", "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "post-id", "type": "string", "required": true, "positional": true, - "help": "Reddit username (no `u/` prefix needed)" + "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" + }, + { + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "Comment text" } ], "columns": [ - "field", - "value" + "status", + "message" ], "type": "js", - "modulePath": "reddit/user.js", - "sourceFile": "reddit/user.js", + "modulePath": "reddit/comment.js", + "sourceFile": "reddit/comment.js", "navigateBefore": "https://reddit.com" }, { "site": "reddit", - "name": "user-comments", - "description": "View a Reddit user's comment history", + "name": "frontpage", + "description": "Reddit Frontpage / r/all", "access": "read", "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit username (no `u/` prefix needed)" - }, { "name": "limit", "type": "int", @@ -13208,997 +11038,981 @@ } ], "columns": [ + "title", "subreddit", - "score", - "body", - "url" + "author", + "upvotes", + "comments", + "url", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "reddit/user-comments.js", - "sourceFile": "reddit/user-comments.js", + "modulePath": "reddit/frontpage.js", + "sourceFile": "reddit/frontpage.js", "navigateBefore": "https://reddit.com" }, { "site": "reddit", - "name": "user-posts", - "description": "View a Reddit user's submitted posts", + "name": "home", + "description": "Reddit personalized home feed (Best, requires login)", "access": "read", "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit username (no `u/` prefix needed)" - }, { "name": "limit", "type": "int", - "default": 15, + "default": 25, "required": false, - "help": "" + "help": "Number of posts (1–100)" } ], "columns": [ + "rank", "title", "subreddit", "score", "comments", - "url" + "postId", + "author", + "url", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "reddit/user-posts.js", - "sourceFile": "reddit/user-posts.js", + "modulePath": "reddit/home.js", + "sourceFile": "reddit/home.js", "navigateBefore": "https://reddit.com" }, { "site": "reddit", - "name": "whoami", - "description": "Show the currently logged-in Reddit user", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "reddit/whoami.js", - "sourceFile": "reddit/whoami.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reuters", - "name": "article-detail", - "description": "Reuters Reuters article detail:title/author/body text", - "access": "read", - "domain": "www.reuters.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "Reuters article URL (must be on reuters.com)" - } - ], - "columns": [ - "title", - "date", - "section", - "section_path", - "authors", - "description", - "word_count", - "url", - "body" - ], - "type": "js", - "modulePath": "reuters/article-detail.js", - "sourceFile": "reuters/article-detail.js", - "navigateBefore": "https://www.reuters.com" - }, - { - "site": "reuters", - "name": "login", - "description": "Open reuters login", - "access": "write", - "domain": "reuters.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "subscribed", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "reuters/auth.js", - "sourceFile": "reuters/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "reuters", - "name": "search", - "description": "Reuters Reuters news search", + "name": "hot", + "description": "Reddit hot posts", "access": "read", - "domain": "www.reuters.com", + "domain": "www.reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", + "name": "subreddit", "type": "str", - "required": true, - "positional": true, - "help": "Search query" + "default": "", + "required": false, + "help": "Subreddit name (e.g. programming). Empty for frontpage" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results (1-40)" + "help": "Number of posts" } ], "columns": [ "rank", "title", - "date", - "section", - "section_path", - "authors", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "reuters/search.js", - "sourceFile": "reuters/search.js", - "navigateBefore": "https://www.reuters.com" - }, - { - "site": "reuters", - "name": "whoami", - "description": "Show the current logged-in reuters account", - "access": "read", - "domain": "reuters.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "subscribed" + "subreddit", + "score", + "comments", + "postId", + "author", + "url", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "reuters/auth.js", - "sourceFile": "reuters/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "reddit/hot.js", + "sourceFile": "reddit/hot.js", + "navigateBefore": "https://www.reddit.com" }, { - "site": "slock", - "name": "attachment-download", - "description": "Download an attachment to a local file. Resolves a signed CDN URL in the page, then fetches bytes node-side (no CORS).", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "attachmentId", - "type": "str", - "required": true, - "positional": true, - "help": "Attachment UUID" - }, - { - "name": "out", - "type": "str", - "required": false, - "help": "Local path to write to. Defaults to ./.bin" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server slug" - } - ], + "site": "reddit", + "name": "login", + "description": "Open reddit login", + "access": "write", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "attachmentId", - "out", - "sizeBytes" + "status", + "logged_in", + "site", + "username", + "id", + "action", + "verify_command" ], "type": "js", - "modulePath": "slock/attachment-download.js", - "sourceFile": "slock/attachment-download.js", - "navigateBefore": "https://app.slock.ai", + "modulePath": "reddit/auth.js", + "sourceFile": "reddit/auth.js", + "navigateBefore": false, "siteSession": "persistent" }, { - "site": "slock", - "name": "attachment-upload", - "description": "Upload a local file to Slock attachments. Prints the attachmentId for use with `message-send --attach`.", - "access": "write", - "domain": "app.slock.ai", + "site": "reddit", + "name": "popular", + "description": "Reddit Popular posts (/r/popular)", + "access": "read", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "file", - "type": "str", - "required": true, - "positional": true, - "help": "Local file path to upload (single file; max 50 MB)" - }, - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name — server requires the attachment be scoped to a channel" - }, - { - "name": "server", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Override active server slug" + "help": "" } ], "columns": [ - "attachmentId", - "filename", - "mimeType", - "sizeBytes" + "rank", + "id", + "title", + "subreddit", + "score", + "comments", + "author", + "url", + "created_utc", + "selftext", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "slock/attachment-upload.js", - "sourceFile": "slock/attachment-upload.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reddit/popular.js", + "sourceFile": "reddit/popular.js", + "navigateBefore": "https://reddit.com" }, { - "site": "slock", - "name": "attachment-url", - "description": "Get a short-lived signed CDN URL for an attachment (does not download bytes).", + "site": "reddit", + "name": "read", + "description": "Read a Reddit post and its comments", "access": "read", - "domain": "app.slock.ai", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "attachmentId", + "name": "post-id", "type": "str", "required": true, "positional": true, - "help": "Attachment UUID" + "help": "Post ID (e.g. 1abc123) or full URL" }, { - "name": "server", + "name": "sort", "type": "str", + "default": "best", "required": false, - "help": "Override active server slug" + "help": "Comment sort: best, top, new, controversial, old, qa" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Number of top-level comments" + }, + { + "name": "depth", + "type": "int", + "default": 2, + "required": false, + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + }, + { + "name": "replies", + "type": "int", + "default": 5, + "required": false, + "help": "Max replies shown per comment at each level (sorted by score)" + }, + { + "name": "max-length", + "type": "int", + "default": 2000, + "required": false, + "help": "Max characters per comment body (min 100)" + }, + { + "name": "expand-more", + "type": "bool", + "default": false, + "required": false, + "help": "Follow Reddit \"more comments\" stubs by calling /api/morechildren.json" + }, + { + "name": "expand-rounds", + "type": "int", + "default": 2, + "required": false, + "help": "Max expansion passes when --expand-more is on (1–5; each round can fan out new \"more\" stubs)" } ], "columns": [ - "attachmentId", - "url", - "expiresAt" + "type", + "author", + "score", + "text", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "slock/attachment-url.js", - "sourceFile": "slock/attachment-url.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reddit/read.js", + "sourceFile": "reddit/read.js", + "navigateBefore": "https://reddit.com" }, { - "site": "slock", - "name": "bookmark-add", - "description": "Bookmark a message (POST /channels/saved). Requires full messageId UUID.", + "site": "reddit", + "name": "reply", + "description": "Reply to a Reddit comment", "access": "write", - "domain": "app.slock.ai", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "messageId", - "type": "str", + "name": "comment-id", + "type": "string", "required": true, "positional": true, - "help": "Full messageId UUID (short ids rejected)" + "help": "Comment ID (e.g. okf3s7u) or fullname (t1_xxx)" }, { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "Reply text" } ], "columns": [ - "messageId", - "saved" + "status", + "message" ], "type": "js", - "modulePath": "slock/bookmark-add.js", - "sourceFile": "slock/bookmark-add.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reddit/reply.js", + "sourceFile": "reddit/reply.js", + "navigateBefore": "https://reddit.com" }, { - "site": "slock", - "name": "bookmark-list", - "description": "List bookmarks (saved messages) in the active server", - "access": "read", - "domain": "app.slock.ai", + "site": "reddit", + "name": "save", + "description": "Save or unsave a Reddit post", + "access": "write", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max results" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Offset" + "name": "post-id", + "type": "string", + "required": true, + "positional": true, + "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" }, { - "name": "server", - "type": "str", + "name": "undo", + "type": "boolean", + "default": false, "required": false, - "help": "Override active server" + "help": "Unsave instead of save" } ], "columns": [ - "id", - "messageId", - "content", - "savedAt" + "status", + "message" ], "type": "js", - "modulePath": "slock/bookmark-list.js", - "sourceFile": "slock/bookmark-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reddit/save.js", + "sourceFile": "reddit/save.js", + "navigateBefore": "https://reddit.com" }, { - "site": "slock", - "name": "bookmark-remove", - "description": "Remove a bookmark (DELETE /channels/saved/:messageId). 404 is treated as already-removed.", - "access": "write", - "domain": "app.slock.ai", + "site": "reddit", + "name": "saved", + "description": "Browse your saved Reddit posts", + "access": "read", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full messageId UUID" - }, - { - "name": "server", - "type": "str", + "name": "limit", + "type": "int", + "default": 15, "required": false, - "help": "Override active server" + "help": "" } ], "columns": [ - "messageId", - "removed", - "note" + "title", + "subreddit", + "score", + "comments", + "url" ], "type": "js", - "modulePath": "slock/bookmark-remove.js", - "sourceFile": "slock/bookmark-remove.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reddit/saved.js", + "sourceFile": "reddit/saved.js", + "navigateBefore": "https://reddit.com" }, { - "site": "slock", - "name": "channel-archive", - "description": "Archive a channel — admin only (POST /channels/:id/archive)", - "access": "write", - "domain": "app.slock.ai", + "site": "reddit", + "name": "search", + "description": "Search Reddit Posts", + "access": "read", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "channel", - "type": "str", + "name": "query", + "type": "string", "required": true, "positional": true, - "help": "channelId UUID or #name" + "help": "Reddit search query" }, { - "name": "server", - "type": "str", + "name": "subreddit", + "type": "string", + "default": "", "required": false, - "help": "Override active server" + "help": "Search within a specific subreddit" + }, + { + "name": "sort", + "type": "string", + "default": "relevance", + "required": false, + "help": "Sort order: relevance, hot, top, new, comments" + }, + { + "name": "time", + "type": "string", + "default": "all", + "required": false, + "help": "Time filter: hour, day, week, month, year, all" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "" } ], "columns": [ - "channel", "id", - "archivedAt", - "result" + "title", + "subreddit", + "author", + "score", + "comments", + "url", + "created_utc", + "selftext", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "slock/channel-archive.js", - "sourceFile": "slock/channel-archive.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reddit/search.js", + "sourceFile": "reddit/search.js", + "navigateBefore": "https://reddit.com" }, { - "site": "slock", - "name": "channel-create", - "description": "Create a channel — admin only (POST /channels/). Public unless --private.", - "access": "write", - "domain": "app.slock.ai", + "site": "reddit", + "name": "subreddit", + "description": "Get posts from a specific Subreddit", + "access": "read", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { "name": "name", - "type": "str", + "type": "string", "required": true, "positional": true, - "help": "Channel name" + "help": "Subreddit name (no `r/` prefix; e.g. `python`)" }, { - "name": "description", - "type": "str", + "name": "sort", + "type": "string", + "default": "hot", "required": false, - "help": "Channel description / topic (≤500 chars)" + "help": "Sorting method: hot, new, top, rising, controversial" }, { - "name": "private", - "type": "bool", - "default": false, + "name": "time", + "type": "string", + "default": "all", "required": false, - "help": "Create a private channel instead of public" + "help": "Time filter for top/controversial: hour, day, week, month, year, all" }, { - "name": "server", - "type": "str", + "name": "limit", + "type": "int", + "default": 15, "required": false, - "help": "Override active server" + "help": "" } ], "columns": [ "id", - "name", - "type", - "result" + "title", + "subreddit", + "author", + "upvotes", + "comments", + "url", + "created_utc", + "selftext", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "slock/channel-create.js", - "sourceFile": "slock/channel-create.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reddit/subreddit.js", + "sourceFile": "reddit/subreddit.js", + "navigateBefore": "https://reddit.com" }, { - "site": "slock", - "name": "channel-files", - "description": "List files shared in a channel (GET /channels/:id/files)", + "site": "reddit", + "name": "subreddit-info", + "description": "Show metadata for a Reddit subreddit (subscribers, description, created date, NSFW)", "access": "read", - "domain": "app.slock.ai", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "channel", - "type": "str", + "name": "name", + "type": "string", "required": true, "positional": true, - "help": "channelId UUID or #name" - }, + "help": "Subreddit name (no `r/` prefix needed)" + } + ], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "reddit/subreddit-info.js", + "sourceFile": "reddit/subreddit-info.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "subscribe", + "description": "Subscribe or unsubscribe to a subreddit", + "access": "write", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max files" + "name": "subreddit", + "type": "string", + "required": true, + "positional": true, + "help": "Subreddit name (e.g. python)" }, { - "name": "server", - "type": "str", + "name": "undo", + "type": "boolean", + "default": false, "required": false, - "help": "Override active server" + "help": "Unsubscribe instead of subscribe" } ], "columns": [ - "id", - "filename", - "mimeType", - "sizeBytes", - "messageId", - "createdAt" + "status", + "message" ], "type": "js", - "modulePath": "slock/channel-files.js", - "sourceFile": "slock/channel-files.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reddit/subscribe.js", + "sourceFile": "reddit/subscribe.js", + "navigateBefore": "https://reddit.com" }, { - "site": "slock", - "name": "channel-info", - "description": "Show one channel's details (GET /channels/:id)", + "site": "reddit", + "name": "subscribed", + "description": "List subreddits you are subscribed to", "access": "read", - "domain": "app.slock.ai", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", + "name": "limit", + "type": "int", + "default": 100, "required": false, - "help": "Override active server" + "help": "Max subreddits to return (1-1000, auto-paginates)" } ], "columns": [ "id", - "name", - "type", - "topic", - "joined", - "archivedAt" + "subreddit", + "title", + "subscribers", + "description", + "url" ], "type": "js", - "modulePath": "slock/channel-info.js", - "sourceFile": "slock/channel-info.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reddit/subscribed.js", + "sourceFile": "reddit/subscribed.js", + "navigateBefore": "https://reddit.com" }, { - "site": "slock", - "name": "channel-join", - "description": "Join a public channel (POST /channels/:id/join)", + "site": "reddit", + "name": "upvote", + "description": "Upvote or downvote a Reddit post", "access": "write", - "domain": "app.slock.ai", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "channel", - "type": "str", + "name": "post-id", + "type": "string", "required": true, "positional": true, - "help": "channelId UUID or #name" + "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" }, { - "name": "server", - "type": "str", + "name": "direction", + "type": "string", + "default": "up", "required": false, - "help": "Override active server" + "help": "Vote direction: up, down, none" } ], "columns": [ - "channel", - "id", - "archivedAt", - "result" + "status", + "message" ], "type": "js", - "modulePath": "slock/channel-join.js", - "sourceFile": "slock/channel-join.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reddit/upvote.js", + "sourceFile": "reddit/upvote.js", + "navigateBefore": "https://reddit.com" }, { - "site": "slock", - "name": "channel-leave", - "description": "Leave a channel (POST /channels/:id/leave)", - "access": "write", - "domain": "app.slock.ai", + "site": "reddit", + "name": "upvoted", + "description": "Browse your upvoted Reddit posts", + "access": "read", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", + "name": "limit", + "type": "int", + "default": 15, "required": false, - "help": "Override active server" + "help": "" } ], "columns": [ - "channel", - "id", - "archivedAt", - "result" + "title", + "subreddit", + "score", + "comments", + "url" ], "type": "js", - "modulePath": "slock/channel-leave.js", - "sourceFile": "slock/channel-leave.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reddit/upvoted.js", + "sourceFile": "reddit/upvoted.js", + "navigateBefore": "https://reddit.com" }, { - "site": "slock", - "name": "channel-list", - "description": "List channels in the active slock server", + "site": "reddit", + "name": "user", + "description": "View a Reddit user profile", "access": "read", - "domain": "app.slock.ai", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id) for this call" + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Reddit username (no `u/` prefix needed)" } ], "columns": [ - "id", - "name", - "topic" + "field", + "value" ], "type": "js", - "modulePath": "slock/channel-list.js", - "sourceFile": "slock/channel-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reddit/user.js", + "sourceFile": "reddit/user.js", + "navigateBefore": "https://reddit.com" }, { - "site": "slock", - "name": "channel-mark", - "description": "Mark a channel read (default), read up to --seq, or --unread.", - "access": "write", - "domain": "app.slock.ai", + "site": "reddit", + "name": "user-comments", + "description": "View a Reddit user's comment history", + "access": "read", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "channel", - "type": "str", + "name": "username", + "type": "string", "required": true, "positional": true, - "help": "channelId UUID or #name" + "help": "Reddit username (no `u/` prefix needed)" }, { - "name": "seq", + "name": "limit", "type": "int", + "default": 15, "required": false, - "help": "Mark read up to this seq (omit for read-all)" - }, + "help": "" + } + ], + "columns": [ + "subreddit", + "score", + "body", + "url" + ], + "type": "js", + "modulePath": "reddit/user-comments.js", + "sourceFile": "reddit/user-comments.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "user-posts", + "description": "View a Reddit user's submitted posts", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "unread", - "type": "bool", - "default": false, - "required": false, - "help": "Mark the channel unread instead of read" + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Reddit username (no `u/` prefix needed)" }, { - "name": "server", - "type": "str", + "name": "limit", + "type": "int", + "default": 15, "required": false, - "help": "Override active server" + "help": "" } ], "columns": [ - "channel", - "action", - "result" + "title", + "subreddit", + "score", + "comments", + "url" ], "type": "js", - "modulePath": "slock/channel-mark.js", - "sourceFile": "slock/channel-mark.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reddit/user-posts.js", + "sourceFile": "reddit/user-posts.js", + "navigateBefore": "https://reddit.com" }, { - "site": "slock", - "name": "channel-members", - "description": "List members of a channel", + "site": "reddit", + "name": "whoami", + "description": "Show the currently logged-in Reddit user", "access": "read", - "domain": "app.slock.ai", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "reddit/whoami.js", + "sourceFile": "reddit/whoami.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reuters", + "name": "article-detail", + "description": "Reuters Reuters article detail:title/author/body text", + "access": "read", + "domain": "www.reuters.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "channel", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id)" + "help": "Reuters article URL (must be on reuters.com)" } ], "columns": [ - "userId", - "name", - "kind", - "role" + "title", + "date", + "section", + "section_path", + "authors", + "description", + "word_count", + "url", + "body" ], "type": "js", - "modulePath": "slock/channel-members.js", - "sourceFile": "slock/channel-members.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reuters/article-detail.js", + "sourceFile": "reuters/article-detail.js", + "navigateBefore": "https://www.reuters.com" }, { - "site": "slock", - "name": "channel-unarchive", - "description": "Unarchive a channel — admin only (POST /channels/:id/unarchive). #name lookups exclude archived channels; pass the channelId UUID for archived ones.", + "site": "reuters", + "name": "login", + "description": "Open reuters login", "access": "write", - "domain": "app.slock.ai", + "domain": "reuters.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "subscribed", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "reuters/auth.js", + "sourceFile": "reuters/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "reuters", + "name": "search", + "description": "Reuters Reuters news search", + "access": "read", + "domain": "www.reuters.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "channel", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "channelId UUID or #name" + "help": "Search query" }, { - "name": "server", - "type": "str", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Override active server" + "help": "Number of results (1-40)" } ], "columns": [ - "channel", - "id", - "archivedAt", - "result" + "rank", + "title", + "date", + "section", + "section_path", + "authors", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "slock/channel-unarchive.js", - "sourceFile": "slock/channel-unarchive.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" + "modulePath": "reuters/search.js", + "sourceFile": "reuters/search.js", + "navigateBefore": "https://www.reuters.com" }, { - "site": "slock", - "name": "dm-list", - "description": "List DM channels in the active server (GET /channels/dm)", + "site": "reuters", + "name": "whoami", + "description": "Show the current logged-in reuters account", "access": "read", - "domain": "app.slock.ai", + "domain": "reuters.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id)" - } - ], + "args": [], "columns": [ - "channelId", - "peerName", - "peerId", - "createdAt" + "logged_in", + "site", + "user_id", + "subscribed" ], "type": "js", - "modulePath": "slock/dm-list.js", - "sourceFile": "slock/dm-list.js", - "navigateBefore": "https://app.slock.ai", + "modulePath": "reuters/auth.js", + "sourceFile": "reuters/auth.js", + "navigateBefore": false, "siteSession": "persistent" }, { "site": "slock", - "name": "inbox", - "description": "List unified inbox items (channels, DMs, followed threads) that need attention.", + "name": "attachment-download", + "description": "Download an attachment to a local file. Resolves a signed CDN URL in the page, then fetches bytes node-side (no CORS).", "access": "read", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "filter", + "name": "attachmentId", "type": "str", - "default": "all", - "required": false, - "help": "all | unread | mentions" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max items (server caps at 100)" + "required": true, + "positional": true, + "help": "Attachment UUID" }, { - "name": "offset", - "type": "int", - "default": 0, + "name": "out", + "type": "str", "required": false, - "help": "Pagination offset" + "help": "Local path to write to. Defaults to ./.bin" }, { "name": "server", "type": "str", "required": false, - "help": "Override active server" + "help": "Override active server slug" } ], "columns": [ - "kind", - "id", - "name", - "unreadCount", - "hasMention", - "lastActivityAt", - "preview" + "attachmentId", + "out", + "sizeBytes" ], "type": "js", - "modulePath": "slock/inbox.js", - "sourceFile": "slock/inbox.js", + "modulePath": "slock/attachment-download.js", + "sourceFile": "slock/attachment-download.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "inbox-done", - "description": "Mark one chat as done / clear it from the inbox (POST /channels/inbox/done)", + "name": "attachment-upload", + "description": "Upload a local file to Slock attachments. Prints the attachmentId for use with `message-send --attach`.", "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ + { + "name": "file", + "type": "str", + "required": true, + "positional": true, + "help": "Local file path to upload (single file; max 50 MB)" + }, { "name": "channel", "type": "str", "required": true, "positional": true, - "help": "channelId UUID or #name" + "help": "channelId UUID or #name — server requires the attachment be scoped to a channel" }, { "name": "server", "type": "str", "required": false, - "help": "Override active server" + "help": "Override active server slug" } ], "columns": [ - "channel", - "result" + "attachmentId", + "filename", + "mimeType", + "sizeBytes" ], "type": "js", - "modulePath": "slock/inbox-done.js", - "sourceFile": "slock/inbox-done.js", + "modulePath": "slock/attachment-upload.js", + "sourceFile": "slock/attachment-upload.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "inbox-read-all", - "description": "Mark the entire inbox as read (POST /channels/inbox/read-all)", - "access": "write", + "name": "attachment-url", + "description": "Get a short-lived signed CDN URL for an attachment (does not download bytes).", + "access": "read", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ + { + "name": "attachmentId", + "type": "str", + "required": true, + "positional": true, + "help": "Attachment UUID" + }, { "name": "server", "type": "str", "required": false, - "help": "Override active server" + "help": "Override active server slug" } ], "columns": [ - "result", - "markedCount" + "attachmentId", + "url", + "expiresAt" ], "type": "js", - "modulePath": "slock/inbox-read-all.js", - "sourceFile": "slock/inbox-read-all.js", + "modulePath": "slock/attachment-url.js", + "sourceFile": "slock/attachment-url.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "login", - "description": "Open slock login", + "name": "bookmark-add", + "description": "Bookmark a message (POST /channels/saved). Requires full messageId UUID.", "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "id", - "name", - "email", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "slock/whoami.js", - "sourceFile": "slock/whoami.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "message-read", - "description": "Read messages in a channel or thread. Thread form: \"#channel:msgIdOrShort\". Use --after seq|UUID for cursor.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, "args": [ { - "name": "channel", + "name": "messageId", "type": "str", "required": true, "positional": true, - "help": "channelId UUID, \"#name\", or \"#channel:msgIdOrShort\"" - }, - { - "name": "after", - "type": "str", - "required": false, - "help": "Cursor: seq number or messageId UUID (exclusive)" - }, - { - "name": "before", - "type": "str", - "required": false, - "help": "seq to page before" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max messages" - }, - { - "name": "no-threads", - "type": "bool", - "default": false, - "required": false, - "help": "Skip /threads enrichment" + "help": "Full messageId UUID (short ids rejected)" }, { "name": "server", @@ -14208,44 +12022,24 @@ } ], "columns": [ - "id", - "seq", - "createdAt", - "senderName", - "content", - "threadChannelId", - "replyCount", - "unreadCount", - "lastReplyAt" + "messageId", + "saved" ], "type": "js", - "modulePath": "slock/message-read.js", - "sourceFile": "slock/message-read.js", + "modulePath": "slock/bookmark-add.js", + "sourceFile": "slock/bookmark-add.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "message-search", - "description": "Search messages", + "name": "bookmark-list", + "description": "List bookmarks (saved messages) in the active server", "access": "read", "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Restrict to a channel (UUID or #name)" - }, + "strategy": "cookie", + "browser": true, + "args": [ { "name": "limit", "type": "int", @@ -14253,6 +12047,13 @@ "required": false, "help": "Max results" }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Offset" + }, { "name": "server", "type": "str", @@ -14262,105 +12063,65 @@ ], "columns": [ "id", - "channelId", - "createdAt", - "senderName", - "content" - ], - "tags": [ - "search" + "messageId", + "content", + "savedAt" ], "type": "js", - "modulePath": "slock/message-search.js", - "sourceFile": "slock/message-search.js", + "modulePath": "slock/bookmark-list.js", + "sourceFile": "slock/bookmark-list.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "message-send", - "description": "Send a message to a channel, DM, or thread (content sent verbatim)", + "name": "bookmark-remove", + "description": "Remove a bookmark (DELETE /channels/saved/:messageId). 404 is treated as already-removed.", "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "target", - "type": "str", - "required": true, - "positional": true, - "help": "\"#channel\", \"#channel:msgIdOrShort\", \"dm:@name\", \"dm:\", or channel UUID" - }, - { - "name": "content", + "name": "messageId", "type": "str", "required": true, "positional": true, - "help": "Message body (sent verbatim, no marker)" - }, - { - "name": "dry-run", - "type": "bool", - "default": false, - "required": false, - "help": "Print the planned payload without sending" - }, - { - "name": "as-task", - "type": "bool", - "default": false, - "required": false, - "help": "Create the message as a task (asTask)" - }, - { - "name": "attach", - "type": "str", - "required": false, - "help": "Comma-separated attachmentId UUIDs (upload separately first)" + "help": "Full messageId UUID" }, { "name": "server", "type": "str", "required": false, - "help": "Override active server (slug or id)" + "help": "Override active server" } ], "columns": [ - "target", - "channelId", - "content", - "result", - "messageId" + "messageId", + "removed", + "note" ], "type": "js", - "modulePath": "slock/message-send.js", - "sourceFile": "slock/message-send.js", + "modulePath": "slock/bookmark-remove.js", + "sourceFile": "slock/bookmark-remove.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "reaction-add", - "description": "Add an emoji reaction to a message (POST /messages/:id/reactions). Idempotent server-side.", + "name": "channel-archive", + "description": "Archive a channel — admin only (POST /channels/:id/archive)", "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full messageId UUID (short ids rejected)" - }, - { - "name": "emoji", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "A single unicode emoji, e.g. 👍" + "help": "channelId UUID or #name" }, { "name": "server", @@ -14370,38 +12131,45 @@ } ], "columns": [ - "messageId", - "emoji", + "channel", + "id", + "archivedAt", "result" ], "type": "js", - "modulePath": "slock/reaction-add.js", - "sourceFile": "slock/reaction-add.js", + "modulePath": "slock/channel-archive.js", + "sourceFile": "slock/channel-archive.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "reaction-remove", - "description": "Remove your emoji reaction from a message (DELETE /messages/:id/reactions).", + "name": "channel-create", + "description": "Create a channel — admin only (POST /channels/). Public unless --private.", "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "messageId", + "name": "name", "type": "str", "required": true, "positional": true, - "help": "Full messageId UUID (short ids rejected)" + "help": "Channel name" }, { - "name": "emoji", + "name": "description", "type": "str", - "required": true, - "positional": true, - "help": "The unicode emoji to remove, e.g. 👍" + "required": false, + "help": "Channel description / topic (≤500 chars)" + }, + { + "name": "private", + "type": "bool", + "default": false, + "required": false, + "help": "Create a private channel instead of public" }, { "name": "server", @@ -14410,82 +12178,77 @@ "help": "Override active server" } ], - "columns": [ - "messageId", - "emoji", - "result" - ], - "type": "js", - "modulePath": "slock/reaction-remove.js", - "sourceFile": "slock/reaction-remove.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "server-list", - "description": "List slock servers you belong to; marks active per localStorage slug", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], "columns": [ "id", - "slug", "name", - "active" + "type", + "result" ], "type": "js", - "modulePath": "slock/server-list.js", - "sourceFile": "slock/server-list.js", + "modulePath": "slock/channel-create.js", + "sourceFile": "slock/channel-create.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "server-use", - "description": "Set the active slock server (writes localStorage.slock_last_server_slug)", - "access": "write", + "name": "channel-files", + "description": "List files shared in a channel (GET /channels/:id/files)", + "access": "read", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "input", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "server slug, \"#slug\", or UUID id" + "help": "channelId UUID or #name" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max files" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ "id", - "slug", - "name", - "written" + "filename", + "mimeType", + "sizeBytes", + "messageId", + "createdAt" ], "type": "js", - "modulePath": "slock/server-use.js", - "sourceFile": "slock/server-use.js", + "modulePath": "slock/channel-files.js", + "sourceFile": "slock/channel-files.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "task-claim", - "description": "Claim a chat task (PATCH /tasks/:id/claim). Requires full task UUID (= message id).", - "access": "write", + "name": "channel-info", + "description": "Show one channel's details (GET /channels/:id)", + "access": "read", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "taskId", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" + "help": "channelId UUID or #name" }, { "name": "server", @@ -14495,32 +12258,34 @@ } ], "columns": [ - "taskId", - "taskStatus", - "assigneeId", - "taskNumber" + "id", + "name", + "type", + "topic", + "joined", + "archivedAt" ], "type": "js", - "modulePath": "slock/task-claim.js", - "sourceFile": "slock/task-claim.js", + "modulePath": "slock/channel-info.js", + "sourceFile": "slock/channel-info.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "task-convert", - "description": "Convert a message into a chat task (POST /tasks/convert-message). Accepts a message UUID or \"#channel:shortId\".", + "name": "channel-join", + "description": "Join a public channel (POST /channels/:id/join)", "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "messageId", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Full message UUID, or \"#channel:shortId\" (short id expanded via /messages/context)" + "help": "channelId UUID or #name" }, { "name": "server", @@ -14530,22 +12295,21 @@ } ], "columns": [ + "channel", "id", - "taskNumber", - "title", - "taskStatus", - "channelId" + "archivedAt", + "result" ], "type": "js", - "modulePath": "slock/task-convert.js", - "sourceFile": "slock/task-convert.js", + "modulePath": "slock/channel-join.js", + "sourceFile": "slock/channel-join.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "task-create", - "description": "Create a task in a channel (single title; batch 1-50 is server-supported but client surface is single — see backlog R4).", + "name": "channel-leave", + "description": "Leave a channel (POST /channels/:id/leave)", "access": "write", "domain": "app.slock.ai", "strategy": "cookie", @@ -14559,60 +12323,79 @@ "help": "channelId UUID or #name" }, { - "name": "title", - "type": "str", - "required": true, - "positional": true, - "help": "Task title (single; batch TODO via R4)" - }, - { - "name": "desc", + "name": "server", "type": "str", "required": false, - "help": "Optional description body for the task" - }, + "help": "Override active server" + } + ], + "columns": [ + "channel", + "id", + "archivedAt", + "result" + ], + "type": "js", + "modulePath": "slock/channel-leave.js", + "sourceFile": "slock/channel-leave.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "channel-list", + "description": "List channels in the active slock server", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ { "name": "server", "type": "str", "required": false, - "help": "Override active server" + "help": "Override active server (slug or id) for this call" } ], "columns": [ "id", - "taskNumber", - "title", - "taskStatus", - "channelId" + "name", + "topic" ], "type": "js", - "modulePath": "slock/task-create.js", - "sourceFile": "slock/task-create.js", + "modulePath": "slock/channel-list.js", + "sourceFile": "slock/channel-list.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "task-delete", - "description": "Delete a chat task (DELETE /tasks/:taskId). Requires --confirm — destructive, irreversible.", + "name": "channel-mark", + "description": "Mark a channel read (default), read up to --seq, or --unread.", "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "taskId", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" + "help": "channelId UUID or #name" }, { - "name": "confirm", + "name": "seq", + "type": "int", + "required": false, + "help": "Mark read up to this seq (omit for read-all)" + }, + { + "name": "unread", "type": "bool", "default": false, "required": false, - "help": "Required acknowledgement: deletion is irreversible" + "help": "Mark the channel unread instead of read" }, { "name": "server", @@ -14622,19 +12405,20 @@ } ], "columns": [ - "taskId", - "deleted" + "channel", + "action", + "result" ], "type": "js", - "modulePath": "slock/task-delete.js", - "sourceFile": "slock/task-delete.js", + "modulePath": "slock/channel-mark.js", + "sourceFile": "slock/channel-mark.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "task-get", - "description": "Fetch a task by channel + taskNumber (GET /tasks/channel/:channelId/number/:taskNumber).", + "name": "channel-members", + "description": "List members of a channel", "access": "read", "domain": "app.slock.ai", "strategy": "cookie", @@ -14647,38 +12431,30 @@ "positional": true, "help": "channelId UUID or #name" }, - { - "name": "number", - "type": "str", - "required": true, - "positional": true, - "help": "taskNumber (per-channel integer, as shown in \"task #N\")" - }, { "name": "server", "type": "str", "required": false, - "help": "Override active server" + "help": "Override active server (slug or id)" } ], "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "assigneeId" + "userId", + "name", + "kind", + "role" ], "type": "js", - "modulePath": "slock/task-get.js", - "sourceFile": "slock/task-get.js", + "modulePath": "slock/channel-members.js", + "sourceFile": "slock/channel-members.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "task-list", - "description": "List tasks (chat tasks = messages with task fields) attached to a channel. Optional --status filter.", - "access": "read", + "name": "channel-unarchive", + "description": "Unarchive a channel — admin only (POST /channels/:id/unarchive). #name lookups exclude archived channels; pass the channelId UUID for archived ones.", + "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, @@ -14690,12 +12466,6 @@ "positional": true, "help": "channelId UUID or #name" }, - { - "name": "status", - "type": "str", - "required": false, - "help": "Filter by status: todo|in_progress|in_review|done|closed" - }, { "name": "server", "type": "str", @@ -14704,76 +12474,74 @@ } ], "columns": [ + "channel", "id", - "taskNumber", - "title", - "taskStatus", - "assigneeId" + "archivedAt", + "result" ], "type": "js", - "modulePath": "slock/task-list.js", - "sourceFile": "slock/task-list.js", + "modulePath": "slock/channel-unarchive.js", + "sourceFile": "slock/channel-unarchive.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "task-list-server", - "description": "List tasks across all channels in the active server (GET /tasks/server). Optional --status filter.", + "name": "dm-list", + "description": "List DM channels in the active server (GET /channels/dm)", "access": "read", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ - { - "name": "status", - "type": "str", - "required": false, - "help": "Filter by status: todo|in_progress|in_review|done|closed" - }, { "name": "server", "type": "str", "required": false, - "help": "Override active server" + "help": "Override active server (slug or id)" } ], "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", "channelId", - "assigneeId" + "peerName", + "peerId", + "createdAt" ], "type": "js", - "modulePath": "slock/task-list-server.js", - "sourceFile": "slock/task-list-server.js", + "modulePath": "slock/dm-list.js", + "sourceFile": "slock/dm-list.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "task-status", - "description": "Set a task's status (PATCH /tasks/:taskId/status, body {status}). One of todo|in_progress|in_review|done|closed.", - "access": "write", + "name": "inbox", + "description": "List unified inbox items (channels, DMs, followed threads) that need attention.", + "access": "read", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "taskId", + "name": "filter", "type": "str", - "required": true, - "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" + "default": "all", + "required": false, + "help": "all | unread | mentions" }, { - "name": "status", - "type": "str", - "required": true, - "positional": true, - "help": "One of: todo|in_progress|in_review|done|closed" + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max items (server caps at 100)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Pagination offset" }, { "name": "server", @@ -14783,32 +12551,35 @@ } ], "columns": [ - "taskId", - "taskStatus", - "assigneeId", - "taskNumber" + "kind", + "id", + "name", + "unreadCount", + "hasMention", + "lastActivityAt", + "preview" ], "type": "js", - "modulePath": "slock/task-status.js", - "sourceFile": "slock/task-status.js", + "modulePath": "slock/inbox.js", + "sourceFile": "slock/inbox.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "task-unclaim", - "description": "Release ownership of a chat task (PATCH /tasks/:id/unclaim).", + "name": "inbox-done", + "description": "Mark one chat as done / clear it from the inbox (POST /channels/inbox/done)", "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "taskId", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" + "help": "channelId UUID or #name" }, { "name": "server", @@ -14818,33 +12589,24 @@ } ], "columns": [ - "taskId", - "taskStatus", - "assigneeId", - "taskNumber" + "channel", + "result" ], "type": "js", - "modulePath": "slock/task-unclaim.js", - "sourceFile": "slock/task-unclaim.js", + "modulePath": "slock/inbox-done.js", + "sourceFile": "slock/inbox-done.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "thread-done", - "description": "Mark a thread as done / hide it from the active list (POST /channels/threads/done)", + "name": "inbox-read-all", + "description": "Mark the entire inbox as read (POST /channels/inbox/read-all)", "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ - { - "name": "threadChannelId", - "type": "str", - "required": true, - "positional": true, - "help": "Thread channel UUID (from thread-list / message-read)" - }, { "name": "server", "type": "str", @@ -14853,30 +12615,81 @@ } ], "columns": [ - "threadChannelId", - "result" + "result", + "markedCount" ], "type": "js", - "modulePath": "slock/thread-done.js", - "sourceFile": "slock/thread-done.js", + "modulePath": "slock/inbox-read-all.js", + "sourceFile": "slock/inbox-read-all.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "thread-follow", - "description": "Follow the thread on a parent message (POST /channels/threads/follow)", + "name": "login", + "description": "Open slock login", "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "id", + "name", + "email", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "slock/whoami.js", + "sourceFile": "slock/whoami.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "message-read", + "description": "Read messages in a channel or thread. Thread form: \"#channel:msgIdOrShort\". Use --after seq|UUID for cursor.", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "parentMessageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full parent messageId UUID (short ids rejected)" + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID, \"#name\", or \"#channel:msgIdOrShort\"" + }, + { + "name": "after", + "type": "str", + "required": false, + "help": "Cursor: seq number or messageId UUID (exclusive)" + }, + { + "name": "before", + "type": "str", + "required": false, + "help": "seq to page before" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max messages" + }, + { + "name": "no-threads", + "type": "bool", + "default": false, + "required": false, + "help": "Skip /threads enrichment" }, { "name": "server", @@ -14886,25 +12699,51 @@ } ], "columns": [ - "parentMessageId", + "id", + "seq", + "createdAt", + "senderName", + "content", "threadChannelId", - "result" + "replyCount", + "unreadCount", + "lastReplyAt" ], "type": "js", - "modulePath": "slock/thread-follow.js", - "sourceFile": "slock/thread-follow.js", + "modulePath": "slock/message-read.js", + "sourceFile": "slock/message-read.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "thread-list", - "description": "List followed threads in the active server (GET /channels/threads/followed)", + "name": "message-search", + "description": "Search messages", "access": "read", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Restrict to a channel (UUID or #name)" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max results" + }, { "name": "server", "type": "str", @@ -14913,67 +12752,106 @@ } ], "columns": [ - "threadChannelId", - "parentMessageId", - "parentChannelName", - "unreadCount", - "replyCount", - "lastReplyAt" + "id", + "channelId", + "createdAt", + "senderName", + "content" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "slock/thread-list.js", - "sourceFile": "slock/thread-list.js", + "modulePath": "slock/message-search.js", + "sourceFile": "slock/message-search.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "thread-undone", - "description": "Restore a done thread to the active list (POST /channels/threads/undone)", + "name": "message-send", + "description": "Send a message to a channel, DM, or thread (content sent verbatim)", "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "threadChannelId", + "name": "target", "type": "str", "required": true, "positional": true, - "help": "Thread channel UUID (from thread-list / message-read)" + "help": "\"#channel\", \"#channel:msgIdOrShort\", \"dm:@name\", \"dm:\", or channel UUID" + }, + { + "name": "content", + "type": "str", + "required": true, + "positional": true, + "help": "Message body (sent verbatim, no marker)" + }, + { + "name": "dry-run", + "type": "bool", + "default": false, + "required": false, + "help": "Print the planned payload without sending" + }, + { + "name": "as-task", + "type": "bool", + "default": false, + "required": false, + "help": "Create the message as a task (asTask)" + }, + { + "name": "attach", + "type": "str", + "required": false, + "help": "Comma-separated attachmentId UUIDs (upload separately first)" }, { "name": "server", "type": "str", "required": false, - "help": "Override active server" + "help": "Override active server (slug or id)" } ], "columns": [ - "threadChannelId", - "result" + "target", + "channelId", + "content", + "result", + "messageId" ], "type": "js", - "modulePath": "slock/thread-undone.js", - "sourceFile": "slock/thread-undone.js", + "modulePath": "slock/message-send.js", + "sourceFile": "slock/message-send.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "thread-unfollow", - "description": "Stop following a thread (POST /channels/threads/unfollow)", + "name": "reaction-add", + "description": "Add an emoji reaction to a message (POST /messages/:id/reactions). Idempotent server-side.", "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "threadChannelId", + "name": "messageId", "type": "str", "required": true, "positional": true, - "help": "Thread channel UUID (from thread-list / message-read)" + "help": "Full messageId UUID (short ids rejected)" + }, + { + "name": "emoji", + "type": "str", + "required": true, + "positional": true, + "help": "A single unicode emoji, e.g. 👍" }, { "name": "server", @@ -14983,2032 +12861,1885 @@ } ], "columns": [ - "threadChannelId", + "messageId", + "emoji", "result" ], "type": "js", - "modulePath": "slock/thread-unfollow.js", - "sourceFile": "slock/thread-unfollow.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "unread-summary", - "description": "Global unread counts across every server you belong to.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "serverId", - "slug", - "name", - "unreadCount" - ], - "type": "js", - "modulePath": "slock/unread-summary.js", - "sourceFile": "slock/unread-summary.js", + "modulePath": "slock/reaction-add.js", + "sourceFile": "slock/reaction-add.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "whoami", - "description": "Show the current logged-in slock account", - "access": "read", + "name": "reaction-remove", + "description": "Remove your emoji reaction from a message (DELETE /messages/:id/reactions).", + "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "id", - "name", - "email" - ], - "type": "js", - "modulePath": "slock/whoami.js", - "sourceFile": "slock/whoami.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "spotify", - "name": "auth", - "description": "Authenticate with Spotify (OAuth — run once)", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "next", - "description": "Skip to next track", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "pause", - "description": "Pause playback", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "play", - "description": "Resume playback or search and play a track/artist", - "access": "write", - "strategy": "local", - "browser": false, "args": [ { - "name": "query", + "name": "messageId", "type": "str", - "default": "", - "required": false, + "required": true, "positional": true, - "help": "Track or artist to play (optional)" + "help": "Full messageId UUID (short ids rejected)" + }, + { + "name": "emoji", + "type": "str", + "required": true, + "positional": true, + "help": "The unicode emoji to remove, e.g. 👍" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], - "columns": [ - "track", - "artist", - "status" - ], + "columns": [ + "messageId", + "emoji", + "result" + ], "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" + "modulePath": "slock/reaction-remove.js", + "sourceFile": "slock/reaction-remove.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "spotify", - "name": "prev", - "description": "Skip to previous track", - "access": "write", - "strategy": "local", - "browser": false, + "site": "slock", + "name": "server-list", + "description": "List slock servers you belong to; marks active per localStorage slug", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [], "columns": [ - "status" + "id", + "slug", + "name", + "active" ], "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" + "modulePath": "slock/server-list.js", + "sourceFile": "slock/server-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "spotify", - "name": "queue", - "description": "Add a track to the playback queue", + "site": "slock", + "name": "server-use", + "description": "Set the active slock server (writes localStorage.slock_last_server_slug)", "access": "write", - "strategy": "local", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "input", "type": "str", "required": true, "positional": true, - "help": "Track to add to queue" + "help": "server slug, \"#slug\", or UUID id" } ], "columns": [ - "track", - "artist", - "status" + "id", + "slug", + "name", + "written" ], "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" + "modulePath": "slock/server-use.js", + "sourceFile": "slock/server-use.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "spotify", - "name": "repeat", - "description": "Set repeat mode (off / track / context)", + "site": "slock", + "name": "task-claim", + "description": "Claim a chat task (PATCH /tasks/:id/claim). Requires full task UUID (= message id).", "access": "write", - "strategy": "local", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "mode", + "name": "taskId", "type": "str", - "default": "context", - "required": false, + "required": true, "positional": true, - "help": "off / track / context", - "choices": [ - "off", - "track", - "context" - ] + "help": "Full task UUID (= message id; short ids rejected)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "repeat" + "taskId", + "taskStatus", + "assigneeId", + "taskNumber" ], "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" + "modulePath": "slock/task-claim.js", + "sourceFile": "slock/task-claim.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "spotify", - "name": "search", - "description": "Search for tracks", - "access": "read", - "strategy": "local", - "browser": false, + "site": "slock", + "name": "task-convert", + "description": "Convert a message into a chat task (POST /tasks/convert-message). Accepts a message UUID or \"#channel:shortId\".", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "messageId", "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "Full message UUID, or \"#channel:shortId\" (short id expanded via /messages/context)" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "server", + "type": "str", "required": false, - "help": "Number of results (default: 10)" + "help": "Override active server" } ], "columns": [ - "track", - "artist", - "album", - "uri" - ], - "tags": [ - "search" + "id", + "taskNumber", + "title", + "taskStatus", + "channelId" ], "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" + "modulePath": "slock/task-convert.js", + "sourceFile": "slock/task-convert.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "spotify", - "name": "shuffle", - "description": "Toggle shuffle on/off", + "site": "slock", + "name": "task-create", + "description": "Create a task in a channel (single title; batch 1-50 is server-supported but client surface is single — see backlog R4).", "access": "write", - "strategy": "local", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "state", + "name": "channel", "type": "str", - "default": "on", - "required": false, + "required": true, "positional": true, - "help": "on or off", - "choices": [ - "on", - "off" - ] - } - ], - "columns": [ - "shuffle" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "status", - "description": "Show current playback status", - "access": "read", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "track", - "artist", - "album", - "status", - "progress" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "volume", - "description": "Set playback volume (0-100)", - "access": "write", - "strategy": "local", - "browser": false, - "args": [ + "help": "channelId UUID or #name" + }, { - "name": "level", - "type": "int", - "default": 50, + "name": "title", + "type": "str", "required": true, "positional": true, - "help": "Volume 0–100" + "help": "Task title (single; batch TODO via R4)" + }, + { + "name": "desc", + "type": "str", + "required": false, + "help": "Optional description body for the task" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "volume" + "id", + "taskNumber", + "title", + "taskStatus", + "channelId" ], "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" + "modulePath": "slock/task-create.js", + "sourceFile": "slock/task-create.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "substack", - "name": "feed", - "description": "Substack popular posts Feed", - "access": "read", - "domain": "substack.com", + "site": "slock", + "name": "task-delete", + "description": "Delete a chat task (DELETE /tasks/:taskId). Requires --confirm — destructive, irreversible.", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "category", + "name": "taskId", "type": "str", - "default": "all", + "required": true, + "positional": true, + "help": "Full task UUID (= message id; short ids rejected)" + }, + { + "name": "confirm", + "type": "bool", + "default": false, "required": false, - "help": "Post category: all, tech, business, culture, politics, science, health" + "help": "Required acknowledgement: deletion is irreversible" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "server", + "type": "str", "required": false, - "help": "Number of posts to return" + "help": "Override active server" } ], "columns": [ - "rank", - "title", - "author", - "date", - "readTime", - "url" + "taskId", + "deleted" ], "type": "js", - "modulePath": "substack/feed.js", - "sourceFile": "substack/feed.js", - "navigateBefore": "https://substack.com" + "modulePath": "slock/task-delete.js", + "sourceFile": "slock/task-delete.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "substack", - "name": "publication", - "description": "Get a specific Substack Newsletter latest posts", + "site": "slock", + "name": "task-get", + "description": "Fetch a task by channel + taskNumber (GET /tasks/channel/:channelId/number/:taskNumber).", "access": "read", - "domain": "substack.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "url", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Newsletter URL(for example https://example.substack.com)" + "help": "channelId UUID or #name" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "number", + "type": "str", + "required": true, + "positional": true, + "help": "taskNumber (per-channel integer, as shown in \"task #N\")" + }, + { + "name": "server", + "type": "str", "required": false, - "help": "Number of posts to return" + "help": "Override active server" } ], "columns": [ - "rank", + "id", + "taskNumber", "title", - "date", - "description", - "url" + "taskStatus", + "assigneeId" ], "type": "js", - "modulePath": "substack/publication.js", - "sourceFile": "substack/publication.js", - "navigateBefore": "https://substack.com" + "modulePath": "slock/task-get.js", + "sourceFile": "slock/task-get.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "substack", - "name": "search", - "description": "Search Substack posts and newsletters", + "site": "slock", + "name": "task-list", + "description": "List tasks (chat tasks = messages with task fields) attached to a channel. Optional --status filter.", "access": "read", - "domain": "substack.com", - "strategy": "public", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "keyword", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Search keyword" + "help": "channelId UUID or #name" }, { - "name": "type", + "name": "status", "type": "str", - "default": "posts", "required": false, - "help": "Search type(posts=posts, publications=Newsletter)", - "choices": [ - "posts", - "publications" - ] + "help": "Filter by status: todo|in_progress|in_review|done|closed" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "server", + "type": "str", "required": false, - "help": "Number of results to return" + "help": "Override active server" } ], "columns": [ - "rank", + "id", + "taskNumber", "title", - "author", - "date", - "description", - "url" - ], - "tags": [ - "search" + "taskStatus", + "assigneeId" ], "type": "js", - "modulePath": "substack/search.js", - "sourceFile": "substack/search.js" + "modulePath": "slock/task-list.js", + "sourceFile": "slock/task-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "suno", - "name": "download", - "description": "Download an existing Suno clip (MP3 + optional WAV/M4A/video) by id", - "access": "write", - "domain": "suno.com", + "site": "slock", + "name": "task-list-server", + "description": "List tasks across all channels in the active server (GET /tasks/server). Optional --status filter.", + "access": "read", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "clip", - "type": "str", - "required": true, - "positional": true, - "help": "Clip UUID or https://suno.com/song/ URL" - }, - { - "name": "formats", + "name": "status", "type": "str", "required": false, - "help": "Comma-separated formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" + "help": "Filter by status: todo|in_progress|in_review|done|closed" }, { - "name": "op", + "name": "server", "type": "str", "required": false, - "help": "Output directory (default: ~/Music/suno)" - }, - { - "name": "confirm-paid", - "type": "boolean", - "default": false, - "required": false, - "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." + "help": "Override active server" } ], "columns": [ - "status", - "clip", + "id", + "taskNumber", "title", - "files", - "link" + "taskStatus", + "channelId", + "assigneeId" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "suno/download.js", - "sourceFile": "suno/download.js", - "navigateBefore": false, + "modulePath": "slock/task-list-server.js", + "sourceFile": "slock/task-list-server.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "suno", - "name": "generate", - "description": "Generate music with Suno (V5.5 chirp-fenix by default) and download clips locally", + "site": "slock", + "name": "task-status", + "description": "Set a task's status (PATCH /tasks/:taskId/status, body {status}). One of todo|in_progress|in_review|done|closed.", "access": "write", - "domain": "suno.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", + "name": "taskId", "type": "str", - "required": false, + "required": true, "positional": true, - "help": "Simple-mode description (ignored when --lyrics is provided)" - }, - { - "name": "lyrics", - "type": "str", - "required": false, - "help": "Custom-mode lyrics (with [Verse]/[Chorus] metatags). Triggers Custom mode." - }, - { - "name": "tags", - "type": "str", - "required": false, - "help": "Custom-mode style tags (genre, BPM, instruments...). Used with --lyrics." - }, - { - "name": "negative-tags", - "type": "str", - "required": false, - "help": "Custom-mode style exclusions (e.g. \"no vocals, no autotune\"). Used with --lyrics." - }, - { - "name": "title", - "type": "str", - "required": false, - "help": "Song title (default: auto-derived from prompt)" - }, - { - "name": "instrumental", - "type": "boolean", - "default": false, - "required": false, - "help": "No vocals" - }, - { - "name": "model", - "type": "str", - "required": false, - "help": "Model id: chirp-fenix, chirp-bluejay, chirp-v4, chirp-v3-5. Default: chirp-fenix" - }, - { - "name": "weirdness", - "type": "str", - "required": false, - "help": "Creative weirdness slider (0..1). Default: 0.5" - }, - { - "name": "style-weight", - "type": "str", - "required": false, - "help": "Style adherence slider (0..1). Default: 0.5" + "help": "Full task UUID (= message id; short ids rejected)" }, { - "name": "formats", + "name": "status", "type": "str", - "required": false, - "help": "Comma-separated download formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" + "required": true, + "positional": true, + "help": "One of: todo|in_progress|in_review|done|closed" }, { - "name": "op", + "name": "server", "type": "str", "required": false, - "help": "Output directory (default: ~/Music/suno)" - }, - { - "name": "timeout", - "type": "int", - "default": 300, - "required": false, - "help": "Max seconds to wait for clips to finish (default: 300)" - }, - { - "name": "sd", - "type": "boolean", - "default": false, - "required": false, - "help": "Skip download; only print clip ids and Suno URLs" - }, - { - "name": "confirm-paid", - "type": "boolean", - "default": false, - "required": false, - "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." + "help": "Override active server" } ], "columns": [ - "status", - "clip", - "title", - "files", - "link" + "taskId", + "taskStatus", + "assigneeId", + "taskNumber" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "suno/generate.js", - "sourceFile": "suno/generate.js", - "navigateBefore": false, + "modulePath": "slock/task-status.js", + "sourceFile": "slock/task-status.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "suno", - "name": "list", - "description": "List recent Suno clips in your library (id, title, status, created_at, link)", - "access": "read", - "domain": "suno.com", + "site": "slock", + "name": "task-unclaim", + "description": "Release ownership of a chat task (PATCH /tasks/:id/unclaim).", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max clips to list (default: 20)" + "name": "taskId", + "type": "str", + "required": true, + "positional": true, + "help": "Full task UUID (= message id; short ids rejected)" }, { - "name": "page", - "type": "int", - "default": 0, + "name": "server", + "type": "str", "required": false, - "help": "Pagination offset, 0-based (default: 0)" + "help": "Override active server" } ], "columns": [ - "rank", - "clip", - "title", - "status", - "created", - "link" + "taskId", + "taskStatus", + "assigneeId", + "taskNumber" ], "type": "js", - "modulePath": "suno/list.js", - "sourceFile": "suno/list.js", - "navigateBefore": false, + "modulePath": "slock/task-unclaim.js", + "sourceFile": "slock/task-unclaim.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "suno", - "name": "login", - "description": "Open suno login", + "site": "slock", + "name": "thread-done", + "description": "Mark a thread as done / hide it from the active list (POST /channels/threads/done)", "access": "write", - "domain": "suno.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "suno/auth.js", - "sourceFile": "suno/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "suno", - "name": "status", - "description": "Check Suno login, plan, credit balance, and captcha readiness", - "access": "read", - "domain": "suno.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "Status", - "Plan", - "Credits", - "Monthly", - "Captcha" + "args": [ + { + "name": "threadChannelId", + "type": "str", + "required": true, + "positional": true, + "help": "Thread channel UUID (from thread-list / message-read)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } ], - "type": "js", - "modulePath": "suno/status.js", - "sourceFile": "suno/status.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "suno", - "name": "whoami", - "description": "Show the current logged-in suno account", - "access": "read", - "domain": "suno.com", - "strategy": "cookie", - "browser": true, - "args": [], "columns": [ - "logged_in", - "site", - "user_id", - "name" + "threadChannelId", + "result" ], "type": "js", - "modulePath": "suno/auth.js", - "sourceFile": "suno/auth.js", - "navigateBefore": false, + "modulePath": "slock/thread-done.js", + "sourceFile": "slock/thread-done.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "tiktok", - "name": "comment", - "description": "Post a comment on a TikTok video", + "site": "slock", + "name": "thread-follow", + "description": "Follow the thread on a parent message (POST /channels/threads/follow)", "access": "write", - "domain": "www.tiktok.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "url", + "name": "parentMessageId", "type": "str", "required": true, "positional": true, - "help": "TikTok video URL (https://www.tiktok.com/@user/video/)" + "help": "Full parent messageId UUID (short ids rejected)" }, { - "name": "text", + "name": "server", "type": "str", - "required": true, - "positional": true, - "help": "Comment text (≤150 chars)" + "required": false, + "help": "Override active server" } ], "columns": [ - "url", - "text", + "parentMessageId", + "threadChannelId", "result" ], "type": "js", - "modulePath": "tiktok/comment.js", - "sourceFile": "tiktok/comment.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/thread-follow.js", + "sourceFile": "slock/thread-follow.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "creator-videos", - "description": "TikTok Studio creator content list (views/likes/comments/saves/shares)", + "site": "slock", + "name": "thread-list", + "description": "List followed threads in the active server (GET /channels/threads/followed)", "access": "read", - "domain": "www.tiktok.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of creator videos to return (max 250)" - }, - { - "name": "cursor", - "type": "string", - "default": "0", + "name": "server", + "type": "str", "required": false, - "help": "Non-negative TikTok Studio pagination cursor" + "help": "Override active server" } ], "columns": [ - "video_id", - "title", - "date", - "views", - "likes", - "comments", - "saves", - "shares", - "url" + "threadChannelId", + "parentMessageId", + "parentChannelName", + "unreadCount", + "replyCount", + "lastReplyAt" ], "type": "js", - "modulePath": "tiktok/creator-videos.js", - "sourceFile": "tiktok/creator-videos.js", - "navigateBefore": "https://www.tiktok.com/tiktokstudio/content" + "modulePath": "slock/thread-list.js", + "sourceFile": "slock/thread-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "explore", - "description": "Get trending TikTok videos from the recommend feed via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", + "site": "slock", + "name": "thread-undone", + "description": "Restore a done thread to the active list (POST /channels/threads/undone)", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "threadChannelId", + "type": "str", + "required": true, + "positional": true, + "help": "Thread channel UUID (from thread-list / message-read)" + }, + { + "name": "server", + "type": "str", "required": false, - "help": "Number of videos to return (max 120)" + "help": "Override active server" } ], - "columns": [ - "index", - "id", - "author", - "url", - "cover", - "title", - "desc", - "plays", - "likes", - "comments", - "shares", - "createTime" - ], - "tags": [ - "search" - ], + "columns": [ + "threadChannelId", + "result" + ], "type": "js", - "modulePath": "tiktok/explore.js", - "sourceFile": "tiktok/explore.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/thread-undone.js", + "sourceFile": "slock/thread-undone.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "follow", - "description": "Follow a TikTok user by username", + "site": "slock", + "name": "thread-unfollow", + "description": "Stop following a thread (POST /channels/threads/unfollow)", "access": "write", - "domain": "www.tiktok.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "threadChannelId", "type": "str", "required": true, "positional": true, - "help": "TikTok username (without @)" + "help": "Thread channel UUID (from thread-list / message-read)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "username", - "url", + "threadChannelId", "result" ], "type": "js", - "modulePath": "tiktok/follow.js", - "sourceFile": "tiktok/follow.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/thread-unfollow.js", + "sourceFile": "slock/thread-unfollow.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "following", - "description": "List accounts the logged-in user follows on TikTok via page-context APIs", + "site": "slock", + "name": "unread-summary", + "description": "Global unread counts across every server you belong to.", "access": "read", - "domain": "www.tiktok.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of accounts (max 200)" - } - ], + "args": [], "columns": [ - "index", - "username", + "serverId", + "slug", "name", - "secUid", - "verified", - "followers", - "following", - "url" + "unreadCount" ], "type": "js", - "modulePath": "tiktok/following.js", - "sourceFile": "tiktok/following.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/unread-summary.js", + "sourceFile": "slock/unread-summary.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "friends", - "description": "Get TikTok friend / who-to-follow suggestions via page-context APIs", + "site": "slock", + "name": "whoami", + "description": "Show the current logged-in slock account", "access": "read", - "domain": "www.tiktok.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "id", + "name", + "email" + ], + "type": "js", + "modulePath": "slock/whoami.js", + "sourceFile": "slock/whoami.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "spotify", + "name": "auth", + "description": "Authenticate with Spotify (OAuth — run once)", + "access": "write", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "next", + "description": "Skip to next track", + "access": "write", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "pause", + "description": "Pause playback", + "access": "write", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "play", + "description": "Resume playback or search and play a track/artist", + "access": "write", + "strategy": "local", + "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "query", + "type": "str", + "default": "", "required": false, - "help": "Number of suggestions (max 100)" + "positional": true, + "help": "Track or artist to play (optional)" } ], "columns": [ - "index", - "username", - "name", - "secUid", - "verified", - "followers", - "following", - "url" + "track", + "artist", + "status" ], "type": "js", - "modulePath": "tiktok/friends.js", - "sourceFile": "tiktok/friends.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "tiktok", - "name": "like", - "description": "Like a TikTok video", + "site": "spotify", + "name": "prev", + "description": "Skip to previous track", "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "queue", + "description": "Add a track to the playback queue", + "access": "write", + "strategy": "local", + "browser": false, "args": [ { - "name": "url", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "TikTok video URL" + "help": "Track to add to queue" } ], "columns": [ - "status", - "likes", - "url" + "track", + "artist", + "status" ], "type": "js", - "modulePath": "tiktok/like.js", - "sourceFile": "tiktok/like.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "tiktok", - "name": "live", - "description": "Browse TikTok live streams via page-context APIs", + "site": "spotify", + "name": "repeat", + "description": "Set repeat mode (off / track / context)", + "access": "write", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "mode", + "type": "str", + "default": "context", + "required": false, + "positional": true, + "help": "off / track / context", + "choices": [ + "off", + "track", + "context" + ] + } + ], + "columns": [ + "repeat" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "search", + "description": "Search for tracks", "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, + "strategy": "local", + "browser": false, "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Number of streams (max 60)" + "help": "Number of results (default: 10)" } ], "columns": [ - "index", - "streamer", - "name", - "title", - "viewers", - "likes", - "secUid", - "url" + "track", + "artist", + "album", + "uri" ], - "type": "js", - "modulePath": "tiktok/live.js", - "sourceFile": "tiktok/live.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "login", - "description": "Open tiktok login", - "access": "write", - "domain": "tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "sec_uid", - "username", - "nickname", - "action", - "verify_command" + "tags": [ + "search" ], "type": "js", - "modulePath": "tiktok/auth.js", - "sourceFile": "tiktok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "tiktok", - "name": "notifications", - "description": "Read TikTok inbox notifications (likes, comments, mentions, followers) via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, + "site": "spotify", + "name": "shuffle", + "description": "Toggle shuffle on/off", + "access": "write", + "strategy": "local", + "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Number of notifications (max 100)" - }, - { - "name": "type", + "name": "state", "type": "str", - "default": "all", + "default": "on", "required": false, - "help": "Notification type", + "positional": true, + "help": "on or off", "choices": [ - "all", - "likes", - "comments", - "mentions", - "followers" + "on", + "off" ] } ], "columns": [ - "index", - "id", - "from", - "text", - "createTime" + "shuffle" ], "type": "js", - "modulePath": "tiktok/notifications.js", - "sourceFile": "tiktok/notifications.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "tiktok", - "name": "profile", - "description": "Get TikTok user profile info", + "site": "spotify", + "name": "status", + "description": "Show current playback status", "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok username (without @)" - } - ], + "strategy": "local", + "browser": false, + "args": [], "columns": [ - "username", - "name", - "followers", - "following", - "likes", - "videos", - "verified", - "bio" + "track", + "artist", + "album", + "status", + "progress" ], "type": "js", - "modulePath": "tiktok/profile.js", - "sourceFile": "tiktok/profile.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "tiktok", - "name": "save", - "description": "Add a TikTok video to Favorites", + "site": "spotify", + "name": "volume", + "description": "Set playback volume (0-100)", "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, + "strategy": "local", + "browser": false, "args": [ { - "name": "url", - "type": "str", + "name": "level", + "type": "int", + "default": 50, "required": true, "positional": true, - "help": "TikTok video URL" + "help": "Volume 0–100" } ], "columns": [ - "status", - "url" + "volume" ], "type": "js", - "modulePath": "tiktok/save.js", - "sourceFile": "tiktok/save.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "tiktok", - "name": "search", - "description": "Search TikTok videos", + "site": "substack", + "name": "feed", + "description": "Substack popular posts Feed", "access": "read", - "domain": "www.tiktok.com", + "domain": "substack.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", + "name": "category", "type": "str", - "required": true, - "positional": true, - "help": "Search query" + "default": "all", + "required": false, + "help": "Post category: all, tech, business, culture, politics, science, health" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results" + "help": "Number of posts to return" } ], "columns": [ "rank", - "desc", + "title", "author", - "url", - "plays", - "likes", - "comments", - "shares" - ], - "tags": [ - "search" + "date", + "readTime", + "url" ], "type": "js", - "modulePath": "tiktok/search.js", - "sourceFile": "tiktok/search.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "substack/feed.js", + "sourceFile": "substack/feed.js", + "navigateBefore": "https://substack.com" }, { - "site": "tiktok", - "name": "unfollow", - "description": "Unfollow a TikTok user by username", - "access": "write", - "domain": "www.tiktok.com", + "site": "substack", + "name": "publication", + "description": "Get a specific Substack Newsletter latest posts", + "access": "read", + "domain": "substack.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "TikTok username (without @)" + "help": "Newsletter URL(for example https://example.substack.com)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" } ], "columns": [ - "username", - "url", - "result" + "rank", + "title", + "date", + "description", + "url" ], "type": "js", - "modulePath": "tiktok/unfollow.js", - "sourceFile": "tiktok/unfollow.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "substack/publication.js", + "sourceFile": "substack/publication.js", + "navigateBefore": "https://substack.com" }, { - "site": "tiktok", - "name": "unlike", - "description": "Unlike a TikTok video", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, + "site": "substack", + "name": "search", + "description": "Search Substack posts and newsletters", + "access": "read", + "domain": "substack.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "url", + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "type", "type": "str", - "required": true, - "positional": true, - "help": "TikTok video URL" + "default": "posts", + "required": false, + "help": "Search type(posts=posts, publications=Newsletter)", + "choices": [ + "posts", + "publications" + ] + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results to return" } ], "columns": [ - "status", - "likes", + "rank", + "title", + "author", + "date", + "description", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "tiktok/unlike.js", - "sourceFile": "tiktok/unlike.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "substack/search.js", + "sourceFile": "substack/search.js" }, { - "site": "tiktok", - "name": "unsave", - "description": "Remove a TikTok video from Favorites", + "site": "suno", + "name": "download", + "description": "Download an existing Suno clip (MP3 + optional WAV/M4A/video) by id", "access": "write", - "domain": "www.tiktok.com", + "domain": "suno.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "url", + "name": "clip", "type": "str", "required": true, "positional": true, - "help": "TikTok video URL" + "help": "Clip UUID or https://suno.com/song/ URL" + }, + { + "name": "formats", + "type": "str", + "required": false, + "help": "Comma-separated formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" + }, + { + "name": "op", + "type": "str", + "required": false, + "help": "Output directory (default: ~/Music/suno)" + }, + { + "name": "confirm-paid", + "type": "boolean", + "default": false, + "required": false, + "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." } ], "columns": [ "status", - "url" + "clip", + "title", + "files", + "link" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "tiktok/unsave.js", - "sourceFile": "tiktok/unsave.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "suno/download.js", + "sourceFile": "suno/download.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "user", - "description": "Get recent videos from a TikTok user via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", + "site": "suno", + "name": "generate", + "description": "Generate music with Suno (V5.5 chirp-fenix by default) and download clips locally", + "access": "write", + "domain": "suno.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "prompt", "type": "str", - "required": true, + "required": false, "positional": true, - "help": "TikTok username (without @)" + "help": "Simple-mode description (ignored when --lyrics is provided)" }, { - "name": "limit", + "name": "lyrics", + "type": "str", + "required": false, + "help": "Custom-mode lyrics (with [Verse]/[Chorus] metatags). Triggers Custom mode." + }, + { + "name": "tags", + "type": "str", + "required": false, + "help": "Custom-mode style tags (genre, BPM, instruments...). Used with --lyrics." + }, + { + "name": "negative-tags", + "type": "str", + "required": false, + "help": "Custom-mode style exclusions (e.g. \"no vocals, no autotune\"). Used with --lyrics." + }, + { + "name": "title", + "type": "str", + "required": false, + "help": "Song title (default: auto-derived from prompt)" + }, + { + "name": "instrumental", + "type": "boolean", + "default": false, + "required": false, + "help": "No vocals" + }, + { + "name": "model", + "type": "str", + "required": false, + "help": "Model id: chirp-fenix, chirp-bluejay, chirp-v4, chirp-v3-5. Default: chirp-fenix" + }, + { + "name": "weirdness", + "type": "str", + "required": false, + "help": "Creative weirdness slider (0..1). Default: 0.5" + }, + { + "name": "style-weight", + "type": "str", + "required": false, + "help": "Style adherence slider (0..1). Default: 0.5" + }, + { + "name": "formats", + "type": "str", + "required": false, + "help": "Comma-separated download formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" + }, + { + "name": "op", + "type": "str", + "required": false, + "help": "Output directory (default: ~/Music/suno)" + }, + { + "name": "timeout", "type": "int", - "default": 20, + "default": 300, "required": false, - "help": "Number of videos to return (max 120)" + "help": "Max seconds to wait for clips to finish (default: 300)" + }, + { + "name": "sd", + "type": "boolean", + "default": false, + "required": false, + "help": "Skip download; only print clip ids and Suno URLs" + }, + { + "name": "confirm-paid", + "type": "boolean", + "default": false, + "required": false, + "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." } ], "columns": [ - "index", - "id", - "source", - "author", - "url", - "cover", + "status", + "clip", "title", - "desc", - "plays", - "likes", - "comments", - "shares", - "createTime" - ], - "type": "js", - "modulePath": "tiktok/user.js", - "sourceFile": "tiktok/user.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "whoami", - "description": "Show the current logged-in tiktok account", - "access": "read", - "domain": "tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "sec_uid", - "username", - "nickname" + "files", + "link" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "tiktok/auth.js", - "sourceFile": "tiktok/auth.js", + "modulePath": "suno/generate.js", + "sourceFile": "suno/generate.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "trae-solo", - "name": "automation-list", - "description": "List Trae SOLO Automation tab content. Default tab is \"Configured\"; pass --tab to switch.", + "site": "suno", + "name": "list", + "description": "List recent Suno clips in your library (id, title, status, created_at, link)", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "suno.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "tab", - "type": "str", - "default": "configured", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Tab to view: configured / run-history / task-template" + "help": "Max clips to list (default: 20)" }, { - "name": "limit", + "name": "page", "type": "int", - "default": 50, + "default": 0, "required": false, - "help": "" + "help": "Pagination offset, 0-based (default: 0)" } ], "columns": [ - "Index", - "Title", - "Summary" - ], - "type": "js", - "modulePath": "trae-solo/automation.js", - "sourceFile": "trae-solo/automation.js", - "navigateBefore": true - }, - { - "site": "trae-solo", - "name": "cookies", - "description": "List cookies on the Trae SOLO renderer (JS-visible via document.cookie; httpOnly cookies not shown).", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version" + "rank", + "clip", + "title", + "status", + "created", + "link" ], "type": "js", - "modulePath": "trae-solo/renderer-storage.js", - "sourceFile": "trae-solo/renderer-storage.js", - "navigateBefore": true + "modulePath": "suno/list.js", + "sourceFile": "suno/list.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "trae-solo", - "name": "extensions-list", - "description": "List VSCode extensions installed in Trae SOLO (~/.trae/extensions/extensions.json). Works while Trae is closed.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "site": "suno", + "name": "login", + "description": "Open suno login", + "access": "write", + "domain": "suno.com", + "strategy": "cookie", + "browser": true, "args": [], "columns": [ - "Index", - "Workspace Id", - "Kind", - "Target", - "Modified", - "Id", - "Version", - "Source", - "Installed" + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" ], "type": "js", - "modulePath": "trae-solo/workspaces-fs.js", - "sourceFile": "trae-solo/workspaces-fs.js" + "modulePath": "suno/auth.js", + "sourceFile": "suno/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "trae-solo", - "name": "history", - "description": "List Trae SOLO projects and the tasks within each (from the project-list view sidebar).", + "site": "suno", + "name": "status", + "description": "Check Suno login, plan, credit balance, and captcha readiness", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "suno.com", + "strategy": "cookie", "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "help": "Filter by project name (substring, case-insensitive)" - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Max tasks per project" - } - ], + "args": [], "columns": [ - "Project", - "Task Index", - "Task" + "Status", + "Plan", + "Credits", + "Monthly", + "Captcha" ], "type": "js", - "modulePath": "trae-solo/history.js", - "sourceFile": "trae-solo/history.js", - "navigateBefore": true + "modulePath": "suno/status.js", + "sourceFile": "suno/status.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "trae-solo", - "name": "idb-list", - "description": "List IndexedDB databases on the Trae SOLO renderer. Trae ships an @byted/ve-rtc DB used by the Volcengine RTC voice/video infrastructure.", + "site": "suno", + "name": "whoami", + "description": "Show the current logged-in suno account", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "suno.com", + "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version" + "logged_in", + "site", + "user_id", + "name" ], "type": "js", - "modulePath": "trae-solo/renderer-storage.js", - "sourceFile": "trae-solo/renderer-storage.js", - "navigateBefore": true + "modulePath": "suno/auth.js", + "sourceFile": "suno/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "trae-solo", - "name": "mode", - "description": "Read or switch TRAE SOLO between Code mode and Work mode.", + "site": "tiktok", + "name": "comment", + "description": "Post a comment on a TikTok video", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "www.tiktok.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "target", + "name": "url", "type": "str", - "required": false, + "required": true, "positional": true, - "help": "Target mode: code or work. Omit to read current." + "help": "TikTok video URL (https://www.tiktok.com/@user/video/)" + }, + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Comment text (≤150 chars)" } ], "columns": [ - "Status", - "Mode" + "url", + "text", + "result" ], "type": "js", - "modulePath": "trae-solo/mode.js", - "sourceFile": "trae-solo/mode.js", - "navigateBefore": true + "modulePath": "tiktok/comment.js", + "sourceFile": "tiktok/comment.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "model", - "description": "Read or switch the current AI model in TRAE SOLO. Without arguments, reports the current model. With argument (substring, case-insensitive), switches to a matching model. Pass --list to enumerate available models.", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "site": "tiktok", + "name": "creator-videos", + "description": "TikTok Studio creator content list (views/likes/comments/saves/shares)", + "access": "read", + "domain": "www.tiktok.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "name", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "positional": true, - "help": "Target model name (substring match, case-insensitive). Omit to read current." + "help": "Number of creator videos to return (max 250)" }, { - "name": "list", - "type": "boolean", - "default": false, + "name": "cursor", + "type": "string", + "default": "0", "required": false, - "help": "List all available models (does not switch)" + "help": "Non-negative TikTok Studio pagination cursor" } ], "columns": [ - "Status", - "Model" + "video_id", + "title", + "date", + "views", + "likes", + "comments", + "saves", + "shares", + "url" ], "type": "js", - "modulePath": "trae-solo/model.js", - "sourceFile": "trae-solo/model.js", - "navigateBefore": true + "modulePath": "tiktok/creator-videos.js", + "sourceFile": "tiktok/creator-videos.js", + "navigateBefore": "https://www.tiktok.com/tiktokstudio/content" }, { - "site": "trae-solo", - "name": "recent-workspaces", - "description": "Show Trae SOLO's recently-opened workspaces (the File → Open Recent menu, stored under key \"history.recentlyOpenedPathsList\" in state.vscdb).", + "site": "tiktok", + "name": "explore", + "description": "Get trending TikTok videos from the recommend feed via page-context APIs", "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "" + "help": "Number of videos to return (max 120)" } ], "columns": [ - "Index", - "Key", - "Kind", - "Path" + "index", + "id", + "author", + "url", + "cover", + "title", + "desc", + "plays", + "likes", + "comments", + "shares", + "createTime" ], - "type": "js", - "modulePath": "trae-solo/state-fs.js", - "sourceFile": "trae-solo/state-fs.js" - }, - { - "site": "trae-solo", - "name": "settings-read", - "description": "Parse and pretty-print Trae SOLO user settings.json (~/Library/Application Support/TRAE SOLO/User/settings.json). Handles VSCode JSONC syntax (line comments + trailing commas).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "Field", - "Value" + "tags": [ + "search" ], "type": "js", - "modulePath": "trae-solo/settings.js", - "sourceFile": "trae-solo/settings.js" + "modulePath": "tiktok/explore.js", + "sourceFile": "tiktok/explore.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "skill-category", - "description": "Filter Skills Marketplace by category. Pass --list to see categories.", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "tiktok", + "name": "follow", + "description": "Follow a TikTok user by username", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "name", + "name": "username", "type": "str", - "required": false, + "required": true, "positional": true, - "help": "Category name (substring; case-insensitive). Common: All / Developer Tools / Data Analysis / UI Design / Content Creation / Productivity" - }, - { - "name": "list", - "type": "boolean", - "default": false, - "required": false, - "help": "List available categories" - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "" + "help": "TikTok username (without @)" } ], "columns": [ - "Index", - "Name", - "Description" + "username", + "url", + "result" ], "type": "js", - "modulePath": "trae-solo/skill.js", - "sourceFile": "trae-solo/skill.js", - "navigateBefore": true + "modulePath": "tiktok/follow.js", + "sourceFile": "tiktok/follow.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "skill-fs-installed", - "description": "List INSTALLED Trae SOLO skills (managedSkills entry in ~/.trae/skill-config.json).", + "site": "tiktok", + "name": "following", + "description": "List accounts the logged-in user follows on TikTok via page-context APIs", "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [], + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of accounts (max 200)" + } + ], "columns": [ - "Index", - "Name", - "Description", - "Source" + "index", + "username", + "name", + "secUid", + "verified", + "followers", + "following", + "url" ], "type": "js", - "modulePath": "trae-solo/skill-fs.js", - "sourceFile": "trae-solo/skill-fs.js" + "modulePath": "tiktok/following.js", + "sourceFile": "tiktok/following.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "skill-fs-list", - "description": "List all Trae SOLO skills present on disk under ~/.trae/skills/. Reads SKILL.md front-matter for descriptions. Works while Trae is closed.", + "site": "tiktok", + "name": "friends", + "description": "Get TikTok friend / who-to-follow suggestions via page-context APIs", "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 200, + "default": 20, "required": false, - "help": "Max rows" + "help": "Number of suggestions (max 100)" } ], "columns": [ - "Index", - "Name", - "Description", - "Source" + "index", + "username", + "name", + "secUid", + "verified", + "followers", + "following", + "url" ], "type": "js", - "modulePath": "trae-solo/skill-fs.js", - "sourceFile": "trae-solo/skill-fs.js" + "modulePath": "tiktok/friends.js", + "sourceFile": "tiktok/friends.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "skill-fs-show", - "description": "Print a skill's SKILL.md content + on-disk path.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "site": "tiktok", + "name": "like", + "description": "Like a TikTok video", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "name", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Skill name (folder under ~/.trae/skills/)" + "help": "TikTok video URL" } ], "columns": [ - "Field", - "Value" + "status", + "likes", + "url" ], "type": "js", - "modulePath": "trae-solo/skill-fs.js", - "sourceFile": "trae-solo/skill-fs.js" + "modulePath": "tiktok/like.js", + "sourceFile": "tiktok/like.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "skill-list", - "description": "List Trae SOLO Skills — by default the Marketplace; pass --installed to list installed ones.", + "site": "tiktok", + "name": "live", + "description": "Browse TikTok live streams via page-context APIs", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.tiktok.com", + "strategy": "cookie", "browser": true, "args": [ - { - "name": "installed", - "type": "boolean", - "default": false, - "required": false, - "help": "List installed skills instead of the marketplace" - }, { "name": "limit", "type": "int", - "default": 100, + "default": 10, "required": false, - "help": "Max rows to return" + "help": "Number of streams (max 60)" } ], "columns": [ - "Index", - "Name", - "Description" + "index", + "streamer", + "name", + "title", + "viewers", + "likes", + "secUid", + "url" ], "type": "js", - "modulePath": "trae-solo/skill.js", - "sourceFile": "trae-solo/skill.js", - "navigateBefore": true + "modulePath": "tiktok/live.js", + "sourceFile": "tiktok/live.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "skill-search", - "description": "Filter Skills Marketplace by keyword.", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "tiktok", + "name": "login", + "description": "Open tiktok login", + "access": "write", + "domain": "tiktok.com", + "strategy": "cookie", "browser": true, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (substring)" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max rows" - } - ], + "args": [], "columns": [ - "Index", - "Name", - "Description" - ], - "tags": [ - "search" + "status", + "logged_in", + "site", + "sec_uid", + "username", + "nickname", + "action", + "verify_command" ], "type": "js", - "modulePath": "trae-solo/skill.js", - "sourceFile": "trae-solo/skill.js", - "navigateBefore": true + "modulePath": "tiktok/auth.js", + "sourceFile": "tiktok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "trae-solo", - "name": "state-get", - "description": "Read a single key from Trae SOLO's globalStorage state.vscdb. Pass --workspace to query a per-workspace DB instead. Returns parsed JSON if the value is JSON.", + "site": "tiktok", + "name": "notifications", + "description": "Read TikTok inbox notifications (likes, comments, mentions, followers) via page-context APIs", "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "State key (use state-keys to discover)" - }, - { - "name": "workspace", - "type": "str", + "name": "limit", + "type": "int", + "default": 15, "required": false, - "help": "Workspace id (from workspaces-list) to query a per-workspace DB" + "help": "Number of notifications (max 100)" }, { - "name": "max-bytes", - "type": "int", - "default": 8000, + "name": "type", + "type": "str", + "default": "all", "required": false, - "help": "Truncate value to this many bytes" + "help": "Notification type", + "choices": [ + "all", + "likes", + "comments", + "mentions", + "followers" + ] } ], "columns": [ - "Field", - "Value" + "index", + "id", + "from", + "text", + "createTime" ], "type": "js", - "modulePath": "trae-solo/state-fs.js", - "sourceFile": "trae-solo/state-fs.js" + "modulePath": "tiktok/notifications.js", + "sourceFile": "tiktok/notifications.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "state-keys", - "description": "List all keys present in Trae SOLO's globalStorage state.vscdb (VSCode-style UI/agent state). Pass --workspace to query a per-workspace DB instead. Use state-get to read a specific value. (See renderer storage-keys for browser-side LS/SS.)", + "site": "tiktok", + "name": "profile", + "description": "Get TikTok user profile info", "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "filter", - "type": "str", - "required": false, - "help": "Case-insensitive substring filter over keys" - }, - { - "name": "workspace", + "name": "username", "type": "str", - "required": false, - "help": "Workspace id (from workspaces-list) to query a per-workspace DB" - }, - { - "name": "limit", - "type": "int", - "default": 200, - "required": false, - "help": "" + "required": true, + "positional": true, + "help": "TikTok username (without @)" } ], "columns": [ - "Index", - "Key", - "Kind", - "Path" + "username", + "name", + "followers", + "following", + "likes", + "videos", + "verified", + "bio" ], "type": "js", - "modulePath": "trae-solo/state-fs.js", - "sourceFile": "trae-solo/state-fs.js" + "modulePath": "tiktok/profile.js", + "sourceFile": "tiktok/profile.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "status", - "description": "Check active CDP connection to Trae SOLO Desktop", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "tiktok", + "name": "save", + "description": "Add a TikTok video to Favorites", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "TikTok video URL" + } + ], "columns": [ - "Status", - "Url", - "Title" + "status", + "url" ], "type": "js", - "modulePath": "trae-solo/status.js", - "sourceFile": "trae-solo/status.js", - "navigateBefore": true + "modulePath": "tiktok/save.js", + "sourceFile": "tiktok/save.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "storage-get", - "description": "Read a single localStorage / sessionStorage value on the Trae SOLO renderer.", + "site": "tiktok", + "name": "search", + "description": "Search TikTok videos", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.tiktok.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "key", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Storage key (use storage-keys to discover)" - }, - { - "name": "storage", - "type": "str", - "default": "local", - "required": false, - "help": "\"local\" or \"session\"" + "help": "Search query" }, { - "name": "max-bytes", + "name": "limit", "type": "int", - "default": 4000, + "default": 10, "required": false, - "help": "Truncate value to this many chars" + "help": "Number of results" } ], "columns": [ - "Field", - "Value" + "rank", + "desc", + "author", + "url", + "plays", + "likes", + "comments", + "shares" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "trae-solo/renderer-storage.js", - "sourceFile": "trae-solo/renderer-storage.js", - "navigateBefore": true + "modulePath": "tiktok/search.js", + "sourceFile": "tiktok/search.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "storage-keys", - "description": "List localStorage / sessionStorage keys on the Trae SOLO renderer (CDP). For the on-disk VSCode state.vscdb, see state-keys.", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "tiktok", + "name": "unfollow", + "description": "Unfollow a TikTok user by username", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "storage", - "type": "str", - "default": "local", - "required": false, - "help": "\"local\" or \"session\"" - }, - { - "name": "filter", + "name": "username", "type": "str", - "required": false, - "help": "Case-insensitive substring filter" - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Max rows to return" + "required": true, + "positional": true, + "help": "TikTok username (without @)" } ], "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version" + "username", + "url", + "result" ], "type": "js", - "modulePath": "trae-solo/renderer-storage.js", - "sourceFile": "trae-solo/renderer-storage.js", - "navigateBefore": true + "modulePath": "tiktok/unfollow.js", + "sourceFile": "tiktok/unfollow.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "task-fs-list", - "description": "List Trae SOLO task ids from disk (snapshot/ + agentconfig/.json). Works while Trae is closed.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "site": "tiktok", + "name": "unlike", + "description": "Unlike a TikTok video", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "" + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "TikTok video URL" } ], "columns": [ - "Index", - "Task Id", - "Has Snapshot", - "Has Config", - "Modified", - "Phase", - "Turn Id", - "Commit" + "status", + "likes", + "url" ], "type": "js", - "modulePath": "trae-solo/task-fs.js", - "sourceFile": "trae-solo/task-fs.js" + "modulePath": "tiktok/unlike.js", + "sourceFile": "tiktok/unlike.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "task-fs-show", - "description": "Show the workspace tree at a given chat-turn ref (via git ls-tree). Pass --turn to pick a turn; otherwise the latest after-chat-turn ref.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "site": "tiktok", + "name": "unsave", + "description": "Remove a TikTok video from Favorites", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "task-id", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Task UUID" - }, - { - "name": "turn", - "type": "str", - "required": false, - "help": "Specific turn id (omit for latest after-chat-turn)" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "" + "help": "TikTok video URL" } ], "columns": [ - "Mode", - "Path", - "Size" + "status", + "url" ], "type": "js", - "modulePath": "trae-solo/task-fs.js", - "sourceFile": "trae-solo/task-fs.js" + "modulePath": "tiktok/unsave.js", + "sourceFile": "tiktok/unsave.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "task-fs-turns", - "description": "Show the chat-turn timeline for a Trae SOLO task as git tags (before-chat-turn-* / after-chat-turn-*).", + "site": "tiktok", + "name": "user", + "description": "Get recent videos from a TikTok user via page-context APIs", "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "task-id", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Task UUID (folder name under snapshot/)" + "help": "TikTok username (without @)" }, { "name": "limit", "type": "int", - "default": 50, + "default": 20, "required": false, - "help": "" + "help": "Number of videos to return (max 120)" } ], "columns": [ - "Index", - "Task Id", - "Has Snapshot", - "Has Config", - "Modified", - "Phase", - "Turn Id", - "Commit" + "index", + "id", + "source", + "author", + "url", + "cover", + "title", + "desc", + "plays", + "likes", + "comments", + "shares", + "createTime" ], "type": "js", - "modulePath": "trae-solo/task-fs.js", - "sourceFile": "trae-solo/task-fs.js" + "modulePath": "tiktok/user.js", + "sourceFile": "tiktok/user.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trae-solo", - "name": "user-rules", - "description": "Print Trae SOLO user rules (~/.trae/user_rules.md).", + "site": "tiktok", + "name": "whoami", + "description": "Show the current logged-in tiktok account", "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "domain": "tiktok.com", + "strategy": "cookie", + "browser": true, "args": [], "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "trae-solo/user-rules.js", - "sourceFile": "trae-solo/user-rules.js" - }, - { - "site": "trae-solo", - "name": "workspaces-list", - "description": "List Trae SOLO workspaceStorage entries (~/Library/.../TRAE SOLO/User/workspaceStorage//), resolving each workspace.json to its single-folder path or multi-folder workspace target. Works while Trae is closed.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Workspace Id", - "Kind", - "Target", - "Modified", - "Id", - "Version", - "Source", - "Installed" + "logged_in", + "site", + "sec_uid", + "username", + "nickname" ], "type": "js", - "modulePath": "trae-solo/workspaces-fs.js", - "sourceFile": "trae-solo/workspaces-fs.js" + "modulePath": "tiktok/auth.js", + "sourceFile": "tiktok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "trip", @@ -19667,50 +17398,6 @@ "sourceFile": "web/fetch-browser.js", "navigateBefore": false }, - { - "site": "yahoo", - "name": "search", - "description": "Search Yahoo (powered by Bing)", - "access": "read", - "domain": "search.yahoo.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 7, - "required": false, - "help": "Number of results per page (max 7)" - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number (1, 2, 3...). Yahoo returns ~7 results per page" - } - ], - "columns": [ - "rank", - "title", - "url", - "snippet" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "yahoo/search.js", - "sourceFile": "yahoo/search.js" - }, { "site": "yahoo-finance", "name": "quote", diff --git a/clis/codex/dump.js b/clis/codex/dump.js deleted file mode 100644 index 9f3111a5..00000000 --- a/clis/codex/dump.js +++ /dev/null @@ -1,2 +0,0 @@ -import { makeDumpCommand } from '../_shared/desktop-commands.js'; -export const dumpCommand = makeDumpCommand('codex'); diff --git a/clis/cursor/dump.js b/clis/cursor/dump.js deleted file mode 100644 index 95f3121f..00000000 --- a/clis/cursor/dump.js +++ /dev/null @@ -1,2 +0,0 @@ -import { makeDumpCommand } from '../_shared/desktop-commands.js'; -export const dumpCommand = makeDumpCommand('cursor'); diff --git a/clis/cursor/screenshot.js b/clis/cursor/screenshot.js deleted file mode 100644 index a53ff45f..00000000 --- a/clis/cursor/screenshot.js +++ /dev/null @@ -1,2 +0,0 @@ -import { makeScreenshotCommand } from '../_shared/desktop-commands.js'; -export const screenshotCursor = makeScreenshotCommand('cursor'); diff --git a/plugin-command-manifest.json b/plugin-command-manifest.json index b5d8f4d5..99abce65 100644 --- a/plugin-command-manifest.json +++ b/plugin-command-manifest.json @@ -1214,6 +1214,257 @@ "modulePath": "plugins/bmwblog/search.js", "sourceFile": "plugins/bmwblog/search.js" }, + { + "site": "brave", + "name": "search", + "description": "Search Brave Search", + "access": "read", + "domain": "search.brave.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results per page (max 18)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Page offset (0, 1, 2...). Brave returns ~18 results per page" + } + ], + "columns": [ + "rank", + "title", + "url", + "snippet" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/brave/search.js", + "sourceFile": "plugins/brave/search.js" + }, + { + "site": "chatwise", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds to wait (default: 30)" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/chatwise/ask.js", + "sourceFile": "plugins/chatwise/ask.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "export", + "description": "Export the current ChatWise conversation to a Markdown file", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file (default: /tmp/chatwise-export.md)" + } + ], + "columns": [ + "Status", + "File", + "Messages" + ], + "type": "js", + "modulePath": "plugins/chatwise/export.js", + "sourceFile": "plugins/chatwise/export.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "history", + "description": "List conversation history in ChatWise sidebar", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Title" + ], + "type": "js", + "modulePath": "plugins/chatwise/history.js", + "sourceFile": "plugins/chatwise/history.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "model", + "description": "Get or switch the active AI model in ChatWise", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "model-name", + "type": "str", + "required": false, + "positional": true, + "help": "Model to switch to (e.g. gpt-4, claude-3)" + } + ], + "columns": [ + "Status", + "Model" + ], + "type": "js", + "modulePath": "plugins/chatwise/model.js", + "sourceFile": "plugins/chatwise/model.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "new", + "description": "Start a new ChatWise conversation session", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/chatwise/new.js", + "sourceFile": "plugins/chatwise/new.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "read", + "description": "Read the current ChatWise conversation history", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Content" + ], + "type": "js", + "modulePath": "plugins/chatwise/read.js", + "sourceFile": "plugins/chatwise/read.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "screenshot", + "description": "Capture a snapshot of the current ChatWise window (DOM + Accessibility tree)", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file path (default: /tmp/chatwise-snapshot.txt)" + } + ], + "columns": [ + "Status", + "File" + ], + "type": "js", + "modulePath": "plugins/chatwise/screenshot.js", + "sourceFile": "plugins/chatwise/screenshot.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "send", + "description": "Send a message to the active ChatWise conversation", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Message to send" + } + ], + "columns": [ + "Status", + "InjectedText" + ], + "type": "js", + "modulePath": "plugins/chatwise/send.js", + "sourceFile": "plugins/chatwise/send.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "status", + "description": "Check active CDP connection to ChatWise Desktop", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "plugins/chatwise/status.js", + "sourceFile": "plugins/chatwise/status.js", + "navigateBefore": true + }, { "site": "cincinnati", "name": "export-postgraduate-courses", @@ -1297,663 +1548,710 @@ "sourceFile": "plugins/cincinnati/export-postgraduate-courses.js" }, { - "site": "coingecko", - "name": "categories", - "description": "Crypto categories ranked by aggregated market cap", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, + "site": "codex", + "name": "archive", + "description": "Archive (Codex's term for delete) the selected conversation via the Chat actions header menu. No confirmation in UI — pass --yes to actually archive.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "sort", + "name": "yes", + "type": "boolean", + "default": false, + "required": false, + "help": "Actually archive (default: dry-run preview)" + }, + { + "name": "project", "type": "str", - "default": "market_cap_desc", "required": false, - "help": "Sort order (market_cap_desc / market_cap_asc / name_desc / name_asc / market_cap_change_24h_desc / market_cap_change_24h_asc)" + "help": "Project label or path to select before running the command" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "conversation", + "type": "str", "required": false, - "help": "Number of categories (1-100; CoinGecko returns ~120 max)" + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "rank", - "id", - "name", - "marketCap", - "volume24h", - "marketCapChange24hPct", - "top3Coins" + "status", + "thread_id", + "project", + "conversation" ], "type": "js", - "modulePath": "plugins/coingecko/categories.js", - "sourceFile": "plugins/coingecko/categories.js" + "modulePath": "plugins/codex/archive.js", + "sourceFile": "plugins/codex/archive.js", + "navigateBefore": true }, { - "site": "coingecko", - "name": "coin", - "description": "Fetch a single cryptocurrency's market data by CoinGecko id (e.g. bitcoin, ethereum).", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, + "site": "codex", + "name": "ask", + "description": "Send a prompt to the current or selected Codex conversation and wait for the AI response", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "id", - "type": "string", + "name": "text", + "type": "str", "required": true, "positional": true, - "help": "CoinGecko coin id (lowercase, e.g. bitcoin / ethereum / solana)." + "help": "Prompt to send" }, { - "name": "currency", - "type": "string", - "default": "usd", + "name": "timeout", + "type": "int", + "default": 60, "required": false, - "help": "Quote currency (usd, cny, eur, jpy, ...)." + "help": "Max seconds to wait for response (default: 60)" + }, + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "id", - "symbol", - "name", - "rank", - "price", - "marketCap", - "volume24h", - "change24hPct", - "change7dPct", - "change30dPct", - "ath", - "athDate", - "atl", - "atlDate", - "circulatingSupply", - "totalSupply", - "maxSupply", - "genesisDate", - "homepage" + "Role", + "Project", + "Conversation", + "Text" ], "type": "js", - "modulePath": "plugins/coingecko/coin.js", - "sourceFile": "plugins/coingecko/coin.js" + "modulePath": "plugins/codex/ask.js", + "sourceFile": "plugins/codex/ask.js", + "navigateBefore": true }, { - "site": "coingecko", - "name": "derivatives", - "description": "Top crypto derivative (perpetual / futures) markets by 24h volume", + "site": "codex", + "name": "dump", + "description": "Dump the DOM and Accessibility tree of codex for reverse-engineering", "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "action", + "files" + ], + "type": "js", + "modulePath": "plugins/codex/dump.js", + "sourceFile": "plugins/codex/dump.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "export", + "description": "Export the current Codex conversation to a Markdown file", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max rows to return (1-500; CoinGecko returns one large page)." - }, - { - "name": "symbol", - "type": "string", + "name": "output", + "type": "str", "required": false, - "help": "Optional symbol substring filter (e.g. \"BTC\", \"ETHUSDT\")." + "help": "Output file (default: /tmp/codex-export.md)" } ], "columns": [ - "rank", - "market", - "symbol", - "indexId", - "contractType", - "price", - "change24hPct", - "fundingRate", - "openInterestUsd", - "volume24hUsd", - "expired" + "Status", + "File", + "Messages" ], "type": "js", - "modulePath": "plugins/coingecko/derivatives.js", - "sourceFile": "plugins/coingecko/derivatives.js" + "modulePath": "plugins/codex/export.js", + "sourceFile": "plugins/codex/export.js", + "navigateBefore": true }, { - "site": "coingecko", - "name": "exchanges", - "description": "Top crypto exchanges by 24h BTC trading volume", + "site": "codex", + "name": "extract-diff", + "description": "Extract visual code review diff patches from Codex", "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "File", + "Diff" + ], + "type": "js", + "modulePath": "plugins/codex/extract-diff.js", + "sourceFile": "plugins/codex/extract-diff.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "history", + "description": "List visible Codex conversation threads grouped by project", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "project", + "type": "str", "required": false, - "help": "Number of exchanges (1-250, CoinGecko per_page upper bound)" + "help": "Filter by project label or path" }, { - "name": "page", - "type": "int", - "default": 1, + "name": "limit", + "type": "str", "required": false, - "help": "Page number (1-based)" + "help": "Max conversations per project" } ], "columns": [ - "rank", - "id", - "name", - "trustScore", - "volume24hBtc", - "country", - "yearEstablished", - "url" + "Project", + "Index", + "Title", + "Updated", + "Active" ], "type": "js", - "modulePath": "plugins/coingecko/exchanges.js", - "sourceFile": "plugins/coingecko/exchanges.js" + "modulePath": "plugins/codex/history.js", + "sourceFile": "plugins/codex/history.js", + "navigateBefore": true }, { - "site": "coingecko", - "name": "global", - "description": "Aggregate crypto market stats: total market cap, volume, dominance", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, + "site": "codex", + "name": "model", + "description": "Read, list, or switch the active model / reasoning level in Codex Desktop. The composer toolbar button toggles a menu that mixes model variants (GPT-5.5, Speed) with reasoning levels (Low/Medium/High/Extra High).", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "currency", - "type": "string", - "default": "usd", + "name": "name", + "type": "str", "required": false, - "help": "Quote currency for total market cap / volume (usd, cny, eur, jpy, ...)" + "positional": true, + "help": "Substring (case-insensitive) of a model / reasoning level to switch to. Omit to read current." + }, + { + "name": "list", + "type": "boolean", + "default": false, + "required": false, + "help": "List all menu options (does not switch)" } ], "columns": [ - "currency", - "totalMarketCap", - "totalVolume24h", - "marketCapChange24hPct", - "btcDominancePct", - "ethDominancePct", - "activeCryptocurrencies", - "markets", - "ongoingIcos", - "updatedAt" + "Status", + "Model" ], "type": "js", - "modulePath": "plugins/coingecko/global.js", - "sourceFile": "plugins/coingecko/global.js" + "modulePath": "plugins/codex/model.js", + "sourceFile": "plugins/codex/model.js", + "navigateBefore": true }, { - "site": "coingecko", - "name": "top", - "description": "Cryptocurrency quotes by market cap (default USD)", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, + "site": "codex", + "name": "new", + "description": "Start a new Codex conversation session", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/codex/new.js", + "sourceFile": "plugins/codex/new.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "pin", + "description": "Pin the selected Codex conversation via the Chat actions header menu.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "currency", - "type": "string", - "default": "usd", + "name": "project", + "type": "str", "required": false, - "help": "quote currency (usd / cny / eur / jpy ...)" + "help": "Project label or path to select before running the command" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "conversation", + "type": "str", "required": false, - "help": "Number to return (default 10, maximum 250)" + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "rank", - "symbol", - "name", - "price", - "change24hPct", - "marketCap", - "volume24h", - "high24h", - "low24h" + "status", + "thread_id", + "project", + "conversation" ], "type": "js", - "modulePath": "plugins/coingecko/top.js", - "sourceFile": "plugins/coingecko/top.js" + "modulePath": "plugins/codex/pin.js", + "sourceFile": "plugins/codex/pin.js", + "navigateBefore": true }, { - "site": "coingecko", - "name": "trending", - "description": "Top trending cryptocurrencies on CoinGecko in the last 24h (search-volume based).", + "site": "codex", + "name": "projects", + "description": "List Codex projects and visible conversations from the sidebar", "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [], + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "project", + "type": "str", + "required": false, + "help": "Filter by project label or path" + }, + { + "name": "limit", + "type": "str", + "required": false, + "help": "Max conversations per project" + } + ], "columns": [ - "rank", - "id", - "symbol", - "name", - "marketCapRank", - "priceBtc", - "thumb" + "Project", + "Index", + "Title", + "Updated", + "Active" ], "type": "js", - "modulePath": "plugins/coingecko/trending.js", - "sourceFile": "plugins/coingecko/trending.js" + "modulePath": "plugins/codex/projects.js", + "sourceFile": "plugins/codex/projects.js", + "navigateBefore": true }, { - "site": "concordia", - "name": "export-postgraduate-courses", - "description": "Export Concordia University Montreal postgraduate programs using official public sources.", + "site": "codex", + "name": "read", + "description": "Read the contents of the current or selected Codex conversation thread", "access": "read", - "example": "webcmd concordia export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.concordia.ca", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", + "name": "project", + "type": "str", "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "help": "Project label or path to select before running the command" }, { - "name": "count", - "type": "int", + "name": "conversation", + "type": "str", "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "Project", + "Conversation", + "Content" ], "type": "js", - "modulePath": "plugins/concordia/export-postgraduate-courses.js", - "sourceFile": "plugins/concordia/export-postgraduate-courses.js" + "modulePath": "plugins/codex/read.js", + "sourceFile": "plugins/codex/read.js", + "navigateBefore": true }, { - "site": "crates", - "name": "crate", - "description": "Single crates.io crate metadata (latest version, downloads, license, repo)", - "access": "read", - "domain": "crates.io", - "strategy": "public", - "browser": false, + "site": "codex", + "name": "rename", + "description": "Rename the selected Codex conversation. Opens the Chat actions menu → \"Rename chat\", then types the new title.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "name", + "name": "title", "type": "str", "required": true, "positional": true, - "help": "crates.io crate name (e.g. \"serde\", \"tokio\")" + "help": "New title (single line, no newlines)" + }, + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "name", - "latestVersion", - "description", - "downloads", - "recentDownloads", - "versions", - "license", - "homepage", - "documentation", - "repository", - "keywords", - "categories", - "created", - "updated", - "url" + "status", + "title", + "thread_id", + "project" ], "type": "js", - "modulePath": "plugins/crates/crate.js", - "sourceFile": "plugins/crates/crate.js" + "modulePath": "plugins/codex/rename.js", + "sourceFile": "plugins/codex/rename.js", + "navigateBefore": true }, { - "site": "crates", - "name": "search", - "description": "Search the public crates.io registry by keyword", + "site": "codex", + "name": "screenshot", + "description": "Capture a snapshot of the current Codex window (DOM + Accessibility tree)", "access": "read", - "domain": "crates.io", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", + "name": "output", "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"serde\", \"async runtime\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, "required": false, - "help": "Max results (1-100)" + "help": "Output file path (default: /tmp/codex-snapshot.txt)" } ], "columns": [ - "rank", - "name", - "latestVersion", - "description", - "downloads", - "recentDownloads", - "repository", - "updated", - "url" - ], - "tags": [ - "search" + "Status", + "File" ], "type": "js", - "modulePath": "plugins/crates/search.js", - "sourceFile": "plugins/crates/search.js" + "modulePath": "plugins/codex/screenshot.js", + "sourceFile": "plugins/codex/screenshot.js", + "navigateBefore": true }, { - "site": "dblp", - "name": "author", - "description": "List dblp publications by a given author (newest first; resolves to top PID match)", - "access": "read", - "domain": "dblp.org", - "strategy": "public", - "browser": false, + "site": "codex", + "name": "send", + "description": "Send text/commands to the current or selected Codex AI composer", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "author", + "name": "text", "type": "str", - "required": false, + "required": true, "positional": true, - "help": "Author name (e.g. \"Yoshua Bengio\"). Optional when --pid is given." + "help": "Text, command (e.g. /review), or skill (e.g. $imagegen)" }, { - "name": "pid", + "name": "project", "type": "str", "required": false, - "help": "Canonical dblp PID (e.g. \"56/953\"). Bypasses author search." + "help": "Project label or path to select before running the command" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "conversation", + "type": "str", "required": false, - "help": "Max publications (1-200)" + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "rank", - "key", - "title", - "authors", - "venue", - "year", - "type", - "doi", - "pid", - "url" + "Status", + "Project", + "Conversation", + "InjectedText" ], "type": "js", - "modulePath": "plugins/dblp/author.js", - "sourceFile": "plugins/dblp/author.js" + "modulePath": "plugins/codex/send.js", + "sourceFile": "plugins/codex/send.js", + "navigateBefore": true }, { - "site": "dblp", - "name": "paper", - "aliases": [ - "detail", - "view" - ], - "description": "Fetch a dblp record by canonical key (e.g. conf/nips/VaswaniSPUJGKP17)", + "site": "codex", + "name": "status", + "description": "Check active CDP connection to OpenAI Codex App", "access": "read", - "domain": "dblp.org", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "plugins/codex/status.js", + "sourceFile": "plugins/codex/status.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "unpin", + "description": "Unpin the selected Codex conversation via the Chat actions header menu.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "key", + "name": "project", "type": "str", - "required": true, - "positional": true, - "help": "dblp record key (round-tripped from the `key` column of `dblp search`)" + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "key", - "type", - "title", - "authors", - "venue", - "year", - "pages", - "doi", - "open_access_url", - "dblp_url" + "status", + "thread_id", + "project", + "conversation" ], "type": "js", - "modulePath": "plugins/dblp/paper.js", - "sourceFile": "plugins/dblp/paper.js" + "modulePath": "plugins/codex/pin.js", + "sourceFile": "plugins/codex/pin.js", + "navigateBefore": true }, { - "site": "dblp", - "name": "search", - "description": "Search dblp computer-science bibliography by free-text query", + "site": "coingecko", + "name": "categories", + "description": "Crypto categories ranked by aggregated market cap", "access": "read", - "domain": "dblp.org", + "domain": "api.coingecko.com", "strategy": "public", "browser": false, "args": [ { - "name": "query", + "name": "sort", "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (title / author / venue, e.g. \"attention is all you need\")" + "default": "market_cap_desc", + "required": false, + "help": "Sort order (market_cap_desc / market_cap_asc / name_desc / name_asc / market_cap_change_24h_desc / market_cap_change_24h_asc)" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max results (1-100, single dblp page)" + "help": "Number of categories (1-100; CoinGecko returns ~120 max)" } ], "columns": [ "rank", - "key", - "title", - "authors", - "venue", - "year", - "type", - "doi", - "url" - ], - "tags": [ - "search" + "id", + "name", + "marketCap", + "volume24h", + "marketCapChange24hPct", + "top3Coins" ], "type": "js", - "modulePath": "plugins/dblp/search.js", - "sourceFile": "plugins/dblp/search.js" + "modulePath": "plugins/coingecko/categories.js", + "sourceFile": "plugins/coingecko/categories.js" }, { - "site": "dblp", - "name": "venue", - "description": "Search dblp venue registry (conferences / journals) by name or acronym", + "site": "coingecko", + "name": "coin", + "description": "Fetch a single cryptocurrency's market data by CoinGecko id (e.g. bitcoin, ethereum).", "access": "read", - "domain": "dblp.org", + "domain": "api.coingecko.com", "strategy": "public", "browser": false, "args": [ { - "name": "query", - "type": "str", + "name": "id", + "type": "string", "required": true, "positional": true, - "help": "Venue name or acronym (e.g. \"ICLR\", \"neural networks\")" + "help": "CoinGecko coin id (lowercase, e.g. bitcoin / ethereum / solana)." }, { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max venues (1-100, single dblp page)" - } - ], - "columns": [ - "rank", - "acronym", - "venue", - "type", - "url" - ], - "type": "js", - "modulePath": "plugins/dblp/venue.js", - "sourceFile": "plugins/dblp/venue.js" - }, - { - "site": "defillama", - "name": "protocol", - "description": "Single DefiLlama protocol details (current TVL, mcap, chains, twitter, github, description)", - "access": "read", - "domain": "defillama.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "slug", + "name": "currency", "type": "string", - "required": true, - "positional": true, - "help": "DefiLlama protocol slug (e.g. \"aave\", \"lido\")" + "default": "usd", + "required": false, + "help": "Quote currency (usd, cny, eur, jpy, ...)." } ], "columns": [ - "slug", + "id", + "symbol", "name", - "category", - "isParent", - "tvl", - "tvlAt", - "mcap", - "chains", - "twitter", - "github", - "audits", - "listedAt", - "description", - "website", - "url" + "rank", + "price", + "marketCap", + "volume24h", + "change24hPct", + "change7dPct", + "change30dPct", + "ath", + "athDate", + "atl", + "atlDate", + "circulatingSupply", + "totalSupply", + "maxSupply", + "genesisDate", + "homepage" ], "type": "js", - "modulePath": "plugins/defillama/protocol.js", - "sourceFile": "plugins/defillama/protocol.js" + "modulePath": "plugins/coingecko/coin.js", + "sourceFile": "plugins/coingecko/coin.js" }, { - "site": "defillama", - "name": "protocols", - "description": "Top DeFi protocols on DefiLlama by current TVL (slug, name, category, TVL, mcap, change_1d/7d, chains)", + "site": "coingecko", + "name": "derivatives", + "description": "Top crypto derivative (perpetual / futures) markets by 24h volume", "access": "read", - "domain": "defillama.com", + "domain": "api.coingecko.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 30, + "default": 20, "required": false, - "help": "Number of rows to return (1-500)" + "help": "Max rows to return (1-500; CoinGecko returns one large page)." + }, + { + "name": "symbol", + "type": "string", + "required": false, + "help": "Optional symbol substring filter (e.g. \"BTC\", \"ETHUSDT\")." } ], "columns": [ "rank", - "slug", - "name", - "category", - "tvl", - "mcap", - "change_1d", - "change_7d", - "chains", - "listedAt", - "url" + "market", + "symbol", + "indexId", + "contractType", + "price", + "change24hPct", + "fundingRate", + "openInterestUsd", + "volume24hUsd", + "expired" ], "type": "js", - "modulePath": "plugins/defillama/protocols.js", - "sourceFile": "plugins/defillama/protocols.js" + "modulePath": "plugins/coingecko/derivatives.js", + "sourceFile": "plugins/coingecko/derivatives.js" }, { - "site": "devto", - "name": "latest", - "description": "Newest dev.to articles (firehose, all tags)", + "site": "coingecko", + "name": "exchanges", + "description": "Top crypto exchanges by 24h BTC trading volume", "access": "read", - "domain": "dev.to", + "domain": "api.coingecko.com", "strategy": "public", "browser": false, "args": [ @@ -1962,7 +2260,7 @@ "type": "int", "default": 20, "required": false, - "help": "Articles per page (1-100)" + "help": "Number of exchanges (1-250, CoinGecko per_page upper bound)" }, { "name": "page", @@ -1975,705 +2273,780 @@ "columns": [ "rank", "id", - "title", - "author", - "tags", - "reactions", - "comments", - "published", + "name", + "trustScore", + "volume24hBtc", + "country", + "yearEstablished", "url" ], "type": "js", - "modulePath": "plugins/devto/latest.js", - "sourceFile": "plugins/devto/latest.js" + "modulePath": "plugins/coingecko/exchanges.js", + "sourceFile": "plugins/coingecko/exchanges.js" }, { - "site": "devto", - "name": "read", - "description": "Read a DEV.to article body by id", + "site": "coingecko", + "name": "global", + "description": "Aggregate crypto market stats: total market cap, volume, dominance", "access": "read", - "domain": "dev.to", + "domain": "api.coingecko.com", "strategy": "public", "browser": false, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "DEV.to article id (numeric, e.g. 3605688)" - }, - { - "name": "max-length", - "type": "int", - "default": 20000, + "name": "currency", + "type": "string", + "default": "usd", "required": false, - "help": "Max characters of body to return (min 100)" + "help": "Quote currency for total market cap / volume (usd, cny, eur, jpy, ...)" } ], "columns": [ - "id", - "title", - "author", - "reactions", - "reading_time", - "tags", - "published_at", - "body", - "url" + "currency", + "totalMarketCap", + "totalVolume24h", + "marketCapChange24hPct", + "btcDominancePct", + "ethDominancePct", + "activeCryptocurrencies", + "markets", + "ongoingIcos", + "updatedAt" ], "type": "js", - "modulePath": "plugins/devto/read.js", - "sourceFile": "plugins/devto/read.js" + "modulePath": "plugins/coingecko/global.js", + "sourceFile": "plugins/coingecko/global.js" }, { - "site": "devto", - "name": "tag", - "description": "Latest DEV.to articles for a specific tag", + "site": "coingecko", + "name": "top", + "description": "Cryptocurrency quotes by market cap (default USD)", "access": "read", - "domain": "dev.to", + "domain": "api.coingecko.com", "strategy": "public", "browser": false, "args": [ { - "name": "tag", - "type": "str", - "required": true, - "positional": true, - "help": "Tag name (e.g. javascript, python, webdev)" + "name": "currency", + "type": "string", + "default": "usd", + "required": false, + "help": "quote currency (usd / cny / eur / jpy ...)" }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Number of articles" + "help": "Number to return (default 10, maximum 250)" } ], "columns": [ "rank", - "id", - "title", - "author", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", - "url" + "symbol", + "name", + "price", + "change24hPct", + "marketCap", + "volume24h", + "high24h", + "low24h" ], "type": "js", - "modulePath": "plugins/devto/tag.js", - "sourceFile": "plugins/devto/tag.js" + "modulePath": "plugins/coingecko/top.js", + "sourceFile": "plugins/coingecko/top.js" }, { - "site": "devto", - "name": "top", - "description": "Top DEV.to articles of the day", + "site": "coingecko", + "name": "trending", + "description": "Top trending cryptocurrencies on CoinGecko in the last 24h (search-volume based).", "access": "read", - "domain": "dev.to", + "domain": "api.coingecko.com", "strategy": "public", "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of articles" - } - ], + "args": [], "columns": [ "rank", "id", - "title", - "author", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", - "url" + "symbol", + "name", + "marketCapRank", + "priceBtc", + "thumb" ], "type": "js", - "modulePath": "plugins/devto/top.js", - "sourceFile": "plugins/devto/top.js" + "modulePath": "plugins/coingecko/trending.js", + "sourceFile": "plugins/coingecko/trending.js" }, { - "site": "devto", - "name": "user", - "description": "Recent DEV.to articles from a specific user", + "site": "concordia", + "name": "export-postgraduate-courses", + "description": "Export Concordia University Montreal postgraduate programs using official public sources.", "access": "read", - "domain": "dev.to", + "example": "webcmd concordia export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.concordia.ca", "strategy": "public", "browser": false, "args": [ { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "DEV.to username (e.g. ben, thepracticaldev)" + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" }, { - "name": "limit", + "name": "count", "type": "int", - "default": 20, "required": false, - "help": "Number of articles" + "help": "Positive maximum number of programs after filtering and deduplication" } ], "columns": [ - "rank", - "id", - "title", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", - "url" + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/devto/user.js", - "sourceFile": "plugins/devto/user.js" + "modulePath": "plugins/concordia/export-postgraduate-courses.js", + "sourceFile": "plugins/concordia/export-postgraduate-courses.js" }, { - "site": "dictionary", - "name": "examples", - "description": "Read real-world example sentences utilizing the word", + "site": "crates", + "name": "crate", + "description": "Single crates.io crate metadata (latest version, downloads, license, repo)", "access": "read", - "domain": "api.dictionaryapi.dev", + "domain": "crates.io", "strategy": "public", "browser": false, "args": [ { - "name": "word", - "type": "string", + "name": "name", + "type": "str", "required": true, "positional": true, - "help": "Word to get example sentences for" + "help": "crates.io crate name (e.g. \"serde\", \"tokio\")" } ], "columns": [ - "word", - "example" + "name", + "latestVersion", + "description", + "downloads", + "recentDownloads", + "versions", + "license", + "homepage", + "documentation", + "repository", + "keywords", + "categories", + "created", + "updated", + "url" ], "type": "js", - "modulePath": "plugins/dictionary/examples.js", - "sourceFile": "plugins/dictionary/examples.js" + "modulePath": "plugins/crates/crate.js", + "sourceFile": "plugins/crates/crate.js" }, { - "site": "dictionary", + "site": "crates", "name": "search", - "description": "Search the Free Dictionary API for definitions, parts of speech, and pronunciations.", + "description": "Search the public crates.io registry by keyword", "access": "read", - "domain": "api.dictionaryapi.dev", + "domain": "crates.io", "strategy": "public", "browser": false, "args": [ { - "name": "word", - "type": "string", + "name": "query", + "type": "str", "required": true, "positional": true, - "help": "Word to define (e.g., serendipity)" + "help": "Search keyword (e.g. \"serde\", \"async runtime\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" } ], "columns": [ - "word", - "phonetic", - "type", - "definition" + "rank", + "name", + "latestVersion", + "description", + "downloads", + "recentDownloads", + "repository", + "updated", + "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/dictionary/search.js", - "sourceFile": "plugins/dictionary/search.js" + "modulePath": "plugins/crates/search.js", + "sourceFile": "plugins/crates/search.js" }, { - "site": "dictionary", - "name": "synonyms", - "description": "Find synonyms for a specific word", - "access": "read", - "domain": "api.dictionaryapi.dev", - "strategy": "public", - "browser": false, + "site": "cursor", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "word", - "type": "string", + "name": "text", + "type": "str", "required": true, "positional": true, - "help": "Word to find synonyms for (e.g., serendipity)" + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds to wait for response (default: 30)" } ], "columns": [ - "word", - "synonyms" + "Role", + "Text" ], "type": "js", - "modulePath": "plugins/dictionary/synonyms.js", - "sourceFile": "plugins/dictionary/synonyms.js" + "modulePath": "plugins/cursor/ask.js", + "sourceFile": "plugins/cursor/ask.js", + "navigateBefore": true }, { - "site": "dockerhub", - "name": "image", - "description": "Fetch a Docker Hub repository's public metadata (stars, pulls, last updated, status)", - "access": "read", - "domain": "hub.docker.com", - "strategy": "public", - "browser": false, + "site": "cursor", + "name": "composer", + "description": "Send a prompt directly into Cursor Composer (Cmd+I shortcut)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "image", + "name": "text", "type": "str", "required": true, "positional": true, - "help": "Image name (e.g. \"nginx\", \"library/nginx\", \"bitnami/redis\")" + "help": "Text to send into Composer" } ], "columns": [ - "image", - "official", - "stars", - "pulls", - "description", - "lastUpdated", - "lastModified", - "registered", - "status", - "url" + "Status", + "InjectedText" ], "type": "js", - "modulePath": "plugins/dockerhub/image.js", - "sourceFile": "plugins/dockerhub/image.js" + "modulePath": "plugins/cursor/composer.js", + "sourceFile": "plugins/cursor/composer.js", + "navigateBefore": true }, { - "site": "dockerhub", - "name": "search", - "description": "Search Docker Hub repositories by keyword", + "site": "cursor", + "name": "dump", + "description": "Dump the DOM and Accessibility tree of cursor for reverse-engineering", "access": "read", - "domain": "hub.docker.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"nginx\", \"bitnami redis\")" - }, - { - "name": "limit", - "type": "int", - "default": 25, + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "action", + "files" + ], + "type": "js", + "modulePath": "plugins/cursor/dump.js", + "sourceFile": "plugins/cursor/dump.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "export", + "description": "Export the current cursor conversation to a Markdown file", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", "required": false, - "help": "Max repositories (1-100, single Docker Hub page)" + "help": "Output file (default: /tmp/cursor-export.md)" } ], "columns": [ - "rank", - "image", - "official", - "stars", - "pulls", - "description", - "url" + "Status", + "File", + "Messages" ], - "tags": [ - "search" + "type": "js", + "modulePath": "plugins/cursor/export.js", + "sourceFile": "plugins/cursor/export.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "extract-code", + "description": "Extract multi-line code blocks from the current Cursor conversation", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Code" ], "type": "js", - "modulePath": "plugins/dockerhub/search.js", - "sourceFile": "plugins/dockerhub/search.js" + "modulePath": "plugins/cursor/extract-code.js", + "sourceFile": "plugins/cursor/extract-code.js", + "navigateBefore": true }, { - "site": "endoflife", - "name": "product", - "description": "Release cycles + EOL / LTS / support dates for one product on endoflife.date", + "site": "cursor", + "name": "history", + "description": "List recent chat sessions from the Cursor sidebar", "access": "read", - "domain": "endoflife.date", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Title" + ], + "type": "js", + "modulePath": "plugins/cursor/history.js", + "sourceFile": "plugins/cursor/history.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "model", + "description": "Get or switch the currently active AI model in Cursor", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "product", - "type": "string", - "required": true, + "name": "model-name", + "type": "str", + "required": false, "positional": true, - "help": "endoflife.date product slug (e.g. \"nodejs\", \"python\", \"ubuntu\")" + "help": "The ID of the model to switch to (e.g. claude-3.5-sonnet)" } ], "columns": [ - "product", - "cycle", - "releaseDate", - "latest", - "latestReleaseDate", - "lts", - "support", - "eol", - "extendedSupport", - "eolStatus", - "url" + "Status", + "Model" ], "type": "js", - "modulePath": "plugins/endoflife/product.js", - "sourceFile": "plugins/endoflife/product.js" + "modulePath": "plugins/cursor/model.js", + "sourceFile": "plugins/cursor/model.js", + "navigateBefore": true }, { - "site": "flathub", - "name": "app", - "description": "Full Flathub appstream metadata for an app id (license, categories, latest release)", + "site": "cursor", + "name": "new", + "description": "Start a new Cursor chat or Composer session", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/cursor/new.js", + "sourceFile": "plugins/cursor/new.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "read", + "description": "Read the current Cursor chat/composer conversation history", "access": "read", - "domain": "flathub.org", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/cursor/read.js", + "sourceFile": "plugins/cursor/read.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "screenshot", + "description": "Capture a snapshot of the current cursor window (DOM + Accessibility tree)", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "appId", + "name": "output", "type": "str", - "required": true, - "positional": true, - "help": "AppStream id (e.g. \"org.mozilla.firefox\", \"org.gnome.Calculator\")" + "required": false, + "help": "Output file path (default: /tmp/cursor-snapshot.txt)" } ], "columns": [ - "appId", - "name", - "summary", - "developer", - "license", - "isFreeLicense", - "isEol", - "categories", - "keywords", - "latestVersion", - "latestReleaseDate", - "homepage", - "bugtracker", - "donation", - "url" + "Status", + "File" ], "type": "js", - "modulePath": "plugins/flathub/app.js", - "sourceFile": "plugins/flathub/app.js" + "modulePath": "plugins/cursor/screenshot.js", + "sourceFile": "plugins/cursor/screenshot.js", + "navigateBefore": true }, { - "site": "flathub", - "name": "search", - "description": "Search Flathub apps by keyword", - "access": "read", - "domain": "flathub.org", - "strategy": "public", - "browser": false, + "site": "cursor", + "name": "send", + "description": "Send a prompt directly into Cursor Composer/Chat", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", + "name": "text", "type": "str", "required": true, "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max apps (1-100)" + "help": "Text to send into Cursor" } ], "columns": [ - "rank", - "appId", - "name", - "summary", - "developer", - "license", - "isFreeLicense", - "mainCategories", - "installsLastMonth", - "updatedAt", - "url" + "Status", + "InjectedText" ], - "tags": [ - "search" + "type": "js", + "modulePath": "plugins/cursor/send.js", + "sourceFile": "plugins/cursor/send.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "status", + "description": "Check active CDP connection to Cursor AI Editor", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" ], "type": "js", - "modulePath": "plugins/flathub/search.js", - "sourceFile": "plugins/flathub/search.js" + "modulePath": "plugins/cursor/status.js", + "sourceFile": "plugins/cursor/status.js", + "navigateBefore": true }, { - "site": "github-trending", - "name": "repos", - "description": "GitHub Trending repositories (public, no login). Filter by --language and --since.", + "site": "dblp", + "name": "author", + "description": "List dblp publications by a given author (newest first; resolves to top PID match)", "access": "read", - "domain": "github.com", + "domain": "dblp.org", "strategy": "public", "browser": false, "args": [ { - "name": "since", - "type": "string", - "default": "daily", + "name": "author", + "type": "str", "required": false, - "help": "Time range: daily / weekly / monthly" + "positional": true, + "help": "Author name (e.g. \"Yoshua Bengio\"). Optional when --pid is given." }, { - "name": "language", - "type": "string", - "default": "", + "name": "pid", + "type": "str", "required": false, - "help": "Filter by programming language slug, e.g. python, rust, \"c++\"" + "help": "Canonical dblp PID (e.g. \"56/953\"). Bypasses author search." }, { "name": "limit", "type": "int", - "default": 25, + "default": 20, "required": false, - "help": "Number of repositories to return (max 25)" + "help": "Max publications (1-200)" } ], "columns": [ "rank", - "repo", - "description", - "language", - "stars", - "forks", - "starsSince", + "key", + "title", + "authors", + "venue", + "year", + "type", + "doi", + "pid", "url" ], "type": "js", - "modulePath": "plugins/github-trending/repos.js", - "sourceFile": "plugins/github-trending/repos.js" + "modulePath": "plugins/dblp/author.js", + "sourceFile": "plugins/dblp/author.js" }, { - "site": "goettingen", - "name": "export-postgraduate-courses", - "description": "Export University of Göttingen postgraduate programmes from the official A-Z API.", + "site": "dblp", + "name": "paper", + "aliases": [ + "detail", + "view" + ], + "description": "Fetch a dblp record by canonical key (e.g. conf/nips/VaswaniSPUJGKP17)", "access": "read", - "example": "webcmd goettingen export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.uni-goettingen.de", + "domain": "dblp.org", "strategy": "public", "browser": false, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Positive maximum number of programmes after filtering and deduplication" + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "dblp record key (round-tripped from the `key` column of `dblp search`)" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "key", + "type", + "title", + "authors", + "venue", + "year", + "pages", + "doi", + "open_access_url", + "dblp_url" ], "type": "js", - "modulePath": "plugins/goettingen/export-postgraduate-courses.js", - "sourceFile": "plugins/goettingen/export-postgraduate-courses.js" + "modulePath": "plugins/dblp/paper.js", + "sourceFile": "plugins/dblp/paper.js" }, { - "site": "goproxy", - "name": "module", - "description": "Latest version + VCS origin metadata for a Go module on proxy.golang.org", + "site": "dblp", + "name": "search", + "description": "Search dblp computer-science bibliography by free-text query", "access": "read", - "domain": "proxy.golang.org", + "domain": "dblp.org", "strategy": "public", "browser": false, "args": [ { - "name": "module", - "type": "string", + "name": "query", + "type": "str", "required": true, "positional": true, - "help": "Go module path (e.g. \"github.com/gin-gonic/gin\", \"golang.org/x/net\")" + "help": "Search keyword (title / author / venue, e.g. \"attention is all you need\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100, single dblp page)" } ], "columns": [ - "module", - "version", - "publishedAt", - "vcs", - "repository", - "commit", - "ref", - "pkgGoDevUrl", + "rank", + "key", + "title", + "authors", + "venue", + "year", + "type", + "doi", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/goproxy/module.js", - "sourceFile": "plugins/goproxy/module.js" + "modulePath": "plugins/dblp/search.js", + "sourceFile": "plugins/dblp/search.js" }, { - "site": "goproxy", - "name": "versions", - "description": "Published version tags for a Go module (newest first), optionally with publish times", + "site": "dblp", + "name": "venue", + "description": "Search dblp venue registry (conferences / journals) by name or acronym", "access": "read", - "domain": "proxy.golang.org", + "domain": "dblp.org", "strategy": "public", "browser": false, "args": [ { - "name": "module", - "type": "string", + "name": "query", + "type": "str", "required": true, "positional": true, - "help": "Go module path (e.g. \"github.com/gin-gonic/gin\")" + "help": "Venue name or acronym (e.g. \"ICLR\", \"neural networks\")" }, { "name": "limit", "type": "int", - "default": 30, - "required": false, - "help": "Max rows to return (1-200)" - }, - { - "name": "with-time", - "type": "boolean", - "default": false, + "default": 20, "required": false, - "help": "Fetch each version's publish time (one extra request per row)" + "help": "Max venues (1-100, single dblp page)" } ], "columns": [ "rank", - "module", - "version", - "publishedAt", + "acronym", + "venue", + "type", "url" ], "type": "js", - "modulePath": "plugins/goproxy/versions.js", - "sourceFile": "plugins/goproxy/versions.js" + "modulePath": "plugins/dblp/venue.js", + "sourceFile": "plugins/dblp/venue.js" }, { - "site": "hackernews", - "name": "ask", - "description": "Hacker News Ask HN posts", + "site": "defillama", + "name": "protocol", + "description": "Single DefiLlama protocol details (current TVL, mcap, chains, twitter, github, description)", "access": "read", - "domain": "news.ycombinator.com", + "domain": "defillama.com", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" + "name": "slug", + "type": "string", + "required": true, + "positional": true, + "help": "DefiLlama protocol slug (e.g. \"aave\", \"lido\")" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", + "slug", + "name", + "category", + "isParent", + "tvl", + "tvlAt", + "mcap", + "chains", + "twitter", + "github", + "audits", + "listedAt", + "description", + "website", "url" ], "type": "js", - "modulePath": "plugins/hackernews/ask.js", - "sourceFile": "plugins/hackernews/ask.js" + "modulePath": "plugins/defillama/protocol.js", + "sourceFile": "plugins/defillama/protocol.js" }, { - "site": "hackernews", - "name": "best", - "description": "Hacker News best stories", + "site": "defillama", + "name": "protocols", + "description": "Top DeFi protocols on DefiLlama by current TVL (slug, name, category, TVL, mcap, change_1d/7d, chains)", "access": "read", - "domain": "news.ycombinator.com", + "domain": "defillama.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 30, "required": false, - "help": "Number of stories" + "help": "Number of rows to return (1-500)" } ], "columns": [ "rank", - "id", - "title", - "score", - "author", - "comments", + "slug", + "name", + "category", + "tvl", + "mcap", + "change_1d", + "change_7d", + "chains", + "listedAt", "url" ], "type": "js", - "modulePath": "plugins/hackernews/best.js", - "sourceFile": "plugins/hackernews/best.js" + "modulePath": "plugins/defillama/protocols.js", + "sourceFile": "plugins/defillama/protocols.js" }, { - "site": "hackernews", - "name": "jobs", - "description": "Hacker News job postings", + "site": "devto", + "name": "latest", + "description": "Newest dev.to articles (firehose, all tags)", "access": "read", - "domain": "news.ycombinator.com", + "domain": "dev.to", "strategy": "public", "browser": false, "args": [ @@ -2682,7 +3055,14 @@ "type": "int", "default": 20, "required": false, - "help": "Number of job postings" + "help": "Articles per page (1-100)" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number (1-based)" } ], "columns": [ @@ -2690,599 +3070,578 @@ "id", "title", "author", + "tags", + "reactions", + "comments", + "published", "url" ], "type": "js", - "modulePath": "plugins/hackernews/jobs.js", - "sourceFile": "plugins/hackernews/jobs.js" + "modulePath": "plugins/devto/latest.js", + "sourceFile": "plugins/devto/latest.js" }, { - "site": "hackernews", - "name": "new", - "description": "Hacker News newest stories", + "site": "devto", + "name": "read", + "description": "Read a DEV.to article body by id", "access": "read", - "domain": "news.ycombinator.com", + "domain": "dev.to", "strategy": "public", "browser": false, "args": [ { - "name": "limit", + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "DEV.to article id (numeric, e.g. 3605688)" + }, + { + "name": "max-length", "type": "int", - "default": 20, + "default": 20000, "required": false, - "help": "Number of stories" + "help": "Max characters of body to return (min 100)" } ], "columns": [ - "rank", "id", "title", - "score", "author", - "comments", + "reactions", + "reading_time", + "tags", + "published_at", + "body", "url" ], "type": "js", - "modulePath": "plugins/hackernews/new.js", - "sourceFile": "plugins/hackernews/new.js" + "modulePath": "plugins/devto/read.js", + "sourceFile": "plugins/devto/read.js" }, { - "site": "hackernews", - "name": "read", - "description": "Read a Hacker News story and its comment tree", + "site": "devto", + "name": "tag", + "description": "Latest DEV.to articles for a specific tag", "access": "read", - "domain": "news.ycombinator.com", + "domain": "dev.to", "strategy": "public", "browser": false, "args": [ { - "name": "id", + "name": "tag", "type": "str", "required": true, "positional": true, - "help": "HN item ID (e.g. 39847301)" + "help": "Tag name (e.g. javascript, python, webdev)" }, { "name": "limit", "type": "int", - "default": 25, - "required": false, - "help": "Max top-level comments" - }, - { - "name": "depth", - "type": "int", - "default": 2, - "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" - }, - { - "name": "replies", - "type": "int", - "default": 5, - "required": false, - "help": "Max replies shown per comment at each level" - }, - { - "name": "max-length", - "type": "int", - "default": 2000, + "default": 20, "required": false, - "help": "Max characters per comment body (min 100)" + "help": "Number of articles" } ], "columns": [ - "type", + "rank", + "id", + "title", "author", - "score", - "text" + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" ], "type": "js", - "modulePath": "plugins/hackernews/read.js", - "sourceFile": "plugins/hackernews/read.js" + "modulePath": "plugins/devto/tag.js", + "sourceFile": "plugins/devto/tag.js" }, { - "site": "hackernews", - "name": "search", - "description": "Search Hacker News stories", + "site": "devto", + "name": "top", + "description": "Top DEV.to articles of the day", "access": "read", - "domain": "news.ycombinator.com", + "domain": "dev.to", "strategy": "public", "browser": false, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of results" - }, - { - "name": "sort", - "type": "str", - "default": "relevance", - "required": false, - "help": "Sort by relevance or date", - "choices": [ - "relevance", - "date" - ] + "help": "Number of articles" } ], "columns": [ "rank", "id", "title", - "score", "author", + "reactions", "comments", + "reading_time", + "published_at", + "tags", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/hackernews/search.js", - "sourceFile": "plugins/hackernews/search.js" + "modulePath": "plugins/devto/top.js", + "sourceFile": "plugins/devto/top.js" }, { - "site": "hackernews", - "name": "show", - "description": "Hacker News Show HN posts", + "site": "devto", + "name": "user", + "description": "Recent DEV.to articles from a specific user", "access": "read", - "domain": "news.ycombinator.com", + "domain": "dev.to", "strategy": "public", "browser": false, "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "DEV.to username (e.g. ben, thepracticaldev)" + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of stories" + "help": "Number of articles" } ], "columns": [ "rank", "id", "title", - "score", - "author", + "reactions", "comments", + "reading_time", + "published_at", + "tags", "url" ], "type": "js", - "modulePath": "plugins/hackernews/show.js", - "sourceFile": "plugins/hackernews/show.js" + "modulePath": "plugins/devto/user.js", + "sourceFile": "plugins/devto/user.js" }, { - "site": "hackernews", - "name": "top", - "description": "Hacker News top stories", + "site": "dictionary", + "name": "examples", + "description": "Read real-world example sentences utilizing the word", "access": "read", - "domain": "news.ycombinator.com", + "domain": "api.dictionaryapi.dev", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to get example sentences for" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" + "word", + "example" ], "type": "js", - "modulePath": "plugins/hackernews/top.js", - "sourceFile": "plugins/hackernews/top.js" + "modulePath": "plugins/dictionary/examples.js", + "sourceFile": "plugins/dictionary/examples.js" }, { - "site": "hackernews", - "name": "user", - "description": "Hacker News user profile", + "site": "dictionary", + "name": "search", + "description": "Search the Free Dictionary API for definitions, parts of speech, and pronunciations.", "access": "read", - "domain": "news.ycombinator.com", + "domain": "api.dictionaryapi.dev", "strategy": "public", "browser": false, "args": [ { - "name": "username", - "type": "str", + "name": "word", + "type": "string", "required": true, "positional": true, - "help": "HN username" + "help": "Word to define (e.g., serendipity)" } ], "columns": [ - "username", - "karma", - "created", - "about" + "word", + "phonetic", + "type", + "definition" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/hackernews/user.js", - "sourceFile": "plugins/hackernews/user.js" + "modulePath": "plugins/dictionary/search.js", + "sourceFile": "plugins/dictionary/search.js" }, { - "site": "heidelberg", - "name": "export-postgraduate-courses", - "description": "Export Heidelberg University non-bachelor/postgraduate programs using the official study finder.", + "site": "dictionary", + "name": "synonyms", + "description": "Find synonyms for a specific word", "access": "read", - "example": "webcmd heidelberg export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.uni-heidelberg.de", + "domain": "api.dictionaryapi.dev", "strategy": "public", "browser": false, "args": [ { - "name": "degree-level", + "name": "word", "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, + "required": true, + "positional": true, + "help": "Word to find synonyms for (e.g., serendipity)" + } + ], + "columns": [ + "word", + "synonyms" + ], + "type": "js", + "modulePath": "plugins/dictionary/synonyms.js", + "sourceFile": "plugins/dictionary/synonyms.js" + }, + { + "site": "dockerhub", + "name": "image", + "description": "Fetch a Docker Hub repository's public metadata (stars, pulls, last updated, status)", + "access": "read", + "domain": "hub.docker.com", + "strategy": "public", + "browser": false, + "args": [ { - "name": "count", - "type": "int", - "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image name (e.g. \"nginx\", \"library/nginx\", \"bitnami/redis\")" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "image", + "official", + "stars", + "pulls", + "description", + "lastUpdated", + "lastModified", + "registered", + "status", + "url" ], "type": "js", - "modulePath": "plugins/heidelberg/export-postgraduate-courses.js", - "sourceFile": "plugins/heidelberg/export-postgraduate-courses.js" + "modulePath": "plugins/dockerhub/image.js", + "sourceFile": "plugins/dockerhub/image.js" }, { - "site": "hft", - "name": "export-postgraduate-courses", - "description": "Export HFT Stuttgart postgraduate programs using the official public programme pages.", + "site": "dockerhub", + "name": "search", + "description": "Search Docker Hub repositories by keyword", "access": "read", - "example": "webcmd hft export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.hft-stuttgart.de", + "domain": "hub.docker.com", "strategy": "public", "browser": false, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"nginx\", \"bitnami redis\")" }, { - "name": "count", + "name": "limit", "type": "int", + "default": 25, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Max repositories (1-100, single Docker Hub page)" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "rank", + "image", + "official", + "stars", + "pulls", + "description", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/hft/export-postgraduate-courses.js", - "sourceFile": "plugins/hft/export-postgraduate-courses.js" + "modulePath": "plugins/dockerhub/search.js", + "sourceFile": "plugins/dockerhub/search.js" }, { - "site": "homebrew", - "name": "cask", - "description": "Fetch a Homebrew cask's metadata (version, homepage, deprecation, download URL)", + "site": "duckduckgo", + "name": "search", + "description": "Search DuckDuckGo", "access": "read", - "domain": "formulae.brew.sh", + "domain": "html.duckduckgo.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "token", + "name": "keyword", "type": "str", "required": true, "positional": true, - "help": "Cask token (e.g. \"firefox\", \"visual-studio-code\", \"google-chrome\")" + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results per page (1-10). For multi-page, use --offset" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Result offset for pagination (0, 10, 20...). Uses XHR POST internally" + }, + { + "name": "region", + "type": "str", + "required": false, + "help": "Region code (e.g. jp-jp, us-en, cn-zh). Default: all regions" + }, + { + "name": "time", + "type": "str", + "required": false, + "help": "Time range: d (day), w (week), m (month), y (year)" } ], "columns": [ - "cask", - "tap", - "name", - "version", - "description", - "homepage", - "deprecated", - "disabled", - "download", - "url" + "rank", + "title", + "url", + "snippet", + "displayUrl", + "icon", + "resultType" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/homebrew/cask.js", - "sourceFile": "plugins/homebrew/cask.js" + "modulePath": "plugins/duckduckgo/search.js", + "sourceFile": "plugins/duckduckgo/search.js" }, { - "site": "homebrew", - "name": "formula", - "description": "Fetch a Homebrew formula's metadata (version, license, deps, deprecation, source)", + "site": "duckduckgo", + "name": "suggest", + "description": "DuckDuckGo search suggestions", "access": "read", - "domain": "formulae.brew.sh", + "domain": "duckduckgo.com", "strategy": "public", "browser": false, "args": [ { - "name": "name", + "name": "keyword", "type": "str", "required": true, "positional": true, - "help": "Formula name (e.g. \"wget\", \"gcc@13\", \"imagemagick\")" + "help": "Search query prefix" + }, + { + "name": "limit", + "type": "int", + "default": 8, + "required": false, + "help": "Max number of suggestions" } ], "columns": [ - "formula", - "tap", - "version", - "license", - "description", - "homepage", - "dependencies", - "deprecated", - "disabled", - "source", - "url" + "phrase" ], "type": "js", - "modulePath": "plugins/homebrew/formula.js", - "sourceFile": "plugins/homebrew/formula.js" + "modulePath": "plugins/duckduckgo/suggest.js", + "sourceFile": "plugins/duckduckgo/suggest.js" }, { - "site": "homebrew", - "name": "popular", - "description": "List most-installed Homebrew formulae or casks (Homebrew's analytics ranking)", + "site": "endoflife", + "name": "product", + "description": "Release cycles + EOL / LTS / support dates for one product on endoflife.date", "access": "read", - "domain": "formulae.brew.sh", + "domain": "endoflife.date", "strategy": "public", "browser": false, "args": [ { - "name": "type", - "type": "str", - "default": "formula", - "required": false, - "help": "Package type (formula / cask)" - }, - { - "name": "window", - "type": "str", - "default": "30d", - "required": false, - "help": "Time window (30d / 90d / 365d)" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max rows (1-500)" + "name": "product", + "type": "string", + "required": true, + "positional": true, + "help": "endoflife.date product slug (e.g. \"nodejs\", \"python\", \"ubuntu\")" } ], "columns": [ - "rank", - "token", - "type", - "installs", - "percent", - "window", - "url" - ], - "type": "js", - "modulePath": "plugins/homebrew/popular.js", - "sourceFile": "plugins/homebrew/popular.js" + "product", + "cycle", + "releaseDate", + "latest", + "latestReleaseDate", + "lts", + "support", + "eol", + "extendedSupport", + "eolStatus", + "url" + ], + "type": "js", + "modulePath": "plugins/endoflife/product.js", + "sourceFile": "plugins/endoflife/product.js" }, { - "site": "iit", - "name": "export-postgraduate-courses", - "description": "Export Illinois Tech postgraduate programs using official public sources.", + "site": "flathub", + "name": "app", + "description": "Full Flathub appstream metadata for an app id (license, categories, latest release)", "access": "read", - "example": "webcmd iit export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.iit.edu", + "domain": "flathub.org", "strategy": "public", "browser": false, "args": [ { - "name": "degree-level", + "name": "appId", + "type": "str", + "required": true, + "positional": true, + "help": "AppStream id (e.g. \"org.mozilla.firefox\", \"org.gnome.Calculator\")" + } + ], + "columns": [ + "appId", + "name", + "summary", + "developer", + "license", + "isFreeLicense", + "isEol", + "categories", + "keywords", + "latestVersion", + "latestReleaseDate", + "homepage", + "bugtracker", + "donation", + "url" + ], + "type": "js", + "modulePath": "plugins/flathub/app.js", + "sourceFile": "plugins/flathub/app.js" + }, + { + "site": "flathub", + "name": "search", + "description": "Search Flathub apps by keyword", + "access": "read", + "domain": "flathub.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max apps (1-100)" + } + ], + "columns": [ + "rank", + "appId", + "name", + "summary", + "developer", + "license", + "isFreeLicense", + "mainCategories", + "installsLastMonth", + "updatedAt", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/flathub/search.js", + "sourceFile": "plugins/flathub/search.js" + }, + { + "site": "github-trending", + "name": "repos", + "description": "GitHub Trending repositories (public, no login). Filter by --language and --since.", + "access": "read", + "domain": "github.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "since", "type": "string", - "default": "all", + "default": "daily", "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "help": "Time range: daily / weekly / monthly" }, { - "name": "count", + "name": "language", + "type": "string", + "default": "", + "required": false, + "help": "Filter by programming language slug, e.g. python, rust, \"c++\"" + }, + { + "name": "limit", "type": "int", + "default": 25, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Number of repositories to return (max 25)" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "rank", + "repo", + "description", + "language", + "stars", + "forks", + "starsSince", + "url" ], "type": "js", - "modulePath": "plugins/iit/export-postgraduate-courses.js", - "sourceFile": "plugins/iit/export-postgraduate-courses.js" + "modulePath": "plugins/github-trending/repos.js", + "sourceFile": "plugins/github-trending/repos.js" }, { - "site": "jhu", + "site": "goettingen", "name": "export-postgraduate-courses", - "description": "Export Johns Hopkins University postgraduate programs using the official Academic Catalogue.", + "description": "Export University of Göttingen postgraduate programmes from the official A-Z API.", "access": "read", - "example": "webcmd jhu export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "e-catalogue.jhu.edu", + "example": "webcmd goettingen export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.uni-goettingen.de", "strategy": "public", "browser": false, "args": [ @@ -3297,7 +3656,7 @@ "name": "count", "type": "int", "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Positive maximum number of programmes after filtering and deduplication" } ], "columns": [ @@ -3355,425 +3714,475 @@ "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/jhu/export-postgraduate-courses.js", - "sourceFile": "plugins/jhu/export-postgraduate-courses.js" + "modulePath": "plugins/goettingen/export-postgraduate-courses.js", + "sourceFile": "plugins/goettingen/export-postgraduate-courses.js" }, { - "site": "jira", - "name": "attachments", - "description": "Jira issue attachment metadata", + "site": "google", + "name": "images", + "description": "Search Google Images for photos and image results", "access": "read", - "domain": "atlassian.net", + "domain": "google.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "key", + "name": "keyword", "type": "str", "required": true, "positional": true, - "help": "Jira issue key, e.g. PROJ-123" + "help": "Image search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of image results (1-100)" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language short code (e.g. en, zh)" + }, + { + "name": "resolve", + "type": "bool", + "default": true, + "required": false, + "help": "Click image previews to resolve original imgurl values" } ], "columns": [ - "id", - "filename", - "mimeType", - "size", - "url" + "rank", + "title", + "imageUrl", + "thumbnailUrl", + "sourceUrl", + "source", + "width", + "height" ], "type": "js", - "modulePath": "plugins/jira/attachments.js", - "sourceFile": "plugins/jira/attachments.js" + "modulePath": "plugins/google/images.js", + "sourceFile": "plugins/google/images.js", + "navigateBefore": false }, { - "site": "jira", - "name": "comments", - "description": "Jira issue comments as Markdown", + "site": "google", + "name": "news", + "description": "Get Google News headlines", "access": "read", - "domain": "atlassian.net", "strategy": "public", "browser": false, "args": [ { - "name": "key", + "name": "keyword", "type": "str", - "required": true, + "required": false, "positional": true, - "help": "Jira issue key, e.g. PROJ-123" + "help": "Search query (omit for top stories)" }, { "name": "limit", "type": "int", - "default": 50, + "default": 10, "required": false, - "help": "Max comments to return (1-100)" - } - ], - "columns": [ - "id", - "author", - "created", - "updated", - "markdown" - ], - "type": "js", - "modulePath": "plugins/jira/comments.js", - "sourceFile": "plugins/jira/comments.js" - }, - { - "site": "jira", - "name": "issue", - "description": "Jira issue detail normalized for agents (description, comments, attachments, links)", - "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ + "help": "Number of results" + }, { - "name": "key", + "name": "lang", "type": "str", - "required": true, - "positional": true, - "help": "Jira issue key, e.g. PROJ-123" + "default": "en", + "required": false, + "help": "Language short code (e.g. en, zh)" }, { - "name": "comments-limit", - "type": "int", - "default": 100, + "name": "region", + "type": "str", + "default": "US", "required": false, - "help": "Max comments to include (1-100)" + "help": "Region code (e.g. US, CN)" } ], "columns": [ - "key", - "summary", - "issueType", - "status", - "priority", - "assignee", - "updated", + "title", + "source", + "date", "url" ], "type": "js", - "modulePath": "plugins/jira/issue.js", - "sourceFile": "plugins/jira/issue.js" + "modulePath": "plugins/google/news.js", + "sourceFile": "plugins/google/news.js" }, { - "site": "jira", - "name": "links", - "description": "Jira issue links", + "site": "google", + "name": "search", + "description": "Search Google", "access": "read", - "domain": "atlassian.net", + "domain": "google.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "key", + "name": "keyword", "type": "str", "required": true, "positional": true, - "help": "Jira issue key, e.g. PROJ-123" + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results (1-100)" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language short code (e.g. en, zh)" } ], "columns": [ - "key", "type", - "direction" + "title", + "url", + "snippet" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/jira/links.js", - "sourceFile": "plugins/jira/links.js" + "modulePath": "plugins/google/search.js", + "sourceFile": "plugins/google/search.js" }, { - "site": "jira", - "name": "search", - "description": "Search Jira issues with JQL", + "site": "google", + "name": "suggest", + "description": "Get Google search suggestions", "access": "read", - "domain": "atlassian.net", "strategy": "public", "browser": false, "args": [ { - "name": "jql", + "name": "keyword", "type": "str", "required": true, "positional": true, - "help": "JQL query, e.g. \"project = PROJ order by updated desc\"" + "help": "Search query" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "lang", + "type": "str", + "default": "zh-CN", "required": false, - "help": "Max issues to return (1-100)" + "help": "Language code" } ], "columns": [ - "key", - "summary", - "issueType", - "status", - "priority", - "assignee", - "updated", - "url" - ], - "tags": [ - "search" + "suggestion" ], "type": "js", - "modulePath": "plugins/jira/search.js", - "sourceFile": "plugins/jira/search.js" + "modulePath": "plugins/google/suggest.js", + "sourceFile": "plugins/google/suggest.js" }, { - "site": "lesswrong", - "name": "comments", - "description": "Top comments on a post", + "site": "google", + "name": "trends", + "description": "Get Google Trends daily trending searches", "access": "read", - "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ { - "name": "url-or-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post URL or LessWrong post ID" + "name": "region", + "type": "str", + "default": "US", + "required": false, + "help": "Region code (e.g. US, CN, JP)" }, { "name": "limit", "type": "int", - "default": 5, + "default": 20, "required": false, - "help": "Number of comments" + "help": "Number of results" } ], "columns": [ - "rank", - "score", - "author", - "text" + "title", + "traffic", + "date" ], "type": "js", - "modulePath": "plugins/lesswrong/comments.js", - "sourceFile": "plugins/lesswrong/comments.js" + "modulePath": "plugins/google/trends.js", + "sourceFile": "plugins/google/trends.js" }, { - "site": "lesswrong", - "name": "curated", - "description": "Curated editor's picks", + "site": "google-scholar", + "name": "cite", + "description": "Get citation for a Google Scholar paper", "access": "read", - "domain": "www.lesswrong.com", + "domain": "scholar.google.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "limit", + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Paper title to search for" + }, + { + "name": "style", + "type": "str", + "default": "bibtex", + "required": false, + "help": "Citation format", + "choices": [ + "bibtex", + "endnote", + "refman", + "refworks" + ] + }, + { + "name": "index", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Number of results" + "help": "Which search result to cite (1-based)" } ], "columns": [ - "rank", "title", - "author", - "karma", - "comments", - "url" + "format", + "citation" ], "type": "js", - "modulePath": "plugins/lesswrong/curated.js", - "sourceFile": "plugins/lesswrong/curated.js" + "modulePath": "plugins/google-scholar/cite.js", + "sourceFile": "plugins/google-scholar/cite.js" }, { - "site": "lesswrong", - "name": "frontpage", - "description": "Algorithmic frontpage", + "site": "google-scholar", + "name": "profile", + "description": "View a Google Scholar author profile", "access": "read", - "domain": "www.lesswrong.com", + "domain": "scholar.google.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ + { + "name": "author", + "type": "str", + "required": true, + "positional": true, + "help": "Author name or Scholar user ID (e.g. JicYPdAAAAAJ)" + }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Number of results" + "help": "Max papers to show (max 20)" } ], "columns": [ "rank", "title", - "author", - "karma", - "comments", - "url" + "cited", + "year" ], "type": "js", - "modulePath": "plugins/lesswrong/frontpage.js", - "sourceFile": "plugins/lesswrong/frontpage.js" + "modulePath": "plugins/google-scholar/profile.js", + "sourceFile": "plugins/google-scholar/profile.js" }, { - "site": "lesswrong", - "name": "new", - "description": "Latest posts", + "site": "google-scholar", + "name": "search", + "description": "Google Scholar scholar search", "access": "read", - "domain": "www.lesswrong.com", + "domain": "scholar.google.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Number of results" + "help": "Number of results to return (max 20)" } ], "columns": [ "rank", "title", - "author", - "karma", - "comments", + "authors", + "source", + "year", + "cited", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/lesswrong/new.js", - "sourceFile": "plugins/lesswrong/new.js" + "modulePath": "plugins/google-scholar/search.js", + "sourceFile": "plugins/google-scholar/search.js" }, { - "site": "lesswrong", - "name": "read", - "description": "Read full post by URL or ID", + "site": "goproxy", + "name": "module", + "description": "Latest version + VCS origin metadata for a Go module on proxy.golang.org", "access": "read", - "domain": "www.lesswrong.com", + "domain": "proxy.golang.org", "strategy": "public", "browser": false, "args": [ { - "name": "url-or-id", + "name": "module", "type": "string", "required": true, "positional": true, - "help": "Post URL or LessWrong post ID" + "help": "Go module path (e.g. \"github.com/gin-gonic/gin\", \"golang.org/x/net\")" } ], "columns": [ - "title", - "author", - "karma", - "comments", - "tags", - "content", + "module", + "version", + "publishedAt", + "vcs", + "repository", + "commit", + "ref", + "pkgGoDevUrl", "url" ], "type": "js", - "modulePath": "plugins/lesswrong/read.js", - "sourceFile": "plugins/lesswrong/read.js" + "modulePath": "plugins/goproxy/module.js", + "sourceFile": "plugins/goproxy/module.js" }, { - "site": "lesswrong", - "name": "sequences", - "description": "List post collections", + "site": "goproxy", + "name": "versions", + "description": "Published version tags for a Go module (newest first), optionally with publish times", "access": "read", - "domain": "www.lesswrong.com", + "domain": "proxy.golang.org", "strategy": "public", "browser": false, "args": [ + { + "name": "module", + "type": "string", + "required": true, + "positional": true, + "help": "Go module path (e.g. \"github.com/gin-gonic/gin\")" + }, { "name": "limit", "type": "int", - "default": 10, + "default": 30, "required": false, - "help": "Number of results" + "help": "Max rows to return (1-200)" + }, + { + "name": "with-time", + "type": "boolean", + "default": false, + "required": false, + "help": "Fetch each version's publish time (one extra request per row)" } ], "columns": [ "rank", - "title", - "author" + "module", + "version", + "publishedAt", + "url" ], "type": "js", - "modulePath": "plugins/lesswrong/sequences.js", - "sourceFile": "plugins/lesswrong/sequences.js" + "modulePath": "plugins/goproxy/versions.js", + "sourceFile": "plugins/goproxy/versions.js" }, { - "site": "lesswrong", - "name": "shortform", - "description": "Quick takes / shortform posts", + "site": "hackernews", + "name": "ask", + "description": "Hacker News Ask HN posts", "access": "read", - "domain": "www.lesswrong.com", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results" + "help": "Number of stories" } ], "columns": [ "rank", + "id", "title", + "score", "author", - "karma", "comments", "url" ], "type": "js", - "modulePath": "plugins/lesswrong/shortform.js", - "sourceFile": "plugins/lesswrong/shortform.js" + "modulePath": "plugins/hackernews/ask.js", + "sourceFile": "plugins/hackernews/ask.js" }, { - "site": "lesswrong", - "name": "tag", - "description": "Posts by tag", + "site": "hackernews", + "name": "best", + "description": "Hacker News best stories", "access": "read", - "domain": "www.lesswrong.com", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ - { - "name": "tag", - "type": "string", - "required": true, - "positional": true, - "help": "Tag slug or name" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results" + "help": "Number of stories" } ], "columns": [ "rank", + "id", "title", + "score", "author", - "karma", "comments", "url" ], "type": "js", - "modulePath": "plugins/lesswrong/tag.js", - "sourceFile": "plugins/lesswrong/tag.js" + "modulePath": "plugins/hackernews/best.js", + "sourceFile": "plugins/hackernews/best.js" }, { - "site": "lesswrong", - "name": "tags", - "description": "List popular tags", + "site": "hackernews", + "name": "jobs", + "description": "Hacker News job postings", "access": "read", - "domain": "www.lesswrong.com", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ @@ -3782,337 +4191,1958 @@ "type": "int", "default": 20, "required": false, - "help": "Number of results" + "help": "Number of job postings" } ], "columns": [ "rank", - "name", - "posts" + "id", + "title", + "author", + "url" ], "type": "js", - "modulePath": "plugins/lesswrong/tags.js", - "sourceFile": "plugins/lesswrong/tags.js" + "modulePath": "plugins/hackernews/jobs.js", + "sourceFile": "plugins/hackernews/jobs.js" }, { - "site": "lesswrong", - "name": "top", - "description": "Top all-time", + "site": "hackernews", + "name": "new", + "description": "Hacker News newest stories", "access": "read", - "domain": "www.lesswrong.com", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results" + "help": "Number of stories" } ], "columns": [ "rank", + "id", "title", + "score", "author", - "karma", "comments", "url" ], "type": "js", - "modulePath": "plugins/lesswrong/top.js", - "sourceFile": "plugins/lesswrong/top.js" + "modulePath": "plugins/hackernews/new.js", + "sourceFile": "plugins/hackernews/new.js" }, { - "site": "lesswrong", - "name": "top-month", - "description": "Top this month", + "site": "hackernews", + "name": "read", + "description": "Read a Hacker News story and its comment tree", "access": "read", - "domain": "www.lesswrong.com", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "HN item ID (e.g. 39847301)" + }, { "name": "limit", "type": "int", - "default": 10, + "default": 25, + "required": false, + "help": "Max top-level comments" + }, + { + "name": "depth", + "type": "int", + "default": 2, + "required": false, + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + }, + { + "name": "replies", + "type": "int", + "default": 5, + "required": false, + "help": "Max replies shown per comment at each level" + }, + { + "name": "max-length", + "type": "int", + "default": 2000, + "required": false, + "help": "Max characters per comment body (min 100)" + } + ], + "columns": [ + "type", + "author", + "score", + "text" + ], + "type": "js", + "modulePath": "plugins/hackernews/read.js", + "sourceFile": "plugins/hackernews/read.js" + }, + { + "site": "hackernews", + "name": "search", + "description": "Search Hacker News stories", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, "required": false, "help": "Number of results" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance or date", + "choices": [ + "relevance", + "date" + ] } ], "columns": [ "rank", + "id", "title", + "score", "author", - "karma", "comments", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/lesswrong/top-month.js", - "sourceFile": "plugins/lesswrong/top-month.js" + "modulePath": "plugins/hackernews/search.js", + "sourceFile": "plugins/hackernews/search.js" }, { - "site": "lesswrong", - "name": "top-week", - "description": "Top this week", + "site": "hackernews", + "name": "show", + "description": "Hacker News Show HN posts", "access": "read", - "domain": "www.lesswrong.com", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results" + "help": "Number of stories" } ], "columns": [ "rank", + "id", "title", + "score", "author", - "karma", "comments", "url" ], "type": "js", - "modulePath": "plugins/lesswrong/top-week.js", - "sourceFile": "plugins/lesswrong/top-week.js" + "modulePath": "plugins/hackernews/show.js", + "sourceFile": "plugins/hackernews/show.js" }, { - "site": "lesswrong", - "name": "top-year", - "description": "Top this year", + "site": "hackernews", + "name": "top", + "description": "Hacker News top stories", "access": "read", - "domain": "www.lesswrong.com", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results" + "help": "Number of stories" } ], "columns": [ "rank", + "id", "title", + "score", "author", - "karma", "comments", "url" ], "type": "js", - "modulePath": "plugins/lesswrong/top-year.js", - "sourceFile": "plugins/lesswrong/top-year.js" + "modulePath": "plugins/hackernews/top.js", + "sourceFile": "plugins/hackernews/top.js" }, { - "site": "lesswrong", + "site": "hackernews", "name": "user", - "description": "User profile", + "description": "Hacker News user profile", "access": "read", - "domain": "www.lesswrong.com", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ { "name": "username", - "type": "string", + "type": "str", "required": true, "positional": true, - "help": "LessWrong username or slug" + "help": "HN username" } ], "columns": [ - "field", - "value" + "username", + "karma", + "created", + "about" ], "type": "js", - "modulePath": "plugins/lesswrong/user.js", - "sourceFile": "plugins/lesswrong/user.js" + "modulePath": "plugins/hackernews/user.js", + "sourceFile": "plugins/hackernews/user.js" }, { - "site": "lesswrong", - "name": "user-posts", - "description": "List a user's posts", + "site": "heidelberg", + "name": "export-postgraduate-courses", + "description": "Export Heidelberg University non-bachelor/postgraduate programs using the official study finder.", "access": "read", - "domain": "www.lesswrong.com", + "example": "webcmd heidelberg export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.uni-heidelberg.de", "strategy": "public", "browser": false, "args": [ { - "name": "username", + "name": "degree-level", "type": "string", - "required": true, - "positional": true, - "help": "LessWrong username or slug" + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" }, { - "name": "limit", + "name": "count", "type": "int", - "default": 10, "required": false, - "help": "Number of results" + "help": "Positive maximum number of programs after filtering and deduplication" } ], "columns": [ - "rank", - "title", - "karma", - "comments", - "date", - "url" + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/lesswrong/user-posts.js", - "sourceFile": "plugins/lesswrong/user-posts.js" + "modulePath": "plugins/heidelberg/export-postgraduate-courses.js", + "sourceFile": "plugins/heidelberg/export-postgraduate-courses.js" }, { - "site": "lichess", - "name": "top", - "description": "Top-N Lichess leaderboard for a perf type (bullet/blitz/rapid/classical/...)", + "site": "hft", + "name": "export-postgraduate-courses", + "description": "Export HFT Stuttgart postgraduate programs using the official public programme pages.", "access": "read", - "domain": "lichess.org", + "example": "webcmd hft export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.hft-stuttgart.de", "strategy": "public", "browser": false, "args": [ { - "name": "perf", - "type": "str", - "required": true, - "positional": true, - "help": "Perf type (bullet, blitz, rapid, classical, ultraBullet, chess960, ...)" + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" }, { - "name": "limit", + "name": "count", "type": "int", - "default": 10, "required": false, - "help": "Top-N rows (1-200)" + "help": "Positive maximum number of programs after filtering and deduplication" } ], "columns": [ - "rank", - "username", - "id", - "title", - "rating", - "progress", - "patron", - "url" + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/lichess/top.js", - "sourceFile": "plugins/lichess/top.js" + "modulePath": "plugins/hft/export-postgraduate-courses.js", + "sourceFile": "plugins/hft/export-postgraduate-courses.js" }, { - "site": "lichess", - "name": "user", - "description": "Fetch a Lichess player profile by username (rating, perfs, counts)", + "site": "homebrew", + "name": "cask", + "description": "Fetch a Homebrew cask's metadata (version, homepage, deprecation, download URL)", "access": "read", - "domain": "lichess.org", + "domain": "formulae.brew.sh", "strategy": "public", "browser": false, "args": [ { - "name": "username", + "name": "token", "type": "str", "required": true, "positional": true, - "help": "Lichess username (case-insensitive)" + "help": "Cask token (e.g. \"firefox\", \"visual-studio-code\", \"google-chrome\")" } ], "columns": [ - "username", - "id", - "title", - "patron", - "online", - "tosViolation", - "createdAt", - "seenAt", - "gamesAll", - "gamesWin", - "gamesLoss", - "gamesDraw", - "topPerfName", - "topPerfRating", - "topPerfGames", - "fideRating", - "country", - "bio", + "cask", + "tap", + "name", + "version", + "description", + "homepage", + "deprecated", + "disabled", + "download", "url" ], "type": "js", - "modulePath": "plugins/lichess/user.js", - "sourceFile": "plugins/lichess/user.js" + "modulePath": "plugins/homebrew/cask.js", + "sourceFile": "plugins/homebrew/cask.js" }, { - "site": "linkedin", - "name": "company", - "description": "Read a LinkedIn company page: industry, size, HQ, founded, website, followers, and about text", + "site": "homebrew", + "name": "formula", + "description": "Fetch a Homebrew formula's metadata (version, license, deps, deprecation, source)", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "formulae.brew.sh", + "strategy": "public", + "browser": false, "args": [ { - "name": "company", - "type": "string", + "name": "name", + "type": "str", "required": true, "positional": true, - "help": "Company universal name, /company/ path, or full URL" + "help": "Formula name (e.g. \"wget\", \"gcc@13\", \"imagemagick\")" } ], "columns": [ - "name", - "industry", - "size", - "headquarters", - "founded", - "website", - "specialties", - "followers", - "about", + "formula", + "tap", + "version", + "license", + "description", + "homepage", + "dependencies", + "deprecated", + "disabled", + "source", "url" ], "type": "js", - "modulePath": "plugins/linkedin/company.js", - "sourceFile": "plugins/linkedin/company.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/homebrew/formula.js", + "sourceFile": "plugins/homebrew/formula.js" }, { - "site": "linkedin", - "name": "connect", - "description": "Fail-closed LinkedIn connection request sender that verifies the exact profile before optionally sending a note", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "profile-url", + "site": "homebrew", + "name": "popular", + "description": "List most-installed Homebrew formulae or casks (Homebrew's analytics ranking)", + "access": "read", + "domain": "formulae.brew.sh", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "type", + "type": "str", + "default": "formula", + "required": false, + "help": "Package type (formula / cask)" + }, + { + "name": "window", + "type": "str", + "default": "30d", + "required": false, + "help": "Time window (30d / 90d / 365d)" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max rows (1-500)" + } + ], + "columns": [ + "rank", + "token", + "type", + "installs", + "percent", + "window", + "url" + ], + "type": "js", + "modulePath": "plugins/homebrew/popular.js", + "sourceFile": "plugins/homebrew/popular.js" + }, + { + "site": "iit", + "name": "export-postgraduate-courses", + "description": "Export Illinois Tech postgraduate programs using official public sources.", + "access": "read", + "example": "webcmd iit export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.iit.edu", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/iit/export-postgraduate-courses.js", + "sourceFile": "plugins/iit/export-postgraduate-courses.js" + }, + { + "site": "jhu", + "name": "export-postgraduate-courses", + "description": "Export Johns Hopkins University postgraduate programs using the official Academic Catalogue.", + "access": "read", + "example": "webcmd jhu export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "e-catalogue.jhu.edu", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/jhu/export-postgraduate-courses.js", + "sourceFile": "plugins/jhu/export-postgraduate-courses.js" + }, + { + "site": "jira", + "name": "attachments", + "description": "Jira issue attachment metadata", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "Jira issue key, e.g. PROJ-123" + } + ], + "columns": [ + "id", + "filename", + "mimeType", + "size", + "url" + ], + "type": "js", + "modulePath": "plugins/jira/attachments.js", + "sourceFile": "plugins/jira/attachments.js" + }, + { + "site": "jira", + "name": "comments", + "description": "Jira issue comments as Markdown", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "Jira issue key, e.g. PROJ-123" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max comments to return (1-100)" + } + ], + "columns": [ + "id", + "author", + "created", + "updated", + "markdown" + ], + "type": "js", + "modulePath": "plugins/jira/comments.js", + "sourceFile": "plugins/jira/comments.js" + }, + { + "site": "jira", + "name": "issue", + "description": "Jira issue detail normalized for agents (description, comments, attachments, links)", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "Jira issue key, e.g. PROJ-123" + }, + { + "name": "comments-limit", + "type": "int", + "default": 100, + "required": false, + "help": "Max comments to include (1-100)" + } + ], + "columns": [ + "key", + "summary", + "issueType", + "status", + "priority", + "assignee", + "updated", + "url" + ], + "type": "js", + "modulePath": "plugins/jira/issue.js", + "sourceFile": "plugins/jira/issue.js" + }, + { + "site": "jira", + "name": "links", + "description": "Jira issue links", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "Jira issue key, e.g. PROJ-123" + } + ], + "columns": [ + "key", + "type", + "direction" + ], + "type": "js", + "modulePath": "plugins/jira/links.js", + "sourceFile": "plugins/jira/links.js" + }, + { + "site": "jira", + "name": "search", + "description": "Search Jira issues with JQL", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "jql", + "type": "str", + "required": true, + "positional": true, + "help": "JQL query, e.g. \"project = PROJ order by updated desc\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max issues to return (1-100)" + } + ], + "columns": [ + "key", + "summary", + "issueType", + "status", + "priority", + "assignee", + "updated", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/jira/search.js", + "sourceFile": "plugins/jira/search.js" + }, + { + "site": "lesswrong", + "name": "comments", + "description": "Top comments on a post", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "url-or-id", + "type": "string", + "required": true, + "positional": true, + "help": "Post URL or LessWrong post ID" + }, + { + "name": "limit", + "type": "int", + "default": 5, + "required": false, + "help": "Number of comments" + } + ], + "columns": [ + "rank", + "score", + "author", + "text" + ], + "type": "js", + "modulePath": "plugins/lesswrong/comments.js", + "sourceFile": "plugins/lesswrong/comments.js" + }, + { + "site": "lesswrong", + "name": "curated", + "description": "Curated editor's picks", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/curated.js", + "sourceFile": "plugins/lesswrong/curated.js" + }, + { + "site": "lesswrong", + "name": "frontpage", + "description": "Algorithmic frontpage", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/frontpage.js", + "sourceFile": "plugins/lesswrong/frontpage.js" + }, + { + "site": "lesswrong", + "name": "new", + "description": "Latest posts", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/new.js", + "sourceFile": "plugins/lesswrong/new.js" + }, + { + "site": "lesswrong", + "name": "read", + "description": "Read full post by URL or ID", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "url-or-id", + "type": "string", + "required": true, + "positional": true, + "help": "Post URL or LessWrong post ID" + } + ], + "columns": [ + "title", + "author", + "karma", + "comments", + "tags", + "content", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/read.js", + "sourceFile": "plugins/lesswrong/read.js" + }, + { + "site": "lesswrong", + "name": "sequences", + "description": "List post collections", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author" + ], + "type": "js", + "modulePath": "plugins/lesswrong/sequences.js", + "sourceFile": "plugins/lesswrong/sequences.js" + }, + { + "site": "lesswrong", + "name": "shortform", + "description": "Quick takes / shortform posts", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/shortform.js", + "sourceFile": "plugins/lesswrong/shortform.js" + }, + { + "site": "lesswrong", + "name": "tag", + "description": "Posts by tag", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "tag", + "type": "string", + "required": true, + "positional": true, + "help": "Tag slug or name" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/tag.js", + "sourceFile": "plugins/lesswrong/tag.js" + }, + { + "site": "lesswrong", + "name": "tags", + "description": "List popular tags", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "name", + "posts" + ], + "type": "js", + "modulePath": "plugins/lesswrong/tags.js", + "sourceFile": "plugins/lesswrong/tags.js" + }, + { + "site": "lesswrong", + "name": "top", + "description": "Top all-time", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/top.js", + "sourceFile": "plugins/lesswrong/top.js" + }, + { + "site": "lesswrong", + "name": "top-month", + "description": "Top this month", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/top-month.js", + "sourceFile": "plugins/lesswrong/top-month.js" + }, + { + "site": "lesswrong", + "name": "top-week", + "description": "Top this week", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/top-week.js", + "sourceFile": "plugins/lesswrong/top-week.js" + }, + { + "site": "lesswrong", + "name": "top-year", + "description": "Top this year", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/top-year.js", + "sourceFile": "plugins/lesswrong/top-year.js" + }, + { + "site": "lesswrong", + "name": "user", + "description": "User profile", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "LessWrong username or slug" + } + ], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "plugins/lesswrong/user.js", + "sourceFile": "plugins/lesswrong/user.js" + }, + { + "site": "lesswrong", + "name": "user-posts", + "description": "List a user's posts", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "LessWrong username or slug" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "karma", + "comments", + "date", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/user-posts.js", + "sourceFile": "plugins/lesswrong/user-posts.js" + }, + { + "site": "lichess", + "name": "top", + "description": "Top-N Lichess leaderboard for a perf type (bullet/blitz/rapid/classical/...)", + "access": "read", + "domain": "lichess.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "perf", + "type": "str", + "required": true, + "positional": true, + "help": "Perf type (bullet, blitz, rapid, classical, ultraBullet, chess960, ...)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Top-N rows (1-200)" + } + ], + "columns": [ + "rank", + "username", + "id", + "title", + "rating", + "progress", + "patron", + "url" + ], + "type": "js", + "modulePath": "plugins/lichess/top.js", + "sourceFile": "plugins/lichess/top.js" + }, + { + "site": "lichess", + "name": "user", + "description": "Fetch a Lichess player profile by username (rating, perfs, counts)", + "access": "read", + "domain": "lichess.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Lichess username (case-insensitive)" + } + ], + "columns": [ + "username", + "id", + "title", + "patron", + "online", + "tosViolation", + "createdAt", + "seenAt", + "gamesAll", + "gamesWin", + "gamesLoss", + "gamesDraw", + "topPerfName", + "topPerfRating", + "topPerfGames", + "fideRating", + "country", + "bio", + "url" + ], + "type": "js", + "modulePath": "plugins/lichess/user.js", + "sourceFile": "plugins/lichess/user.js" + }, + { + "site": "linkedin", + "name": "company", + "description": "Read a LinkedIn company page: industry, size, HQ, founded, website, followers, and about text", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "company", + "type": "string", + "required": true, + "positional": true, + "help": "Company universal name, /company/ path, or full URL" + } + ], + "columns": [ + "name", + "industry", + "size", + "headquarters", + "founded", + "website", + "specialties", + "followers", + "about", + "url" + ], + "type": "js", + "modulePath": "plugins/linkedin/company.js", + "sourceFile": "plugins/linkedin/company.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "connect", + "description": "Fail-closed LinkedIn connection request sender that verifies the exact profile before optionally sending a note", + "access": "write", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": true, + "positional": true, + "help": "Exact LinkedIn profile URL to open and verify" + }, + { + "name": "expected-name", + "type": "string", + "required": true, + "help": "Expected visible profile name" + }, + { + "name": "note", + "type": "string", + "default": "", + "required": false, + "help": "Optional connection note, max 300 chars" + }, + { + "name": "send", + "type": "bool", + "default": false, + "required": false, + "help": "Actually click Send. Default is dry-run verification only." + } + ], + "columns": [ + "status", + "recipient", + "reason", + "profile_url", + "note_chars", + "connectable", + "delivery_verified", + "matched_invitation_name", + "matched_invitation_url", + "actualValue", + "blockReason", + "expectedValue", + "observedUrl", + "safety" + ], + "type": "js", + "modulePath": "plugins/linkedin/connect.js", + "sourceFile": "plugins/linkedin/connect.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "connections", + "description": "List your LinkedIn first-degree connections with names, headlines, and profile URLs", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of connections to return (max 500)" + } + ], + "columns": [ + "rank", + "name", + "occupation", + "public_id", + "connected_at", + "url" + ], + "type": "js", + "modulePath": "plugins/linkedin/connections.js", + "sourceFile": "plugins/linkedin/connections.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "inbox", + "description": "List LinkedIn messaging inbox conversations and unread messages", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 40, + "required": false, + "help": "Maximum conversations to return (1-100)" + }, + { + "name": "unread-only", + "type": "bool", + "default": false, + "required": false, + "help": "Return only conversations with unread messages" + } + ], + "columns": [ + "rank", + "thread_url", + "thread_id", + "person_name", + "last_message_preview", + "unread", + "counterparty_type", + "category", + "timestamp" + ], + "type": "js", + "modulePath": "plugins/linkedin/inbox.js", + "sourceFile": "plugins/linkedin/inbox.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "job-detail", + "description": "Read one LinkedIn job page with description, apply URL, workplace type, applicants, and company metadata", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "job-url", + "type": "string", + "required": true, + "positional": true, + "help": "Exact LinkedIn job URL, e.g. https://www.linkedin.com/jobs/view/123/" + } + ], + "columns": [ + "title", + "company", + "location", + "workplace_type", + "job_type", + "applicants", + "listed", + "apply_url", + "company_url", + "url", + "description" + ], + "type": "js", + "modulePath": "plugins/linkedin/job-detail.js", + "sourceFile": "plugins/linkedin/job-detail.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "jobs-preferences", + "description": "Read visible LinkedIn Jobs preferences and alert settings without changing them", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "open_to_work", + "job_titles", + "locations", + "job_alerts", + "preferences_url", + "alerts_url", + "raw_preferences" + ], + "type": "js", + "modulePath": "plugins/linkedin/jobs-preferences.js", + "sourceFile": "plugins/linkedin/jobs-preferences.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "login", + "description": "Open linkedin login", + "access": "write", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "public_id", + "plain_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/linkedin/auth.js", + "sourceFile": "plugins/linkedin/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "linkedin", + "name": "people-search", + "description": "Search standard LinkedIn (not Sales Navigator) for people by keyword. Each invocation consumes against LinkedIn's monthly Commercial Use Limit on people search; throttle accordingly.", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "keywords", + "type": "string", + "required": true, + "positional": true, + "help": "People search keywords, e.g. \"site reliability engineer berlin\"" + }, + { + "name": "limit", + "type": "int", + "default": 5, + "required": false, + "help": "Maximum people to return (1-10); each query counts toward LinkedIn's monthly CUL" + } + ], + "columns": [ + "rank", + "name", + "headline", + "location", + "profile_url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/linkedin/people-search.js", + "sourceFile": "plugins/linkedin/people-search.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "post-analytics", + "description": "Summarize raw visible LinkedIn post counters without custom scoring or classification", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Maximum posts to summarize (1-100)" + } + ], + "columns": [ + "posts_analyzed", + "total_reactions", + "total_comments", + "total_reposts", + "total_impressions", + "posts_with_media", + "posts_with_urls", + "latest_posted_at", + "latest_reactions", + "latest_comments", + "latest_reposts", + "latest_impressions", + "latest_url" + ], + "type": "js", + "modulePath": "plugins/linkedin/post-analytics.js", + "sourceFile": "plugins/linkedin/post-analytics.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "post-comments", + "description": "List unique commenters and reply authors from one exact LinkedIn post URL", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "post-url", + "type": "string", + "required": true, + "positional": true, + "help": "Exact LinkedIn post URL" + }, + { + "name": "limit", + "type": "int", + "required": false, + "help": "Maximum unique commenters to return; omit to fetch all" + } + ], + "columns": [ + "rank", + "name", + "headline", + "profile_url", + "comment_count", + "sample_comment", + "commented_at", + "source_post" + ], + "type": "js", + "modulePath": "plugins/linkedin/post-comments.js", + "sourceFile": "plugins/linkedin/post-comments.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "posts", + "description": "Export visible posts from a LinkedIn profile activity page with engagement metrics", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum posts to return (1-100)" + } + ], + "columns": [ + "rank", + "author", + "posted_at", + "body", + "reactions", + "comments", + "reposts", + "impressions", + "media", + "media_urls", + "url", + "raw_text" + ], + "type": "js", + "modulePath": "plugins/linkedin/posts.js", + "sourceFile": "plugins/linkedin/posts.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "profile-analytics", + "description": "Read visible LinkedIn profile dashboard metrics such as profile views, post impressions, and search appearances", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], + "columns": [ + "profile_url", + "profile_views", + "post_impressions", + "search_appearances", + "followers", + "connections", + "raw_analytics" + ], + "type": "js", + "modulePath": "plugins/linkedin/profile-analytics.js", + "sourceFile": "plugins/linkedin/profile-analytics.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "profile-experience", + "description": "Read visible LinkedIn profile experience entries with titles, dates, locations, skills, media, and URLs", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], + "columns": [ + "rank", + "total_count", + "title", + "employment_type", + "company", + "date_range", + "start_date", + "end_date", + "location", + "location_type", + "description", + "skills", + "media", + "urls", + "skill_url", + "media_url", + "profile_url", + "raw_text" + ], + "type": "js", + "modulePath": "plugins/linkedin/profile-experience.js", + "sourceFile": "plugins/linkedin/profile-experience.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "profile-projects", + "description": "Read visible LinkedIn profile projects with descriptions, dates, skills, media, and URLs", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], + "columns": [ + "rank", + "title", + "date_range", + "associated_with", + "description", + "skills", + "media", + "urls", + "profile_url", + "raw_text" + ], + "type": "js", + "modulePath": "plugins/linkedin/profile-projects.js", + "sourceFile": "plugins/linkedin/profile-projects.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "profile-read", + "description": "Read visible LinkedIn profile sections: headline, About, experience, education, services, and featured sections", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], + "columns": [ + "profile_url", + "name", + "headline", + "location", + "about", + "about_character_count", + "about_skills", + "experience", + "education", + "services", + "featured" + ], + "type": "js", + "modulePath": "plugins/linkedin/profile-read.js", + "sourceFile": "plugins/linkedin/profile-read.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "safe-send", + "description": "Fail-closed LinkedIn message sender that verifies exact thread, recipient, and latest message before filling/sending", + "access": "write", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "thread-url", + "type": "str", "required": true, - "positional": true, - "help": "Exact LinkedIn profile URL to open and verify" + "help": "Exact LinkedIn messaging thread URL to open and verify" }, { "name": "expected-name", - "type": "string", + "type": "str", "required": true, - "help": "Expected visible profile name" + "help": "Expected visible recipient name in the active thread header" }, { - "name": "note", - "type": "string", - "default": "", + "name": "message", + "type": "str", + "required": true, + "help": "Message body to send or dry-run" + }, + { + "name": "expected-last-text", + "type": "str", "required": false, - "help": "Optional connection note, max 300 chars" + "help": "Substring expected in the currently visible latest conversation context" + }, + { + "name": "expected-last-hash", + "type": "str", + "required": false, + "help": "SHA-256 hash of expected latest visible message text" }, { "name": "send", @@ -4120,189 +6150,149 @@ "default": false, "required": false, "help": "Actually click Send. Default is dry-run verification only." + }, + { + "name": "screenshot", + "type": "bool", + "default": false, + "required": false, + "help": "Capture a screenshot during verification" } ], "columns": [ "status", "recipient", "reason", - "profile_url", - "note_chars", - "connectable", - "delivery_verified", - "matched_invitation_name", - "matched_invitation_url", - "actualValue", - "blockReason", - "expectedValue", - "observedUrl", - "safety" + "thread_url", + "message_chars", + "screenshot" ], "type": "js", - "modulePath": "plugins/linkedin/connect.js", - "sourceFile": "plugins/linkedin/connect.js", + "modulePath": "plugins/linkedin/safe-send.js", + "sourceFile": "plugins/linkedin/safe-send.js", "navigateBefore": true }, { "site": "linkedin", - "name": "connections", - "description": "List your LinkedIn first-degree connections with names, headlines, and profile URLs", + "name": "salesnav-inbox", + "description": "List LinkedIn Sales Navigator message conversations with API pagination", "access": "read", "domain": "www.linkedin.com", - "strategy": "cookie", + "strategy": "ui", "browser": true, "args": [ { "name": "limit", - "type": "int", - "default": 20, + "type": "number", + "default": 40, "required": false, - "help": "Number of connections to return (max 500)" - } - ], - "columns": [ - "rank", - "name", - "occupation", - "public_id", - "connected_at", - "url" - ], - "type": "js", - "modulePath": "plugins/linkedin/connections.js", - "sourceFile": "plugins/linkedin/connections.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "inbox", - "description": "List LinkedIn messaging inbox conversations and unread messages", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ + "help": "Maximum conversations to return (1-500)" + }, { - "name": "limit", - "type": "int", - "default": 40, + "name": "max-pages", + "type": "number", + "default": 30, "required": false, - "help": "Maximum conversations to return (1-100)" + "help": "Maximum Sales Navigator API pages to fetch" }, { "name": "unread-only", "type": "bool", "default": false, "required": false, - "help": "Return only conversations with unread messages" + "help": "Return only unread conversations" } ], "columns": [ "rank", - "thread_url", "thread_id", + "thread_url", "person_name", - "last_message_preview", + "last_message_snippet", + "last_activity_time", "unread", - "counterparty_type", - "category", - "timestamp" + "unread_count", + "total_message_count", + "archived", + "participants", + "next_page_starts_at" ], "type": "js", - "modulePath": "plugins/linkedin/inbox.js", - "sourceFile": "plugins/linkedin/inbox.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/linkedin/salesnav-inbox.js", + "sourceFile": "plugins/linkedin/salesnav-inbox.js", + "navigateBefore": true }, { "site": "linkedin", - "name": "job-detail", - "description": "Read one LinkedIn job page with description, apply URL, workplace type, applicants, and company metadata", - "access": "read", + "name": "salesnav-message", + "description": "Send or dry-run a LinkedIn Sales Navigator InMail to a lead using the Sales Navigator messaging API", + "access": "write", "domain": "www.linkedin.com", - "strategy": "cookie", + "strategy": "ui", "browser": true, "args": [ { - "name": "job-url", + "name": "recipient", "type": "string", "required": true, "positional": true, - "help": "Exact LinkedIn job URL, e.g. https://www.linkedin.com/jobs/view/123/" + "help": "Sales Navigator lead URL, LinkedIn /in/ URL from salesnav-search, or urn:li:fs_salesProfile:(...)" + }, + { + "name": "subject", + "type": "string", + "required": true, + "help": "InMail subject" + }, + { + "name": "body", + "type": "string", + "required": true, + "help": "InMail body" + }, + { + "name": "send", + "type": "bool", + "default": false, + "required": false, + "help": "Actually send the InMail. Default is dry-run validation only." + }, + { + "name": "copy-to-crm", + "type": "bool", + "default": false, + "required": false, + "help": "Set Sales Navigator copyToCrm on the message request" } ], "columns": [ + "status", + "recipient", "title", "company", - "location", - "workplace_type", - "job_type", - "applicants", - "listed", - "apply_url", - "company_url", - "url", - "description" - ], - "type": "js", - "modulePath": "plugins/linkedin/job-detail.js", - "sourceFile": "plugins/linkedin/job-detail.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "jobs-preferences", - "description": "Read visible LinkedIn Jobs preferences and alert settings without changing them", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "open_to_work", - "job_titles", - "locations", - "job_alerts", - "preferences_url", - "alerts_url", - "raw_preferences" - ], - "type": "js", - "modulePath": "plugins/linkedin/jobs-preferences.js", - "sourceFile": "plugins/linkedin/jobs-preferences.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "login", - "description": "Open linkedin login", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "public_id", - "plain_id", - "name", - "action", - "verify_command" + "credits_remaining", + "credits_before", + "credits_after", + "sent_in_salesnav", + "message_chars", + "subject_chars", + "recipient_urn", + "degree", + "inmail_restriction", + "open_link" ], "type": "js", - "modulePath": "plugins/linkedin/auth.js", - "sourceFile": "plugins/linkedin/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/linkedin/salesnav-message.js", + "sourceFile": "plugins/linkedin/salesnav-message.js", + "navigateBefore": true }, { "site": "linkedin", - "name": "people-search", - "description": "Search standard LinkedIn (not Sales Navigator) for people by keyword. Each invocation consumes against LinkedIn's monthly Commercial Use Limit on people search; throttle accordingly.", + "name": "salesnav-search", + "description": "Search LinkedIn Sales Navigator for people leads by keyword", "access": "read", "domain": "www.linkedin.com", - "strategy": "cookie", + "strategy": "ui", "browser": true, "args": [ { @@ -4310,116 +6300,200 @@ "type": "string", "required": true, "positional": true, - "help": "People search keywords, e.g. \"site reliability engineer berlin\"" + "help": "People search keywords, e.g. \"quality manager food manufacturing\"" }, { "name": "limit", - "type": "int", - "default": 5, + "type": "number", + "default": 25, "required": false, - "help": "Maximum people to return (1-10); each query counts toward LinkedIn's monthly CUL" + "help": "Maximum leads to return (1-500, fetched 25 per request)" } ], "columns": [ "rank", "name", - "headline", + "title", + "company", "location", - "profile_url" + "degree", + "profile_url", + "lead_url", + "recipient_urn" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/linkedin/people-search.js", - "sourceFile": "plugins/linkedin/people-search.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/linkedin/salesnav-search.js", + "sourceFile": "plugins/linkedin/salesnav-search.js", + "navigateBefore": true }, { "site": "linkedin", - "name": "post-analytics", - "description": "Summarize raw visible LinkedIn post counters without custom scoring or classification", + "name": "salesnav-thread", + "description": "Return full Sales Navigator message history for a thread id, Sales Navigator inbox URL, lead URL, recipient urn, or exact recipient name", "access": "read", "domain": "www.linkedin.com", - "strategy": "cookie", + "strategy": "ui", "browser": true, "args": [ { - "name": "profile-url", + "name": "thread-or-recipient", "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "required": true, + "positional": true, + "help": "Sales Navigator inbox URL/thread id, Sales Navigator lead URL, recipient urn, or exact participant name" }, { "name": "limit", - "type": "int", + "type": "number", + "default": 200, + "required": false, + "help": "Maximum messages to return (1-500)" + }, + { + "name": "max-pages", + "type": "number", "default": 30, "required": false, - "help": "Maximum posts to summarize (1-100)" + "help": "Maximum inbox pages to scan when resolving a recipient" } ], "columns": [ - "posts_analyzed", - "total_reactions", - "total_comments", - "total_reposts", - "total_impressions", - "posts_with_media", - "posts_with_urls", - "latest_posted_at", - "latest_reactions", - "latest_comments", - "latest_reposts", - "latest_impressions", - "latest_url" + "index", + "thread_id", + "thread_url", + "sender", + "text", + "timestamp", + "subject", + "message_id", + "sender_urn", + "delivered_at", + "type", + "total_message_count" ], "type": "js", - "modulePath": "plugins/linkedin/post-analytics.js", - "sourceFile": "plugins/linkedin/post-analytics.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/linkedin/salesnav-thread.js", + "sourceFile": "plugins/linkedin/salesnav-thread.js", + "navigateBefore": true }, { "site": "linkedin", - "name": "post-comments", - "description": "List unique commenters and reply authors from one exact LinkedIn post URL", + "name": "search", + "description": "Search LinkedIn jobs", "access": "read", "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "post-url", + "name": "query", "type": "string", "required": true, "positional": true, - "help": "Exact LinkedIn post URL" + "help": "Job search keywords" + }, + { + "name": "location", + "type": "string", + "required": false, + "help": "Location text such as San Francisco Bay Area" }, { "name": "limit", "type": "int", + "default": 10, "required": false, - "help": "Maximum unique commenters to return; omit to fetch all" + "help": "Number of jobs to return (max 100)" + }, + { + "name": "start", + "type": "int", + "default": 0, + "required": false, + "help": "Result offset for pagination" + }, + { + "name": "details", + "type": "bool", + "default": false, + "required": false, + "help": "Include full job description and apply URL (slower)" + }, + { + "name": "company", + "type": "string", + "required": false, + "help": "Comma-separated company names or LinkedIn company IDs" + }, + { + "name": "experience-level", + "type": "string", + "required": false, + "help": "Comma-separated: internship, entry, associate, mid-senior, director, executive" + }, + { + "name": "job-type", + "type": "string", + "required": false, + "help": "Comma-separated: full-time, part-time, contract, temporary, volunteer, internship, other" + }, + { + "name": "date-posted", + "type": "string", + "required": false, + "help": "One of: any, month, week, 24h" + }, + { + "name": "remote", + "type": "string", + "required": false, + "help": "Comma-separated: on-site, hybrid, remote" } ], + "columns": [ + "rank", + "title", + "company", + "location", + "listed", + "salary", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/linkedin/search.js", + "sourceFile": "plugins/linkedin/search.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "sent-invitations", + "description": "List pending LinkedIn sent invitations for CRM reconciliation", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ "rank", "name", - "headline", "profile_url", - "comment_count", - "sample_comment", - "commented_at", - "source_post" + "invited_date_text" ], "type": "js", - "modulePath": "plugins/linkedin/post-comments.js", - "sourceFile": "plugins/linkedin/post-comments.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/linkedin/sent-invitations.js", + "sourceFile": "plugins/linkedin/sent-invitations.js", + "navigateBefore": true }, { "site": "linkedin", - "name": "posts", - "description": "Export visible posts from a LinkedIn profile activity page with engagement metrics", + "name": "services-read", + "description": "Read LinkedIn Services page details including services, overview, availability, pricing, and media titles/descriptions", "access": "read", "domain": "www.linkedin.com", "strategy": "cookie", @@ -4432,1355 +6506,1272 @@ "help": "LinkedIn /in// profile URL. Defaults to /in/me/." }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "services-url", + "type": "string", "required": false, - "help": "Maximum posts to return (1-100)" + "help": "LinkedIn /services/page// URL. If omitted, it is discovered from the profile." } ], "columns": [ - "rank", - "author", - "posted_at", - "body", - "reactions", - "comments", - "reposts", - "impressions", + "service_url", + "page_title", + "overview", + "availability", + "work_locations", + "pricing", + "services_provided", + "services_count", "media", - "media_urls", - "url", - "raw_text" + "media_count", + "messages", + "reviews_visibility" ], "type": "js", - "modulePath": "plugins/linkedin/posts.js", - "sourceFile": "plugins/linkedin/posts.js", + "modulePath": "plugins/linkedin/services-read.js", + "sourceFile": "plugins/linkedin/services-read.js", "navigateBefore": "https://www.linkedin.com" }, { "site": "linkedin", - "name": "profile-analytics", - "description": "Read visible LinkedIn profile dashboard metrics such as profile views, post impressions, and search appearances", + "name": "thread-snapshot", + "description": "Load a LinkedIn messaging thread, scroll for available history, and return a full context snapshot", "access": "read", "domain": "www.linkedin.com", - "strategy": "cookie", + "strategy": "ui", "browser": true, "args": [ { - "name": "profile-url", - "type": "string", + "name": "thread-url", + "type": "str", + "required": true, + "help": "Exact LinkedIn messaging thread URL to open and snapshot" + }, + { + "name": "max-scrolls", + "type": "number", + "default": 30, "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "Maximum upward scroll attempts to load older messages" + }, + { + "name": "json", + "type": "bool", + "default": false, + "required": false, + "help": "Return only JSON snapshot string in the snapshot_json field" } ], "columns": [ - "profile_url", - "profile_views", - "post_impressions", - "search_appearances", - "followers", - "connections", - "raw_analytics" + "thread_url", + "recipient", + "message_count", + "latest_text", + "snapshot_json" ], "type": "js", - "modulePath": "plugins/linkedin/profile-analytics.js", - "sourceFile": "plugins/linkedin/profile-analytics.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/linkedin/thread-snapshot.js", + "sourceFile": "plugins/linkedin/thread-snapshot.js", + "navigateBefore": true }, { "site": "linkedin", - "name": "profile-experience", - "description": "Read visible LinkedIn profile experience entries with titles, dates, locations, skills, media, and URLs", + "name": "timeline", + "description": "Read LinkedIn home timeline posts", "access": "read", "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "profile-url", - "type": "string", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "Number of posts to return (max 100)" } ], "columns": [ "rank", - "total_count", - "title", - "employment_type", - "company", - "date_range", - "start_date", - "end_date", - "location", - "location_type", - "description", - "skills", - "media", - "urls", - "skill_url", - "media_url", - "profile_url", - "raw_text" + "author", + "author_url", + "headline", + "text", + "posted_at", + "reactions", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/profile-experience.js", - "sourceFile": "plugins/linkedin/profile-experience.js", + "modulePath": "plugins/linkedin/timeline.js", + "sourceFile": "plugins/linkedin/timeline.js", "navigateBefore": "https://www.linkedin.com" }, { "site": "linkedin", - "name": "profile-projects", - "description": "Read visible LinkedIn profile projects with descriptions, dates, skills, media, and URLs", + "name": "whoami", + "description": "Show the current logged-in linkedin account", "access": "read", "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - } - ], + "args": [], "columns": [ - "rank", - "title", - "date_range", - "associated_with", - "description", - "skills", - "media", - "urls", - "profile_url", - "raw_text" + "logged_in", + "site", + "public_id", + "plain_id", + "name" ], "type": "js", - "modulePath": "plugins/linkedin/profile-projects.js", - "sourceFile": "plugins/linkedin/profile-projects.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/linkedin/auth.js", + "sourceFile": "plugins/linkedin/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "linkedin", - "name": "profile-read", - "description": "Read visible LinkedIn profile sections: headline, About, experience, education, services, and featured sections", + "site": "lobsters", + "name": "active", + "description": "Lobste.rs most active discussions", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "lobste.rs", + "strategy": "public", + "browser": false, "args": [ { - "name": "profile-url", - "type": "string", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "Number of stories" } ], "columns": [ - "profile_url", - "name", - "headline", - "location", - "about", - "about_character_count", - "about_skills", - "experience", - "education", - "services", - "featured" + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/profile-read.js", - "sourceFile": "plugins/linkedin/profile-read.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/lobsters/active.js", + "sourceFile": "plugins/lobsters/active.js" }, { - "site": "linkedin", - "name": "safe-send", - "description": "Fail-closed LinkedIn message sender that verifies exact thread, recipient, and latest message before filling/sending", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, + "site": "lobsters", + "name": "domain", + "description": "Lobste.rs stories submitted from a specific domain", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, "args": [ { - "name": "thread-url", - "type": "str", - "required": true, - "help": "Exact LinkedIn messaging thread URL to open and verify" - }, - { - "name": "expected-name", - "type": "str", - "required": true, - "help": "Expected visible recipient name in the active thread header" - }, - { - "name": "message", + "name": "domain", "type": "str", "required": true, - "help": "Message body to send or dry-run" - }, - { - "name": "expected-last-text", - "type": "str", - "required": false, - "help": "Substring expected in the currently visible latest conversation context" - }, - { - "name": "expected-last-hash", - "type": "str", - "required": false, - "help": "SHA-256 hash of expected latest visible message text" - }, - { - "name": "send", - "type": "bool", - "default": false, - "required": false, - "help": "Actually click Send. Default is dry-run verification only." + "positional": true, + "help": "Source domain (e.g. github.com, arxiv.org, blog.cloudflare.com)" }, { - "name": "screenshot", - "type": "bool", - "default": false, + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Capture a screenshot during verification" + "help": "Number of stories (1-25 — single page)" } ], "columns": [ - "status", - "recipient", - "reason", - "thread_url", - "message_chars", - "screenshot" + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "submission_url", + "comments_url" ], "type": "js", - "modulePath": "plugins/linkedin/safe-send.js", - "sourceFile": "plugins/linkedin/safe-send.js", - "navigateBefore": true + "modulePath": "plugins/lobsters/domain.js", + "sourceFile": "plugins/lobsters/domain.js" }, { - "site": "linkedin", - "name": "salesnav-inbox", - "description": "List LinkedIn Sales Navigator message conversations with API pagination", + "site": "lobsters", + "name": "hot", + "description": "Lobste.rs hottest stories", "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, + "domain": "lobste.rs", + "strategy": "public", + "browser": false, "args": [ { "name": "limit", - "type": "number", - "default": 40, - "required": false, - "help": "Maximum conversations to return (1-500)" - }, - { - "name": "max-pages", - "type": "number", - "default": 30, + "type": "int", + "default": 20, "required": false, - "help": "Maximum Sales Navigator API pages to fetch" - }, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/lobsters/hot.js", + "sourceFile": "plugins/lobsters/hot.js" + }, + { + "site": "lobsters", + "name": "newest", + "description": "Lobste.rs newest stories", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ { - "name": "unread-only", - "type": "bool", - "default": false, + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Return only unread conversations" + "help": "Number of stories" } ], "columns": [ "rank", - "thread_id", - "thread_url", - "person_name", - "last_message_snippet", - "last_activity_time", - "unread", - "unread_count", - "total_message_count", - "archived", - "participants", - "next_page_starts_at" + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-inbox.js", - "sourceFile": "plugins/linkedin/salesnav-inbox.js", - "navigateBefore": true + "modulePath": "plugins/lobsters/newest.js", + "sourceFile": "plugins/lobsters/newest.js" }, { - "site": "linkedin", - "name": "salesnav-message", - "description": "Send or dry-run a LinkedIn Sales Navigator InMail to a lead using the Sales Navigator messaging API", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, + "site": "lobsters", + "name": "read", + "description": "Read a Lobste.rs story and its comment tree", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, "args": [ { - "name": "recipient", - "type": "string", + "name": "id", + "type": "str", "required": true, "positional": true, - "help": "Sales Navigator lead URL, LinkedIn /in/ URL from salesnav-search, or urn:li:fs_salesProfile:(...)" + "help": "Lobste.rs short_id (e.g. 6cmh6h)" }, { - "name": "subject", - "type": "string", - "required": true, - "help": "InMail subject" + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max top-level comments" }, { - "name": "body", - "type": "string", - "required": true, - "help": "InMail body" + "name": "depth", + "type": "int", + "default": 2, + "required": false, + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" }, { - "name": "send", - "type": "bool", - "default": false, + "name": "replies", + "type": "int", + "default": 5, "required": false, - "help": "Actually send the InMail. Default is dry-run validation only." + "help": "Max replies shown per comment at each level" }, { - "name": "copy-to-crm", - "type": "bool", - "default": false, + "name": "max-length", + "type": "int", + "default": 2000, "required": false, - "help": "Set Sales Navigator copyToCrm on the message request" + "help": "Max characters per comment body (min 100)" } ], "columns": [ - "status", - "recipient", - "title", - "company", - "credits_remaining", - "credits_before", - "credits_after", - "sent_in_salesnav", - "message_chars", - "subject_chars", - "recipient_urn", - "degree", - "inmail_restriction", - "open_link" + "type", + "author", + "score", + "text" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-message.js", - "sourceFile": "plugins/linkedin/salesnav-message.js", - "navigateBefore": true + "modulePath": "plugins/lobsters/read.js", + "sourceFile": "plugins/lobsters/read.js" }, { - "site": "linkedin", - "name": "salesnav-search", - "description": "Search LinkedIn Sales Navigator for people leads by keyword", + "site": "lobsters", + "name": "tag", + "description": "Lobste.rs stories by tag", "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, + "domain": "lobste.rs", + "strategy": "public", + "browser": false, "args": [ { - "name": "keywords", - "type": "string", + "name": "tag", + "type": "str", "required": true, "positional": true, - "help": "People search keywords, e.g. \"quality manager food manufacturing\"" + "help": "Tag name (e.g. programming, rust, security, ai)" }, { "name": "limit", - "type": "number", - "default": 25, + "type": "int", + "default": 20, "required": false, - "help": "Maximum leads to return (1-500, fetched 25 per request)" + "help": "Number of stories" } ], "columns": [ "rank", - "name", + "id", "title", - "company", - "location", - "degree", - "profile_url", - "lead_url", - "recipient_urn" - ], - "tags": [ - "search" + "score", + "author", + "comments", + "created_at", + "tags", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-search.js", - "sourceFile": "plugins/linkedin/salesnav-search.js", - "navigateBefore": true + "modulePath": "plugins/lobsters/tag.js", + "sourceFile": "plugins/lobsters/tag.js" }, { - "site": "linkedin", - "name": "salesnav-thread", - "description": "Return full Sales Navigator message history for a thread id, Sales Navigator inbox URL, lead URL, recipient urn, or exact recipient name", - "access": "read", - "domain": "www.linkedin.com", + "site": "luma", + "name": "create-event", + "description": "Create a free single-session Luma event", + "access": "write", + "domain": "luma.com", "strategy": "ui", "browser": true, "args": [ { - "name": "thread-or-recipient", - "type": "string", + "name": "name", + "type": "str", "required": true, - "positional": true, - "help": "Sales Navigator inbox URL/thread id, Sales Navigator lead URL, recipient urn, or exact participant name" + "help": "" }, { - "name": "limit", - "type": "number", - "default": 200, - "required": false, - "help": "Maximum messages to return (1-500)" + "name": "start", + "type": "str", + "required": true, + "help": "" }, { - "name": "max-pages", - "type": "number", - "default": 30, - "required": false, - "help": "Maximum inbox pages to scan when resolving a recipient" - } - ], - "columns": [ - "index", - "thread_id", - "thread_url", - "sender", - "text", - "timestamp", - "subject", - "message_id", - "sender_urn", - "delivered_at", - "type", - "total_message_count" - ], - "type": "js", - "modulePath": "plugins/linkedin/salesnav-thread.js", - "sourceFile": "plugins/linkedin/salesnav-thread.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "search", - "description": "Search LinkedIn jobs", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "string", + "name": "end", + "type": "str", "required": true, - "positional": true, - "help": "Job search keywords" + "help": "" }, { - "name": "location", - "type": "string", - "required": false, - "help": "Location text such as San Francisco Bay Area" + "name": "timezone", + "type": "str", + "required": true, + "help": "" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "calendar", + "type": "str", "required": false, - "help": "Number of jobs to return (max 100)" + "help": "" }, { - "name": "start", - "type": "int", - "default": 0, + "name": "description", + "type": "str", "required": false, - "help": "Result offset for pagination" + "help": "" }, { - "name": "details", - "type": "bool", - "default": false, + "name": "location", + "type": "str", "required": false, - "help": "Include full job description and apply URL (slower)" + "help": "" }, { - "name": "company", - "type": "string", + "name": "virtual-url", + "type": "str", "required": false, - "help": "Comma-separated company names or LinkedIn company IDs" + "help": "" }, { - "name": "experience-level", - "type": "string", + "name": "visibility", + "type": "str", + "default": "public", "required": false, - "help": "Comma-separated: internship, entry, associate, mid-senior, director, executive" + "help": "", + "choices": [ + "public", + "private", + "members-only" + ] }, { - "name": "job-type", - "type": "string", + "name": "capacity", + "type": "int", "required": false, - "help": "Comma-separated: full-time, part-time, contract, temporary, volunteer, internship, other" + "help": "" }, { - "name": "date-posted", - "type": "string", + "name": "require-approval", + "type": "boolean", + "default": false, "required": false, - "help": "One of: any, month, week, 24h" + "help": "" }, { - "name": "remote", - "type": "string", + "name": "confirm", + "type": "boolean", + "default": false, "required": false, - "help": "Comma-separated: on-site, hybrid, remote" + "help": "" } ], "columns": [ - "rank", - "title", - "company", - "location", - "listed", - "salary", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/linkedin/search.js", - "sourceFile": "plugins/linkedin/search.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "sent-invitations", - "description": "List pending LinkedIn sent invitations for CRM reconciliation", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "rank", + "eventId", "name", - "profile_url", - "invited_date_text" + "startsAt", + "endsAt", + "timezone", + "visibility", + "requireApproval", + "capacity", + "eventUrl", + "manageUrl" ], "type": "js", - "modulePath": "plugins/linkedin/sent-invitations.js", - "sourceFile": "plugins/linkedin/sent-invitations.js", - "navigateBefore": true + "modulePath": "plugins/luma/create-event.js", + "sourceFile": "plugins/luma/create-event.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true }, { - "site": "linkedin", - "name": "services-read", - "description": "Read LinkedIn Services page details including services, overview, availability, pricing, and media titles/descriptions", + "site": "luma", + "name": "events", + "description": "List upcoming or past Luma events managed by the logged-in account", "access": "read", - "domain": "www.linkedin.com", + "example": "webcmd luma events --period future --limit 25 -f json", + "domain": "luma.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "profile-url", - "type": "string", + "name": "period", + "type": "str", + "default": "future", "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "List future or past events", + "choices": [ + "future", + "past" + ] }, { - "name": "services-url", - "type": "string", + "name": "limit", + "type": "int", + "default": 25, "required": false, - "help": "LinkedIn /services/page// URL. If omitted, it is discovered from the profile." + "help": "Maximum number of events to request" } ], "columns": [ - "service_url", - "page_title", - "overview", - "availability", - "work_locations", - "pricing", - "services_provided", - "services_count", - "media", - "media_count", - "messages", - "reviews_visibility" + "eventId", + "name", + "startsAt", + "endsAt", + "timezone", + "guestCount", + "requireApproval", + "managerLevel", + "location", + "manageUrl", + "eventUrl" ], "type": "js", - "modulePath": "plugins/linkedin/services-read.js", - "sourceFile": "plugins/linkedin/services-read.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/luma/events.js", + "sourceFile": "plugins/luma/events.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "linkedin", - "name": "thread-snapshot", - "description": "Load a LinkedIn messaging thread, scroll for available history, and return a full context snapshot", + "site": "luma", + "name": "guests", + "description": "List guests and all custom registration answers for a managed Luma event", "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", + "example": "webcmd luma guests evt-abc --status pending_approval --limit 100 -f json", + "domain": "luma.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "thread-url", + "name": "eventId", "type": "str", "required": true, - "help": "Exact LinkedIn messaging thread URL to open and snapshot" + "positional": true, + "help": "Luma event ID returned by webcmd luma events" }, { - "name": "max-scrolls", - "type": "number", - "default": 30, + "name": "status", + "type": "str", + "default": "all", "required": false, - "help": "Maximum upward scroll attempts to load older messages" + "help": "Filter by guest approval status", + "choices": [ + "all", + "approved", + "pending_approval", + "declined", + "waitlist", + "invited" + ] }, - { - "name": "json", - "type": "bool", - "default": false, - "required": false, - "help": "Return only JSON snapshot string in the snapshot_json field" - } - ], - "columns": [ - "thread_url", - "recipient", - "message_count", - "latest_text", - "snapshot_json" - ], - "type": "js", - "modulePath": "plugins/linkedin/thread-snapshot.js", - "sourceFile": "plugins/linkedin/thread-snapshot.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "timeline", - "description": "Read LinkedIn home timeline posts", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 100, "required": false, - "help": "Number of posts to return (max 100)" + "help": "Maximum matching guests to return" + }, + { + "name": "query", + "type": "str", + "default": "", + "required": false, + "help": "Search text passed to Luma guest search" } ], "columns": [ - "rank", - "author", - "author_url", - "headline", - "text", - "posted_at", - "reactions", - "comments", - "url" + "eventId", + "guestId", + "userId", + "name", + "email", + "phone", + "status", + "registeredAt", + "profiles", + "answers" ], "type": "js", - "modulePath": "plugins/linkedin/timeline.js", - "sourceFile": "plugins/linkedin/timeline.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/luma/guests.js", + "sourceFile": "plugins/luma/guests.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "linkedin", - "name": "whoami", - "description": "Show the current logged-in linkedin account", - "access": "read", - "domain": "www.linkedin.com", + "site": "luma", + "name": "login", + "description": "Open Luma sign in", + "access": "write", + "domain": "luma.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ + "status", "logged_in", "site", - "public_id", - "plain_id", - "name" + "name", + "email", + "url", + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/linkedin/auth.js", - "sourceFile": "plugins/linkedin/auth.js", + "modulePath": "plugins/luma/auth.js", + "sourceFile": "plugins/luma/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "lobsters", - "name": "active", - "description": "Lobste.rs most active discussions", - "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, + "site": "luma", + "name": "set-registration-questions", + "description": "Append or replace custom registration questions on a managed Luma event", + "access": "write", + "domain": "luma.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "eventId", + "type": "str", + "required": true, + "positional": true, + "help": "" + }, + { + "name": "questions-file", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "mode", + "type": "str", + "required": true, + "help": "", + "choices": [ + "append", + "replace" + ] + }, + { + "name": "confirm", + "type": "boolean", + "default": false, "required": false, - "help": "Number of stories" + "help": "" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "url" + "eventId", + "mode", + "previousCount", + "questionCount", + "questions", + "registrationUrl" ], "type": "js", - "modulePath": "plugins/lobsters/active.js", - "sourceFile": "plugins/lobsters/active.js" + "modulePath": "plugins/luma/set-registration-questions.js", + "sourceFile": "plugins/luma/set-registration-questions.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true }, { - "site": "lobsters", - "name": "domain", - "description": "Lobste.rs stories submitted from a specific domain", - "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, + "site": "luma", + "name": "update-guest-status", + "description": "Approve or decline a pending Luma guest after explicit confirmation", + "access": "write", + "example": "webcmd luma update-guest-status evt-abc gst-abc --status approved --confirm true -f json", + "domain": "luma.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "domain", + "name": "eventId", "type": "str", "required": true, "positional": true, - "help": "Source domain (e.g. github.com, arxiv.org, blog.cloudflare.com)" + "help": "Luma event ID returned by webcmd luma events" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "guestId", + "type": "str", + "required": true, + "positional": true, + "help": "Luma guest ID returned by webcmd luma guests" + }, + { + "name": "status", + "type": "str", + "required": true, + "help": "New guest status", + "choices": [ + "approved", + "declined" + ] + }, + { + "name": "suppress-email", + "type": "boolean", + "default": false, "required": false, - "help": "Number of stories (1-25 — single page)" + "help": "Set true to prevent Luma from emailing the guest" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "Required. Set --confirm true to change the real guest status" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "submission_url", - "comments_url" + "eventId", + "guestId", + "name", + "email", + "previousStatus", + "status", + "emailSuppressed" ], "type": "js", - "modulePath": "plugins/lobsters/domain.js", - "sourceFile": "plugins/lobsters/domain.js" + "modulePath": "plugins/luma/update-guest-status.js", + "sourceFile": "plugins/luma/update-guest-status.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true }, { - "site": "lobsters", - "name": "hot", - "description": "Lobste.rs hottest stories", + "site": "luma", + "name": "whoami", + "description": "Show the current logged-in Luma account", "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], + "domain": "luma.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", + "logged_in", + "site", + "name", + "email", "url" ], "type": "js", - "modulePath": "plugins/lobsters/hot.js", - "sourceFile": "plugins/lobsters/hot.js" + "modulePath": "plugins/luma/auth.js", + "sourceFile": "plugins/luma/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "lobsters", - "name": "newest", - "description": "Lobste.rs newest stories", + "site": "maven", + "name": "artifact", + "description": "Fetch a Maven Central artifact's version history (groupId:artifactId[:version])", "access": "read", - "domain": "lobste.rs", + "domain": "search.maven.org", "strategy": "public", "browser": false, "args": [ + { + "name": "coordinate", + "type": "str", + "required": true, + "positional": true, + "help": "Maven coord \"groupId:artifactId\" or \"groupId:artifactId:version\"" + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of stories" + "help": "Max versions (1-200, ignored when version is pinned)" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", + "groupId", + "artifactId", + "version", + "packaging", + "publishedAt", "tags", "url" ], "type": "js", - "modulePath": "plugins/lobsters/newest.js", - "sourceFile": "plugins/lobsters/newest.js" + "modulePath": "plugins/maven/artifact.js", + "sourceFile": "plugins/maven/artifact.js" }, { - "site": "lobsters", - "name": "read", - "description": "Read a Lobste.rs story and its comment tree", + "site": "maven", + "name": "search", + "description": "Search Maven Central by keyword (artifact name, groupId, tag)", "access": "read", - "domain": "lobste.rs", + "domain": "search.maven.org", "strategy": "public", "browser": false, "args": [ { - "name": "id", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Lobste.rs short_id (e.g. 6cmh6h)" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max top-level comments" - }, - { - "name": "depth", - "type": "int", - "default": 2, - "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" - }, - { - "name": "replies", - "type": "int", - "default": 5, - "required": false, - "help": "Max replies shown per comment at each level" + "help": "Search keyword (e.g. \"jackson\", \"guava\", \"ai.koog\")" }, { - "name": "max-length", + "name": "limit", "type": "int", - "default": 2000, + "default": 30, "required": false, - "help": "Max characters per comment body (min 100)" + "help": "Max artifacts (1-200)" } ], "columns": [ - "type", - "author", - "score", - "text" + "rank", + "coordinate", + "groupId", + "artifactId", + "latestVersion", + "packaging", + "versions", + "lastPublished", + "repository", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/lobsters/read.js", - "sourceFile": "plugins/lobsters/read.js" + "modulePath": "plugins/maven/search.js", + "sourceFile": "plugins/maven/search.js" }, { - "site": "lobsters", - "name": "tag", - "description": "Lobste.rs stories by tag", + "site": "mdn", + "name": "search", + "description": "Search MDN Web Docs by keyword", "access": "read", - "domain": "lobste.rs", + "domain": "developer.mozilla.org", "strategy": "public", "browser": false, "args": [ { - "name": "tag", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Tag name (e.g. programming, rust, security, ai)" + "help": "Search keyword (e.g. \"fetch\", \"flexbox\", \"Array.prototype.map\")" }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Number of stories" + "help": "Max results (1-50)" + }, + { + "name": "locale", + "type": "str", + "default": "en-US", + "required": false, + "help": "Doc locale (en-US default; de / es / fr / ja / ko / pt-BR / ru / zh-CN / zh-TW)" } ], "columns": [ "rank", - "id", "title", - "score", - "author", - "comments", - "created_at", - "tags", + "slug", + "locale", + "summary", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/lobsters/tag.js", - "sourceFile": "plugins/lobsters/tag.js" + "modulePath": "plugins/mdn/search.js", + "sourceFile": "plugins/mdn/search.js" }, { - "site": "luma", - "name": "create-event", - "description": "Create a free single-session Luma event", - "access": "write", - "domain": "luma.com", - "strategy": "ui", - "browser": true, + "site": "npm", + "name": "downloads", + "description": "Daily download counts for an npm package over a window", + "access": "read", + "domain": "api.npmjs.org", + "strategy": "public", + "browser": false, "args": [ { "name": "name", "type": "str", "required": true, - "help": "" - }, - { - "name": "start", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "end", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "timezone", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "calendar", - "type": "str", - "required": false, - "help": "" - }, - { - "name": "description", - "type": "str", - "required": false, - "help": "" - }, - { - "name": "location", - "type": "str", - "required": false, - "help": "" - }, - { - "name": "virtual-url", - "type": "str", - "required": false, - "help": "" + "positional": true, + "help": "npm package name (e.g. \"react\", \"@vercel/og\")" }, { - "name": "visibility", + "name": "period", "type": "str", - "default": "public", - "required": false, - "help": "", - "choices": [ - "public", - "private", - "members-only" - ] - }, - { - "name": "capacity", - "type": "int", - "required": false, - "help": "" - }, - { - "name": "require-approval", - "type": "boolean", - "default": false, - "required": false, - "help": "" - }, - { - "name": "confirm", - "type": "boolean", - "default": false, + "default": "last-week", "required": false, - "help": "" + "help": "last-day / last-week / last-month / last-year, or YYYY-MM-DD:YYYY-MM-DD" } ], "columns": [ - "eventId", - "name", - "startsAt", - "endsAt", - "timezone", - "visibility", - "requireApproval", - "capacity", - "eventUrl", - "manageUrl" + "rank", + "package", + "day", + "downloads" ], "type": "js", - "modulePath": "plugins/luma/create-event.js", - "sourceFile": "plugins/luma/create-event.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "modulePath": "plugins/npm/downloads.js", + "sourceFile": "plugins/npm/downloads.js" }, { - "site": "luma", - "name": "events", - "description": "List upcoming or past Luma events managed by the logged-in account", + "site": "npm", + "name": "package", + "description": "Single npm package metadata (latest version, license, homepage, repository). Use `npm downloads` for stats.", "access": "read", - "example": "webcmd luma events --period future --limit 25 -f json", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, + "domain": "registry.npmjs.org", + "strategy": "public", + "browser": false, "args": [ { - "name": "period", + "name": "name", "type": "str", - "default": "future", - "required": false, - "help": "List future or past events", - "choices": [ - "future", - "past" - ] - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Maximum number of events to request" + "required": true, + "positional": true, + "help": "npm package name (e.g. \"react\", \"@vercel/og\")" } ], "columns": [ - "eventId", "name", - "startsAt", - "endsAt", - "timezone", - "guestCount", - "requireApproval", - "managerLevel", - "location", - "manageUrl", - "eventUrl" + "latestVersion", + "description", + "license", + "homepage", + "repository", + "bugs", + "maintainers", + "keywords", + "created", + "modified", + "url" ], "type": "js", - "modulePath": "plugins/luma/events.js", - "sourceFile": "plugins/luma/events.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/npm/package.js", + "sourceFile": "plugins/npm/package.js" }, { - "site": "luma", - "name": "guests", - "description": "List guests and all custom registration answers for a managed Luma event", + "site": "npm", + "name": "search", + "description": "Search the public npm registry by keyword", "access": "read", - "example": "webcmd luma guests evt-abc --status pending_approval --limit 100 -f json", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, + "domain": "registry.npmjs.org", + "strategy": "public", + "browser": false, "args": [ { - "name": "eventId", - "type": "str", - "required": true, - "positional": true, - "help": "Luma event ID returned by webcmd luma events" - }, - { - "name": "status", + "name": "query", "type": "str", - "default": "all", - "required": false, - "help": "Filter by guest approval status", - "choices": [ - "all", - "approved", - "pending_approval", - "declined", - "waitlist", - "invited" - ] + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"react\", \"graphql client\")" }, { "name": "limit", "type": "int", - "default": 100, - "required": false, - "help": "Maximum matching guests to return" - }, - { - "name": "query", - "type": "str", - "default": "", + "default": 20, "required": false, - "help": "Search text passed to Luma guest search" + "help": "Max results (1-250)" } ], "columns": [ - "eventId", - "guestId", - "userId", + "rank", "name", - "email", - "phone", - "status", - "registeredAt", - "profiles", - "answers" + "version", + "description", + "weeklyDownloads", + "dependents", + "license", + "publisher", + "updated", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/luma/guests.js", - "sourceFile": "plugins/luma/guests.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/npm/search.js", + "sourceFile": "plugins/npm/search.js" }, { - "site": "luma", - "name": "login", - "description": "Open Luma sign in", - "access": "write", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, - "args": [], + "site": "nuget", + "name": "package", + "description": "Full NuGet package version history (catalogEntry per release)", + "access": "read", + "domain": "api.nuget.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "NuGet package id (e.g. \"Newtonsoft.Json\", case-insensitive)" + } + ], "columns": [ - "status", - "logged_in", - "site", - "name", - "email", - "url", - "action", - "verify_command" + "rank", + "id", + "version", + "title", + "authors", + "tags", + "language", + "licenseExpression", + "projectUrl", + "published", + "listed", + "url" ], "type": "js", - "modulePath": "plugins/luma/auth.js", - "sourceFile": "plugins/luma/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/nuget/package.js", + "sourceFile": "plugins/nuget/package.js" }, { - "site": "luma", - "name": "set-registration-questions", - "description": "Append or replace custom registration questions on a managed Luma event", - "access": "write", - "domain": "luma.com", - "strategy": "ui", - "browser": true, + "site": "nuget", + "name": "search", + "description": "Search NuGet packages by keyword", + "access": "read", + "domain": "api.nuget.org", + "strategy": "public", + "browser": false, "args": [ { - "name": "eventId", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "" - }, - { - "name": "questions-file", - "type": "str", - "required": true, - "help": "" + "help": "Search keyword" }, { - "name": "mode", - "type": "str", - "required": true, - "help": "", - "choices": [ - "append", - "replace" - ] + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max packages (1-1000)" }, { - "name": "confirm", + "name": "prerelease", "type": "boolean", "default": false, "required": false, - "help": "" + "help": "Include prerelease versions" } ], "columns": [ - "eventId", - "mode", - "previousCount", - "questionCount", - "questions", - "registrationUrl" + "rank", + "id", + "version", + "title", + "description", + "authors", + "tags", + "totalDownloads", + "verified", + "projectUrl", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/luma/set-registration-questions.js", - "sourceFile": "plugins/luma/set-registration-questions.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "modulePath": "plugins/nuget/search.js", + "sourceFile": "plugins/nuget/search.js" }, { - "site": "luma", - "name": "update-guest-status", - "description": "Approve or decline a pending Luma guest after explicit confirmation", - "access": "write", - "example": "webcmd luma update-guest-status evt-abc gst-abc --status approved --confirm true -f json", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, + "site": "nvd", + "name": "cve", + "description": "NIST NVD CVE detail (description, CVSS, CWE, KEV flag)", + "access": "read", + "domain": "services.nvd.nist.gov", + "strategy": "public", + "browser": false, "args": [ { - "name": "eventId", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Luma event ID returned by webcmd luma events" - }, + "help": "CVE identifier (e.g. \"CVE-2021-44228\")" + } + ], + "columns": [ + "id", + "published", + "lastModified", + "vulnStatus", + "baseScore", + "severity", + "attackVector", + "cwe", + "kevAdded", + "description", + "url" + ], + "type": "js", + "modulePath": "plugins/nvd/cve.js", + "sourceFile": "plugins/nvd/cve.js" + }, + { + "site": "oeis", + "name": "search", + "description": "Search OEIS sequences by keyword or numeric pattern", + "access": "read", + "domain": "oeis.org", + "strategy": "public", + "browser": false, + "args": [ { - "name": "guestId", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Luma guest ID returned by webcmd luma guests" - }, - { - "name": "status", - "type": "str", - "required": true, - "help": "New guest status", - "choices": [ - "approved", - "declined" - ] - }, - { - "name": "suppress-email", - "type": "boolean", - "default": false, - "required": false, - "help": "Set true to prevent Luma from emailing the guest" + "help": "Search keyword or comma-separated terms (e.g. \"fibonacci\", \"1,1,2,3,5,8\")" }, { - "name": "confirm", - "type": "boolean", - "default": false, + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Required. Set --confirm true to change the real guest status" + "help": "Max sequences (1-100)" } ], "columns": [ - "eventId", - "guestId", + "rank", + "id", "name", - "email", - "previousStatus", - "status", - "emailSuppressed" + "keywords", + "preview", + "author", + "created", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/luma/update-guest-status.js", - "sourceFile": "plugins/luma/update-guest-status.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "modulePath": "plugins/oeis/search.js", + "sourceFile": "plugins/oeis/search.js" }, { - "site": "luma", - "name": "whoami", - "description": "Show the current logged-in Luma account", + "site": "oeis", + "name": "sequence", + "description": "Full OEIS sequence detail by A-number (terms, name, keywords, formula counts)", "access": "read", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, - "args": [], + "domain": "oeis.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "OEIS sequence id (e.g. \"A000045\" for Fibonacci)" + } + ], "columns": [ - "logged_in", - "site", + "id", "name", - "email", + "keywords", + "preview", + "termCount", + "offset", + "author", + "created", + "revision", + "commentCount", + "formulaCount", + "referenceCount", + "xrefCount", + "linkCount", "url" ], "type": "js", - "modulePath": "plugins/luma/auth.js", - "sourceFile": "plugins/luma/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/oeis/sequence.js", + "sourceFile": "plugins/oeis/sequence.js" }, { - "site": "maven", - "name": "artifact", - "description": "Fetch a Maven Central artifact's version history (groupId:artifactId[:version])", + "site": "openalex", + "name": "search", + "description": "Search OpenAlex Works (papers, books, preprints) by keyword", "access": "read", - "domain": "search.maven.org", + "domain": "api.openalex.org", "strategy": "public", "browser": false, "args": [ { - "name": "coordinate", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Maven coord \"groupId:artifactId\" or \"groupId:artifactId:version\"" + "help": "Search text (e.g. \"transformers\", \"open access scholarly\")" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max versions (1-200, ignored when version is pinned)" + "help": "Max works (1-200, single OpenAlex page)" } ], "columns": [ - "groupId", - "artifactId", - "version", - "packaging", - "publishedAt", - "tags", + "rank", + "id", + "title", + "year", + "citations", + "firstAuthor", + "venue", + "openAccess", + "type", + "doi", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/maven/artifact.js", - "sourceFile": "plugins/maven/artifact.js" + "modulePath": "plugins/openalex/search.js", + "sourceFile": "plugins/openalex/search.js" }, { - "site": "maven", - "name": "search", - "description": "Search Maven Central by keyword (artifact name, groupId, tag)", + "site": "openalex", + "name": "work", + "description": "Fetch a single OpenAlex Work (paper / preprint / book) — metadata + abstract", "access": "read", - "domain": "search.maven.org", + "domain": "api.openalex.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "OpenAlex Work id (\"W2741809807\"), DOI (\"10.7717/peerj.4375\"), or full URL" + } + ], + "columns": [ + "id", + "title", + "type", + "year", + "date", + "language", + "authors", + "venue", + "citations", + "openAccess", + "openAccessUrl", + "referencedCount", + "doi", + "abstract", + "url" + ], + "type": "js", + "modulePath": "plugins/openalex/work.js", + "sourceFile": "plugins/openalex/work.js" + }, + { + "site": "openfda", + "name": "drug-label", + "description": "Search FDA-approved drug labels (brand or generic name)", + "access": "read", + "domain": "fda.gov", "strategy": "public", "browser": false, "args": [ @@ -5789,398 +7780,413 @@ "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"jackson\", \"guava\", \"ai.koog\")" + "help": "Brand or generic drug name (e.g. \"aspirin\", \"lisinopril\")" }, { "name": "limit", "type": "int", - "default": 30, + "default": 5, "required": false, - "help": "Max artifacts (1-200)" + "help": "Max rows (1-25, default 5; openFDA caps anonymous tier at 25/page)" } ], "columns": [ "rank", - "coordinate", - "groupId", - "artifactId", - "latestVersion", - "packaging", - "versions", - "lastPublished", - "repository", - "url" - ], - "tags": [ - "search" + "id", + "brandName", + "genericName", + "manufacturer", + "productType", + "route", + "productNdc", + "pharmClass", + "purpose", + "indications", + "warnings", + "dosage", + "effectiveTime" ], "type": "js", - "modulePath": "plugins/maven/search.js", - "sourceFile": "plugins/maven/search.js" + "modulePath": "plugins/openfda/drug-label.js", + "sourceFile": "plugins/openfda/drug-label.js" }, { - "site": "mdn", - "name": "search", - "description": "Search MDN Web Docs by keyword", + "site": "openfda", + "name": "food-recall", + "description": "FDA food recall and enforcement actions (most recent first)", "access": "read", - "domain": "developer.mozilla.org", + "domain": "fda.gov", "strategy": "public", "browser": false, "args": [ { "name": "query", "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"fetch\", \"flexbox\", \"Array.prototype.map\")" + "required": false, + "help": "Free-text Lucene query (e.g. \"salmonella\", \"listeria\"); default: all recent recalls" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "status", + "type": "str", "required": false, - "help": "Max results (1-50)" + "help": "Filter by status: \"Ongoing\", \"Completed\", \"Terminated\"" }, { - "name": "locale", + "name": "classification", "type": "str", - "default": "en-US", "required": false, - "help": "Doc locale (en-US default; de / es / fr / ja / ko / pt-BR / ru / zh-CN / zh-TW)" + "help": "Filter by class: \"Class I\" (most serious), \"Class II\", \"Class III\"" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max rows (1-100, default 10; openFDA caps anonymous tier at 100/page)" } ], "columns": [ "rank", - "title", - "slug", - "locale", - "summary", - "url" - ], - "tags": [ - "search" + "recallNumber", + "status", + "classification", + "voluntary", + "recallingFirm", + "city", + "state", + "country", + "productDescription", + "reasonForRecall", + "productQuantity", + "distributionPattern", + "reportDate", + "recallInitiationDate", + "terminationDate" ], "type": "js", - "modulePath": "plugins/mdn/search.js", - "sourceFile": "plugins/mdn/search.js" + "modulePath": "plugins/openfda/food-recall.js", + "sourceFile": "plugins/openfda/food-recall.js" }, { - "site": "npm", - "name": "downloads", - "description": "Daily download counts for an npm package over a window", + "site": "openreview", + "name": "author", + "description": "List OpenReview submissions by an author profile id (newest first)", "access": "read", - "domain": "api.npmjs.org", + "domain": "openreview.net", "strategy": "public", "browser": false, "args": [ { - "name": "name", + "name": "profile", "type": "str", "required": true, "positional": true, - "help": "npm package name (e.g. \"react\", \"@vercel/og\")" + "help": "OpenReview profile id (e.g. \"~Yoshua_Bengio1\"). Find it on the author profile URL on openreview.net." }, { - "name": "period", - "type": "str", - "default": "last-week", + "name": "limit", + "type": "int", + "default": 50, "required": false, - "help": "last-day / last-week / last-month / last-year, or YYYY-MM-DD:YYYY-MM-DD" + "help": "Max submissions (1-1000)" } ], "columns": [ "rank", - "package", - "day", - "downloads" + "id", + "title", + "authors", + "venue", + "pdate", + "url" ], "type": "js", - "modulePath": "plugins/npm/downloads.js", - "sourceFile": "plugins/npm/downloads.js" + "modulePath": "plugins/openreview/author.js", + "sourceFile": "plugins/openreview/author.js" }, { - "site": "npm", - "name": "package", - "description": "Single npm package metadata (latest version, license, homepage, repository). Use `npm downloads` for stats.", + "site": "openreview", + "name": "paper", + "description": "Show full metadata for a single OpenReview paper", "access": "read", - "domain": "registry.npmjs.org", + "domain": "openreview.net", "strategy": "public", "browser": false, "args": [ { - "name": "name", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "npm package name (e.g. \"react\", \"@vercel/og\")" + "help": "OpenReview note id (e.g. \"5sRnsubyAK\")" } ], "columns": [ - "name", - "latestVersion", - "description", - "license", - "homepage", - "repository", - "bugs", - "maintainers", + "id", + "title", + "authors", "keywords", - "created", - "modified", + "venue", + "venueid", + "primary_area", + "abstract", + "pdate", + "pdf", "url" ], "type": "js", - "modulePath": "plugins/npm/package.js", - "sourceFile": "plugins/npm/package.js" + "modulePath": "plugins/openreview/paper.js", + "sourceFile": "plugins/openreview/paper.js" }, { - "site": "npm", - "name": "search", - "description": "Search the public npm registry by keyword", + "site": "openreview", + "name": "reviews", + "description": "Show full review thread (paper + reviews + decisions) for an OpenReview forum", "access": "read", - "domain": "registry.npmjs.org", + "domain": "openreview.net", "strategy": "public", "browser": false, "args": [ { - "name": "query", + "name": "forum", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"react\", \"graphql client\")" + "help": "OpenReview forum id (same as paper id)" }, { - "name": "limit", + "name": "max-length", "type": "int", - "default": 20, + "default": 4000, "required": false, - "help": "Max results (1-250)" + "help": "Per-row text truncation (min 200)" } ], "columns": [ - "rank", - "name", - "version", - "description", - "weeklyDownloads", - "dependents", - "license", - "publisher", - "updated", - "url" - ], - "tags": [ - "search" + "type", + "author", + "rating", + "confidence", + "text" ], "type": "js", - "modulePath": "plugins/npm/search.js", - "sourceFile": "plugins/npm/search.js" + "modulePath": "plugins/openreview/reviews.js", + "sourceFile": "plugins/openreview/reviews.js" }, { - "site": "nuget", - "name": "package", - "description": "Full NuGet package version history (catalogEntry per release)", + "site": "openreview", + "name": "search", + "description": "Search OpenReview papers by free-text query", "access": "read", - "domain": "api.nuget.org", + "domain": "openreview.net", "strategy": "public", "browser": false, "args": [ { - "name": "id", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "NuGet package id (e.g. \"Newtonsoft.Json\", case-insensitive)" + "help": "Search keyword (e.g. \"diffusion model\")" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max results (max 50)" } ], "columns": [ "rank", "id", - "version", "title", "authors", - "tags", - "language", - "licenseExpression", - "projectUrl", - "published", - "listed", + "venue", + "pdate", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/nuget/package.js", - "sourceFile": "plugins/nuget/package.js" + "modulePath": "plugins/openreview/search.js", + "sourceFile": "plugins/openreview/search.js" }, { - "site": "nuget", - "name": "search", - "description": "Search NuGet packages by keyword", + "site": "openreview", + "name": "venue", + "description": "List papers at an OpenReview venue (e.g. \"ICLR 2024 oral\" or full invitation id)", "access": "read", - "domain": "api.nuget.org", + "domain": "openreview.net", "strategy": "public", "browser": false, "args": [ { - "name": "query", + "name": "venue", "type": "str", "required": true, "positional": true, - "help": "Search keyword" + "help": "Venue name (\"ICLR 2024 oral\") or invitation (\"ICLR.cc/2025/Conference/-/Submission\")" }, { "name": "limit", "type": "int", - "default": 20, + "default": 25, "required": false, - "help": "Max packages (1-1000)" + "help": "Max results (max 200)" }, { - "name": "prerelease", - "type": "boolean", - "default": false, + "name": "offset", + "type": "int", + "default": 0, "required": false, - "help": "Include prerelease versions" + "help": "Pagination offset" } ], "columns": [ "rank", "id", - "version", "title", - "description", "authors", - "tags", - "totalDownloads", - "verified", - "projectUrl", + "keywords", + "primary_area", + "pdate", + "pdf", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/nuget/search.js", - "sourceFile": "plugins/nuget/search.js" + "modulePath": "plugins/openreview/venue.js", + "sourceFile": "plugins/openreview/venue.js" }, { - "site": "nvd", - "name": "cve", - "description": "NIST NVD CVE detail (description, CVSS, CWE, KEV flag)", + "site": "osv", + "name": "query", + "description": "OSV.dev vulnerabilities affecting a package (optionally pinned to a version)", "access": "read", - "domain": "services.nvd.nist.gov", + "domain": "osv.dev", "strategy": "public", "browser": false, "args": [ { - "name": "id", - "type": "str", + "name": "package", + "type": "string", "required": true, "positional": true, - "help": "CVE identifier (e.g. \"CVE-2021-44228\")" + "help": "Package name (e.g. \"lodash\", \"django\")" + }, + { + "name": "ecosystem", + "type": "string", + "required": true, + "help": "OSV ecosystem (npm / PyPI / Go / Maven / NuGet / RubyGems / crates.io / Packagist / ...)" + }, + { + "name": "version", + "type": "string", + "required": false, + "help": "Pin to a specific version (e.g. \"4.17.20\"); omit for all known vulns" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max rows to return (1-200)" } ], "columns": [ + "rank", "id", - "published", - "lastModified", - "vulnStatus", - "baseScore", + "summary", "severity", - "attackVector", - "cwe", - "kevAdded", - "description", + "aliases", + "published", + "modified", + "affectedPackages", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/nvd/cve.js", - "sourceFile": "plugins/nvd/cve.js" + "modulePath": "plugins/osv/query.js", + "sourceFile": "plugins/osv/query.js" }, { - "site": "oeis", - "name": "search", - "description": "Search OEIS sequences by keyword or numeric pattern", + "site": "osv", + "name": "vulnerability", + "description": "Single OSV.dev vulnerability detail (severity, affected packages, CVE/GHSA aliases)", "access": "read", - "domain": "oeis.org", + "domain": "osv.dev", "strategy": "public", "browser": false, "args": [ { - "name": "query", - "type": "str", + "name": "id", + "type": "string", "required": true, "positional": true, - "help": "Search keyword or comma-separated terms (e.g. \"fibonacci\", \"1,1,2,3,5,8\")" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max sequences (1-100)" + "help": "OSV vulnerability id (e.g. \"GHSA-29mw-wpgm-hmr9\", \"CVE-2020-28500\")" } ], "columns": [ - "rank", "id", - "name", - "keywords", - "preview", - "author", - "created", - "url" - ], - "tags": [ - "search" + "summary", + "severity", + "aliases", + "published", + "modified", + "affectedPackages", + "cwes", + "referenceCount", + "url" ], "type": "js", - "modulePath": "plugins/oeis/search.js", - "sourceFile": "plugins/oeis/search.js" + "modulePath": "plugins/osv/vulnerability.js", + "sourceFile": "plugins/osv/vulnerability.js" }, { - "site": "oeis", - "name": "sequence", - "description": "Full OEIS sequence detail by A-number (terms, name, keywords, formula counts)", + "site": "packagist", + "name": "package", + "description": "Fetch a Packagist package's metadata (version, downloads, license, repo, GitHub stars)", "access": "read", - "domain": "oeis.org", + "domain": "packagist.org", "strategy": "public", "browser": false, "args": [ { - "name": "id", + "name": "name", "type": "str", "required": true, "positional": true, - "help": "OEIS sequence id (e.g. \"A000045\" for Fibonacci)" + "help": "Composer package \"/\" (e.g. \"symfony/console\", \"monolog/monolog\")" } ], "columns": [ - "id", - "name", - "keywords", - "preview", - "termCount", - "offset", - "author", - "created", - "revision", - "commentCount", - "formulaCount", - "referenceCount", - "xrefCount", - "linkCount", + "package", + "version", + "releasedAt", + "license", + "description", + "repository", + "githubStars", + "favers", + "downloads", + "monthlyDownloads", + "dailyDownloads", "url" ], "type": "js", - "modulePath": "plugins/oeis/sequence.js", - "sourceFile": "plugins/oeis/sequence.js" + "modulePath": "plugins/packagist/package.js", + "sourceFile": "plugins/packagist/package.js" }, { - "site": "openalex", + "site": "packagist", "name": "search", - "description": "Search OpenAlex Works (papers, books, preprints) by keyword", + "description": "Search Packagist (PHP / Composer) packages by keyword", "access": "read", - "domain": "api.openalex.org", + "domain": "packagist.org", "strategy": "public", "browser": false, "args": [ @@ -6189,460 +8195,671 @@ "type": "str", "required": true, "positional": true, - "help": "Search text (e.g. \"transformers\", \"open access scholarly\")" + "help": "Search keyword (e.g. \"symfony\", \"laravel http\")" }, { "name": "limit", "type": "int", - "default": 20, + "default": 30, "required": false, - "help": "Max works (1-200, single OpenAlex page)" + "help": "Max packages (1-100, single Packagist page)" } ], "columns": [ "rank", - "id", - "title", - "year", - "citations", - "firstAuthor", - "venue", - "openAccess", - "type", - "doi", + "package", + "description", + "downloads", + "favers", + "repository", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/openalex/search.js", - "sourceFile": "plugins/openalex/search.js" + "modulePath": "plugins/packagist/search.js", + "sourceFile": "plugins/packagist/search.js" }, { - "site": "openalex", - "name": "work", - "description": "Fetch a single OpenAlex Work (paper / preprint / book) — metadata + abstract", + "site": "pubmed", + "name": "article", + "aliases": [ + "paper", + "read" + ], + "description": "Get detailed information for a PubMed article by PMID", "access": "read", - "domain": "api.openalex.org", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "id", + "name": "pmid", "type": "str", "required": true, "positional": true, - "help": "OpenAlex Work id (\"W2741809807\"), DOI (\"10.7717/peerj.4375\"), or full URL" + "help": "PubMed ID, e.g. 37780221" + }, + { + "name": "full-abstract", + "type": "boolean", + "default": false, + "required": false, + "help": "Do not truncate the abstract in table output" } ], "columns": [ - "id", + "pmid", "title", - "type", + "authors", + "journal", "year", "date", + "article_type", "language", - "authors", - "venue", - "citations", - "openAccess", - "openAccessUrl", - "referencedCount", "doi", + "pmc", + "affiliations", + "grants", + "mesh_terms", + "keywords", "abstract", "url" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/openalex/work.js", - "sourceFile": "plugins/openalex/work.js" + "modulePath": "plugins/pubmed/article.js", + "sourceFile": "plugins/pubmed/article.js" }, { - "site": "openfda", - "name": "drug-label", - "description": "Search FDA-approved drug labels (brand or generic name)", + "site": "pubmed", + "name": "author", + "description": "Search PubMed articles by author name and optional affiliation", "access": "read", - "domain": "fda.gov", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "query", + "name": "name", "type": "str", "required": true, "positional": true, - "help": "Brand or generic drug name (e.g. \"aspirin\", \"lisinopril\")" + "help": "Author name, e.g. \"Smith J\"" }, { "name": "limit", "type": "int", - "default": 5, + "default": 20, "required": false, - "help": "Max rows (1-25, default 5; openFDA caps anonymous tier at 25/page)" + "help": "Max results (1-100)" + }, + { + "name": "affiliation", + "type": "str", + "required": false, + "help": "Filter by author affiliation" + }, + { + "name": "position", + "type": "str", + "default": "any", + "required": false, + "help": "Author position: any, first, or last", + "choices": [ + "any", + "first", + "last" + ] + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "sort", + "type": "str", + "default": "date", + "required": false, + "help": "Sort by date or relevance", + "choices": [ + "date", + "relevance" + ] } ], "columns": [ "rank", - "id", - "brandName", - "genericName", - "manufacturer", - "productType", - "route", - "productNdc", - "pharmClass", - "purpose", - "indications", - "warnings", - "dosage", - "effectiveTime" + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" ], "type": "js", - "modulePath": "plugins/openfda/drug-label.js", - "sourceFile": "plugins/openfda/drug-label.js" + "modulePath": "plugins/pubmed/author.js", + "sourceFile": "plugins/pubmed/author.js" }, { - "site": "openfda", - "name": "food-recall", - "description": "FDA food recall and enforcement actions (most recent first)", + "site": "pubmed", + "name": "citations", + "description": "Get PubMed citation relationships for an article", "access": "read", - "domain": "fda.gov", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "query", - "type": "str", - "required": false, - "help": "Free-text Lucene query (e.g. \"salmonella\", \"listeria\"); default: all recent recalls" - }, - { - "name": "status", + "name": "pmid", "type": "str", - "required": false, - "help": "Filter by status: \"Ongoing\", \"Completed\", \"Terminated\"" + "required": true, + "positional": true, + "help": "PubMed ID, e.g. 37780221" }, { - "name": "classification", + "name": "direction", "type": "str", + "default": "citedby", "required": false, - "help": "Filter by class: \"Class I\" (most serious), \"Class II\", \"Class III\"" + "help": "citedby or references", + "choices": [ + "citedby", + "references" + ] }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Max rows (1-100, default 10; openFDA caps anonymous tier at 100/page)" + "help": "Max results (1-100)" } ], - "columns": [ - "rank", - "recallNumber", - "status", - "classification", - "voluntary", - "recallingFirm", - "city", - "state", - "country", - "productDescription", - "reasonForRecall", - "productQuantity", - "distributionPattern", - "reportDate", - "recallInitiationDate", - "terminationDate" - ], + "columns": [ + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" + ], "type": "js", - "modulePath": "plugins/openfda/food-recall.js", - "sourceFile": "plugins/openfda/food-recall.js" + "modulePath": "plugins/pubmed/citations.js", + "sourceFile": "plugins/pubmed/citations.js" }, { - "site": "openreview", - "name": "author", - "description": "List OpenReview submissions by an author profile id (newest first)", + "site": "pubmed", + "name": "clinical-trial", + "description": "Search PubMed clinical trials with a trial-study preset", "access": "read", - "domain": "openreview.net", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "profile", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "OpenReview profile id (e.g. \"~Yoshua_Bengio1\"). Find it on the author profile URL on openreview.net." + "help": "Clinical topic query, e.g. \"breast cancer\"" }, { "name": "limit", "type": "int", - "default": 50, + "default": 20, "required": false, - "help": "Max submissions (1-1000)" + "help": "Max results (1-100)" + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "free-full-text", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include free full text articles" + }, + { + "name": "sort", + "type": "str", + "default": "date", + "required": false, + "help": "Sort by date or relevance", + "choices": [ + "date", + "relevance" + ] } ], "columns": [ "rank", - "id", + "pmid", "title", "authors", - "venue", - "pdate", + "journal", + "year", + "article_type", + "doi", "url" ], "type": "js", - "modulePath": "plugins/openreview/author.js", - "sourceFile": "plugins/openreview/author.js" + "modulePath": "plugins/pubmed/clinical-trial.js", + "sourceFile": "plugins/pubmed/clinical-trial.js" }, { - "site": "openreview", - "name": "paper", - "description": "Show full metadata for a single OpenReview paper", + "site": "pubmed", + "name": "journal", + "description": "Search PubMed articles by journal name", "access": "read", - "domain": "openreview.net", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "id", + "name": "journal", "type": "str", "required": true, "positional": true, - "help": "OpenReview note id (e.g. \"5sRnsubyAK\")" + "help": "Journal name, e.g. \"Nature\" or \"The Lancet\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance or date", + "choices": [ + "relevance", + "date" + ] } ], "columns": [ - "id", + "rank", + "pmid", "title", "authors", - "keywords", - "venue", - "venueid", - "primary_area", - "abstract", - "pdate", - "pdf", + "journal", + "year", + "article_type", + "doi", "url" ], "type": "js", - "modulePath": "plugins/openreview/paper.js", - "sourceFile": "plugins/openreview/paper.js" + "modulePath": "plugins/pubmed/journal.js", + "sourceFile": "plugins/pubmed/journal.js" }, { - "site": "openreview", - "name": "reviews", - "description": "Show full review thread (paper + reviews + decisions) for an OpenReview forum", + "site": "pubmed", + "name": "mesh", + "description": "Search PubMed articles by MeSH term", "access": "read", - "domain": "openreview.net", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "forum", + "name": "term", "type": "str", "required": true, "positional": true, - "help": "OpenReview forum id (same as paper id)" + "help": "MeSH term, e.g. \"Neoplasms\" or \"Machine Learning\"" }, { - "name": "max-length", + "name": "limit", "type": "int", - "default": 4000, + "default": 20, "required": false, - "help": "Per-row text truncation (min 200)" + "help": "Max results (1-100)" + }, + { + "name": "major", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include articles where this is a major MeSH topic" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance or date", + "choices": [ + "relevance", + "date" + ] } ], "columns": [ - "type", - "author", - "rating", - "confidence", - "text" + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" ], "type": "js", - "modulePath": "plugins/openreview/reviews.js", - "sourceFile": "plugins/openreview/reviews.js" + "modulePath": "plugins/pubmed/mesh.js", + "sourceFile": "plugins/pubmed/mesh.js" }, { - "site": "openreview", - "name": "search", - "description": "Search OpenReview papers by free-text query", + "site": "pubmed", + "name": "related", + "description": "Find articles related to a PubMed article", "access": "read", - "domain": "openreview.net", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "query", + "name": "pmid", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"diffusion model\")" + "help": "PubMed ID, e.g. 37780221" }, { "name": "limit", "type": "int", - "default": 25, + "default": 20, "required": false, - "help": "Max results (max 50)" + "help": "Max results (1-100)" + }, + { + "name": "score", + "type": "boolean", + "default": false, + "required": false, + "help": "Show similarity scores when available" } ], "columns": [ "rank", - "id", + "pmid", "title", "authors", - "venue", - "pdate", + "journal", + "year", + "article_type", + "score", + "doi", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/openreview/search.js", - "sourceFile": "plugins/openreview/search.js" + "modulePath": "plugins/pubmed/related.js", + "sourceFile": "plugins/pubmed/related.js" }, { - "site": "openreview", - "name": "venue", - "description": "List papers at an OpenReview venue (e.g. \"ICLR 2024 oral\" or full invitation id)", + "site": "pubmed", + "name": "review", + "description": "Search PubMed review articles with a review preset", "access": "read", - "domain": "openreview.net", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "venue", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Venue name (\"ICLR 2024 oral\") or invitation (\"ICLR.cc/2025/Conference/-/Submission\")" + "help": "Review topic query, e.g. \"immunotherapy\"" }, { "name": "limit", "type": "int", - "default": 25, + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "has-abstract", + "type": "boolean", + "default": false, "required": false, - "help": "Max results (max 200)" + "help": "Only include articles with abstracts" }, { - "name": "offset", - "type": "int", - "default": 0, + "name": "sort", + "type": "str", + "default": "date", "required": false, - "help": "Pagination offset" + "help": "Sort by date or relevance", + "choices": [ + "date", + "relevance" + ] } ], "columns": [ "rank", - "id", + "pmid", "title", "authors", - "keywords", - "primary_area", - "pdate", - "pdf", + "journal", + "year", + "article_type", + "doi", "url" ], "type": "js", - "modulePath": "plugins/openreview/venue.js", - "sourceFile": "plugins/openreview/venue.js" + "modulePath": "plugins/pubmed/review.js", + "sourceFile": "plugins/pubmed/review.js" }, { - "site": "osv", - "name": "query", - "description": "OSV.dev vulnerabilities affecting a package (optionally pinned to a version)", + "site": "pubmed", + "name": "search", + "description": "Search PubMed articles with advanced filters", "access": "read", - "domain": "osv.dev", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "package", - "type": "string", + "name": "query", + "type": "str", "required": true, "positional": true, - "help": "Package name (e.g. \"lodash\", \"django\")" + "help": "Search query, e.g. \"machine learning cancer\"" }, { - "name": "ecosystem", - "type": "string", - "required": true, - "help": "OSV ecosystem (npm / PyPI / Go / Maven / NuGet / RubyGems / crates.io / Packagist / ...)" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" }, { - "name": "version", - "type": "string", + "name": "author", + "type": "str", "required": false, - "help": "Pin to a specific version (e.g. \"4.17.20\"); omit for all known vulns" + "help": "Filter by author name" }, { - "name": "limit", + "name": "journal", + "type": "str", + "required": false, + "help": "Filter by journal name" + }, + { + "name": "year-from", "type": "int", - "default": 30, "required": false, - "help": "Max rows to return (1-200)" + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "article-type", + "type": "str", + "required": false, + "help": "Filter by publication type, e.g. Review or Clinical Trial" + }, + { + "name": "has-abstract", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include articles with abstracts" + }, + { + "name": "free-full-text", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include free full text articles" + }, + { + "name": "humans-only", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include human studies" + }, + { + "name": "english-only", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include English articles" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance, date, author, or journal", + "choices": [ + "relevance", + "date", + "author", + "journal" + ] } ], "columns": [ "rank", - "id", - "summary", - "severity", - "aliases", - "published", - "modified", - "affectedPackages", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/osv/query.js", - "sourceFile": "plugins/osv/query.js" + "modulePath": "plugins/pubmed/search.js", + "sourceFile": "plugins/pubmed/search.js" }, { - "site": "osv", - "name": "vulnerability", - "description": "Single OSV.dev vulnerability detail (severity, affected packages, CVE/GHSA aliases)", + "site": "pypi", + "name": "downloads", + "description": "PyPI download stats for a package (recent totals or full daily history)", "access": "read", - "domain": "osv.dev", + "domain": "pypistats.org", "strategy": "public", "browser": false, "args": [ { - "name": "id", - "type": "string", + "name": "name", + "type": "str", "required": true, "positional": true, - "help": "OSV vulnerability id (e.g. \"GHSA-29mw-wpgm-hmr9\", \"CVE-2020-28500\")" + "help": "PyPI package name (e.g. \"requests\", \"pandas\")" + }, + { + "name": "period", + "type": "str", + "default": "recent", + "required": false, + "help": "recent (default — 1 row, last day/week/month) or overall (1 row per day)" } ], "columns": [ - "id", - "summary", - "severity", - "aliases", - "published", - "modified", - "affectedPackages", - "cwes", - "referenceCount", - "url" + "rank", + "package", + "period", + "date", + "downloads" ], "type": "js", - "modulePath": "plugins/osv/vulnerability.js", - "sourceFile": "plugins/osv/vulnerability.js" + "modulePath": "plugins/pypi/downloads.js", + "sourceFile": "plugins/pypi/downloads.js" }, { - "site": "packagist", + "site": "pypi", "name": "package", - "description": "Fetch a Packagist package's metadata (version, downloads, license, repo, GitHub stars)", + "description": "Single PyPI package metadata (latest version, license, homepage, classifiers)", "access": "read", - "domain": "packagist.org", + "domain": "pypi.org", "strategy": "public", "browser": false, "args": [ @@ -6651,489 +8868,399 @@ "type": "str", "required": true, "positional": true, - "help": "Composer package \"/\" (e.g. \"symfony/console\", \"monolog/monolog\")" + "help": "PyPI package name (e.g. \"requests\", \"pandas\")" } ], "columns": [ - "package", - "version", - "releasedAt", + "name", + "latestVersion", + "summary", + "author", "license", - "description", + "homepage", "repository", - "githubStars", - "favers", - "downloads", - "monthlyDownloads", - "dailyDownloads", + "requiresPython", + "keywords", + "releases", + "firstReleased", + "lastReleased", "url" ], "type": "js", - "modulePath": "plugins/packagist/package.js", - "sourceFile": "plugins/packagist/package.js" + "modulePath": "plugins/pypi/package.js", + "sourceFile": "plugins/pypi/package.js" }, { - "site": "packagist", - "name": "search", - "description": "Search Packagist (PHP / Composer) packages by keyword", + "site": "pypi", + "name": "releases", + "description": "List recent public PyPI package releases", "access": "read", - "domain": "packagist.org", + "domain": "pypi.org", "strategy": "public", "browser": false, "args": [ { - "name": "query", - "type": "str", + "name": "name", + "type": "string", "required": true, "positional": true, - "help": "Search keyword (e.g. \"symfony\", \"laravel http\")" + "help": "Python package name, for example django" }, { "name": "limit", "type": "int", - "default": 30, + "default": 10, "required": false, - "help": "Max packages (1-100, single Packagist page)" + "help": "Maximum releases to return (1-50)" } ], "columns": [ - "rank", - "package", - "description", - "downloads", - "favers", - "repository", + "version", + "uploadedAt", + "fileCount", + "pythonVersions", + "yanked", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/packagist/search.js", - "sourceFile": "plugins/packagist/search.js" - }, - { - "site": "pubmed", - "name": "article", - "aliases": [ - "paper", - "read" - ], - "description": "Get detailed information for a PubMed article by PMID", + "modulePath": "plugins/pypi/releases.js", + "sourceFile": "plugins/pypi/releases.js" + }, + { + "site": "rest-countries", + "name": "country", + "description": "Look up countries by name (common / official, substring match)", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", + "domain": "restcountries.com", "strategy": "public", "browser": false, "args": [ { - "name": "pmid", + "name": "name", "type": "str", "required": true, "positional": true, - "help": "PubMed ID, e.g. 37780221" + "help": "Country name (e.g. \"japan\", \"united kingdom\")" }, { - "name": "full-abstract", - "type": "boolean", - "default": false, + "name": "limit", + "type": "int", + "default": 25, "required": false, - "help": "Do not truncate the abstract in table output" + "help": "Max rows (1-250)" } ], "columns": [ - "pmid", - "title", - "authors", - "journal", - "year", - "date", - "article_type", - "language", - "doi", - "pmc", - "affiliations", - "grants", - "mesh_terms", - "keywords", - "abstract", + "rank", + "commonName", + "officialName", + "cca2", + "cca3", + "ccn3", + "capital", + "region", + "subregion", + "population", + "area", + "languages", + "currencies", + "latitude", + "longitude", + "timezones", + "independent", + "unMember", + "landlocked", + "flag", "url" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/pubmed/article.js", - "sourceFile": "plugins/pubmed/article.js" + "modulePath": "plugins/rest-countries/country.js", + "sourceFile": "plugins/rest-countries/country.js" }, { - "site": "pubmed", - "name": "author", - "description": "Search PubMed articles by author name and optional affiliation", + "site": "rest-countries", + "name": "region", + "description": "List countries in a region (africa / americas / asia / europe / oceania / antarctic)", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", + "domain": "restcountries.com", "strategy": "public", "browser": false, "args": [ { - "name": "name", + "name": "region", "type": "str", "required": true, "positional": true, - "help": "Author name, e.g. \"Smith J\"" + "help": "Region name (case-insensitive)" }, { "name": "limit", "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "affiliation", - "type": "str", - "required": false, - "help": "Filter by author affiliation" - }, - { - "name": "position", - "type": "str", - "default": "any", - "required": false, - "help": "Author position: any, first, or last", - "choices": [ - "any", - "first", - "last" - ] - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, - { - "name": "sort", - "type": "str", - "default": "date", + "default": 250, "required": false, - "help": "Sort by date or relevance", - "choices": [ - "date", - "relevance" - ] + "help": "Max rows (1-250)" } ], "columns": [ "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", + "commonName", + "officialName", + "cca2", + "cca3", + "ccn3", + "capital", + "region", + "subregion", + "population", + "area", + "languages", + "currencies", + "latitude", + "longitude", + "timezones", + "independent", + "unMember", + "landlocked", + "flag", "url" ], "type": "js", - "modulePath": "plugins/pubmed/author.js", - "sourceFile": "plugins/pubmed/author.js" + "modulePath": "plugins/rest-countries/region.js", + "sourceFile": "plugins/rest-countries/region.js" }, { - "site": "pubmed", - "name": "citations", - "description": "Get PubMed citation relationships for an article", + "site": "rfc", + "name": "rfc", + "description": "Single IETF RFC metadata (title, abstract, working group, authors, std level)", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", + "domain": "datatracker.ietf.org", "strategy": "public", "browser": false, "args": [ { - "name": "pmid", - "type": "str", + "name": "number", + "type": "int", "required": true, "positional": true, - "help": "PubMed ID, e.g. 37780221" - }, - { - "name": "direction", - "type": "str", - "default": "citedby", - "required": false, - "help": "citedby or references", - "choices": [ - "citedby", - "references" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" + "help": "RFC number (e.g. 9000, 791, 2616)" } ], "columns": [ - "rank", - "pmid", + "rfc", "title", + "state", + "stdLevel", + "group", + "groupType", + "pages", + "published", "authors", - "journal", - "year", - "article_type", - "doi", + "abstract", + "rfcEditorUrl", "url" ], "type": "js", - "modulePath": "plugins/pubmed/citations.js", - "sourceFile": "plugins/pubmed/citations.js" + "modulePath": "plugins/rfc/rfc.js", + "sourceFile": "plugins/rfc/rfc.js" }, { - "site": "pubmed", - "name": "clinical-trial", - "description": "Search PubMed clinical trials with a trial-study preset", + "site": "rubygems", + "name": "gem", + "description": "Fetch a RubyGems.org gem's metadata (version, downloads, license, links)", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", + "domain": "rubygems.org", "strategy": "public", "browser": false, "args": [ { - "name": "query", + "name": "name", "type": "str", "required": true, "positional": true, - "help": "Clinical topic query, e.g. \"breast cancer\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, + "help": "Gem name (e.g. \"rails\", \"sidekiq\")" + } + ], + "columns": [ + "gem", + "version", + "releasedAt", + "downloads", + "versionDownloads", + "license", + "authors", + "homepage", + "source", + "bugs", + "info", + "url" + ], + "type": "js", + "modulePath": "plugins/rubygems/gem.js", + "sourceFile": "plugins/rubygems/gem.js" + }, + { + "site": "rubygems", + "name": "search", + "description": "Search RubyGems.org gems by keyword", + "access": "read", + "domain": "rubygems.org", + "strategy": "public", + "browser": false, + "args": [ { - "name": "free-full-text", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include free full text articles" + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"rails\", \"redis\")" }, { - "name": "sort", - "type": "str", - "default": "date", + "name": "limit", + "type": "int", + "default": 30, "required": false, - "help": "Sort by date or relevance", - "choices": [ - "date", - "relevance" - ] + "help": "Max gems (1-100, single RubyGems page)" } ], "columns": [ "rank", - "pmid", - "title", + "gem", + "version", + "downloads", + "license", "authors", - "journal", - "year", - "article_type", - "doi", + "info", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/pubmed/clinical-trial.js", - "sourceFile": "plugins/pubmed/clinical-trial.js" + "modulePath": "plugins/rubygems/search.js", + "sourceFile": "plugins/rubygems/search.js" }, { - "site": "pubmed", - "name": "journal", - "description": "Search PubMed articles by journal name", + "site": "semanticscholar", + "name": "citations", + "description": "List papers that cite a Semantic Scholar paper (paginated)", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", + "domain": "api.semanticscholar.org", "strategy": "public", "browser": false, "args": [ { - "name": "journal", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Journal name, e.g. \"Nature\" or \"The Lancet\"" + "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max results (1-100)" - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" + "help": "Max citing papers (1-1000, single Semantic Scholar page)" }, { - "name": "year-to", + "name": "offset", "type": "int", + "default": 0, "required": false, - "help": "Filter publication year to" - }, - { - "name": "sort", - "type": "str", - "default": "relevance", - "required": false, - "help": "Sort by relevance or date", - "choices": [ - "relevance", - "date" - ] + "help": "Page offset (0-based)" } ], "columns": [ "rank", - "pmid", + "paperId", + "doi", "title", - "authors", - "journal", "year", - "article_type", - "doi", + "firstAuthor", + "citationCount", "url" ], "type": "js", - "modulePath": "plugins/pubmed/journal.js", - "sourceFile": "plugins/pubmed/journal.js" + "modulePath": "plugins/semanticscholar/citations.js", + "sourceFile": "plugins/semanticscholar/citations.js" }, { - "site": "pubmed", - "name": "mesh", - "description": "Search PubMed articles by MeSH term", + "site": "semanticscholar", + "name": "paper", + "description": "Semantic Scholar paper detail (citation graph + AI tldr) by paperId, DOI, or arXiv id", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", + "domain": "api.semanticscholar.org", "strategy": "public", "browser": false, "args": [ { - "name": "term", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "MeSH term, e.g. \"Neoplasms\" or \"Machine Learning\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "major", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include articles where this is a major MeSH topic" - }, - { - "name": "sort", - "type": "str", - "default": "relevance", - "required": false, - "help": "Sort by relevance or date", - "choices": [ - "relevance", - "date" - ] + "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id (e.g. \"ARXIV:1706.03762\", \"PMID:12345\")" } ], "columns": [ - "rank", - "pmid", + "paperId", + "doi", "title", - "authors", - "journal", "year", - "article_type", - "doi", + "firstAuthor", + "citationCount", + "influentialCitationCount", + "referenceCount", + "tldr", "url" ], "type": "js", - "modulePath": "plugins/pubmed/mesh.js", - "sourceFile": "plugins/pubmed/mesh.js" + "modulePath": "plugins/semanticscholar/paper.js", + "sourceFile": "plugins/semanticscholar/paper.js" }, { - "site": "pubmed", - "name": "related", - "description": "Find articles related to a PubMed article", + "site": "semanticscholar", + "name": "recommendations", + "description": "Semantic Scholar AI-curated related papers for a paperId, DOI, or arXiv id", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", + "domain": "api.semanticscholar.org", "strategy": "public", "browser": false, "args": [ { - "name": "pmid", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "PubMed ID, e.g. 37780221" + "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" }, { "name": "limit", "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "score", - "type": "boolean", - "default": false, + "default": 10, "required": false, - "help": "Show similarity scores when available" + "help": "Max recommendations (1-500)" } ], "columns": [ "rank", - "pmid", + "paperId", + "doi", "title", - "authors", - "journal", "year", - "article_type", - "score", - "doi", + "firstAuthor", + "citationCount", "url" ], "type": "js", - "modulePath": "plugins/pubmed/related.js", - "sourceFile": "plugins/pubmed/related.js" + "modulePath": "plugins/semanticscholar/recommendations.js", + "sourceFile": "plugins/semanticscholar/recommendations.js" }, { - "site": "pubmed", - "name": "review", - "description": "Search PubMed review articles with a review preset", + "site": "semanticscholar", + "name": "search", + "description": "Search Semantic Scholar papers by free text", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", + "domain": "api.semanticscholar.org", "strategy": "public", "browser": false, "args": [ @@ -7142,1243 +9269,1385 @@ "type": "str", "required": true, "positional": true, - "help": "Review topic query, e.g. \"immunotherapy\"" + "help": "Search text (e.g. \"attention is all you need\", \"diffusion model\")" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max results (1-100)" - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, - { - "name": "has-abstract", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include articles with abstracts" - }, - { - "name": "sort", - "type": "str", - "default": "date", - "required": false, - "help": "Sort by date or relevance", - "choices": [ - "date", - "relevance" - ] + "help": "Max papers (1-100, single Semantic Scholar page)" } ], "columns": [ "rank", - "pmid", + "paperId", + "doi", "title", - "authors", - "journal", "year", - "article_type", - "doi", + "firstAuthor", + "citationCount", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/pubmed/review.js", - "sourceFile": "plugins/pubmed/review.js" + "modulePath": "plugins/semanticscholar/search.js", + "sourceFile": "plugins/semanticscholar/search.js" }, { - "site": "pubmed", - "name": "search", - "description": "Search PubMed articles with advanced filters", + "site": "skyscanner", + "name": "flights", + "description": "Skyscanner visible round-trip flight results from a warmed browser session", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, + "domain": "www.skyscanner.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", + "name": "origin", "type": "str", "required": true, "positional": true, - "help": "Search query, e.g. \"machine learning cancer\"" + "help": "Skyscanner origin route code, for example nyca" }, { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" + "name": "destination", + "type": "str", + "required": true, + "positional": true, + "help": "Skyscanner destination route code, for example lond" }, { - "name": "author", + "name": "depart-date", "type": "str", - "required": false, - "help": "Filter by author name" + "required": true, + "help": "Outbound date as YYYY-MM-DD" }, { - "name": "journal", + "name": "return-date", "type": "str", - "required": false, - "help": "Filter by journal name" + "required": true, + "help": "Return date as YYYY-MM-DD" }, { - "name": "year-from", + "name": "limit", "type": "int", + "default": 10, "required": false, - "help": "Filter publication year from" - }, + "help": "Maximum flight rows to return (1-30)" + } + ], + "columns": [ + "rank", + "priceText", + "airlines", + "outboundTime", + "outboundRoute", + "outboundDuration", + "outboundStops", + "returnTime", + "returnRoute", + "returnDuration", + "returnStops", + "url" + ], + "type": "js", + "modulePath": "plugins/skyscanner/flights.js", + "sourceFile": "plugins/skyscanner/flights.js", + "navigateBefore": false + }, + { + "site": "stackoverflow", + "name": "bounties", + "description": "Active bounties on Stack Overflow", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ { - "name": "year-to", + "name": "limit", "type": "int", + "default": 10, "required": false, - "help": "Filter publication year to" - }, - { - "name": "article-type", - "type": "str", - "required": false, - "help": "Filter by publication type, e.g. Review or Clinical Trial" - }, - { - "name": "has-abstract", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include articles with abstracts" - }, - { - "name": "free-full-text", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include free full text articles" - }, - { - "name": "humans-only", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include human studies" - }, - { - "name": "english-only", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include English articles" - }, - { - "name": "sort", - "type": "str", - "default": "relevance", - "required": false, - "help": "Sort by relevance, date, author, or journal", - "choices": [ - "relevance", - "date", - "author", - "journal" - ] + "help": "Max number of results" } ], "columns": [ "rank", - "pmid", + "id", + "bounty", "title", - "authors", - "journal", - "year", - "article_type", - "doi", + "score", + "answers", + "views", + "is_answered", + "tags", + "author", + "creation_date", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/pubmed/search.js", - "sourceFile": "plugins/pubmed/search.js" + "modulePath": "plugins/stackoverflow/bounties.js", + "sourceFile": "plugins/stackoverflow/bounties.js" }, { - "site": "pypi", - "name": "downloads", - "description": "PyPI download stats for a package (recent totals or full daily history)", + "site": "stackoverflow", + "name": "hot", + "description": "Hot Stack Overflow questions", "access": "read", - "domain": "pypistats.org", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "PyPI package name (e.g. \"requests\", \"pandas\")" - }, - { - "name": "period", - "type": "str", - "default": "recent", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "recent (default — 1 row, last day/week/month) or overall (1 row per day)" + "help": "Max number of results" } ], "columns": [ "rank", - "package", - "period", - "date", - "downloads" + "id", + "title", + "score", + "answers", + "views", + "is_answered", + "tags", + "author", + "creation_date", + "url" ], "type": "js", - "modulePath": "plugins/pypi/downloads.js", - "sourceFile": "plugins/pypi/downloads.js" + "modulePath": "plugins/stackoverflow/hot.js", + "sourceFile": "plugins/stackoverflow/hot.js" }, { - "site": "pypi", - "name": "package", - "description": "Single PyPI package metadata (latest version, license, homepage, classifiers)", + "site": "stackoverflow", + "name": "read", + "description": "Read a Stack Overflow question with answers and comments", "access": "read", - "domain": "pypi.org", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ { - "name": "name", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "PyPI package name (e.g. \"requests\", \"pandas\")" + "help": "Stack Overflow question id (numeric, e.g. 79935770)" + }, + { + "name": "answers-limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max answers to include (1-100; accepted answer always included first)" + }, + { + "name": "comments-limit", + "type": "int", + "default": 5, + "required": false, + "help": "Max comments per question/answer (1-100)" + }, + { + "name": "max-length", + "type": "int", + "default": 4000, + "required": false, + "help": "Max characters per body / answer / comment (min 100)" } ], "columns": [ - "name", - "latestVersion", - "summary", + "type", "author", - "license", - "homepage", - "repository", - "requiresPython", - "keywords", - "releases", - "firstReleased", - "lastReleased", - "url" + "score", + "accepted", + "text" ], "type": "js", - "modulePath": "plugins/pypi/package.js", - "sourceFile": "plugins/pypi/package.js" + "modulePath": "plugins/stackoverflow/read.js", + "sourceFile": "plugins/stackoverflow/read.js" }, { - "site": "pypi", - "name": "releases", - "description": "List recent public PyPI package releases", + "site": "stackoverflow", + "name": "related", + "description": "List Stack Overflow questions related to a given question id.", "access": "read", - "domain": "pypi.org", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ { - "name": "name", + "name": "id", "type": "string", "required": true, "positional": true, - "help": "Python package name, for example django" + "help": "Stack Overflow question id (numeric, e.g. 79935770)." + }, + { + "name": "sort", + "type": "string", + "default": "rank", + "required": false, + "help": "Sort key: rank, activity, votes, creation (rank = SO relevance default)." }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Maximum releases to return (1-50)" + "help": "Max related questions (1-100)." } ], "columns": [ - "version", - "uploadedAt", - "fileCount", - "pythonVersions", - "yanked", + "rank", + "id", + "title", + "score", + "answers", + "views", + "isAnswered", + "tags", + "author", + "createdAt", + "lastActivityAt", "url" ], "type": "js", - "modulePath": "plugins/pypi/releases.js", - "sourceFile": "plugins/pypi/releases.js" + "modulePath": "plugins/stackoverflow/related.js", + "sourceFile": "plugins/stackoverflow/related.js" }, { - "site": "rest-countries", - "name": "country", - "description": "Look up countries by name (common / official, substring match)", + "site": "stackoverflow", + "name": "search", + "description": "Search Stack Overflow questions", "access": "read", - "domain": "restcountries.com", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ { - "name": "name", - "type": "str", + "name": "query", + "type": "string", "required": true, "positional": true, - "help": "Country name (e.g. \"japan\", \"united kingdom\")" + "help": "Search query" }, { "name": "limit", "type": "int", - "default": 25, + "default": 10, "required": false, - "help": "Max rows (1-250)" + "help": "Max number of results" } ], "columns": [ "rank", - "commonName", - "officialName", - "cca2", - "cca3", - "ccn3", - "capital", - "region", - "subregion", - "population", - "area", - "languages", - "currencies", - "latitude", - "longitude", - "timezones", - "independent", - "unMember", - "landlocked", - "flag", + "id", + "title", + "score", + "answers", + "views", + "is_answered", + "tags", + "author", + "creation_date", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/rest-countries/country.js", - "sourceFile": "plugins/rest-countries/country.js" + "modulePath": "plugins/stackoverflow/search.js", + "sourceFile": "plugins/stackoverflow/search.js" }, { - "site": "rest-countries", - "name": "region", - "description": "List countries in a region (africa / americas / asia / europe / oceania / antarctic)", + "site": "stackoverflow", + "name": "tag", + "description": "List Stack Overflow questions tagged with a given tag (most active first).", "access": "read", - "domain": "restcountries.com", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ { - "name": "region", - "type": "str", + "name": "tag", + "type": "string", "required": true, "positional": true, - "help": "Region name (case-insensitive)" + "help": "Tag slug (e.g. python, rust, typescript)." + }, + { + "name": "sort", + "type": "string", + "default": "activity", + "required": false, + "help": "Sort key: activity, votes, creation, hot, week, month" }, { "name": "limit", "type": "int", - "default": 250, + "default": 20, "required": false, - "help": "Max rows (1-250)" + "help": "Max questions to return (max 100)." } ], "columns": [ "rank", - "commonName", - "officialName", - "cca2", - "cca3", - "ccn3", - "capital", - "region", - "subregion", - "population", - "area", - "languages", - "currencies", - "latitude", - "longitude", - "timezones", - "independent", - "unMember", - "landlocked", - "flag", + "id", + "title", + "score", + "answers", + "views", + "isAnswered", + "tags", + "author", + "createdAt", + "lastActivityAt", "url" ], "type": "js", - "modulePath": "plugins/rest-countries/region.js", - "sourceFile": "plugins/rest-countries/region.js" + "modulePath": "plugins/stackoverflow/tag.js", + "sourceFile": "plugins/stackoverflow/tag.js" }, { - "site": "rfc", - "name": "rfc", - "description": "Single IETF RFC metadata (title, abstract, working group, authors, std level)", + "site": "stackoverflow", + "name": "unanswered", + "description": "Top voted unanswered questions on Stack Overflow", "access": "read", - "domain": "datatracker.ietf.org", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ { - "name": "number", + "name": "limit", "type": "int", - "required": true, - "positional": true, - "help": "RFC number (e.g. 9000, 791, 2616)" + "default": 10, + "required": false, + "help": "Max number of results" } ], "columns": [ - "rfc", + "rank", + "id", "title", - "state", - "stdLevel", - "group", - "groupType", - "pages", - "published", - "authors", - "abstract", - "rfcEditorUrl", + "score", + "answers", + "views", + "tags", + "author", + "creation_date", "url" ], "type": "js", - "modulePath": "plugins/rfc/rfc.js", - "sourceFile": "plugins/rfc/rfc.js" + "modulePath": "plugins/stackoverflow/unanswered.js", + "sourceFile": "plugins/stackoverflow/unanswered.js" }, { - "site": "rubygems", - "name": "gem", - "description": "Fetch a RubyGems.org gem's metadata (version, downloads, license, links)", + "site": "stackoverflow", + "name": "user", + "description": "Find Stack Overflow users by display name (highest reputation first).", "access": "read", - "domain": "rubygems.org", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ { "name": "name", - "type": "str", + "type": "string", "required": true, "positional": true, - "help": "Gem name (e.g. \"rails\", \"sidekiq\")" + "help": "Display name (or substring) to search." + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max users to return (max 100)." } ], "columns": [ - "gem", - "version", - "releasedAt", - "downloads", - "versionDownloads", - "license", - "authors", - "homepage", - "source", - "bugs", - "info", + "userId", + "displayName", + "reputation", + "goldBadges", + "silverBadges", + "bronzeBadges", + "location", + "createdAt", + "lastAccessAt", "url" ], "type": "js", - "modulePath": "plugins/rubygems/gem.js", - "sourceFile": "plugins/rubygems/gem.js" + "modulePath": "plugins/stackoverflow/user.js", + "sourceFile": "plugins/stackoverflow/user.js" }, { - "site": "rubygems", - "name": "search", - "description": "Search RubyGems.org gems by keyword", + "site": "steam", + "name": "app", + "description": "Steam storefront detail for a single app id", "access": "read", - "domain": "rubygems.org", + "domain": "store.steampowered.com", "strategy": "public", "browser": false, "args": [ { - "name": "query", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"rails\", \"redis\")" + "help": "Numeric Steam app id (e.g. \"620\" for Portal 2)" }, { - "name": "limit", - "type": "int", - "default": 30, + "name": "currency", + "type": "str", + "default": "us", "required": false, - "help": "Max gems (1-100, single RubyGems page)" + "help": "Storefront country code (e.g. us / cn / jp / de)" } ], "columns": [ - "rank", - "gem", - "version", - "downloads", - "license", - "authors", - "info", + "id", + "name", + "type", + "isFree", + "releaseDate", + "developers", + "publishers", + "price", + "currency", + "metacritic", + "recommendations", + "genres", + "categories", + "shortDescription", + "website", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/rubygems/search.js", - "sourceFile": "plugins/rubygems/search.js" + "modulePath": "plugins/steam/app.js", + "sourceFile": "plugins/steam/app.js" }, { - "site": "semanticscholar", - "name": "citations", - "description": "List papers that cite a Semantic Scholar paper (paginated)", + "site": "steam", + "name": "search", + "description": "Search the Steam storefront by name keyword", "access": "read", - "domain": "api.semanticscholar.org", + "domain": "store.steampowered.com", "strategy": "public", "browser": false, "args": [ { - "name": "id", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" + "help": "Search keyword (e.g. \"portal\", \"stardew\")" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max citing papers (1-1000, single Semantic Scholar page)" + "help": "Max results (1-50)" }, { - "name": "offset", - "type": "int", - "default": 0, + "name": "currency", + "type": "str", + "default": "us", "required": false, - "help": "Page offset (0-based)" + "help": "Storefront country code (e.g. us / cn / jp / de)" } ], "columns": [ "rank", - "paperId", - "doi", - "title", - "year", - "firstAuthor", - "citationCount", + "id", + "name", + "price", + "currency", + "metascore", + "platforms", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/semanticscholar/citations.js", - "sourceFile": "plugins/semanticscholar/citations.js" + "modulePath": "plugins/steam/search.js", + "sourceFile": "plugins/steam/search.js" }, { - "site": "semanticscholar", - "name": "paper", - "description": "Semantic Scholar paper detail (citation graph + AI tldr) by paperId, DOI, or arXiv id", + "site": "steam", + "name": "top-sellers", + "description": "Steam top selling games", "access": "read", - "domain": "api.semanticscholar.org", + "domain": "store.steampowered.com", "strategy": "public", "browser": false, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id (e.g. \"ARXIV:1706.03762\", \"PMID:12345\")" + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of games" } ], "columns": [ - "paperId", - "doi", - "title", - "year", - "firstAuthor", - "citationCount", - "influentialCitationCount", - "referenceCount", - "tldr", + "rank", + "name", + "price", + "discount", "url" ], "type": "js", - "modulePath": "plugins/semanticscholar/paper.js", - "sourceFile": "plugins/semanticscholar/paper.js" + "modulePath": "plugins/steam/top-sellers.js", + "sourceFile": "plugins/steam/top-sellers.js" }, { - "site": "semanticscholar", - "name": "recommendations", - "description": "Semantic Scholar AI-curated related papers for a paperId, DOI, or arXiv id", + "site": "techcrunch", + "name": "article", + "description": "Read a TechCrunch article from its URL", "access": "read", - "domain": "api.semanticscholar.org", + "domain": "techcrunch.com", "strategy": "public", "browser": false, "args": [ { - "name": "id", - "type": "str", + "name": "url", + "type": "string", "required": true, "positional": true, - "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max recommendations (1-500)" + "help": "TechCrunch article URL" } ], "columns": [ - "rank", - "paperId", - "doi", "title", - "year", - "firstAuthor", - "citationCount", + "author", + "publishedAt", + "categories", + "description", + "content", "url" ], "type": "js", - "modulePath": "plugins/semanticscholar/recommendations.js", - "sourceFile": "plugins/semanticscholar/recommendations.js" + "modulePath": "plugins/techcrunch/article.js", + "sourceFile": "plugins/techcrunch/article.js" }, { - "site": "semanticscholar", + "site": "techcrunch", "name": "search", - "description": "Search Semantic Scholar papers by free text", + "description": "Search TechCrunch stories or list the latest stories", "access": "read", - "domain": "api.semanticscholar.org", + "domain": "techcrunch.com", "strategy": "public", "browser": false, "args": [ { "name": "query", - "type": "str", - "required": true, + "type": "string", + "required": false, "positional": true, - "help": "Search text (e.g. \"attention is all you need\", \"diffusion model\")" + "help": "Words to search for" + }, + { + "name": "latest", + "type": "boolean", + "default": false, + "required": false, + "help": "List the latest stories instead of searching" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max papers (1-100, single Semantic Scholar page)" + "help": "Maximum stories to return (1-50)" } ], "columns": [ "rank", - "paperId", - "doi", "title", - "year", - "firstAuthor", - "citationCount", + "author", + "publishedAt", + "description", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/semanticscholar/search.js", - "sourceFile": "plugins/semanticscholar/search.js" + "modulePath": "plugins/techcrunch/search.js", + "sourceFile": "plugins/techcrunch/search.js" }, { - "site": "skyscanner", - "name": "flights", - "description": "Skyscanner visible round-trip flight results from a warmed browser session", + "site": "trae-solo", + "name": "automation-list", + "description": "List Trae SOLO Automation tab content. Default tab is \"Configured\"; pass --tab to switch.", "access": "read", - "domain": "www.skyscanner.com", + "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "origin", - "type": "str", - "required": true, - "positional": true, - "help": "Skyscanner origin route code, for example nyca" - }, - { - "name": "destination", + "name": "tab", "type": "str", - "required": true, - "positional": true, - "help": "Skyscanner destination route code, for example lond" + "default": "configured", + "required": false, + "help": "Tab to view: configured / run-history / task-template" }, { - "name": "depart-date", - "type": "str", - "required": true, - "help": "Outbound date as YYYY-MM-DD" - }, + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "" + } + ], + "columns": [ + "Index", + "Title", + "Summary" + ], + "type": "js", + "modulePath": "plugins/trae-solo/automation.js", + "sourceFile": "plugins/trae-solo/automation.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "cookies", + "description": "List cookies on the Trae SOLO renderer (JS-visible via document.cookie; httpOnly cookies not shown).", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version" + ], + "type": "js", + "modulePath": "plugins/trae-solo/renderer-storage.js", + "sourceFile": "plugins/trae-solo/renderer-storage.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "extensions-list", + "description": "List VSCode extensions installed in Trae SOLO (~/.trae/extensions/extensions.json). Works while Trae is closed.", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "Index", + "Workspace Id", + "Kind", + "Target", + "Modified", + "Id", + "Version", + "Source", + "Installed" + ], + "type": "js", + "modulePath": "plugins/trae-solo/workspaces-fs.js", + "sourceFile": "plugins/trae-solo/workspaces-fs.js" + }, + { + "site": "trae-solo", + "name": "history", + "description": "List Trae SOLO projects and the tasks within each (from the project-list view sidebar).", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ { - "name": "return-date", + "name": "project", "type": "str", - "required": true, - "help": "Return date as YYYY-MM-DD" + "required": false, + "help": "Filter by project name (substring, case-insensitive)" }, { "name": "limit", "type": "int", - "default": 10, + "default": 100, "required": false, - "help": "Maximum flight rows to return (1-30)" + "help": "Max tasks per project" } ], "columns": [ - "rank", - "priceText", - "airlines", - "outboundTime", - "outboundRoute", - "outboundDuration", - "outboundStops", - "returnTime", - "returnRoute", - "returnDuration", - "returnStops", - "url" + "Project", + "Task Index", + "Task" ], "type": "js", - "modulePath": "plugins/skyscanner/flights.js", - "sourceFile": "plugins/skyscanner/flights.js", - "navigateBefore": false + "modulePath": "plugins/trae-solo/history.js", + "sourceFile": "plugins/trae-solo/history.js", + "navigateBefore": true }, { - "site": "stackoverflow", - "name": "bounties", - "description": "Active bounties on Stack Overflow", + "site": "trae-solo", + "name": "idb-list", + "description": "List IndexedDB databases on the Trae SOLO renderer. Trae ships an @byted/ve-rtc DB used by the Volcengine RTC voice/video infrastructure.", "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version" + ], + "type": "js", + "modulePath": "plugins/trae-solo/renderer-storage.js", + "sourceFile": "plugins/trae-solo/renderer-storage.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "mode", + "description": "Read or switch TRAE SOLO between Code mode and Work mode.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "target", + "type": "str", + "required": false, + "positional": true, + "help": "Target mode: code or work. Omit to read current." + } + ], + "columns": [ + "Status", + "Mode" + ], + "type": "js", + "modulePath": "plugins/trae-solo/mode.js", + "sourceFile": "plugins/trae-solo/mode.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "model", + "description": "Read or switch the current AI model in TRAE SOLO. Without arguments, reports the current model. With argument (substring, case-insensitive), switches to a matching model. Pass --list to enumerate available models.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 10, + "name": "name", + "type": "str", "required": false, - "help": "Max number of results" + "positional": true, + "help": "Target model name (substring match, case-insensitive). Omit to read current." + }, + { + "name": "list", + "type": "boolean", + "default": false, + "required": false, + "help": "List all available models (does not switch)" } ], "columns": [ - "rank", - "id", - "bounty", - "title", - "score", - "answers", - "views", - "is_answered", - "tags", - "author", - "creation_date", - "url" + "Status", + "Model" ], "type": "js", - "modulePath": "plugins/stackoverflow/bounties.js", - "sourceFile": "plugins/stackoverflow/bounties.js" + "modulePath": "plugins/trae-solo/model.js", + "sourceFile": "plugins/trae-solo/model.js", + "navigateBefore": true }, { - "site": "stackoverflow", - "name": "hot", - "description": "Hot Stack Overflow questions", + "site": "trae-solo", + "name": "recent-workspaces", + "description": "Show Trae SOLO's recently-opened workspaces (the File → Open Recent menu, stored under key \"history.recentlyOpenedPathsList\" in state.vscdb).", "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Max number of results" + "help": "" } ], "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "is_answered", - "tags", - "author", - "creation_date", - "url" + "Index", + "Key", + "Kind", + "Path" ], "type": "js", - "modulePath": "plugins/stackoverflow/hot.js", - "sourceFile": "plugins/stackoverflow/hot.js" + "modulePath": "plugins/trae-solo/state-fs.js", + "sourceFile": "plugins/trae-solo/state-fs.js" }, { - "site": "stackoverflow", - "name": "read", - "description": "Read a Stack Overflow question with answers and comments", + "site": "trae-solo", + "name": "settings-read", + "description": "Parse and pretty-print Trae SOLO user settings.json (~/Library/Application Support/TRAE SOLO/User/settings.json). Handles VSCode JSONC syntax (line comments + trailing commas).", "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, + "args": [], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "plugins/trae-solo/settings.js", + "sourceFile": "plugins/trae-solo/settings.js" + }, + { + "site": "trae-solo", + "name": "skill-category", + "description": "Filter Skills Marketplace by category. Pass --list to see categories.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "id", + "name": "name", "type": "str", - "required": true, + "required": false, "positional": true, - "help": "Stack Overflow question id (numeric, e.g. 79935770)" + "help": "Category name (substring; case-insensitive). Common: All / Developer Tools / Data Analysis / UI Design / Content Creation / Productivity" }, { - "name": "answers-limit", - "type": "int", - "default": 10, + "name": "list", + "type": "boolean", + "default": false, "required": false, - "help": "Max answers to include (1-100; accepted answer always included first)" + "help": "List available categories" }, { - "name": "comments-limit", + "name": "limit", "type": "int", - "default": 5, + "default": 100, "required": false, - "help": "Max comments per question/answer (1-100)" - }, + "help": "" + } + ], + "columns": [ + "Index", + "Name", + "Description" + ], + "type": "js", + "modulePath": "plugins/trae-solo/skill.js", + "sourceFile": "plugins/trae-solo/skill.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "skill-fs-installed", + "description": "List INSTALLED Trae SOLO skills (managedSkills entry in ~/.trae/skill-config.json).", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "Index", + "Name", + "Description", + "Source" + ], + "type": "js", + "modulePath": "plugins/trae-solo/skill-fs.js", + "sourceFile": "plugins/trae-solo/skill-fs.js" + }, + { + "site": "trae-solo", + "name": "skill-fs-list", + "description": "List all Trae SOLO skills present on disk under ~/.trae/skills/. Reads SKILL.md front-matter for descriptions. Works while Trae is closed.", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ { - "name": "max-length", + "name": "limit", "type": "int", - "default": 4000, + "default": 200, "required": false, - "help": "Max characters per body / answer / comment (min 100)" + "help": "Max rows" } ], "columns": [ - "type", - "author", - "score", - "accepted", - "text" + "Index", + "Name", + "Description", + "Source" ], "type": "js", - "modulePath": "plugins/stackoverflow/read.js", - "sourceFile": "plugins/stackoverflow/read.js" + "modulePath": "plugins/trae-solo/skill-fs.js", + "sourceFile": "plugins/trae-solo/skill-fs.js" }, { - "site": "stackoverflow", - "name": "related", - "description": "List Stack Overflow questions related to a given question id.", + "site": "trae-solo", + "name": "skill-fs-show", + "description": "Print a skill's SKILL.md content + on-disk path.", "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ { - "name": "id", - "type": "string", + "name": "name", + "type": "str", "required": true, "positional": true, - "help": "Stack Overflow question id (numeric, e.g. 79935770)." - }, + "help": "Skill name (folder under ~/.trae/skills/)" + } + ], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "plugins/trae-solo/skill-fs.js", + "sourceFile": "plugins/trae-solo/skill-fs.js" + }, + { + "site": "trae-solo", + "name": "skill-list", + "description": "List Trae SOLO Skills — by default the Marketplace; pass --installed to list installed ones.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ { - "name": "sort", - "type": "string", - "default": "rank", + "name": "installed", + "type": "boolean", + "default": false, "required": false, - "help": "Sort key: rank, activity, votes, creation (rank = SO relevance default)." + "help": "List installed skills instead of the marketplace" }, { "name": "limit", "type": "int", - "default": 20, + "default": 100, "required": false, - "help": "Max related questions (1-100)." + "help": "Max rows to return" } ], "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "isAnswered", - "tags", - "author", - "createdAt", - "lastActivityAt", - "url" + "Index", + "Name", + "Description" ], "type": "js", - "modulePath": "plugins/stackoverflow/related.js", - "sourceFile": "plugins/stackoverflow/related.js" + "modulePath": "plugins/trae-solo/skill.js", + "sourceFile": "plugins/trae-solo/skill.js", + "navigateBefore": true }, { - "site": "stackoverflow", - "name": "search", - "description": "Search Stack Overflow questions", + "site": "trae-solo", + "name": "skill-search", + "description": "Filter Skills Marketplace by keyword.", "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", - "type": "string", + "name": "keyword", + "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "Search keyword (substring)" }, { "name": "limit", "type": "int", - "default": 10, + "default": 50, "required": false, - "help": "Max number of results" + "help": "Max rows" } ], "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "is_answered", - "tags", - "author", - "creation_date", - "url" + "Index", + "Name", + "Description" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/stackoverflow/search.js", - "sourceFile": "plugins/stackoverflow/search.js" + "modulePath": "plugins/trae-solo/skill.js", + "sourceFile": "plugins/trae-solo/skill.js", + "navigateBefore": true }, { - "site": "stackoverflow", - "name": "tag", - "description": "List Stack Overflow questions tagged with a given tag (most active first).", + "site": "trae-solo", + "name": "state-get", + "description": "Read a single key from Trae SOLO's globalStorage state.vscdb. Pass --workspace to query a per-workspace DB instead. Returns parsed JSON if the value is JSON.", "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ { - "name": "tag", - "type": "string", + "name": "key", + "type": "str", "required": true, "positional": true, - "help": "Tag slug (e.g. python, rust, typescript)." + "help": "State key (use state-keys to discover)" }, { - "name": "sort", - "type": "string", - "default": "activity", + "name": "workspace", + "type": "str", "required": false, - "help": "Sort key: activity, votes, creation, hot, week, month" + "help": "Workspace id (from workspaces-list) to query a per-workspace DB" }, { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max questions to return (max 100)." - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "isAnswered", - "tags", - "author", - "createdAt", - "lastActivityAt", - "url" - ], - "type": "js", - "modulePath": "plugins/stackoverflow/tag.js", - "sourceFile": "plugins/stackoverflow/tag.js" - }, - { - "site": "stackoverflow", - "name": "unanswered", - "description": "Top voted unanswered questions on Stack Overflow", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", + "name": "max-bytes", "type": "int", - "default": 10, + "default": 8000, "required": false, - "help": "Max number of results" + "help": "Truncate value to this many bytes" } ], - "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "tags", - "author", - "creation_date", - "url" - ], + "columns": [ + "Field", + "Value" + ], "type": "js", - "modulePath": "plugins/stackoverflow/unanswered.js", - "sourceFile": "plugins/stackoverflow/unanswered.js" + "modulePath": "plugins/trae-solo/state-fs.js", + "sourceFile": "plugins/trae-solo/state-fs.js" }, { - "site": "stackoverflow", - "name": "user", - "description": "Find Stack Overflow users by display name (highest reputation first).", + "site": "trae-solo", + "name": "state-keys", + "description": "List all keys present in Trae SOLO's globalStorage state.vscdb (VSCode-style UI/agent state). Pass --workspace to query a per-workspace DB instead. Use state-get to read a specific value. (See renderer storage-keys for browser-side LS/SS.)", "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ { - "name": "name", - "type": "string", - "required": true, - "positional": true, - "help": "Display name (or substring) to search." + "name": "filter", + "type": "str", + "required": false, + "help": "Case-insensitive substring filter over keys" + }, + { + "name": "workspace", + "type": "str", + "required": false, + "help": "Workspace id (from workspaces-list) to query a per-workspace DB" }, { "name": "limit", "type": "int", - "default": 10, + "default": 200, "required": false, - "help": "Max users to return (max 100)." + "help": "" } ], "columns": [ - "userId", - "displayName", - "reputation", - "goldBadges", - "silverBadges", - "bronzeBadges", - "location", - "createdAt", - "lastAccessAt", - "url" + "Index", + "Key", + "Kind", + "Path" ], "type": "js", - "modulePath": "plugins/stackoverflow/user.js", - "sourceFile": "plugins/stackoverflow/user.js" + "modulePath": "plugins/trae-solo/state-fs.js", + "sourceFile": "plugins/trae-solo/state-fs.js" }, { - "site": "steam", - "name": "app", - "description": "Steam storefront detail for a single app id", + "site": "trae-solo", + "name": "status", + "description": "Check active CDP connection to Trae SOLO Desktop", "access": "read", - "domain": "store.steampowered.com", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "plugins/trae-solo/status.js", + "sourceFile": "plugins/trae-solo/status.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "storage-get", + "description": "Read a single localStorage / sessionStorage value on the Trae SOLO renderer.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "id", + "name": "key", "type": "str", "required": true, "positional": true, - "help": "Numeric Steam app id (e.g. \"620\" for Portal 2)" + "help": "Storage key (use storage-keys to discover)" }, { - "name": "currency", + "name": "storage", "type": "str", - "default": "us", + "default": "local", "required": false, - "help": "Storefront country code (e.g. us / cn / jp / de)" + "help": "\"local\" or \"session\"" + }, + { + "name": "max-bytes", + "type": "int", + "default": 4000, + "required": false, + "help": "Truncate value to this many chars" } ], "columns": [ - "id", - "name", - "type", - "isFree", - "releaseDate", - "developers", - "publishers", - "price", - "currency", - "metacritic", - "recommendations", - "genres", - "categories", - "shortDescription", - "website", - "url" + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/steam/app.js", - "sourceFile": "plugins/steam/app.js" + "modulePath": "plugins/trae-solo/renderer-storage.js", + "sourceFile": "plugins/trae-solo/renderer-storage.js", + "navigateBefore": true }, { - "site": "steam", - "name": "search", - "description": "Search the Steam storefront by name keyword", + "site": "trae-solo", + "name": "storage-keys", + "description": "List localStorage / sessionStorage keys on the Trae SOLO renderer (CDP). For the on-disk VSCode state.vscdb, see state-keys.", "access": "read", - "domain": "store.steampowered.com", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", + "name": "storage", "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"portal\", \"stardew\")" + "default": "local", + "required": false, + "help": "\"local\" or \"session\"" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "filter", + "type": "str", "required": false, - "help": "Max results (1-50)" + "help": "Case-insensitive substring filter" }, { - "name": "currency", - "type": "str", - "default": "us", + "name": "limit", + "type": "int", + "default": 100, "required": false, - "help": "Storefront country code (e.g. us / cn / jp / de)" + "help": "Max rows to return" } ], "columns": [ - "rank", - "id", - "name", - "price", - "currency", - "metascore", - "platforms", - "url" - ], - "tags": [ - "search" + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version" ], "type": "js", - "modulePath": "plugins/steam/search.js", - "sourceFile": "plugins/steam/search.js" + "modulePath": "plugins/trae-solo/renderer-storage.js", + "sourceFile": "plugins/trae-solo/renderer-storage.js", + "navigateBefore": true }, { - "site": "steam", - "name": "top-sellers", - "description": "Steam top selling games", + "site": "trae-solo", + "name": "task-fs-list", + "description": "List Trae SOLO task ids from disk (snapshot/ + agentconfig/.json). Works while Trae is closed.", "access": "read", - "domain": "store.steampowered.com", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 100, "required": false, - "help": "Number of games" + "help": "" } ], "columns": [ - "rank", - "name", - "price", - "discount", - "url" + "Index", + "Task Id", + "Has Snapshot", + "Has Config", + "Modified", + "Phase", + "Turn Id", + "Commit" ], "type": "js", - "modulePath": "plugins/steam/top-sellers.js", - "sourceFile": "plugins/steam/top-sellers.js" + "modulePath": "plugins/trae-solo/task-fs.js", + "sourceFile": "plugins/trae-solo/task-fs.js" }, { - "site": "techcrunch", - "name": "article", - "description": "Read a TechCrunch article from its URL", + "site": "trae-solo", + "name": "task-fs-show", + "description": "Show the workspace tree at a given chat-turn ref (via git ls-tree). Pass --turn to pick a turn; otherwise the latest after-chat-turn ref.", "access": "read", - "domain": "techcrunch.com", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ { - "name": "url", - "type": "string", + "name": "task-id", + "type": "str", "required": true, "positional": true, - "help": "TechCrunch article URL" + "help": "Task UUID" + }, + { + "name": "turn", + "type": "str", + "required": false, + "help": "Specific turn id (omit for latest after-chat-turn)" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "" } ], "columns": [ - "title", - "author", - "publishedAt", - "categories", - "description", - "content", - "url" + "Mode", + "Path", + "Size" ], "type": "js", - "modulePath": "plugins/techcrunch/article.js", - "sourceFile": "plugins/techcrunch/article.js" + "modulePath": "plugins/trae-solo/task-fs.js", + "sourceFile": "plugins/trae-solo/task-fs.js" }, { - "site": "techcrunch", - "name": "search", - "description": "Search TechCrunch stories or list the latest stories", + "site": "trae-solo", + "name": "task-fs-turns", + "description": "Show the chat-turn timeline for a Trae SOLO task as git tags (before-chat-turn-* / after-chat-turn-*).", "access": "read", - "domain": "techcrunch.com", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ { - "name": "query", - "type": "string", - "required": false, + "name": "task-id", + "type": "str", + "required": true, "positional": true, - "help": "Words to search for" + "help": "Task UUID (folder name under snapshot/)" }, { - "name": "latest", - "type": "boolean", - "default": false, + "name": "limit", + "type": "int", + "default": 50, "required": false, - "help": "List the latest stories instead of searching" - }, + "help": "" + } + ], + "columns": [ + "Index", + "Task Id", + "Has Snapshot", + "Has Config", + "Modified", + "Phase", + "Turn Id", + "Commit" + ], + "type": "js", + "modulePath": "plugins/trae-solo/task-fs.js", + "sourceFile": "plugins/trae-solo/task-fs.js" + }, + { + "site": "trae-solo", + "name": "user-rules", + "description": "Print Trae SOLO user rules (~/.trae/user_rules.md).", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "plugins/trae-solo/user-rules.js", + "sourceFile": "plugins/trae-solo/user-rules.js" + }, + { + "site": "trae-solo", + "name": "workspaces-list", + "description": "List Trae SOLO workspaceStorage entries (~/Library/.../TRAE SOLO/User/workspaceStorage//), resolving each workspace.json to its single-folder path or multi-folder workspace target. Works while Trae is closed.", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 100, "required": false, - "help": "Maximum stories to return (1-50)" + "help": "" } ], "columns": [ - "rank", - "title", - "author", - "publishedAt", - "description", - "url" - ], - "tags": [ - "search" + "Index", + "Workspace Id", + "Kind", + "Target", + "Modified", + "Id", + "Version", + "Source", + "Installed" ], "type": "js", - "modulePath": "plugins/techcrunch/search.js", - "sourceFile": "plugins/techcrunch/search.js" + "modulePath": "plugins/trae-solo/workspaces-fs.js", + "sourceFile": "plugins/trae-solo/workspaces-fs.js" }, { "site": "tvmaze", @@ -8903,6 +11172,50 @@ "modulePath": "plugins/wttr/forecast.js", "sourceFile": "plugins/wttr/forecast.js" }, + { + "site": "yahoo", + "name": "search", + "description": "Search Yahoo (powered by Bing)", + "access": "read", + "domain": "search.yahoo.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 7, + "required": false, + "help": "Number of results per page (max 7)" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number (1, 2, 3...). Yahoo returns ~7 results per page" + } + ], + "columns": [ + "rank", + "title", + "url", + "snippet" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/yahoo/search.js", + "sourceFile": "plugins/yahoo/search.js" + }, { "site": "yale", "name": "export-postgraduate-courses", diff --git a/plugins/brave/README.md b/plugins/brave/README.md new file mode 100644 index 00000000..f2acf871 --- /dev/null +++ b/plugins/brave/README.md @@ -0,0 +1,15 @@ +# webcmd-plugin-brave + +Webcmd commands for brave. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/brave +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd brave search` | Search Brave Search | diff --git a/plugins/brave/package.json b/plugins/brave/package.json new file mode 100644 index 00000000..b3c31e59 --- /dev/null +++ b/plugins/brave/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-brave", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for brave", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/brave/search.js b/plugins/brave/search.js similarity index 98% rename from clis/brave/search.js rename to plugins/brave/search.js index 0c6b9ccd..50d54fe0 100644 --- a/clis/brave/search.js +++ b/plugins/brave/search.js @@ -7,7 +7,7 @@ import { requireSearchQuery, runBrowserStep, toHttpsUrl, -} from '../_shared/search-adapter.js'; +} from '@agentrhq/webcmd/plugin-runtime'; function buildExtractorJs(limit) { return ` diff --git a/clis/brave/search.test.js b/plugins/brave/test/search.test.js similarity index 98% rename from clis/brave/search.test.js rename to plugins/brave/test/search.test.js index 7b40836a..f34b7c00 100644 --- a/clis/brave/search.test.js +++ b/plugins/brave/test/search.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; -const { __test__ } = await import('./search.js'); +const { __test__ } = await import('../search.js'); const command = __test__.command; function createPageMock(evaluateResult = []) { diff --git a/plugins/brave/webcmd-plugin.json b/plugins/brave/webcmd-plugin.json new file mode 100644 index 00000000..cba10151 --- /dev/null +++ b/plugins/brave/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "brave", + "version": "0.1.0", + "description": "Webcmd commands for brave", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/chatwise/README.md b/plugins/chatwise/README.md new file mode 100644 index 00000000..cde46fc1 --- /dev/null +++ b/plugins/chatwise/README.md @@ -0,0 +1,23 @@ +# webcmd-plugin-chatwise + +Webcmd commands for chatwise. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/chatwise +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd chatwise ask` | Send a prompt and wait for the AI response (send + wait + read) | +| `webcmd chatwise export` | Export the current ChatWise conversation to a Markdown file | +| `webcmd chatwise history` | List conversation history in ChatWise sidebar | +| `webcmd chatwise model` | Get or switch the active AI model in ChatWise | +| `webcmd chatwise new` | Start a new ChatWise conversation session | +| `webcmd chatwise read` | Read the current ChatWise conversation history | +| `webcmd chatwise screenshot` | Capture a snapshot of the current ChatWise window (DOM + Accessibility tree) | +| `webcmd chatwise send` | Send a message to the active ChatWise conversation | +| `webcmd chatwise status` | Check active CDP connection to ChatWise Desktop | diff --git a/clis/chatwise/ask.js b/plugins/chatwise/ask.js similarity index 100% rename from clis/chatwise/ask.js rename to plugins/chatwise/ask.js diff --git a/clis/chatwise/export.js b/plugins/chatwise/export.js similarity index 100% rename from clis/chatwise/export.js rename to plugins/chatwise/export.js diff --git a/clis/chatwise/history.js b/plugins/chatwise/history.js similarity index 100% rename from clis/chatwise/history.js rename to plugins/chatwise/history.js diff --git a/clis/chatwise/model.js b/plugins/chatwise/model.js similarity index 100% rename from clis/chatwise/model.js rename to plugins/chatwise/model.js diff --git a/clis/chatwise/new.js b/plugins/chatwise/new.js similarity index 54% rename from clis/chatwise/new.js rename to plugins/chatwise/new.js index 51587ab3..6314abfd 100644 --- a/clis/chatwise/new.js +++ b/plugins/chatwise/new.js @@ -1,2 +1,2 @@ -import { makeNewCommand } from '../_shared/desktop-commands.js'; +import { makeNewCommand } from '@agentrhq/webcmd/plugin-runtime'; export const newCommand = makeNewCommand('chatwise', 'ChatWise conversation'); diff --git a/plugins/chatwise/package.json b/plugins/chatwise/package.json new file mode 100644 index 00000000..eb939472 --- /dev/null +++ b/plugins/chatwise/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-chatwise", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for chatwise", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/chatwise/read.js b/plugins/chatwise/read.js similarity index 100% rename from clis/chatwise/read.js rename to plugins/chatwise/read.js diff --git a/clis/chatwise/screenshot.js b/plugins/chatwise/screenshot.js similarity index 52% rename from clis/chatwise/screenshot.js rename to plugins/chatwise/screenshot.js index cc3185a9..6ed17129 100644 --- a/clis/chatwise/screenshot.js +++ b/plugins/chatwise/screenshot.js @@ -1,2 +1,2 @@ -import { makeScreenshotCommand } from '../_shared/desktop-commands.js'; +import { makeScreenshotCommand } from '@agentrhq/webcmd/plugin-runtime'; export const screenshotCommand = makeScreenshotCommand('chatwise', 'ChatWise'); diff --git a/clis/chatwise/send.js b/plugins/chatwise/send.js similarity index 100% rename from clis/chatwise/send.js rename to plugins/chatwise/send.js diff --git a/clis/chatwise/status.js b/plugins/chatwise/status.js similarity index 53% rename from clis/chatwise/status.js rename to plugins/chatwise/status.js index 68579484..a96b3659 100644 --- a/clis/chatwise/status.js +++ b/plugins/chatwise/status.js @@ -1,2 +1,2 @@ -import { makeStatusCommand } from '../_shared/desktop-commands.js'; +import { makeStatusCommand } from '@agentrhq/webcmd/plugin-runtime'; export const statusCommand = makeStatusCommand('chatwise', 'ChatWise Desktop'); diff --git a/clis/chatwise/composer.test.js b/plugins/chatwise/test/composer.test.js similarity index 99% rename from clis/chatwise/composer.test.js rename to plugins/chatwise/test/composer.test.js index 6fa62f79..f02fff2e 100644 --- a/clis/chatwise/composer.test.js +++ b/plugins/chatwise/test/composer.test.js @@ -8,8 +8,8 @@ import { requirePositiveTimeout, scoreChatwiseComposerCandidate, selectBestChatwiseComposer, -} from './utils.js'; -import { askCommand } from './ask.js'; +} from '../utils.js'; +import { askCommand } from '../ask.js'; function candidate(overrides = {}) { return { diff --git a/clis/chatwise/utils.js b/plugins/chatwise/utils.js similarity index 100% rename from clis/chatwise/utils.js rename to plugins/chatwise/utils.js diff --git a/plugins/chatwise/webcmd-plugin.json b/plugins/chatwise/webcmd-plugin.json new file mode 100644 index 00000000..00e0e74b --- /dev/null +++ b/plugins/chatwise/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "chatwise", + "version": "0.1.0", + "description": "Webcmd commands for chatwise", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/codex/README.md b/plugins/codex/README.md new file mode 100644 index 00000000..eccf5242 --- /dev/null +++ b/plugins/codex/README.md @@ -0,0 +1,30 @@ +# webcmd-plugin-codex + +Webcmd commands for codex. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/codex +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd codex archive` | Archive (Codex's term for delete) the selected conversation via the Chat actions header menu. No confirmation in UI — pass --yes to actually archive. | +| `webcmd codex ask` | Send a prompt to the current or selected Codex conversation and wait for the AI response | +| `webcmd codex dump` | Dump the DOM and Accessibility tree of codex for reverse-engineering | +| `webcmd codex export` | Export the current Codex conversation to a Markdown file | +| `webcmd codex extract-diff` | Extract visual code review diff patches from Codex | +| `webcmd codex history` | List visible Codex conversation threads grouped by project | +| `webcmd codex model` | Read, list, or switch the active model / reasoning level in Codex Desktop. The composer toolbar button toggles a menu that mixes model variants (GPT-5.5, Speed) with reasoning levels (Low/Medium/High/Extra High). | +| `webcmd codex new` | Start a new Codex conversation session | +| `webcmd codex pin` | Pin the selected Codex conversation via the Chat actions header menu. | +| `webcmd codex projects` | List Codex projects and visible conversations from the sidebar | +| `webcmd codex read` | Read the contents of the current or selected Codex conversation thread | +| `webcmd codex rename` | Rename the selected Codex conversation. Opens the Chat actions menu → "Rename chat", then types the new title. | +| `webcmd codex screenshot` | Capture a snapshot of the current Codex window (DOM + Accessibility tree) | +| `webcmd codex send` | Send text/commands to the current or selected Codex AI composer | +| `webcmd codex status` | Check active CDP connection to OpenAI Codex App | +| `webcmd codex unpin` | Unpin the selected Codex conversation via the Chat actions header menu. | diff --git a/clis/codex/_actions.js b/plugins/codex/_actions.js similarity index 100% rename from clis/codex/_actions.js rename to plugins/codex/_actions.js diff --git a/clis/codex/archive.js b/plugins/codex/archive.js similarity index 100% rename from clis/codex/archive.js rename to plugins/codex/archive.js diff --git a/clis/codex/ask.js b/plugins/codex/ask.js similarity index 100% rename from clis/codex/ask.js rename to plugins/codex/ask.js diff --git a/plugins/codex/dump.js b/plugins/codex/dump.js new file mode 100644 index 00000000..bfdf4e3c --- /dev/null +++ b/plugins/codex/dump.js @@ -0,0 +1,2 @@ +import { makeDumpCommand } from '@agentrhq/webcmd/plugin-runtime'; +export const dumpCommand = makeDumpCommand('codex'); diff --git a/clis/codex/export.js b/plugins/codex/export.js similarity index 100% rename from clis/codex/export.js rename to plugins/codex/export.js diff --git a/clis/codex/extract-diff.js b/plugins/codex/extract-diff.js similarity index 100% rename from clis/codex/extract-diff.js rename to plugins/codex/extract-diff.js diff --git a/clis/codex/history.js b/plugins/codex/history.js similarity index 100% rename from clis/codex/history.js rename to plugins/codex/history.js diff --git a/clis/codex/model.js b/plugins/codex/model.js similarity index 100% rename from clis/codex/model.js rename to plugins/codex/model.js diff --git a/clis/codex/new.js b/plugins/codex/new.js similarity index 52% rename from clis/codex/new.js rename to plugins/codex/new.js index e65992e4..197cfce4 100644 --- a/clis/codex/new.js +++ b/plugins/codex/new.js @@ -1,2 +1,2 @@ -import { makeNewCommand } from '../_shared/desktop-commands.js'; +import { makeNewCommand } from '@agentrhq/webcmd/plugin-runtime'; export const newCommand = makeNewCommand('codex', 'Codex conversation'); diff --git a/plugins/codex/package.json b/plugins/codex/package.json new file mode 100644 index 00000000..3162aaf7 --- /dev/null +++ b/plugins/codex/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-codex", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for codex", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/codex/pin.js b/plugins/codex/pin.js similarity index 100% rename from clis/codex/pin.js rename to plugins/codex/pin.js diff --git a/clis/codex/projects.js b/plugins/codex/projects.js similarity index 100% rename from clis/codex/projects.js rename to plugins/codex/projects.js diff --git a/clis/codex/read.js b/plugins/codex/read.js similarity index 100% rename from clis/codex/read.js rename to plugins/codex/read.js diff --git a/clis/codex/rename.js b/plugins/codex/rename.js similarity index 100% rename from clis/codex/rename.js rename to plugins/codex/rename.js diff --git a/clis/codex/screenshot.js b/plugins/codex/screenshot.js similarity index 50% rename from clis/codex/screenshot.js rename to plugins/codex/screenshot.js index fcde1172..ce274552 100644 --- a/clis/codex/screenshot.js +++ b/plugins/codex/screenshot.js @@ -1,2 +1,2 @@ -import { makeScreenshotCommand } from '../_shared/desktop-commands.js'; +import { makeScreenshotCommand } from '@agentrhq/webcmd/plugin-runtime'; export const screenshotCommand = makeScreenshotCommand('codex', 'Codex'); diff --git a/clis/codex/send.js b/plugins/codex/send.js similarity index 100% rename from clis/codex/send.js rename to plugins/codex/send.js diff --git a/clis/codex/sidebar.js b/plugins/codex/sidebar.js similarity index 100% rename from clis/codex/sidebar.js rename to plugins/codex/sidebar.js diff --git a/clis/codex/status.js b/plugins/codex/status.js similarity index 52% rename from clis/codex/status.js rename to plugins/codex/status.js index 85762172..a39608de 100644 --- a/clis/codex/status.js +++ b/plugins/codex/status.js @@ -1,2 +1,2 @@ -import { makeStatusCommand } from '../_shared/desktop-commands.js'; +import { makeStatusCommand } from '@agentrhq/webcmd/plugin-runtime'; export const statusCommand = makeStatusCommand('codex', 'OpenAI Codex App'); diff --git a/clis/codex/sidebar.test.js b/plugins/codex/test/sidebar.test.js similarity index 98% rename from clis/codex/sidebar.test.js rename to plugins/codex/test/sidebar.test.js index 023240dc..9b243340 100644 --- a/clis/codex/sidebar.test.js +++ b/plugins/codex/test/sidebar.test.js @@ -1,23 +1,23 @@ import { describe, expect, it } from 'vitest'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { askCommand } from './ask.js'; -import { historyCommand } from './history.js'; -import { projectsCommand } from './projects.js'; +import { askCommand } from '../ask.js'; +import { historyCommand } from '../history.js'; +import { projectsCommand } from '../projects.js'; import { collectCodexProjectsFromDocument, flattenCodexProjects, openCodexConversation, selectCodexConversationInDocument, -} from './sidebar.js'; +} from '../sidebar.js'; import { findActiveCodexConversation, findCodexConversation, resolveActionConversation, -} from './_actions.js'; +} from '../_actions.js'; import { findUniqueModelOption, modelSelectionVerified, -} from './model.js'; +} from '../model.js'; class FakeElement { constructor(tagName = 'div', attrs = {}, children = [], text = '') { diff --git a/plugins/codex/webcmd-plugin.json b/plugins/codex/webcmd-plugin.json new file mode 100644 index 00000000..ed317e7f --- /dev/null +++ b/plugins/codex/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "codex", + "version": "0.1.0", + "description": "Webcmd commands for codex", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/cursor/README.md b/plugins/cursor/README.md new file mode 100644 index 00000000..0e879772 --- /dev/null +++ b/plugins/cursor/README.md @@ -0,0 +1,26 @@ +# webcmd-plugin-cursor + +Webcmd commands for cursor. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/cursor +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd cursor ask` | Send a prompt and wait for the AI response (send + wait + read) | +| `webcmd cursor composer` | Send a prompt directly into Cursor Composer (Cmd+I shortcut) | +| `webcmd cursor dump` | Dump the DOM and Accessibility tree of cursor for reverse-engineering | +| `webcmd cursor export` | Export the current cursor conversation to a Markdown file | +| `webcmd cursor extract-code` | Extract multi-line code blocks from the current Cursor conversation | +| `webcmd cursor history` | List recent chat sessions from the Cursor sidebar | +| `webcmd cursor model` | Get or switch the currently active AI model in Cursor | +| `webcmd cursor new` | Start a new Cursor chat or Composer session | +| `webcmd cursor read` | Read the current Cursor chat/composer conversation history | +| `webcmd cursor screenshot` | Capture a snapshot of the current cursor window (DOM + Accessibility tree) | +| `webcmd cursor send` | Send a prompt directly into Cursor Composer/Chat | +| `webcmd cursor status` | Check active CDP connection to Cursor AI Editor | diff --git a/clis/cursor/ask.js b/plugins/cursor/ask.js similarity index 100% rename from clis/cursor/ask.js rename to plugins/cursor/ask.js diff --git a/clis/cursor/composer.js b/plugins/cursor/composer.js similarity index 100% rename from clis/cursor/composer.js rename to plugins/cursor/composer.js diff --git a/plugins/cursor/dump.js b/plugins/cursor/dump.js new file mode 100644 index 00000000..c888375a --- /dev/null +++ b/plugins/cursor/dump.js @@ -0,0 +1,2 @@ +import { makeDumpCommand } from '@agentrhq/webcmd/plugin-runtime'; +export const dumpCommand = makeDumpCommand('cursor'); diff --git a/clis/cursor/export.js b/plugins/cursor/export.js similarity index 100% rename from clis/cursor/export.js rename to plugins/cursor/export.js diff --git a/clis/cursor/extract-code.js b/plugins/cursor/extract-code.js similarity index 100% rename from clis/cursor/extract-code.js rename to plugins/cursor/extract-code.js diff --git a/clis/cursor/history.js b/plugins/cursor/history.js similarity index 100% rename from clis/cursor/history.js rename to plugins/cursor/history.js diff --git a/clis/cursor/model.js b/plugins/cursor/model.js similarity index 100% rename from clis/cursor/model.js rename to plugins/cursor/model.js diff --git a/clis/cursor/new.js b/plugins/cursor/new.js similarity index 54% rename from clis/cursor/new.js rename to plugins/cursor/new.js index 19696352..3e2c062d 100644 --- a/clis/cursor/new.js +++ b/plugins/cursor/new.js @@ -1,2 +1,2 @@ -import { makeNewCommand } from '../_shared/desktop-commands.js'; +import { makeNewCommand } from '@agentrhq/webcmd/plugin-runtime'; export const newCommand = makeNewCommand('cursor', 'Cursor chat or Composer'); diff --git a/plugins/cursor/package.json b/plugins/cursor/package.json new file mode 100644 index 00000000..661b1920 --- /dev/null +++ b/plugins/cursor/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-cursor", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for cursor", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/cursor/read.js b/plugins/cursor/read.js similarity index 100% rename from clis/cursor/read.js rename to plugins/cursor/read.js diff --git a/plugins/cursor/screenshot.js b/plugins/cursor/screenshot.js new file mode 100644 index 00000000..ef8351ae --- /dev/null +++ b/plugins/cursor/screenshot.js @@ -0,0 +1,2 @@ +import { makeScreenshotCommand } from '@agentrhq/webcmd/plugin-runtime'; +export const screenshotCursor = makeScreenshotCommand('cursor'); diff --git a/clis/cursor/send.js b/plugins/cursor/send.js similarity index 100% rename from clis/cursor/send.js rename to plugins/cursor/send.js diff --git a/clis/cursor/status.js b/plugins/cursor/status.js similarity index 53% rename from clis/cursor/status.js rename to plugins/cursor/status.js index 0195a6a8..0a42300d 100644 --- a/clis/cursor/status.js +++ b/plugins/cursor/status.js @@ -1,2 +1,2 @@ -import { makeStatusCommand } from '../_shared/desktop-commands.js'; +import { makeStatusCommand } from '@agentrhq/webcmd/plugin-runtime'; export const statusCommand = makeStatusCommand('cursor', 'Cursor AI Editor'); diff --git a/plugins/cursor/webcmd-plugin.json b/plugins/cursor/webcmd-plugin.json new file mode 100644 index 00000000..2663daae --- /dev/null +++ b/plugins/cursor/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "cursor", + "version": "0.1.0", + "description": "Webcmd commands for cursor", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/duckduckgo/README.md b/plugins/duckduckgo/README.md new file mode 100644 index 00000000..e9883fd0 --- /dev/null +++ b/plugins/duckduckgo/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-duckduckgo + +Webcmd commands for duckduckgo. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/duckduckgo +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd duckduckgo search` | Search DuckDuckGo | +| `webcmd duckduckgo suggest` | DuckDuckGo search suggestions | diff --git a/plugins/duckduckgo/package.json b/plugins/duckduckgo/package.json new file mode 100644 index 00000000..20331154 --- /dev/null +++ b/plugins/duckduckgo/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-duckduckgo", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for duckduckgo", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/duckduckgo/search.js b/plugins/duckduckgo/search.js similarity index 99% rename from clis/duckduckgo/search.js rename to plugins/duckduckgo/search.js index a575fb0c..e6d92f2a 100644 --- a/clis/duckduckgo/search.js +++ b/plugins/duckduckgo/search.js @@ -8,7 +8,7 @@ import { requireSearchQuery, runBrowserStep, toHttpsUrl, -} from '../_shared/search-adapter.js'; +} from '@agentrhq/webcmd/plugin-runtime'; function decodeDdgUrl(href) { if (!href) return ''; diff --git a/clis/duckduckgo/suggest.js b/plugins/duckduckgo/suggest.js similarity index 94% rename from clis/duckduckgo/suggest.js rename to plugins/duckduckgo/suggest.js index ed77e0a8..3a0a4bc6 100644 --- a/clis/duckduckgo/suggest.js +++ b/plugins/duckduckgo/suggest.js @@ -1,6 +1,6 @@ import { cli, Strategy } from '@agentrhq/webcmd/registry'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { requireBoundedInteger, requireSearchQuery } from '../_shared/search-adapter.js'; +import { requireBoundedInteger, requireSearchQuery } from '@agentrhq/webcmd/plugin-runtime'; const command = cli({ site: 'duckduckgo', diff --git a/clis/duckduckgo/search.test.js b/plugins/duckduckgo/test/search.test.js similarity index 98% rename from clis/duckduckgo/search.test.js rename to plugins/duckduckgo/test/search.test.js index 68544391..4065c760 100644 --- a/clis/duckduckgo/search.test.js +++ b/plugins/duckduckgo/test/search.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { JSDOM } from 'jsdom'; -const { __test__ } = await import('./search.js'); +const { __test__ } = await import('../search.js'); const command = __test__.command; function createPageMock(evaluateResult = []) { diff --git a/clis/duckduckgo/suggest.test.js b/plugins/duckduckgo/test/suggest.test.js similarity index 97% rename from clis/duckduckgo/suggest.test.js rename to plugins/duckduckgo/test/suggest.test.js index 0fd32732..4dd550d0 100644 --- a/clis/duckduckgo/suggest.test.js +++ b/plugins/duckduckgo/test/suggest.test.js @@ -1,6 +1,6 @@ import { afterEach, describe, it, expect, vi } from 'vitest'; -const { __test__ } = await import('./suggest.js'); +const { __test__ } = await import('../suggest.js'); const command = __test__.command; afterEach(() => { diff --git a/plugins/duckduckgo/webcmd-plugin.json b/plugins/duckduckgo/webcmd-plugin.json new file mode 100644 index 00000000..a6e12bd4 --- /dev/null +++ b/plugins/duckduckgo/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "duckduckgo", + "version": "0.1.0", + "description": "Webcmd commands for duckduckgo", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/google-scholar/README.md b/plugins/google-scholar/README.md new file mode 100644 index 00000000..d8f224f2 --- /dev/null +++ b/plugins/google-scholar/README.md @@ -0,0 +1,17 @@ +# webcmd-plugin-google-scholar + +Webcmd commands for google-scholar. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/google-scholar +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd google-scholar cite` | Get citation for a Google Scholar paper | +| `webcmd google-scholar profile` | View a Google Scholar author profile | +| `webcmd google-scholar search` | Google Scholar scholar search | diff --git a/clis/google-scholar/cite.js b/plugins/google-scholar/cite.js similarity index 97% rename from clis/google-scholar/cite.js rename to plugins/google-scholar/cite.js index 3faf9e0f..97a06390 100644 --- a/clis/google-scholar/cite.js +++ b/plugins/google-scholar/cite.js @@ -1,6 +1,6 @@ import { cli, Strategy } from '@agentrhq/webcmd/registry'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { requireNonEmptyQuery } from '../_shared/common.js'; +import { requireNonEmptyQuery } from '@agentrhq/webcmd/plugin-runtime'; cli({ site: 'google-scholar', diff --git a/plugins/google-scholar/package.json b/plugins/google-scholar/package.json new file mode 100644 index 00000000..e6c70389 --- /dev/null +++ b/plugins/google-scholar/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-google-scholar", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for google-scholar", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/google-scholar/profile.js b/plugins/google-scholar/profile.js similarity index 97% rename from clis/google-scholar/profile.js rename to plugins/google-scholar/profile.js index de6042a7..75e5a1fe 100644 --- a/clis/google-scholar/profile.js +++ b/plugins/google-scholar/profile.js @@ -1,6 +1,6 @@ import { cli, Strategy } from '@agentrhq/webcmd/registry'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { clampInt, requireNonEmptyQuery } from '../_shared/common.js'; +import { clampInt, requireNonEmptyQuery } from '@agentrhq/webcmd/plugin-runtime'; cli({ site: 'google-scholar', diff --git a/clis/google-scholar/search.js b/plugins/google-scholar/search.js similarity index 97% rename from clis/google-scholar/search.js rename to plugins/google-scholar/search.js index 24488e68..2f1f5c22 100644 --- a/clis/google-scholar/search.js +++ b/plugins/google-scholar/search.js @@ -1,6 +1,6 @@ import { cli, Strategy } from '@agentrhq/webcmd/registry'; import { CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { clampInt, requireNonEmptyQuery } from '../_shared/common.js'; +import { clampInt, requireNonEmptyQuery } from '@agentrhq/webcmd/plugin-runtime'; cli({ site: 'google-scholar', diff --git a/clis/google-scholar/cite.test.js b/plugins/google-scholar/test/cite.test.js similarity index 99% rename from clis/google-scholar/cite.test.js rename to plugins/google-scholar/test/cite.test.js index 85a6c832..f62a1668 100644 --- a/clis/google-scholar/cite.test.js +++ b/plugins/google-scholar/test/cite.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './cite.js'; +import '../cite.js'; describe('google-scholar cite command', () => { const command = getRegistry().get('google-scholar/cite'); diff --git a/clis/google-scholar/profile.test.js b/plugins/google-scholar/test/profile.test.js similarity index 98% rename from clis/google-scholar/profile.test.js rename to plugins/google-scholar/test/profile.test.js index 6dd64458..ceb64eca 100644 --- a/clis/google-scholar/profile.test.js +++ b/plugins/google-scholar/test/profile.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './profile.js'; +import '../profile.js'; describe('google-scholar profile command', () => { const command = getRegistry().get('google-scholar/profile'); diff --git a/clis/google-scholar/search.test.js b/plugins/google-scholar/test/search.test.js similarity index 99% rename from clis/google-scholar/search.test.js rename to plugins/google-scholar/test/search.test.js index 3abf797f..db602ddd 100644 --- a/clis/google-scholar/search.test.js +++ b/plugins/google-scholar/test/search.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './search.js'; +import '../search.js'; describe('google-scholar search command', () => { const command = getRegistry().get('google-scholar/search'); diff --git a/plugins/google-scholar/webcmd-plugin.json b/plugins/google-scholar/webcmd-plugin.json new file mode 100644 index 00000000..c6b075aa --- /dev/null +++ b/plugins/google-scholar/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "google-scholar", + "version": "0.1.0", + "description": "Webcmd commands for google-scholar", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/google/README.md b/plugins/google/README.md new file mode 100644 index 00000000..0ceb3972 --- /dev/null +++ b/plugins/google/README.md @@ -0,0 +1,19 @@ +# webcmd-plugin-google + +Webcmd commands for google. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/google +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd google images` | Search Google Images for photos and image results | +| `webcmd google news` | Get Google News headlines | +| `webcmd google search` | Search Google | +| `webcmd google suggest` | Get Google search suggestions | +| `webcmd google trends` | Get Google Trends daily trending searches | diff --git a/clis/google/images.js b/plugins/google/images.js similarity index 99% rename from clis/google/images.js rename to plugins/google/images.js index 6bda842b..0217c558 100644 --- a/clis/google/images.js +++ b/plugins/google/images.js @@ -14,7 +14,7 @@ import { runBrowserStep, toHttpsUrl, unwrapBrowserResult, -} from '../_shared/search-adapter.js'; +} from '@agentrhq/webcmd/plugin-runtime'; function isNavigationRejected(error) { return /Navigation rejected/i.test(String(error?.message || error)); diff --git a/clis/google/news.js b/plugins/google/news.js similarity index 100% rename from clis/google/news.js rename to plugins/google/news.js diff --git a/plugins/google/package.json b/plugins/google/package.json new file mode 100644 index 00000000..3a0ee2e0 --- /dev/null +++ b/plugins/google/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-google", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for google", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/google/search.js b/plugins/google/search.js similarity index 100% rename from clis/google/search.js rename to plugins/google/search.js diff --git a/clis/google/suggest.js b/plugins/google/suggest.js similarity index 100% rename from clis/google/suggest.js rename to plugins/google/suggest.js diff --git a/clis/google/images.test.js b/plugins/google/test/images.test.js similarity index 99% rename from clis/google/images.test.js rename to plugins/google/test/images.test.js index 99dcb32a..8ff0980b 100644 --- a/clis/google/images.test.js +++ b/plugins/google/test/images.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { JSDOM } from 'jsdom'; -const { __test__ } = await import('./images.js'); +const { __test__ } = await import('../images.js'); const { command, extractGoogleImageRows, inspectGoogleImagesPage, normalizeImageRows } = __test__; function createPageMock(evaluateResult = []) { diff --git a/clis/google/utils.test.js b/plugins/google/test/utils.test.js similarity index 98% rename from clis/google/utils.test.js rename to plugins/google/test/utils.test.js index f1ab4310..42d6ef5c 100644 --- a/clis/google/utils.test.js +++ b/plugins/google/test/utils.test.js @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { parseRssItems } from './utils.js'; +import { parseRssItems } from '../utils.js'; describe('parseRssItems', () => { it('extracts plain text fields', () => { const xml = ` diff --git a/clis/google/trends.js b/plugins/google/trends.js similarity index 100% rename from clis/google/trends.js rename to plugins/google/trends.js diff --git a/clis/google/utils.js b/plugins/google/utils.js similarity index 100% rename from clis/google/utils.js rename to plugins/google/utils.js diff --git a/plugins/google/webcmd-plugin.json b/plugins/google/webcmd-plugin.json new file mode 100644 index 00000000..b6c9df50 --- /dev/null +++ b/plugins/google/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "google", + "version": "0.1.0", + "description": "Webcmd commands for google", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/trae-solo/README.md b/plugins/trae-solo/README.md new file mode 100644 index 00000000..70972d5e --- /dev/null +++ b/plugins/trae-solo/README.md @@ -0,0 +1,39 @@ +# webcmd-plugin-trae-solo + +Webcmd commands for trae-solo. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/trae-solo +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd trae-solo automation-list` | List Trae SOLO Automation tab content. Default tab is "Configured"; pass --tab to switch. | +| `webcmd trae-solo cookies` | List cookies on the Trae SOLO renderer (JS-visible via document.cookie; httpOnly cookies not shown). | +| `webcmd trae-solo extensions-list` | List VSCode extensions installed in Trae SOLO (~/.trae/extensions/extensions.json). Works while Trae is closed. | +| `webcmd trae-solo history` | List Trae SOLO projects and the tasks within each (from the project-list view sidebar). | +| `webcmd trae-solo idb-list` | List IndexedDB databases on the Trae SOLO renderer. Trae ships an @byted/ve-rtc DB used by the Volcengine RTC voice/video infrastructure. | +| `webcmd trae-solo mode` | Read or switch TRAE SOLO between Code mode and Work mode. | +| `webcmd trae-solo model` | Read or switch the current AI model in TRAE SOLO. Without arguments, reports the current model. With argument (substring, case-insensitive), switches to a matching model. Pass --list to enumerate available models. | +| `webcmd trae-solo recent-workspaces` | Show Trae SOLO's recently-opened workspaces (the File → Open Recent menu, stored under key "history.recentlyOpenedPathsList" in state.vscdb). | +| `webcmd trae-solo settings-read` | Parse and pretty-print Trae SOLO user settings.json (~/Library/Application Support/TRAE SOLO/User/settings.json). Handles VSCode JSONC syntax (line comments + trailing commas). | +| `webcmd trae-solo skill-category` | Filter Skills Marketplace by category. Pass --list to see categories. | +| `webcmd trae-solo skill-fs-installed` | List INSTALLED Trae SOLO skills (managedSkills entry in ~/.trae/skill-config.json). | +| `webcmd trae-solo skill-fs-list` | List all Trae SOLO skills present on disk under ~/.trae/skills/. Reads SKILL.md front-matter for descriptions. Works while Trae is closed. | +| `webcmd trae-solo skill-fs-show` | Print a skill's SKILL.md content + on-disk path. | +| `webcmd trae-solo skill-list` | List Trae SOLO Skills — by default the Marketplace; pass --installed to list installed ones. | +| `webcmd trae-solo skill-search` | Filter Skills Marketplace by keyword. | +| `webcmd trae-solo state-get` | Read a single key from Trae SOLO's globalStorage state.vscdb. Pass --workspace to query a per-workspace DB instead. Returns parsed JSON if the value is JSON. | +| `webcmd trae-solo state-keys` | List all keys present in Trae SOLO's globalStorage state.vscdb (VSCode-style UI/agent state). Pass --workspace to query a per-workspace DB instead. Use state-get to read a specific value. (See renderer storage-keys for browser-side LS/SS.) | +| `webcmd trae-solo status` | Check active CDP connection to Trae SOLO Desktop | +| `webcmd trae-solo storage-get` | Read a single localStorage / sessionStorage value on the Trae SOLO renderer. | +| `webcmd trae-solo storage-keys` | List localStorage / sessionStorage keys on the Trae SOLO renderer (CDP). For the on-disk VSCode state.vscdb, see state-keys. | +| `webcmd trae-solo task-fs-list` | List Trae SOLO task ids from disk (snapshot/ + agentconfig/.json). Works while Trae is closed. | +| `webcmd trae-solo task-fs-show` | Show the workspace tree at a given chat-turn ref (via git ls-tree). Pass --turn to pick a turn; otherwise the latest after-chat-turn ref. | +| `webcmd trae-solo task-fs-turns` | Show the chat-turn timeline for a Trae SOLO task as git tags (before-chat-turn-* / after-chat-turn-*). | +| `webcmd trae-solo user-rules` | Print Trae SOLO user rules (~/.trae/user_rules.md). | +| `webcmd trae-solo workspaces-list` | List Trae SOLO workspaceStorage entries (~/Library/.../TRAE SOLO/User/workspaceStorage//), resolving each workspace.json to its single-folder path or multi-folder workspace target. Works while Trae is closed. | diff --git a/clis/trae-solo/_actions.js b/plugins/trae-solo/_actions.js similarity index 100% rename from clis/trae-solo/_actions.js rename to plugins/trae-solo/_actions.js diff --git a/clis/trae-solo/_fs.js b/plugins/trae-solo/_fs.js similarity index 100% rename from clis/trae-solo/_fs.js rename to plugins/trae-solo/_fs.js diff --git a/clis/trae-solo/_state.js b/plugins/trae-solo/_state.js similarity index 100% rename from clis/trae-solo/_state.js rename to plugins/trae-solo/_state.js diff --git a/clis/trae-solo/automation.js b/plugins/trae-solo/automation.js similarity index 100% rename from clis/trae-solo/automation.js rename to plugins/trae-solo/automation.js diff --git a/clis/trae-solo/history.js b/plugins/trae-solo/history.js similarity index 100% rename from clis/trae-solo/history.js rename to plugins/trae-solo/history.js diff --git a/clis/trae-solo/mode.js b/plugins/trae-solo/mode.js similarity index 100% rename from clis/trae-solo/mode.js rename to plugins/trae-solo/mode.js diff --git a/clis/trae-solo/model.js b/plugins/trae-solo/model.js similarity index 100% rename from clis/trae-solo/model.js rename to plugins/trae-solo/model.js diff --git a/plugins/trae-solo/package.json b/plugins/trae-solo/package.json new file mode 100644 index 00000000..5ebba65f --- /dev/null +++ b/plugins/trae-solo/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-trae-solo", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for trae-solo", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/trae-solo/renderer-storage.js b/plugins/trae-solo/renderer-storage.js similarity index 100% rename from clis/trae-solo/renderer-storage.js rename to plugins/trae-solo/renderer-storage.js diff --git a/clis/trae-solo/settings.js b/plugins/trae-solo/settings.js similarity index 100% rename from clis/trae-solo/settings.js rename to plugins/trae-solo/settings.js diff --git a/clis/trae-solo/skill-fs.js b/plugins/trae-solo/skill-fs.js similarity index 100% rename from clis/trae-solo/skill-fs.js rename to plugins/trae-solo/skill-fs.js diff --git a/clis/trae-solo/skill.js b/plugins/trae-solo/skill.js similarity index 100% rename from clis/trae-solo/skill.js rename to plugins/trae-solo/skill.js diff --git a/clis/trae-solo/state-fs.js b/plugins/trae-solo/state-fs.js similarity index 100% rename from clis/trae-solo/state-fs.js rename to plugins/trae-solo/state-fs.js diff --git a/clis/trae-solo/status.js b/plugins/trae-solo/status.js similarity index 54% rename from clis/trae-solo/status.js rename to plugins/trae-solo/status.js index de8df716..de8f173a 100644 --- a/clis/trae-solo/status.js +++ b/plugins/trae-solo/status.js @@ -1,2 +1,2 @@ -import { makeStatusCommand } from '../_shared/desktop-commands.js'; +import { makeStatusCommand } from '@agentrhq/webcmd/plugin-runtime'; export const statusCommand = makeStatusCommand('trae-solo', 'Trae SOLO Desktop'); diff --git a/clis/trae-solo/task-fs.js b/plugins/trae-solo/task-fs.js similarity index 100% rename from clis/trae-solo/task-fs.js rename to plugins/trae-solo/task-fs.js diff --git a/clis/trae-solo/trae-solo.test.js b/plugins/trae-solo/test/trae-solo.test.js similarity index 96% rename from clis/trae-solo/trae-solo.test.js rename to plugins/trae-solo/test/trae-solo.test.js index f147d80a..7912b586 100644 --- a/clis/trae-solo/trae-solo.test.js +++ b/plugins/trae-solo/test/trae-solo.test.js @@ -2,8 +2,8 @@ import { mkdtempSync, mkdirSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import path from 'node:path'; import { describe, expect, it } from 'vitest'; -import { parseSkillMd } from './_fs.js'; -import { resolveWorkspaceJson } from './_state.js'; +import { parseSkillMd } from '../_fs.js'; +import { resolveWorkspaceJson } from '../_state.js'; describe('trae-solo filesystem helpers', () => { it('parses skill front matter without requiring the Trae app', () => { diff --git a/clis/trae-solo/user-rules.js b/plugins/trae-solo/user-rules.js similarity index 100% rename from clis/trae-solo/user-rules.js rename to plugins/trae-solo/user-rules.js diff --git a/plugins/trae-solo/webcmd-plugin.json b/plugins/trae-solo/webcmd-plugin.json new file mode 100644 index 00000000..ba46b5e2 --- /dev/null +++ b/plugins/trae-solo/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "trae-solo", + "version": "0.1.0", + "description": "Webcmd commands for trae-solo", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/clis/trae-solo/workspaces-fs.js b/plugins/trae-solo/workspaces-fs.js similarity index 100% rename from clis/trae-solo/workspaces-fs.js rename to plugins/trae-solo/workspaces-fs.js diff --git a/plugins/yahoo/README.md b/plugins/yahoo/README.md new file mode 100644 index 00000000..ff828970 --- /dev/null +++ b/plugins/yahoo/README.md @@ -0,0 +1,15 @@ +# webcmd-plugin-yahoo + +Webcmd commands for yahoo. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/yahoo +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd yahoo search` | Search Yahoo (powered by Bing) | diff --git a/plugins/yahoo/package.json b/plugins/yahoo/package.json new file mode 100644 index 00000000..a212464a --- /dev/null +++ b/plugins/yahoo/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-yahoo", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for yahoo", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/yahoo/search.js b/plugins/yahoo/search.js similarity index 98% rename from clis/yahoo/search.js rename to plugins/yahoo/search.js index 152c9a30..0f7da511 100644 --- a/clis/yahoo/search.js +++ b/plugins/yahoo/search.js @@ -6,7 +6,7 @@ import { requireSearchQuery, runBrowserStep, toHttpsUrl, -} from '../_shared/search-adapter.js'; +} from '@agentrhq/webcmd/plugin-runtime'; function decodeYahooUrl(href) { if (!href) return ''; diff --git a/clis/yahoo/search.test.js b/plugins/yahoo/test/search.test.js similarity index 98% rename from clis/yahoo/search.test.js rename to plugins/yahoo/test/search.test.js index 8da0a9cc..b5d1aa36 100644 --- a/clis/yahoo/search.test.js +++ b/plugins/yahoo/test/search.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; -const { __test__ } = await import('./search.js'); +const { __test__ } = await import('../search.js'); const command = __test__.command; function createPageMock(evaluateResult = []) { diff --git a/plugins/yahoo/webcmd-plugin.json b/plugins/yahoo/webcmd-plugin.json new file mode 100644 index 00000000..25cf2323 --- /dev/null +++ b/plugins/yahoo/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "yahoo", + "version": "0.1.0", + "description": "Webcmd commands for yahoo", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/scripts/typed-error-lint-baseline.json b/scripts/typed-error-lint-baseline.json index c54bd7cf..6bf482ac 100644 --- a/scripts/typed-error-lint-baseline.json +++ b/scripts/typed-error-lint-baseline.json @@ -50,7 +50,7 @@ { "rule": "silent-clamp", "command": "google/news", - "file": "clis/google/news.js", + "file": "plugins/google/news.js", "line": 23, "text": "const limit = Math.max(1, Math.min(Number(args.limit), 100));", "occurrence": 0 @@ -58,7 +58,7 @@ { "rule": "silent-clamp", "command": "google/search", - "file": "clis/google/search.js", + "file": "plugins/google/search.js", "line": 28, "text": "const limit = Math.max(1, Math.min(Number(args.limit), 100));", "occurrence": 0 @@ -66,7 +66,7 @@ { "rule": "silent-clamp", "command": "google/trends", - "file": "clis/google/trends.js", + "file": "plugins/google/trends.js", "line": 21, "text": "const limit = Math.max(1, Math.min(Number(args.limit), 100));", "occurrence": 0 diff --git a/src/hosted/availability.test.ts b/src/hosted/availability.test.ts index 994e7b85..7bbb72df 100644 --- a/src/hosted/availability.test.ts +++ b/src/hosted/availability.test.ts @@ -179,9 +179,9 @@ describe('hosted availability', () => { it('matches the reviewed local-only adapter exception sets exactly', () => { const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); - const entries = JSON.parse( - fs.readFileSync(path.join(root, 'cli-manifest.json'), 'utf8'), - ) as ManifestEntry[]; + const entries = ['cli-manifest.json', 'plugin-command-manifest.json'].flatMap(file => JSON.parse( + fs.readFileSync(path.join(root, file), 'utf8'), + ) as ManifestEntry[]); const byReason = new Map([ ['local-tool', []], ['desktop-app', []], diff --git a/webcmd-plugin.json b/webcmd-plugin.json index 572cc411..666d84f6 100644 --- a/webcmd-plugin.json +++ b/webcmd-plugin.json @@ -74,6 +74,26 @@ "handle": "agentrhq" } }, + "brave": { + "path": "plugins/brave", + "version": "0.1.0", + "description": "Webcmd commands for brave", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "chatwise": { + "path": "plugins/chatwise", + "version": "0.1.0", + "description": "Webcmd commands for chatwise", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "cincinnati": { "path": "plugins/cincinnati", "version": "0.1.0", @@ -84,6 +104,16 @@ "handle": "agentrhq" } }, + "codex": { + "path": "plugins/codex", + "version": "0.1.0", + "description": "Webcmd commands for codex", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "coingecko": { "path": "plugins/coingecko", "version": "0.1.0", @@ -114,6 +144,16 @@ "handle": "agentrhq" } }, + "cursor": { + "path": "plugins/cursor", + "version": "0.1.0", + "description": "Webcmd commands for cursor", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "dblp": { "path": "plugins/dblp", "version": "0.1.0", @@ -164,6 +204,16 @@ "handle": "agentrhq" } }, + "duckduckgo": { + "path": "plugins/duckduckgo", + "version": "0.1.0", + "description": "Webcmd commands for duckduckgo", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "endoflife": { "path": "plugins/endoflife", "version": "0.1.0", @@ -204,6 +254,26 @@ "handle": "agentrhq" } }, + "google": { + "path": "plugins/google", + "version": "0.1.0", + "description": "Webcmd commands for google", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "google-scholar": { + "path": "plugins/google-scholar", + "version": "0.1.0", + "description": "Webcmd commands for google-scholar", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "goproxy": { "path": "plugins/goproxy", "version": "0.1.0", @@ -544,6 +614,16 @@ "handle": "agentrhq" } }, + "trae-solo": { + "path": "plugins/trae-solo", + "version": "0.1.0", + "description": "Webcmd commands for trae-solo", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "tvmaze": { "path": "plugins/tvmaze", "version": "0.1.0", @@ -594,6 +674,16 @@ "handle": "agentrhq" } }, + "yahoo": { + "path": "plugins/yahoo", + "version": "0.1.0", + "description": "Webcmd commands for yahoo", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "yale": { "path": "plugins/yale", "version": "0.1.0", From f893411ac6b1f7ca5fea1b39af7fb05317f7a5b0 Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 17:17:28 +0530 Subject: [PATCH 15/39] refactor: migrate content and market adapters to plugins --- cli-manifest.json | 2044 +--------- plugin-command-manifest.json | 3382 +++++++++++++---- plugins/barchart/README.md | 18 + {clis => plugins}/barchart/flow.js | 0 {clis => plugins}/barchart/greeks.js | 0 {clis => plugins}/barchart/options.js | 0 plugins/barchart/package.json | 9 + {clis => plugins}/barchart/quote.js | 0 .../barchart/test}/greeks.test.js | 4 +- plugins/barchart/webcmd-plugin.json | 10 + plugins/bloomberg/README.md | 27 + {clis => plugins}/bloomberg/businessweek.js | 0 {clis => plugins}/bloomberg/crypto.js | 0 {clis => plugins}/bloomberg/economics.js | 0 {clis => plugins}/bloomberg/feeds.js | 0 {clis => plugins}/bloomberg/green.js | 0 {clis => plugins}/bloomberg/industries.js | 0 {clis => plugins}/bloomberg/main.js | 0 {clis => plugins}/bloomberg/markets.js | 0 {clis => plugins}/bloomberg/news.js | 0 {clis => plugins}/bloomberg/opinions.js | 0 plugins/bloomberg/package.json | 9 + {clis => plugins}/bloomberg/politics.js | 0 {clis => plugins}/bloomberg/pursuits.js | 0 {clis => plugins}/bloomberg/tech.js | 0 .../bloomberg/test}/businessweek.test.js | 2 +- .../bloomberg/test}/utils.test.js | 2 +- {clis => plugins}/bloomberg/utils.js | 0 plugins/bloomberg/webcmd-plugin.json | 10 + plugins/booking/README.md | 15 + plugins/booking/package.json | 9 + {clis => plugins}/booking/search.js | 0 .../booking/test}/booking.test.js | 4 +- plugins/booking/webcmd-plugin.json | 10 + plugins/chess/README.md | 18 + {clis => plugins}/chess/analyze.js | 0 {clis => plugins}/chess/game.js | 0 {clis => plugins}/chess/games.js | 0 plugins/chess/package.json | 9 + {clis => plugins}/chess/stats.js | 0 .../chess/test}/analyze.test.js | 12 +- .../chess => plugins/chess/test}/game.test.js | 4 +- .../chess/test}/games.test.js | 4 +- .../chess/test}/stats.test.js | 2 +- .../chess/test}/utils.test.js | 2 +- {clis => plugins}/chess/utils.js | 0 plugins/chess/webcmd-plugin.json | 10 + plugins/imdb/README.md | 20 + plugins/imdb/package.json | 9 + {clis => plugins}/imdb/person.js | 0 {clis => plugins}/imdb/reviews.js | 0 {clis => plugins}/imdb/search.js | 0 .../imdb => plugins/imdb/test}/utils.test.js | 2 +- {clis => plugins}/imdb/title.js | 0 {clis => plugins}/imdb/top.js | 0 {clis => plugins}/imdb/trending.js | 0 {clis => plugins}/imdb/utils.js | 0 plugins/imdb/webcmd-plugin.json | 10 + plugins/indeed/README.md | 16 + {clis => plugins}/indeed/job.js | 0 plugins/indeed/package.json | 9 + {clis => plugins}/indeed/search.js | 0 .../indeed/test}/indeed.test.js | 6 +- {clis => plugins}/indeed/utils.js | 0 plugins/indeed/webcmd-plugin.json | 10 + plugins/medium/README.md | 18 + {clis => plugins}/medium/feed.js | 0 plugins/medium/package.json | 9 + {clis => plugins}/medium/search.js | 0 {clis => plugins}/medium/tag.js | 0 {clis => plugins}/medium/user.js | 0 {clis => plugins}/medium/utils.js | 0 plugins/medium/webcmd-plugin.json | 10 + plugins/producthunt/README.md | 18 + {clis => plugins}/producthunt/browse.js | 0 {clis => plugins}/producthunt/hot.js | 0 plugins/producthunt/package.json | 9 + {clis => plugins}/producthunt/posts.js | 0 .../test}/browser-commands.test.js | 4 +- .../producthunt/test}/utils.test.js | 4 +- {clis => plugins}/producthunt/today.js | 0 {clis => plugins}/producthunt/utils.js | 0 plugins/producthunt/webcmd-plugin.json | 10 + plugins/substack/README.md | 17 + {clis => plugins}/substack/feed.js | 0 plugins/substack/package.json | 9 + {clis => plugins}/substack/publication.js | 0 {clis => plugins}/substack/search.js | 0 .../substack/test}/utils.test.js | 2 +- {clis => plugins}/substack/utils.js | 0 plugins/substack/webcmd-plugin.json | 10 + plugins/uiverse/README.md | 16 + {clis => plugins}/uiverse/_shared.js | 0 {clis => plugins}/uiverse/code.js | 0 plugins/uiverse/package.json | 9 + {clis => plugins}/uiverse/preview.js | 0 .../uiverse/test}/_shared.test.js | 2 +- .../uiverse/test}/navigation.test.js | 4 +- plugins/uiverse/webcmd-plugin.json | 10 + plugins/web/README.md | 15 + {clis => plugins}/web/fetch-browser.js | 0 {clis => plugins}/web/fetch.js | 0 plugins/web/package.json | 9 + .../web/test}/fetch-browser.test.js | 2 +- plugins/web/webcmd-plugin.json | 10 + plugins/yahoo-finance/README.md | 15 + plugins/yahoo-finance/package.json | 9 + {clis => plugins}/yahoo-finance/quote.js | 0 plugins/yahoo-finance/webcmd-plugin.json | 10 + plugins/zlibrary/README.md | 16 + {clis => plugins}/zlibrary/info.js | 0 plugins/zlibrary/package.json | 9 + {clis => plugins}/zlibrary/search.js | 0 .../zlibrary/test}/commands.test.js | 8 +- plugins/zlibrary/test/page-mock.js | 11 + {clis => plugins}/zlibrary/utils.js | 0 plugins/zlibrary/webcmd-plugin.json | 10 + scripts/silent-column-drop-baseline.json | 20 +- scripts/typed-error-lint-baseline.json | 30 +- webcmd-plugin.json | 130 + 120 files changed, 3390 insertions(+), 2773 deletions(-) create mode 100644 plugins/barchart/README.md rename {clis => plugins}/barchart/flow.js (100%) rename {clis => plugins}/barchart/greeks.js (100%) rename {clis => plugins}/barchart/options.js (100%) create mode 100644 plugins/barchart/package.json rename {clis => plugins}/barchart/quote.js (100%) rename {clis/barchart => plugins/barchart/test}/greeks.test.js (98%) create mode 100644 plugins/barchart/webcmd-plugin.json create mode 100644 plugins/bloomberg/README.md rename {clis => plugins}/bloomberg/businessweek.js (100%) rename {clis => plugins}/bloomberg/crypto.js (100%) rename {clis => plugins}/bloomberg/economics.js (100%) rename {clis => plugins}/bloomberg/feeds.js (100%) rename {clis => plugins}/bloomberg/green.js (100%) rename {clis => plugins}/bloomberg/industries.js (100%) rename {clis => plugins}/bloomberg/main.js (100%) rename {clis => plugins}/bloomberg/markets.js (100%) rename {clis => plugins}/bloomberg/news.js (100%) rename {clis => plugins}/bloomberg/opinions.js (100%) create mode 100644 plugins/bloomberg/package.json rename {clis => plugins}/bloomberg/politics.js (100%) rename {clis => plugins}/bloomberg/pursuits.js (100%) rename {clis => plugins}/bloomberg/tech.js (100%) rename {clis/bloomberg => plugins/bloomberg/test}/businessweek.test.js (99%) rename {clis/bloomberg => plugins/bloomberg/test}/utils.test.js (99%) rename {clis => plugins}/bloomberg/utils.js (100%) create mode 100644 plugins/bloomberg/webcmd-plugin.json create mode 100644 plugins/booking/README.md create mode 100644 plugins/booking/package.json rename {clis => plugins}/booking/search.js (100%) rename {clis/booking => plugins/booking/test}/booking.test.js (99%) create mode 100644 plugins/booking/webcmd-plugin.json create mode 100644 plugins/chess/README.md rename {clis => plugins}/chess/analyze.js (100%) rename {clis => plugins}/chess/game.js (100%) rename {clis => plugins}/chess/games.js (100%) create mode 100644 plugins/chess/package.json rename {clis => plugins}/chess/stats.js (100%) rename {clis/chess => plugins/chess/test}/analyze.test.js (91%) rename {clis/chess => plugins/chess/test}/game.test.js (98%) rename {clis/chess => plugins/chess/test}/games.test.js (98%) rename {clis/chess => plugins/chess/test}/stats.test.js (99%) rename {clis/chess => plugins/chess/test}/utils.test.js (99%) rename {clis => plugins}/chess/utils.js (100%) create mode 100644 plugins/chess/webcmd-plugin.json create mode 100644 plugins/imdb/README.md create mode 100644 plugins/imdb/package.json rename {clis => plugins}/imdb/person.js (100%) rename {clis => plugins}/imdb/reviews.js (100%) rename {clis => plugins}/imdb/search.js (100%) rename {clis/imdb => plugins/imdb/test}/utils.test.js (99%) rename {clis => plugins}/imdb/title.js (100%) rename {clis => plugins}/imdb/top.js (100%) rename {clis => plugins}/imdb/trending.js (100%) rename {clis => plugins}/imdb/utils.js (100%) create mode 100644 plugins/imdb/webcmd-plugin.json create mode 100644 plugins/indeed/README.md rename {clis => plugins}/indeed/job.js (100%) create mode 100644 plugins/indeed/package.json rename {clis => plugins}/indeed/search.js (100%) rename {clis/indeed => plugins/indeed/test}/indeed.test.js (99%) rename {clis => plugins}/indeed/utils.js (100%) create mode 100644 plugins/indeed/webcmd-plugin.json create mode 100644 plugins/medium/README.md rename {clis => plugins}/medium/feed.js (100%) create mode 100644 plugins/medium/package.json rename {clis => plugins}/medium/search.js (100%) rename {clis => plugins}/medium/tag.js (100%) rename {clis => plugins}/medium/user.js (100%) rename {clis => plugins}/medium/utils.js (100%) create mode 100644 plugins/medium/webcmd-plugin.json create mode 100644 plugins/producthunt/README.md rename {clis => plugins}/producthunt/browse.js (100%) rename {clis => plugins}/producthunt/hot.js (100%) create mode 100644 plugins/producthunt/package.json rename {clis => plugins}/producthunt/posts.js (100%) rename {clis/producthunt => plugins/producthunt/test}/browser-commands.test.js (98%) rename {clis/producthunt => plugins/producthunt/test}/utils.test.js (98%) rename {clis => plugins}/producthunt/today.js (100%) rename {clis => plugins}/producthunt/utils.js (100%) create mode 100644 plugins/producthunt/webcmd-plugin.json create mode 100644 plugins/substack/README.md rename {clis => plugins}/substack/feed.js (100%) create mode 100644 plugins/substack/package.json rename {clis => plugins}/substack/publication.js (100%) rename {clis => plugins}/substack/search.js (100%) rename {clis/substack => plugins/substack/test}/utils.test.js (99%) rename {clis => plugins}/substack/utils.js (100%) create mode 100644 plugins/substack/webcmd-plugin.json create mode 100644 plugins/uiverse/README.md rename {clis => plugins}/uiverse/_shared.js (100%) rename {clis => plugins}/uiverse/code.js (100%) create mode 100644 plugins/uiverse/package.json rename {clis => plugins}/uiverse/preview.js (100%) rename {clis/uiverse => plugins/uiverse/test}/_shared.test.js (98%) rename {clis/uiverse => plugins/uiverse/test}/navigation.test.js (90%) create mode 100644 plugins/uiverse/webcmd-plugin.json create mode 100644 plugins/web/README.md rename {clis => plugins}/web/fetch-browser.js (100%) rename {clis => plugins}/web/fetch.js (100%) create mode 100644 plugins/web/package.json rename {clis/web => plugins/web/test}/fetch-browser.test.js (99%) create mode 100644 plugins/web/webcmd-plugin.json create mode 100644 plugins/yahoo-finance/README.md create mode 100644 plugins/yahoo-finance/package.json rename {clis => plugins}/yahoo-finance/quote.js (100%) create mode 100644 plugins/yahoo-finance/webcmd-plugin.json create mode 100644 plugins/zlibrary/README.md rename {clis => plugins}/zlibrary/info.js (100%) create mode 100644 plugins/zlibrary/package.json rename {clis => plugins}/zlibrary/search.js (100%) rename {clis/zlibrary => plugins/zlibrary/test}/commands.test.js (95%) create mode 100644 plugins/zlibrary/test/page-mock.js rename {clis => plugins}/zlibrary/utils.js (100%) create mode 100644 plugins/zlibrary/webcmd-plugin.json diff --git a/cli-manifest.json b/cli-manifest.json index d838e3fc..8704ef33 100644 --- a/cli-manifest.json +++ b/cli-manifest.json @@ -1640,193 +1640,6 @@ "navigateBefore": false, "siteSession": "persistent" }, - { - "site": "barchart", - "name": "flow", - "description": "Barchart unusual options activity / options flow", - "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "type", - "type": "str", - "default": "all", - "required": false, - "help": "Filter: all, call, or put", - "choices": [ - "all", - "call", - "put" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "symbol", - "type", - "strike", - "expiration", - "last", - "volume", - "openInterest", - "volOiRatio", - "iv" - ], - "type": "js", - "modulePath": "barchart/flow.js", - "sourceFile": "barchart/flow.js", - "navigateBefore": "https://www.barchart.com" - }, - { - "site": "barchart", - "name": "greeks", - "description": "Barchart options greeks overview (IV, delta, gamma, theta, vega)", - "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Stock ticker (e.g. AAPL)" - }, - { - "name": "expiration", - "type": "str", - "required": false, - "help": "Expiration date (YYYY-MM-DD). Defaults to the nearest available expiration." - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of near-the-money strikes per type (1-100)" - } - ], - "columns": [ - "type", - "strike", - "last", - "iv", - "delta", - "gamma", - "theta", - "vega", - "rho", - "volume", - "openInterest", - "expiration" - ], - "type": "js", - "modulePath": "barchart/greeks.js", - "sourceFile": "barchart/greeks.js", - "navigateBefore": "https://www.barchart.com" - }, - { - "site": "barchart", - "name": "options", - "description": "Barchart options chain with greeks, IV, volume, and open interest", - "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Stock ticker (e.g. AAPL)" - }, - { - "name": "type", - "type": "str", - "default": "Call", - "required": false, - "help": "Option type: Call or Put", - "choices": [ - "Call", - "Put" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max number of strikes to return" - } - ], - "columns": [ - "strike", - "bid", - "ask", - "last", - "change", - "volume", - "openInterest", - "iv", - "delta", - "gamma", - "theta", - "vega", - "expiration" - ], - "type": "js", - "modulePath": "barchart/options.js", - "sourceFile": "barchart/options.js", - "navigateBefore": "https://www.barchart.com" - }, - { - "site": "barchart", - "name": "quote", - "description": "Barchart stock quote with price, volume, and key metrics", - "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" - } - ], - "columns": [ - "symbol", - "name", - "price", - "change", - "changePct", - "open", - "high", - "low", - "prevClose", - "volume", - "avgVolume", - "marketCap", - "peRatio", - "eps" - ], - "type": "js", - "modulePath": "barchart/quote.js", - "sourceFile": "barchart/quote.js", - "navigateBefore": "https://www.barchart.com" - }, { "site": "bigbasket", "name": "add-to-cart", @@ -2357,447 +2170,6 @@ "navigateBefore": false, "siteSession": "persistent" }, - { - "site": "bloomberg", - "name": "businessweek", - "description": "Bloomberg Businessweek top stories", - "access": "read", - "domain": "www.bloomberg.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of stories to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "bloomberg/businessweek.js", - "sourceFile": "bloomberg/businessweek.js" - }, - { - "site": "bloomberg", - "name": "crypto", - "description": "Bloomberg Crypto top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "bloomberg/crypto.js", - "sourceFile": "bloomberg/crypto.js" - }, - { - "site": "bloomberg", - "name": "economics", - "description": "Bloomberg Economics top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "bloomberg/economics.js", - "sourceFile": "bloomberg/economics.js" - }, - { - "site": "bloomberg", - "name": "feeds", - "description": "List the Bloomberg RSS feed aliases used by the adapter", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "name", - "url" - ], - "type": "js", - "modulePath": "bloomberg/feeds.js", - "sourceFile": "bloomberg/feeds.js" - }, - { - "site": "bloomberg", - "name": "green", - "description": "Bloomberg Green (climate & energy) top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "bloomberg/green.js", - "sourceFile": "bloomberg/green.js" - }, - { - "site": "bloomberg", - "name": "industries", - "description": "Bloomberg Industries top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "bloomberg/industries.js", - "sourceFile": "bloomberg/industries.js" - }, - { - "site": "bloomberg", - "name": "main", - "description": "Bloomberg homepage top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "bloomberg/main.js", - "sourceFile": "bloomberg/main.js" - }, - { - "site": "bloomberg", - "name": "markets", - "description": "Bloomberg Markets top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "bloomberg/markets.js", - "sourceFile": "bloomberg/markets.js" - }, - { - "site": "bloomberg", - "name": "news", - "description": "Read a Bloomberg story/article page and return title, full content, and media links", - "access": "read", - "domain": "www.bloomberg.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "link", - "type": "str", - "required": true, - "positional": true, - "help": "Bloomberg story/article URL or relative Bloomberg path" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks", - "content" - ], - "type": "js", - "modulePath": "bloomberg/news.js", - "sourceFile": "bloomberg/news.js", - "navigateBefore": "https://www.bloomberg.com" - }, - { - "site": "bloomberg", - "name": "opinions", - "description": "Bloomberg Opinion top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "bloomberg/opinions.js", - "sourceFile": "bloomberg/opinions.js" - }, - { - "site": "bloomberg", - "name": "politics", - "description": "Bloomberg Politics top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "bloomberg/politics.js", - "sourceFile": "bloomberg/politics.js" - }, - { - "site": "bloomberg", - "name": "pursuits", - "description": "Bloomberg Pursuits (lifestyle) top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "bloomberg/pursuits.js", - "sourceFile": "bloomberg/pursuits.js" - }, - { - "site": "bloomberg", - "name": "tech", - "description": "Bloomberg Tech top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "bloomberg/tech.js", - "sourceFile": "bloomberg/tech.js" - }, - { - "site": "booking", - "name": "search", - "description": "Search Booking.com hotels by destination and dates (server-rendered card scrape).", - "access": "read", - "example": "webcmd booking search Tokyo --checkin 2026-06-15 --checkout 2026-06-17 -f yaml", - "domain": "www.booking.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "destination", - "type": "str", - "required": true, - "positional": true, - "help": "Destination keyword (city, district, or hotel name)" - }, - { - "name": "checkin", - "type": "str", - "required": true, - "help": "Check-in date YYYY-MM-DD" - }, - { - "name": "checkout", - "type": "str", - "required": true, - "help": "Check-out date YYYY-MM-DD" - }, - { - "name": "adults", - "type": "int", - "default": 2, - "required": false, - "help": "Number of adults (1-30)" - }, - { - "name": "rooms", - "type": "int", - "default": 1, - "required": false, - "help": "Number of rooms (1-30)" - }, - { - "name": "children", - "type": "int", - "default": 0, - "required": false, - "help": "Number of children (0-10)" - }, - { - "name": "currency", - "type": "str", - "required": false, - "help": "Force result currency (e.g. USD, JPY, CNY)" - }, - { - "name": "lang", - "type": "str", - "required": false, - "help": "Force result language (e.g. en-us, zh-cn, ja)" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max rows to return (1-100; Booking pages 25 per request)" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Result offset for pagination (multiple of 25)" - } - ], - "columns": [ - "rank", - "name", - "country", - "slug", - "star_rating", - "review_score", - "review_count", - "price_amount", - "price_currency", - "distance", - "recommended_room", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "booking/search.js", - "sourceFile": "booking/search.js" - }, { "site": "chatgpt", "name": "ask", @@ -3572,164 +2944,27 @@ } ], "columns": [ - "Status" - ], - "type": "js", - "modulePath": "chatgpt-app/send.js", - "sourceFile": "chatgpt-app/send.js" - }, - { - "site": "chatgpt-app", - "name": "status", - "description": "Check if ChatGPT Desktop App is running natively on macOS", - "access": "read", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "chatgpt-app/status.js", - "sourceFile": "chatgpt-app/status.js" - }, - { - "site": "chess", - "name": "analyze", - "description": "Open a Chess.com game in the browser analysis board", - "access": "read", - "domain": "www.chess.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "game-url", - "type": "string", - "required": true, - "positional": true, - "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" - } - ], - "columns": [ - "kind", - "game_id", - "analysis_url" - ], - "type": "js", - "modulePath": "chess/analyze.js", - "sourceFile": "chess/analyze.js", - "navigateBefore": false - }, - { - "site": "chess", - "name": "game", - "description": "Chess.com single-game detail (white, black, result, ECO, time control) by full game URL", - "access": "read", - "domain": "www.chess.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "game-url", - "type": "string", - "required": true, - "positional": true, - "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" - } - ], - "columns": [ - "kind", - "game_id", - "date", - "white", - "white_rating", - "black", - "black_rating", - "result", - "winner_color", - "termination", - "eco", - "time_control", - "rated", - "ply_count", - "url" - ], - "type": "js", - "modulePath": "chess/game.js", - "sourceFile": "chess/game.js" - }, - { - "site": "chess", - "name": "games", - "description": "Chess.com recent games for a player, newest first", - "access": "read", - "domain": "api.chess.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Chess.com username" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of recent games (1-100)" - } - ], - "columns": [ - "date", - "time_class", - "rated", - "my_color", - "my_rating", - "my_result", - "opponent", - "opponent_rating", - "accuracy_white", - "accuracy_black", - "eco", - "opening_name", - "url" + "Status" ], "type": "js", - "modulePath": "chess/games.js", - "sourceFile": "chess/games.js" + "modulePath": "chatgpt-app/send.js", + "sourceFile": "chatgpt-app/send.js" }, { - "site": "chess", - "name": "stats", - "description": "Chess.com player ratings + win/loss record across game kinds", + "site": "chatgpt-app", + "name": "status", + "description": "Check if ChatGPT Desktop App is running natively on macOS", "access": "read", - "domain": "api.chess.com", + "domain": "localhost", "strategy": "public", "browser": false, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Chess.com username (case-insensitive)" - } - ], + "args": [], "columns": [ - "kind", - "rating_current", - "rating_best", - "wins", - "losses", - "draws" + "Status" ], "type": "js", - "modulePath": "chess/stats.js", - "sourceFile": "chess/stats.js" + "modulePath": "chatgpt-app/status.js", + "sourceFile": "chatgpt-app/status.js" }, { "site": "claude", @@ -7059,402 +6294,107 @@ "help": "Optional name/owner substring filter (e.g. \"stability\", \"openai/\")" }, { - "name": "sdk", - "type": "string", - "required": false, - "help": "Filter by Space SDK: gradio / streamlit / docker / static" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max spaces (max 100; one API page)." - } - ], - "columns": [ - "rank", - "id", - "author", - "sdk", - "likes", - "tags", - "lastModified", - "url" - ], - "type": "js", - "modulePath": "hf/spaces.js", - "sourceFile": "hf/spaces.js" - }, - { - "site": "hf", - "name": "top", - "description": "Top upvoted Hugging Face papers", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of papers" - }, - { - "name": "all", - "type": "bool", - "default": false, - "required": false, - "help": "Return all papers (ignore limit)" - }, - { - "name": "date", - "type": "str", - "required": false, - "help": "Date (YYYY-MM-DD), defaults to most recent" - }, - { - "name": "period", - "type": "str", - "default": "daily", - "required": false, - "help": "Time period: daily, weekly, or monthly", - "choices": [ - "daily", - "weekly", - "monthly" - ] - } - ], - "columns": [ - "rank", - "id", - "title", - "upvotes", - "authors" - ], - "type": "js", - "modulePath": "hf/top.js", - "sourceFile": "hf/top.js" - }, - { - "site": "hf", - "name": "whoami", - "description": "Show the current logged-in hf account", - "access": "read", - "domain": "huggingface.co", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "username", - "fullname", - "type" - ], - "type": "js", - "modulePath": "hf/auth.js", - "sourceFile": "hf/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "imdb", - "name": "person", - "description": "Get actor or director info", - "access": "read", - "domain": "www.imdb.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "IMDb person ID (nm0634240) or URL" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max filmography entries" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "imdb/person.js", - "sourceFile": "imdb/person.js" - }, - { - "site": "imdb", - "name": "reviews", - "description": "Get user reviews for a movie or TV show", - "access": "read", - "domain": "www.imdb.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "IMDb title ID (tt1375666) or URL" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of reviews" - } - ], - "columns": [ - "rank", - "title", - "rating", - "author", - "date", - "text" - ], - "type": "js", - "modulePath": "imdb/reviews.js", - "sourceFile": "imdb/reviews.js" - }, - { - "site": "imdb", - "name": "search", - "description": "Search IMDb for movies, TV shows, and people", - "access": "read", - "domain": "www.imdb.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "id", - "title", - "year", - "type", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "imdb/search.js", - "sourceFile": "imdb/search.js" - }, - { - "site": "imdb", - "name": "title", - "description": "Get movie or TV show details", - "access": "read", - "domain": "www.imdb.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "IMDb title ID (tt1375666) or URL" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "imdb/title.js", - "sourceFile": "imdb/title.js" - }, - { - "site": "imdb", - "name": "top", - "description": "IMDb Top 250 Movies", - "access": "read", - "domain": "www.imdb.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "rating", - "votes", - "genre", - "url" - ], - "type": "js", - "modulePath": "imdb/top.js", - "sourceFile": "imdb/top.js" - }, - { - "site": "imdb", - "name": "trending", - "description": "IMDb Most Popular Movies", - "access": "read", - "domain": "www.imdb.com", - "strategy": "public", - "browser": true, - "args": [ - { + "name": "sdk", + "type": "string", + "required": false, + "help": "Filter by Space SDK: gradio / streamlit / docker / static" + }, + { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of results" + "help": "Max spaces (max 100; one API page)." } ], "columns": [ "rank", - "title", - "rating", - "genre", - "url" - ], - "type": "js", - "modulePath": "imdb/trending.js", - "sourceFile": "imdb/trending.js" - }, - { - "site": "indeed", - "name": "job", - "aliases": [ - "detail", - "view" - ], - "description": "Read the full Indeed job posting by jk (job key)", - "access": "read", - "domain": "www.indeed.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Job key (16-char hex from `indeed search`, e.g. \"dccc07ac5a6a3683\")" - } - ], - "columns": [ "id", - "title", - "company", - "location", - "salary", - "job_type", - "description", + "author", + "sdk", + "likes", + "tags", + "lastModified", "url" ], "type": "js", - "modulePath": "indeed/job.js", - "sourceFile": "indeed/job.js", - "navigateBefore": false + "modulePath": "hf/spaces.js", + "sourceFile": "hf/spaces.js" }, { - "site": "indeed", - "name": "search", - "description": "Indeed keyword job search (rendered DOM via browser session, US site)", + "site": "hf", + "name": "top", + "description": "Top upvoted Hugging Face papers", "access": "read", - "domain": "www.indeed.com", - "strategy": "cookie", - "browser": true, + "domain": "huggingface.co", + "strategy": "public", + "browser": false, "args": [ { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Job keyword (title / skill / company)" - }, - { - "name": "location", - "type": "string", - "default": "", - "required": false, - "help": "Location filter (e.g. \"remote\", \"New York, NY\", \"San Francisco\")" - }, - { - "name": "fromage", - "type": "string", - "default": "", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Recency filter, days back: 1 / 3 / 7 / 14" + "help": "Number of papers" }, { - "name": "sort", - "type": "string", - "default": "relevance", + "name": "all", + "type": "bool", + "default": false, "required": false, - "help": "Sort order: relevance | date" + "help": "Return all papers (ignore limit)" }, { - "name": "start", - "type": "int", - "default": 0, + "name": "date", + "type": "str", "required": false, - "help": "Pagination offset (multiple of 10, 0-based)" + "help": "Date (YYYY-MM-DD), defaults to most recent" }, { - "name": "limit", - "type": "int", - "default": 15, + "name": "period", + "type": "str", + "default": "daily", "required": false, - "help": "Max rows to return (1-25, capped at one page)" + "help": "Time period: daily, weekly, or monthly", + "choices": [ + "daily", + "weekly", + "monthly" + ] } ], "columns": [ "rank", "id", "title", - "company", - "location", - "salary", - "tags", - "url" + "upvotes", + "authors" ], - "tags": [ - "search" + "type": "js", + "modulePath": "hf/top.js", + "sourceFile": "hf/top.js" + }, + { + "site": "hf", + "name": "whoami", + "description": "Show the current logged-in hf account", + "access": "read", + "domain": "huggingface.co", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "username", + "fullname", + "type" ], "type": "js", - "modulePath": "indeed/search.js", - "sourceFile": "indeed/search.js", - "navigateBefore": false + "modulePath": "hf/auth.js", + "sourceFile": "hf/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "instagram", @@ -8539,208 +7479,56 @@ "browser": true, "args": [], "columns": [ - "ID", - "Name", - "Description", - "Source" - ], - "type": "js", - "modulePath": "manus/skills.js", - "sourceFile": "manus/skills.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "status", - "description": "Show current Manus user profile and credit summary.", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "manus/status.js", - "sourceFile": "manus/status.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "whoami", - "description": "Show the current logged-in manus account", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "manus/auth.js", - "sourceFile": "manus/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "medium", - "name": "feed", - "description": "Medium popular posts Feed", - "access": "read", - "domain": "medium.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "topic", - "type": "str", - "default": "", - "required": false, - "help": "Topic (for example technology, programming, ai)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" - } - ], - "columns": [ - "rank", - "title", - "author", - "date", - "readTime", - "claps" - ], - "type": "js", - "modulePath": "medium/feed.js", - "sourceFile": "medium/feed.js", - "navigateBefore": "https://medium.com" - }, - { - "site": "medium", - "name": "search", - "description": "Search Medium posts", - "access": "read", - "domain": "medium.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" - } - ], - "columns": [ - "rank", - "title", - "author", - "date", - "readTime", - "claps", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "medium/search.js", - "sourceFile": "medium/search.js", - "navigateBefore": "https://medium.com" - }, - { - "site": "medium", - "name": "tag", - "description": "Latest Medium articles tagged with a given keyword (RSS feed)", - "access": "read", - "domain": "medium.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "tag", - "type": "str", - "required": true, - "positional": true, - "help": "Lowercase tag slug (e.g. \"programming\", \"machine-learning\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max articles (1-25 — single RSS page)" - } - ], - "columns": [ - "rank", - "title", - "author", - "description", - "categories", - "published", - "url" + "ID", + "Name", + "Description", + "Source" ], "type": "js", - "modulePath": "medium/tag.js", - "sourceFile": "medium/tag.js" + "modulePath": "manus/skills.js", + "sourceFile": "manus/skills.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "medium", - "name": "user", - "description": "Get Medium user posts", + "site": "manus", + "name": "status", + "description": "Show current Manus user profile and credit summary.", "access": "read", - "domain": "medium.com", + "domain": "manus.im", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Medium username(for example @username or username)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" - } + "args": [], + "columns": [ + "Field", + "Value" ], + "type": "js", + "modulePath": "manus/status.js", + "sourceFile": "manus/status.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "whoami", + "description": "Show the current logged-in manus account", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "title", - "date", - "readTime", - "claps", - "url" + "logged_in", + "site", + "user_id", + "name" ], "type": "js", - "modulePath": "medium/user.js", - "sourceFile": "medium/user.js", - "navigateBefore": "https://medium.com" + "modulePath": "manus/auth.js", + "sourceFile": "manus/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "mercury", @@ -10457,137 +9245,6 @@ "navigateBefore": false, "siteSession": "persistent" }, - { - "site": "producthunt", - "name": "browse", - "description": "Best products in a Product Hunt category", - "access": "read", - "domain": "www.producthunt.com", - "strategy": "intercept", - "browser": true, - "args": [ - { - "name": "category", - "type": "string", - "required": true, - "positional": true, - "help": "Category slug, e.g. vibe-coding, ai-agents, developer-tools" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results (max 50)" - } - ], - "columns": [ - "rank", - "name", - "tagline", - "reviews", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "producthunt/browse.js", - "sourceFile": "producthunt/browse.js", - "navigateBefore": true - }, - { - "site": "producthunt", - "name": "hot", - "description": "Today's top Product Hunt launches with vote counts", - "access": "read", - "domain": "www.producthunt.com", - "strategy": "intercept", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results (max 50)" - } - ], - "columns": [ - "rank", - "name", - "votes", - "url" - ], - "type": "js", - "modulePath": "producthunt/hot.js", - "sourceFile": "producthunt/hot.js", - "navigateBefore": true - }, - { - "site": "producthunt", - "name": "posts", - "description": "Latest Product Hunt launches (optional category filter)", - "access": "read", - "domain": "www.producthunt.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results (max 50)" - }, - { - "name": "category", - "type": "string", - "default": "", - "required": false, - "help": "Category filter: ai-agents, ai-coding-agents, ai-code-editors, ai-chatbots, ai-workflow-automation, vibe-coding, developer-tools, productivity, design-creative, marketing-sales, no-code-platforms, llms, finance, social-community, engineering-development" - } - ], - "columns": [ - "rank", - "name", - "tagline", - "author", - "date", - "url" - ], - "type": "js", - "modulePath": "producthunt/posts.js", - "sourceFile": "producthunt/posts.js" - }, - { - "site": "producthunt", - "name": "today", - "description": "Today's Product Hunt launches (most recent day in feed)", - "access": "read", - "domain": "www.producthunt.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results" - } - ], - "columns": [ - "rank", - "name", - "tagline", - "author", - "url" - ], - "type": "js", - "modulePath": "producthunt/today.js", - "sourceFile": "producthunt/today.js" - }, { "site": "qoder", "name": "account", @@ -13718,183 +12375,60 @@ "positional": true, "help": "on or off", "choices": [ - "on", - "off" - ] - } - ], - "columns": [ - "shuffle" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "status", - "description": "Show current playback status", - "access": "read", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "track", - "artist", - "album", - "status", - "progress" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "volume", - "description": "Set playback volume (0-100)", - "access": "write", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "level", - "type": "int", - "default": 50, - "required": true, - "positional": true, - "help": "Volume 0–100" - } - ], - "columns": [ - "volume" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "substack", - "name": "feed", - "description": "Substack popular posts Feed", - "access": "read", - "domain": "substack.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "category", - "type": "str", - "default": "all", - "required": false, - "help": "Post category: all, tech, business, culture, politics, science, health" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" - } - ], - "columns": [ - "rank", - "title", - "author", - "date", - "readTime", - "url" - ], - "type": "js", - "modulePath": "substack/feed.js", - "sourceFile": "substack/feed.js", - "navigateBefore": "https://substack.com" - }, - { - "site": "substack", - "name": "publication", - "description": "Get a specific Substack Newsletter latest posts", - "access": "read", - "domain": "substack.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "Newsletter URL(for example https://example.substack.com)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" + "on", + "off" + ] } ], "columns": [ - "rank", - "title", - "date", - "description", - "url" + "shuffle" ], "type": "js", - "modulePath": "substack/publication.js", - "sourceFile": "substack/publication.js", - "navigateBefore": "https://substack.com" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "substack", - "name": "search", - "description": "Search Substack posts and newsletters", + "site": "spotify", + "name": "status", + "description": "Show current playback status", "access": "read", - "domain": "substack.com", - "strategy": "public", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "track", + "artist", + "album", + "status", + "progress" + ], + "type": "js", + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" + }, + { + "site": "spotify", + "name": "volume", + "description": "Set playback volume (0-100)", + "access": "write", + "strategy": "local", "browser": false, "args": [ { - "name": "keyword", - "type": "str", + "name": "level", + "type": "int", + "default": 50, "required": true, "positional": true, - "help": "Search keyword" - }, - { - "name": "type", - "type": "str", - "default": "posts", - "required": false, - "help": "Search type(posts=posts, publications=Newsletter)", - "choices": [ - "posts", - "publications" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results to return" + "help": "Volume 0–100" } ], "columns": [ - "rank", - "title", - "author", - "date", - "description", - "url" - ], - "tags": [ - "search" + "volume" ], "type": "js", - "modulePath": "substack/search.js", - "sourceFile": "substack/search.js" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { "site": "suno", @@ -17012,89 +15546,6 @@ "navigateBefore": false, "siteSession": "persistent" }, - { - "site": "uiverse", - "name": "code", - "description": "Export Uiverse component code (HTML, CSS, React, or Vue)", - "access": "read", - "domain": "uiverse.io", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "Uiverse URL or author/slug identifier" - }, - { - "name": "target", - "type": "str", - "required": true, - "help": "Code target to export", - "choices": [ - "html", - "css", - "react", - "vue" - ] - } - ], - "columns": [ - "target", - "username", - "slug", - "language", - "length" - ], - "type": "js", - "modulePath": "uiverse/code.js", - "sourceFile": "uiverse/code.js", - "navigateBefore": "https://uiverse.io" - }, - { - "site": "uiverse", - "name": "preview", - "description": "Capture a screenshot of the Uiverse preview element", - "access": "read", - "domain": "uiverse.io", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "Uiverse URL or author/slug identifier" - }, - { - "name": "output", - "type": "str", - "required": false, - "help": "Output image path (defaults to a temp file)" - }, - { - "name": "padding", - "type": "int", - "default": 8, - "required": false, - "help": "Extra padding around the captured preview in pixels" - } - ], - "columns": [ - "username", - "slug", - "width", - "height", - "output" - ], - "type": "js", - "modulePath": "uiverse/preview.js", - "sourceFile": "uiverse/preview.js", - "navigateBefore": "https://uiverse.io" - }, { "site": "upwork", "name": "detail", @@ -17305,133 +15756,6 @@ "navigateBefore": false, "siteSession": "persistent" }, - { - "site": "web", - "name": "fetch-browser", - "description": "Fetch any web page and export as Markdown", - "access": "read", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "help": "Any web page URL" - }, - { - "name": "output", - "type": "str", - "default": "./web-articles", - "required": false, - "help": "Output directory" - }, - { - "name": "download-images", - "type": "boolean", - "default": true, - "required": false, - "help": "Download images locally" - }, - { - "name": "wait", - "type": "int", - "default": 3, - "required": false, - "help": "Seconds to wait after page load" - }, - { - "name": "wait-for", - "type": "str", - "required": false, - "valueRequired": true, - "help": "CSS selector to wait for in the main document or same-origin iframes" - }, - { - "name": "wait-until", - "type": "str", - "default": "domstable", - "required": false, - "help": "Readiness policy after navigation: domstable or networkidle", - "choices": [ - "domstable", - "networkidle" - ] - }, - { - "name": "frames", - "type": "str", - "default": "same-origin", - "required": false, - "help": "Iframe handling mode: relevant same-origin, all-same-origin, or none", - "choices": [ - "same-origin", - "all-same-origin", - "none" - ] - }, - { - "name": "diagnose", - "type": "boolean", - "default": false, - "required": false, - "help": "Print render diagnostics (frames, empty containers, XHR/API-like requests) to stderr" - }, - { - "name": "stdout", - "type": "boolean", - "default": false, - "required": false, - "help": "Print markdown to stdout instead of saving to a file" - } - ], - "columns": [ - "title", - "author", - "publish_time", - "status", - "size", - "saved" - ], - "type": "js", - "modulePath": "web/fetch-browser.js", - "sourceFile": "web/fetch-browser.js", - "navigateBefore": false - }, - { - "site": "yahoo-finance", - "name": "quote", - "description": "Yahoo Finance stock quote", - "access": "read", - "domain": "finance.yahoo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" - } - ], - "columns": [ - "symbol", - "name", - "price", - "change", - "changePercent", - "open", - "high", - "low", - "volume", - "marketCap" - ], - "type": "js", - "modulePath": "yahoo-finance/quote.js", - "sourceFile": "yahoo-finance/quote.js", - "navigateBefore": "https://finance.yahoo.com" - }, { "site": "yollomi", "name": "background", @@ -18781,71 +17105,5 @@ "sourceFile": "zepto/auth.js", "navigateBefore": false, "siteSession": "persistent" - }, - { - "site": "zlibrary", - "name": "info", - "description": "Get book details and available download formats from a Z-Library book page", - "access": "read", - "domain": "z-library.im", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "Z-Library book page URL (e.g. https://z-library.im/book/...)" - } - ], - "columns": [ - "title", - "pdf", - "epub", - "url" - ], - "type": "js", - "modulePath": "zlibrary/info.js", - "sourceFile": "zlibrary/info.js", - "navigateBefore": false - }, - { - "site": "zlibrary", - "name": "search", - "description": "Search Z-Library for books by title, author, ISBN, or keyword", - "access": "read", - "domain": "z-library.im", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (title, author, ISBN, etc.)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max results (1–25)" - } - ], - "columns": [ - "rank", - "title", - "author", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "zlibrary/search.js", - "sourceFile": "zlibrary/search.js", - "navigateBefore": false } ] diff --git a/plugin-command-manifest.json b/plugin-command-manifest.json index 99abce65..cf9afd70 100644 --- a/plugin-command-manifest.json +++ b/plugin-command-manifest.json @@ -410,6 +410,193 @@ "modulePath": "plugins/arxiv/search.js", "sourceFile": "plugins/arxiv/search.js" }, + { + "site": "barchart", + "name": "flow", + "description": "Barchart unusual options activity / options flow", + "access": "read", + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "type", + "type": "str", + "default": "all", + "required": false, + "help": "Filter: all, call, or put", + "choices": [ + "all", + "call", + "put" + ] + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "symbol", + "type", + "strike", + "expiration", + "last", + "volume", + "openInterest", + "volOiRatio", + "iv" + ], + "type": "js", + "modulePath": "plugins/barchart/flow.js", + "sourceFile": "plugins/barchart/flow.js", + "navigateBefore": "https://www.barchart.com" + }, + { + "site": "barchart", + "name": "greeks", + "description": "Barchart options greeks overview (IV, delta, gamma, theta, vega)", + "access": "read", + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Stock ticker (e.g. AAPL)" + }, + { + "name": "expiration", + "type": "str", + "required": false, + "help": "Expiration date (YYYY-MM-DD). Defaults to the nearest available expiration." + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of near-the-money strikes per type (1-100)" + } + ], + "columns": [ + "type", + "strike", + "last", + "iv", + "delta", + "gamma", + "theta", + "vega", + "rho", + "volume", + "openInterest", + "expiration" + ], + "type": "js", + "modulePath": "plugins/barchart/greeks.js", + "sourceFile": "plugins/barchart/greeks.js", + "navigateBefore": "https://www.barchart.com" + }, + { + "site": "barchart", + "name": "options", + "description": "Barchart options chain with greeks, IV, volume, and open interest", + "access": "read", + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Stock ticker (e.g. AAPL)" + }, + { + "name": "type", + "type": "str", + "default": "Call", + "required": false, + "help": "Option type: Call or Put", + "choices": [ + "Call", + "Put" + ] + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max number of strikes to return" + } + ], + "columns": [ + "strike", + "bid", + "ask", + "last", + "change", + "volume", + "openInterest", + "iv", + "delta", + "gamma", + "theta", + "vega", + "expiration" + ], + "type": "js", + "modulePath": "plugins/barchart/options.js", + "sourceFile": "plugins/barchart/options.js", + "navigateBefore": "https://www.barchart.com" + }, + { + "site": "barchart", + "name": "quote", + "description": "Barchart stock quote with price, volume, and key metrics", + "access": "read", + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" + } + ], + "columns": [ + "symbol", + "name", + "price", + "change", + "changePct", + "open", + "high", + "low", + "prevClose", + "volume", + "avgVolume", + "marketCap", + "peRatio", + "eps" + ], + "type": "js", + "modulePath": "plugins/barchart/quote.js", + "sourceFile": "plugins/barchart/quote.js", + "navigateBefore": "https://www.barchart.com" + }, { "site": "bbc", "name": "news", @@ -820,514 +1007,955 @@ "sourceFile": "plugins/binance/trades.js" }, { - "site": "bluesky", - "name": "feeds", - "description": "Popular Bluesky feed generators", + "site": "bloomberg", + "name": "businessweek", + "description": "Bloomberg Businessweek top stories", "access": "read", - "domain": "public.api.bsky.app", + "domain": "www.bloomberg.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of feeds" + "help": "Number of stories to return (max 20)" } ], "columns": [ - "rank", - "name", - "likes", - "creator", - "description" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/feeds.js", - "sourceFile": "plugins/bluesky/feeds.js" + "modulePath": "plugins/bloomberg/businessweek.js", + "sourceFile": "plugins/bloomberg/businessweek.js" }, { - "site": "bluesky", - "name": "followers", - "description": "List followers of a Bluesky user", + "site": "bloomberg", + "name": "crypto", + "description": "Bloomberg Crypto top stories (RSS)", "access": "read", - "domain": "public.api.bsky.app", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ - { - "name": "handle", - "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of followers" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "handle", - "name", - "description" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/followers.js", - "sourceFile": "plugins/bluesky/followers.js" + "modulePath": "plugins/bloomberg/crypto.js", + "sourceFile": "plugins/bloomberg/crypto.js" }, { - "site": "bluesky", - "name": "following", - "description": "List accounts a Bluesky user is following", + "site": "bloomberg", + "name": "economics", + "description": "Bloomberg Economics top stories (RSS)", "access": "read", - "domain": "public.api.bsky.app", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ - { - "name": "handle", - "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of accounts" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "handle", - "name", - "description" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/following.js", - "sourceFile": "plugins/bluesky/following.js" + "modulePath": "plugins/bloomberg/economics.js", + "sourceFile": "plugins/bloomberg/economics.js" }, { - "site": "bluesky", - "name": "profile", - "description": "Get Bluesky user profile info", + "site": "bloomberg", + "name": "feeds", + "description": "List the Bloomberg RSS feed aliases used by the adapter", "access": "read", - "domain": "public.api.bsky.app", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, - "args": [ - { - "name": "handle", - "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle (e.g. bsky.app, jay.bsky.team)" - } - ], + "args": [], "columns": [ - "handle", "name", - "followers", - "following", - "posts", - "description" + "url" ], "type": "js", - "modulePath": "plugins/bluesky/profile.js", - "sourceFile": "plugins/bluesky/profile.js" + "modulePath": "plugins/bloomberg/feeds.js", + "sourceFile": "plugins/bloomberg/feeds.js" }, { - "site": "bluesky", - "name": "search", - "description": "Search Bluesky users", + "site": "bloomberg", + "name": "green", + "description": "Bloomberg Green (climate & energy) top stories (RSS)", "access": "read", - "domain": "public.api.bsky.app", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Number of results" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "handle", - "name", - "followers", - "description" - ], - "tags": [ - "search" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/search.js", - "sourceFile": "plugins/bluesky/search.js" + "modulePath": "plugins/bloomberg/green.js", + "sourceFile": "plugins/bloomberg/green.js" }, { - "site": "bluesky", - "name": "starter-packs", - "description": "Get starter packs created by a Bluesky user", + "site": "bloomberg", + "name": "industries", + "description": "Bloomberg Industries top stories (RSS)", "access": "read", - "domain": "public.api.bsky.app", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ - { - "name": "handle", - "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Number of starter packs" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "name", - "description", - "members", - "joins" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/starter-packs.js", - "sourceFile": "plugins/bluesky/starter-packs.js" + "modulePath": "plugins/bloomberg/industries.js", + "sourceFile": "plugins/bloomberg/industries.js" }, { - "site": "bluesky", - "name": "thread", - "description": "Get a Bluesky post thread with replies", + "site": "bloomberg", + "name": "main", + "description": "Bloomberg homepage top stories (RSS)", "access": "read", - "domain": "public.api.bsky.app", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ - { - "name": "uri", - "type": "str", - "required": true, - "positional": true, - "help": "Post AT URI (at://did:.../app.bsky.feed.post/...) or bsky.app URL" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of replies" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "author", - "text", - "likes", - "reposts", - "replies_count" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/thread.js", - "sourceFile": "plugins/bluesky/thread.js" + "modulePath": "plugins/bloomberg/main.js", + "sourceFile": "plugins/bloomberg/main.js" }, { - "site": "bluesky", - "name": "trending", - "description": "Trending topics on Bluesky", + "site": "bloomberg", + "name": "markets", + "description": "Bloomberg Markets top stories (RSS)", "access": "read", - "domain": "public.api.bsky.app", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of topics" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "topic", - "link" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/trending.js", - "sourceFile": "plugins/bluesky/trending.js" + "modulePath": "plugins/bloomberg/markets.js", + "sourceFile": "plugins/bloomberg/markets.js" }, { - "site": "bluesky", - "name": "user", - "description": "Get recent posts from a Bluesky user", + "site": "bloomberg", + "name": "news", + "description": "Read a Bloomberg story/article page and return title, full content, and media links", "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, + "domain": "www.bloomberg.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "handle", + "name": "link", "type": "str", "required": true, "positional": true, - "help": "Bluesky handle (e.g. bsky.app)" - }, + "help": "Bloomberg story/article URL or relative Bloomberg path" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks", + "content" + ], + "type": "js", + "modulePath": "plugins/bloomberg/news.js", + "sourceFile": "plugins/bloomberg/news.js", + "navigateBefore": "https://www.bloomberg.com" + }, + { + "site": "bloomberg", + "name": "opinions", + "description": "Bloomberg Opinion top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of posts" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "uri", - "text", - "likes", - "reposts", - "replies" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/user.js", - "sourceFile": "plugins/bluesky/user.js" + "modulePath": "plugins/bloomberg/opinions.js", + "sourceFile": "plugins/bloomberg/opinions.js" }, { - "site": "bmwblog", - "name": "article", - "description": "Read a BMWBLOG article by URL or slug", + "site": "bloomberg", + "name": "politics", + "description": "Bloomberg Politics top stories (RSS)", "access": "read", - "domain": "www.bmwblog.com", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ { - "name": "url-or-slug", - "type": "str", - "required": true, - "positional": true, - "help": "BMWBLOG article URL or slug" + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" } ], "columns": [ "title", - "date", - "author", - "sections", - "excerpt", - "url", - "content" + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bmwblog/article.js", - "sourceFile": "plugins/bmwblog/article.js" + "modulePath": "plugins/bloomberg/politics.js", + "sourceFile": "plugins/bloomberg/politics.js" }, { - "site": "bmwblog", - "name": "latest", - "description": "List the latest BMWBLOG articles", + "site": "bloomberg", + "name": "pursuits", + "description": "Bloomberg Pursuits (lifestyle) top stories (RSS)", "access": "read", - "domain": "www.bmwblog.com", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Number of articles (1-50)" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", "title", - "date", - "author", - "section", - "excerpt", - "url" + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bmwblog/latest.js", - "sourceFile": "plugins/bmwblog/latest.js" + "modulePath": "plugins/bloomberg/pursuits.js", + "sourceFile": "plugins/bloomberg/pursuits.js" }, { - "site": "bmwblog", - "name": "search", - "description": "Search BMWBLOG articles", + "site": "bloomberg", + "name": "tech", + "description": "Bloomberg Tech top stories (RSS)", "access": "read", - "domain": "www.bmwblog.com", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Number of results (1-50)" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", "title", - "date", - "author", - "section", - "excerpt", - "url" - ], - "tags": [ - "search" + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bmwblog/search.js", - "sourceFile": "plugins/bmwblog/search.js" + "modulePath": "plugins/bloomberg/tech.js", + "sourceFile": "plugins/bloomberg/tech.js" }, { - "site": "brave", - "name": "search", - "description": "Search Brave Search", + "site": "bluesky", + "name": "feeds", + "description": "Popular Bluesky feed generators", "access": "read", - "domain": "search.brave.com", + "domain": "public.api.bsky.app", "strategy": "public", - "browser": true, + "browser": false, "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, { "name": "limit", "type": "int", - "default": 10, - "required": false, - "help": "Number of results per page (max 18)" - }, - { - "name": "offset", - "type": "int", - "default": 0, + "default": 20, "required": false, - "help": "Page offset (0, 1, 2...). Brave returns ~18 results per page" + "help": "Number of feeds" } ], "columns": [ "rank", - "title", - "url", - "snippet" - ], - "tags": [ - "search" + "name", + "likes", + "creator", + "description" ], "type": "js", - "modulePath": "plugins/brave/search.js", - "sourceFile": "plugins/brave/search.js" + "modulePath": "plugins/bluesky/feeds.js", + "sourceFile": "plugins/bluesky/feeds.js" }, { - "site": "chatwise", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "site": "bluesky", + "name": "followers", + "description": "List followers of a Bluesky user", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, "args": [ { - "name": "text", + "name": "handle", "type": "str", "required": true, "positional": true, - "help": "Prompt to send" + "help": "Bluesky handle" }, { - "name": "timeout", + "name": "limit", "type": "int", - "default": 30, + "default": 20, "required": false, - "help": "Max seconds to wait (default: 30)" + "help": "Number of followers" } ], "columns": [ - "Role", - "Text" + "rank", + "handle", + "name", + "description" ], "type": "js", - "modulePath": "plugins/chatwise/ask.js", - "sourceFile": "plugins/chatwise/ask.js", - "navigateBefore": true + "modulePath": "plugins/bluesky/followers.js", + "sourceFile": "plugins/bluesky/followers.js" }, { - "site": "chatwise", - "name": "export", - "description": "Export the current ChatWise conversation to a Markdown file", + "site": "bluesky", + "name": "following", + "description": "List accounts a Bluesky user is following", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, "args": [ { - "name": "output", + "name": "handle", "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle" + }, + { + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Output file (default: /tmp/chatwise-export.md)" + "help": "Number of accounts" } ], "columns": [ - "Status", - "File", - "Messages" + "rank", + "handle", + "name", + "description" ], "type": "js", - "modulePath": "plugins/chatwise/export.js", - "sourceFile": "plugins/chatwise/export.js", - "navigateBefore": true + "modulePath": "plugins/bluesky/following.js", + "sourceFile": "plugins/bluesky/following.js" }, { - "site": "chatwise", - "name": "history", - "description": "List conversation history in ChatWise sidebar", + "site": "bluesky", + "name": "profile", + "description": "Get Bluesky user profile info", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle (e.g. bsky.app, jay.bsky.team)" + } + ], + "columns": [ + "handle", + "name", + "followers", + "following", + "posts", + "description" + ], + "type": "js", + "modulePath": "plugins/bluesky/profile.js", + "sourceFile": "plugins/bluesky/profile.js" + }, + { + "site": "bluesky", + "name": "search", + "description": "Search Bluesky users", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "handle", + "name", + "followers", + "description" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/bluesky/search.js", + "sourceFile": "plugins/bluesky/search.js" + }, + { + "site": "bluesky", + "name": "starter-packs", + "description": "Get starter packs created by a Bluesky user", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of starter packs" + } + ], + "columns": [ + "rank", + "name", + "description", + "members", + "joins" + ], + "type": "js", + "modulePath": "plugins/bluesky/starter-packs.js", + "sourceFile": "plugins/bluesky/starter-packs.js" + }, + { + "site": "bluesky", + "name": "thread", + "description": "Get a Bluesky post thread with replies", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "uri", + "type": "str", + "required": true, + "positional": true, + "help": "Post AT URI (at://did:.../app.bsky.feed.post/...) or bsky.app URL" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of replies" + } + ], + "columns": [ + "author", + "text", + "likes", + "reposts", + "replies_count" + ], + "type": "js", + "modulePath": "plugins/bluesky/thread.js", + "sourceFile": "plugins/bluesky/thread.js" + }, + { + "site": "bluesky", + "name": "trending", + "description": "Trending topics on Bluesky", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of topics" + } + ], + "columns": [ + "rank", + "topic", + "link" + ], + "type": "js", + "modulePath": "plugins/bluesky/trending.js", + "sourceFile": "plugins/bluesky/trending.js" + }, + { + "site": "bluesky", + "name": "user", + "description": "Get recent posts from a Bluesky user", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle (e.g. bsky.app)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts" + } + ], + "columns": [ + "rank", + "uri", + "text", + "likes", + "reposts", + "replies" + ], + "type": "js", + "modulePath": "plugins/bluesky/user.js", + "sourceFile": "plugins/bluesky/user.js" + }, + { + "site": "bmwblog", + "name": "article", + "description": "Read a BMWBLOG article by URL or slug", + "access": "read", + "domain": "www.bmwblog.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "url-or-slug", + "type": "str", + "required": true, + "positional": true, + "help": "BMWBLOG article URL or slug" + } + ], + "columns": [ + "title", + "date", + "author", + "sections", + "excerpt", + "url", + "content" + ], + "type": "js", + "modulePath": "plugins/bmwblog/article.js", + "sourceFile": "plugins/bmwblog/article.js" + }, + { + "site": "bmwblog", + "name": "latest", + "description": "List the latest BMWBLOG articles", + "access": "read", + "domain": "www.bmwblog.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of articles (1-50)" + } + ], + "columns": [ + "rank", + "title", + "date", + "author", + "section", + "excerpt", + "url" + ], + "type": "js", + "modulePath": "plugins/bmwblog/latest.js", + "sourceFile": "plugins/bmwblog/latest.js" + }, + { + "site": "bmwblog", + "name": "search", + "description": "Search BMWBLOG articles", + "access": "read", + "domain": "www.bmwblog.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results (1-50)" + } + ], + "columns": [ + "rank", + "title", + "date", + "author", + "section", + "excerpt", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/bmwblog/search.js", + "sourceFile": "plugins/bmwblog/search.js" + }, + { + "site": "booking", + "name": "search", + "description": "Search Booking.com hotels by destination and dates (server-rendered card scrape).", + "access": "read", + "example": "webcmd booking search Tokyo --checkin 2026-06-15 --checkout 2026-06-17 -f yaml", + "domain": "www.booking.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "destination", + "type": "str", + "required": true, + "positional": true, + "help": "Destination keyword (city, district, or hotel name)" + }, + { + "name": "checkin", + "type": "str", + "required": true, + "help": "Check-in date YYYY-MM-DD" + }, + { + "name": "checkout", + "type": "str", + "required": true, + "help": "Check-out date YYYY-MM-DD" + }, + { + "name": "adults", + "type": "int", + "default": 2, + "required": false, + "help": "Number of adults (1-30)" + }, + { + "name": "rooms", + "type": "int", + "default": 1, + "required": false, + "help": "Number of rooms (1-30)" + }, + { + "name": "children", + "type": "int", + "default": 0, + "required": false, + "help": "Number of children (0-10)" + }, + { + "name": "currency", + "type": "str", + "required": false, + "help": "Force result currency (e.g. USD, JPY, CNY)" + }, + { + "name": "lang", + "type": "str", + "required": false, + "help": "Force result language (e.g. en-us, zh-cn, ja)" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max rows to return (1-100; Booking pages 25 per request)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Result offset for pagination (multiple of 25)" + } + ], + "columns": [ + "rank", + "name", + "country", + "slug", + "star_rating", + "review_score", + "review_count", + "price_amount", + "price_currency", + "distance", + "recommended_room", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/booking/search.js", + "sourceFile": "plugins/booking/search.js" + }, + { + "site": "brave", + "name": "search", + "description": "Search Brave Search", + "access": "read", + "domain": "search.brave.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results per page (max 18)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Page offset (0, 1, 2...). Brave returns ~18 results per page" + } + ], + "columns": [ + "rank", + "title", + "url", + "snippet" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/brave/search.js", + "sourceFile": "plugins/brave/search.js" + }, + { + "site": "chatwise", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds to wait (default: 30)" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/chatwise/ask.js", + "sourceFile": "plugins/chatwise/ask.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "export", + "description": "Export the current ChatWise conversation to a Markdown file", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file (default: /tmp/chatwise-export.md)" + } + ], + "columns": [ + "Status", + "File", + "Messages" + ], + "type": "js", + "modulePath": "plugins/chatwise/export.js", + "sourceFile": "plugins/chatwise/export.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "history", + "description": "List conversation history in ChatWise sidebar", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", "Title" ], "type": "js", @@ -1412,58 +2040,195 @@ } ], "columns": [ - "Status", - "File" + "Status", + "File" + ], + "type": "js", + "modulePath": "plugins/chatwise/screenshot.js", + "sourceFile": "plugins/chatwise/screenshot.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "send", + "description": "Send a message to the active ChatWise conversation", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Message to send" + } + ], + "columns": [ + "Status", + "InjectedText" + ], + "type": "js", + "modulePath": "plugins/chatwise/send.js", + "sourceFile": "plugins/chatwise/send.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "status", + "description": "Check active CDP connection to ChatWise Desktop", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "plugins/chatwise/status.js", + "sourceFile": "plugins/chatwise/status.js", + "navigateBefore": true + }, + { + "site": "chess", + "name": "analyze", + "description": "Open a Chess.com game in the browser analysis board", + "access": "read", + "domain": "www.chess.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "game-url", + "type": "string", + "required": true, + "positional": true, + "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" + } + ], + "columns": [ + "kind", + "game_id", + "analysis_url" + ], + "type": "js", + "modulePath": "plugins/chess/analyze.js", + "sourceFile": "plugins/chess/analyze.js", + "navigateBefore": false + }, + { + "site": "chess", + "name": "game", + "description": "Chess.com single-game detail (white, black, result, ECO, time control) by full game URL", + "access": "read", + "domain": "www.chess.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "game-url", + "type": "string", + "required": true, + "positional": true, + "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" + } + ], + "columns": [ + "kind", + "game_id", + "date", + "white", + "white_rating", + "black", + "black_rating", + "result", + "winner_color", + "termination", + "eco", + "time_control", + "rated", + "ply_count", + "url" ], "type": "js", - "modulePath": "plugins/chatwise/screenshot.js", - "sourceFile": "plugins/chatwise/screenshot.js", - "navigateBefore": true + "modulePath": "plugins/chess/game.js", + "sourceFile": "plugins/chess/game.js" }, { - "site": "chatwise", - "name": "send", - "description": "Send a message to the active ChatWise conversation", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "site": "chess", + "name": "games", + "description": "Chess.com recent games for a player, newest first", + "access": "read", + "domain": "api.chess.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "text", - "type": "str", + "name": "username", + "type": "string", "required": true, "positional": true, - "help": "Message to send" + "help": "Chess.com username" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of recent games (1-100)" } ], "columns": [ - "Status", - "InjectedText" + "date", + "time_class", + "rated", + "my_color", + "my_rating", + "my_result", + "opponent", + "opponent_rating", + "accuracy_white", + "accuracy_black", + "eco", + "opening_name", + "url" ], "type": "js", - "modulePath": "plugins/chatwise/send.js", - "sourceFile": "plugins/chatwise/send.js", - "navigateBefore": true + "modulePath": "plugins/chess/games.js", + "sourceFile": "plugins/chess/games.js" }, { - "site": "chatwise", - "name": "status", - "description": "Check active CDP connection to ChatWise Desktop", + "site": "chess", + "name": "stats", + "description": "Chess.com player ratings + win/loss record across game kinds", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], + "domain": "api.chess.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Chess.com username (case-insensitive)" + } + ], "columns": [ - "Status", - "Url", - "Title" + "kind", + "rating_current", + "rating_best", + "wins", + "losses", + "draws" ], "type": "js", - "modulePath": "plugins/chatwise/status.js", - "sourceFile": "plugins/chatwise/status.js", - "navigateBefore": true + "modulePath": "plugins/chess/stats.js", + "sourceFile": "plugins/chess/stats.js" }, { "site": "cincinnati", @@ -4022,105 +4787,329 @@ { "name": "limit", "type": "int", - "default": 10, + "default": 10, + "required": false, + "help": "Number of results to return (max 20)" + } + ], + "columns": [ + "rank", + "title", + "authors", + "source", + "year", + "cited", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/google-scholar/search.js", + "sourceFile": "plugins/google-scholar/search.js" + }, + { + "site": "goproxy", + "name": "module", + "description": "Latest version + VCS origin metadata for a Go module on proxy.golang.org", + "access": "read", + "domain": "proxy.golang.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "module", + "type": "string", + "required": true, + "positional": true, + "help": "Go module path (e.g. \"github.com/gin-gonic/gin\", \"golang.org/x/net\")" + } + ], + "columns": [ + "module", + "version", + "publishedAt", + "vcs", + "repository", + "commit", + "ref", + "pkgGoDevUrl", + "url" + ], + "type": "js", + "modulePath": "plugins/goproxy/module.js", + "sourceFile": "plugins/goproxy/module.js" + }, + { + "site": "goproxy", + "name": "versions", + "description": "Published version tags for a Go module (newest first), optionally with publish times", + "access": "read", + "domain": "proxy.golang.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "module", + "type": "string", + "required": true, + "positional": true, + "help": "Go module path (e.g. \"github.com/gin-gonic/gin\")" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max rows to return (1-200)" + }, + { + "name": "with-time", + "type": "boolean", + "default": false, + "required": false, + "help": "Fetch each version's publish time (one extra request per row)" + } + ], + "columns": [ + "rank", + "module", + "version", + "publishedAt", + "url" + ], + "type": "js", + "modulePath": "plugins/goproxy/versions.js", + "sourceFile": "plugins/goproxy/versions.js" + }, + { + "site": "hackernews", + "name": "ask", + "description": "Hacker News Ask HN posts", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/hackernews/ask.js", + "sourceFile": "plugins/hackernews/ask.js" + }, + { + "site": "hackernews", + "name": "best", + "description": "Hacker News best stories", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/hackernews/best.js", + "sourceFile": "plugins/hackernews/best.js" + }, + { + "site": "hackernews", + "name": "jobs", + "description": "Hacker News job postings", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of job postings" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "url" + ], + "type": "js", + "modulePath": "plugins/hackernews/jobs.js", + "sourceFile": "plugins/hackernews/jobs.js" + }, + { + "site": "hackernews", + "name": "new", + "description": "Hacker News newest stories", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Number of results to return (max 20)" + "help": "Number of stories" } ], "columns": [ "rank", + "id", "title", - "authors", - "source", - "year", - "cited", + "score", + "author", + "comments", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/google-scholar/search.js", - "sourceFile": "plugins/google-scholar/search.js" + "modulePath": "plugins/hackernews/new.js", + "sourceFile": "plugins/hackernews/new.js" }, { - "site": "goproxy", - "name": "module", - "description": "Latest version + VCS origin metadata for a Go module on proxy.golang.org", + "site": "hackernews", + "name": "read", + "description": "Read a Hacker News story and its comment tree", "access": "read", - "domain": "proxy.golang.org", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ { - "name": "module", - "type": "string", + "name": "id", + "type": "str", "required": true, "positional": true, - "help": "Go module path (e.g. \"github.com/gin-gonic/gin\", \"golang.org/x/net\")" + "help": "HN item ID (e.g. 39847301)" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max top-level comments" + }, + { + "name": "depth", + "type": "int", + "default": 2, + "required": false, + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + }, + { + "name": "replies", + "type": "int", + "default": 5, + "required": false, + "help": "Max replies shown per comment at each level" + }, + { + "name": "max-length", + "type": "int", + "default": 2000, + "required": false, + "help": "Max characters per comment body (min 100)" } ], "columns": [ - "module", - "version", - "publishedAt", - "vcs", - "repository", - "commit", - "ref", - "pkgGoDevUrl", - "url" + "type", + "author", + "score", + "text" ], "type": "js", - "modulePath": "plugins/goproxy/module.js", - "sourceFile": "plugins/goproxy/module.js" + "modulePath": "plugins/hackernews/read.js", + "sourceFile": "plugins/hackernews/read.js" }, { - "site": "goproxy", - "name": "versions", - "description": "Published version tags for a Go module (newest first), optionally with publish times", + "site": "hackernews", + "name": "search", + "description": "Search Hacker News stories", "access": "read", - "domain": "proxy.golang.org", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ { - "name": "module", - "type": "string", + "name": "query", + "type": "str", "required": true, "positional": true, - "help": "Go module path (e.g. \"github.com/gin-gonic/gin\")" + "help": "Search query" }, { "name": "limit", "type": "int", - "default": 30, + "default": 20, "required": false, - "help": "Max rows to return (1-200)" + "help": "Number of results" }, { - "name": "with-time", - "type": "boolean", - "default": false, + "name": "sort", + "type": "str", + "default": "relevance", "required": false, - "help": "Fetch each version's publish time (one extra request per row)" + "help": "Sort by relevance or date", + "choices": [ + "relevance", + "date" + ] } ], "columns": [ "rank", - "module", - "version", - "publishedAt", + "id", + "title", + "score", + "author", + "comments", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/goproxy/versions.js", - "sourceFile": "plugins/goproxy/versions.js" + "modulePath": "plugins/hackernews/search.js", + "sourceFile": "plugins/hackernews/search.js" }, { "site": "hackernews", - "name": "ask", - "description": "Hacker News Ask HN posts", + "name": "show", + "description": "Hacker News Show HN posts", "access": "read", "domain": "news.ycombinator.com", "strategy": "public", @@ -4144,13 +5133,13 @@ "url" ], "type": "js", - "modulePath": "plugins/hackernews/ask.js", - "sourceFile": "plugins/hackernews/ask.js" + "modulePath": "plugins/hackernews/show.js", + "sourceFile": "plugins/hackernews/show.js" }, { "site": "hackernews", - "name": "best", - "description": "Hacker News best stories", + "name": "top", + "description": "Hacker News top stories", "access": "read", "domain": "news.ycombinator.com", "strategy": "public", @@ -4174,267 +5163,318 @@ "url" ], "type": "js", - "modulePath": "plugins/hackernews/best.js", - "sourceFile": "plugins/hackernews/best.js" + "modulePath": "plugins/hackernews/top.js", + "sourceFile": "plugins/hackernews/top.js" }, { "site": "hackernews", - "name": "jobs", - "description": "Hacker News job postings", + "name": "user", + "description": "Hacker News user profile", "access": "read", "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of job postings" + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "HN username" } ], "columns": [ - "rank", - "id", - "title", - "author", - "url" + "username", + "karma", + "created", + "about" ], "type": "js", - "modulePath": "plugins/hackernews/jobs.js", - "sourceFile": "plugins/hackernews/jobs.js" + "modulePath": "plugins/hackernews/user.js", + "sourceFile": "plugins/hackernews/user.js" }, { - "site": "hackernews", - "name": "new", - "description": "Hacker News newest stories", + "site": "heidelberg", + "name": "export-postgraduate-courses", + "description": "Export Heidelberg University non-bachelor/postgraduate programs using the official study finder.", "access": "read", - "domain": "news.ycombinator.com", + "example": "webcmd heidelberg export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.uni-heidelberg.de", "strategy": "public", "browser": false, "args": [ { - "name": "limit", + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", "type": "int", - "default": 20, "required": false, - "help": "Number of stories" + "help": "Positive maximum number of programs after filtering and deduplication" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/hackernews/new.js", - "sourceFile": "plugins/hackernews/new.js" + "modulePath": "plugins/heidelberg/export-postgraduate-courses.js", + "sourceFile": "plugins/heidelberg/export-postgraduate-courses.js" }, - { - "site": "hackernews", - "name": "read", - "description": "Read a Hacker News story and its comment tree", + { + "site": "hft", + "name": "export-postgraduate-courses", + "description": "Export HFT Stuttgart postgraduate programs using the official public programme pages.", "access": "read", - "domain": "news.ycombinator.com", + "example": "webcmd hft export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.hft-stuttgart.de", "strategy": "public", "browser": false, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "HN item ID (e.g. 39847301)" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max top-level comments" - }, - { - "name": "depth", - "type": "int", - "default": 2, - "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" - }, - { - "name": "replies", - "type": "int", - "default": 5, + "name": "degree-level", + "type": "string", + "default": "all", "required": false, - "help": "Max replies shown per comment at each level" + "help": "all, masters, certificate, diploma, professional, or doctorate" }, { - "name": "max-length", + "name": "count", "type": "int", - "default": 2000, "required": false, - "help": "Max characters per comment body (min 100)" + "help": "Positive maximum number of programs after filtering and deduplication" } ], "columns": [ - "type", - "author", - "score", - "text" + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/hackernews/read.js", - "sourceFile": "plugins/hackernews/read.js" + "modulePath": "plugins/hft/export-postgraduate-courses.js", + "sourceFile": "plugins/hft/export-postgraduate-courses.js" }, { - "site": "hackernews", - "name": "search", - "description": "Search Hacker News stories", + "site": "homebrew", + "name": "cask", + "description": "Fetch a Homebrew cask's metadata (version, homepage, deprecation, download URL)", "access": "read", - "domain": "news.ycombinator.com", + "domain": "formulae.brew.sh", "strategy": "public", "browser": false, "args": [ { - "name": "query", + "name": "token", "type": "str", "required": true, "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - }, - { - "name": "sort", - "type": "str", - "default": "relevance", - "required": false, - "help": "Sort by relevance or date", - "choices": [ - "relevance", - "date" - ] + "help": "Cask token (e.g. \"firefox\", \"visual-studio-code\", \"google-chrome\")" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", + "cask", + "tap", + "name", + "version", + "description", + "homepage", + "deprecated", + "disabled", + "download", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/hackernews/search.js", - "sourceFile": "plugins/hackernews/search.js" + "modulePath": "plugins/homebrew/cask.js", + "sourceFile": "plugins/homebrew/cask.js" }, { - "site": "hackernews", - "name": "show", - "description": "Hacker News Show HN posts", + "site": "homebrew", + "name": "formula", + "description": "Fetch a Homebrew formula's metadata (version, license, deps, deprecation, source)", "access": "read", - "domain": "news.ycombinator.com", + "domain": "formulae.brew.sh", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Formula name (e.g. \"wget\", \"gcc@13\", \"imagemagick\")" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", + "formula", + "tap", + "version", + "license", + "description", + "homepage", + "dependencies", + "deprecated", + "disabled", + "source", "url" ], "type": "js", - "modulePath": "plugins/hackernews/show.js", - "sourceFile": "plugins/hackernews/show.js" + "modulePath": "plugins/homebrew/formula.js", + "sourceFile": "plugins/homebrew/formula.js" }, { - "site": "hackernews", - "name": "top", - "description": "Hacker News top stories", + "site": "homebrew", + "name": "popular", + "description": "List most-installed Homebrew formulae or casks (Homebrew's analytics ranking)", "access": "read", - "domain": "news.ycombinator.com", + "domain": "formulae.brew.sh", "strategy": "public", "browser": false, "args": [ + { + "name": "type", + "type": "str", + "default": "formula", + "required": false, + "help": "Package type (formula / cask)" + }, + { + "name": "window", + "type": "str", + "default": "30d", + "required": false, + "help": "Time window (30d / 90d / 365d)" + }, { "name": "limit", "type": "int", - "default": 20, + "default": 30, "required": false, - "help": "Number of stories" + "help": "Max rows (1-500)" } ], "columns": [ "rank", - "id", - "title", - "score", - "author", - "comments", + "token", + "type", + "installs", + "percent", + "window", "url" ], "type": "js", - "modulePath": "plugins/hackernews/top.js", - "sourceFile": "plugins/hackernews/top.js" - }, - { - "site": "hackernews", - "name": "user", - "description": "Hacker News user profile", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "HN username" - } - ], - "columns": [ - "username", - "karma", - "created", - "about" - ], - "type": "js", - "modulePath": "plugins/hackernews/user.js", - "sourceFile": "plugins/hackernews/user.js" + "modulePath": "plugins/homebrew/popular.js", + "sourceFile": "plugins/homebrew/popular.js" }, { - "site": "heidelberg", + "site": "iit", "name": "export-postgraduate-courses", - "description": "Export Heidelberg University non-bachelor/postgraduate programs using the official study finder.", + "description": "Export Illinois Tech postgraduate programs using official public sources.", "access": "read", - "example": "webcmd heidelberg export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.uni-heidelberg.de", + "example": "webcmd iit export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.iit.edu", "strategy": "public", "browser": false, "args": [ @@ -4507,283 +5547,303 @@ "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/heidelberg/export-postgraduate-courses.js", - "sourceFile": "plugins/heidelberg/export-postgraduate-courses.js" + "modulePath": "plugins/iit/export-postgraduate-courses.js", + "sourceFile": "plugins/iit/export-postgraduate-courses.js" }, { - "site": "hft", - "name": "export-postgraduate-courses", - "description": "Export HFT Stuttgart postgraduate programs using the official public programme pages.", + "site": "imdb", + "name": "person", + "description": "Get actor or director info", "access": "read", - "example": "webcmd hft export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.hft-stuttgart.de", + "domain": "www.imdb.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "IMDb person ID (nm0634240) or URL" }, { - "name": "count", + "name": "limit", "type": "int", + "default": 10, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Max filmography entries" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "field", + "value" ], "type": "js", - "modulePath": "plugins/hft/export-postgraduate-courses.js", - "sourceFile": "plugins/hft/export-postgraduate-courses.js" + "modulePath": "plugins/imdb/person.js", + "sourceFile": "plugins/imdb/person.js" }, { - "site": "homebrew", - "name": "cask", - "description": "Fetch a Homebrew cask's metadata (version, homepage, deprecation, download URL)", + "site": "imdb", + "name": "reviews", + "description": "Get user reviews for a movie or TV show", "access": "read", - "domain": "formulae.brew.sh", + "domain": "www.imdb.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "token", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Cask token (e.g. \"firefox\", \"visual-studio-code\", \"google-chrome\")" + "help": "IMDb title ID (tt1375666) or URL" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of reviews" } ], "columns": [ - "cask", - "tap", - "name", - "version", - "description", - "homepage", - "deprecated", - "disabled", - "download", - "url" + "rank", + "title", + "rating", + "author", + "date", + "text" ], "type": "js", - "modulePath": "plugins/homebrew/cask.js", - "sourceFile": "plugins/homebrew/cask.js" + "modulePath": "plugins/imdb/reviews.js", + "sourceFile": "plugins/imdb/reviews.js" }, { - "site": "homebrew", - "name": "formula", - "description": "Fetch a Homebrew formula's metadata (version, license, deps, deprecation, source)", + "site": "imdb", + "name": "search", + "description": "Search IMDb for movies, TV shows, and people", "access": "read", - "domain": "formulae.brew.sh", + "domain": "www.imdb.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "name", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Formula name (e.g. \"wget\", \"gcc@13\", \"imagemagick\")" + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" } ], "columns": [ - "formula", - "tap", - "version", - "license", - "description", - "homepage", - "dependencies", - "deprecated", - "disabled", - "source", + "rank", + "id", + "title", + "year", + "type", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/homebrew/formula.js", - "sourceFile": "plugins/homebrew/formula.js" + "modulePath": "plugins/imdb/search.js", + "sourceFile": "plugins/imdb/search.js" }, { - "site": "homebrew", - "name": "popular", - "description": "List most-installed Homebrew formulae or casks (Homebrew's analytics ranking)", + "site": "imdb", + "name": "title", + "description": "Get movie or TV show details", "access": "read", - "domain": "formulae.brew.sh", + "domain": "www.imdb.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "type", + "name": "id", "type": "str", - "default": "formula", - "required": false, - "help": "Package type (formula / cask)" - }, + "required": true, + "positional": true, + "help": "IMDb title ID (tt1375666) or URL" + } + ], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "plugins/imdb/title.js", + "sourceFile": "plugins/imdb/title.js" + }, + { + "site": "imdb", + "name": "top", + "description": "IMDb Top 250 Movies", + "access": "read", + "domain": "www.imdb.com", + "strategy": "public", + "browser": true, + "args": [ { - "name": "window", - "type": "str", - "default": "30d", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Time window (30d / 90d / 365d)" - }, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "rating", + "votes", + "genre", + "url" + ], + "type": "js", + "modulePath": "plugins/imdb/top.js", + "sourceFile": "plugins/imdb/top.js" + }, + { + "site": "imdb", + "name": "trending", + "description": "IMDb Most Popular Movies", + "access": "read", + "domain": "www.imdb.com", + "strategy": "public", + "browser": true, + "args": [ { "name": "limit", "type": "int", - "default": 30, + "default": 20, "required": false, - "help": "Max rows (1-500)" + "help": "Number of results" } ], "columns": [ "rank", - "token", - "type", - "installs", - "percent", - "window", + "title", + "rating", + "genre", + "url" + ], + "type": "js", + "modulePath": "plugins/imdb/trending.js", + "sourceFile": "plugins/imdb/trending.js" + }, + { + "site": "indeed", + "name": "job", + "aliases": [ + "detail", + "view" + ], + "description": "Read the full Indeed job posting by jk (job key)", + "access": "read", + "domain": "www.indeed.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Job key (16-char hex from `indeed search`, e.g. \"dccc07ac5a6a3683\")" + } + ], + "columns": [ + "id", + "title", + "company", + "location", + "salary", + "job_type", + "description", "url" ], "type": "js", - "modulePath": "plugins/homebrew/popular.js", - "sourceFile": "plugins/homebrew/popular.js" + "modulePath": "plugins/indeed/job.js", + "sourceFile": "plugins/indeed/job.js", + "navigateBefore": false }, { - "site": "iit", - "name": "export-postgraduate-courses", - "description": "Export Illinois Tech postgraduate programs using official public sources.", + "site": "indeed", + "name": "search", + "description": "Indeed keyword job search (rendered DOM via browser session, US site)", "access": "read", - "example": "webcmd iit export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.iit.edu", - "strategy": "public", - "browser": false, + "domain": "www.indeed.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "degree-level", + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Job keyword (title / skill / company)" + }, + { + "name": "location", "type": "string", - "default": "all", + "default": "", "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "help": "Location filter (e.g. \"remote\", \"New York, NY\", \"San Francisco\")" }, { - "name": "count", + "name": "fromage", + "type": "string", + "default": "", + "required": false, + "help": "Recency filter, days back: 1 / 3 / 7 / 14" + }, + { + "name": "sort", + "type": "string", + "default": "relevance", + "required": false, + "help": "Sort order: relevance | date" + }, + { + "name": "start", "type": "int", + "default": 0, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Pagination offset (multiple of 10, 0-based)" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Max rows to return (1-25, capped at one page)" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "rank", + "id", + "title", + "company", + "location", + "salary", + "tags", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/iit/export-postgraduate-courses.js", - "sourceFile": "plugins/iit/export-postgraduate-courses.js" + "modulePath": "plugins/indeed/search.js", + "sourceFile": "plugins/indeed/search.js", + "navigateBefore": false }, { "site": "jhu", @@ -7289,89 +8349,241 @@ "name": "search", "description": "Search Maven Central by keyword (artifact name, groupId, tag)", "access": "read", - "domain": "search.maven.org", + "domain": "search.maven.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"jackson\", \"guava\", \"ai.koog\")" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max artifacts (1-200)" + } + ], + "columns": [ + "rank", + "coordinate", + "groupId", + "artifactId", + "latestVersion", + "packaging", + "versions", + "lastPublished", + "repository", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/maven/search.js", + "sourceFile": "plugins/maven/search.js" + }, + { + "site": "mdn", + "name": "search", + "description": "Search MDN Web Docs by keyword", + "access": "read", + "domain": "developer.mozilla.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"fetch\", \"flexbox\", \"Array.prototype.map\")" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results (1-50)" + }, + { + "name": "locale", + "type": "str", + "default": "en-US", + "required": false, + "help": "Doc locale (en-US default; de / es / fr / ja / ko / pt-BR / ru / zh-CN / zh-TW)" + } + ], + "columns": [ + "rank", + "title", + "slug", + "locale", + "summary", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/mdn/search.js", + "sourceFile": "plugins/mdn/search.js" + }, + { + "site": "medium", + "name": "feed", + "description": "Medium popular posts Feed", + "access": "read", + "domain": "medium.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "topic", + "type": "str", + "default": "", + "required": false, + "help": "Topic (for example technology, programming, ai)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" + } + ], + "columns": [ + "rank", + "title", + "author", + "date", + "readTime", + "claps" + ], + "type": "js", + "modulePath": "plugins/medium/feed.js", + "sourceFile": "plugins/medium/feed.js", + "navigateBefore": "https://medium.com" + }, + { + "site": "medium", + "name": "search", + "description": "Search Medium posts", + "access": "read", + "domain": "medium.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" + } + ], + "columns": [ + "rank", + "title", + "author", + "date", + "readTime", + "claps", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/medium/search.js", + "sourceFile": "plugins/medium/search.js", + "navigateBefore": "https://medium.com" + }, + { + "site": "medium", + "name": "tag", + "description": "Latest Medium articles tagged with a given keyword (RSS feed)", + "access": "read", + "domain": "medium.com", "strategy": "public", "browser": false, "args": [ { - "name": "query", + "name": "tag", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"jackson\", \"guava\", \"ai.koog\")" + "help": "Lowercase tag slug (e.g. \"programming\", \"machine-learning\")" }, { "name": "limit", "type": "int", - "default": 30, + "default": 20, "required": false, - "help": "Max artifacts (1-200)" + "help": "Max articles (1-25 — single RSS page)" } ], "columns": [ "rank", - "coordinate", - "groupId", - "artifactId", - "latestVersion", - "packaging", - "versions", - "lastPublished", - "repository", + "title", + "author", + "description", + "categories", + "published", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/maven/search.js", - "sourceFile": "plugins/maven/search.js" + "modulePath": "plugins/medium/tag.js", + "sourceFile": "plugins/medium/tag.js" }, { - "site": "mdn", - "name": "search", - "description": "Search MDN Web Docs by keyword", + "site": "medium", + "name": "user", + "description": "Get Medium user posts", "access": "read", - "domain": "developer.mozilla.org", - "strategy": "public", - "browser": false, + "domain": "medium.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"fetch\", \"flexbox\", \"Array.prototype.map\")" + "help": "Medium username(for example @username or username)" }, { "name": "limit", "type": "int", - "default": 10, - "required": false, - "help": "Max results (1-50)" - }, - { - "name": "locale", - "type": "str", - "default": "en-US", + "default": 20, "required": false, - "help": "Doc locale (en-US default; de / es / fr / ja / ko / pt-BR / ru / zh-CN / zh-TW)" + "help": "Number of posts to return" } ], "columns": [ "rank", "title", - "slug", - "locale", - "summary", + "date", + "readTime", + "claps", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/mdn/search.js", - "sourceFile": "plugins/mdn/search.js" + "modulePath": "plugins/medium/user.js", + "sourceFile": "plugins/medium/user.js", + "navigateBefore": "https://medium.com" }, { "site": "npm", @@ -8221,6 +9433,137 @@ "modulePath": "plugins/packagist/search.js", "sourceFile": "plugins/packagist/search.js" }, + { + "site": "producthunt", + "name": "browse", + "description": "Best products in a Product Hunt category", + "access": "read", + "domain": "www.producthunt.com", + "strategy": "intercept", + "browser": true, + "args": [ + { + "name": "category", + "type": "string", + "required": true, + "positional": true, + "help": "Category slug, e.g. vibe-coding, ai-agents, developer-tools" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results (max 50)" + } + ], + "columns": [ + "rank", + "name", + "tagline", + "reviews", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/producthunt/browse.js", + "sourceFile": "plugins/producthunt/browse.js", + "navigateBefore": true + }, + { + "site": "producthunt", + "name": "hot", + "description": "Today's top Product Hunt launches with vote counts", + "access": "read", + "domain": "www.producthunt.com", + "strategy": "intercept", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results (max 50)" + } + ], + "columns": [ + "rank", + "name", + "votes", + "url" + ], + "type": "js", + "modulePath": "plugins/producthunt/hot.js", + "sourceFile": "plugins/producthunt/hot.js", + "navigateBefore": true + }, + { + "site": "producthunt", + "name": "posts", + "description": "Latest Product Hunt launches (optional category filter)", + "access": "read", + "domain": "www.producthunt.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results (max 50)" + }, + { + "name": "category", + "type": "string", + "default": "", + "required": false, + "help": "Category filter: ai-agents, ai-coding-agents, ai-code-editors, ai-chatbots, ai-workflow-automation, vibe-coding, developer-tools, productivity, design-creative, marketing-sales, no-code-platforms, llms, finance, social-community, engineering-development" + } + ], + "columns": [ + "rank", + "name", + "tagline", + "author", + "date", + "url" + ], + "type": "js", + "modulePath": "plugins/producthunt/posts.js", + "sourceFile": "plugins/producthunt/posts.js" + }, + { + "site": "producthunt", + "name": "today", + "description": "Today's Product Hunt launches (most recent day in feed)", + "access": "read", + "domain": "www.producthunt.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results" + } + ], + "columns": [ + "rank", + "name", + "tagline", + "author", + "url" + ], + "type": "js", + "modulePath": "plugins/producthunt/today.js", + "sourceFile": "plugins/producthunt/today.js" + }, { "site": "pubmed", "name": "article", @@ -9738,80 +11081,203 @@ "sourceFile": "plugins/steam/app.js" }, { - "site": "steam", - "name": "search", - "description": "Search the Steam storefront by name keyword", + "site": "steam", + "name": "search", + "description": "Search the Steam storefront by name keyword", + "access": "read", + "domain": "store.steampowered.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"portal\", \"stardew\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-50)" + }, + { + "name": "currency", + "type": "str", + "default": "us", + "required": false, + "help": "Storefront country code (e.g. us / cn / jp / de)" + } + ], + "columns": [ + "rank", + "id", + "name", + "price", + "currency", + "metascore", + "platforms", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/steam/search.js", + "sourceFile": "plugins/steam/search.js" + }, + { + "site": "steam", + "name": "top-sellers", + "description": "Steam top selling games", + "access": "read", + "domain": "store.steampowered.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of games" + } + ], + "columns": [ + "rank", + "name", + "price", + "discount", + "url" + ], + "type": "js", + "modulePath": "plugins/steam/top-sellers.js", + "sourceFile": "plugins/steam/top-sellers.js" + }, + { + "site": "substack", + "name": "feed", + "description": "Substack popular posts Feed", + "access": "read", + "domain": "substack.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "category", + "type": "str", + "default": "all", + "required": false, + "help": "Post category: all, tech, business, culture, politics, science, health" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" + } + ], + "columns": [ + "rank", + "title", + "author", + "date", + "readTime", + "url" + ], + "type": "js", + "modulePath": "plugins/substack/feed.js", + "sourceFile": "plugins/substack/feed.js", + "navigateBefore": "https://substack.com" + }, + { + "site": "substack", + "name": "publication", + "description": "Get a specific Substack Newsletter latest posts", "access": "read", - "domain": "store.steampowered.com", - "strategy": "public", - "browser": false, + "domain": "substack.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"portal\", \"stardew\")" + "help": "Newsletter URL(for example https://example.substack.com)" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max results (1-50)" - }, - { - "name": "currency", - "type": "str", - "default": "us", - "required": false, - "help": "Storefront country code (e.g. us / cn / jp / de)" + "help": "Number of posts to return" } ], "columns": [ "rank", - "id", - "name", - "price", - "currency", - "metascore", - "platforms", + "title", + "date", + "description", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/steam/search.js", - "sourceFile": "plugins/steam/search.js" + "modulePath": "plugins/substack/publication.js", + "sourceFile": "plugins/substack/publication.js", + "navigateBefore": "https://substack.com" }, { - "site": "steam", - "name": "top-sellers", - "description": "Steam top selling games", + "site": "substack", + "name": "search", + "description": "Search Substack posts and newsletters", "access": "read", - "domain": "store.steampowered.com", + "domain": "substack.com", "strategy": "public", "browser": false, "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "type", + "type": "str", + "default": "posts", + "required": false, + "help": "Search type(posts=posts, publications=Newsletter)", + "choices": [ + "posts", + "publications" + ] + }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of games" + "help": "Number of results to return" } ], "columns": [ "rank", - "name", - "price", - "discount", + "title", + "author", + "date", + "description", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/steam/top-sellers.js", - "sourceFile": "plugins/steam/top-sellers.js" + "modulePath": "plugins/substack/search.js", + "sourceFile": "plugins/substack/search.js" }, { "site": "techcrunch", @@ -10821,6 +12287,182 @@ "sourceFile": "plugins/ualberta/export-postgraduate-courses.js", "navigateBefore": false }, + { + "site": "uiverse", + "name": "code", + "description": "Export Uiverse component code (HTML, CSS, React, or Vue)", + "access": "read", + "domain": "uiverse.io", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": true, + "positional": true, + "help": "Uiverse URL or author/slug identifier" + }, + { + "name": "target", + "type": "str", + "required": true, + "help": "Code target to export", + "choices": [ + "html", + "css", + "react", + "vue" + ] + } + ], + "columns": [ + "target", + "username", + "slug", + "language", + "length" + ], + "type": "js", + "modulePath": "plugins/uiverse/code.js", + "sourceFile": "plugins/uiverse/code.js", + "navigateBefore": "https://uiverse.io" + }, + { + "site": "uiverse", + "name": "preview", + "description": "Capture a screenshot of the Uiverse preview element", + "access": "read", + "domain": "uiverse.io", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": true, + "positional": true, + "help": "Uiverse URL or author/slug identifier" + }, + { + "name": "output", + "type": "str", + "required": false, + "help": "Output image path (defaults to a temp file)" + }, + { + "name": "padding", + "type": "int", + "default": 8, + "required": false, + "help": "Extra padding around the captured preview in pixels" + } + ], + "columns": [ + "username", + "slug", + "width", + "height", + "output" + ], + "type": "js", + "modulePath": "plugins/uiverse/preview.js", + "sourceFile": "plugins/uiverse/preview.js", + "navigateBefore": "https://uiverse.io" + }, + { + "site": "web", + "name": "fetch-browser", + "description": "Fetch any web page and export as Markdown", + "access": "read", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "help": "Any web page URL" + }, + { + "name": "output", + "type": "str", + "default": "./web-articles", + "required": false, + "help": "Output directory" + }, + { + "name": "download-images", + "type": "boolean", + "default": true, + "required": false, + "help": "Download images locally" + }, + { + "name": "wait", + "type": "int", + "default": 3, + "required": false, + "help": "Seconds to wait after page load" + }, + { + "name": "wait-for", + "type": "str", + "required": false, + "valueRequired": true, + "help": "CSS selector to wait for in the main document or same-origin iframes" + }, + { + "name": "wait-until", + "type": "str", + "default": "domstable", + "required": false, + "help": "Readiness policy after navigation: domstable or networkidle", + "choices": [ + "domstable", + "networkidle" + ] + }, + { + "name": "frames", + "type": "str", + "default": "same-origin", + "required": false, + "help": "Iframe handling mode: relevant same-origin, all-same-origin, or none", + "choices": [ + "same-origin", + "all-same-origin", + "none" + ] + }, + { + "name": "diagnose", + "type": "boolean", + "default": false, + "required": false, + "help": "Print render diagnostics (frames, empty containers, XHR/API-like requests) to stderr" + }, + { + "name": "stdout", + "type": "boolean", + "default": false, + "required": false, + "help": "Print markdown to stdout instead of saving to a file" + } + ], + "columns": [ + "title", + "author", + "publish_time", + "status", + "size", + "saved" + ], + "type": "js", + "modulePath": "plugins/web/fetch-browser.js", + "sourceFile": "plugins/web/fetch-browser.js", + "navigateBefore": false + }, { "site": "wikidata", "name": "entity", @@ -11216,6 +12858,40 @@ "modulePath": "plugins/yahoo/search.js", "sourceFile": "plugins/yahoo/search.js" }, + { + "site": "yahoo-finance", + "name": "quote", + "description": "Yahoo Finance stock quote", + "access": "read", + "domain": "finance.yahoo.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" + } + ], + "columns": [ + "symbol", + "name", + "price", + "change", + "changePercent", + "open", + "high", + "low", + "volume", + "marketCap" + ], + "type": "js", + "modulePath": "plugins/yahoo-finance/quote.js", + "sourceFile": "plugins/yahoo-finance/quote.js", + "navigateBefore": "https://finance.yahoo.com" + }, { "site": "yale", "name": "export-postgraduate-courses", @@ -11389,5 +13065,71 @@ "modulePath": "plugins/ycombinator/company.js", "sourceFile": "plugins/ycombinator/company.js", "navigateBefore": false + }, + { + "site": "zlibrary", + "name": "info", + "description": "Get book details and available download formats from a Z-Library book page", + "access": "read", + "domain": "z-library.im", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "Z-Library book page URL (e.g. https://z-library.im/book/...)" + } + ], + "columns": [ + "title", + "pdf", + "epub", + "url" + ], + "type": "js", + "modulePath": "plugins/zlibrary/info.js", + "sourceFile": "plugins/zlibrary/info.js", + "navigateBefore": false + }, + { + "site": "zlibrary", + "name": "search", + "description": "Search Z-Library for books by title, author, ISBN, or keyword", + "access": "read", + "domain": "z-library.im", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (title, author, ISBN, etc.)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results (1–25)" + } + ], + "columns": [ + "rank", + "title", + "author", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/zlibrary/search.js", + "sourceFile": "plugins/zlibrary/search.js", + "navigateBefore": false } ] diff --git a/plugins/barchart/README.md b/plugins/barchart/README.md new file mode 100644 index 00000000..bdad5700 --- /dev/null +++ b/plugins/barchart/README.md @@ -0,0 +1,18 @@ +# webcmd-plugin-barchart + +Webcmd commands for barchart. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/barchart +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd barchart flow` | Barchart unusual options activity / options flow | +| `webcmd barchart greeks` | Barchart options greeks overview (IV, delta, gamma, theta, vega) | +| `webcmd barchart options` | Barchart options chain with greeks, IV, volume, and open interest | +| `webcmd barchart quote` | Barchart stock quote with price, volume, and key metrics | diff --git a/clis/barchart/flow.js b/plugins/barchart/flow.js similarity index 100% rename from clis/barchart/flow.js rename to plugins/barchart/flow.js diff --git a/clis/barchart/greeks.js b/plugins/barchart/greeks.js similarity index 100% rename from clis/barchart/greeks.js rename to plugins/barchart/greeks.js diff --git a/clis/barchart/options.js b/plugins/barchart/options.js similarity index 100% rename from clis/barchart/options.js rename to plugins/barchart/options.js diff --git a/plugins/barchart/package.json b/plugins/barchart/package.json new file mode 100644 index 00000000..928221a3 --- /dev/null +++ b/plugins/barchart/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-barchart", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for barchart", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/barchart/quote.js b/plugins/barchart/quote.js similarity index 100% rename from clis/barchart/quote.js rename to plugins/barchart/quote.js diff --git a/clis/barchart/greeks.test.js b/plugins/barchart/test/greeks.test.js similarity index 98% rename from clis/barchart/greeks.test.js rename to plugins/barchart/test/greeks.test.js index df553af1..69010868 100644 --- a/clis/barchart/greeks.test.js +++ b/plugins/barchart/test/greeks.test.js @@ -1,9 +1,9 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './greeks.js'; +import '../greeks.js'; -const { normalizeExpiration, normalizeSymbol, parseLimit, unwrapBrowserResult } = await import('./greeks.js').then((m) => m.__test__); +const { normalizeExpiration, normalizeSymbol, parseLimit, unwrapBrowserResult } = await import('../greeks.js').then((m) => m.__test__); function makePage(evaluateResult) { return { diff --git a/plugins/barchart/webcmd-plugin.json b/plugins/barchart/webcmd-plugin.json new file mode 100644 index 00000000..79a01b7a --- /dev/null +++ b/plugins/barchart/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "barchart", + "version": "0.1.0", + "description": "Webcmd commands for barchart", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/bloomberg/README.md b/plugins/bloomberg/README.md new file mode 100644 index 00000000..4def2087 --- /dev/null +++ b/plugins/bloomberg/README.md @@ -0,0 +1,27 @@ +# webcmd-plugin-bloomberg + +Webcmd commands for bloomberg. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/bloomberg +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd bloomberg businessweek` | Bloomberg Businessweek top stories | +| `webcmd bloomberg crypto` | Bloomberg Crypto top stories (RSS) | +| `webcmd bloomberg economics` | Bloomberg Economics top stories (RSS) | +| `webcmd bloomberg feeds` | List the Bloomberg RSS feed aliases used by the adapter | +| `webcmd bloomberg green` | Bloomberg Green (climate & energy) top stories (RSS) | +| `webcmd bloomberg industries` | Bloomberg Industries top stories (RSS) | +| `webcmd bloomberg main` | Bloomberg homepage top stories (RSS) | +| `webcmd bloomberg markets` | Bloomberg Markets top stories (RSS) | +| `webcmd bloomberg news` | Read a Bloomberg story/article page and return title, full content, and media links | +| `webcmd bloomberg opinions` | Bloomberg Opinion top stories (RSS) | +| `webcmd bloomberg politics` | Bloomberg Politics top stories (RSS) | +| `webcmd bloomberg pursuits` | Bloomberg Pursuits (lifestyle) top stories (RSS) | +| `webcmd bloomberg tech` | Bloomberg Tech top stories (RSS) | diff --git a/clis/bloomberg/businessweek.js b/plugins/bloomberg/businessweek.js similarity index 100% rename from clis/bloomberg/businessweek.js rename to plugins/bloomberg/businessweek.js diff --git a/clis/bloomberg/crypto.js b/plugins/bloomberg/crypto.js similarity index 100% rename from clis/bloomberg/crypto.js rename to plugins/bloomberg/crypto.js diff --git a/clis/bloomberg/economics.js b/plugins/bloomberg/economics.js similarity index 100% rename from clis/bloomberg/economics.js rename to plugins/bloomberg/economics.js diff --git a/clis/bloomberg/feeds.js b/plugins/bloomberg/feeds.js similarity index 100% rename from clis/bloomberg/feeds.js rename to plugins/bloomberg/feeds.js diff --git a/clis/bloomberg/green.js b/plugins/bloomberg/green.js similarity index 100% rename from clis/bloomberg/green.js rename to plugins/bloomberg/green.js diff --git a/clis/bloomberg/industries.js b/plugins/bloomberg/industries.js similarity index 100% rename from clis/bloomberg/industries.js rename to plugins/bloomberg/industries.js diff --git a/clis/bloomberg/main.js b/plugins/bloomberg/main.js similarity index 100% rename from clis/bloomberg/main.js rename to plugins/bloomberg/main.js diff --git a/clis/bloomberg/markets.js b/plugins/bloomberg/markets.js similarity index 100% rename from clis/bloomberg/markets.js rename to plugins/bloomberg/markets.js diff --git a/clis/bloomberg/news.js b/plugins/bloomberg/news.js similarity index 100% rename from clis/bloomberg/news.js rename to plugins/bloomberg/news.js diff --git a/clis/bloomberg/opinions.js b/plugins/bloomberg/opinions.js similarity index 100% rename from clis/bloomberg/opinions.js rename to plugins/bloomberg/opinions.js diff --git a/plugins/bloomberg/package.json b/plugins/bloomberg/package.json new file mode 100644 index 00000000..a4b6f43b --- /dev/null +++ b/plugins/bloomberg/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-bloomberg", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for bloomberg", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/bloomberg/politics.js b/plugins/bloomberg/politics.js similarity index 100% rename from clis/bloomberg/politics.js rename to plugins/bloomberg/politics.js diff --git a/clis/bloomberg/pursuits.js b/plugins/bloomberg/pursuits.js similarity index 100% rename from clis/bloomberg/pursuits.js rename to plugins/bloomberg/pursuits.js diff --git a/clis/bloomberg/tech.js b/plugins/bloomberg/tech.js similarity index 100% rename from clis/bloomberg/tech.js rename to plugins/bloomberg/tech.js diff --git a/clis/bloomberg/businessweek.test.js b/plugins/bloomberg/test/businessweek.test.js similarity index 99% rename from clis/bloomberg/businessweek.test.js rename to plugins/bloomberg/test/businessweek.test.js index 9d1449f3..35cd38b7 100644 --- a/clis/bloomberg/businessweek.test.js +++ b/plugins/bloomberg/test/businessweek.test.js @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; import { ArgumentError, CliError } from '@agentrhq/webcmd/errors'; -import { __test__ } from './businessweek.js'; +import { __test__ } from '../businessweek.js'; const { command, diff --git a/clis/bloomberg/utils.test.js b/plugins/bloomberg/test/utils.test.js similarity index 99% rename from clis/bloomberg/utils.test.js rename to plugins/bloomberg/test/utils.test.js index c9c0bc7f..bc367203 100644 --- a/clis/bloomberg/utils.test.js +++ b/plugins/bloomberg/test/utils.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { extractStoryMediaLinks, parseBloombergRss, renderStoryBody } from './utils.js'; +import { extractStoryMediaLinks, parseBloombergRss, renderStoryBody } from '../utils.js'; describe('Bloomberg utils', () => { it('parses Bloomberg RSS items with summary, link, and deduped media links', () => { const xml = ` diff --git a/clis/bloomberg/utils.js b/plugins/bloomberg/utils.js similarity index 100% rename from clis/bloomberg/utils.js rename to plugins/bloomberg/utils.js diff --git a/plugins/bloomberg/webcmd-plugin.json b/plugins/bloomberg/webcmd-plugin.json new file mode 100644 index 00000000..5e682245 --- /dev/null +++ b/plugins/bloomberg/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "bloomberg", + "version": "0.1.0", + "description": "Webcmd commands for bloomberg", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/booking/README.md b/plugins/booking/README.md new file mode 100644 index 00000000..76b6badd --- /dev/null +++ b/plugins/booking/README.md @@ -0,0 +1,15 @@ +# webcmd-plugin-booking + +Webcmd commands for booking. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/booking +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd booking search` | Search Booking.com hotels by destination and dates (server-rendered card scrape). | diff --git a/plugins/booking/package.json b/plugins/booking/package.json new file mode 100644 index 00000000..cb62e381 --- /dev/null +++ b/plugins/booking/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-booking", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for booking", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/booking/search.js b/plugins/booking/search.js similarity index 100% rename from clis/booking/search.js rename to plugins/booking/search.js diff --git a/clis/booking/booking.test.js b/plugins/booking/test/booking.test.js similarity index 99% rename from clis/booking/booking.test.js rename to plugins/booking/test/booking.test.js index fa423f4c..b555754e 100644 --- a/clis/booking/booking.test.js +++ b/plugins/booking/test/booking.test.js @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './search.js'; -import { __test__ } from './search.js'; +import '../search.js'; +import { __test__ } from '../search.js'; const { normalizePositiveInt, diff --git a/plugins/booking/webcmd-plugin.json b/plugins/booking/webcmd-plugin.json new file mode 100644 index 00000000..8dd33ee4 --- /dev/null +++ b/plugins/booking/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "booking", + "version": "0.1.0", + "description": "Webcmd commands for booking", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/chess/README.md b/plugins/chess/README.md new file mode 100644 index 00000000..ce01350b --- /dev/null +++ b/plugins/chess/README.md @@ -0,0 +1,18 @@ +# webcmd-plugin-chess + +Webcmd commands for chess. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/chess +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd chess analyze` | Open a Chess.com game in the browser analysis board | +| `webcmd chess game` | Chess.com single-game detail (white, black, result, ECO, time control) by full game URL | +| `webcmd chess games` | Chess.com recent games for a player, newest first | +| `webcmd chess stats` | Chess.com player ratings + win/loss record across game kinds | diff --git a/clis/chess/analyze.js b/plugins/chess/analyze.js similarity index 100% rename from clis/chess/analyze.js rename to plugins/chess/analyze.js diff --git a/clis/chess/game.js b/plugins/chess/game.js similarity index 100% rename from clis/chess/game.js rename to plugins/chess/game.js diff --git a/clis/chess/games.js b/plugins/chess/games.js similarity index 100% rename from clis/chess/games.js rename to plugins/chess/games.js diff --git a/plugins/chess/package.json b/plugins/chess/package.json new file mode 100644 index 00000000..4ffb8024 --- /dev/null +++ b/plugins/chess/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-chess", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for chess", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/chess/stats.js b/plugins/chess/stats.js similarity index 100% rename from clis/chess/stats.js rename to plugins/chess/stats.js diff --git a/clis/chess/analyze.test.js b/plugins/chess/test/analyze.test.js similarity index 91% rename from clis/chess/analyze.test.js rename to plugins/chess/test/analyze.test.js index f424c74a..709cf7c1 100644 --- a/clis/chess/analyze.test.js +++ b/plugins/chess/test/analyze.test.js @@ -4,10 +4,10 @@ import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { readFileSync } from 'node:fs'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; -import './analyze.js'; +import '../analyze.js'; const __dirname = dirname(fileURLToPath(import.meta.url)); -const manifestPath = resolve(__dirname, '../../cli-manifest.json'); +const manifestPath = resolve(__dirname, '../../../plugin-command-manifest.json'); function loadManifestCommand(name) { const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')); @@ -68,12 +68,12 @@ describe('chess analyze command', () => { it('build manifest keeps analyze pre-navigation disabled and game source attribution stable', () => { expect(loadManifestCommand('analyze')).toMatchObject({ navigateBefore: false, - modulePath: 'chess/analyze.js', - sourceFile: 'chess/analyze.js', + modulePath: 'plugins/chess/analyze.js', + sourceFile: 'plugins/chess/analyze.js', }); expect(loadManifestCommand('game')).toMatchObject({ - modulePath: 'chess/game.js', - sourceFile: 'chess/game.js', + modulePath: 'plugins/chess/game.js', + sourceFile: 'plugins/chess/game.js', }); }); }); diff --git a/clis/chess/game.test.js b/plugins/chess/test/game.test.js similarity index 98% rename from clis/chess/game.test.js rename to plugins/chess/test/game.test.js index 7f0e87a8..71695abf 100644 --- a/clis/chess/game.test.js +++ b/plugins/chess/test/game.test.js @@ -1,9 +1,9 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './game.js'; +import '../game.js'; -const { summarizeGame } = await import('./game.js').then((m) => m.__test__); +const { summarizeGame } = await import('../game.js').then((m) => m.__test__); afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/chess/games.test.js b/plugins/chess/test/games.test.js similarity index 98% rename from clis/chess/games.test.js rename to plugins/chess/test/games.test.js index 5625aa49..e822d36d 100644 --- a/clis/chess/games.test.js +++ b/plugins/chess/test/games.test.js @@ -1,9 +1,9 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './games.js'; +import '../games.js'; -const { parseLimit } = await import('./games.js').then((m) => m.__test__); +const { parseLimit } = await import('../games.js').then((m) => m.__test__); afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/chess/stats.test.js b/plugins/chess/test/stats.test.js similarity index 99% rename from clis/chess/stats.test.js rename to plugins/chess/test/stats.test.js index c32913dd..53a82a3d 100644 --- a/clis/chess/stats.test.js +++ b/plugins/chess/test/stats.test.js @@ -1,7 +1,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './stats.js'; +import '../stats.js'; afterEach(() => { vi.unstubAllGlobals(); diff --git a/clis/chess/utils.test.js b/plugins/chess/test/utils.test.js similarity index 99% rename from clis/chess/utils.test.js rename to plugins/chess/test/utils.test.js index 1d3896e4..e21c7390 100644 --- a/clis/chess/utils.test.js +++ b/plugins/chess/test/utils.test.js @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { __test__ } from './utils.js'; +import { __test__ } from '../utils.js'; const { validateUsername, parseGameUrl, chessApi, summarizeStats, formatDate, mapGameRow, openingName } = __test__; diff --git a/clis/chess/utils.js b/plugins/chess/utils.js similarity index 100% rename from clis/chess/utils.js rename to plugins/chess/utils.js diff --git a/plugins/chess/webcmd-plugin.json b/plugins/chess/webcmd-plugin.json new file mode 100644 index 00000000..4772e321 --- /dev/null +++ b/plugins/chess/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "chess", + "version": "0.1.0", + "description": "Webcmd commands for chess", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/imdb/README.md b/plugins/imdb/README.md new file mode 100644 index 00000000..38dab5b4 --- /dev/null +++ b/plugins/imdb/README.md @@ -0,0 +1,20 @@ +# webcmd-plugin-imdb + +Webcmd commands for imdb. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/imdb +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd imdb person` | Get actor or director info | +| `webcmd imdb reviews` | Get user reviews for a movie or TV show | +| `webcmd imdb search` | Search IMDb for movies, TV shows, and people | +| `webcmd imdb title` | Get movie or TV show details | +| `webcmd imdb top` | IMDb Top 250 Movies | +| `webcmd imdb trending` | IMDb Most Popular Movies | diff --git a/plugins/imdb/package.json b/plugins/imdb/package.json new file mode 100644 index 00000000..e93ef221 --- /dev/null +++ b/plugins/imdb/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-imdb", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for imdb", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/imdb/person.js b/plugins/imdb/person.js similarity index 100% rename from clis/imdb/person.js rename to plugins/imdb/person.js diff --git a/clis/imdb/reviews.js b/plugins/imdb/reviews.js similarity index 100% rename from clis/imdb/reviews.js rename to plugins/imdb/reviews.js diff --git a/clis/imdb/search.js b/plugins/imdb/search.js similarity index 100% rename from clis/imdb/search.js rename to plugins/imdb/search.js diff --git a/clis/imdb/utils.test.js b/plugins/imdb/test/utils.test.js similarity index 99% rename from clis/imdb/utils.test.js rename to plugins/imdb/test/utils.test.js index 2eac7622..927d2c58 100644 --- a/clis/imdb/utils.test.js +++ b/plugins/imdb/test/utils.test.js @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from 'vitest'; -import { extractJsonLd, forceEnglishUrl, formatDuration, getCurrentImdbId, isChallengePage, normalizeImdbTitleType, normalizeImdbId, waitForImdbPath, waitForImdbReviewsReady, waitForImdbSearchReady, } from './utils.js'; +import { extractJsonLd, forceEnglishUrl, formatDuration, getCurrentImdbId, isChallengePage, normalizeImdbTitleType, normalizeImdbId, waitForImdbPath, waitForImdbReviewsReady, waitForImdbSearchReady, } from '../utils.js'; describe('normalizeImdbId', () => { it('passes through bare ids', () => { expect(normalizeImdbId('tt1375666', 'tt')).toBe('tt1375666'); diff --git a/clis/imdb/title.js b/plugins/imdb/title.js similarity index 100% rename from clis/imdb/title.js rename to plugins/imdb/title.js diff --git a/clis/imdb/top.js b/plugins/imdb/top.js similarity index 100% rename from clis/imdb/top.js rename to plugins/imdb/top.js diff --git a/clis/imdb/trending.js b/plugins/imdb/trending.js similarity index 100% rename from clis/imdb/trending.js rename to plugins/imdb/trending.js diff --git a/clis/imdb/utils.js b/plugins/imdb/utils.js similarity index 100% rename from clis/imdb/utils.js rename to plugins/imdb/utils.js diff --git a/plugins/imdb/webcmd-plugin.json b/plugins/imdb/webcmd-plugin.json new file mode 100644 index 00000000..0dd128df --- /dev/null +++ b/plugins/imdb/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "imdb", + "version": "0.1.0", + "description": "Webcmd commands for imdb", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/indeed/README.md b/plugins/indeed/README.md new file mode 100644 index 00000000..1fc6002a --- /dev/null +++ b/plugins/indeed/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-indeed + +Webcmd commands for indeed. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/indeed +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd indeed job` | Read the full Indeed job posting by jk (job key) | +| `webcmd indeed search` | Indeed keyword job search (rendered DOM via browser session, US site) | diff --git a/clis/indeed/job.js b/plugins/indeed/job.js similarity index 100% rename from clis/indeed/job.js rename to plugins/indeed/job.js diff --git a/plugins/indeed/package.json b/plugins/indeed/package.json new file mode 100644 index 00000000..0af209d6 --- /dev/null +++ b/plugins/indeed/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-indeed", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for indeed", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/indeed/search.js b/plugins/indeed/search.js similarity index 100% rename from clis/indeed/search.js rename to plugins/indeed/search.js diff --git a/clis/indeed/indeed.test.js b/plugins/indeed/test/indeed.test.js similarity index 99% rename from clis/indeed/indeed.test.js rename to plugins/indeed/test/indeed.test.js index 048da5c4..96a00412 100644 --- a/clis/indeed/indeed.test.js +++ b/plugins/indeed/test/indeed.test.js @@ -16,9 +16,9 @@ import { buildJobUrl, dedupeTags, searchCardToRow, -} from './utils.js'; -import './search.js'; -import './job.js'; +} from '../utils.js'; +import '../search.js'; +import '../job.js'; function createPageMock(evaluateResult) { const evaluate = typeof evaluateResult === 'function' diff --git a/clis/indeed/utils.js b/plugins/indeed/utils.js similarity index 100% rename from clis/indeed/utils.js rename to plugins/indeed/utils.js diff --git a/plugins/indeed/webcmd-plugin.json b/plugins/indeed/webcmd-plugin.json new file mode 100644 index 00000000..9086b718 --- /dev/null +++ b/plugins/indeed/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "indeed", + "version": "0.1.0", + "description": "Webcmd commands for indeed", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/medium/README.md b/plugins/medium/README.md new file mode 100644 index 00000000..497a8c97 --- /dev/null +++ b/plugins/medium/README.md @@ -0,0 +1,18 @@ +# webcmd-plugin-medium + +Webcmd commands for medium. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/medium +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd medium feed` | Medium popular posts Feed | +| `webcmd medium search` | Search Medium posts | +| `webcmd medium tag` | Latest Medium articles tagged with a given keyword (RSS feed) | +| `webcmd medium user` | Get Medium user posts | diff --git a/clis/medium/feed.js b/plugins/medium/feed.js similarity index 100% rename from clis/medium/feed.js rename to plugins/medium/feed.js diff --git a/plugins/medium/package.json b/plugins/medium/package.json new file mode 100644 index 00000000..14563e9e --- /dev/null +++ b/plugins/medium/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-medium", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for medium", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/medium/search.js b/plugins/medium/search.js similarity index 100% rename from clis/medium/search.js rename to plugins/medium/search.js diff --git a/clis/medium/tag.js b/plugins/medium/tag.js similarity index 100% rename from clis/medium/tag.js rename to plugins/medium/tag.js diff --git a/clis/medium/user.js b/plugins/medium/user.js similarity index 100% rename from clis/medium/user.js rename to plugins/medium/user.js diff --git a/clis/medium/utils.js b/plugins/medium/utils.js similarity index 100% rename from clis/medium/utils.js rename to plugins/medium/utils.js diff --git a/plugins/medium/webcmd-plugin.json b/plugins/medium/webcmd-plugin.json new file mode 100644 index 00000000..958ea917 --- /dev/null +++ b/plugins/medium/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "medium", + "version": "0.1.0", + "description": "Webcmd commands for medium", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/producthunt/README.md b/plugins/producthunt/README.md new file mode 100644 index 00000000..458b56e9 --- /dev/null +++ b/plugins/producthunt/README.md @@ -0,0 +1,18 @@ +# webcmd-plugin-producthunt + +Webcmd commands for producthunt. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/producthunt +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd producthunt browse` | Best products in a Product Hunt category | +| `webcmd producthunt hot` | Today's top Product Hunt launches with vote counts | +| `webcmd producthunt posts` | Latest Product Hunt launches (optional category filter) | +| `webcmd producthunt today` | Today's Product Hunt launches (most recent day in feed) | diff --git a/clis/producthunt/browse.js b/plugins/producthunt/browse.js similarity index 100% rename from clis/producthunt/browse.js rename to plugins/producthunt/browse.js diff --git a/clis/producthunt/hot.js b/plugins/producthunt/hot.js similarity index 100% rename from clis/producthunt/hot.js rename to plugins/producthunt/hot.js diff --git a/plugins/producthunt/package.json b/plugins/producthunt/package.json new file mode 100644 index 00000000..d8aa528e --- /dev/null +++ b/plugins/producthunt/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-producthunt", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for producthunt", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/producthunt/posts.js b/plugins/producthunt/posts.js similarity index 100% rename from clis/producthunt/posts.js rename to plugins/producthunt/posts.js diff --git a/clis/producthunt/browser-commands.test.js b/plugins/producthunt/test/browser-commands.test.js similarity index 98% rename from clis/producthunt/browser-commands.test.js rename to plugins/producthunt/test/browser-commands.test.js index a2684394..f0f44469 100644 --- a/clis/producthunt/browser-commands.test.js +++ b/plugins/producthunt/test/browser-commands.test.js @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './hot.js'; -import './browse.js'; +import '../hot.js'; +import '../browse.js'; function pageMock({ evaluations, captureError } = {}) { const queue = [...(evaluations ?? [])]; diff --git a/clis/producthunt/utils.test.js b/plugins/producthunt/test/utils.test.js similarity index 98% rename from clis/producthunt/utils.test.js rename to plugins/producthunt/test/utils.test.js index 32acd5f3..6d4a4b75 100644 --- a/clis/producthunt/utils.test.js +++ b/plugins/producthunt/test/utils.test.js @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; -import { parseFeed, pickVoteCount, PRODUCTHUNT_CATEGORY_SLUGS } from './utils.js'; -import * as productHuntUtils from './utils.js'; +import { parseFeed, pickVoteCount, PRODUCTHUNT_CATEGORY_SLUGS } from '../utils.js'; +import * as productHuntUtils from '../utils.js'; const SAMPLE_ATOM = ` Product Hunt diff --git a/clis/producthunt/today.js b/plugins/producthunt/today.js similarity index 100% rename from clis/producthunt/today.js rename to plugins/producthunt/today.js diff --git a/clis/producthunt/utils.js b/plugins/producthunt/utils.js similarity index 100% rename from clis/producthunt/utils.js rename to plugins/producthunt/utils.js diff --git a/plugins/producthunt/webcmd-plugin.json b/plugins/producthunt/webcmd-plugin.json new file mode 100644 index 00000000..0dfcf6b6 --- /dev/null +++ b/plugins/producthunt/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "producthunt", + "version": "0.1.0", + "description": "Webcmd commands for producthunt", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/substack/README.md b/plugins/substack/README.md new file mode 100644 index 00000000..12f4a044 --- /dev/null +++ b/plugins/substack/README.md @@ -0,0 +1,17 @@ +# webcmd-plugin-substack + +Webcmd commands for substack. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/substack +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd substack feed` | Substack popular posts Feed | +| `webcmd substack publication` | Get a specific Substack Newsletter latest posts | +| `webcmd substack search` | Search Substack posts and newsletters | diff --git a/clis/substack/feed.js b/plugins/substack/feed.js similarity index 100% rename from clis/substack/feed.js rename to plugins/substack/feed.js diff --git a/plugins/substack/package.json b/plugins/substack/package.json new file mode 100644 index 00000000..038496ee --- /dev/null +++ b/plugins/substack/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-substack", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for substack", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/substack/publication.js b/plugins/substack/publication.js similarity index 100% rename from clis/substack/publication.js rename to plugins/substack/publication.js diff --git a/clis/substack/search.js b/plugins/substack/search.js similarity index 100% rename from clis/substack/search.js rename to plugins/substack/search.js diff --git a/clis/substack/utils.test.js b/plugins/substack/test/utils.test.js similarity index 99% rename from clis/substack/utils.test.js rename to plugins/substack/test/utils.test.js index 39146ecd..6ac73c8d 100644 --- a/clis/substack/utils.test.js +++ b/plugins/substack/test/utils.test.js @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from 'vitest'; -import { __test__, loadSubstackArchive, loadSubstackFeed } from './utils.js'; +import { __test__, loadSubstackArchive, loadSubstackFeed } from '../utils.js'; function createPageMock(evaluateResult) { return { goto: vi.fn().mockResolvedValue(undefined), diff --git a/clis/substack/utils.js b/plugins/substack/utils.js similarity index 100% rename from clis/substack/utils.js rename to plugins/substack/utils.js diff --git a/plugins/substack/webcmd-plugin.json b/plugins/substack/webcmd-plugin.json new file mode 100644 index 00000000..60ce4e4e --- /dev/null +++ b/plugins/substack/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "substack", + "version": "0.1.0", + "description": "Webcmd commands for substack", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/uiverse/README.md b/plugins/uiverse/README.md new file mode 100644 index 00000000..d229cce5 --- /dev/null +++ b/plugins/uiverse/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-uiverse + +Webcmd commands for uiverse. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/uiverse +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd uiverse code` | Export Uiverse component code (HTML, CSS, React, or Vue) | +| `webcmd uiverse preview` | Capture a screenshot of the Uiverse preview element | diff --git a/clis/uiverse/_shared.js b/plugins/uiverse/_shared.js similarity index 100% rename from clis/uiverse/_shared.js rename to plugins/uiverse/_shared.js diff --git a/clis/uiverse/code.js b/plugins/uiverse/code.js similarity index 100% rename from clis/uiverse/code.js rename to plugins/uiverse/code.js diff --git a/plugins/uiverse/package.json b/plugins/uiverse/package.json new file mode 100644 index 00000000..c430d185 --- /dev/null +++ b/plugins/uiverse/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-uiverse", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for uiverse", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/uiverse/preview.js b/plugins/uiverse/preview.js similarity index 100% rename from clis/uiverse/preview.js rename to plugins/uiverse/preview.js diff --git a/clis/uiverse/_shared.test.js b/plugins/uiverse/test/_shared.test.js similarity index 98% rename from clis/uiverse/_shared.test.js rename to plugins/uiverse/test/_shared.test.js index 67a73a64..d14e7cf9 100644 --- a/clis/uiverse/_shared.test.js +++ b/plugins/uiverse/test/_shared.test.js @@ -6,7 +6,7 @@ import { getPreviewFallbackTags, inferLanguage, getCodeLength, -} from './_shared.js'; +} from '../_shared.js'; describe('uiverse shared helpers', () => { it('parses full URLs and author/slug identifiers', () => { diff --git a/clis/uiverse/navigation.test.js b/plugins/uiverse/test/navigation.test.js similarity index 90% rename from clis/uiverse/navigation.test.js rename to plugins/uiverse/test/navigation.test.js index 59e7dda7..a4aa71e0 100644 --- a/clis/uiverse/navigation.test.js +++ b/plugins/uiverse/test/navigation.test.js @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './code.js'; -import './preview.js'; +import '../code.js'; +import '../preview.js'; describe('uiverse navigateBefore hardening', () => { it.each(['uiverse/code', 'uiverse/preview'])('%s starts from uiverse home instead of about:blank', (name) => { diff --git a/plugins/uiverse/webcmd-plugin.json b/plugins/uiverse/webcmd-plugin.json new file mode 100644 index 00000000..fdae328a --- /dev/null +++ b/plugins/uiverse/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "uiverse", + "version": "0.1.0", + "description": "Webcmd commands for uiverse", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/web/README.md b/plugins/web/README.md new file mode 100644 index 00000000..90009b99 --- /dev/null +++ b/plugins/web/README.md @@ -0,0 +1,15 @@ +# webcmd-plugin-web + +Webcmd commands for web. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/web +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd web fetch-browser` | Fetch any web page and export as Markdown | diff --git a/clis/web/fetch-browser.js b/plugins/web/fetch-browser.js similarity index 100% rename from clis/web/fetch-browser.js rename to plugins/web/fetch-browser.js diff --git a/clis/web/fetch.js b/plugins/web/fetch.js similarity index 100% rename from clis/web/fetch.js rename to plugins/web/fetch.js diff --git a/plugins/web/package.json b/plugins/web/package.json new file mode 100644 index 00000000..6f084e4e --- /dev/null +++ b/plugins/web/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-web", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for web", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/web/fetch-browser.test.js b/plugins/web/test/fetch-browser.test.js similarity index 99% rename from clis/web/fetch-browser.test.js rename to plugins/web/test/fetch-browser.test.js index 7cd2401a..500a7ac3 100644 --- a/clis/web/fetch-browser.test.js +++ b/plugins/web/test/fetch-browser.test.js @@ -9,7 +9,7 @@ vi.mock('@agentrhq/webcmd/download/article-download', () => ({ downloadArticle: mockDownloadArticle, })); -const { __test__ } = await import('./fetch-browser.js'); +const { __test__ } = await import('../fetch-browser.js'); describe('web/fetch-browser stdout behavior', () => { const read = __test__.command; diff --git a/plugins/web/webcmd-plugin.json b/plugins/web/webcmd-plugin.json new file mode 100644 index 00000000..5bfa40eb --- /dev/null +++ b/plugins/web/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "web", + "version": "0.1.0", + "description": "Webcmd commands for web", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/yahoo-finance/README.md b/plugins/yahoo-finance/README.md new file mode 100644 index 00000000..9dc0cfdb --- /dev/null +++ b/plugins/yahoo-finance/README.md @@ -0,0 +1,15 @@ +# webcmd-plugin-yahoo-finance + +Webcmd commands for yahoo-finance. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/yahoo-finance +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd yahoo-finance quote` | Yahoo Finance stock quote | diff --git a/plugins/yahoo-finance/package.json b/plugins/yahoo-finance/package.json new file mode 100644 index 00000000..b31e02c0 --- /dev/null +++ b/plugins/yahoo-finance/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-yahoo-finance", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for yahoo-finance", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/yahoo-finance/quote.js b/plugins/yahoo-finance/quote.js similarity index 100% rename from clis/yahoo-finance/quote.js rename to plugins/yahoo-finance/quote.js diff --git a/plugins/yahoo-finance/webcmd-plugin.json b/plugins/yahoo-finance/webcmd-plugin.json new file mode 100644 index 00000000..52f83215 --- /dev/null +++ b/plugins/yahoo-finance/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "yahoo-finance", + "version": "0.1.0", + "description": "Webcmd commands for yahoo-finance", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/zlibrary/README.md b/plugins/zlibrary/README.md new file mode 100644 index 00000000..36560bad --- /dev/null +++ b/plugins/zlibrary/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-zlibrary + +Webcmd commands for zlibrary. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/zlibrary +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd zlibrary info` | Get book details and available download formats from a Z-Library book page | +| `webcmd zlibrary search` | Search Z-Library for books by title, author, ISBN, or keyword | diff --git a/clis/zlibrary/info.js b/plugins/zlibrary/info.js similarity index 100% rename from clis/zlibrary/info.js rename to plugins/zlibrary/info.js diff --git a/plugins/zlibrary/package.json b/plugins/zlibrary/package.json new file mode 100644 index 00000000..6de144f7 --- /dev/null +++ b/plugins/zlibrary/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-zlibrary", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for zlibrary", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/zlibrary/search.js b/plugins/zlibrary/search.js similarity index 100% rename from clis/zlibrary/search.js rename to plugins/zlibrary/search.js diff --git a/clis/zlibrary/commands.test.js b/plugins/zlibrary/test/commands.test.js similarity index 95% rename from clis/zlibrary/commands.test.js rename to plugins/zlibrary/test/commands.test.js index ab129afe..40f62b18 100644 --- a/clis/zlibrary/commands.test.js +++ b/plugins/zlibrary/test/commands.test.js @@ -4,10 +4,10 @@ import { getRegistry } from '@agentrhq/webcmd/registry'; import { buildSearchUrl, normalizeZlibraryBookUrl, -} from './utils.js'; -import './search.js'; -import './info.js'; -import { createPageMock } from '../test-utils.js'; +} from '../utils.js'; +import '../search.js'; +import '../info.js'; +import { createPageMock } from './page-mock.js'; describe('zlibrary commands', () => { diff --git a/plugins/zlibrary/test/page-mock.js b/plugins/zlibrary/test/page-mock.js new file mode 100644 index 00000000..473a2eb0 --- /dev/null +++ b/plugins/zlibrary/test/page-mock.js @@ -0,0 +1,11 @@ +import { vi } from 'vitest'; + +export function createPageMock(evaluateResults = []) { + const evaluate = vi.fn(); + for (const result of evaluateResults) evaluate.mockResolvedValueOnce(result); + return { + evaluate, + goto: vi.fn().mockResolvedValue(undefined), + wait: vi.fn().mockResolvedValue(undefined), + }; +} diff --git a/clis/zlibrary/utils.js b/plugins/zlibrary/utils.js similarity index 100% rename from clis/zlibrary/utils.js rename to plugins/zlibrary/utils.js diff --git a/plugins/zlibrary/webcmd-plugin.json b/plugins/zlibrary/webcmd-plugin.json new file mode 100644 index 00000000..fd0df836 --- /dev/null +++ b/plugins/zlibrary/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "zlibrary", + "version": "0.1.0", + "description": "Webcmd commands for zlibrary", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/scripts/silent-column-drop-baseline.json b/scripts/silent-column-drop-baseline.json index 9b54bb72..dd335a6b 100644 --- a/scripts/silent-column-drop-baseline.json +++ b/scripts/silent-column-drop-baseline.json @@ -153,7 +153,7 @@ }, { "command": "bloomberg/news", - "file": "clis/bloomberg/news.js", + "file": "plugins/bloomberg/news.js", "missing": [ "errorCode", "message", @@ -162,7 +162,7 @@ }, { "command": "bloomberg/news", - "file": "clis/bloomberg/news.js", + "file": "plugins/bloomberg/news.js", "missing": [ "errorCode", "preview" @@ -194,7 +194,7 @@ }, { "command": "indeed/job", - "file": "clis/indeed/job.js", + "file": "plugins/indeed/job.js", "missing": [ "challenge", "jobType", @@ -204,7 +204,7 @@ }, { "command": "indeed/search", - "file": "clis/indeed/search.js", + "file": "plugins/indeed/search.js", "missing": [ "jk" ] @@ -317,7 +317,7 @@ }, { "command": "producthunt/hot", - "file": "clis/producthunt/hot.js", + "file": "plugins/producthunt/hot.js", "missing": [ "voteCandidates" ] @@ -372,7 +372,7 @@ }, { "command": "uiverse/code", - "file": "clis/uiverse/code.js", + "file": "plugins/uiverse/code.js", "missing": [ "code", "isTailwind", @@ -383,7 +383,7 @@ }, { "command": "uiverse/preview", - "file": "clis/uiverse/preview.js", + "file": "plugins/uiverse/preview.js", "missing": [ "matchedClassName", "matchedTag", @@ -396,7 +396,7 @@ }, { "command": "web/fetch-browser", - "file": "clis/web/fetch-browser.js", + "file": "plugins/web/fetch-browser.js", "missing": [ "accessible", "index", @@ -407,7 +407,7 @@ }, { "command": "web/fetch-browser", - "file": "clis/web/fetch-browser.js", + "file": "plugins/web/fetch-browser.js", "missing": [ "bodyTruncated", "contentType", @@ -417,7 +417,7 @@ }, { "command": "yahoo-finance/quote", - "file": "clis/yahoo-finance/quote.js", + "file": "plugins/yahoo-finance/quote.js", "missing": [ "currency", "exchange" diff --git a/scripts/typed-error-lint-baseline.json b/scripts/typed-error-lint-baseline.json index 6bf482ac..e8f2ab3f 100644 --- a/scripts/typed-error-lint-baseline.json +++ b/scripts/typed-error-lint-baseline.json @@ -122,7 +122,7 @@ { "rule": "silent-clamp", "command": "imdb/person", - "file": "clis/imdb/person.js", + "file": "plugins/imdb/person.js", "line": 23, "text": "const limit = Math.max(1, Math.min(Number(args.limit) || 10, 30));", "occurrence": 0 @@ -130,7 +130,7 @@ { "rule": "silent-clamp", "command": "imdb/reviews", - "file": "clis/imdb/reviews.js", + "file": "plugins/imdb/reviews.js", "line": 22, "text": "const limit = Math.max(1, Math.min(Number(args.limit) || 10, 25));", "occurrence": 0 @@ -138,7 +138,7 @@ { "rule": "silent-clamp", "command": "imdb/search", - "file": "clis/imdb/search.js", + "file": "plugins/imdb/search.js", "line": 27, "text": "const limit = Math.max(1, Math.min(Number(args.limit) || 20, 50));", "occurrence": 0 @@ -146,7 +146,7 @@ { "rule": "silent-clamp", "command": "imdb/top", - "file": "clis/imdb/top.js", + "file": "plugins/imdb/top.js", "line": 31, "text": "const limit = Math.max(1, Math.min(Number(args.limit) || 20, 250));", "occurrence": 0 @@ -154,7 +154,7 @@ { "rule": "silent-clamp", "command": "imdb/trending", - "file": "clis/imdb/trending.js", + "file": "plugins/imdb/trending.js", "line": 31, "text": "const limit = Math.max(1, Math.min(Number(args.limit) || 20, 100));", "occurrence": 0 @@ -178,7 +178,7 @@ { "rule": "silent-clamp", "command": "producthunt/browse", - "file": "clis/producthunt/browse.js", + "file": "plugins/producthunt/browse.js", "line": 30, "text": "const count = Math.min(Number(args.limit) || 20, 50);", "occurrence": 0 @@ -186,7 +186,7 @@ { "rule": "silent-clamp", "command": "producthunt/hot", - "file": "clis/producthunt/hot.js", + "file": "plugins/producthunt/hot.js", "line": 21, "text": "const count = Math.min(Number(args.limit) || 20, 50);", "occurrence": 0 @@ -194,7 +194,7 @@ { "rule": "silent-clamp", "command": "producthunt/posts", - "file": "clis/producthunt/posts.js", + "file": "plugins/producthunt/posts.js", "line": 24, "text": "const count = Math.min(Number(args.limit) || 20, 50);", "occurrence": 0 @@ -202,7 +202,7 @@ { "rule": "silent-clamp", "command": "producthunt/today", - "file": "clis/producthunt/today.js", + "file": "plugins/producthunt/today.js", "line": 21, "text": "const count = Math.min(Number(args.limit) || 20, 50);", "occurrence": 0 @@ -314,7 +314,7 @@ { "rule": "silent-clamp", "command": "substack/search", - "file": "clis/substack/search.js", + "file": "plugins/substack/search.js", "line": 75, "text": "const limit = Math.max(1, Math.min(Number(args.limit) || 20, 50));", "occurrence": 0 @@ -466,7 +466,7 @@ { "rule": "silent-clamp", "command": "zlibrary/search", - "file": "clis/zlibrary/search.js", + "file": "plugins/zlibrary/search.js", "line": 31, "text": "const limit = Math.max(1, Math.min(Number(args.limit) || 10, 25));", "occurrence": 0 @@ -546,7 +546,7 @@ { "rule": "silent-sentinel", "command": "web/fetch-browser", - "file": "clis/web/fetch-browser.js", + "file": "plugins/web/fetch-browser.js", "line": 378, "text": "lines.push(` [frame ${frame.index}] ${frame.sameOrigin ? 'same-origin' : 'cross-origin'} ${frame.accessible ? 'accessible' : 'blocked'} text=${frame.textLength || 0} ${frame.src || '-'}`);", "occurrence": 0 @@ -554,7 +554,7 @@ { "rule": "silent-sentinel", "command": "web/fetch-browser", - "file": "clis/web/fetch-browser.js", + "file": "plugins/web/fetch-browser.js", "line": 390, "text": "lines.push(` ${entry.method} ${entry.status || '-'} ${entry.contentType || '-'} ${entry.url}`);", "occurrence": 0 @@ -562,7 +562,7 @@ { "rule": "silent-sentinel", "command": "web/fetch-browser", - "file": "clis/web/fetch-browser.js", + "file": "plugins/web/fetch-browser.js", "line": 384, "text": "lines.push(` ${item.scope}: ${selector} (${item.url || '-'})`);", "occurrence": 0 @@ -570,7 +570,7 @@ { "rule": "silent-sentinel", "command": "web/fetch-browser", - "file": "clis/web/fetch-browser.js", + "file": "plugins/web/fetch-browser.js", "line": 375, "text": "lines.push(`url: ${diag.url || '-'}`);", "occurrence": 0 diff --git a/webcmd-plugin.json b/webcmd-plugin.json index 666d84f6..e0d5bf42 100644 --- a/webcmd-plugin.json +++ b/webcmd-plugin.json @@ -34,6 +34,16 @@ "handle": "agentrhq" } }, + "barchart": { + "path": "plugins/barchart", + "version": "0.1.0", + "description": "Webcmd commands for barchart", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "bbc": { "path": "plugins/bbc", "version": "0.1.0", @@ -54,6 +64,16 @@ "handle": "agentrhq" } }, + "bloomberg": { + "path": "plugins/bloomberg", + "version": "0.1.0", + "description": "Webcmd commands for bloomberg", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "bluesky": { "path": "plugins/bluesky", "version": "0.1.0", @@ -74,6 +94,16 @@ "handle": "agentrhq" } }, + "booking": { + "path": "plugins/booking", + "version": "0.1.0", + "description": "Webcmd commands for booking", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "brave": { "path": "plugins/brave", "version": "0.1.0", @@ -94,6 +124,16 @@ "handle": "agentrhq" } }, + "chess": { + "path": "plugins/chess", + "version": "0.1.0", + "description": "Webcmd commands for chess", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "cincinnati": { "path": "plugins/cincinnati", "version": "0.1.0", @@ -334,6 +374,26 @@ "handle": "agentrhq" } }, + "imdb": { + "path": "plugins/imdb", + "version": "0.1.0", + "description": "Webcmd commands for imdb", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "indeed": { + "path": "plugins/indeed", + "version": "0.1.0", + "description": "Webcmd commands for indeed", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "jhu": { "path": "plugins/jhu", "version": "0.1.0", @@ -424,6 +484,16 @@ "handle": "agentrhq" } }, + "medium": { + "path": "plugins/medium", + "version": "0.1.0", + "description": "Webcmd commands for medium", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "npm": { "path": "plugins/npm", "version": "0.1.0", @@ -514,6 +584,16 @@ "handle": "agentrhq" } }, + "producthunt": { + "path": "plugins/producthunt", + "version": "0.1.0", + "description": "Webcmd commands for producthunt", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "pubmed": { "path": "plugins/pubmed", "version": "0.1.0", @@ -604,6 +684,16 @@ "handle": "agentrhq" } }, + "substack": { + "path": "plugins/substack", + "version": "0.1.0", + "description": "Webcmd commands for substack", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "techcrunch": { "path": "plugins/techcrunch", "version": "0.1.0", @@ -644,6 +734,26 @@ "handle": "agentrhq" } }, + "uiverse": { + "path": "plugins/uiverse", + "version": "0.1.0", + "description": "Webcmd commands for uiverse", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "web": { + "path": "plugins/web", + "version": "0.1.0", + "description": "Webcmd commands for web", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "wikidata": { "path": "plugins/wikidata", "version": "0.1.0", @@ -684,6 +794,16 @@ "handle": "agentrhq" } }, + "yahoo-finance": { + "path": "plugins/yahoo-finance", + "version": "0.1.0", + "description": "Webcmd commands for yahoo-finance", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "yale": { "path": "plugins/yale", "version": "0.1.0", @@ -703,6 +823,16 @@ "name": "WebCMD Agent", "handle": "agentrhq" } + }, + "zlibrary": { + "path": "plugins/zlibrary", + "version": "0.1.0", + "description": "Webcmd commands for zlibrary", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } } } } From fb9eafcc77c3edb157782b41c23aeafef330cbfc Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 17:21:45 +0530 Subject: [PATCH 16/39] refactor: migrate commerce and utility adapters to plugins --- cli-manifest.json | 5923 +++++------------ plugin-command-manifest.json | 4483 ++++++++++--- plugins/amazon-in/README.md | 21 + {clis => plugins}/amazon-in/auth.js | 2 +- .../amazon-in/checkout-status.js | 0 {clis => plugins}/amazon-in/checkout.js | 0 plugins/amazon-in/package.json | 9 + {clis => plugins}/amazon-in/parsers.js | 0 {clis => plugins}/amazon-in/product.js | 0 {clis => plugins}/amazon-in/search.js | 0 {clis => plugins}/amazon-in/shared.js | 0 .../amazon-in/test}/parsers.test.js | 4 +- plugins/amazon-in/webcmd-plugin.json | 10 + {clis => plugins}/amazon-in/wishlist.js | 0 plugins/amazon/README.md | 23 + {clis => plugins}/amazon/auth.js | 2 +- {clis => plugins}/amazon/bestsellers.js | 0 {clis => plugins}/amazon/discussion.js | 0 {clis => plugins}/amazon/movers-shakers.js | 0 {clis => plugins}/amazon/new-releases.js | 0 {clis => plugins}/amazon/offer.js | 0 plugins/amazon/package.json | 9 + {clis => plugins}/amazon/product.js | 0 {clis => plugins}/amazon/rankings.js | 0 {clis => plugins}/amazon/search.js | 0 {clis => plugins}/amazon/shared.js | 0 .../amazon/test}/bestsellers.test.js | 2 +- .../amazon/test}/discussion.test.js | 6 +- .../amazon/test}/offer.test.js | 2 +- plugins/amazon/test/page-mock.js | 11 + .../amazon/test}/product.test.js | 2 +- .../amazon/test}/rankings.test.js | 2 +- .../amazon/test}/search.test.js | 2 +- .../amazon/test}/shared.test.js | 2 +- plugins/amazon/webcmd-plugin.json | 10 + plugins/band/README.md | 20 + {clis => plugins}/band/auth.js | 2 +- {clis => plugins}/band/bands.js | 0 {clis => plugins}/band/mentions.js | 0 plugins/band/package.json | 9 + {clis => plugins}/band/post.js | 0 {clis => plugins}/band/posts.js | 0 plugins/band/webcmd-plugin.json | 10 + plugins/blinkit/README.md | 23 + {clis => plugins}/blinkit/add-to-cart.js | 0 {clis => plugins}/blinkit/auth.js | 2 +- {clis => plugins}/blinkit/cart.js | 0 {clis => plugins}/blinkit/checkout.js | 0 {clis => plugins}/blinkit/location.js | 1 - plugins/blinkit/package.json | 9 + {clis => plugins}/blinkit/place-order.js | 0 {clis => plugins}/blinkit/product.js | 0 {clis => plugins}/blinkit/search.js | 0 .../blinkit/test}/blinkit.test.js | 18 +- {clis => plugins}/blinkit/utils.js | 0 plugins/blinkit/webcmd-plugin.json | 10 + plugins/coupang/README.md | 19 + {clis => plugins}/coupang/add-to-cart.js | 0 {clis => plugins}/coupang/auth.js | 2 +- plugins/coupang/package.json | 9 + {clis => plugins}/coupang/product.js | 0 {clis => plugins}/coupang/search.js | 0 .../coupang/test}/coupang.test.js | 8 +- .../coupang/test}/utils.test.js | 2 +- {clis => plugins}/coupang/utils.js | 0 plugins/coupang/webcmd-plugin.json | 10 + plugins/district/README.md | 23 + {clis => plugins}/district/_lib.js | 0 {clis => plugins}/district/auth.js | 2 +- {clis => plugins}/district/checkout.js | 0 {clis => plugins}/district/listings.js | 0 {clis => plugins}/district/locations.js | 0 plugins/district/package.json | 9 + {clis => plugins}/district/search.js | 0 {clis => plugins}/district/seats.js | 0 {clis => plugins}/district/set-location.js | 0 {clis => plugins}/district/showtimes.js | 0 .../district/test}/auth.test.js | 2 +- .../district/test}/checkout.test.ts | 2 +- plugins/district/webcmd-plugin.json | 10 + plugins/github/README.md | 16 + {clis => plugins}/github/auth.js | 2 +- plugins/github/package.json | 9 + plugins/github/webcmd-plugin.json | 10 + plugins/hf/README.md | 21 + {clis => plugins}/hf/auth.js | 2 +- {clis => plugins}/hf/datasets.js | 0 {clis => plugins}/hf/models.js | 0 plugins/hf/package.json | 9 + {clis => plugins}/hf/paper.js | 0 {clis => plugins}/hf/spaces.js | 0 {clis/hf => plugins/hf/test}/hf.test.js | 4 +- {clis => plugins}/hf/top.js | 0 plugins/hf/webcmd-plugin.json | 10 + plugins/linkedin-learning/README.md | 19 + {clis => plugins}/linkedin-learning/auth.js | 2 +- {clis => plugins}/linkedin-learning/course.js | 0 plugins/linkedin-learning/package.json | 9 + {clis => plugins}/linkedin-learning/search.js | 0 .../linkedin-learning/test}/course.test.js | 4 +- .../linkedin-learning/test}/search.test.js | 4 +- .../linkedin-learning/test}/trending.test.js | 4 +- .../linkedin-learning/trending.js | 0 plugins/linkedin-learning/webcmd-plugin.json | 10 + plugins/manus/README.md | 22 + {clis => plugins}/manus/_utils.js | 0 {clis => plugins}/manus/auth.js | 2 +- {clis => plugins}/manus/connectors.js | 0 {clis => plugins}/manus/credits.js | 0 {clis => plugins}/manus/list.js | 0 plugins/manus/package.json | 9 + {clis => plugins}/manus/read.js | 0 {clis => plugins}/manus/skills.js | 0 {clis => plugins}/manus/status.js | 0 .../manus/test}/manus.test.js | 14 +- plugins/manus/webcmd-plugin.json | 10 + scripts/silent-column-drop-baseline.json | 24 +- webcmd-plugin.json | 100 + 118 files changed, 5775 insertions(+), 5268 deletions(-) create mode 100644 plugins/amazon-in/README.md rename {clis => plugins}/amazon-in/auth.js (95%) rename {clis => plugins}/amazon-in/checkout-status.js (100%) rename {clis => plugins}/amazon-in/checkout.js (100%) create mode 100644 plugins/amazon-in/package.json rename {clis => plugins}/amazon-in/parsers.js (100%) rename {clis => plugins}/amazon-in/product.js (100%) rename {clis => plugins}/amazon-in/search.js (100%) rename {clis => plugins}/amazon-in/shared.js (100%) rename {clis/amazon-in => plugins/amazon-in/test}/parsers.test.js (99%) create mode 100644 plugins/amazon-in/webcmd-plugin.json rename {clis => plugins}/amazon-in/wishlist.js (100%) create mode 100644 plugins/amazon/README.md rename {clis => plugins}/amazon/auth.js (96%) rename {clis => plugins}/amazon/bestsellers.js (100%) rename {clis => plugins}/amazon/discussion.js (100%) rename {clis => plugins}/amazon/movers-shakers.js (100%) rename {clis => plugins}/amazon/new-releases.js (100%) rename {clis => plugins}/amazon/offer.js (100%) create mode 100644 plugins/amazon/package.json rename {clis => plugins}/amazon/product.js (100%) rename {clis => plugins}/amazon/rankings.js (100%) rename {clis => plugins}/amazon/search.js (100%) rename {clis => plugins}/amazon/shared.js (100%) rename {clis/amazon => plugins/amazon/test}/bestsellers.test.js (96%) rename {clis/amazon => plugins/amazon/test}/discussion.test.js (97%) rename {clis/amazon => plugins/amazon/test}/offer.test.js (97%) create mode 100644 plugins/amazon/test/page-mock.js rename {clis/amazon => plugins/amazon/test}/product.test.js (96%) rename {clis/amazon => plugins/amazon/test}/rankings.test.js (97%) rename {clis/amazon => plugins/amazon/test}/search.test.js (96%) rename {clis/amazon => plugins/amazon/test}/shared.test.js (98%) create mode 100644 plugins/amazon/webcmd-plugin.json create mode 100644 plugins/band/README.md rename {clis => plugins}/band/auth.js (96%) rename {clis => plugins}/band/bands.js (100%) rename {clis => plugins}/band/mentions.js (100%) create mode 100644 plugins/band/package.json rename {clis => plugins}/band/post.js (100%) rename {clis => plugins}/band/posts.js (100%) create mode 100644 plugins/band/webcmd-plugin.json create mode 100644 plugins/blinkit/README.md rename {clis => plugins}/blinkit/add-to-cart.js (100%) rename {clis => plugins}/blinkit/auth.js (97%) rename {clis => plugins}/blinkit/cart.js (100%) rename {clis => plugins}/blinkit/checkout.js (100%) rename {clis => plugins}/blinkit/location.js (99%) create mode 100644 plugins/blinkit/package.json rename {clis => plugins}/blinkit/place-order.js (100%) rename {clis => plugins}/blinkit/product.js (100%) rename {clis => plugins}/blinkit/search.js (100%) rename {clis/blinkit => plugins/blinkit/test}/blinkit.test.js (94%) rename {clis => plugins}/blinkit/utils.js (100%) create mode 100644 plugins/blinkit/webcmd-plugin.json create mode 100644 plugins/coupang/README.md rename {clis => plugins}/coupang/add-to-cart.js (100%) rename {clis => plugins}/coupang/auth.js (95%) create mode 100644 plugins/coupang/package.json rename {clis => plugins}/coupang/product.js (100%) rename {clis => plugins}/coupang/search.js (100%) rename {clis/coupang => plugins/coupang/test}/coupang.test.js (98%) rename {clis/coupang => plugins/coupang/test}/utils.test.js (97%) rename {clis => plugins}/coupang/utils.js (100%) create mode 100644 plugins/coupang/webcmd-plugin.json create mode 100644 plugins/district/README.md rename {clis => plugins}/district/_lib.js (100%) rename {clis => plugins}/district/auth.js (96%) rename {clis => plugins}/district/checkout.js (100%) rename {clis => plugins}/district/listings.js (100%) rename {clis => plugins}/district/locations.js (100%) create mode 100644 plugins/district/package.json rename {clis => plugins}/district/search.js (100%) rename {clis => plugins}/district/seats.js (100%) rename {clis => plugins}/district/set-location.js (100%) rename {clis => plugins}/district/showtimes.js (100%) rename {clis/district => plugins/district/test}/auth.test.js (97%) rename {clis/district => plugins/district/test}/checkout.test.ts (97%) create mode 100644 plugins/district/webcmd-plugin.json create mode 100644 plugins/github/README.md rename {clis => plugins}/github/auth.js (94%) create mode 100644 plugins/github/package.json create mode 100644 plugins/github/webcmd-plugin.json create mode 100644 plugins/hf/README.md rename {clis => plugins}/hf/auth.js (95%) rename {clis => plugins}/hf/datasets.js (100%) rename {clis => plugins}/hf/models.js (100%) create mode 100644 plugins/hf/package.json rename {clis => plugins}/hf/paper.js (100%) rename {clis => plugins}/hf/spaces.js (100%) rename {clis/hf => plugins/hf/test}/hf.test.js (92%) rename {clis => plugins}/hf/top.js (100%) create mode 100644 plugins/hf/webcmd-plugin.json create mode 100644 plugins/linkedin-learning/README.md rename {clis => plugins}/linkedin-learning/auth.js (97%) rename {clis => plugins}/linkedin-learning/course.js (100%) create mode 100644 plugins/linkedin-learning/package.json rename {clis => plugins}/linkedin-learning/search.js (100%) rename {clis/linkedin-learning => plugins/linkedin-learning/test}/course.test.js (97%) rename {clis/linkedin-learning => plugins/linkedin-learning/test}/search.test.js (98%) rename {clis/linkedin-learning => plugins/linkedin-learning/test}/trending.test.js (97%) rename {clis => plugins}/linkedin-learning/trending.js (100%) create mode 100644 plugins/linkedin-learning/webcmd-plugin.json create mode 100644 plugins/manus/README.md rename {clis => plugins}/manus/_utils.js (100%) rename {clis => plugins}/manus/auth.js (96%) rename {clis => plugins}/manus/connectors.js (100%) rename {clis => plugins}/manus/credits.js (100%) rename {clis => plugins}/manus/list.js (100%) create mode 100644 plugins/manus/package.json rename {clis => plugins}/manus/read.js (100%) rename {clis => plugins}/manus/skills.js (100%) rename {clis => plugins}/manus/status.js (100%) rename {clis/manus => plugins/manus/test}/manus.test.js (98%) create mode 100644 plugins/manus/webcmd-plugin.json diff --git a/cli-manifest.json b/cli-manifest.json index 8704ef33..6a177917 100644 --- a/cli-manifest.json +++ b/cli-manifest.json @@ -1,599 +1,415 @@ [ { - "site": "amazon", - "name": "bestsellers", - "description": "Amazon Best Sellers pages for category candidate discovery", - "access": "read", - "domain": "amazon.com", - "strategy": "cookie", + "site": "antigravity", + "name": "add-context", + "description": "Click the Add context button in the composer (opens file/URL picker for context attachment).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": false, - "positional": true, - "help": "Ranking URL or supported Amazon path. Omit to use the list root." - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Maximum number of ranked items to return (default 100)" - } + "args": [], + "columns": [ + "Status" ], + "type": "js", + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "cookies", + "description": "List cookies on the Antigravity renderer (JS-visible via document.cookie).", + "access": "read", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "list_type", - "rank", - "asin", - "title", - "price_text", - "rating_value", - "review_count" + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" ], "type": "js", - "modulePath": "amazon/bestsellers.js", - "sourceFile": "amazon/bestsellers.js", - "navigateBefore": false + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js", + "navigateBefore": true }, { - "site": "amazon", - "name": "discussion", - "description": "Amazon review summary and sample customer discussion from product review pages", + "site": "antigravity", + "name": "copy-code", + "description": "Return the text of a code block in the current conversation. Default: last code block; pass --index N (1-based from top) to pick a specific one.", "access": "read", - "domain": "amazon.com", - "strategy": "cookie", + "domain": "127.0.0.1", + "strategy": "ui", "browser": true, "args": [ { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "ASIN or product URL, for example B0FJS72893" - }, - { - "name": "limit", + "name": "index", "type": "int", - "default": 10, "required": false, - "help": "Maximum number of review samples to return (default 10)" + "help": "1-based index of code block (default: last)" } ], "columns": [ - "asin", - "average_rating_value", - "total_review_count" + "Field", + "Value" ], "type": "js", - "modulePath": "amazon/discussion.js", - "sourceFile": "amazon/discussion.js", - "navigateBefore": false + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true }, { - "site": "amazon", - "name": "login", - "description": "Open amazon login", + "site": "antigravity", + "name": "copy-message", + "description": "Return the text of the last assistant message (best-effort: walks up from the last visible Copy button).", "access": "write", - "domain": "amazon.com", - "strategy": "cookie", + "domain": "127.0.0.1", + "strategy": "ui", "browser": true, - "args": [], + "args": [ + { + "name": "click-button", + "type": "boolean", + "default": false, + "required": false, + "help": "Also click the in-UI Copy button" + } + ], "columns": [ - "status", - "logged_in", - "site", - "user_name", - "action", - "verify_command" + "Field", + "Value" ], "type": "js", - "modulePath": "amazon/auth.js", - "sourceFile": "amazon/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true }, { - "site": "amazon", - "name": "movers-shakers", - "description": "Amazon Movers & Shakers pages for short-term growth signals", - "access": "read", - "domain": "amazon.com", - "strategy": "cookie", + "site": "antigravity", + "name": "delete", + "description": "Delete an Antigravity conversation by ID. Antigravity asks for confirmation; we click through it. Require --yes to actually delete.", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", "browser": true, "args": [ { - "name": "input", - "type": "str", - "required": false, + "name": "id", + "type": "string", + "required": true, "positional": true, - "help": "Ranking URL or supported Amazon path. Omit to use the list root." + "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" }, { - "name": "limit", - "type": "int", - "default": 100, + "name": "yes", + "type": "boolean", + "default": false, "required": false, - "help": "Maximum number of ranked items to return (default 100)" + "help": "Actually delete (default: dry-run preview)" } ], "columns": [ - "list_type", - "rank", - "asin", - "title", - "price_text", - "rating_value", - "review_count" + "status", + "id" ], "type": "js", - "modulePath": "amazon/movers-shakers.js", - "sourceFile": "amazon/movers-shakers.js", - "navigateBefore": false + "modulePath": "antigravity/delete.js", + "sourceFile": "antigravity/delete.js", + "navigateBefore": true }, { - "site": "amazon", - "name": "new-releases", - "description": "Amazon New Releases pages for early momentum discovery", + "site": "antigravity", + "name": "display-options", + "description": "Open the Display Options menu and list its items.", "access": "read", - "domain": "amazon.com", - "strategy": "cookie", + "domain": "127.0.0.1", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": false, - "positional": true, - "help": "Ranking URL or supported Amazon path. Omit to use the list root." - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Maximum number of ranked items to return (default 100)" - } - ], + "args": [], "columns": [ - "list_type", - "rank", - "asin", - "title", - "price_text", - "rating_value", - "review_count" + "Index", + "Item" ], "type": "js", - "modulePath": "amazon/new-releases.js", - "sourceFile": "amazon/new-releases.js", - "navigateBefore": false + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true }, { - "site": "amazon", - "name": "offer", - "description": "Amazon seller, buy box, and fulfillment facts from the product page", + "site": "antigravity", + "name": "dump", + "description": "Dump the DOM to help AI understand the UI", "access": "read", - "domain": "amazon.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "ASIN or product URL, for example B0FJS72893" - } - ], + "args": [], "columns": [ - "asin", - "price_text", - "sold_by", - "ships_from", - "is_amazon_sold", - "is_amazon_fulfilled" + "htmlFile", + "snapFile" ], "type": "js", - "modulePath": "amazon/offer.js", - "sourceFile": "amazon/offer.js", - "navigateBefore": false + "modulePath": "antigravity/dump.js", + "sourceFile": "antigravity/dump.js", + "navigateBefore": true }, { - "site": "amazon", - "name": "product", - "description": "Amazon product page facts for candidate validation", + "site": "antigravity", + "name": "extract-code", + "description": "Extract multi-line code blocks from the current Antigravity conversation", "access": "read", - "domain": "amazon.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "ASIN or product URL, for example B0FJS72893" - } - ], + "args": [], "columns": [ - "asin", - "title", - "price_text", - "rating_value", - "review_count" + "code" ], "type": "js", - "modulePath": "amazon/product.js", - "sourceFile": "amazon/product.js", - "navigateBefore": false + "modulePath": "antigravity/extract-code.js", + "sourceFile": "antigravity/extract-code.js", + "navigateBefore": true }, { - "site": "amazon", - "name": "search", - "description": "Amazon search results for product discovery and coarse filtering", + "site": "antigravity", + "name": "history", + "description": "List visible Antigravity conversations from the sidebar", "access": "read", - "domain": "amazon.com", - "strategy": "cookie", + "domain": "127.0.0.1", + "strategy": "ui", "browser": true, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query, for example \"desk shelf organizer\"" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 50, "required": false, - "help": "Maximum number of results to return (default 20)" + "help": "Max conversations to return" } ], "columns": [ - "rank", - "asin", - "title", - "price_text", - "rating_value", - "review_count" - ], - "tags": [ - "search" + "Index", + "Id", + "Title" ], "type": "js", - "modulePath": "amazon/search.js", - "sourceFile": "amazon/search.js", - "navigateBefore": false + "modulePath": "antigravity/history.js", + "sourceFile": "antigravity/history.js", + "navigateBefore": true }, { - "site": "amazon", - "name": "whoami", - "description": "Show the current logged-in amazon account", + "site": "antigravity", + "name": "idb-list", + "description": "List IndexedDB databases on the Antigravity renderer.", "access": "read", - "domain": "amazon.com", - "strategy": "cookie", + "domain": "127.0.0.1", + "strategy": "ui", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "user_name" - ], - "type": "js", - "modulePath": "amazon/auth.js", - "sourceFile": "amazon/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "amazon-in", - "name": "checkout", - "description": "Prepare a guarded Amazon.in checkout with browser-only payment handoff", - "access": "write", - "domain": "amazon.in", + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" + ], + "type": "js", + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "mark-read", + "description": "Mark an unread Antigravity conversation as read. Fails if the row is already read or the postcondition cannot be verified.", + "access": "write", + "domain": "127.0.0.1", "strategy": "ui", "browser": true, "args": [ { - "name": "input", - "type": "str", + "name": "id", + "type": "string", "required": true, "positional": true, - "help": "Amazon.in product URL or ASIN" - }, - { - "name": "quantity", - "type": "int", - "default": 1, - "required": false, - "help": "Quantity (1-10)" - }, - { - "name": "size", - "type": "str", - "required": false, - "help": "Exact visible size label" - }, - { - "name": "colour", - "type": "str", - "required": false, - "help": "Exact visible colour label" - }, - { - "name": "payment", - "type": "str", - "required": true, - "help": "Payment method; secrets remain browser-only", - "choices": [ - "upi", - "saved-card", - "new-card", - "cod" - ] - }, - { - "name": "card-last4", - "type": "str", - "required": false, - "help": "Saved-card selector: exactly four digits" - }, - { - "name": "place-order", - "type": "boolean", - "default": false, - "required": false, - "help": "Submit the final Amazon action once" + "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" } ], "columns": [ "status", - "asin", - "title", - "size", - "colour", - "quantity", - "item_price", - "total", - "payment_method", - "delivery_date", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "amazon-in/checkout.js", - "sourceFile": "amazon-in/checkout.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true - }, - { - "site": "amazon-in", - "name": "checkout-status", - "description": "Read the current Amazon.in checkout or payment state without clicking", - "access": "read", - "domain": "amazon.in", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "status", - "order_id", - "total", - "payment_method", - "action" + "id", + "clicked" ], "type": "js", - "modulePath": "amazon-in/checkout-status.js", - "sourceFile": "amazon-in/checkout-status.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "antigravity/mark-read.js", + "sourceFile": "antigravity/mark-read.js", + "navigateBefore": true }, { - "site": "amazon-in", - "name": "login", - "description": "Open amazon-in login", + "site": "antigravity", + "name": "model", + "description": "Read or switch the active model in Antigravity. Without arguments, reports the current model. With (substring, case-insensitive), switches.", "access": "write", - "domain": "amazon.in", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "amazon-in/auth.js", - "sourceFile": "amazon-in/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "amazon-in", - "name": "product", - "description": "Fetch the current Amazon.in price and selected product variant", - "access": "read", - "domain": "amazon.in", + "domain": "127.0.0.1", "strategy": "ui", "browser": true, "args": [ { - "name": "input", + "name": "name", "type": "str", - "required": true, + "required": false, "positional": true, - "help": "Amazon.in product URL or ASIN" + "help": "Substring (case-insensitive) of target model name. Omit to read current." + }, + { + "name": "list", + "type": "boolean", + "default": false, + "required": false, + "help": "List models in the picker (does not switch)" } ], "columns": [ - "asin", - "title", - "price", - "mrp", - "discount", - "availability", - "size", - "colour", - "image_url", - "product_url" + "Status", + "Model" ], "type": "js", - "modulePath": "amazon-in/product.js", - "sourceFile": "amazon-in/product.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "antigravity/model.js", + "sourceFile": "antigravity/model.js", + "navigateBefore": true }, { - "site": "amazon-in", - "name": "search", - "description": "Search Amazon.in products with inclusive INR price bounds and images", - "access": "read", - "domain": "amazon.in", + "site": "antigravity", + "name": "nav", + "description": "Click Go Back or Go Forward (Antigravity in-app history).", + "access": "write", + "domain": "127.0.0.1", "strategy": "ui", "browser": true, "args": [ { - "name": "query", + "name": "direction", "type": "str", "required": true, "positional": true, - "help": "Product search query" - }, - { - "name": "min-price", - "type": "number", - "required": false, - "help": "Inclusive minimum price in rupees" - }, - { - "name": "max-price", - "type": "number", - "required": false, - "help": "Inclusive maximum price in rupees" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum results (1-50)" + "help": "back or forward" } ], "columns": [ - "rank", - "asin", - "title", - "price", - "mrp", - "rating", - "review_count", - "image_url", - "product_url", - "is_sponsored" - ], - "tags": [ - "search" + "Status" ], "type": "js", - "modulePath": "amazon-in/search.js", - "sourceFile": "amazon-in/search.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true }, { - "site": "amazon-in", - "name": "whoami", - "description": "Show the current logged-in amazon-in account", + "site": "antigravity", + "name": "new", + "description": "Start a new conversation / clear context in Antigravity", "access": "read", - "domain": "amazon.in", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "user_name" + "status" ], "type": "js", - "modulePath": "amazon-in/auth.js", - "sourceFile": "amazon-in/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "antigravity/new.js", + "sourceFile": "antigravity/new.js", + "navigateBefore": true }, { - "site": "amazon-in", - "name": "wishlist", - "description": "Fetch current prices for products in the default Amazon.in wishlist", - "access": "read", - "domain": "amazon.in", + "site": "antigravity", + "name": "react", + "description": "Click \"Good response\" or \"Bad response\" on the LAST assistant message.", + "access": "write", + "domain": "127.0.0.1", "strategy": "ui", "browser": true, "args": [ { - "name": "filter", + "name": "kind", "type": "str", - "default": "unpurchased", - "required": false, - "help": "Wishlist items to include", - "choices": [ - "unpurchased", - "all" - ] + "required": true, + "positional": true, + "help": "good or bad" } ], "columns": [ - "list_name", - "item_id", - "asin", - "title", - "price", - "mrp", - "availability", - "size", - "colour", - "image_url", - "product_url" + "Status", + "Reaction" ], "type": "js", - "modulePath": "amazon-in/wishlist.js", - "sourceFile": "amazon-in/wishlist.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", + "navigateBefore": true }, { "site": "antigravity", - "name": "add-context", - "description": "Click the Add context button in the composer (opens file/URL picker for context attachment).", - "access": "write", - "domain": "127.0.0.1", + "name": "read", + "description": "Read the latest chat messages from Antigravity AI", + "access": "read", + "domain": "localhost", "strategy": "ui", "browser": true, - "args": [], + "args": [ + { + "name": "last", + "type": "str", + "required": false, + "help": "Number of recent messages to read (not fully implemented due to generic structure, currently returns full history text or latest chunk)" + } + ], "columns": [ - "Status" + "role", + "content" ], "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", + "modulePath": "antigravity/read.js", + "sourceFile": "antigravity/read.js", "navigateBefore": true }, { "site": "antigravity", - "name": "cookies", - "description": "List cookies on the Antigravity renderer (JS-visible via document.cookie).", + "name": "recent-paths", + "description": "Show Antigravity's recently-opened folders/files (history.recentlyOpenedPathsList).", "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max rows to return" + } + ], "columns": [ "Index", "Key", @@ -612,54 +428,59 @@ ], "type": "js", "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js", - "navigateBefore": true + "sourceFile": "antigravity/storage.js" }, { "site": "antigravity", - "name": "copy-code", - "description": "Return the text of a code block in the current conversation. Default: last code block; pass --index N (1-based from top) to pick a specific one.", - "access": "read", + "name": "rename", + "description": "Rename an Antigravity conversation by ID (NOT YET IMPLEMENTED — see source comment).", + "access": "write", "domain": "127.0.0.1", "strategy": "ui", "browser": true, "args": [ { - "name": "index", - "type": "int", - "required": false, - "help": "1-based index of code block (default: last)" + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" + }, + { + "name": "title", + "type": "string", + "required": true, + "positional": true, + "help": "New title" } ], "columns": [ - "Field", - "Value" + "status" ], "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", + "modulePath": "antigravity/rename.js", + "sourceFile": "antigravity/rename.js", "navigateBefore": true }, { "site": "antigravity", - "name": "copy-message", - "description": "Return the text of the last assistant message (best-effort: walks up from the last visible Copy button).", + "name": "revert", + "description": "Click the revert button (per-message revert for agent changes). Requires --yes (this modifies your workspace).", "access": "write", "domain": "127.0.0.1", "strategy": "ui", "browser": true, "args": [ { - "name": "click-button", + "name": "yes", "type": "boolean", "default": false, "required": false, - "help": "Also click the in-UI Copy button" + "help": "Actually revert (default: dry-run)" } ], "columns": [ - "Field", - "Value" + "Status" ], "type": "js", "modulePath": "antigravity/audit-extras.js", @@ -668,49 +489,41 @@ }, { "site": "antigravity", - "name": "delete", - "description": "Delete an Antigravity conversation by ID. Antigravity asks for confirmation; we click through it. Require --yes to actually delete.", + "name": "send", + "description": "Send a message to Antigravity AI via the internal Lexical editor", "access": "write", - "domain": "127.0.0.1", + "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "id", - "type": "string", + "name": "message", + "type": "str", "required": true, "positional": true, - "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" - }, - { - "name": "yes", - "type": "boolean", - "default": false, - "required": false, - "help": "Actually delete (default: dry-run preview)" + "help": "The message text to send" } ], "columns": [ - "status", - "id" + "Status", + "Message" ], "type": "js", - "modulePath": "antigravity/delete.js", - "sourceFile": "antigravity/delete.js", + "modulePath": "antigravity/send.js", + "sourceFile": "antigravity/send.js", "navigateBefore": true }, { "site": "antigravity", - "name": "display-options", - "description": "Open the Display Options menu and list its items.", - "access": "read", + "name": "settings", + "description": "Click the Antigravity settings button (matched by data-testid=\"settings-button\").", + "access": "write", "domain": "127.0.0.1", "strategy": "ui", "browser": true, "args": [], "columns": [ - "Index", - "Item" + "Status" ], "type": "js", "modulePath": "antigravity/audit-extras.js", @@ -719,75 +532,129 @@ }, { "site": "antigravity", - "name": "dump", - "description": "Dump the DOM to help AI understand the UI", + "name": "settings-read", + "description": "Read Antigravity's user settings.json (theme, proxy, agCockpit, tfa.system.autoAccept, etc.).", "access": "read", "domain": "localhost", - "strategy": "ui", - "browser": true, + "strategy": "local", + "browser": false, "args": [], "columns": [ - "htmlFile", - "snapFile" + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" ], "type": "js", - "modulePath": "antigravity/dump.js", - "sourceFile": "antigravity/dump.js", - "navigateBefore": true + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js" }, { "site": "antigravity", - "name": "extract-code", - "description": "Extract multi-line code blocks from the current Antigravity conversation", - "access": "read", - "domain": "localhost", + "name": "sidebar-toggle", + "description": "Click Toggle Sidebar (collapses/expands the Antigravity sidebar).", + "access": "write", + "domain": "127.0.0.1", "strategy": "ui", "browser": true, "args": [], "columns": [ - "code" + "Status" ], "type": "js", - "modulePath": "antigravity/extract-code.js", - "sourceFile": "antigravity/extract-code.js", + "modulePath": "antigravity/audit-extras.js", + "sourceFile": "antigravity/audit-extras.js", "navigateBefore": true }, { "site": "antigravity", - "name": "history", - "description": "List visible Antigravity conversations from the sidebar", + "name": "state-get", + "description": "Read one value from Antigravity's state.vscdb. Pass --workspace for per-workspace.", "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, + "domain": "localhost", + "strategy": "local", + "browser": false, "args": [ { - "name": "limit", + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "Storage key name" + }, + { + "name": "workspace", + "type": "str", + "required": false, + "help": "Workspace id (from workspaces-list) to query per-workspace DB" + }, + { + "name": "max-bytes", "type": "int", - "default": 50, + "default": 8000, "required": false, - "help": "Max conversations to return" + "help": "Truncate value to this many chars" } ], "columns": [ "Index", - "Id", - "Title" + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" ], "type": "js", - "modulePath": "antigravity/history.js", - "sourceFile": "antigravity/history.js", - "navigateBefore": true + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js" }, { "site": "antigravity", - "name": "idb-list", - "description": "List IndexedDB databases on the Antigravity renderer.", + "name": "state-keys", + "description": "List keys in Antigravity's globalStorage state.vscdb (VSCode-style). Pass --workspace to query a per-workspace DB. Works while Antigravity is closed.", "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "filter", + "type": "str", + "required": false, + "help": "Case-insensitive substring filter over keys" + }, + { + "name": "workspace", + "type": "str", + "required": false, + "help": "Workspace id (from workspaces-list) to query per-workspace DB" + }, + { + "name": "limit", + "type": "int", + "default": 200, + "required": false, + "help": "Max rows to return" + } + ], "columns": [ "Index", "Key", @@ -806,131 +673,141 @@ ], "type": "js", "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js", - "navigateBefore": true + "sourceFile": "antigravity/storage.js" }, { "site": "antigravity", - "name": "mark-read", - "description": "Mark an unread Antigravity conversation as read. Fails if the row is already read or the postcondition cannot be verified.", - "access": "write", - "domain": "127.0.0.1", + "name": "status", + "description": "Check Antigravity CDP connection and get current page state", + "access": "read", + "domain": "localhost", "strategy": "ui", "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" - } - ], + "args": [], "columns": [ "status", - "id", - "clicked" + "url", + "title" ], "type": "js", - "modulePath": "antigravity/mark-read.js", - "sourceFile": "antigravity/mark-read.js", + "modulePath": "antigravity/status.js", + "sourceFile": "antigravity/status.js", "navigateBefore": true }, { "site": "antigravity", - "name": "model", - "description": "Read or switch the active model in Antigravity. Without arguments, reports the current model. With (substring, case-insensitive), switches.", - "access": "write", + "name": "storage-get", + "description": "Read a single localStorage / sessionStorage value on the Antigravity renderer.", + "access": "read", "domain": "127.0.0.1", "strategy": "ui", "browser": true, "args": [ { - "name": "name", + "name": "key", "type": "str", - "required": false, + "required": true, "positional": true, - "help": "Substring (case-insensitive) of target model name. Omit to read current." + "help": "Storage key name" }, { - "name": "list", - "type": "boolean", - "default": false, + "name": "storage", + "type": "str", + "default": "local", "required": false, - "help": "List models in the picker (does not switch)" - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "antigravity/model.js", - "sourceFile": "antigravity/model.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "nav", - "description": "Click Go Back or Go Forward (Antigravity in-app history).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ + "help": "\"local\" or \"session\"" + }, { - "name": "direction", - "type": "str", - "required": true, - "positional": true, - "help": "back or forward" + "name": "max-bytes", + "type": "int", + "default": 4000, + "required": false, + "help": "Truncate value to this many chars" } ], "columns": [ - "Status" + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" ], "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js", "navigateBefore": true }, { "site": "antigravity", - "name": "new", - "description": "Start a new conversation / clear context in Antigravity", + "name": "storage-keys", + "description": "List localStorage / sessionStorage keys on the Antigravity renderer (CDP).", "access": "read", - "domain": "localhost", + "domain": "127.0.0.1", "strategy": "ui", "browser": true, - "args": [], + "args": [ + { + "name": "storage", + "type": "str", + "default": "local", + "required": false, + "help": "\"local\" or \"session\"" + }, + { + "name": "filter", + "type": "str", + "required": false, + "help": "Case-insensitive substring filter" + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Max rows to return" + } + ], "columns": [ - "status" + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" ], "type": "js", - "modulePath": "antigravity/new.js", - "sourceFile": "antigravity/new.js", + "modulePath": "antigravity/storage.js", + "sourceFile": "antigravity/storage.js", "navigateBefore": true }, { "site": "antigravity", - "name": "react", - "description": "Click \"Good response\" or \"Bad response\" on the LAST assistant message.", + "name": "toggle-aux", + "description": "Toggle the Auxiliary Pane (Antigravity's secondary panel for code/preview).", "access": "write", "domain": "127.0.0.1", "strategy": "ui", "browser": true, - "args": [ - { - "name": "kind", - "type": "str", - "required": true, - "positional": true, - "help": "good or bad" - } - ], + "args": [], "columns": [ - "Status", - "Reaction" + "Status" ], "type": "js", "modulePath": "antigravity/audit-extras.js", @@ -939,33 +816,31 @@ }, { "site": "antigravity", - "name": "read", - "description": "Read the latest chat messages from Antigravity AI", + "name": "watch", + "description": "Stream new chat messages from Antigravity in real-time", "access": "read", "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "last", - "type": "str", + "name": "timeout", + "type": "int", + "default": 86400, "required": false, - "help": "Number of recent messages to read (not fully implemented due to generic structure, currently returns full history text or latest chunk)" + "help": "Max seconds to keep watching (default: 86400 — 24h)" } ], - "columns": [ - "role", - "content" - ], + "columns": [], "type": "js", - "modulePath": "antigravity/read.js", - "sourceFile": "antigravity/read.js", + "modulePath": "antigravity/watch.js", + "sourceFile": "antigravity/watch.js", "navigateBefore": true }, { "site": "antigravity", - "name": "recent-paths", - "description": "Show Antigravity's recently-opened folders/files (history.recentlyOpenedPathsList).", + "name": "workspaces-list", + "description": "List Antigravity workspaceStorage entries (each represents a previously-opened folder).", "access": "read", "domain": "localhost", "strategy": "local", @@ -974,7 +849,7 @@ { "name": "limit", "type": "int", - "default": 20, + "default": 50, "required": false, "help": "Max rows to return" } @@ -1000,1978 +875,236 @@ "sourceFile": "antigravity/storage.js" }, { - "site": "antigravity", - "name": "rename", - "description": "Rename an Antigravity conversation by ID (NOT YET IMPLEMENTED — see source comment).", + "site": "bigbasket", + "name": "add-to-cart", + "description": "Add a BigBasket product to cart", "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", + "domain": "www.bigbasket.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "id", - "type": "string", + "name": "product", + "type": "str", "required": true, "positional": true, - "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" + "help": "Product ID or URL" }, { - "name": "title", - "type": "string", - "required": true, - "positional": true, - "help": "New title" + "name": "quantity", + "type": "int", + "default": 1, + "required": false, + "help": "Quantity to add (max 20)" } ], "columns": [ - "status" + "ok", + "product_id", + "quantity", + "url", + "message" ], "type": "js", - "modulePath": "antigravity/rename.js", - "sourceFile": "antigravity/rename.js", - "navigateBefore": true + "modulePath": "bigbasket/add-to-cart.js", + "sourceFile": "bigbasket/add-to-cart.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "antigravity", - "name": "revert", - "description": "Click the revert button (per-message revert for agent changes). Requires --yes (this modifies your workspace).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", + "site": "bigbasket", + "name": "cart", + "description": "Read BigBasket cart line items", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", "browser": true, - "args": [ - { - "name": "yes", - "type": "boolean", - "default": false, - "required": false, - "help": "Actually revert (default: dry-run)" - } - ], + "args": [], "columns": [ - "Status" + "product_id", + "title", + "quantity", + "price", + "line_total", + "availability", + "url" ], "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", - "navigateBefore": true + "modulePath": "bigbasket/cart.js", + "sourceFile": "bigbasket/cart.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "antigravity", - "name": "send", - "description": "Send a message to Antigravity AI via the internal Lexical editor", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "site": "bigbasket", + "name": "category", + "description": "Read BigBasket category product cards", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "message", + "name": "category", "type": "str", "required": true, "positional": true, - "help": "The message text to send" + "help": "Category URL or slug" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum products to return (max 50)" } ], "columns": [ - "Status", - "Message" + "rank", + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", + "url" ], "type": "js", - "modulePath": "antigravity/send.js", - "sourceFile": "antigravity/send.js", - "navigateBefore": true + "modulePath": "bigbasket/category.js", + "sourceFile": "bigbasket/category.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "antigravity", - "name": "settings", - "description": "Click the Antigravity settings button (matched by data-testid=\"settings-button\").", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "settings-read", - "description": "Read Antigravity's user settings.json (theme, proxy, agCockpit, tfa.system.autoAccept, etc.).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js" - }, - { - "site": "antigravity", - "name": "sidebar-toggle", - "description": "Click Toggle Sidebar (collapses/expands the Antigravity sidebar).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "state-get", - "description": "Read one value from Antigravity's state.vscdb. Pass --workspace for per-workspace.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Storage key name" - }, - { - "name": "workspace", - "type": "str", - "required": false, - "help": "Workspace id (from workspaces-list) to query per-workspace DB" - }, - { - "name": "max-bytes", - "type": "int", - "default": 8000, - "required": false, - "help": "Truncate value to this many chars" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js" - }, - { - "site": "antigravity", - "name": "state-keys", - "description": "List keys in Antigravity's globalStorage state.vscdb (VSCode-style). Pass --workspace to query a per-workspace DB. Works while Antigravity is closed.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "filter", - "type": "str", - "required": false, - "help": "Case-insensitive substring filter over keys" - }, - { - "name": "workspace", - "type": "str", - "required": false, - "help": "Workspace id (from workspaces-list) to query per-workspace DB" - }, - { - "name": "limit", - "type": "int", - "default": 200, - "required": false, - "help": "Max rows to return" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js" - }, - { - "site": "antigravity", - "name": "status", - "description": "Check Antigravity CDP connection and get current page state", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "status", - "url", - "title" - ], - "type": "js", - "modulePath": "antigravity/status.js", - "sourceFile": "antigravity/status.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "storage-get", - "description": "Read a single localStorage / sessionStorage value on the Antigravity renderer.", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Storage key name" - }, - { - "name": "storage", - "type": "str", - "default": "local", - "required": false, - "help": "\"local\" or \"session\"" - }, - { - "name": "max-bytes", - "type": "int", - "default": 4000, - "required": false, - "help": "Truncate value to this many chars" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "storage-keys", - "description": "List localStorage / sessionStorage keys on the Antigravity renderer (CDP).", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "storage", - "type": "str", - "default": "local", - "required": false, - "help": "\"local\" or \"session\"" - }, - { - "name": "filter", - "type": "str", - "required": false, - "help": "Case-insensitive substring filter" - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Max rows to return" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "toggle-aux", - "description": "Toggle the Auxiliary Pane (Antigravity's secondary panel for code/preview).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "watch", - "description": "Stream new chat messages from Antigravity in real-time", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "timeout", - "type": "int", - "default": 86400, - "required": false, - "help": "Max seconds to keep watching (default: 86400 — 24h)" - } - ], - "columns": [], - "type": "js", - "modulePath": "antigravity/watch.js", - "sourceFile": "antigravity/watch.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "workspaces-list", - "description": "List Antigravity workspaceStorage entries (each represents a previously-opened folder).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max rows to return" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js" - }, - { - "site": "band", - "name": "bands", - "description": "List all Bands you belong to", - "access": "read", - "domain": "www.band.us", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "band_no", - "name", - "members" - ], - "type": "js", - "modulePath": "band/bands.js", - "sourceFile": "band/bands.js", - "navigateBefore": "https://www.band.us" - }, - { - "site": "band", - "name": "login", - "description": "Open band login", - "access": "write", - "domain": "band.us", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "band/auth.js", - "sourceFile": "band/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "band", - "name": "mentions", - "description": "Show Band notifications where you are @mentioned", - "access": "read", - "domain": "www.band.us", - "strategy": "intercept", - "browser": true, - "args": [ - { - "name": "filter", - "type": "str", - "default": "mentioned", - "required": false, - "help": "Filter: mentioned (default) | all | post | comment", - "choices": [ - "mentioned", - "all", - "post", - "comment" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results" - }, - { - "name": "unread", - "type": "bool", - "default": false, - "required": false, - "help": "Show only unread notifications" - } - ], - "columns": [ - "time", - "band", - "type", - "from", - "text", - "url" - ], - "type": "js", - "modulePath": "band/mentions.js", - "sourceFile": "band/mentions.js", - "navigateBefore": true - }, - { - "site": "band", - "name": "post", - "description": "Export full content of a post including comments", - "access": "read", - "domain": "www.band.us", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "band_no", - "type": "int", - "required": true, - "positional": true, - "help": "Band number" - }, - { - "name": "post_no", - "type": "int", - "required": true, - "positional": true, - "help": "Post number" - }, - { - "name": "output", - "type": "str", - "default": "", - "required": false, - "help": "Directory to save attached photos" - }, - { - "name": "comments", - "type": "bool", - "default": true, - "required": false, - "help": "Include comments (default: true)" - } - ], - "columns": [ - "type", - "author", - "date", - "text" - ], - "type": "js", - "modulePath": "band/post.js", - "sourceFile": "band/post.js", - "navigateBefore": false - }, - { - "site": "band", - "name": "posts", - "description": "List posts from a Band", - "access": "read", - "domain": "www.band.us", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "band_no", - "type": "int", - "required": true, - "positional": true, - "help": "Band number (get it from: band bands)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results" - } - ], - "columns": [ - "date", - "author", - "content", - "comments", - "url" - ], - "type": "js", - "modulePath": "band/posts.js", - "sourceFile": "band/posts.js", - "navigateBefore": false - }, - { - "site": "band", - "name": "whoami", - "description": "Show the current logged-in band account", - "access": "read", - "domain": "band.us", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id" - ], - "type": "js", - "modulePath": "band/auth.js", - "sourceFile": "band/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "bigbasket", - "name": "add-to-cart", - "description": "Add a BigBasket product to cart", - "access": "write", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "product", - "type": "str", - "required": true, - "positional": true, - "help": "Product ID or URL" - }, - { - "name": "quantity", - "type": "int", - "default": 1, - "required": false, - "help": "Quantity to add (max 20)" - } - ], - "columns": [ - "ok", - "product_id", - "quantity", - "url", - "message" - ], - "type": "js", - "modulePath": "bigbasket/add-to-cart.js", - "sourceFile": "bigbasket/add-to-cart.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "cart", - "description": "Read BigBasket cart line items", - "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "product_id", - "title", - "quantity", - "price", - "line_total", - "availability", - "url" - ], - "type": "js", - "modulePath": "bigbasket/cart.js", - "sourceFile": "bigbasket/cart.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "category", - "description": "Read BigBasket category product cards", - "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "category", - "type": "str", - "required": true, - "positional": true, - "help": "Category URL or slug" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum products to return (max 50)" - } - ], - "columns": [ - "rank", - "product_id", - "title", - "brand", - "pack_size", - "price", - "mrp", - "discount", - "availability", - "url" - ], - "type": "js", - "modulePath": "bigbasket/category.js", - "sourceFile": "bigbasket/category.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "checkout", - "description": "Open BigBasket checkout review without placing an order", - "access": "write", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "ok", - "stage", - "cart_total", - "address_ready", - "delivery_ready", - "payment_ready", - "next_action", - "url" - ], - "type": "js", - "modulePath": "bigbasket/checkout.js", - "sourceFile": "bigbasket/checkout.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "location", - "description": "Show the selected BigBasket delivery location", - "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "selected", - "label", - "area", - "city", - "pincode", - "source" - ], - "type": "js", - "modulePath": "bigbasket/location.js", - "sourceFile": "bigbasket/location.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "product", - "description": "Read BigBasket product details", - "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "product", - "type": "str", - "required": true, - "positional": true, - "help": "Product ID or URL" - } - ], - "columns": [ - "product_id", - "title", - "brand", - "pack_size", - "price", - "mrp", - "discount", - "availability", - "delivery", - "image_url", - "url" - ], - "type": "js", - "modulePath": "bigbasket/product.js", - "sourceFile": "bigbasket/product.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "search", - "description": "Search BigBasket products", - "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum products to return (max 50)" - } - ], - "columns": [ - "rank", - "product_id", - "title", - "brand", - "pack_size", - "price", - "mrp", - "discount", - "availability", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "bigbasket/search.js", - "sourceFile": "bigbasket/search.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "blinkit", - "name": "add-to-cart", - "description": "Add a Blinkit product to cart", - "access": "write", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "productId", - "type": "str", - "required": true, - "positional": true, - "help": "Blinkit product id" - }, - { - "name": "quantity", - "type": "int", - "default": 1, - "required": false, - "help": "Quantity to add (default 1, max 12)" - }, - { - "name": "lat", - "type": "str", - "required": false, - "help": "Delivery latitude (defaults to current Blinkit browser location)" - }, - { - "name": "lon", - "type": "str", - "required": false, - "help": "Delivery longitude (defaults to current Blinkit browser location)" - } - ], - "columns": [ - "status", - "productId", - "quantity", - "itemCount", - "itemsTotal", - "payable", - "message" - ], - "type": "js", - "modulePath": "blinkit/add-to-cart.js", - "sourceFile": "blinkit/add-to-cart.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "cart", - "description": "Show the current Blinkit cart", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "productId", - "name", - "variant", - "price", - "quantity", - "total", - "itemCount", - "payable", - "cartState" - ], - "type": "js", - "modulePath": "blinkit/cart.js", - "sourceFile": "blinkit/cart.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "checkout", - "description": "Review Blinkit checkout totals and blockers without placing an order", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "itemCount", - "itemsTotal", - "deliveryCharge", - "handlingCharge", - "payable", - "cartState", - "checkoutBlocked", - "validations" - ], - "type": "js", - "modulePath": "blinkit/checkout.js", - "sourceFile": "blinkit/checkout.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "location", - "description": "Show the selected Blinkit delivery location", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "selected", - "label", - "area", - "city", - "pincode", - "hasCoordinates", - "source" - ], - "type": "js", - "modulePath": "blinkit/location.js", - "sourceFile": "blinkit/location.js", - "navigateBefore": "https://blinkit.com" - }, - { - "site": "blinkit", - "name": "login", - "description": "Open blinkit login", - "access": "write", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "phone", - "user_id", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "blinkit/auth.js", - "sourceFile": "blinkit/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "blinkit", - "name": "place-order", - "description": "Submit the visible Blinkit final order/payment action. Requires --confirm.", - "access": "write", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "confirm", - "type": "bool", - "default": false, - "required": false, - "help": "Required acknowledgement that this may place/pay for a real order" - } - ], - "columns": [ - "status", - "confirmed", - "itemCount", - "payable", - "orderId", - "url", - "message" - ], - "type": "js", - "modulePath": "blinkit/place-order.js", - "sourceFile": "blinkit/place-order.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "product", - "description": "Read Blinkit product details for a delivery location", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "productId", - "type": "str", - "required": true, - "positional": true, - "help": "Blinkit product id" - }, - { - "name": "lat", - "type": "str", - "required": false, - "help": "Delivery latitude (defaults to current Blinkit browser location)" - }, - { - "name": "lon", - "type": "str", - "required": false, - "help": "Delivery longitude (defaults to current Blinkit browser location)" - } - ], - "columns": [ - "productId", - "name", - "brand", - "variant", - "price", - "mrp", - "currency", - "inventory", - "available", - "imageUrl", - "url" - ], - "type": "js", - "modulePath": "blinkit/product.js", - "sourceFile": "blinkit/product.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "search", - "description": "Search Blinkit products for a delivery location", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (max 48)" - }, - { - "name": "lat", - "type": "str", - "required": false, - "help": "Delivery latitude (defaults to current Blinkit browser location)" - }, - { - "name": "lon", - "type": "str", - "required": false, - "help": "Delivery longitude (defaults to current Blinkit browser location)" - } - ], - "columns": [ - "rank", - "productId", - "name", - "brand", - "variant", - "price", - "mrp", - "currency", - "inventory", - "available", - "imageUrl", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "blinkit/search.js", - "sourceFile": "blinkit/search.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "whoami", - "description": "Show the current logged-in blinkit account", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "phone", - "user_id" - ], - "type": "js", - "modulePath": "blinkit/auth.js", - "sourceFile": "blinkit/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "ask", - "description": "Send a prompt to ChatGPT web and wait for the response", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait for response" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Continue an existing ChatGPT conversation ID or /c/ URL" - }, - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" - }, - { - "name": "wait", - "type": "boolean", - "default": true, - "required": false, - "help": "Wait for the assistant response after sending" - }, - { - "name": "deep-research", - "type": "boolean", - "default": false, - "required": false, - "help": "Enable ChatGPT Deep Research (Deep Research)" - }, - { - "name": "web-search", - "type": "boolean", - "default": false, - "required": false, - "help": "Enable ChatGPT Web Search (Web Search)" - } - ], - "columns": [ - "conversationId", - "conversationUrl", - "tool", - "response" - ], - "type": "js", - "modulePath": "chatgpt/ask.js", - "sourceFile": "chatgpt/ask.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "deep-research-result", - "description": "Read a ChatGPT Deep Research report or progress from the conversation payload", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Conversation ID or full /c/ URL" - }, - { - "name": "wait", - "type": "boolean", - "default": false, - "required": false, - "help": "Wait until Deep Research completes or becomes extractable" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait when --wait is true" - }, - { - "name": "stable", - "type": "int", - "default": 6, - "required": false, - "help": "Seconds the report text must remain unchanged when --wait is true" - } - ], - "columns": [ - "conversationId", - "status", - "report", - "sources", - "progress", - "asyncTaskConversationId", - "widgetSessionId", - "asyncStatus", - "venusMessageType", - "venusStatus", - "waitingForUserUntil", - "planTitle", - "planId", - "url", - "method", - "diagnostics" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "chatgpt/deep-research-result.js", - "sourceFile": "chatgpt/deep-research-result.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "detail", - "description": "Open a ChatGPT web conversation by ID and read its messages", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Conversation ID or full /c/ URL" - }, - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" - }, - { - "name": "wait", - "type": "boolean", - "default": false, - "required": false, - "help": "Wait until the conversation stops generating and stabilizes" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait when --wait is true" - }, - { - "name": "stable", - "type": "int", - "default": 6, - "required": false, - "help": "Seconds the final messages must remain unchanged when --wait is true" - } - ], - "columns": [ - "Index", - "Role", - "Text", - "Generating", - "StableSeconds" - ], - "type": "js", - "modulePath": "chatgpt/detail.js", - "sourceFile": "chatgpt/detail.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "history", - "description": "List visible ChatGPT web conversation history from the sidebar", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max conversations to show" - } - ], - "columns": [ - "Index", - "Id", - "Title", - "Url" - ], - "type": "js", - "modulePath": "chatgpt/history.js", - "sourceFile": "chatgpt/history.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "image", - "description": "Generate images with ChatGPT web and save them locally", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Image prompt to send to ChatGPT" - }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Local image path to attach before prompting; comma-separated paths are supported", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - }, - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Start image generation inside a ChatGPT project ID or /g/g-p- URL" - }, - { - "name": "op", - "type": "str", - "required": false, - "help": "Output directory (default: ~/Pictures/chatgpt)", - "file": { - "direction": "output", - "pathKind": "directory", - "multiple": false, - "defaultPath": "~/Pictures/chatgpt" - } - }, - { - "name": "sd", - "type": "boolean", - "default": false, - "required": false, - "help": "Skip download shorthand; only show ChatGPT link" - }, - { - "name": "timeout", - "type": "int", - "default": 240, - "required": false, - "help": "Max seconds for the overall command (default: 240)" - } - ], - "columns": [ - "status", - "file", - "link" - ], - "defaultFormat": "plain", - "type": "js", - "modulePath": "chatgpt/image.js", - "sourceFile": "chatgpt/image.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "login", - "description": "Open chatgpt login", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "chatgpt/auth.js", - "sourceFile": "chatgpt/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "model", - "description": "Switch ChatGPT web model or intelligence level (GPT-5.6 Pro, fast, balanced, advanced, very-high, pro)", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "model", - "type": "str", - "required": true, - "positional": true, - "help": "ChatGPT model or intelligence level to switch to", - "choices": [ - "fast", - "speed", - "instant", - "balanced", - "balance", - "medium", - "advanced", - "high", - "thinking", - "very-high", - "ultra", - "xhigh", - "x-high", - "extra-high", - "very high", - "gpt-5.6-pro", - "gpt-5-6-pro", - "gpt-5.6-sol-pro", - "gpt-5-6-sol-pro", - "gpt-5.6", - "gpt-5-6", - "5.6-pro", - "5.6", - "pro", - "professional" - ] - }, - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Open a ChatGPT project ID or /g/g-p- URL before switching intelligence level" - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "chatgpt/model.js", - "sourceFile": "chatgpt/model.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "new", - "description": "Start a new ChatGPT web conversation", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" - } - ], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "chatgpt/new.js", - "sourceFile": "chatgpt/new.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "project-file-add", - "description": "Upload files to a ChatGPT project as project knowledge (not just conversation attachments)", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "file", - "type": "str", - "required": true, - "positional": true, - "help": "Local file path(s) to upload; comma-separated paths are supported", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "application/pdf", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - }, - { - "name": "id", - "type": "str", - "required": true, - "help": "Project ID or /g/g-p- URL" - } - ], - "columns": [ - "Status", - "File" - ], - "type": "js", - "modulePath": "chatgpt/project-file-add.js", - "sourceFile": "chatgpt/project-file-add.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "project-list", - "description": "List visible ChatGPT projects from the sidebar", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max projects to show" - } - ], - "columns": [ - "Index", - "Id", - "Title", - "Url" - ], - "type": "js", - "modulePath": "chatgpt/project-list.js", - "sourceFile": "chatgpt/project-list.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "read", - "description": "Read messages in the current ChatGPT web conversation", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" - } - ], - "columns": [ - "Index", - "Role", - "Text" - ], - "type": "js", - "modulePath": "chatgpt/read.js", - "sourceFile": "chatgpt/read.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "send", - "description": "Send a prompt to ChatGPT web without waiting for the response", + "site": "bigbasket", + "name": "checkout", + "description": "Open BigBasket checkout review without placing an order", "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Continue an existing ChatGPT conversation ID or /c/ URL" - }, - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" - } - ], - "columns": [ - "Status", - "InjectedText" - ], - "type": "js", - "modulePath": "chatgpt/send.js", - "sourceFile": "chatgpt/send.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "status", - "description": "Check ChatGPT web page availability and login state", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status", - "Login", - "Url" - ], - "type": "js", - "modulePath": "chatgpt/status.js", - "sourceFile": "chatgpt/status.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "whoami", - "description": "Show the current logged-in chatgpt account", - "access": "read", - "domain": "chatgpt.com", + "domain": "www.bigbasket.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "chatgpt/auth.js", - "sourceFile": "chatgpt/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt-app", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", - "access": "write", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "model", - "type": "str", - "required": false, - "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] - }, - { - "name": "timeout", - "type": "int", - "default": 30, - "required": false, - "help": "Max seconds to wait for response (default: 30)" - }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Path to local image to attach (optional)" - } - ], - "columns": [ - "Role", - "Text" + "ok", + "stage", + "cart_total", + "address_ready", + "delivery_ready", + "payment_ready", + "next_action", + "url" ], "type": "js", - "modulePath": "chatgpt-app/ask.js", - "sourceFile": "chatgpt-app/ask.js" + "modulePath": "bigbasket/checkout.js", + "sourceFile": "bigbasket/checkout.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "chatgpt-app", - "name": "model", - "description": "Switch ChatGPT Desktop model/mode (auto, instant, thinking, 5.2-instant, 5.2-thinking)", + "site": "bigbasket", + "name": "location", + "description": "Show the selected BigBasket delivery location", "access": "read", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "model", - "type": "str", - "required": true, - "positional": true, - "help": "Model to switch to", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] - } - ], + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "Status", - "Model" + "selected", + "label", + "area", + "city", + "pincode", + "source" ], "type": "js", - "modulePath": "chatgpt-app/model.js", - "sourceFile": "chatgpt-app/model.js" + "modulePath": "bigbasket/location.js", + "sourceFile": "bigbasket/location.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "chatgpt-app", - "name": "new", - "description": "Open a new chat in ChatGPT Desktop App", - "access": "write", - "domain": "localhost", - "strategy": "public", - "browser": false, + "site": "bigbasket", + "name": "product", + "description": "Read BigBasket product details", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "temp", - "type": "boolean", - "default": false, - "required": false, - "help": "Open a temporary chat with privacy protection" + "name": "product", + "type": "str", + "required": true, + "positional": true, + "help": "Product ID or URL" } ], "columns": [ - "Status" + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", + "delivery", + "image_url", + "url" ], "type": "js", - "modulePath": "chatgpt-app/new.js", - "sourceFile": "chatgpt-app/new.js" + "modulePath": "bigbasket/product.js", + "sourceFile": "bigbasket/product.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "chatgpt-app", - "name": "read", - "description": "Read the last visible message from the focused ChatGPT Desktop window", + "site": "bigbasket", + "name": "search", + "description": "Search BigBasket products", "access": "read", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "chatgpt-app/read.js", - "sourceFile": "chatgpt-app/read.js" - }, - { - "site": "chatgpt-app", - "name": "send", - "description": "Send a message to the active ChatGPT Desktop App window", - "access": "write", - "domain": "localhost", - "strategy": "public", - "browser": false, + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "text", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Message to send" + "help": "Search query" }, { - "name": "model", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] + "help": "Maximum products to return (max 50)" } ], "columns": [ - "Status" + "rank", + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", + "url" ], - "type": "js", - "modulePath": "chatgpt-app/send.js", - "sourceFile": "chatgpt-app/send.js" - }, - { - "site": "chatgpt-app", - "name": "status", - "description": "Check if ChatGPT Desktop App is running natively on macOS", - "access": "read", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "Status" + "tags": [ + "search" ], "type": "js", - "modulePath": "chatgpt-app/status.js", - "sourceFile": "chatgpt-app/status.js" + "modulePath": "bigbasket/search.js", + "sourceFile": "bigbasket/search.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "claude", + "site": "chatgpt", "name": "ask", - "description": "Send a prompt to Claude and get the response", + "description": "Send a prompt to ChatGPT web and wait for the response", "access": "write", - "domain": "claude.ai", + "domain": "chatgpt.com", "strategy": "cookie", "browser": true, "args": [ @@ -2997,63 +1130,124 @@ "help": "Start a new chat before sending" }, { - "name": "model", + "name": "conversation", "type": "str", - "default": "sonnet", "required": false, - "help": "Model to use: sonnet, opus, or haiku", - "choices": [ - "sonnet", - "opus", - "haiku" - ] + "valueRequired": true, + "help": "Continue an existing ChatGPT conversation ID or /c/ URL" + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + }, + { + "name": "wait", + "type": "boolean", + "default": true, + "required": false, + "help": "Wait for the assistant response after sending" + }, + { + "name": "deep-research", + "type": "boolean", + "default": false, + "required": false, + "help": "Enable ChatGPT Deep Research (Deep Research)" + }, + { + "name": "web-search", + "type": "boolean", + "default": false, + "required": false, + "help": "Enable ChatGPT Web Search (Web Search)" + } + ], + "columns": [ + "conversationId", + "conversationUrl", + "tool", + "response" + ], + "type": "js", + "modulePath": "chatgpt/ask.js", + "sourceFile": "chatgpt/ask.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "deep-research-result", + "description": "Read a ChatGPT Deep Research report or progress from the conversation payload", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Conversation ID or full /c/ URL" + }, + { + "name": "wait", + "type": "boolean", + "default": false, + "required": false, + "help": "Wait until Deep Research completes or becomes extractable" }, { - "name": "think", - "type": "boolean", - "default": false, + "name": "timeout", + "type": "int", + "default": 120, "required": false, - "help": "Enable Adaptive thinking" + "help": "Max seconds to wait when --wait is true" }, { - "name": "file", - "type": "str", + "name": "stable", + "type": "int", + "default": 6, "required": false, - "help": "Attach a file (image, PDF, text) with the prompt", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "application/pdf", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } + "help": "Seconds the report text must remain unchanged when --wait is true" } ], "columns": [ - "response" + "conversationId", + "status", + "report", + "sources", + "progress", + "asyncTaskConversationId", + "widgetSessionId", + "asyncStatus", + "venusMessageType", + "venusStatus", + "waitingForUserUntil", + "planTitle", + "planId", + "url", + "method", + "diagnostics" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "claude/ask.js", - "sourceFile": "claude/ask.js", + "modulePath": "chatgpt/deep-research-result.js", + "sourceFile": "chatgpt/deep-research-result.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "claude", + "site": "chatgpt", "name": "detail", - "description": "Open a Claude conversation by ID and read its messages", + "description": "Open a ChatGPT web conversation by ID and read its messages", "access": "read", - "domain": "claude.ai", + "domain": "chatgpt.com", "strategy": "cookie", "browser": true, "args": [ @@ -3062,26 +1256,56 @@ "type": "str", "required": true, "positional": true, - "help": "Conversation ID (UUID from /chat/)" + "help": "Conversation ID or full /c/ URL" + }, + { + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" + }, + { + "name": "wait", + "type": "boolean", + "default": false, + "required": false, + "help": "Wait until the conversation stops generating and stabilizes" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait when --wait is true" + }, + { + "name": "stable", + "type": "int", + "default": 6, + "required": false, + "help": "Seconds the final messages must remain unchanged when --wait is true" } ], "columns": [ "Index", "Role", - "Text" + "Text", + "Generating", + "StableSeconds" ], "type": "js", - "modulePath": "claude/detail.js", - "sourceFile": "claude/detail.js", + "modulePath": "chatgpt/detail.js", + "sourceFile": "chatgpt/detail.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "claude", + "site": "chatgpt", "name": "history", - "description": "List conversation history from Claude /recents", + "description": "List visible ChatGPT web conversation history from the sidebar", "access": "read", - "domain": "claude.ai", + "domain": "chatgpt.com", "strategy": "cookie", "browser": true, "args": [ @@ -3100,80 +1324,17 @@ "Url" ], "type": "js", - "modulePath": "claude/history.js", - "sourceFile": "claude/history.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "login", - "description": "Open claude login", - "access": "write", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "org_name", - "org_uuid", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "claude/auth.js", - "sourceFile": "claude/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "new", - "description": "Start a new conversation in Claude", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "claude/new.js", - "sourceFile": "claude/new.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "read", - "description": "Read the current Claude conversation", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Index", - "Role", - "Text" - ], - "type": "js", - "modulePath": "claude/read.js", - "sourceFile": "claude/read.js", + "modulePath": "chatgpt/history.js", + "sourceFile": "chatgpt/history.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "claude", - "name": "send", - "description": "Send a prompt to Claude without waiting for the response", + "site": "chatgpt", + "name": "image", + "description": "Generate images with ChatGPT web and save them locally", "access": "write", - "domain": "claude.ai", + "domain": "chatgpt.com", "strategy": "cookie", "browser": true, "args": [ @@ -3182,1316 +1343,1399 @@ "type": "str", "required": true, "positional": true, - "help": "Prompt to send" + "help": "Image prompt to send to ChatGPT" }, { - "name": "new", + "name": "image", + "type": "str", + "required": false, + "help": "Local image path to attach before prompting; comma-separated paths are supported", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start image generation inside a ChatGPT project ID or /g/g-p- URL" + }, + { + "name": "op", + "type": "str", + "required": false, + "help": "Output directory (default: ~/Pictures/chatgpt)", + "file": { + "direction": "output", + "pathKind": "directory", + "multiple": false, + "defaultPath": "~/Pictures/chatgpt" + } + }, + { + "name": "sd", "type": "boolean", "default": false, "required": false, - "help": "Start a new chat before sending" + "help": "Skip download shorthand; only show ChatGPT link" + }, + { + "name": "timeout", + "type": "int", + "default": 240, + "required": false, + "help": "Max seconds for the overall command (default: 240)" } ], - "columns": [ - "Status", - "SubmittedBy", - "InjectedText" - ], - "type": "js", - "modulePath": "claude/send.js", - "sourceFile": "claude/send.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "status", - "description": "Check Claude page availability and login state", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status", - "Login", - "Url" + "columns": [ + "status", + "file", + "link" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "claude/status.js", - "sourceFile": "claude/status.js", + "modulePath": "chatgpt/image.js", + "sourceFile": "chatgpt/image.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "claude", - "name": "whoami", - "description": "Show the current logged-in claude account", - "access": "read", - "domain": "claude.ai", + "site": "chatgpt", + "name": "login", + "description": "Open chatgpt login", + "access": "write", + "domain": "chatgpt.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ + "status", "logged_in", "site", "user_id", - "org_name", - "org_uuid" + "name", + "action", + "verify_command" ], "type": "js", - "modulePath": "claude/auth.js", - "sourceFile": "claude/auth.js", + "modulePath": "chatgpt/auth.js", + "sourceFile": "chatgpt/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "confluence", - "name": "create", - "description": "Create a Confluence page from Markdown or storage XHTML", + "site": "chatgpt", + "name": "model", + "description": "Switch ChatGPT web model or intelligence level (GPT-5.6 Pro, fast, balanced, advanced, very-high, pro)", "access": "write", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "space", - "type": "string", - "required": true, - "help": "Cloud space id, or Data Center space key" - }, - { - "name": "title", - "type": "string", - "required": true, - "help": "Page title" - }, - { - "name": "file", - "type": "string", + "name": "model", + "type": "str", "required": true, - "help": "Markdown file path" - }, - { - "name": "parent", - "type": "string", - "required": false, - "help": "Optional parent page id" - }, - { - "name": "representation", - "type": "string", - "default": "markdown", - "required": false, - "help": "Input file format", + "positional": true, + "help": "ChatGPT model or intelligence level to switch to", "choices": [ - "markdown", - "storage" + "fast", + "speed", + "instant", + "balanced", + "balance", + "medium", + "advanced", + "high", + "thinking", + "very-high", + "ultra", + "xhigh", + "x-high", + "extra-high", + "very high", + "gpt-5.6-pro", + "gpt-5-6-pro", + "gpt-5.6-sol-pro", + "gpt-5-6-sol-pro", + "gpt-5.6", + "gpt-5-6", + "5.6-pro", + "5.6", + "pro", + "professional" ] }, { - "name": "execute", - "type": "boolean", + "name": "project", + "type": "str", "required": false, - "help": "Actually create the remote page" + "valueRequired": true, + "help": "Open a ChatGPT project ID or /g/g-p- URL before switching intelligence level" } ], "columns": [ - "status", - "id", - "title", - "spaceId", - "spaceKey", - "version", - "url" + "Status", + "Model" ], "type": "js", - "modulePath": "confluence/create.js", - "sourceFile": "confluence/create.js" + "modulePath": "chatgpt/model.js", + "sourceFile": "chatgpt/model.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "confluence", - "name": "page", - "description": "Confluence page by id with storage and Markdown body", + "site": "chatgpt", + "name": "new", + "description": "Start a new ChatGPT web conversation", "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "project", "type": "str", - "required": true, - "positional": true, - "help": "Confluence page id" + "required": false, + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" } ], "columns": [ - "id", - "title", - "status", - "spaceId", - "spaceKey", - "version", - "url" + "Status" ], "type": "js", - "modulePath": "confluence/page.js", - "sourceFile": "confluence/page.js" + "modulePath": "chatgpt/new.js", + "sourceFile": "chatgpt/new.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "confluence", - "name": "search", - "description": "Search Confluence content with CQL", - "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "site": "chatgpt", + "name": "project-file-add", + "description": "Upload files to a ChatGPT project as project knowledge (not just conversation attachments)", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "cql", + "name": "file", "type": "str", "required": true, "positional": true, - "help": "CQL query, e.g. \"type = page and title ~ \\\"RCA\\\"\"" + "help": "Local file path(s) to upload; comma-separated paths are supported", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "application/pdf", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } }, { - "name": "space", - "type": "string", - "required": false, - "help": "Limit search to a Confluence space key" - }, + "name": "id", + "type": "str", + "required": true, + "help": "Project ID or /g/g-p- URL" + } + ], + "columns": [ + "Status", + "File" + ], + "type": "js", + "modulePath": "chatgpt/project-file-add.js", + "sourceFile": "chatgpt/project-file-add.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "project-list", + "description": "List visible ChatGPT projects from the sidebar", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max results to return (1-100)" + "help": "Max projects to show" } ], "columns": [ - "id", - "title", - "type", - "spaceKey", - "status", - "lastModified", - "url" + "Index", + "Id", + "Title", + "Url" + ], + "type": "js", + "modulePath": "chatgpt/project-list.js", + "sourceFile": "chatgpt/project-list.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "read", + "description": "Read messages in the current ChatGPT web conversation", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" + } ], - "tags": [ - "search" + "columns": [ + "Index", + "Role", + "Text" ], "type": "js", - "modulePath": "confluence/search.js", - "sourceFile": "confluence/search.js" + "modulePath": "chatgpt/read.js", + "sourceFile": "chatgpt/read.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "confluence", - "name": "update", - "description": "Update a Confluence page body from Markdown or storage XHTML", + "site": "chatgpt", + "name": "send", + "description": "Send a prompt to ChatGPT web without waiting for the response", "access": "write", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "prompt", "type": "str", "required": true, "positional": true, - "help": "Confluence page id" - }, - { - "name": "file", - "type": "string", - "required": true, - "help": "Markdown file path" - }, - { - "name": "title", - "type": "string", - "required": false, - "help": "Optional replacement title; defaults to current title" + "help": "Prompt to send" }, { - "name": "version-message", - "type": "string", + "name": "new", + "type": "boolean", + "default": false, "required": false, - "help": "Confluence version message" + "help": "Start a new chat before sending" }, { - "name": "representation", - "type": "string", - "default": "markdown", + "name": "conversation", + "type": "str", "required": false, - "help": "Input file format", - "choices": [ - "markdown", - "storage" - ] + "valueRequired": true, + "help": "Continue an existing ChatGPT conversation ID or /c/ URL" }, { - "name": "execute", - "type": "boolean", + "name": "project", + "type": "str", "required": false, - "help": "Actually update the remote page" + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" } ], "columns": [ - "status", - "id", - "title", - "spaceId", - "spaceKey", - "version", - "url" + "Status", + "InjectedText" ], "type": "js", - "modulePath": "confluence/update.js", - "sourceFile": "confluence/update.js" + "modulePath": "chatgpt/send.js", + "sourceFile": "chatgpt/send.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "coupang", - "name": "add-to-cart", - "description": "Add a Coupang product to cart using logged-in browser session", - "access": "write", - "domain": "www.coupang.com", + "site": "chatgpt", + "name": "status", + "description": "Check ChatGPT web page availability and login state", + "access": "read", + "domain": "chatgpt.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "product-id", - "type": "str", - "required": false, - "positional": true, - "help": "Coupang product ID" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Canonical product URL" - } - ], + "args": [], "columns": [ - "ok", - "product_id", - "url", - "message" + "Status", + "Login", + "Url" ], "type": "js", - "modulePath": "coupang/add-to-cart.js", - "sourceFile": "coupang/add-to-cart.js", - "navigateBefore": "https://www.coupang.com" + "modulePath": "chatgpt/status.js", + "sourceFile": "chatgpt/status.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "coupang", - "name": "login", - "description": "Open coupang login", - "access": "write", - "domain": "coupang.com", + "site": "chatgpt", + "name": "whoami", + "description": "Show the current logged-in chatgpt account", + "access": "read", + "domain": "chatgpt.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "status", "logged_in", "site", - "name", - "action", - "verify_command" + "user_id", + "name" ], "type": "js", - "modulePath": "coupang/auth.js", - "sourceFile": "coupang/auth.js", + "modulePath": "chatgpt/auth.js", + "sourceFile": "chatgpt/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "coupang", - "name": "product", - "description": "Read full product detail (price, rating, seller, delivery) for a Coupang product", - "access": "read", - "domain": "www.coupang.com", - "strategy": "cookie", - "browser": true, + "site": "chatgpt-app", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "public", + "browser": false, "args": [ { - "name": "product-id", + "name": "text", "type": "str", - "required": false, + "required": true, "positional": true, - "help": "Coupang product ID (digits only)" + "help": "Prompt to send" }, { - "name": "url", + "name": "model", + "type": "str", + "required": false, + "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds to wait for response (default: 30)" + }, + { + "name": "image", "type": "str", "required": false, - "help": "Canonical Coupang product URL (alternative to --product-id)" + "help": "Path to local image to attach (optional)" } ], "columns": [ - "product_id", - "title", - "price", - "original_price", - "discount_rate", - "rating", - "review_count", - "seller", - "brand", - "rocket", - "delivery_promise", - "image_url", - "url" + "Role", + "Text" ], "type": "js", - "modulePath": "coupang/product.js", - "sourceFile": "coupang/product.js", - "navigateBefore": "https://www.coupang.com" + "modulePath": "chatgpt-app/ask.js", + "sourceFile": "chatgpt-app/ask.js" }, { - "site": "coupang", - "name": "search", - "description": "Search Coupang products with logged-in browser session", + "site": "chatgpt-app", + "name": "model", + "description": "Switch ChatGPT Desktop model/mode (auto, instant, thinking, 5.2-instant, 5.2-thinking)", "access": "read", - "domain": "www.coupang.com", - "strategy": "cookie", - "browser": true, + "domain": "localhost", + "strategy": "public", + "browser": false, "args": [ { - "name": "query", + "name": "model", "type": "str", "required": true, "positional": true, - "help": "Search keyword" - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Search result page number" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (max 50)" - }, - { - "name": "filter", - "type": "str", - "required": false, - "help": "Optional search filter (currently supports: rocket)" + "help": "Model to switch to", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] } ], "columns": [ - "rank", - "product_id", - "title", - "price", - "unit_price", - "rating", - "review_count", - "rocket", - "delivery_type", - "delivery_promise", - "url" - ], - "tags": [ - "search" + "Status", + "Model" ], "type": "js", - "modulePath": "coupang/search.js", - "sourceFile": "coupang/search.js", - "navigateBefore": "https://www.coupang.com" + "modulePath": "chatgpt-app/model.js", + "sourceFile": "chatgpt-app/model.js" }, { - "site": "coupang", - "name": "whoami", - "description": "Show the current logged-in coupang account", - "access": "read", - "domain": "coupang.com", - "strategy": "cookie", - "browser": true, - "args": [], + "site": "chatgpt-app", + "name": "new", + "description": "Open a new chat in ChatGPT Desktop App", + "access": "write", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "temp", + "type": "boolean", + "default": false, + "required": false, + "help": "Open a temporary chat with privacy protection" + } + ], "columns": [ - "logged_in", - "site", - "name" + "Status" ], - "type": "js", - "modulePath": "coupang/auth.js", - "sourceFile": "coupang/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "type": "js", + "modulePath": "chatgpt-app/new.js", + "sourceFile": "chatgpt-app/new.js" }, { - "site": "discord-app", - "name": "channels", - "description": "List channels in the current Discord server", + "site": "chatgpt-app", + "name": "read", + "description": "Read the last visible message from the focused ChatGPT Desktop window", "access": "read", "domain": "localhost", - "strategy": "ui", - "browser": true, + "strategy": "public", + "browser": false, "args": [], "columns": [ - "Index", - "Channel", - "Type", - "guild_id", - "channel_id", - "url" + "Role", + "Text" ], "type": "js", - "modulePath": "discord-app/channels.js", - "sourceFile": "discord-app/channels.js", - "navigateBefore": true + "modulePath": "chatgpt-app/read.js", + "sourceFile": "chatgpt-app/read.js" }, { - "site": "discord-app", - "name": "delete", - "description": "Delete a message by its ID in the active Discord channel", + "site": "chatgpt-app", + "name": "send", + "description": "Send a message to the active ChatGPT Desktop App window", "access": "write", "domain": "localhost", - "strategy": "ui", - "browser": true, + "strategy": "public", + "browser": false, "args": [ { - "name": "message_id", - "type": "string", + "name": "text", + "type": "str", "required": true, "positional": true, - "help": "The ID of the message to delete (visible via Developer Mode or the read command)" + "help": "Message to send" + }, + { + "name": "model", + "type": "str", + "required": false, + "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] } ], "columns": [ - "status", - "message" + "Status" ], "type": "js", - "modulePath": "discord-app/delete.js", - "sourceFile": "discord-app/delete.js", - "navigateBefore": true + "modulePath": "chatgpt-app/send.js", + "sourceFile": "chatgpt-app/send.js" }, { - "site": "discord-app", - "name": "goto", - "description": "Open a Discord channel by id/name/url without sending messages", + "site": "chatgpt-app", + "name": "status", + "description": "Check if ChatGPT Desktop App is running natively on macOS", "access": "read", "domain": "localhost", - "strategy": "ui", + "strategy": "public", + "browser": false, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "chatgpt-app/status.js", + "sourceFile": "chatgpt-app/status.js" + }, + { + "site": "claude", + "name": "ask", + "description": "Send a prompt to Claude and get the response", + "access": "write", + "domain": "claude.ai", + "strategy": "cookie", "browser": true, "args": [ { - "name": "guild", + "name": "prompt", "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 120, "required": false, - "help": "Guild/server id or visible name" + "help": "Max seconds to wait for response" }, { - "name": "channel", - "type": "str", + "name": "new", + "type": "boolean", + "default": false, "required": false, - "help": "Channel id or visible name" + "help": "Start a new chat before sending" }, { - "name": "url", + "name": "model", "type": "str", + "default": "sonnet", "required": false, - "help": "Discord channel URL" + "help": "Model to use: sonnet, opus, or haiku", + "choices": [ + "sonnet", + "opus", + "haiku" + ] }, { - "name": "timeout", + "name": "think", + "type": "boolean", + "default": false, + "required": false, + "help": "Enable Adaptive thinking" + }, + { + "name": "file", "type": "str", - "default": "8", "required": false, - "help": "Seconds to wait for Discord to show the route (default: 8)" + "help": "Attach a file (image, PDF, text) with the prompt", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "application/pdf", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } } ], "columns": [ - "Status", - "guild_id", - "channel_id", - "url" + "response" ], "type": "js", - "modulePath": "discord-app/goto.js", - "sourceFile": "discord-app/goto.js", - "navigateBefore": true + "modulePath": "claude/ask.js", + "sourceFile": "claude/ask.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "members", - "description": "List online members in the current Discord channel", + "site": "claude", + "name": "detail", + "description": "Open a Claude conversation by ID and read its messages", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "claude.ai", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Conversation ID (UUID from /chat/)" + } + ], "columns": [ "Index", - "Name", - "Status" + "Role", + "Text" ], "type": "js", - "modulePath": "discord-app/members.js", - "sourceFile": "discord-app/members.js", - "navigateBefore": true + "modulePath": "claude/detail.js", + "sourceFile": "claude/detail.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "read", - "description": "Read recent messages from the active or targeted Discord channel", + "site": "claude", + "name": "history", + "description": "List conversation history from Claude /recents", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "claude.ai", + "strategy": "cookie", "browser": true, "args": [ { - "name": "count", - "type": "str", - "default": "20", - "required": false, - "help": "Number of messages to read (default: 20)" - }, - { - "name": "guild", - "type": "str", - "required": false, - "help": "Guild/server id or visible name for targeted reads" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Channel id or visible name for targeted reads" - }, - { - "name": "url", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Discord channel URL to open before reading" + "help": "Max conversations to show" } ], "columns": [ - "Author", - "Time", - "Message", - "channel_id", - "message_id" + "Index", + "Id", + "Title", + "Url" + ], + "type": "js", + "modulePath": "claude/history.js", + "sourceFile": "claude/history.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "login", + "description": "Open claude login", + "access": "write", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "org_name", + "org_uuid", + "action", + "verify_command" ], "type": "js", - "modulePath": "discord-app/read.js", - "sourceFile": "discord-app/read.js", - "navigateBefore": true + "modulePath": "claude/auth.js", + "sourceFile": "claude/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "search", - "description": "Search messages in the current Discord server/channel (Cmd+F)", + "site": "claude", + "name": "new", + "description": "Start a new conversation in Claude", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "claude.ai", + "strategy": "cookie", "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - } + "args": [], + "columns": [ + "Status" ], + "type": "js", + "modulePath": "claude/new.js", + "sourceFile": "claude/new.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "read", + "description": "Read the current Claude conversation", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ "Index", - "Author", - "Message" - ], - "tags": [ - "search" + "Role", + "Text" ], "type": "js", - "modulePath": "discord-app/search.js", - "sourceFile": "discord-app/search.js", - "navigateBefore": true + "modulePath": "claude/read.js", + "sourceFile": "claude/read.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", + "site": "claude", "name": "send", - "description": "Send a message in the active Discord channel", + "description": "Send a prompt to Claude without waiting for the response", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "claude.ai", + "strategy": "cookie", "browser": true, "args": [ { - "name": "text", + "name": "prompt", "type": "str", "required": true, "positional": true, - "help": "Message to send" + "help": "Prompt to send" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" } ], "columns": [ - "Status" + "Status", + "SubmittedBy", + "InjectedText" ], "type": "js", - "modulePath": "discord-app/send.js", - "sourceFile": "discord-app/send.js", - "navigateBefore": true + "modulePath": "claude/send.js", + "sourceFile": "claude/send.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "servers", - "description": "List all Discord servers (guilds) in the sidebar", + "site": "claude", + "name": "status", + "description": "Check Claude page availability and login state", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "claude.ai", + "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Index", - "Server", - "guild_id", - "url" + "Status", + "Login", + "Url" ], "type": "js", - "modulePath": "discord-app/servers.js", - "sourceFile": "discord-app/servers.js", - "navigateBefore": true + "modulePath": "claude/status.js", + "sourceFile": "claude/status.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "status", - "description": "Check active CDP connection to Discord Desktop", + "site": "claude", + "name": "whoami", + "description": "Show the current logged-in claude account", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "claude.ai", + "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Status", - "Url", - "Title" + "logged_in", + "site", + "user_id", + "org_name", + "org_uuid" ], "type": "js", - "modulePath": "discord-app/status.js", - "sourceFile": "discord-app/status.js", - "navigateBefore": true + "modulePath": "claude/auth.js", + "sourceFile": "claude/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "thread-read", - "description": "Read recent messages from a Discord thread/post by id or URL", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "site": "confluence", + "name": "create", + "description": "Create a Confluence page from Markdown or storage XHTML", + "access": "write", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "thread", - "type": "str", - "required": false, - "help": "Thread/post id, or a full Discord thread/post URL" + "name": "space", + "type": "string", + "required": true, + "help": "Cloud space id, or Data Center space key" }, { - "name": "count", - "type": "str", - "default": "20", - "required": false, - "help": "Number of messages to read (default: 20)" + "name": "title", + "type": "string", + "required": true, + "help": "Page title" }, { - "name": "guild", - "type": "str", + "name": "file", + "type": "string", + "required": true, + "help": "Markdown file path" + }, + { + "name": "parent", + "type": "string", "required": false, - "help": "Parent guild/server id or visible name" + "help": "Optional parent page id" }, { - "name": "channel", - "type": "str", + "name": "representation", + "type": "string", + "default": "markdown", "required": false, - "help": "Parent forum/channel id or visible name" + "help": "Input file format", + "choices": [ + "markdown", + "storage" + ] }, { - "name": "url", - "type": "str", + "name": "execute", + "type": "boolean", "required": false, - "help": "Discord thread/post URL" + "help": "Actually create the remote page" } ], "columns": [ - "Author", - "Time", - "Message", - "channel_id", - "message_id" + "status", + "id", + "title", + "spaceId", + "spaceKey", + "version", + "url" ], "type": "js", - "modulePath": "discord-app/thread-read.js", - "sourceFile": "discord-app/thread-read.js", - "navigateBefore": true + "modulePath": "confluence/create.js", + "sourceFile": "confluence/create.js" }, { - "site": "discord-app", - "name": "threads", - "description": "List visible Discord forum/thread posts in the active or targeted channel", + "site": "confluence", + "name": "page", + "description": "Confluence page by id with storage and Markdown body", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Confluence page id" + } + ], + "columns": [ + "id", + "title", + "status", + "spaceId", + "spaceKey", + "version", + "url" + ], + "type": "js", + "modulePath": "confluence/page.js", + "sourceFile": "confluence/page.js" + }, + { + "site": "confluence", + "name": "search", + "description": "Search Confluence content with CQL", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "limit", - "type": "str", - "default": "30", - "required": false, - "help": "Maximum thread/post cards to return (default: 30)" - }, - { - "name": "guild", + "name": "cql", "type": "str", - "required": false, - "help": "Guild/server id or visible name for targeted thread listing" + "required": true, + "positional": true, + "help": "CQL query, e.g. \"type = page and title ~ \\\"RCA\\\"\"" }, { - "name": "channel", - "type": "str", + "name": "space", + "type": "string", "required": false, - "help": "Forum/channel id or visible name for targeted thread listing" + "help": "Limit search to a Confluence space key" }, { - "name": "url", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Discord forum/channel URL to open before listing threads" + "help": "Max results to return (1-100)" } ], "columns": [ - "Index", - "Thread", - "Author", - "Updated", - "Preview", - "guild_id", - "channel_id", - "thread_id", + "id", + "title", + "type", + "spaceKey", + "status", + "lastModified", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "discord-app/threads.js", - "sourceFile": "discord-app/threads.js", - "navigateBefore": true + "modulePath": "confluence/search.js", + "sourceFile": "confluence/search.js" }, { - "site": "district", - "name": "checkout", - "description": "Select District movie seats and open the UPI QR payment scanner", + "site": "confluence", + "name": "update", + "description": "Update a Confluence page body from Markdown or storage XHTML", "access": "write", - "domain": "www.district.in", - "strategy": "cookie", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "show", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "District seat-layout URL or showId from district showtimes" + "help": "Confluence page id" }, { - "name": "seats", - "type": "str", + "name": "file", + "type": "string", "required": true, - "help": "Comma-separated seat labels to select, e.g. I22,I21" + "help": "Markdown file path" }, { - "name": "format-id", - "type": "str", + "name": "title", + "type": "string", "required": false, - "help": "District formatId from showtimes; required when show is a showId" + "help": "Optional replacement title; defaults to current title" }, { - "name": "content-id", - "type": "str", + "name": "version-message", + "type": "string", "required": false, - "help": "District content id; required when show is a showId" + "help": "Confluence version message" }, { - "name": "timeout", - "type": "int", - "default": 45, + "name": "representation", + "type": "string", + "default": "markdown", "required": false, - "help": "Maximum seconds to wait for selection, review page, and payment handoff" + "help": "Input file format", + "choices": [ + "markdown", + "storage" + ] }, { - "name": "payment", - "type": "str", - "default": "upi-qr", + "name": "execute", + "type": "boolean", "required": false, - "help": "Payment handoff target: upi-qr opens the scanner; review stops on order review" + "help": "Actually update the remote page" } ], "columns": [ "status", - "movie", - "cinema", - "date", - "time", - "seats", - "ticketCount", - "orderAmount", - "bookingCharge", - "total", - "paymentMethod", - "paymentState", - "upiQrVisible", - "paymentAmount", - "paymentUrl", - "showId" - ], - "type": "js", - "modulePath": "district/checkout.js", - "sourceFile": "district/checkout.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "id", + "title", + "spaceId", + "spaceKey", + "version", + "url" + ], + "type": "js", + "modulePath": "confluence/update.js", + "sourceFile": "confluence/update.js" }, { - "site": "district", - "name": "listings", - "aliases": [ - "ls" - ], - "description": "List public District by Zomato movies, events, and nearby going-out cards", + "site": "discord-app", + "name": "channels", + "description": "List channels in the current Discord server", "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "input", - "type": "str", - "default": "home", - "required": false, - "positional": true, - "help": "home, movies, events, a district.in URL, or a District path" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum rows to return (1-100)" - } - ], + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "rank", - "title", - "category", - "date", - "venue", - "price", + "Index", + "Channel", + "Type", + "guild_id", + "channel_id", "url" ], "type": "js", - "modulePath": "district/listings.js", - "sourceFile": "district/listings.js" + "modulePath": "discord-app/channels.js", + "sourceFile": "discord-app/channels.js", + "navigateBefore": true }, { - "site": "district", - "name": "locations", - "aliases": [ - "location-search" - ], - "description": "Search District-supported cities, areas, malls, and places for booking filters", - "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, + "site": "discord-app", + "name": "delete", + "description": "Delete a message by its ID in the active Discord channel", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", - "type": "str", + "name": "message_id", + "type": "string", "required": true, "positional": true, - "help": "City, area, mall, or locality, for example \"bangalore\" or \"indiranagar\"" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum location rows to return (1-50)" + "help": "The ID of the message to delete (visible via Developer Mode or the read command)" } ], - "columns": [ - "rank", - "name", - "kind", - "city", - "state", - "cityKey", - "cityId", - "placeId", - "lat", - "lng", - "distanceKm", - "source" - ], - "type": "js", - "modulePath": "district/locations.js", - "sourceFile": "district/locations.js" - }, - { - "site": "district", - "name": "login", - "description": "Open district login", - "access": "write", - "domain": "www.district.in", - "strategy": "cookie", - "browser": true, - "args": [], "columns": [ "status", - "logged_in", - "site", - "user_id", - "name", - "phone_number", - "email", - "action", - "verify_command" + "message" ], "type": "js", - "modulePath": "district/auth.js", - "sourceFile": "district/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "discord-app/delete.js", + "sourceFile": "discord-app/delete.js", + "navigateBefore": true }, { - "site": "district", - "name": "search", - "aliases": [ - "s" - ], - "description": "Search District by Zomato across movies, events, dining, stores, activities, and play", + "site": "discord-app", + "name": "goto", + "description": "Open a Discord channel by id/name/url without sending messages", "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", + "name": "guild", "type": "str", - "required": true, - "positional": true, - "help": "Search query, for example \"hamlet\" or \"arijit\"" + "required": false, + "help": "Guild/server id or visible name" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "channel", + "type": "str", "required": false, - "help": "Maximum rows to return (1-100)" + "help": "Channel id or visible name" }, { - "name": "tab", + "name": "url", "type": "str", - "default": "all", "required": false, - "help": "Search tab: all, dining, events, movies, stores, activities, or play" + "help": "Discord channel URL" + }, + { + "name": "timeout", + "type": "str", + "default": "8", + "required": false, + "help": "Seconds to wait for Discord to show the route (default: 8)" } ], "columns": [ - "rank", - "title", - "category", - "date", - "venue", - "price", + "Status", + "guild_id", + "channel_id", "url" ], - "tags": [ - "search" + "type": "js", + "modulePath": "discord-app/goto.js", + "sourceFile": "discord-app/goto.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "members", + "description": "List online members in the current Discord channel", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Name", + "Status" ], "type": "js", - "modulePath": "district/search.js", - "sourceFile": "district/search.js" + "modulePath": "discord-app/members.js", + "sourceFile": "discord-app/members.js", + "navigateBefore": true }, { - "site": "district", - "name": "seats", - "description": "List available seats for a District movie showtime", + "site": "discord-app", + "name": "read", + "description": "Read recent messages from the active or targeted Discord channel", "access": "read", - "domain": "www.district.in", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "show", - "type": "str", - "required": true, - "positional": true, - "help": "District seat-layout URL or showId from district showtimes" - }, - { - "name": "format-id", + "name": "count", "type": "str", + "default": "20", "required": false, - "help": "District formatId from showtimes; required when show is a showId" + "help": "Number of messages to read (default: 20)" }, { - "name": "content-id", + "name": "guild", "type": "str", "required": false, - "help": "District content id; required when show is a showId" + "help": "Guild/server id or visible name for targeted reads" }, { - "name": "class", + "name": "channel", "type": "str", "required": false, - "help": "Optional seat class filter, e.g. premium, premium xl, or recliner" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Number of seats to choose (1-10); without count, seats are listed normally" + "help": "Channel id or visible name for targeted reads" }, { - "name": "together", + "name": "url", "type": "str", "required": false, - "help": "Require selected seats to be adjacent when count is provided" - }, - { - "name": "max-price", - "type": "float", - "required": false, - "help": "Maximum price per seat" - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Maximum seats to return (1-300)" - }, - { - "name": "timeout", - "type": "int", - "default": 30, - "required": false, - "help": "Maximum seconds to wait for the seat map to render" + "help": "Discord channel URL to open before reading" } ], "columns": [ - "rank", - "seat", - "row", - "number", - "column", - "seatClass", - "price", - "status", - "flags", - "showId", - "formatId", - "url" + "Author", + "Time", + "Message", + "channel_id", + "message_id" ], "type": "js", - "modulePath": "district/seats.js", - "sourceFile": "district/seats.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "discord-app/read.js", + "sourceFile": "discord-app/read.js", + "navigateBefore": true }, { - "site": "district", - "name": "set-location", - "aliases": [ - "setlocation" - ], - "description": "Set the District browser session location for movie booking filters", - "access": "write", - "domain": "www.district.in", - "strategy": "cookie", + "site": "discord-app", + "name": "search", + "description": "Search messages in the current Discord server/channel (Cmd+F)", + "access": "read", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "location", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "City, area, mall, or locality, for example \"Bangalore\" or \"Indiranagar\"" - }, - { - "name": "rank", - "type": "int", - "default": 1, - "required": false, - "help": "Pick the Nth District location result (1-20), default: 1" - }, - { - "name": "timeout", - "type": "int", - "default": 45, - "required": false, - "help": "Maximum seconds to wait for the picker and location change" + "help": "Search query" } ], "columns": [ - "status", - "name", - "city", - "state", - "cityKey", - "cityId", - "placeId", - "subzoneId", - "lat", - "lng", - "availableTabs", - "source" + "Index", + "Author", + "Message" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "district/set-location.js", - "sourceFile": "district/set-location.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "discord-app/search.js", + "sourceFile": "discord-app/search.js", + "navigateBefore": true }, { - "site": "district", - "name": "showtimes", - "aliases": [ - "shows" - ], - "description": "List District movie showtimes with location, time, cinema, language, price, and format filters", - "access": "read", - "domain": "www.district.in", - "strategy": "cookie", + "site": "discord-app", + "name": "send", + "description": "Send a message in the active Discord channel", + "access": "write", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "movie", + "name": "text", "type": "str", "required": true, "positional": true, - "help": "Movie name or District movie URL" - }, + "help": "Message to send" + } + ], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "discord-app/send.js", + "sourceFile": "discord-app/send.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "servers", + "description": "List all Discord servers (guilds) in the sidebar", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Server", + "guild_id", + "url" + ], + "type": "js", + "modulePath": "discord-app/servers.js", + "sourceFile": "discord-app/servers.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "status", + "description": "Check active CDP connection to Discord Desktop", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "discord-app/status.js", + "sourceFile": "discord-app/status.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "thread-read", + "description": "Read recent messages from a Discord thread/post by id or URL", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ { - "name": "date", + "name": "thread", "type": "str", "required": false, - "help": "Show date in YYYY-MM-DD format; defaults to District selected date" + "help": "Thread/post id, or a full Discord thread/post URL" }, { - "name": "city", + "name": "count", "type": "str", + "default": "20", "required": false, - "help": "District city name/key, for example Bangalore or Bengaluru" + "help": "Number of messages to read (default: 20)" }, { - "name": "near", + "name": "guild", "type": "str", "required": false, - "help": "Area, mall, or locality to search near, for example Indiranagar" + "help": "Parent guild/server id or visible name" }, { - "name": "city-key", + "name": "channel", "type": "str", "required": false, - "help": "Legacy District city key override, for example bengaluru" + "help": "Parent forum/channel id or visible name" }, { - "name": "after", + "name": "url", "type": "str", "required": false, - "help": "Only shows at or after HH:MM, 24-hour time" - }, + "help": "Discord thread/post URL" + } + ], + "columns": [ + "Author", + "Time", + "Message", + "channel_id", + "message_id" + ], + "type": "js", + "modulePath": "discord-app/thread-read.js", + "sourceFile": "discord-app/thread-read.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "threads", + "description": "List visible Discord forum/thread posts in the active or targeted channel", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ { - "name": "before", + "name": "limit", "type": "str", + "default": "30", "required": false, - "help": "Only shows at or before HH:MM, 24-hour time" + "help": "Maximum thread/post cards to return (default: 30)" }, { - "name": "cinema", + "name": "guild", "type": "str", "required": false, - "help": "Filter cinema/theatre name, for example PVR, INOX, Orion" + "help": "Guild/server id or visible name for targeted thread listing" }, { - "name": "language", + "name": "channel", "type": "str", "required": false, - "help": "Filter movie language, for example English, Hindi, Kannada" - }, - { - "name": "max-price", - "type": "float", - "required": false, - "help": "Only shows with at least one ticket class at or below this price" + "help": "Forum/channel id or visible name for targeted thread listing" }, { - "name": "quality", + "name": "url", "type": "str", "required": false, - "help": "Generic format/quality filter, for example 2D, 3D, IMAX, IMAX 3D, 4DX" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Maximum showtime rows to return (1-200)" + "help": "Discord forum/channel URL to open before listing threads" } ], "columns": [ - "rank", - "movie", - "language", - "date", - "time", - "cinema", - "format", - "priceRange", - "available", - "showId", - "formatId", - "url" - ], - "type": "js", - "modulePath": "district/showtimes.js", - "sourceFile": "district/showtimes.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "district", - "name": "whoami", - "description": "Show the current logged-in district account", - "access": "read", - "domain": "www.district.in", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name", - "phone_number", - "email" + "Index", + "Thread", + "Author", + "Updated", + "Preview", + "guild_id", + "channel_id", + "thread_id", + "url" ], "type": "js", - "modulePath": "district/auth.js", - "sourceFile": "district/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "discord-app/threads.js", + "sourceFile": "discord-app/threads.js", + "navigateBefore": true }, { "site": "facebook", @@ -5555,55 +3799,6 @@ "sourceFile": "geogebra/triangle.js", "navigateBefore": false }, - { - "site": "github", - "name": "login", - "description": "Open github login", - "access": "write", - "domain": "github.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "id", - "username", - "name", - "url", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "github/auth.js", - "sourceFile": "github/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "github", - "name": "whoami", - "description": "Show the current logged-in github account", - "access": "read", - "domain": "github.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "id", - "username", - "name", - "url" - ], - "type": "js", - "modulePath": "github/auth.js", - "sourceFile": "github/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, { "site": "grok", "name": "ask", @@ -6090,309 +4285,32 @@ ], "columns": [ "status", - "id" - ], - "type": "js", - "modulePath": "grok/pin.js", - "sourceFile": "grok/pin.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "whoami", - "description": "Show the current logged-in grok account", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "grok/auth.js", - "sourceFile": "grok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "hf", - "name": "datasets", - "description": "Top Hugging Face datasets (downloads / likes / trending / freshness).", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "sort", - "type": "string", - "default": "downloads", - "required": false, - "help": "Sort key: downloads, likes, trending, created_at, last_modified" - }, - { - "name": "search", - "type": "string", - "required": false, - "help": "Optional name/owner substring filter." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max datasets (max 100; one API page)." - } - ], - "columns": [ - "rank", - "id", - "author", - "downloads", - "likes", - "tags", - "lastModified", - "url" - ], - "type": "js", - "modulePath": "hf/datasets.js", - "sourceFile": "hf/datasets.js" - }, - { - "site": "hf", - "name": "login", - "description": "Open hf login", - "access": "write", - "domain": "huggingface.co", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "username", - "fullname", - "type", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "hf/auth.js", - "sourceFile": "hf/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "hf", - "name": "models", - "description": "Top Hugging Face models (downloads / likes / trending / freshness).", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "sort", - "type": "string", - "default": "downloads", - "required": false, - "help": "Sort key: downloads, likes, trending, created_at, last_modified" - }, - { - "name": "search", - "type": "string", - "required": false, - "help": "Optional name/owner substring filter (e.g. \"llama\", \"mistralai/\")" - }, - { - "name": "pipeline", - "type": "string", - "required": false, - "help": "Filter by pipeline tag (e.g. text-generation, image-classification)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max models (max 100; one API page)." - } - ], - "columns": [ - "rank", - "id", - "author", - "pipelineTag", - "downloads", - "likes", - "tags", - "lastModified", - "url" - ], - "type": "js", - "modulePath": "hf/models.js", - "sourceFile": "hf/models.js" - }, - { - "site": "hf", - "name": "paper", - "description": "Hugging Face paper detail by arXiv id (full title / summary / authors / AI keywords)", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "arXiv id (e.g. \"1706.03762\") — same value HF uses to mirror the paper" - } - ], - "columns": [ - "id", - "title", - "authors", - "publishedAt", - "upvotes", - "aiKeywords", - "summary", - "aiSummary", - "url" - ], - "type": "js", - "modulePath": "hf/paper.js", - "sourceFile": "hf/paper.js" - }, - { - "site": "hf", - "name": "spaces", - "description": "Top Hugging Face Spaces (likes / created_at / last_modified).", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "sort", - "type": "string", - "default": "likes", - "required": false, - "help": "Sort key: likes, created_at, last_modified" - }, - { - "name": "search", - "type": "string", - "required": false, - "help": "Optional name/owner substring filter (e.g. \"stability\", \"openai/\")" - }, - { - "name": "sdk", - "type": "string", - "required": false, - "help": "Filter by Space SDK: gradio / streamlit / docker / static" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max spaces (max 100; one API page)." - } - ], - "columns": [ - "rank", - "id", - "author", - "sdk", - "likes", - "tags", - "lastModified", - "url" - ], - "type": "js", - "modulePath": "hf/spaces.js", - "sourceFile": "hf/spaces.js" - }, - { - "site": "hf", - "name": "top", - "description": "Top upvoted Hugging Face papers", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of papers" - }, - { - "name": "all", - "type": "bool", - "default": false, - "required": false, - "help": "Return all papers (ignore limit)" - }, - { - "name": "date", - "type": "str", - "required": false, - "help": "Date (YYYY-MM-DD), defaults to most recent" - }, - { - "name": "period", - "type": "str", - "default": "daily", - "required": false, - "help": "Time period: daily, weekly, or monthly", - "choices": [ - "daily", - "weekly", - "monthly" - ] - } - ], - "columns": [ - "rank", - "id", - "title", - "upvotes", - "authors" + "id" ], "type": "js", - "modulePath": "hf/top.js", - "sourceFile": "hf/top.js" + "modulePath": "grok/pin.js", + "sourceFile": "grok/pin.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" }, { - "site": "hf", + "site": "grok", "name": "whoami", - "description": "Show the current logged-in hf account", + "description": "Show the current logged-in grok account", "access": "read", - "domain": "huggingface.co", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ "logged_in", "site", - "username", - "fullname", - "type" + "user_id", + "name" ], "type": "js", - "modulePath": "hf/auth.js", - "sourceFile": "hf/auth.js", + "modulePath": "grok/auth.js", + "sourceFile": "grok/auth.js", "navigateBefore": false, "siteSession": "persistent" }, @@ -7177,359 +5095,6 @@ "navigateBefore": false, "siteSession": "persistent" }, - { - "site": "linkedin-learning", - "name": "course", - "description": "Get LinkedIn Learning course detail by slug or course URL", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "slug", - "type": "string", - "required": true, - "positional": true, - "help": "Course slug (e.g. agentic-ai-build-your-first-agentic-ai-system) or full /learning/ URL" - } - ], - "columns": [ - "title", - "slug", - "description", - "difficulty", - "duration_sec", - "videos_count", - "rating", - "rating_count", - "released", - "url" - ], - "type": "js", - "modulePath": "linkedin-learning/course.js", - "sourceFile": "linkedin-learning/course.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin-learning", - "name": "login", - "description": "Open linkedin-learning login", - "access": "write", - "domain": "linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "public_id", - "plain_id", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "linkedin-learning/auth.js", - "sourceFile": "linkedin-learning/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "linkedin-learning", - "name": "search", - "description": "Search LinkedIn Learning courses, videos, and learning paths by keyword", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "keywords", - "type": "string", - "required": true, - "positional": true, - "help": "Search keywords, e.g. \"AI agent\"" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum results to return (1-50)" - } - ], - "columns": [ - "rank", - "type", - "title", - "instructor", - "difficulty", - "duration_sec", - "rating", - "rating_count", - "viewers", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "linkedin-learning/search.js", - "sourceFile": "linkedin-learning/search.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin-learning", - "name": "trending", - "description": "Browse LinkedIn Learning recommended courses across personalized carousels", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum results to return (1-50)" - } - ], - "columns": [ - "rank", - "group", - "type", - "title", - "difficulty", - "viewers", - "url" - ], - "type": "js", - "modulePath": "linkedin-learning/trending.js", - "sourceFile": "linkedin-learning/trending.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin-learning", - "name": "whoami", - "description": "Show the current logged-in linkedin-learning account", - "access": "read", - "domain": "linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "public_id", - "plain_id", - "name" - ], - "type": "js", - "modulePath": "linkedin-learning/auth.js", - "sourceFile": "linkedin-learning/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "connectors", - "description": "List available Manus connectors (integrations).", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max connectors to return" - } - ], - "columns": [ - "UID", - "Name", - "Brief" - ], - "type": "js", - "modulePath": "manus/connectors.js", - "sourceFile": "manus/connectors.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "credits", - "description": "Show Manus credit balance and refresh details.", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "manus/credits.js", - "sourceFile": "manus/credits.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "list", - "description": "List Manus sessions (tasks).", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max sessions to return" - }, - { - "name": "archived", - "type": "bool", - "default": false, - "required": false, - "help": "Include archived sessions" - } - ], - "columns": [ - "id", - "Title", - "Status", - "Last Message", - "Last Updated", - "Credits" - ], - "type": "js", - "modulePath": "manus/list.js", - "sourceFile": "manus/list.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "login", - "description": "Open manus login", - "access": "write", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "manus/auth.js", - "sourceFile": "manus/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "read", - "description": "Show details for a specific Manus session.", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "uid", - "type": "str", - "required": true, - "positional": true, - "help": "Session UID" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "manus/read.js", - "sourceFile": "manus/read.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "skills", - "description": "List Manus skills (user-added and system).", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "ID", - "Name", - "Description", - "Source" - ], - "type": "js", - "modulePath": "manus/skills.js", - "sourceFile": "manus/skills.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "status", - "description": "Show current Manus user profile and credit summary.", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "manus/status.js", - "sourceFile": "manus/status.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "whoami", - "description": "Show the current logged-in manus account", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "manus/auth.js", - "sourceFile": "manus/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, { "site": "mercury", "name": "check-login", diff --git a/plugin-command-manifest.json b/plugin-command-manifest.json index cf9afd70..15317902 100644 --- a/plugin-command-manifest.json +++ b/plugin-command-manifest.json @@ -1,1766 +1,2837 @@ [ { - "site": "apple-podcasts", - "name": "episodes", - "description": "List recent episodes of an Apple Podcast (use ID from search)", + "site": "amazon", + "name": "bestsellers", + "description": "Amazon Best Sellers pages for category candidate discovery", "access": "read", - "strategy": "public", - "browser": false, + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "input", "type": "str", - "required": true, + "required": false, "positional": true, - "help": "Podcast ID (collectionId from search output)" + "help": "Ranking URL or supported Amazon path. Omit to use the list root." }, { "name": "limit", "type": "int", - "default": 15, + "default": 100, "required": false, - "help": "Max episodes to show" + "help": "Maximum number of ranked items to return (default 100)" } ], "columns": [ + "list_type", + "rank", + "asin", "title", - "duration", - "date" + "price_text", + "rating_value", + "review_count" ], "type": "js", - "modulePath": "plugins/apple-podcasts/episodes.js", - "sourceFile": "plugins/apple-podcasts/episodes.js" + "modulePath": "plugins/amazon/bestsellers.js", + "sourceFile": "plugins/amazon/bestsellers.js", + "navigateBefore": false }, { - "site": "apple-podcasts", - "name": "search", - "description": "Search Apple Podcasts", + "site": "amazon", + "name": "discussion", + "description": "Amazon review summary and sample customer discussion from product review pages", "access": "read", - "strategy": "public", - "browser": false, + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "input", "type": "str", "required": true, "positional": true, - "help": "Search keyword" + "help": "ASIN or product URL, for example B0FJS72893" }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Max results" + "help": "Maximum number of review samples to return (default 10)" } ], "columns": [ - "id", - "title", - "author", - "episodes", - "genre", - "url" - ], - "tags": [ - "search" + "asin", + "average_rating_value", + "total_review_count" ], "type": "js", - "modulePath": "plugins/apple-podcasts/search.js", - "sourceFile": "plugins/apple-podcasts/search.js" + "modulePath": "plugins/amazon/discussion.js", + "sourceFile": "plugins/amazon/discussion.js", + "navigateBefore": false }, { - "site": "apple-podcasts", - "name": "top", - "description": "Top podcasts chart on Apple Podcasts", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of podcasts (max 100)" - }, - { - "name": "country", - "type": "str", - "default": "us", - "required": false, - "help": "Country code (e.g. us, cn, gb, jp)" - } - ], + "site": "amazon", + "name": "login", + "description": "Open amazon login", + "access": "write", + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "title", - "author", - "id" + "status", + "logged_in", + "site", + "user_name", + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/apple-podcasts/top.js", - "sourceFile": "plugins/apple-podcasts/top.js" + "modulePath": "plugins/amazon/auth.js", + "sourceFile": "plugins/amazon/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "archive", - "name": "item", - "description": "Fetch metadata for a single Internet Archive item by identifier.", + "site": "amazon", + "name": "movers-shakers", + "description": "Amazon Movers & Shakers pages for short-term growth signals", "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "identifier", + "name": "input", "type": "str", - "required": true, + "required": false, "positional": true, - "help": "Archive item identifier (e.g. \"open-syllabus\", \"FinalFantasy2_356\")." + "help": "Ranking URL or supported Amazon path. Omit to use the list root." + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Maximum number of ranked items to return (default 100)" } ], "columns": [ - "identifier", + "list_type", + "rank", + "asin", "title", - "creator", - "date", - "mediatype", - "collection", - "description", - "file_count", - "url" + "price_text", + "rating_value", + "review_count" ], "type": "js", - "modulePath": "plugins/archive/item.js", - "sourceFile": "plugins/archive/item.js" + "modulePath": "plugins/amazon/movers-shakers.js", + "sourceFile": "plugins/amazon/movers-shakers.js", + "navigateBefore": false }, { - "site": "archive", - "name": "search", - "description": "Search Internet Archive items across books, movies, audio, software, and web.", + "site": "amazon", + "name": "new-releases", + "description": "Amazon New Releases pages for early momentum discovery", "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "input", "type": "str", - "required": true, - "positional": true, - "help": "Full-text query (matches title, description, creator, subject)." - }, - { - "name": "mediatype", - "type": "string", - "required": false, - "help": "Restrict to mediatype: texts, movies, audio, software, image, web, data, collection" - }, - { - "name": "sort", - "type": "string", - "default": "downloads", "required": false, - "help": "Sort key: downloads, date, addeddate, week, title" + "positional": true, + "help": "Ranking URL or supported Amazon path. Omit to use the list root." }, { "name": "limit", "type": "int", - "default": 20, + "default": 100, "required": false, - "help": "Max items (max 100; one API page)." + "help": "Maximum number of ranked items to return (default 100)" } ], "columns": [ + "list_type", "rank", - "identifier", + "asin", "title", - "creator", - "date", - "mediatype", - "downloads", - "url" - ], - "tags": [ - "search" + "price_text", + "rating_value", + "review_count" ], "type": "js", - "modulePath": "plugins/archive/search.js", - "sourceFile": "plugins/archive/search.js" + "modulePath": "plugins/amazon/new-releases.js", + "sourceFile": "plugins/amazon/new-releases.js", + "navigateBefore": false }, { - "site": "archive", - "name": "snapshots", - "description": "List Wayback Machine snapshots over time for a URL via the CDX API.", + "site": "amazon", + "name": "offer", + "description": "Amazon seller, buy box, and fulfillment facts from the product page", "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "url", + "name": "input", "type": "str", "required": true, "positional": true, - "help": "URL to look up (with or without scheme)." - }, - { - "name": "from", - "type": "string", - "required": false, - "help": "Earliest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" - }, - { - "name": "to", - "type": "string", - "required": false, - "help": "Latest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max snapshots to return (max 1000)." + "help": "ASIN or product URL, for example B0FJS72893" } ], "columns": [ - "timestamp", - "snapshot_url", - "status", - "mimetype", - "original_url" + "asin", + "price_text", + "sold_by", + "ships_from", + "is_amazon_sold", + "is_amazon_fulfilled" ], "type": "js", - "modulePath": "plugins/archive/snapshots.js", - "sourceFile": "plugins/archive/snapshots.js" + "modulePath": "plugins/amazon/offer.js", + "sourceFile": "plugins/amazon/offer.js", + "navigateBefore": false }, { - "site": "archive", - "name": "wayback", - "description": "Look up the closest Wayback Machine snapshot for a URL.", + "site": "amazon", + "name": "product", + "description": "Amazon product page facts for candidate validation", "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "url", + "name": "input", "type": "str", "required": true, "positional": true, - "help": "URL to look up (with or without scheme)." - }, - { - "name": "timestamp", - "type": "string", - "required": false, - "help": "Target timestamp (YYYY[MM[DD[hh[mm[ss]]]]] or ISO date). Defaults to most recent snapshot." + "help": "ASIN or product URL, for example B0FJS72893" } ], "columns": [ - "original_url", - "requested_timestamp", - "snapshot_timestamp", - "snapshot_url", - "status" + "asin", + "title", + "price_text", + "rating_value", + "review_count" ], "type": "js", - "modulePath": "plugins/archive/wayback.js", - "sourceFile": "plugins/archive/wayback.js" + "modulePath": "plugins/amazon/product.js", + "sourceFile": "plugins/amazon/product.js", + "navigateBefore": false }, { - "site": "arxiv", - "name": "author", - "description": "List arXiv papers by a given author (newest first)", + "site": "amazon", + "name": "search", + "description": "Amazon search results for product discovery and coarse filtering", "access": "read", - "strategy": "public", - "browser": false, + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "author", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Author name (e.g. \"Yoshua Bengio\" or \"Y Bengio\")" + "help": "Search query, for example \"desk shelf organizer\"" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max papers to return (max 50)" + "help": "Maximum number of results to return (default 20)" } ], "columns": [ - "id", + "rank", + "asin", "title", - "authors", - "published", - "primary_category", - "url" + "price_text", + "rating_value", + "review_count" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/arxiv/author.js", - "sourceFile": "plugins/arxiv/author.js" + "modulePath": "plugins/amazon/search.js", + "sourceFile": "plugins/amazon/search.js", + "navigateBefore": false }, { - "site": "arxiv", - "name": "paper", - "description": "Get arXiv paper details by ID", + "site": "amazon", + "name": "whoami", + "description": "Show the current logged-in amazon account", "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, + "domain": "amazon.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_name" + ], + "type": "js", + "modulePath": "plugins/amazon/auth.js", + "sourceFile": "plugins/amazon/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "amazon-in", + "name": "checkout", + "description": "Prepare a guarded Amazon.in checkout with browser-only payment handoff", + "access": "write", + "domain": "amazon.in", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": true, "positional": true, - "help": "arXiv paper ID (e.g. 1706.03762)" + "help": "Amazon.in product URL or ASIN" + }, + { + "name": "quantity", + "type": "int", + "default": 1, + "required": false, + "help": "Quantity (1-10)" + }, + { + "name": "size", + "type": "str", + "required": false, + "help": "Exact visible size label" + }, + { + "name": "colour", + "type": "str", + "required": false, + "help": "Exact visible colour label" + }, + { + "name": "payment", + "type": "str", + "required": true, + "help": "Payment method; secrets remain browser-only", + "choices": [ + "upi", + "saved-card", + "new-card", + "cod" + ] + }, + { + "name": "card-last4", + "type": "str", + "required": false, + "help": "Saved-card selector: exactly four digits" + }, + { + "name": "place-order", + "type": "boolean", + "default": false, + "required": false, + "help": "Submit the final Amazon action once" } ], "columns": [ - "id", + "status", + "asin", "title", - "authors", - "published", - "updated", - "primary_category", - "categories", - "abstract", - "comment", - "pdf", - "url" + "size", + "colour", + "quantity", + "item_price", + "total", + "payment_method", + "delivery_date", + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/arxiv/paper.js", - "sourceFile": "plugins/arxiv/paper.js" + "modulePath": "plugins/amazon-in/checkout.js", + "sourceFile": "plugins/amazon-in/checkout.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true }, { - "site": "arxiv", - "name": "recent", - "description": "List recent arXiv submissions in a category", + "site": "amazon-in", + "name": "checkout-status", + "description": "Read the current Amazon.in checkout or payment state without clicking", "access": "read", - "strategy": "public", - "browser": false, + "domain": "amazon.in", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "status", + "order_id", + "total", + "payment_method", + "action" + ], + "type": "js", + "modulePath": "plugins/amazon-in/checkout-status.js", + "sourceFile": "plugins/amazon-in/checkout-status.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "amazon-in", + "name": "login", + "description": "Open amazon-in login", + "access": "write", + "domain": "amazon.in", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/amazon-in/auth.js", + "sourceFile": "plugins/amazon-in/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "amazon-in", + "name": "product", + "description": "Fetch the current Amazon.in price and selected product variant", + "access": "read", + "domain": "amazon.in", + "strategy": "ui", + "browser": true, "args": [ { - "name": "category", + "name": "input", "type": "str", "required": true, "positional": true, - "help": "arXiv category (e.g. cs.CL, cs.LG, math.PR, q-bio.NC)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max results (max 50)" + "help": "Amazon.in product URL or ASIN" } ], "columns": [ - "id", + "asin", "title", - "authors", - "published", - "primary_category", - "url" + "price", + "mrp", + "discount", + "availability", + "size", + "colour", + "image_url", + "product_url" ], "type": "js", - "modulePath": "plugins/arxiv/recent.js", - "sourceFile": "plugins/arxiv/recent.js" + "modulePath": "plugins/amazon-in/product.js", + "sourceFile": "plugins/amazon-in/product.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "arxiv", + "site": "amazon-in", "name": "search", - "description": "Search arXiv papers", + "description": "Search Amazon.in products with inclusive INR price bounds and images", "access": "read", - "strategy": "public", - "browser": false, + "domain": "amazon.in", + "strategy": "ui", + "browser": true, "args": [ { "name": "query", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"attention is all you need\")" + "help": "Product search query" + }, + { + "name": "min-price", + "type": "number", + "required": false, + "help": "Inclusive minimum price in rupees" + }, + { + "name": "max-price", + "type": "number", + "required": false, + "help": "Inclusive maximum price in rupees" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Max results (max 25)" + "help": "Maximum results (1-50)" } ], "columns": [ - "id", + "rank", + "asin", "title", - "authors", - "published", - "primary_category", - "url" + "price", + "mrp", + "rating", + "review_count", + "image_url", + "product_url", + "is_sponsored" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/arxiv/search.js", - "sourceFile": "plugins/arxiv/search.js" + "modulePath": "plugins/amazon-in/search.js", + "sourceFile": "plugins/amazon-in/search.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "barchart", - "name": "flow", - "description": "Barchart unusual options activity / options flow", + "site": "amazon-in", + "name": "whoami", + "description": "Show the current logged-in amazon-in account", "access": "read", - "domain": "www.barchart.com", + "domain": "amazon.in", "strategy": "cookie", "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_name" + ], + "type": "js", + "modulePath": "plugins/amazon-in/auth.js", + "sourceFile": "plugins/amazon-in/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "amazon-in", + "name": "wishlist", + "description": "Fetch current prices for products in the default Amazon.in wishlist", + "access": "read", + "domain": "amazon.in", + "strategy": "ui", + "browser": true, "args": [ { - "name": "type", + "name": "filter", "type": "str", - "default": "all", + "default": "unpurchased", "required": false, - "help": "Filter: all, call, or put", + "help": "Wishlist items to include", "choices": [ - "all", - "call", - "put" + "unpurchased", + "all" ] + } + ], + "columns": [ + "list_name", + "item_id", + "asin", + "title", + "price", + "mrp", + "availability", + "size", + "colour", + "image_url", + "product_url" + ], + "type": "js", + "modulePath": "plugins/amazon-in/wishlist.js", + "sourceFile": "plugins/amazon-in/wishlist.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "apple-podcasts", + "name": "episodes", + "description": "List recent episodes of an Apple Podcast (use ID from search)", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Podcast ID (collectionId from search output)" }, { "name": "limit", "type": "int", - "default": 20, + "default": 15, "required": false, - "help": "Number of results" + "help": "Max episodes to show" } ], "columns": [ - "symbol", - "type", - "strike", - "expiration", - "last", - "volume", - "openInterest", - "volOiRatio", - "iv" + "title", + "duration", + "date" ], "type": "js", - "modulePath": "plugins/barchart/flow.js", - "sourceFile": "plugins/barchart/flow.js", - "navigateBefore": "https://www.barchart.com" + "modulePath": "plugins/apple-podcasts/episodes.js", + "sourceFile": "plugins/apple-podcasts/episodes.js" }, { - "site": "barchart", - "name": "greeks", - "description": "Barchart options greeks overview (IV, delta, gamma, theta, vega)", + "site": "apple-podcasts", + "name": "search", + "description": "Search Apple Podcasts", "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", - "browser": true, + "strategy": "public", + "browser": false, "args": [ { - "name": "symbol", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Stock ticker (e.g. AAPL)" - }, - { - "name": "expiration", - "type": "str", - "required": false, - "help": "Expiration date (YYYY-MM-DD). Defaults to the nearest available expiration." + "help": "Search keyword" }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Number of near-the-money strikes per type (1-100)" + "help": "Max results" } ], "columns": [ - "type", - "strike", - "last", - "iv", - "delta", - "gamma", - "theta", - "vega", - "rho", - "volume", - "openInterest", - "expiration" + "id", + "title", + "author", + "episodes", + "genre", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/barchart/greeks.js", - "sourceFile": "plugins/barchart/greeks.js", - "navigateBefore": "https://www.barchart.com" + "modulePath": "plugins/apple-podcasts/search.js", + "sourceFile": "plugins/apple-podcasts/search.js" }, { - "site": "barchart", - "name": "options", - "description": "Barchart options chain with greeks, IV, volume, and open interest", + "site": "apple-podcasts", + "name": "top", + "description": "Top podcasts chart on Apple Podcasts", "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", - "browser": true, + "strategy": "public", + "browser": false, "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Stock ticker (e.g. AAPL)" - }, - { - "name": "type", - "type": "str", - "default": "Call", - "required": false, - "help": "Option type: Call or Put", - "choices": [ - "Call", - "Put" - ] - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max number of strikes to return" + "help": "Number of podcasts (max 100)" + }, + { + "name": "country", + "type": "str", + "default": "us", + "required": false, + "help": "Country code (e.g. us, cn, gb, jp)" } ], "columns": [ - "strike", - "bid", - "ask", - "last", - "change", - "volume", - "openInterest", - "iv", - "delta", - "gamma", - "theta", - "vega", - "expiration" + "rank", + "title", + "author", + "id" ], "type": "js", - "modulePath": "plugins/barchart/options.js", - "sourceFile": "plugins/barchart/options.js", - "navigateBefore": "https://www.barchart.com" + "modulePath": "plugins/apple-podcasts/top.js", + "sourceFile": "plugins/apple-podcasts/top.js" }, { - "site": "barchart", - "name": "quote", - "description": "Barchart stock quote with price, volume, and key metrics", + "site": "archive", + "name": "item", + "description": "Fetch metadata for a single Internet Archive item by identifier.", "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", - "browser": true, + "domain": "archive.org", + "strategy": "public", + "browser": false, "args": [ { - "name": "symbol", + "name": "identifier", "type": "str", "required": true, "positional": true, - "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" + "help": "Archive item identifier (e.g. \"open-syllabus\", \"FinalFantasy2_356\")." } ], "columns": [ - "symbol", - "name", - "price", - "change", - "changePct", - "open", - "high", - "low", - "prevClose", - "volume", - "avgVolume", - "marketCap", - "peRatio", - "eps" + "identifier", + "title", + "creator", + "date", + "mediatype", + "collection", + "description", + "file_count", + "url" ], "type": "js", - "modulePath": "plugins/barchart/quote.js", - "sourceFile": "plugins/barchart/quote.js", - "navigateBefore": "https://www.barchart.com" + "modulePath": "plugins/archive/item.js", + "sourceFile": "plugins/archive/item.js" }, { - "site": "bbc", - "name": "news", - "description": "BBC News headlines (RSS)", + "site": "archive", + "name": "search", + "description": "Search Internet Archive items across books, movies, audio, software, and web.", "access": "read", - "domain": "www.bbc.com", + "domain": "archive.org", "strategy": "public", "browser": false, "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Full-text query (matches title, description, creator, subject)." + }, + { + "name": "mediatype", + "type": "string", + "required": false, + "help": "Restrict to mediatype: texts, movies, audio, software, image, web, data, collection" + }, + { + "name": "sort", + "type": "string", + "default": "downloads", + "required": false, + "help": "Sort key: downloads, date, addeddate, week, title" + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of headlines (max 50)" + "help": "Max items (max 100; one API page)." } ], "columns": [ "rank", + "identifier", "title", - "description", + "creator", + "date", + "mediatype", + "downloads", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/bbc/news.js", - "sourceFile": "plugins/bbc/news.js" + "modulePath": "plugins/archive/search.js", + "sourceFile": "plugins/archive/search.js" }, { - "site": "bbc", - "name": "topic", - "description": "BBC News headlines for a specific section (RSS feed)", + "site": "archive", + "name": "snapshots", + "description": "List Wayback Machine snapshots over time for a URL via the CDX API.", "access": "read", - "domain": "www.bbc.com", + "domain": "archive.org", "strategy": "public", "browser": false, "args": [ { - "name": "topic", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Section name (world / business / politics / health / education / science_and_environment / technology / entertainment_and_arts)" + "help": "URL to look up (with or without scheme)." + }, + { + "name": "from", + "type": "string", + "required": false, + "help": "Earliest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" + }, + { + "name": "to", + "type": "string", + "required": false, + "help": "Latest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max headlines (1-50)" + "help": "Max snapshots to return (max 1000)." } ], "columns": [ - "rank", - "title", - "description", - "pubDate", - "url" + "timestamp", + "snapshot_url", + "status", + "mimetype", + "original_url" ], "type": "js", - "modulePath": "plugins/bbc/topic.js", - "sourceFile": "plugins/bbc/topic.js" + "modulePath": "plugins/archive/snapshots.js", + "sourceFile": "plugins/archive/snapshots.js" }, { - "site": "binance", - "name": "asks", - "description": "Order book ask prices for a trading pair", + "site": "archive", + "name": "wayback", + "description": "Look up the closest Wayback Machine snapshot for a URL.", "access": "read", - "domain": "data-api.binance.vision", + "domain": "archive.org", "strategy": "public", "browser": false, "args": [ { - "name": "symbol", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + "help": "URL to look up (with or without scheme)." }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "timestamp", + "type": "string", "required": false, - "help": "Number of price levels (5, 10, 20, 50, 100)" + "help": "Target timestamp (YYYY[MM[DD[hh[mm[ss]]]]] or ISO date). Defaults to most recent snapshot." } ], "columns": [ - "rank", - "ask_price", - "ask_qty" + "original_url", + "requested_timestamp", + "snapshot_timestamp", + "snapshot_url", + "status" ], "type": "js", - "modulePath": "plugins/binance/asks.js", - "sourceFile": "plugins/binance/asks.js" + "modulePath": "plugins/archive/wayback.js", + "sourceFile": "plugins/archive/wayback.js" }, { - "site": "binance", - "name": "depth", - "description": "Order book bid and ask prices for a trading pair", + "site": "arxiv", + "name": "author", + "description": "List arXiv papers by a given author (newest first)", "access": "read", - "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { - "name": "symbol", + "name": "author", "type": "str", "required": true, "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + "help": "Author name (e.g. \"Yoshua Bengio\" or \"Y Bengio\")" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of price levels (5, 10, 20, 50, 100)" + "help": "Max papers to return (max 50)" } ], "columns": [ - "rank", - "bid_price", - "bid_qty", - "ask_price", - "ask_qty" + "id", + "title", + "authors", + "published", + "primary_category", + "url" ], "type": "js", - "modulePath": "plugins/binance/depth.js", - "sourceFile": "plugins/binance/depth.js" + "modulePath": "plugins/arxiv/author.js", + "sourceFile": "plugins/arxiv/author.js" }, { - "site": "binance", - "name": "gainers", - "description": "Top gaining trading pairs by 24h price change", + "site": "arxiv", + "name": "paper", + "description": "Get arXiv paper details by ID", "access": "read", - "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of trading pairs" + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "arXiv paper ID (e.g. 1706.03762)" } ], "columns": [ - "rank", - "symbol", - "price", - "change_24h", - "volume" + "id", + "title", + "authors", + "published", + "updated", + "primary_category", + "categories", + "abstract", + "comment", + "pdf", + "url" ], "type": "js", - "modulePath": "plugins/binance/gainers.js", - "sourceFile": "plugins/binance/gainers.js" + "modulePath": "plugins/arxiv/paper.js", + "sourceFile": "plugins/arxiv/paper.js" }, { - "site": "binance", - "name": "klines", - "description": "Candlestick/kline data for a trading pair", + "site": "arxiv", + "name": "recent", + "description": "List recent arXiv submissions in a category", "access": "read", - "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { - "name": "symbol", + "name": "category", "type": "str", "required": true, "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" - }, - { - "name": "interval", - "type": "str", - "default": "1d", - "required": false, - "help": "Kline interval (1m, 5m, 15m, 1h, 4h, 1d, 1w, 1M)" + "help": "arXiv category (e.g. cs.CL, cs.LG, math.PR, q-bio.NC)" }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Number of klines (max 1000)" + "help": "Max results (max 50)" } ], "columns": [ - "open", - "high", - "low", - "close", - "volume" + "id", + "title", + "authors", + "published", + "primary_category", + "url" ], "type": "js", - "modulePath": "plugins/binance/klines.js", - "sourceFile": "plugins/binance/klines.js" + "modulePath": "plugins/arxiv/recent.js", + "sourceFile": "plugins/arxiv/recent.js" }, { - "site": "binance", - "name": "losers", - "description": "Top losing trading pairs by 24h price change", + "site": "arxiv", + "name": "search", + "description": "Search arXiv papers", "access": "read", - "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"attention is all you need\")" + }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Number of trading pairs" + "help": "Max results (max 25)" } ], "columns": [ - "rank", - "symbol", - "price", - "change_24h", - "volume" + "id", + "title", + "authors", + "published", + "primary_category", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/binance/losers.js", - "sourceFile": "plugins/binance/losers.js" + "modulePath": "plugins/arxiv/search.js", + "sourceFile": "plugins/arxiv/search.js" }, { - "site": "binance", - "name": "pairs", - "description": "List active trading pairs on Binance", + "site": "band", + "name": "bands", + "description": "List all Bands you belong to", "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, + "domain": "www.band.us", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "band_no", + "name", + "members" + ], + "type": "js", + "modulePath": "plugins/band/bands.js", + "sourceFile": "plugins/band/bands.js", + "navigateBefore": "https://www.band.us" + }, + { + "site": "band", + "name": "login", + "description": "Open band login", + "access": "write", + "domain": "band.us", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/band/auth.js", + "sourceFile": "plugins/band/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "band", + "name": "mentions", + "description": "Show Band notifications where you are @mentioned", + "access": "read", + "domain": "www.band.us", + "strategy": "intercept", + "browser": true, "args": [ + { + "name": "filter", + "type": "str", + "default": "mentioned", + "required": false, + "help": "Filter: mentioned (default) | all | post | comment", + "choices": [ + "mentioned", + "all", + "post", + "comment" + ] + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of trading pairs" + "help": "Max results" + }, + { + "name": "unread", + "type": "bool", + "default": false, + "required": false, + "help": "Show only unread notifications" } ], "columns": [ - "symbol", - "base", - "quote", - "status" + "time", + "band", + "type", + "from", + "text", + "url" ], "type": "js", - "modulePath": "plugins/binance/pairs.js", - "sourceFile": "plugins/binance/pairs.js" + "modulePath": "plugins/band/mentions.js", + "sourceFile": "plugins/band/mentions.js", + "navigateBefore": true }, { - "site": "binance", - "name": "price", - "description": "Quick price check for a trading pair", + "site": "band", + "name": "post", + "description": "Export full content of a post including comments", "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, + "domain": "www.band.us", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "symbol", - "type": "str", + "name": "band_no", + "type": "int", "required": true, "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + "help": "Band number" + }, + { + "name": "post_no", + "type": "int", + "required": true, + "positional": true, + "help": "Post number" + }, + { + "name": "output", + "type": "str", + "default": "", + "required": false, + "help": "Directory to save attached photos" + }, + { + "name": "comments", + "type": "bool", + "default": true, + "required": false, + "help": "Include comments (default: true)" } ], "columns": [ - "symbol", - "price", - "change", - "change_pct", - "high", - "low", - "volume", - "quote_volume", - "trades" + "type", + "author", + "date", + "text" ], "type": "js", - "modulePath": "plugins/binance/price.js", - "sourceFile": "plugins/binance/price.js" + "modulePath": "plugins/band/post.js", + "sourceFile": "plugins/band/post.js", + "navigateBefore": false }, { - "site": "binance", - "name": "prices", - "description": "Latest prices for all trading pairs", + "site": "band", + "name": "posts", + "description": "List posts from a Band", "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, + "domain": "www.band.us", + "strategy": "cookie", + "browser": true, "args": [ + { + "name": "band_no", + "type": "int", + "required": true, + "positional": true, + "help": "Band number (get it from: band bands)" + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of prices" + "help": "Max results" } ], "columns": [ - "rank", - "symbol", - "price" + "date", + "author", + "content", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/binance/prices.js", - "sourceFile": "plugins/binance/prices.js" + "modulePath": "plugins/band/posts.js", + "sourceFile": "plugins/band/posts.js", + "navigateBefore": false }, { - "site": "binance", - "name": "ticker", - "description": "24h ticker statistics for top trading pairs by volume", + "site": "band", + "name": "whoami", + "description": "Show the current logged-in band account", "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, + "domain": "band.us", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id" + ], + "type": "js", + "modulePath": "plugins/band/auth.js", + "sourceFile": "plugins/band/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "barchart", + "name": "flow", + "description": "Barchart unusual options activity / options flow", + "access": "read", + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, "args": [ + { + "name": "type", + "type": "str", + "default": "all", + "required": false, + "help": "Filter: all, call, or put", + "choices": [ + "all", + "call", + "put" + ] + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of tickers" + "help": "Number of results" } ], "columns": [ "symbol", - "price", - "change_pct", - "high", - "low", + "type", + "strike", + "expiration", + "last", "volume", - "quote_vol", - "trades" + "openInterest", + "volOiRatio", + "iv" ], "type": "js", - "modulePath": "plugins/binance/ticker.js", - "sourceFile": "plugins/binance/ticker.js" + "modulePath": "plugins/barchart/flow.js", + "sourceFile": "plugins/barchart/flow.js", + "navigateBefore": "https://www.barchart.com" }, { - "site": "binance", - "name": "top", - "description": "Top trading pairs by 24h volume on Binance", + "site": "barchart", + "name": "greeks", + "description": "Barchart options greeks overview (IV, delta, gamma, theta, vega)", "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Stock ticker (e.g. AAPL)" + }, + { + "name": "expiration", + "type": "str", + "required": false, + "help": "Expiration date (YYYY-MM-DD). Defaults to the nearest available expiration." + }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Number of trading pairs" + "help": "Number of near-the-money strikes per type (1-100)" } ], "columns": [ - "rank", - "symbol", - "price", - "change_24h", - "high", - "low", - "volume" + "type", + "strike", + "last", + "iv", + "delta", + "gamma", + "theta", + "vega", + "rho", + "volume", + "openInterest", + "expiration" ], "type": "js", - "modulePath": "plugins/binance/top.js", - "sourceFile": "plugins/binance/top.js" + "modulePath": "plugins/barchart/greeks.js", + "sourceFile": "plugins/barchart/greeks.js", + "navigateBefore": "https://www.barchart.com" }, { - "site": "binance", - "name": "trades", - "description": "Recent trades for a trading pair", + "site": "barchart", + "name": "options", + "description": "Barchart options chain with greeks, IV, volume, and open interest", "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "symbol", "type": "str", "required": true, "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + "help": "Stock ticker (e.g. AAPL)" + }, + { + "name": "type", + "type": "str", + "default": "Call", + "required": false, + "help": "Option type: Call or Put", + "choices": [ + "Call", + "Put" + ] }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of trades (max 1000)" + "help": "Max number of strikes to return" } ], "columns": [ - "id", - "price", - "qty", - "quote_qty", - "buyer_maker" + "strike", + "bid", + "ask", + "last", + "change", + "volume", + "openInterest", + "iv", + "delta", + "gamma", + "theta", + "vega", + "expiration" ], "type": "js", - "modulePath": "plugins/binance/trades.js", - "sourceFile": "plugins/binance/trades.js" + "modulePath": "plugins/barchart/options.js", + "sourceFile": "plugins/barchart/options.js", + "navigateBefore": "https://www.barchart.com" }, { - "site": "bloomberg", - "name": "businessweek", - "description": "Bloomberg Businessweek top stories", + "site": "barchart", + "name": "quote", + "description": "Barchart stock quote with price, volume, and key metrics", "access": "read", - "domain": "www.bloomberg.com", - "strategy": "public", + "domain": "www.barchart.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of stories to return (max 20)" + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "symbol", + "name", + "price", + "change", + "changePct", + "open", + "high", + "low", + "prevClose", + "volume", + "avgVolume", + "marketCap", + "peRatio", + "eps" ], "type": "js", - "modulePath": "plugins/bloomberg/businessweek.js", - "sourceFile": "plugins/bloomberg/businessweek.js" + "modulePath": "plugins/barchart/quote.js", + "sourceFile": "plugins/barchart/quote.js", + "navigateBefore": "https://www.barchart.com" }, { - "site": "bloomberg", - "name": "crypto", - "description": "Bloomberg Crypto top stories (RSS)", + "site": "bbc", + "name": "news", + "description": "BBC News headlines (RSS)", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "www.bbc.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of headlines (max 50)" } ], "columns": [ + "rank", "title", - "summary", - "link", - "mediaLinks" + "description", + "url" ], "type": "js", - "modulePath": "plugins/bloomberg/crypto.js", - "sourceFile": "plugins/bloomberg/crypto.js" + "modulePath": "plugins/bbc/news.js", + "sourceFile": "plugins/bbc/news.js" }, { - "site": "bloomberg", - "name": "economics", - "description": "Bloomberg Economics top stories (RSS)", + "site": "bbc", + "name": "topic", + "description": "BBC News headlines for a specific section (RSS feed)", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "www.bbc.com", "strategy": "public", "browser": false, "args": [ + { + "name": "topic", + "type": "str", + "required": true, + "positional": true, + "help": "Section name (world / business / politics / health / education / science_and_environment / technology / entertainment_and_arts)" + }, { "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Max headlines (1-50)" } ], "columns": [ + "rank", "title", - "summary", - "link", - "mediaLinks" + "description", + "pubDate", + "url" ], "type": "js", - "modulePath": "plugins/bloomberg/economics.js", - "sourceFile": "plugins/bloomberg/economics.js" + "modulePath": "plugins/bbc/topic.js", + "sourceFile": "plugins/bbc/topic.js" }, { - "site": "bloomberg", - "name": "feeds", - "description": "List the Bloomberg RSS feed aliases used by the adapter", + "site": "binance", + "name": "asks", + "description": "Order book ask prices for a trading pair", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, - "args": [], + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of price levels (5, 10, 20, 50, 100)" + } + ], "columns": [ - "name", - "url" + "rank", + "ask_price", + "ask_qty" ], "type": "js", - "modulePath": "plugins/bloomberg/feeds.js", - "sourceFile": "plugins/bloomberg/feeds.js" + "modulePath": "plugins/binance/asks.js", + "sourceFile": "plugins/binance/asks.js" }, { - "site": "bloomberg", - "name": "green", - "description": "Bloomberg Green (climate & energy) top stories (RSS)", + "site": "binance", + "name": "depth", + "description": "Order book bid and ask prices for a trading pair", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, { "name": "limit", "type": "int", - "default": 1, + "default": 10, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of price levels (5, 10, 20, 50, 100)" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "rank", + "bid_price", + "bid_qty", + "ask_price", + "ask_qty" ], "type": "js", - "modulePath": "plugins/bloomberg/green.js", - "sourceFile": "plugins/bloomberg/green.js" + "modulePath": "plugins/binance/depth.js", + "sourceFile": "plugins/binance/depth.js" }, { - "site": "bloomberg", - "name": "industries", - "description": "Bloomberg Industries top stories (RSS)", + "site": "binance", + "name": "gainers", + "description": "Top gaining trading pairs by 24h price change", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 1, + "default": 10, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of trading pairs" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "rank", + "symbol", + "price", + "change_24h", + "volume" ], "type": "js", - "modulePath": "plugins/bloomberg/industries.js", - "sourceFile": "plugins/bloomberg/industries.js" + "modulePath": "plugins/binance/gainers.js", + "sourceFile": "plugins/binance/gainers.js" }, { - "site": "bloomberg", - "name": "main", - "description": "Bloomberg homepage top stories (RSS)", + "site": "binance", + "name": "klines", + "description": "Candlestick/kline data for a trading pair", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, + { + "name": "interval", + "type": "str", + "default": "1d", + "required": false, + "help": "Kline interval (1m, 5m, 15m, 1h, 4h, 1d, 1w, 1M)" + }, { "name": "limit", "type": "int", - "default": 1, + "default": 10, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of klines (max 1000)" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "open", + "high", + "low", + "close", + "volume" ], "type": "js", - "modulePath": "plugins/bloomberg/main.js", - "sourceFile": "plugins/bloomberg/main.js" + "modulePath": "plugins/binance/klines.js", + "sourceFile": "plugins/binance/klines.js" }, { - "site": "bloomberg", - "name": "markets", - "description": "Bloomberg Markets top stories (RSS)", + "site": "binance", + "name": "losers", + "description": "Top losing trading pairs by 24h price change", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 1, + "default": 10, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of trading pairs" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "rank", + "symbol", + "price", + "change_24h", + "volume" ], "type": "js", - "modulePath": "plugins/bloomberg/markets.js", - "sourceFile": "plugins/bloomberg/markets.js" + "modulePath": "plugins/binance/losers.js", + "sourceFile": "plugins/binance/losers.js" }, { - "site": "bloomberg", - "name": "news", - "description": "Read a Bloomberg story/article page and return title, full content, and media links", + "site": "binance", + "name": "pairs", + "description": "List active trading pairs on Binance", "access": "read", - "domain": "www.bloomberg.com", - "strategy": "cookie", - "browser": true, + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, "args": [ { - "name": "link", - "type": "str", - "required": true, - "positional": true, - "help": "Bloomberg story/article URL or relative Bloomberg path" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of trading pairs" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks", - "content" + "symbol", + "base", + "quote", + "status" ], "type": "js", - "modulePath": "plugins/bloomberg/news.js", - "sourceFile": "plugins/bloomberg/news.js", - "navigateBefore": "https://www.bloomberg.com" + "modulePath": "plugins/binance/pairs.js", + "sourceFile": "plugins/binance/pairs.js" }, { - "site": "bloomberg", - "name": "opinions", - "description": "Bloomberg Opinion top stories (RSS)", + "site": "binance", + "name": "price", + "description": "Quick price check for a trading pair", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "symbol", + "price", + "change", + "change_pct", + "high", + "low", + "volume", + "quote_volume", + "trades" ], "type": "js", - "modulePath": "plugins/bloomberg/opinions.js", - "sourceFile": "plugins/bloomberg/opinions.js" + "modulePath": "plugins/binance/price.js", + "sourceFile": "plugins/binance/price.js" }, { - "site": "bloomberg", - "name": "politics", - "description": "Bloomberg Politics top stories (RSS)", + "site": "binance", + "name": "prices", + "description": "Latest prices for all trading pairs", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of prices" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "rank", + "symbol", + "price" ], "type": "js", - "modulePath": "plugins/bloomberg/politics.js", - "sourceFile": "plugins/bloomberg/politics.js" + "modulePath": "plugins/binance/prices.js", + "sourceFile": "plugins/binance/prices.js" }, { - "site": "bloomberg", - "name": "pursuits", - "description": "Bloomberg Pursuits (lifestyle) top stories (RSS)", + "site": "binance", + "name": "ticker", + "description": "24h ticker statistics for top trading pairs by volume", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of tickers" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "symbol", + "price", + "change_pct", + "high", + "low", + "volume", + "quote_vol", + "trades" ], "type": "js", - "modulePath": "plugins/bloomberg/pursuits.js", - "sourceFile": "plugins/bloomberg/pursuits.js" + "modulePath": "plugins/binance/ticker.js", + "sourceFile": "plugins/binance/ticker.js" }, { - "site": "bloomberg", - "name": "tech", - "description": "Bloomberg Tech top stories (RSS)", + "site": "binance", + "name": "top", + "description": "Top trading pairs by 24h volume on Binance", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of trading pairs" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "rank", + "symbol", + "price", + "change_24h", + "high", + "low", + "volume" ], "type": "js", - "modulePath": "plugins/bloomberg/tech.js", - "sourceFile": "plugins/bloomberg/tech.js" + "modulePath": "plugins/binance/top.js", + "sourceFile": "plugins/binance/top.js" }, { - "site": "bluesky", - "name": "feeds", - "description": "Popular Bluesky feed generators", + "site": "binance", + "name": "trades", + "description": "Recent trades for a trading pair", "access": "read", - "domain": "public.api.bsky.app", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of feeds" + "help": "Number of trades (max 1000)" } ], "columns": [ - "rank", - "name", - "likes", - "creator", - "description" + "id", + "price", + "qty", + "quote_qty", + "buyer_maker" ], "type": "js", - "modulePath": "plugins/bluesky/feeds.js", - "sourceFile": "plugins/bluesky/feeds.js" + "modulePath": "plugins/binance/trades.js", + "sourceFile": "plugins/binance/trades.js" }, { - "site": "bluesky", - "name": "followers", - "description": "List followers of a Bluesky user", - "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, + "site": "blinkit", + "name": "add-to-cart", + "description": "Add a Blinkit product to cart", + "access": "write", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "handle", + "name": "productId", "type": "str", "required": true, "positional": true, - "help": "Bluesky handle" + "help": "Blinkit product id" }, { - "name": "limit", + "name": "quantity", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of followers" + "help": "Quantity to add (default 1, max 12)" + }, + { + "name": "lat", + "type": "str", + "required": false, + "help": "Delivery latitude (defaults to current Blinkit browser location)" + }, + { + "name": "lon", + "type": "str", + "required": false, + "help": "Delivery longitude (defaults to current Blinkit browser location)" } ], "columns": [ - "rank", - "handle", + "status", + "productId", + "quantity", + "itemCount", + "itemsTotal", + "payable", + "message" + ], + "type": "js", + "modulePath": "plugins/blinkit/add-to-cart.js", + "sourceFile": "plugins/blinkit/add-to-cart.js", + "navigateBefore": false + }, + { + "site": "blinkit", + "name": "cart", + "description": "Show the current Blinkit cart", + "access": "read", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "productId", "name", - "description" + "variant", + "price", + "quantity", + "total", + "itemCount", + "payable", + "cartState" ], "type": "js", - "modulePath": "plugins/bluesky/followers.js", - "sourceFile": "plugins/bluesky/followers.js" + "modulePath": "plugins/blinkit/cart.js", + "sourceFile": "plugins/blinkit/cart.js", + "navigateBefore": false }, { - "site": "bluesky", - "name": "following", - "description": "List accounts a Bluesky user is following", + "site": "blinkit", + "name": "checkout", + "description": "Review Blinkit checkout totals and blockers without placing an order", "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "itemCount", + "itemsTotal", + "deliveryCharge", + "handlingCharge", + "payable", + "cartState", + "checkoutBlocked", + "validations" + ], + "type": "js", + "modulePath": "plugins/blinkit/checkout.js", + "sourceFile": "plugins/blinkit/checkout.js", + "navigateBefore": false + }, + { + "site": "blinkit", + "name": "location", + "description": "Show the selected Blinkit delivery location", + "access": "read", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "selected", + "label", + "area", + "city", + "pincode", + "hasCoordinates", + "source" + ], + "type": "js", + "modulePath": "plugins/blinkit/location.js", + "sourceFile": "plugins/blinkit/location.js", + "navigateBefore": "https://blinkit.com" + }, + { + "site": "blinkit", + "name": "login", + "description": "Open blinkit login", + "access": "write", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "phone", + "user_id", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/blinkit/auth.js", + "sourceFile": "plugins/blinkit/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "blinkit", + "name": "place-order", + "description": "Submit the visible Blinkit final order/payment action. Requires --confirm.", + "access": "write", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "handle", - "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle" - }, - { - "name": "limit", - "type": "int", - "default": 20, + "name": "confirm", + "type": "bool", + "default": false, "required": false, - "help": "Number of accounts" + "help": "Required acknowledgement that this may place/pay for a real order" } ], "columns": [ - "rank", - "handle", - "name", - "description" + "status", + "confirmed", + "itemCount", + "payable", + "orderId", + "url", + "message" ], "type": "js", - "modulePath": "plugins/bluesky/following.js", - "sourceFile": "plugins/bluesky/following.js" + "modulePath": "plugins/blinkit/place-order.js", + "sourceFile": "plugins/blinkit/place-order.js", + "navigateBefore": false }, { - "site": "bluesky", - "name": "profile", - "description": "Get Bluesky user profile info", + "site": "blinkit", + "name": "product", + "description": "Read Blinkit product details for a delivery location", "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "handle", + "name": "productId", "type": "str", "required": true, "positional": true, - "help": "Bluesky handle (e.g. bsky.app, jay.bsky.team)" + "help": "Blinkit product id" + }, + { + "name": "lat", + "type": "str", + "required": false, + "help": "Delivery latitude (defaults to current Blinkit browser location)" + }, + { + "name": "lon", + "type": "str", + "required": false, + "help": "Delivery longitude (defaults to current Blinkit browser location)" } ], "columns": [ - "handle", + "productId", "name", - "followers", - "following", - "posts", - "description" + "brand", + "variant", + "price", + "mrp", + "currency", + "inventory", + "available", + "imageUrl", + "url" ], "type": "js", - "modulePath": "plugins/bluesky/profile.js", - "sourceFile": "plugins/bluesky/profile.js" + "modulePath": "plugins/blinkit/product.js", + "sourceFile": "plugins/blinkit/product.js", + "navigateBefore": false }, { - "site": "bluesky", + "site": "blinkit", "name": "search", - "description": "Search Bluesky users", + "description": "Search Blinkit products for a delivery location", "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "query", "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "Search keyword" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results" + "help": "Max results (max 48)" + }, + { + "name": "lat", + "type": "str", + "required": false, + "help": "Delivery latitude (defaults to current Blinkit browser location)" + }, + { + "name": "lon", + "type": "str", + "required": false, + "help": "Delivery longitude (defaults to current Blinkit browser location)" } ], "columns": [ "rank", - "handle", + "productId", "name", - "followers", - "description" + "brand", + "variant", + "price", + "mrp", + "currency", + "inventory", + "available", + "imageUrl", + "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/bluesky/search.js", - "sourceFile": "plugins/bluesky/search.js" + "modulePath": "plugins/blinkit/search.js", + "sourceFile": "plugins/blinkit/search.js", + "navigateBefore": false }, { - "site": "bluesky", - "name": "starter-packs", - "description": "Get starter packs created by a Bluesky user", + "site": "blinkit", + "name": "whoami", + "description": "Show the current logged-in blinkit account", "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "phone", + "user_id" + ], + "type": "js", + "modulePath": "plugins/blinkit/auth.js", + "sourceFile": "plugins/blinkit/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "bloomberg", + "name": "businessweek", + "description": "Bloomberg Businessweek top stories", + "access": "read", + "domain": "www.bloomberg.com", + "strategy": "public", + "browser": true, "args": [ - { - "name": "handle", - "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Number of starter packs" + "help": "Number of stories to return (max 20)" } ], "columns": [ - "rank", - "name", - "description", - "members", - "joins" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/starter-packs.js", - "sourceFile": "plugins/bluesky/starter-packs.js" + "modulePath": "plugins/bloomberg/businessweek.js", + "sourceFile": "plugins/bloomberg/businessweek.js" }, { - "site": "bluesky", - "name": "thread", - "description": "Get a Bluesky post thread with replies", + "site": "bloomberg", + "name": "crypto", + "description": "Bloomberg Crypto top stories (RSS)", "access": "read", - "domain": "public.api.bsky.app", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ - { - "name": "uri", - "type": "str", - "required": true, - "positional": true, - "help": "Post AT URI (at://did:.../app.bsky.feed.post/...) or bsky.app URL" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of replies" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "author", - "text", - "likes", - "reposts", - "replies_count" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/thread.js", - "sourceFile": "plugins/bluesky/thread.js" + "modulePath": "plugins/bloomberg/crypto.js", + "sourceFile": "plugins/bloomberg/crypto.js" }, { - "site": "bluesky", - "name": "trending", - "description": "Trending topics on Bluesky", + "site": "bloomberg", + "name": "economics", + "description": "Bloomberg Economics top stories (RSS)", "access": "read", - "domain": "public.api.bsky.app", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of topics" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "topic", - "link" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/trending.js", - "sourceFile": "plugins/bluesky/trending.js" + "modulePath": "plugins/bloomberg/economics.js", + "sourceFile": "plugins/bloomberg/economics.js" }, { - "site": "bluesky", - "name": "user", - "description": "Get recent posts from a Bluesky user", + "site": "bloomberg", + "name": "feeds", + "description": "List the Bloomberg RSS feed aliases used by the adapter", "access": "read", - "domain": "public.api.bsky.app", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [], + "columns": [ + "name", + "url" + ], + "type": "js", + "modulePath": "plugins/bloomberg/feeds.js", + "sourceFile": "plugins/bloomberg/feeds.js" + }, + { + "site": "bloomberg", + "name": "green", + "description": "Bloomberg Green (climate & energy) top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ - { - "name": "handle", - "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle (e.g. bsky.app)" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of posts" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "uri", - "text", - "likes", - "reposts", - "replies" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/user.js", - "sourceFile": "plugins/bluesky/user.js" + "modulePath": "plugins/bloomberg/green.js", + "sourceFile": "plugins/bloomberg/green.js" }, { - "site": "bmwblog", - "name": "article", - "description": "Read a BMWBLOG article by URL or slug", + "site": "bloomberg", + "name": "industries", + "description": "Bloomberg Industries top stories (RSS)", "access": "read", - "domain": "www.bmwblog.com", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ { - "name": "url-or-slug", - "type": "str", - "required": true, - "positional": true, - "help": "BMWBLOG article URL or slug" + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" } ], "columns": [ "title", - "date", - "author", - "sections", - "excerpt", - "url", - "content" + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bmwblog/article.js", - "sourceFile": "plugins/bmwblog/article.js" + "modulePath": "plugins/bloomberg/industries.js", + "sourceFile": "plugins/bloomberg/industries.js" }, { - "site": "bmwblog", - "name": "latest", - "description": "List the latest BMWBLOG articles", + "site": "bloomberg", + "name": "main", + "description": "Bloomberg homepage top stories (RSS)", "access": "read", - "domain": "www.bmwblog.com", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Number of articles (1-50)" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", "title", - "date", - "author", - "section", - "excerpt", - "url" + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bmwblog/latest.js", - "sourceFile": "plugins/bmwblog/latest.js" + "modulePath": "plugins/bloomberg/main.js", + "sourceFile": "plugins/bloomberg/main.js" }, { - "site": "bmwblog", - "name": "search", - "description": "Search BMWBLOG articles", + "site": "bloomberg", + "name": "markets", + "description": "Bloomberg Markets top stories (RSS)", "access": "read", - "domain": "www.bmwblog.com", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Number of results (1-50)" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", "title", - "date", - "author", - "section", - "excerpt", - "url" - ], - "tags": [ - "search" + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bmwblog/search.js", - "sourceFile": "plugins/bmwblog/search.js" + "modulePath": "plugins/bloomberg/markets.js", + "sourceFile": "plugins/bloomberg/markets.js" }, { - "site": "booking", - "name": "search", - "description": "Search Booking.com hotels by destination and dates (server-rendered card scrape).", + "site": "bloomberg", + "name": "news", + "description": "Read a Bloomberg story/article page and return title, full content, and media links", "access": "read", - "example": "webcmd booking search Tokyo --checkin 2026-06-15 --checkout 2026-06-17 -f yaml", - "domain": "www.booking.com", - "strategy": "public", + "domain": "www.bloomberg.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "destination", + "name": "link", "type": "str", "required": true, "positional": true, - "help": "Destination keyword (city, district, or hotel name)" - }, + "help": "Bloomberg story/article URL or relative Bloomberg path" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks", + "content" + ], + "type": "js", + "modulePath": "plugins/bloomberg/news.js", + "sourceFile": "plugins/bloomberg/news.js", + "navigateBefore": "https://www.bloomberg.com" + }, + { + "site": "bloomberg", + "name": "opinions", + "description": "Bloomberg Opinion top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "plugins/bloomberg/opinions.js", + "sourceFile": "plugins/bloomberg/opinions.js" + }, + { + "site": "bloomberg", + "name": "politics", + "description": "Bloomberg Politics top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "plugins/bloomberg/politics.js", + "sourceFile": "plugins/bloomberg/politics.js" + }, + { + "site": "bloomberg", + "name": "pursuits", + "description": "Bloomberg Pursuits (lifestyle) top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "plugins/bloomberg/pursuits.js", + "sourceFile": "plugins/bloomberg/pursuits.js" + }, + { + "site": "bloomberg", + "name": "tech", + "description": "Bloomberg Tech top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 1, + "required": false, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "plugins/bloomberg/tech.js", + "sourceFile": "plugins/bloomberg/tech.js" + }, + { + "site": "bluesky", + "name": "feeds", + "description": "Popular Bluesky feed generators", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of feeds" + } + ], + "columns": [ + "rank", + "name", + "likes", + "creator", + "description" + ], + "type": "js", + "modulePath": "plugins/bluesky/feeds.js", + "sourceFile": "plugins/bluesky/feeds.js" + }, + { + "site": "bluesky", + "name": "followers", + "description": "List followers of a Bluesky user", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of followers" + } + ], + "columns": [ + "rank", + "handle", + "name", + "description" + ], + "type": "js", + "modulePath": "plugins/bluesky/followers.js", + "sourceFile": "plugins/bluesky/followers.js" + }, + { + "site": "bluesky", + "name": "following", + "description": "List accounts a Bluesky user is following", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of accounts" + } + ], + "columns": [ + "rank", + "handle", + "name", + "description" + ], + "type": "js", + "modulePath": "plugins/bluesky/following.js", + "sourceFile": "plugins/bluesky/following.js" + }, + { + "site": "bluesky", + "name": "profile", + "description": "Get Bluesky user profile info", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle (e.g. bsky.app, jay.bsky.team)" + } + ], + "columns": [ + "handle", + "name", + "followers", + "following", + "posts", + "description" + ], + "type": "js", + "modulePath": "plugins/bluesky/profile.js", + "sourceFile": "plugins/bluesky/profile.js" + }, + { + "site": "bluesky", + "name": "search", + "description": "Search Bluesky users", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "handle", + "name", + "followers", + "description" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/bluesky/search.js", + "sourceFile": "plugins/bluesky/search.js" + }, + { + "site": "bluesky", + "name": "starter-packs", + "description": "Get starter packs created by a Bluesky user", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of starter packs" + } + ], + "columns": [ + "rank", + "name", + "description", + "members", + "joins" + ], + "type": "js", + "modulePath": "plugins/bluesky/starter-packs.js", + "sourceFile": "plugins/bluesky/starter-packs.js" + }, + { + "site": "bluesky", + "name": "thread", + "description": "Get a Bluesky post thread with replies", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "uri", + "type": "str", + "required": true, + "positional": true, + "help": "Post AT URI (at://did:.../app.bsky.feed.post/...) or bsky.app URL" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of replies" + } + ], + "columns": [ + "author", + "text", + "likes", + "reposts", + "replies_count" + ], + "type": "js", + "modulePath": "plugins/bluesky/thread.js", + "sourceFile": "plugins/bluesky/thread.js" + }, + { + "site": "bluesky", + "name": "trending", + "description": "Trending topics on Bluesky", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of topics" + } + ], + "columns": [ + "rank", + "topic", + "link" + ], + "type": "js", + "modulePath": "plugins/bluesky/trending.js", + "sourceFile": "plugins/bluesky/trending.js" + }, + { + "site": "bluesky", + "name": "user", + "description": "Get recent posts from a Bluesky user", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle (e.g. bsky.app)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts" + } + ], + "columns": [ + "rank", + "uri", + "text", + "likes", + "reposts", + "replies" + ], + "type": "js", + "modulePath": "plugins/bluesky/user.js", + "sourceFile": "plugins/bluesky/user.js" + }, + { + "site": "bmwblog", + "name": "article", + "description": "Read a BMWBLOG article by URL or slug", + "access": "read", + "domain": "www.bmwblog.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "url-or-slug", + "type": "str", + "required": true, + "positional": true, + "help": "BMWBLOG article URL or slug" + } + ], + "columns": [ + "title", + "date", + "author", + "sections", + "excerpt", + "url", + "content" + ], + "type": "js", + "modulePath": "plugins/bmwblog/article.js", + "sourceFile": "plugins/bmwblog/article.js" + }, + { + "site": "bmwblog", + "name": "latest", + "description": "List the latest BMWBLOG articles", + "access": "read", + "domain": "www.bmwblog.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of articles (1-50)" + } + ], + "columns": [ + "rank", + "title", + "date", + "author", + "section", + "excerpt", + "url" + ], + "type": "js", + "modulePath": "plugins/bmwblog/latest.js", + "sourceFile": "plugins/bmwblog/latest.js" + }, + { + "site": "bmwblog", + "name": "search", + "description": "Search BMWBLOG articles", + "access": "read", + "domain": "www.bmwblog.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results (1-50)" + } + ], + "columns": [ + "rank", + "title", + "date", + "author", + "section", + "excerpt", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/bmwblog/search.js", + "sourceFile": "plugins/bmwblog/search.js" + }, + { + "site": "booking", + "name": "search", + "description": "Search Booking.com hotels by destination and dates (server-rendered card scrape).", + "access": "read", + "example": "webcmd booking search Tokyo --checkin 2026-06-15 --checkout 2026-06-17 -f yaml", + "domain": "www.booking.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "destination", + "type": "str", + "required": true, + "positional": true, + "help": "Destination keyword (city, district, or hotel name)" + }, { "name": "checkin", "type": "str", @@ -3222,8 +4293,186 @@ "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/concordia/export-postgraduate-courses.js", - "sourceFile": "plugins/concordia/export-postgraduate-courses.js" + "modulePath": "plugins/concordia/export-postgraduate-courses.js", + "sourceFile": "plugins/concordia/export-postgraduate-courses.js" + }, + { + "site": "coupang", + "name": "add-to-cart", + "description": "Add a Coupang product to cart using logged-in browser session", + "access": "write", + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product-id", + "type": "str", + "required": false, + "positional": true, + "help": "Coupang product ID" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Canonical product URL" + } + ], + "columns": [ + "ok", + "product_id", + "url", + "message" + ], + "type": "js", + "modulePath": "plugins/coupang/add-to-cart.js", + "sourceFile": "plugins/coupang/add-to-cart.js", + "navigateBefore": "https://www.coupang.com" + }, + { + "site": "coupang", + "name": "login", + "description": "Open coupang login", + "access": "write", + "domain": "coupang.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/coupang/auth.js", + "sourceFile": "plugins/coupang/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "coupang", + "name": "product", + "description": "Read full product detail (price, rating, seller, delivery) for a Coupang product", + "access": "read", + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product-id", + "type": "str", + "required": false, + "positional": true, + "help": "Coupang product ID (digits only)" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Canonical Coupang product URL (alternative to --product-id)" + } + ], + "columns": [ + "product_id", + "title", + "price", + "original_price", + "discount_rate", + "rating", + "review_count", + "seller", + "brand", + "rocket", + "delivery_promise", + "image_url", + "url" + ], + "type": "js", + "modulePath": "plugins/coupang/product.js", + "sourceFile": "plugins/coupang/product.js", + "navigateBefore": "https://www.coupang.com" + }, + { + "site": "coupang", + "name": "search", + "description": "Search Coupang products with logged-in browser session", + "access": "read", + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Search result page number" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (max 50)" + }, + { + "name": "filter", + "type": "str", + "required": false, + "help": "Optional search filter (currently supports: rocket)" + } + ], + "columns": [ + "rank", + "product_id", + "title", + "price", + "unit_price", + "rating", + "review_count", + "rocket", + "delivery_type", + "delivery_promise", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/coupang/search.js", + "sourceFile": "plugins/coupang/search.js", + "navigateBefore": "https://www.coupang.com" + }, + { + "site": "coupang", + "name": "whoami", + "description": "Show the current logged-in coupang account", + "access": "read", + "domain": "coupang.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name" + ], + "type": "js", + "modulePath": "plugins/coupang/auth.js", + "sourceFile": "plugins/coupang/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "crates", @@ -3921,160 +5170,667 @@ "url" ], "type": "js", - "modulePath": "plugins/devto/tag.js", - "sourceFile": "plugins/devto/tag.js" + "modulePath": "plugins/devto/tag.js", + "sourceFile": "plugins/devto/tag.js" + }, + { + "site": "devto", + "name": "top", + "description": "Top DEV.to articles of the day", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of articles" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/top.js", + "sourceFile": "plugins/devto/top.js" + }, + { + "site": "devto", + "name": "user", + "description": "Recent DEV.to articles from a specific user", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "DEV.to username (e.g. ben, thepracticaldev)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of articles" + } + ], + "columns": [ + "rank", + "id", + "title", + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/user.js", + "sourceFile": "plugins/devto/user.js" + }, + { + "site": "dictionary", + "name": "examples", + "description": "Read real-world example sentences utilizing the word", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to get example sentences for" + } + ], + "columns": [ + "word", + "example" + ], + "type": "js", + "modulePath": "plugins/dictionary/examples.js", + "sourceFile": "plugins/dictionary/examples.js" + }, + { + "site": "dictionary", + "name": "search", + "description": "Search the Free Dictionary API for definitions, parts of speech, and pronunciations.", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to define (e.g., serendipity)" + } + ], + "columns": [ + "word", + "phonetic", + "type", + "definition" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/dictionary/search.js", + "sourceFile": "plugins/dictionary/search.js" + }, + { + "site": "dictionary", + "name": "synonyms", + "description": "Find synonyms for a specific word", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to find synonyms for (e.g., serendipity)" + } + ], + "columns": [ + "word", + "synonyms" + ], + "type": "js", + "modulePath": "plugins/dictionary/synonyms.js", + "sourceFile": "plugins/dictionary/synonyms.js" + }, + { + "site": "district", + "name": "checkout", + "description": "Select District movie seats and open the UPI QR payment scanner", + "access": "write", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "show", + "type": "str", + "required": true, + "positional": true, + "help": "District seat-layout URL or showId from district showtimes" + }, + { + "name": "seats", + "type": "str", + "required": true, + "help": "Comma-separated seat labels to select, e.g. I22,I21" + }, + { + "name": "format-id", + "type": "str", + "required": false, + "help": "District formatId from showtimes; required when show is a showId" + }, + { + "name": "content-id", + "type": "str", + "required": false, + "help": "District content id; required when show is a showId" + }, + { + "name": "timeout", + "type": "int", + "default": 45, + "required": false, + "help": "Maximum seconds to wait for selection, review page, and payment handoff" + }, + { + "name": "payment", + "type": "str", + "default": "upi-qr", + "required": false, + "help": "Payment handoff target: upi-qr opens the scanner; review stops on order review" + } + ], + "columns": [ + "status", + "movie", + "cinema", + "date", + "time", + "seats", + "ticketCount", + "orderAmount", + "bookingCharge", + "total", + "paymentMethod", + "paymentState", + "upiQrVisible", + "paymentAmount", + "paymentUrl", + "showId" + ], + "type": "js", + "modulePath": "plugins/district/checkout.js", + "sourceFile": "plugins/district/checkout.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true + }, + { + "site": "district", + "name": "listings", + "aliases": [ + "ls" + ], + "description": "List public District by Zomato movies, events, and nearby going-out cards", + "access": "read", + "domain": "www.district.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "input", + "type": "str", + "default": "home", + "required": false, + "positional": true, + "help": "home, movies, events, a district.in URL, or a District path" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum rows to return (1-100)" + } + ], + "columns": [ + "rank", + "title", + "category", + "date", + "venue", + "price", + "url" + ], + "type": "js", + "modulePath": "plugins/district/listings.js", + "sourceFile": "plugins/district/listings.js" }, { - "site": "devto", - "name": "top", - "description": "Top DEV.to articles of the day", + "site": "district", + "name": "locations", + "aliases": [ + "location-search" + ], + "description": "Search District-supported cities, areas, malls, and places for booking filters", "access": "read", - "domain": "dev.to", + "domain": "www.district.in", "strategy": "public", "browser": false, "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "City, area, mall, or locality, for example \"bangalore\" or \"indiranagar\"" + }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Number of articles" + "help": "Maximum location rows to return (1-50)" } ], "columns": [ "rank", - "id", - "title", - "author", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", - "url" + "name", + "kind", + "city", + "state", + "cityKey", + "cityId", + "placeId", + "lat", + "lng", + "distanceKm", + "source" ], "type": "js", - "modulePath": "plugins/devto/top.js", - "sourceFile": "plugins/devto/top.js" + "modulePath": "plugins/district/locations.js", + "sourceFile": "plugins/district/locations.js" }, { - "site": "devto", - "name": "user", - "description": "Recent DEV.to articles from a specific user", + "site": "district", + "name": "login", + "description": "Open district login", + "access": "write", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "phone_number", + "email", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/district/auth.js", + "sourceFile": "plugins/district/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "district", + "name": "search", + "aliases": [ + "s" + ], + "description": "Search District by Zomato across movies, events, dining, stores, activities, and play", "access": "read", - "domain": "dev.to", + "domain": "www.district.in", "strategy": "public", "browser": false, "args": [ { - "name": "username", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "DEV.to username (e.g. ben, thepracticaldev)" + "help": "Search query, for example \"hamlet\" or \"arijit\"" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of articles" + "help": "Maximum rows to return (1-100)" + }, + { + "name": "tab", + "type": "str", + "default": "all", + "required": false, + "help": "Search tab: all, dining, events, movies, stores, activities, or play" } ], "columns": [ "rank", - "id", "title", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", + "category", + "date", + "venue", + "price", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/devto/user.js", - "sourceFile": "plugins/devto/user.js" + "modulePath": "plugins/district/search.js", + "sourceFile": "plugins/district/search.js" }, { - "site": "dictionary", - "name": "examples", - "description": "Read real-world example sentences utilizing the word", + "site": "district", + "name": "seats", + "description": "List available seats for a District movie showtime", "access": "read", - "domain": "api.dictionaryapi.dev", - "strategy": "public", - "browser": false, + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "word", - "type": "string", + "name": "show", + "type": "str", "required": true, "positional": true, - "help": "Word to get example sentences for" + "help": "District seat-layout URL or showId from district showtimes" + }, + { + "name": "format-id", + "type": "str", + "required": false, + "help": "District formatId from showtimes; required when show is a showId" + }, + { + "name": "content-id", + "type": "str", + "required": false, + "help": "District content id; required when show is a showId" + }, + { + "name": "class", + "type": "str", + "required": false, + "help": "Optional seat class filter, e.g. premium, premium xl, or recliner" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Number of seats to choose (1-10); without count, seats are listed normally" + }, + { + "name": "together", + "type": "str", + "required": false, + "help": "Require selected seats to be adjacent when count is provided" + }, + { + "name": "max-price", + "type": "float", + "required": false, + "help": "Maximum price per seat" + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Maximum seats to return (1-300)" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Maximum seconds to wait for the seat map to render" } ], "columns": [ - "word", - "example" + "rank", + "seat", + "row", + "number", + "column", + "seatClass", + "price", + "status", + "flags", + "showId", + "formatId", + "url" ], "type": "js", - "modulePath": "plugins/dictionary/examples.js", - "sourceFile": "plugins/dictionary/examples.js" + "modulePath": "plugins/district/seats.js", + "sourceFile": "plugins/district/seats.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "dictionary", - "name": "search", - "description": "Search the Free Dictionary API for definitions, parts of speech, and pronunciations.", - "access": "read", - "domain": "api.dictionaryapi.dev", - "strategy": "public", - "browser": false, + "site": "district", + "name": "set-location", + "aliases": [ + "setlocation" + ], + "description": "Set the District browser session location for movie booking filters", + "access": "write", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "word", - "type": "string", + "name": "location", + "type": "str", "required": true, "positional": true, - "help": "Word to define (e.g., serendipity)" + "help": "City, area, mall, or locality, for example \"Bangalore\" or \"Indiranagar\"" + }, + { + "name": "rank", + "type": "int", + "default": 1, + "required": false, + "help": "Pick the Nth District location result (1-20), default: 1" + }, + { + "name": "timeout", + "type": "int", + "default": 45, + "required": false, + "help": "Maximum seconds to wait for the picker and location change" } ], "columns": [ - "word", - "phonetic", - "type", - "definition" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/dictionary/search.js", - "sourceFile": "plugins/dictionary/search.js" + "status", + "name", + "city", + "state", + "cityKey", + "cityId", + "placeId", + "subzoneId", + "lat", + "lng", + "availableTabs", + "source" + ], + "type": "js", + "modulePath": "plugins/district/set-location.js", + "sourceFile": "plugins/district/set-location.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "dictionary", - "name": "synonyms", - "description": "Find synonyms for a specific word", + "site": "district", + "name": "showtimes", + "aliases": [ + "shows" + ], + "description": "List District movie showtimes with location, time, cinema, language, price, and format filters", "access": "read", - "domain": "api.dictionaryapi.dev", - "strategy": "public", - "browser": false, + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "word", - "type": "string", + "name": "movie", + "type": "str", "required": true, "positional": true, - "help": "Word to find synonyms for (e.g., serendipity)" + "help": "Movie name or District movie URL" + }, + { + "name": "date", + "type": "str", + "required": false, + "help": "Show date in YYYY-MM-DD format; defaults to District selected date" + }, + { + "name": "city", + "type": "str", + "required": false, + "help": "District city name/key, for example Bangalore or Bengaluru" + }, + { + "name": "near", + "type": "str", + "required": false, + "help": "Area, mall, or locality to search near, for example Indiranagar" + }, + { + "name": "city-key", + "type": "str", + "required": false, + "help": "Legacy District city key override, for example bengaluru" + }, + { + "name": "after", + "type": "str", + "required": false, + "help": "Only shows at or after HH:MM, 24-hour time" + }, + { + "name": "before", + "type": "str", + "required": false, + "help": "Only shows at or before HH:MM, 24-hour time" + }, + { + "name": "cinema", + "type": "str", + "required": false, + "help": "Filter cinema/theatre name, for example PVR, INOX, Orion" + }, + { + "name": "language", + "type": "str", + "required": false, + "help": "Filter movie language, for example English, Hindi, Kannada" + }, + { + "name": "max-price", + "type": "float", + "required": false, + "help": "Only shows with at least one ticket class at or below this price" + }, + { + "name": "quality", + "type": "str", + "required": false, + "help": "Generic format/quality filter, for example 2D, 3D, IMAX, IMAX 3D, 4DX" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Maximum showtime rows to return (1-200)" } ], "columns": [ - "word", - "synonyms" + "rank", + "movie", + "language", + "date", + "time", + "cinema", + "format", + "priceRange", + "available", + "showId", + "formatId", + "url" + ], + "type": "js", + "modulePath": "plugins/district/showtimes.js", + "sourceFile": "plugins/district/showtimes.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "district", + "name": "whoami", + "description": "Show the current logged-in district account", + "access": "read", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name", + "phone_number", + "email" ], "type": "js", - "modulePath": "plugins/dictionary/synonyms.js", - "sourceFile": "plugins/dictionary/synonyms.js" + "modulePath": "plugins/district/auth.js", + "sourceFile": "plugins/district/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "dockerhub", @@ -4355,6 +6111,55 @@ "modulePath": "plugins/flathub/search.js", "sourceFile": "plugins/flathub/search.js" }, + { + "site": "github", + "name": "login", + "description": "Open github login", + "access": "write", + "domain": "github.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "id", + "username", + "name", + "url", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/github/auth.js", + "sourceFile": "plugins/github/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "github", + "name": "whoami", + "description": "Show the current logged-in github account", + "access": "read", + "domain": "github.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "id", + "username", + "name", + "url" + ], + "type": "js", + "modulePath": "plugins/github/auth.js", + "sourceFile": "plugins/github/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, { "site": "github-trending", "name": "repos", @@ -5272,8 +7077,285 @@ "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/heidelberg/export-postgraduate-courses.js", - "sourceFile": "plugins/heidelberg/export-postgraduate-courses.js" + "modulePath": "plugins/heidelberg/export-postgraduate-courses.js", + "sourceFile": "plugins/heidelberg/export-postgraduate-courses.js" + }, + { + "site": "hf", + "name": "datasets", + "description": "Top Hugging Face datasets (downloads / likes / trending / freshness).", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "sort", + "type": "string", + "default": "downloads", + "required": false, + "help": "Sort key: downloads, likes, trending, created_at, last_modified" + }, + { + "name": "search", + "type": "string", + "required": false, + "help": "Optional name/owner substring filter." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max datasets (max 100; one API page)." + } + ], + "columns": [ + "rank", + "id", + "author", + "downloads", + "likes", + "tags", + "lastModified", + "url" + ], + "type": "js", + "modulePath": "plugins/hf/datasets.js", + "sourceFile": "plugins/hf/datasets.js" + }, + { + "site": "hf", + "name": "login", + "description": "Open hf login", + "access": "write", + "domain": "huggingface.co", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "username", + "fullname", + "type", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/hf/auth.js", + "sourceFile": "plugins/hf/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "hf", + "name": "models", + "description": "Top Hugging Face models (downloads / likes / trending / freshness).", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "sort", + "type": "string", + "default": "downloads", + "required": false, + "help": "Sort key: downloads, likes, trending, created_at, last_modified" + }, + { + "name": "search", + "type": "string", + "required": false, + "help": "Optional name/owner substring filter (e.g. \"llama\", \"mistralai/\")" + }, + { + "name": "pipeline", + "type": "string", + "required": false, + "help": "Filter by pipeline tag (e.g. text-generation, image-classification)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max models (max 100; one API page)." + } + ], + "columns": [ + "rank", + "id", + "author", + "pipelineTag", + "downloads", + "likes", + "tags", + "lastModified", + "url" + ], + "type": "js", + "modulePath": "plugins/hf/models.js", + "sourceFile": "plugins/hf/models.js" + }, + { + "site": "hf", + "name": "paper", + "description": "Hugging Face paper detail by arXiv id (full title / summary / authors / AI keywords)", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "arXiv id (e.g. \"1706.03762\") — same value HF uses to mirror the paper" + } + ], + "columns": [ + "id", + "title", + "authors", + "publishedAt", + "upvotes", + "aiKeywords", + "summary", + "aiSummary", + "url" + ], + "type": "js", + "modulePath": "plugins/hf/paper.js", + "sourceFile": "plugins/hf/paper.js" + }, + { + "site": "hf", + "name": "spaces", + "description": "Top Hugging Face Spaces (likes / created_at / last_modified).", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "sort", + "type": "string", + "default": "likes", + "required": false, + "help": "Sort key: likes, created_at, last_modified" + }, + { + "name": "search", + "type": "string", + "required": false, + "help": "Optional name/owner substring filter (e.g. \"stability\", \"openai/\")" + }, + { + "name": "sdk", + "type": "string", + "required": false, + "help": "Filter by Space SDK: gradio / streamlit / docker / static" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max spaces (max 100; one API page)." + } + ], + "columns": [ + "rank", + "id", + "author", + "sdk", + "likes", + "tags", + "lastModified", + "url" + ], + "type": "js", + "modulePath": "plugins/hf/spaces.js", + "sourceFile": "plugins/hf/spaces.js" + }, + { + "site": "hf", + "name": "top", + "description": "Top upvoted Hugging Face papers", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of papers" + }, + { + "name": "all", + "type": "bool", + "default": false, + "required": false, + "help": "Return all papers (ignore limit)" + }, + { + "name": "date", + "type": "str", + "required": false, + "help": "Date (YYYY-MM-DD), defaults to most recent" + }, + { + "name": "period", + "type": "str", + "default": "daily", + "required": false, + "help": "Time period: daily, weekly, or monthly", + "choices": [ + "daily", + "weekly", + "monthly" + ] + } + ], + "columns": [ + "rank", + "id", + "title", + "upvotes", + "authors" + ], + "type": "js", + "modulePath": "plugins/hf/top.js", + "sourceFile": "plugins/hf/top.js" + }, + { + "site": "hf", + "name": "whoami", + "description": "Show the current logged-in hf account", + "access": "read", + "domain": "huggingface.co", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "username", + "fullname", + "type" + ], + "type": "js", + "modulePath": "plugins/hf/auth.js", + "sourceFile": "plugins/hf/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "hft", @@ -7688,6 +9770,162 @@ "navigateBefore": false, "siteSession": "persistent" }, + { + "site": "linkedin-learning", + "name": "course", + "description": "Get LinkedIn Learning course detail by slug or course URL", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "slug", + "type": "string", + "required": true, + "positional": true, + "help": "Course slug (e.g. agentic-ai-build-your-first-agentic-ai-system) or full /learning/ URL" + } + ], + "columns": [ + "title", + "slug", + "description", + "difficulty", + "duration_sec", + "videos_count", + "rating", + "rating_count", + "released", + "url" + ], + "type": "js", + "modulePath": "plugins/linkedin-learning/course.js", + "sourceFile": "plugins/linkedin-learning/course.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin-learning", + "name": "login", + "description": "Open linkedin-learning login", + "access": "write", + "domain": "linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "public_id", + "plain_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/linkedin-learning/auth.js", + "sourceFile": "plugins/linkedin-learning/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "linkedin-learning", + "name": "search", + "description": "Search LinkedIn Learning courses, videos, and learning paths by keyword", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "keywords", + "type": "string", + "required": true, + "positional": true, + "help": "Search keywords, e.g. \"AI agent\"" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum results to return (1-50)" + } + ], + "columns": [ + "rank", + "type", + "title", + "instructor", + "difficulty", + "duration_sec", + "rating", + "rating_count", + "viewers", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/linkedin-learning/search.js", + "sourceFile": "plugins/linkedin-learning/search.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin-learning", + "name": "trending", + "description": "Browse LinkedIn Learning recommended courses across personalized carousels", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum results to return (1-50)" + } + ], + "columns": [ + "rank", + "group", + "type", + "title", + "difficulty", + "viewers", + "url" + ], + "type": "js", + "modulePath": "plugins/linkedin-learning/trending.js", + "sourceFile": "plugins/linkedin-learning/trending.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin-learning", + "name": "whoami", + "description": "Show the current logged-in linkedin-learning account", + "access": "read", + "domain": "linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "public_id", + "plain_id", + "name" + ], + "type": "js", + "modulePath": "plugins/linkedin-learning/auth.js", + "sourceFile": "plugins/linkedin-learning/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, { "site": "lobsters", "name": "active", @@ -8307,6 +10545,203 @@ "navigateBefore": false, "siteSession": "persistent" }, + { + "site": "manus", + "name": "connectors", + "description": "List available Manus connectors (integrations).", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max connectors to return" + } + ], + "columns": [ + "UID", + "Name", + "Brief" + ], + "type": "js", + "modulePath": "plugins/manus/connectors.js", + "sourceFile": "plugins/manus/connectors.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "credits", + "description": "Show Manus credit balance and refresh details.", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "plugins/manus/credits.js", + "sourceFile": "plugins/manus/credits.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "list", + "description": "List Manus sessions (tasks).", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max sessions to return" + }, + { + "name": "archived", + "type": "bool", + "default": false, + "required": false, + "help": "Include archived sessions" + } + ], + "columns": [ + "id", + "Title", + "Status", + "Last Message", + "Last Updated", + "Credits" + ], + "type": "js", + "modulePath": "plugins/manus/list.js", + "sourceFile": "plugins/manus/list.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "login", + "description": "Open manus login", + "access": "write", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/manus/auth.js", + "sourceFile": "plugins/manus/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "read", + "description": "Show details for a specific Manus session.", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "uid", + "type": "str", + "required": true, + "positional": true, + "help": "Session UID" + } + ], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "plugins/manus/read.js", + "sourceFile": "plugins/manus/read.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "skills", + "description": "List Manus skills (user-added and system).", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "ID", + "Name", + "Description", + "Source" + ], + "type": "js", + "modulePath": "plugins/manus/skills.js", + "sourceFile": "plugins/manus/skills.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "status", + "description": "Show current Manus user profile and credit summary.", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "plugins/manus/status.js", + "sourceFile": "plugins/manus/status.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "whoami", + "description": "Show the current logged-in manus account", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" + ], + "type": "js", + "modulePath": "plugins/manus/auth.js", + "sourceFile": "plugins/manus/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, { "site": "maven", "name": "artifact", diff --git a/plugins/amazon-in/README.md b/plugins/amazon-in/README.md new file mode 100644 index 00000000..7f2441de --- /dev/null +++ b/plugins/amazon-in/README.md @@ -0,0 +1,21 @@ +# webcmd-plugin-amazon-in + +Webcmd commands for amazon-in. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/amazon-in +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd amazon-in checkout` | Prepare a guarded Amazon.in checkout with browser-only payment handoff | +| `webcmd amazon-in checkout-status` | Read the current Amazon.in checkout or payment state without clicking | +| `webcmd amazon-in login` | Open amazon-in login | +| `webcmd amazon-in product` | Fetch the current Amazon.in price and selected product variant | +| `webcmd amazon-in search` | Search Amazon.in products with inclusive INR price bounds and images | +| `webcmd amazon-in whoami` | Show the current logged-in amazon-in account | +| `webcmd amazon-in wishlist` | Fetch current prices for products in the default Amazon.in wishlist | diff --git a/clis/amazon-in/auth.js b/plugins/amazon-in/auth.js similarity index 95% rename from clis/amazon-in/auth.js rename to plugins/amazon-in/auth.js index 131ed2fe..bfdd98e0 100644 --- a/clis/amazon-in/auth.js +++ b/plugins/amazon-in/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; import { hasAmazonInAuthCookie } from './parsers.js'; import { DOMAIN, HOME_URL, SITE } from './shared.js'; diff --git a/clis/amazon-in/checkout-status.js b/plugins/amazon-in/checkout-status.js similarity index 100% rename from clis/amazon-in/checkout-status.js rename to plugins/amazon-in/checkout-status.js diff --git a/clis/amazon-in/checkout.js b/plugins/amazon-in/checkout.js similarity index 100% rename from clis/amazon-in/checkout.js rename to plugins/amazon-in/checkout.js diff --git a/plugins/amazon-in/package.json b/plugins/amazon-in/package.json new file mode 100644 index 00000000..6b596505 --- /dev/null +++ b/plugins/amazon-in/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-amazon-in", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for amazon-in", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/amazon-in/parsers.js b/plugins/amazon-in/parsers.js similarity index 100% rename from clis/amazon-in/parsers.js rename to plugins/amazon-in/parsers.js diff --git a/clis/amazon-in/product.js b/plugins/amazon-in/product.js similarity index 100% rename from clis/amazon-in/product.js rename to plugins/amazon-in/product.js diff --git a/clis/amazon-in/search.js b/plugins/amazon-in/search.js similarity index 100% rename from clis/amazon-in/search.js rename to plugins/amazon-in/search.js diff --git a/clis/amazon-in/shared.js b/plugins/amazon-in/shared.js similarity index 100% rename from clis/amazon-in/shared.js rename to plugins/amazon-in/shared.js diff --git a/clis/amazon-in/parsers.test.js b/plugins/amazon-in/test/parsers.test.js similarity index 99% rename from clis/amazon-in/parsers.test.js rename to plugins/amazon-in/test/parsers.test.js index 6ceafb83..b2d77cc3 100644 --- a/clis/amazon-in/parsers.test.js +++ b/plugins/amazon-in/test/parsers.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { __test__ as checkoutTest } from './checkout.js'; +import { __test__ as checkoutTest } from '../checkout.js'; import { buildProductUrl, classifyCheckoutSnapshot, @@ -16,7 +16,7 @@ import { validateCheckoutArgs, validatePositiveInteger, validatePriceBounds, -} from './parsers.js'; +} from '../parsers.js'; describe('amazon-in parsers', () => { it('normalizes ASINs and parses Indian prices and counts', () => { diff --git a/plugins/amazon-in/webcmd-plugin.json b/plugins/amazon-in/webcmd-plugin.json new file mode 100644 index 00000000..0ee3eafc --- /dev/null +++ b/plugins/amazon-in/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "amazon-in", + "version": "0.1.0", + "description": "Webcmd commands for amazon-in", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/clis/amazon-in/wishlist.js b/plugins/amazon-in/wishlist.js similarity index 100% rename from clis/amazon-in/wishlist.js rename to plugins/amazon-in/wishlist.js diff --git a/plugins/amazon/README.md b/plugins/amazon/README.md new file mode 100644 index 00000000..2b0d214d --- /dev/null +++ b/plugins/amazon/README.md @@ -0,0 +1,23 @@ +# webcmd-plugin-amazon + +Webcmd commands for amazon. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/amazon +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd amazon bestsellers` | Amazon Best Sellers pages for category candidate discovery | +| `webcmd amazon discussion` | Amazon review summary and sample customer discussion from product review pages | +| `webcmd amazon login` | Open amazon login | +| `webcmd amazon movers-shakers` | Amazon Movers & Shakers pages for short-term growth signals | +| `webcmd amazon new-releases` | Amazon New Releases pages for early momentum discovery | +| `webcmd amazon offer` | Amazon seller, buy box, and fulfillment facts from the product page | +| `webcmd amazon product` | Amazon product page facts for candidate validation | +| `webcmd amazon search` | Amazon search results for product discovery and coarse filtering | +| `webcmd amazon whoami` | Show the current logged-in amazon account | diff --git a/clis/amazon/auth.js b/plugins/amazon/auth.js similarity index 96% rename from clis/amazon/auth.js rename to plugins/amazon/auth.js index 6e1168f1..88650505 100644 --- a/clis/amazon/auth.js +++ b/plugins/amazon/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasAmazonSessionCookies(page) { const cookies = await page.getCookies({ url: 'https://www.amazon.com' }); diff --git a/clis/amazon/bestsellers.js b/plugins/amazon/bestsellers.js similarity index 100% rename from clis/amazon/bestsellers.js rename to plugins/amazon/bestsellers.js diff --git a/clis/amazon/discussion.js b/plugins/amazon/discussion.js similarity index 100% rename from clis/amazon/discussion.js rename to plugins/amazon/discussion.js diff --git a/clis/amazon/movers-shakers.js b/plugins/amazon/movers-shakers.js similarity index 100% rename from clis/amazon/movers-shakers.js rename to plugins/amazon/movers-shakers.js diff --git a/clis/amazon/new-releases.js b/plugins/amazon/new-releases.js similarity index 100% rename from clis/amazon/new-releases.js rename to plugins/amazon/new-releases.js diff --git a/clis/amazon/offer.js b/plugins/amazon/offer.js similarity index 100% rename from clis/amazon/offer.js rename to plugins/amazon/offer.js diff --git a/plugins/amazon/package.json b/plugins/amazon/package.json new file mode 100644 index 00000000..b0a3c57e --- /dev/null +++ b/plugins/amazon/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-amazon", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for amazon", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/amazon/product.js b/plugins/amazon/product.js similarity index 100% rename from clis/amazon/product.js rename to plugins/amazon/product.js diff --git a/clis/amazon/rankings.js b/plugins/amazon/rankings.js similarity index 100% rename from clis/amazon/rankings.js rename to plugins/amazon/rankings.js diff --git a/clis/amazon/search.js b/plugins/amazon/search.js similarity index 100% rename from clis/amazon/search.js rename to plugins/amazon/search.js diff --git a/clis/amazon/shared.js b/plugins/amazon/shared.js similarity index 100% rename from clis/amazon/shared.js rename to plugins/amazon/shared.js diff --git a/clis/amazon/bestsellers.test.js b/plugins/amazon/test/bestsellers.test.js similarity index 96% rename from clis/amazon/bestsellers.test.js rename to plugins/amazon/test/bestsellers.test.js index 235393b4..3723b217 100644 --- a/clis/amazon/bestsellers.test.js +++ b/plugins/amazon/test/bestsellers.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { __test__ } from './rankings.js'; +import { __test__ } from '../rankings.js'; describe('amazon bestsellers normalization', () => { it('normalizes bestseller cards and infers review counts from card text', () => { const result = __test__.normalizeRankingCandidate({ diff --git a/clis/amazon/discussion.test.js b/plugins/amazon/test/discussion.test.js similarity index 97% rename from clis/amazon/discussion.test.js rename to plugins/amazon/test/discussion.test.js index ff98aa6e..050483de 100644 --- a/clis/amazon/discussion.test.js +++ b/plugins/amazon/test/discussion.test.js @@ -1,9 +1,9 @@ import { describe, expect, it, vi } from 'vitest'; import { AuthRequiredError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ } from './discussion.js'; -import './discussion.js'; -import { createPageMock } from '../test-utils.js'; +import { __test__ } from '../discussion.js'; +import '../discussion.js'; +import { createPageMock } from './page-mock.js'; describe('amazon discussion normalization', () => { diff --git a/clis/amazon/offer.test.js b/plugins/amazon/test/offer.test.js similarity index 97% rename from clis/amazon/offer.test.js rename to plugins/amazon/test/offer.test.js index f25a6f50..a27061be 100644 --- a/clis/amazon/offer.test.js +++ b/plugins/amazon/test/offer.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { __test__ } from './offer.js'; +import { __test__ } from '../offer.js'; describe('amazon offer normalization', () => { it('extracts sold-by and fulfillment facts from product offer text', () => { const result = __test__.normalizeOfferPayload({ diff --git a/plugins/amazon/test/page-mock.js b/plugins/amazon/test/page-mock.js new file mode 100644 index 00000000..473a2eb0 --- /dev/null +++ b/plugins/amazon/test/page-mock.js @@ -0,0 +1,11 @@ +import { vi } from 'vitest'; + +export function createPageMock(evaluateResults = []) { + const evaluate = vi.fn(); + for (const result of evaluateResults) evaluate.mockResolvedValueOnce(result); + return { + evaluate, + goto: vi.fn().mockResolvedValue(undefined), + wait: vi.fn().mockResolvedValue(undefined), + }; +} diff --git a/clis/amazon/product.test.js b/plugins/amazon/test/product.test.js similarity index 96% rename from clis/amazon/product.test.js rename to plugins/amazon/test/product.test.js index 2de78936..9cb7eb01 100644 --- a/clis/amazon/product.test.js +++ b/plugins/amazon/test/product.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { __test__ } from './product.js'; +import { __test__ } from '../product.js'; describe('amazon product normalization', () => { it('normalizes product facts from the product page', () => { const result = __test__.normalizeProductPayload({ diff --git a/clis/amazon/rankings.test.js b/plugins/amazon/test/rankings.test.js similarity index 97% rename from clis/amazon/rankings.test.js rename to plugins/amazon/test/rankings.test.js index c7b9d10b..fdf6b4f5 100644 --- a/clis/amazon/rankings.test.js +++ b/plugins/amazon/test/rankings.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { __test__ } from './rankings.js'; +import { __test__ } from '../rankings.js'; describe('amazon rankings helpers', () => { it('normalizes ranking candidates with unified schema', () => { const result = __test__.normalizeRankingCandidate({ diff --git a/clis/amazon/search.test.js b/plugins/amazon/test/search.test.js similarity index 96% rename from clis/amazon/search.test.js rename to plugins/amazon/test/search.test.js index bfc35ea7..e8814831 100644 --- a/clis/amazon/search.test.js +++ b/plugins/amazon/test/search.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { __test__ } from './search.js'; +import { __test__ } from '../search.js'; describe('amazon search normalization', () => { it('normalizes search cards into research-friendly fields', () => { const result = __test__.normalizeSearchCandidate({ diff --git a/clis/amazon/shared.test.js b/plugins/amazon/test/shared.test.js similarity index 98% rename from clis/amazon/shared.test.js rename to plugins/amazon/test/shared.test.js index c8575add..ec040cb9 100644 --- a/clis/amazon/shared.test.js +++ b/plugins/amazon/test/shared.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { __test__ } from './shared.js'; +import { __test__ } from '../shared.js'; describe('amazon shared helpers', () => { it('builds canonical product and discussion URLs from ASINs and product URLs', () => { expect(__test__.buildProductUrl('B0FJS72893')).toBe('https://www.amazon.com/dp/B0FJS72893'); diff --git a/plugins/amazon/webcmd-plugin.json b/plugins/amazon/webcmd-plugin.json new file mode 100644 index 00000000..6048297b --- /dev/null +++ b/plugins/amazon/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "amazon", + "version": "0.1.0", + "description": "Webcmd commands for amazon", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/band/README.md b/plugins/band/README.md new file mode 100644 index 00000000..446ec885 --- /dev/null +++ b/plugins/band/README.md @@ -0,0 +1,20 @@ +# webcmd-plugin-band + +Webcmd commands for band. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/band +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd band bands` | List all Bands you belong to | +| `webcmd band login` | Open band login | +| `webcmd band mentions` | Show Band notifications where you are @mentioned | +| `webcmd band post` | Export full content of a post including comments | +| `webcmd band posts` | List posts from a Band | +| `webcmd band whoami` | Show the current logged-in band account | diff --git a/clis/band/auth.js b/plugins/band/auth.js similarity index 96% rename from clis/band/auth.js rename to plugins/band/auth.js index c9dab7df..f0491029 100644 --- a/clis/band/auth.js +++ b/plugins/band/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasBandSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://www.band.us' }); diff --git a/clis/band/bands.js b/plugins/band/bands.js similarity index 100% rename from clis/band/bands.js rename to plugins/band/bands.js diff --git a/clis/band/mentions.js b/plugins/band/mentions.js similarity index 100% rename from clis/band/mentions.js rename to plugins/band/mentions.js diff --git a/plugins/band/package.json b/plugins/band/package.json new file mode 100644 index 00000000..f57c72e5 --- /dev/null +++ b/plugins/band/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-band", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for band", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/band/post.js b/plugins/band/post.js similarity index 100% rename from clis/band/post.js rename to plugins/band/post.js diff --git a/clis/band/posts.js b/plugins/band/posts.js similarity index 100% rename from clis/band/posts.js rename to plugins/band/posts.js diff --git a/plugins/band/webcmd-plugin.json b/plugins/band/webcmd-plugin.json new file mode 100644 index 00000000..4d9ba6ee --- /dev/null +++ b/plugins/band/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "band", + "version": "0.1.0", + "description": "Webcmd commands for band", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/blinkit/README.md b/plugins/blinkit/README.md new file mode 100644 index 00000000..bf016a06 --- /dev/null +++ b/plugins/blinkit/README.md @@ -0,0 +1,23 @@ +# webcmd-plugin-blinkit + +Webcmd commands for blinkit. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/blinkit +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd blinkit add-to-cart` | Add a Blinkit product to cart | +| `webcmd blinkit cart` | Show the current Blinkit cart | +| `webcmd blinkit checkout` | Review Blinkit checkout totals and blockers without placing an order | +| `webcmd blinkit location` | Show the selected Blinkit delivery location | +| `webcmd blinkit login` | Open blinkit login | +| `webcmd blinkit place-order` | Submit the visible Blinkit final order/payment action. Requires --confirm. | +| `webcmd blinkit product` | Read Blinkit product details for a delivery location | +| `webcmd blinkit search` | Search Blinkit products for a delivery location | +| `webcmd blinkit whoami` | Show the current logged-in blinkit account | diff --git a/clis/blinkit/add-to-cart.js b/plugins/blinkit/add-to-cart.js similarity index 100% rename from clis/blinkit/add-to-cart.js rename to plugins/blinkit/add-to-cart.js diff --git a/clis/blinkit/auth.js b/plugins/blinkit/auth.js similarity index 97% rename from clis/blinkit/auth.js rename to plugins/blinkit/auth.js index 1068b55e..d39a7408 100644 --- a/clis/blinkit/auth.js +++ b/plugins/blinkit/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; import { BASE, DOMAIN } from './utils.js'; async function probeBlinkitIdentity(page) { diff --git a/clis/blinkit/cart.js b/plugins/blinkit/cart.js similarity index 100% rename from clis/blinkit/cart.js rename to plugins/blinkit/cart.js diff --git a/clis/blinkit/checkout.js b/plugins/blinkit/checkout.js similarity index 100% rename from clis/blinkit/checkout.js rename to plugins/blinkit/checkout.js diff --git a/clis/blinkit/location.js b/plugins/blinkit/location.js similarity index 99% rename from clis/blinkit/location.js rename to plugins/blinkit/location.js index fc90a1b6..6353559e 100644 --- a/clis/blinkit/location.js +++ b/plugins/blinkit/location.js @@ -26,4 +26,3 @@ cli({ return [normalizeLocationState(await page.evaluate(LOCATION_EVALUATE))]; }, }); - diff --git a/plugins/blinkit/package.json b/plugins/blinkit/package.json new file mode 100644 index 00000000..101bd164 --- /dev/null +++ b/plugins/blinkit/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-blinkit", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for blinkit", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/blinkit/place-order.js b/plugins/blinkit/place-order.js similarity index 100% rename from clis/blinkit/place-order.js rename to plugins/blinkit/place-order.js diff --git a/clis/blinkit/product.js b/plugins/blinkit/product.js similarity index 100% rename from clis/blinkit/product.js rename to plugins/blinkit/product.js diff --git a/clis/blinkit/search.js b/plugins/blinkit/search.js similarity index 100% rename from clis/blinkit/search.js rename to plugins/blinkit/search.js diff --git a/clis/blinkit/blinkit.test.js b/plugins/blinkit/test/blinkit.test.js similarity index 94% rename from clis/blinkit/blinkit.test.js rename to plugins/blinkit/test/blinkit.test.js index 30dd1e86..9fb6d619 100644 --- a/clis/blinkit/blinkit.test.js +++ b/plugins/blinkit/test/blinkit.test.js @@ -1,15 +1,15 @@ import { describe, expect, it, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ as authTest } from './auth.js'; -import { __test__ as searchTest } from './search.js'; -import { __test__ as productTest } from './product.js'; -import { __test__ as addToCartTest } from './add-to-cart.js'; -import { __test__ as placeOrderTest } from './place-order.js'; -import { normalizeLocationState, resolveCoordinates } from './utils.js'; -import './cart.js'; -import './checkout.js'; -import './location.js'; +import { __test__ as authTest } from '../auth.js'; +import { __test__ as searchTest } from '../search.js'; +import { __test__ as productTest } from '../product.js'; +import { __test__ as addToCartTest } from '../add-to-cart.js'; +import { __test__ as placeOrderTest } from '../place-order.js'; +import { normalizeLocationState, resolveCoordinates } from '../utils.js'; +import '../cart.js'; +import '../checkout.js'; +import '../location.js'; describe('blinkit helpers', () => { it('rejects invalid external args before browser work', () => { diff --git a/clis/blinkit/utils.js b/plugins/blinkit/utils.js similarity index 100% rename from clis/blinkit/utils.js rename to plugins/blinkit/utils.js diff --git a/plugins/blinkit/webcmd-plugin.json b/plugins/blinkit/webcmd-plugin.json new file mode 100644 index 00000000..c4346e5b --- /dev/null +++ b/plugins/blinkit/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "blinkit", + "version": "0.1.0", + "description": "Webcmd commands for blinkit", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/coupang/README.md b/plugins/coupang/README.md new file mode 100644 index 00000000..c6613765 --- /dev/null +++ b/plugins/coupang/README.md @@ -0,0 +1,19 @@ +# webcmd-plugin-coupang + +Webcmd commands for coupang. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/coupang +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd coupang add-to-cart` | Add a Coupang product to cart using logged-in browser session | +| `webcmd coupang login` | Open coupang login | +| `webcmd coupang product` | Read full product detail (price, rating, seller, delivery) for a Coupang product | +| `webcmd coupang search` | Search Coupang products with logged-in browser session | +| `webcmd coupang whoami` | Show the current logged-in coupang account | diff --git a/clis/coupang/add-to-cart.js b/plugins/coupang/add-to-cart.js similarity index 100% rename from clis/coupang/add-to-cart.js rename to plugins/coupang/add-to-cart.js diff --git a/clis/coupang/auth.js b/plugins/coupang/auth.js similarity index 95% rename from clis/coupang/auth.js rename to plugins/coupang/auth.js index 0c03b593..67c0ec56 100644 --- a/clis/coupang/auth.js +++ b/plugins/coupang/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasCoupangSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://www.coupang.com' }); diff --git a/plugins/coupang/package.json b/plugins/coupang/package.json new file mode 100644 index 00000000..bcf8dddc --- /dev/null +++ b/plugins/coupang/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-coupang", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for coupang", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/coupang/product.js b/plugins/coupang/product.js similarity index 100% rename from clis/coupang/product.js rename to plugins/coupang/product.js diff --git a/clis/coupang/search.js b/plugins/coupang/search.js similarity index 100% rename from clis/coupang/search.js rename to plugins/coupang/search.js diff --git a/clis/coupang/coupang.test.js b/plugins/coupang/test/coupang.test.js similarity index 98% rename from clis/coupang/coupang.test.js rename to plugins/coupang/test/coupang.test.js index 7e98e6d8..e2400687 100644 --- a/clis/coupang/coupang.test.js +++ b/plugins/coupang/test/coupang.test.js @@ -1,10 +1,10 @@ import { describe, expect, it } from 'vitest'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './search.js'; -import './product.js'; -import './add-to-cart.js'; -import { parseLimitArg, parsePageArg, requireProductIdArg } from './utils.js'; +import '../search.js'; +import '../product.js'; +import '../add-to-cart.js'; +import { parseLimitArg, parsePageArg, requireProductIdArg } from '../utils.js'; describe('coupang utils — parseLimitArg / parsePageArg (no silent clamp)', () => { it('parseLimitArg returns fallback for empty / undefined', () => { diff --git a/clis/coupang/utils.test.js b/plugins/coupang/test/utils.test.js similarity index 97% rename from clis/coupang/utils.test.js rename to plugins/coupang/test/utils.test.js index 21029f1b..bfa1fca4 100644 --- a/clis/coupang/utils.test.js +++ b/plugins/coupang/test/utils.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { canonicalizeProductUrl, dedupeSearchItems, normalizeProductId, normalizeSearchItem, sanitizeSearchItems, } from './utils.js'; +import { canonicalizeProductUrl, dedupeSearchItems, normalizeProductId, normalizeSearchItem, sanitizeSearchItems, } from '../utils.js'; describe('normalizeProductId', () => { it('extracts product id from canonical path', () => { expect(normalizeProductId('https://www.coupang.com/vp/products/123456789')).toBe('123456789'); diff --git a/clis/coupang/utils.js b/plugins/coupang/utils.js similarity index 100% rename from clis/coupang/utils.js rename to plugins/coupang/utils.js diff --git a/plugins/coupang/webcmd-plugin.json b/plugins/coupang/webcmd-plugin.json new file mode 100644 index 00000000..0174e3c5 --- /dev/null +++ b/plugins/coupang/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "coupang", + "version": "0.1.0", + "description": "Webcmd commands for coupang", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/district/README.md b/plugins/district/README.md new file mode 100644 index 00000000..9ed0d2c2 --- /dev/null +++ b/plugins/district/README.md @@ -0,0 +1,23 @@ +# webcmd-plugin-district + +Webcmd commands for district. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/district +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd district checkout` | Select District movie seats and open the UPI QR payment scanner | +| `webcmd district listings` | List public District by Zomato movies, events, and nearby going-out cards | +| `webcmd district locations` | Search District-supported cities, areas, malls, and places for booking filters | +| `webcmd district login` | Open district login | +| `webcmd district search` | Search District by Zomato across movies, events, dining, stores, activities, and play | +| `webcmd district seats` | List available seats for a District movie showtime | +| `webcmd district set-location` | Set the District browser session location for movie booking filters | +| `webcmd district showtimes` | List District movie showtimes with location, time, cinema, language, price, and format filters | +| `webcmd district whoami` | Show the current logged-in district account | diff --git a/clis/district/_lib.js b/plugins/district/_lib.js similarity index 100% rename from clis/district/_lib.js rename to plugins/district/_lib.js diff --git a/clis/district/auth.js b/plugins/district/auth.js similarity index 96% rename from clis/district/auth.js rename to plugins/district/auth.js index a8fd5bfd..1048a118 100644 --- a/clis/district/auth.js +++ b/plugins/district/auth.js @@ -1,5 +1,5 @@ import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; import { BASE, profileProbe } from './_lib.js'; async function navigateHome(page) { diff --git a/clis/district/checkout.js b/plugins/district/checkout.js similarity index 100% rename from clis/district/checkout.js rename to plugins/district/checkout.js diff --git a/clis/district/listings.js b/plugins/district/listings.js similarity index 100% rename from clis/district/listings.js rename to plugins/district/listings.js diff --git a/clis/district/locations.js b/plugins/district/locations.js similarity index 100% rename from clis/district/locations.js rename to plugins/district/locations.js diff --git a/plugins/district/package.json b/plugins/district/package.json new file mode 100644 index 00000000..ea36f1f8 --- /dev/null +++ b/plugins/district/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-district", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for district", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/district/search.js b/plugins/district/search.js similarity index 100% rename from clis/district/search.js rename to plugins/district/search.js diff --git a/clis/district/seats.js b/plugins/district/seats.js similarity index 100% rename from clis/district/seats.js rename to plugins/district/seats.js diff --git a/clis/district/set-location.js b/plugins/district/set-location.js similarity index 100% rename from clis/district/set-location.js rename to plugins/district/set-location.js diff --git a/clis/district/showtimes.js b/plugins/district/showtimes.js similarity index 100% rename from clis/district/showtimes.js rename to plugins/district/showtimes.js diff --git a/clis/district/auth.test.js b/plugins/district/test/auth.test.js similarity index 97% rename from clis/district/auth.test.js rename to plugins/district/test/auth.test.js index ba8ac646..1d66ff05 100644 --- a/clis/district/auth.test.js +++ b/plugins/district/test/auth.test.js @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './auth.js'; +import '../auth.js'; describe('district auth', () => { it('opens the avatar login modal without waiting for full page load', async () => { diff --git a/clis/district/checkout.test.ts b/plugins/district/test/checkout.test.ts similarity index 97% rename from clis/district/checkout.test.ts rename to plugins/district/test/checkout.test.ts index 482229da..2d18c0bf 100644 --- a/clis/district/checkout.test.ts +++ b/plugins/district/test/checkout.test.ts @@ -1,6 +1,6 @@ import { JSDOM } from 'jsdom'; import { describe, expect, it } from 'vitest'; -import * as checkout from './checkout.js'; +import * as checkout from '../checkout.js'; describe('district checkout payment handoff', () => { it('selects Scan QR to pay before reporting the UPI scanner ready', async () => { diff --git a/plugins/district/webcmd-plugin.json b/plugins/district/webcmd-plugin.json new file mode 100644 index 00000000..f21c8150 --- /dev/null +++ b/plugins/district/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "district", + "version": "0.1.0", + "description": "Webcmd commands for district", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/github/README.md b/plugins/github/README.md new file mode 100644 index 00000000..bd3ecc4b --- /dev/null +++ b/plugins/github/README.md @@ -0,0 +1,16 @@ +# webcmd-plugin-github + +Webcmd commands for github. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/github +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd github login` | Open github login | +| `webcmd github whoami` | Show the current logged-in github account | diff --git a/clis/github/auth.js b/plugins/github/auth.js similarity index 94% rename from clis/github/auth.js rename to plugins/github/auth.js index f0b0a5bf..dce6e4dd 100644 --- a/clis/github/auth.js +++ b/plugins/github/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasGithubSessionCookies(page) { const cookies = await page.getCookies({ url: 'https://github.com' }); diff --git a/plugins/github/package.json b/plugins/github/package.json new file mode 100644 index 00000000..c07bd0bb --- /dev/null +++ b/plugins/github/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-github", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for github", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/plugins/github/webcmd-plugin.json b/plugins/github/webcmd-plugin.json new file mode 100644 index 00000000..d4cebbcb --- /dev/null +++ b/plugins/github/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "github", + "version": "0.1.0", + "description": "Webcmd commands for github", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/hf/README.md b/plugins/hf/README.md new file mode 100644 index 00000000..7f4eab26 --- /dev/null +++ b/plugins/hf/README.md @@ -0,0 +1,21 @@ +# webcmd-plugin-hf + +Webcmd commands for hf. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/hf +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd hf datasets` | Top Hugging Face datasets (downloads / likes / trending / freshness). | +| `webcmd hf login` | Open hf login | +| `webcmd hf models` | Top Hugging Face models (downloads / likes / trending / freshness). | +| `webcmd hf paper` | Hugging Face paper detail by arXiv id (full title / summary / authors / AI keywords) | +| `webcmd hf spaces` | Top Hugging Face Spaces (likes / created_at / last_modified). | +| `webcmd hf top` | Top upvoted Hugging Face papers | +| `webcmd hf whoami` | Show the current logged-in hf account | diff --git a/clis/hf/auth.js b/plugins/hf/auth.js similarity index 95% rename from clis/hf/auth.js rename to plugins/hf/auth.js index b62b6e5d..30d442f4 100644 --- a/clis/hf/auth.js +++ b/plugins/hf/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; // Hugging Face's `token` cookie is httpOnly; use the documented // /api/whoami-v2 endpoint (401 when anonymous) via a no-nav probe. diff --git a/clis/hf/datasets.js b/plugins/hf/datasets.js similarity index 100% rename from clis/hf/datasets.js rename to plugins/hf/datasets.js diff --git a/clis/hf/models.js b/plugins/hf/models.js similarity index 100% rename from clis/hf/models.js rename to plugins/hf/models.js diff --git a/plugins/hf/package.json b/plugins/hf/package.json new file mode 100644 index 00000000..1c83e389 --- /dev/null +++ b/plugins/hf/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-hf", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for hf", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/hf/paper.js b/plugins/hf/paper.js similarity index 100% rename from clis/hf/paper.js rename to plugins/hf/paper.js diff --git a/clis/hf/spaces.js b/plugins/hf/spaces.js similarity index 100% rename from clis/hf/spaces.js rename to plugins/hf/spaces.js diff --git a/clis/hf/hf.test.js b/plugins/hf/test/hf.test.js similarity index 92% rename from clis/hf/hf.test.js rename to plugins/hf/test/hf.test.js index d9636fed..b09ad0e2 100644 --- a/clis/hf/hf.test.js +++ b/plugins/hf/test/hf.test.js @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './top.js'; -import './paper.js'; +import '../top.js'; +import '../paper.js'; describe('hf adapter registry contracts', () => { it('declares hf top columns so paper ids round-trip into hf paper', () => { diff --git a/clis/hf/top.js b/plugins/hf/top.js similarity index 100% rename from clis/hf/top.js rename to plugins/hf/top.js diff --git a/plugins/hf/webcmd-plugin.json b/plugins/hf/webcmd-plugin.json new file mode 100644 index 00000000..d71bd2c1 --- /dev/null +++ b/plugins/hf/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "hf", + "version": "0.1.0", + "description": "Webcmd commands for hf", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/linkedin-learning/README.md b/plugins/linkedin-learning/README.md new file mode 100644 index 00000000..57799abf --- /dev/null +++ b/plugins/linkedin-learning/README.md @@ -0,0 +1,19 @@ +# webcmd-plugin-linkedin-learning + +Webcmd commands for linkedin-learning. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/linkedin-learning +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd linkedin-learning course` | Get LinkedIn Learning course detail by slug or course URL | +| `webcmd linkedin-learning login` | Open linkedin-learning login | +| `webcmd linkedin-learning search` | Search LinkedIn Learning courses, videos, and learning paths by keyword | +| `webcmd linkedin-learning trending` | Browse LinkedIn Learning recommended courses across personalized carousels | +| `webcmd linkedin-learning whoami` | Show the current logged-in linkedin-learning account | diff --git a/clis/linkedin-learning/auth.js b/plugins/linkedin-learning/auth.js similarity index 97% rename from clis/linkedin-learning/auth.js rename to plugins/linkedin-learning/auth.js index c45e53a1..ec36e825 100644 --- a/clis/linkedin-learning/auth.js +++ b/plugins/linkedin-learning/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasLinkedinSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://www.linkedin.com' }); diff --git a/clis/linkedin-learning/course.js b/plugins/linkedin-learning/course.js similarity index 100% rename from clis/linkedin-learning/course.js rename to plugins/linkedin-learning/course.js diff --git a/plugins/linkedin-learning/package.json b/plugins/linkedin-learning/package.json new file mode 100644 index 00000000..57da6a1f --- /dev/null +++ b/plugins/linkedin-learning/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-linkedin-learning", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for linkedin-learning", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/linkedin-learning/search.js b/plugins/linkedin-learning/search.js similarity index 100% rename from clis/linkedin-learning/search.js rename to plugins/linkedin-learning/search.js diff --git a/clis/linkedin-learning/course.test.js b/plugins/linkedin-learning/test/course.test.js similarity index 97% rename from clis/linkedin-learning/course.test.js rename to plugins/linkedin-learning/test/course.test.js index 2bcf613f..946e3440 100644 --- a/clis/linkedin-learning/course.test.js +++ b/plugins/linkedin-learning/test/course.test.js @@ -1,9 +1,9 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './course.js'; +import '../course.js'; -const { parseSlug, parseCourse } = await import('./course.js').then((m) => m.__test__); +const { parseSlug, parseCourse } = await import('../course.js').then((m) => m.__test__); function makePage({ evaluateResult, cookies = [{ name: 'JSESSIONID', value: '"ajax:abc"' }] } = {}) { return { diff --git a/clis/linkedin-learning/search.test.js b/plugins/linkedin-learning/test/search.test.js similarity index 98% rename from clis/linkedin-learning/search.test.js rename to plugins/linkedin-learning/test/search.test.js index 5f107998..29fabff3 100644 --- a/clis/linkedin-learning/search.test.js +++ b/plugins/linkedin-learning/test/search.test.js @@ -1,9 +1,9 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './search.js'; +import '../search.js'; -const { parseLimit, parseAuthors, durationSeconds, averageRating, parseRow, buildFetchScript } = await import('./search.js').then((m) => m.__test__); +const { parseLimit, parseAuthors, durationSeconds, averageRating, parseRow, buildFetchScript } = await import('../search.js').then((m) => m.__test__); function makePage({ evaluateResult, cookies = [{ name: 'JSESSIONID', value: '"ajax:abc"' }] } = {}) { return { diff --git a/clis/linkedin-learning/trending.test.js b/plugins/linkedin-learning/test/trending.test.js similarity index 97% rename from clis/linkedin-learning/trending.test.js rename to plugins/linkedin-learning/test/trending.test.js index ebf671c0..261081c0 100644 --- a/clis/linkedin-learning/trending.test.js +++ b/plugins/linkedin-learning/test/trending.test.js @@ -1,9 +1,9 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './trending.js'; +import '../trending.js'; -const { parseLimit, parseCard } = await import('./trending.js').then((m) => m.__test__); +const { parseLimit, parseCard } = await import('../trending.js').then((m) => m.__test__); function makePage({ evaluateResult, cookies = [{ name: 'JSESSIONID', value: '"ajax:abc"' }] } = {}) { return { diff --git a/clis/linkedin-learning/trending.js b/plugins/linkedin-learning/trending.js similarity index 100% rename from clis/linkedin-learning/trending.js rename to plugins/linkedin-learning/trending.js diff --git a/plugins/linkedin-learning/webcmd-plugin.json b/plugins/linkedin-learning/webcmd-plugin.json new file mode 100644 index 00000000..ad9281c1 --- /dev/null +++ b/plugins/linkedin-learning/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "linkedin-learning", + "version": "0.1.0", + "description": "Webcmd commands for linkedin-learning", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/manus/README.md b/plugins/manus/README.md new file mode 100644 index 00000000..769d22cf --- /dev/null +++ b/plugins/manus/README.md @@ -0,0 +1,22 @@ +# webcmd-plugin-manus + +Webcmd commands for manus. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/manus +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd manus connectors` | List available Manus connectors (integrations). | +| `webcmd manus credits` | Show Manus credit balance and refresh details. | +| `webcmd manus list` | List Manus sessions (tasks). | +| `webcmd manus login` | Open manus login | +| `webcmd manus read` | Show details for a specific Manus session. | +| `webcmd manus skills` | List Manus skills (user-added and system). | +| `webcmd manus status` | Show current Manus user profile and credit summary. | +| `webcmd manus whoami` | Show the current logged-in manus account | diff --git a/clis/manus/_utils.js b/plugins/manus/_utils.js similarity index 100% rename from clis/manus/_utils.js rename to plugins/manus/_utils.js diff --git a/clis/manus/auth.js b/plugins/manus/auth.js similarity index 96% rename from clis/manus/auth.js rename to plugins/manus/auth.js index 0cd730fa..a51d3c60 100644 --- a/clis/manus/auth.js +++ b/plugins/manus/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasManusSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://manus.im' }); diff --git a/clis/manus/connectors.js b/plugins/manus/connectors.js similarity index 100% rename from clis/manus/connectors.js rename to plugins/manus/connectors.js diff --git a/clis/manus/credits.js b/plugins/manus/credits.js similarity index 100% rename from clis/manus/credits.js rename to plugins/manus/credits.js diff --git a/clis/manus/list.js b/plugins/manus/list.js similarity index 100% rename from clis/manus/list.js rename to plugins/manus/list.js diff --git a/plugins/manus/package.json b/plugins/manus/package.json new file mode 100644 index 00000000..5c2d7839 --- /dev/null +++ b/plugins/manus/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-manus", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for manus", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/manus/read.js b/plugins/manus/read.js similarity index 100% rename from clis/manus/read.js rename to plugins/manus/read.js diff --git a/clis/manus/skills.js b/plugins/manus/skills.js similarity index 100% rename from clis/manus/skills.js rename to plugins/manus/skills.js diff --git a/clis/manus/status.js b/plugins/manus/status.js similarity index 100% rename from clis/manus/status.js rename to plugins/manus/status.js diff --git a/clis/manus/manus.test.js b/plugins/manus/test/manus.test.js similarity index 98% rename from clis/manus/manus.test.js rename to plugins/manus/test/manus.test.js index 6aa7745f..d3284b2e 100644 --- a/clis/manus/manus.test.js +++ b/plugins/manus/test/manus.test.js @@ -1,13 +1,13 @@ import { beforeAll, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { isManusUrl } from './_utils.js'; -import './status.js'; -import './list.js'; -import './read.js'; -import './credits.js'; -import './connectors.js'; -import './skills.js'; +import { isManusUrl } from '../_utils.js'; +import '../status.js'; +import '../list.js'; +import '../read.js'; +import '../credits.js'; +import '../connectors.js'; +import '../skills.js'; // ── Mock data (matching spec response samples) ───────────────────────────── diff --git a/plugins/manus/webcmd-plugin.json b/plugins/manus/webcmd-plugin.json new file mode 100644 index 00000000..f3abee47 --- /dev/null +++ b/plugins/manus/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "manus", + "version": "0.1.0", + "description": "Webcmd commands for manus", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/scripts/silent-column-drop-baseline.json b/scripts/silent-column-drop-baseline.json index dd335a6b..0c9e981b 100644 --- a/scripts/silent-column-drop-baseline.json +++ b/scripts/silent-column-drop-baseline.json @@ -1,7 +1,7 @@ [ { "command": "amazon/discussion", - "file": "clis/amazon/discussion.js", + "file": "plugins/amazon/discussion.js", "missing": [ "average_rating_text", "discussion_url", @@ -13,7 +13,7 @@ }, { "command": "amazon/offer", - "file": "clis/amazon/offer.js", + "file": "plugins/amazon/offer.js", "missing": [ "buybox_text", "href", @@ -27,7 +27,7 @@ }, { "command": "amazon/offer", - "file": "clis/amazon/offer.js", + "file": "plugins/amazon/offer.js", "missing": [ "currency", "merchant_info_text", @@ -40,7 +40,7 @@ }, { "command": "amazon/product", - "file": "clis/amazon/product.js", + "file": "plugins/amazon/product.js", "missing": [ "brand_text", "breadcrumbs", @@ -56,7 +56,7 @@ }, { "command": "amazon/product", - "file": "clis/amazon/product.js", + "file": "plugins/amazon/product.js", "missing": [ "breadcrumbs", "bullets", @@ -71,7 +71,7 @@ }, { "command": "amazon/search", - "file": "clis/amazon/search.js", + "file": "plugins/amazon/search.js", "missing": [ "badge_texts", "href", @@ -82,7 +82,7 @@ }, { "command": "amazon/search", - "file": "clis/amazon/search.js", + "file": "plugins/amazon/search.js", "missing": [ "badges", "currency", @@ -95,7 +95,7 @@ }, { "command": "band/post", - "file": "clis/band/post.js", + "file": "plugins/band/post.js", "missing": [ "comments", "photos" @@ -103,14 +103,14 @@ }, { "command": "band/post", - "file": "clis/band/post.js", + "file": "plugins/band/post.js", "missing": [ "depth" ] }, { "command": "band/post", - "file": "clis/band/post.js", + "file": "plugins/band/post.js", "missing": [ "filename", "url" @@ -170,7 +170,7 @@ }, { "command": "coupang/search", - "file": "clis/coupang/search.js", + "file": "plugins/coupang/search.js", "missing": [ "badge", "category", @@ -186,7 +186,7 @@ }, { "command": "coupang/search", - "file": "clis/coupang/search.js", + "file": "plugins/coupang/search.js", "missing": [ "originalPrice", "unitPrice" diff --git a/webcmd-plugin.json b/webcmd-plugin.json index e0d5bf42..a3986ca4 100644 --- a/webcmd-plugin.json +++ b/webcmd-plugin.json @@ -4,6 +4,26 @@ "description": "Webcmd plugin collection", "webcmd": ">=0.2.0", "plugins": { + "amazon": { + "path": "plugins/amazon", + "version": "0.1.0", + "description": "Webcmd commands for amazon", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "amazon-in": { + "path": "plugins/amazon-in", + "version": "0.1.0", + "description": "Webcmd commands for amazon-in", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "apple-podcasts": { "path": "plugins/apple-podcasts", "version": "0.1.0", @@ -34,6 +54,16 @@ "handle": "agentrhq" } }, + "band": { + "path": "plugins/band", + "version": "0.1.0", + "description": "Webcmd commands for band", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "barchart": { "path": "plugins/barchart", "version": "0.1.0", @@ -64,6 +94,16 @@ "handle": "agentrhq" } }, + "blinkit": { + "path": "plugins/blinkit", + "version": "0.1.0", + "description": "Webcmd commands for blinkit", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "bloomberg": { "path": "plugins/bloomberg", "version": "0.1.0", @@ -174,6 +214,16 @@ "handle": "agentrhq" } }, + "coupang": { + "path": "plugins/coupang", + "version": "0.1.0", + "description": "Webcmd commands for coupang", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "crates": { "path": "plugins/crates", "version": "0.1.0", @@ -234,6 +284,16 @@ "handle": "agentrhq" } }, + "district": { + "path": "plugins/district", + "version": "0.1.0", + "description": "Webcmd commands for district", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "dockerhub": { "path": "plugins/dockerhub", "version": "0.1.0", @@ -274,6 +334,16 @@ "handle": "agentrhq" } }, + "github": { + "path": "plugins/github", + "version": "0.1.0", + "description": "Webcmd commands for github", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "github-trending": { "path": "plugins/github-trending", "version": "0.1.0", @@ -344,6 +414,16 @@ "handle": "agentrhq" } }, + "hf": { + "path": "plugins/hf", + "version": "0.1.0", + "description": "Webcmd commands for hf", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "hft": { "path": "plugins/hft", "version": "0.1.0", @@ -444,6 +524,16 @@ "handle": "agentrhq" } }, + "linkedin-learning": { + "path": "plugins/linkedin-learning", + "version": "0.1.0", + "description": "Webcmd commands for linkedin-learning", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "lobsters": { "path": "plugins/lobsters", "version": "0.1.0", @@ -464,6 +554,16 @@ "handle": "agentrhq" } }, + "manus": { + "path": "plugins/manus", + "version": "0.1.0", + "description": "Webcmd commands for manus", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "maven": { "path": "plugins/maven", "version": "0.1.0", From ecc9499fbdef4bc63a72982add36caa6de879f8a Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 17:28:24 +0530 Subject: [PATCH 17/39] refactor: migrate chat and professional adapters to plugins --- cli-manifest.json | 7586 +++------ plugin-command-manifest.json | 14180 ++++++++++------ plugins/chatgpt/README.md | 28 + {clis => plugins}/chatgpt/ask.js | 0 {clis => plugins}/chatgpt/auth.js | 2 +- .../chatgpt/deep-research-result.js | 0 {clis => plugins}/chatgpt/detail.js | 0 {clis => plugins}/chatgpt/history.js | 0 {clis => plugins}/chatgpt/image.js | 0 {clis => plugins}/chatgpt/model.js | 0 {clis => plugins}/chatgpt/new.js | 0 plugins/chatgpt/package.json | 9 + {clis => plugins}/chatgpt/project-file-add.js | 0 {clis => plugins}/chatgpt/project-list.js | 0 {clis => plugins}/chatgpt/read.js | 0 {clis => plugins}/chatgpt/send.js | 0 {clis => plugins}/chatgpt/status.js | 0 .../chatgpt/test}/ask.test.js | 2 +- .../chatgpt/test}/commands.test.js | 24 +- .../chatgpt/test}/envelope.test.js | 2 +- .../chatgpt/test}/image.test.js | 4 +- .../chatgpt/test}/model.test.js | 4 +- .../chatgpt/test}/utils.test.js | 18 +- {clis => plugins}/chatgpt/utils.js | 0 plugins/chatgpt/webcmd-plugin.json | 10 + plugins/claude/README.md | 23 + {clis => plugins}/claude/ask.js | 0 {clis => plugins}/claude/auth.js | 2 +- {clis => plugins}/claude/detail.js | 0 {clis => plugins}/claude/history.js | 0 {clis => plugins}/claude/new.js | 0 plugins/claude/package.json | 9 + {clis => plugins}/claude/read.js | 0 {clis => plugins}/claude/send.js | 0 {clis => plugins}/claude/status.js | 0 .../claude/test}/ask.test.js | 4 +- .../claude/test}/commands.test.js | 12 +- .../claude/test}/utils.test.js | 2 +- {clis => plugins}/claude/utils.js | 0 plugins/claude/webcmd-plugin.json | 10 + plugins/gemini/README.md | 26 + {clis => plugins}/gemini/ask.js | 0 {clis => plugins}/gemini/auth.js | 2 +- .../gemini/deep-research-result.js | 0 {clis => plugins}/gemini/deep-research.js | 0 {clis => plugins}/gemini/detail.js | 0 {clis => plugins}/gemini/history.js | 0 {clis => plugins}/gemini/image.js | 0 {clis => plugins}/gemini/models.js | 0 {clis => plugins}/gemini/new.js | 0 plugins/gemini/package.json | 9 + {clis => plugins}/gemini/read.js | 0 {clis => plugins}/gemini/status.js | 0 .../gemini/test}/ask.test.js | 8 +- .../gemini/test}/commands.test.js | 12 +- .../gemini/test}/deep-research-result.test.js | 4 +- .../gemini/test}/deep-research.test.js | 4 +- .../gemini/test}/models.test.js | 6 +- .../gemini/test}/reply-state.test.js | 2 +- .../gemini/test}/utils.test.js | 2 +- {clis => plugins}/gemini/utils.js | 0 plugins/gemini/webcmd-plugin.json | 10 + plugins/pixiv/README.md | 22 + {clis => plugins}/pixiv/auth.js | 2 +- {clis => plugins}/pixiv/detail.js | 0 {clis => plugins}/pixiv/download.js | 0 {clis => plugins}/pixiv/illusts.js | 0 plugins/pixiv/package.json | 9 + {clis => plugins}/pixiv/ranking.js | 0 {clis => plugins}/pixiv/search.js | 0 .../pixiv/test}/detail.test.js | 4 +- .../pixiv/test}/download.test.js | 4 +- .../pixiv/test}/illusts.test.js | 4 +- plugins/pixiv/test/page-mock.js | 13 + .../pixiv/test}/search.test.js | 4 +- .../pixiv => plugins/pixiv/test}/user.test.js | 4 +- {clis => plugins}/pixiv/user.js | 0 {clis => plugins}/pixiv/utils.js | 0 plugins/pixiv/webcmd-plugin.json | 10 + plugins/practo/README.md | 26 + {clis => plugins}/practo/appointment.js | 0 {clis => plugins}/practo/appointments.js | 0 {clis => plugins}/practo/book-confirm.js | 0 {clis => plugins}/practo/book-preview.js | 0 {clis => plugins}/practo/booking-link.js | 0 {clis => plugins}/practo/cancel.js | 0 {clis => plugins}/practo/contact.js | 0 {clis => plugins}/practo/login.js | 2 +- plugins/practo/package.json | 9 + {clis => plugins}/practo/profile.js | 0 {clis => plugins}/practo/search.js | 0 {clis => plugins}/practo/slots.js | 0 .../practo/test}/practo.test.js | 24 +- {clis => plugins}/practo/utils.js | 0 plugins/practo/webcmd-plugin.json | 10 + plugins/reuters/README.md | 18 + {clis => plugins}/reuters/article-detail.js | 0 {clis => plugins}/reuters/auth.js | 2 +- plugins/reuters/package.json | 9 + {clis => plugins}/reuters/search.js | 0 .../reuters/test}/reuters.test.js | 6 +- {clis => plugins}/reuters/utils.js | 0 plugins/reuters/webcmd-plugin.json | 10 + plugins/suno/README.md | 20 + {clis => plugins}/suno/auth.js | 2 +- {clis => plugins}/suno/download.js | 0 {clis => plugins}/suno/generate.js | 0 {clis => plugins}/suno/list.js | 0 plugins/suno/package.json | 9 + {clis => plugins}/suno/status.js | 0 .../suno/test}/commands.test.js | 6 +- .../suno/test}/download.test.js | 4 +- .../suno/test}/generate.test.js | 4 +- .../suno => plugins/suno/test}/utils.test.js | 2 +- {clis => plugins}/suno/utils.js | 0 plugins/suno/webcmd-plugin.json | 10 + plugins/upwork/README.md | 19 + {clis => plugins}/upwork/auth.js | 2 +- {clis => plugins}/upwork/detail.js | 0 {clis => plugins}/upwork/feed.js | 0 plugins/upwork/package.json | 9 + {clis => plugins}/upwork/search.js | 0 .../upwork/test}/upwork.test.js | 8 +- {clis => plugins}/upwork/utils.js | 0 plugins/upwork/webcmd-plugin.json | 10 + plugins/zepto/README.md | 23 + {clis => plugins}/zepto/add-to-cart.js | 0 {clis => plugins}/zepto/auth.js | 2 +- {clis => plugins}/zepto/cart.js | 0 {clis => plugins}/zepto/checkout.js | 0 {clis => plugins}/zepto/location.js | 0 plugins/zepto/package.json | 9 + {clis => plugins}/zepto/place-order.js | 0 {clis => plugins}/zepto/product.js | 0 {clis => plugins}/zepto/search.js | 0 .../zepto/test}/zepto.test.js | 18 +- {clis => plugins}/zepto/utils.js | 0 plugins/zepto/webcmd-plugin.json | 10 + src/hosted/file-contract.test.ts | 5 +- webcmd-plugin.json | 90 + 140 files changed, 11476 insertions(+), 10994 deletions(-) create mode 100644 plugins/chatgpt/README.md rename {clis => plugins}/chatgpt/ask.js (100%) rename {clis => plugins}/chatgpt/auth.js (96%) rename {clis => plugins}/chatgpt/deep-research-result.js (100%) rename {clis => plugins}/chatgpt/detail.js (100%) rename {clis => plugins}/chatgpt/history.js (100%) rename {clis => plugins}/chatgpt/image.js (100%) rename {clis => plugins}/chatgpt/model.js (100%) rename {clis => plugins}/chatgpt/new.js (100%) create mode 100644 plugins/chatgpt/package.json rename {clis => plugins}/chatgpt/project-file-add.js (100%) rename {clis => plugins}/chatgpt/project-list.js (100%) rename {clis => plugins}/chatgpt/read.js (100%) rename {clis => plugins}/chatgpt/send.js (100%) rename {clis => plugins}/chatgpt/status.js (100%) rename {clis/chatgpt => plugins/chatgpt/test}/ask.test.js (89%) rename {clis/chatgpt => plugins/chatgpt/test}/commands.test.js (98%) rename {clis/chatgpt => plugins/chatgpt/test}/envelope.test.js (99%) rename {clis/chatgpt => plugins/chatgpt/test}/image.test.js (99%) rename {clis/chatgpt => plugins/chatgpt/test}/model.test.js (94%) rename {clis/chatgpt => plugins/chatgpt/test}/utils.test.js (99%) rename {clis => plugins}/chatgpt/utils.js (100%) create mode 100644 plugins/chatgpt/webcmd-plugin.json create mode 100644 plugins/claude/README.md rename {clis => plugins}/claude/ask.js (100%) rename {clis => plugins}/claude/auth.js (97%) rename {clis => plugins}/claude/detail.js (100%) rename {clis => plugins}/claude/history.js (100%) rename {clis => plugins}/claude/new.js (100%) create mode 100644 plugins/claude/package.json rename {clis => plugins}/claude/read.js (100%) rename {clis => plugins}/claude/send.js (100%) rename {clis => plugins}/claude/status.js (100%) rename {clis/claude => plugins/claude/test}/ask.test.js (99%) rename {clis/claude => plugins/claude/test}/commands.test.js (94%) rename {clis/claude => plugins/claude/test}/utils.test.js (98%) rename {clis => plugins}/claude/utils.js (100%) create mode 100644 plugins/claude/webcmd-plugin.json create mode 100644 plugins/gemini/README.md rename {clis => plugins}/gemini/ask.js (100%) rename {clis => plugins}/gemini/auth.js (95%) rename {clis => plugins}/gemini/deep-research-result.js (100%) rename {clis => plugins}/gemini/deep-research.js (100%) rename {clis => plugins}/gemini/detail.js (100%) rename {clis => plugins}/gemini/history.js (100%) rename {clis => plugins}/gemini/image.js (100%) rename {clis => plugins}/gemini/models.js (100%) rename {clis => plugins}/gemini/new.js (100%) create mode 100644 plugins/gemini/package.json rename {clis => plugins}/gemini/read.js (100%) rename {clis => plugins}/gemini/status.js (100%) rename {clis/gemini => plugins/gemini/test}/ask.test.js (99%) rename {clis/gemini => plugins/gemini/test}/commands.test.js (96%) rename {clis/gemini => plugins/gemini/test}/deep-research-result.test.js (98%) rename {clis/gemini => plugins/gemini/test}/deep-research.test.js (99%) rename {clis/gemini => plugins/gemini/test}/models.test.js (99%) rename {clis/gemini => plugins/gemini/test}/reply-state.test.js (99%) rename {clis/gemini => plugins/gemini/test}/utils.test.js (99%) rename {clis => plugins}/gemini/utils.js (100%) create mode 100644 plugins/gemini/webcmd-plugin.json create mode 100644 plugins/pixiv/README.md rename {clis => plugins}/pixiv/auth.js (97%) rename {clis => plugins}/pixiv/detail.js (100%) rename {clis => plugins}/pixiv/download.js (100%) rename {clis => plugins}/pixiv/illusts.js (100%) create mode 100644 plugins/pixiv/package.json rename {clis => plugins}/pixiv/ranking.js (100%) rename {clis => plugins}/pixiv/search.js (100%) rename {clis/pixiv => plugins/pixiv/test}/detail.test.js (98%) rename {clis/pixiv => plugins/pixiv/test}/download.test.js (98%) rename {clis/pixiv => plugins/pixiv/test}/illusts.test.js (98%) create mode 100644 plugins/pixiv/test/page-mock.js rename {clis/pixiv => plugins/pixiv/test}/search.test.js (97%) rename {clis/pixiv => plugins/pixiv/test}/user.test.js (98%) rename {clis => plugins}/pixiv/user.js (100%) rename {clis => plugins}/pixiv/utils.js (100%) create mode 100644 plugins/pixiv/webcmd-plugin.json create mode 100644 plugins/practo/README.md rename {clis => plugins}/practo/appointment.js (100%) rename {clis => plugins}/practo/appointments.js (100%) rename {clis => plugins}/practo/book-confirm.js (100%) rename {clis => plugins}/practo/book-preview.js (100%) rename {clis => plugins}/practo/booking-link.js (100%) rename {clis => plugins}/practo/cancel.js (100%) rename {clis => plugins}/practo/contact.js (100%) rename {clis => plugins}/practo/login.js (88%) create mode 100644 plugins/practo/package.json rename {clis => plugins}/practo/profile.js (100%) rename {clis => plugins}/practo/search.js (100%) rename {clis => plugins}/practo/slots.js (100%) rename {clis/practo => plugins/practo/test}/practo.test.js (94%) rename {clis => plugins}/practo/utils.js (100%) create mode 100644 plugins/practo/webcmd-plugin.json create mode 100644 plugins/reuters/README.md rename {clis => plugins}/reuters/article-detail.js (100%) rename {clis => plugins}/reuters/auth.js (96%) create mode 100644 plugins/reuters/package.json rename {clis => plugins}/reuters/search.js (100%) rename {clis/reuters => plugins/reuters/test}/reuters.test.js (99%) rename {clis => plugins}/reuters/utils.js (100%) create mode 100644 plugins/reuters/webcmd-plugin.json create mode 100644 plugins/suno/README.md rename {clis => plugins}/suno/auth.js (97%) rename {clis => plugins}/suno/download.js (100%) rename {clis => plugins}/suno/generate.js (100%) rename {clis => plugins}/suno/list.js (100%) create mode 100644 plugins/suno/package.json rename {clis => plugins}/suno/status.js (100%) rename {clis/suno => plugins/suno/test}/commands.test.js (98%) rename {clis/suno => plugins/suno/test}/download.test.js (98%) rename {clis/suno => plugins/suno/test}/generate.test.js (99%) rename {clis/suno => plugins/suno/test}/utils.test.js (99%) rename {clis => plugins}/suno/utils.js (100%) create mode 100644 plugins/suno/webcmd-plugin.json create mode 100644 plugins/upwork/README.md rename {clis => plugins}/upwork/auth.js (96%) rename {clis => plugins}/upwork/detail.js (100%) rename {clis => plugins}/upwork/feed.js (100%) create mode 100644 plugins/upwork/package.json rename {clis => plugins}/upwork/search.js (100%) rename {clis/upwork => plugins/upwork/test}/upwork.test.js (99%) rename {clis => plugins}/upwork/utils.js (100%) create mode 100644 plugins/upwork/webcmd-plugin.json create mode 100644 plugins/zepto/README.md rename {clis => plugins}/zepto/add-to-cart.js (100%) rename {clis => plugins}/zepto/auth.js (95%) rename {clis => plugins}/zepto/cart.js (100%) rename {clis => plugins}/zepto/checkout.js (100%) rename {clis => plugins}/zepto/location.js (100%) create mode 100644 plugins/zepto/package.json rename {clis => plugins}/zepto/place-order.js (100%) rename {clis => plugins}/zepto/product.js (100%) rename {clis => plugins}/zepto/search.js (100%) rename {clis/zepto => plugins/zepto/test}/zepto.test.js (97%) rename {clis => plugins}/zepto/utils.js (100%) create mode 100644 plugins/zepto/webcmd-plugin.json diff --git a/cli-manifest.json b/cli-manifest.json index 6a177917..d7233e70 100644 --- a/cli-manifest.json +++ b/cli-manifest.json @@ -1100,756 +1100,585 @@ "navigateBefore": "https://www.bigbasket.com" }, { - "site": "chatgpt", + "site": "chatgpt-app", "name": "ask", - "description": "Send a prompt to ChatGPT web and wait for the response", + "description": "Send a prompt and wait for the AI response (send + wait + read)", "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "domain": "localhost", + "strategy": "public", + "browser": false, "args": [ { - "name": "prompt", + "name": "text", "type": "str", "required": true, "positional": true, "help": "Prompt to send" }, { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait for response" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Continue an existing ChatGPT conversation ID or /c/ URL" - }, - { - "name": "project", + "name": "model", "type": "str", "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" - }, - { - "name": "wait", - "type": "boolean", - "default": true, - "required": false, - "help": "Wait for the assistant response after sending" + "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] }, { - "name": "deep-research", - "type": "boolean", - "default": false, + "name": "timeout", + "type": "int", + "default": 30, "required": false, - "help": "Enable ChatGPT Deep Research (Deep Research)" + "help": "Max seconds to wait for response (default: 30)" }, { - "name": "web-search", - "type": "boolean", - "default": false, + "name": "image", + "type": "str", "required": false, - "help": "Enable ChatGPT Web Search (Web Search)" + "help": "Path to local image to attach (optional)" } ], "columns": [ - "conversationId", - "conversationUrl", - "tool", - "response" + "Role", + "Text" ], "type": "js", - "modulePath": "chatgpt/ask.js", - "sourceFile": "chatgpt/ask.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "chatgpt-app/ask.js", + "sourceFile": "chatgpt-app/ask.js" }, { - "site": "chatgpt", - "name": "deep-research-result", - "description": "Read a ChatGPT Deep Research report or progress from the conversation payload", + "site": "chatgpt-app", + "name": "model", + "description": "Switch ChatGPT Desktop model/mode (auto, instant, thinking, 5.2-instant, 5.2-thinking)", "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "domain": "localhost", + "strategy": "public", + "browser": false, "args": [ { - "name": "id", + "name": "model", "type": "str", "required": true, "positional": true, - "help": "Conversation ID or full /c/ URL" - }, - { - "name": "wait", - "type": "boolean", - "default": false, - "required": false, - "help": "Wait until Deep Research completes or becomes extractable" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait when --wait is true" - }, - { - "name": "stable", - "type": "int", - "default": 6, - "required": false, - "help": "Seconds the report text must remain unchanged when --wait is true" + "help": "Model to switch to", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] } ], "columns": [ - "conversationId", - "status", - "report", - "sources", - "progress", - "asyncTaskConversationId", - "widgetSessionId", - "asyncStatus", - "venusMessageType", - "venusStatus", - "waitingForUserUntil", - "planTitle", - "planId", - "url", - "method", - "diagnostics" - ], - "tags": [ - "search" + "Status", + "Model" ], "type": "js", - "modulePath": "chatgpt/deep-research-result.js", - "sourceFile": "chatgpt/deep-research-result.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "chatgpt-app/model.js", + "sourceFile": "chatgpt-app/model.js" }, { - "site": "chatgpt", - "name": "detail", - "description": "Open a ChatGPT web conversation by ID and read its messages", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "site": "chatgpt-app", + "name": "new", + "description": "Open a new chat in ChatGPT Desktop App", + "access": "write", + "domain": "localhost", + "strategy": "public", + "browser": false, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Conversation ID or full /c/ URL" - }, - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" - }, - { - "name": "wait", + "name": "temp", "type": "boolean", "default": false, "required": false, - "help": "Wait until the conversation stops generating and stabilizes" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait when --wait is true" - }, - { - "name": "stable", - "type": "int", - "default": 6, - "required": false, - "help": "Seconds the final messages must remain unchanged when --wait is true" + "help": "Open a temporary chat with privacy protection" } ], "columns": [ - "Index", - "Role", - "Text", - "Generating", - "StableSeconds" + "Status" ], "type": "js", - "modulePath": "chatgpt/detail.js", - "sourceFile": "chatgpt/detail.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "chatgpt-app/new.js", + "sourceFile": "chatgpt-app/new.js" }, { - "site": "chatgpt", - "name": "history", - "description": "List visible ChatGPT web conversation history from the sidebar", + "site": "chatgpt-app", + "name": "read", + "description": "Read the last visible message from the focused ChatGPT Desktop window", "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max conversations to show" - } - ], + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [], "columns": [ - "Index", - "Id", - "Title", - "Url" + "Role", + "Text" ], "type": "js", - "modulePath": "chatgpt/history.js", - "sourceFile": "chatgpt/history.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "chatgpt-app/read.js", + "sourceFile": "chatgpt-app/read.js" }, { - "site": "chatgpt", - "name": "image", - "description": "Generate images with ChatGPT web and save them locally", + "site": "chatgpt-app", + "name": "send", + "description": "Send a message to the active ChatGPT Desktop App window", "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "domain": "localhost", + "strategy": "public", + "browser": false, "args": [ { - "name": "prompt", + "name": "text", "type": "str", "required": true, "positional": true, - "help": "Image prompt to send to ChatGPT" - }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Local image path to attach before prompting; comma-separated paths are supported", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - }, - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Start image generation inside a ChatGPT project ID or /g/g-p- URL" + "help": "Message to send" }, { - "name": "op", + "name": "model", "type": "str", "required": false, - "help": "Output directory (default: ~/Pictures/chatgpt)", - "file": { - "direction": "output", - "pathKind": "directory", - "multiple": false, - "defaultPath": "~/Pictures/chatgpt" - } - }, - { - "name": "sd", - "type": "boolean", - "default": false, - "required": false, - "help": "Skip download shorthand; only show ChatGPT link" - }, - { - "name": "timeout", - "type": "int", - "default": 240, - "required": false, - "help": "Max seconds for the overall command (default: 240)" + "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] } ], "columns": [ - "status", - "file", - "link" + "Status" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "chatgpt/image.js", - "sourceFile": "chatgpt/image.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "chatgpt-app/send.js", + "sourceFile": "chatgpt-app/send.js" }, { - "site": "chatgpt", - "name": "login", - "description": "Open chatgpt login", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "site": "chatgpt-app", + "name": "status", + "description": "Check if ChatGPT Desktop App is running natively on macOS", + "access": "read", + "domain": "localhost", + "strategy": "public", + "browser": false, "args": [], "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" + "Status" ], "type": "js", - "modulePath": "chatgpt/auth.js", - "sourceFile": "chatgpt/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "chatgpt-app/status.js", + "sourceFile": "chatgpt-app/status.js" }, { - "site": "chatgpt", - "name": "model", - "description": "Switch ChatGPT web model or intelligence level (GPT-5.6 Pro, fast, balanced, advanced, very-high, pro)", + "site": "confluence", + "name": "create", + "description": "Create a Confluence page from Markdown or storage XHTML", "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "model", - "type": "str", + "name": "space", + "type": "string", "required": true, - "positional": true, - "help": "ChatGPT model or intelligence level to switch to", + "help": "Cloud space id, or Data Center space key" + }, + { + "name": "title", + "type": "string", + "required": true, + "help": "Page title" + }, + { + "name": "file", + "type": "string", + "required": true, + "help": "Markdown file path" + }, + { + "name": "parent", + "type": "string", + "required": false, + "help": "Optional parent page id" + }, + { + "name": "representation", + "type": "string", + "default": "markdown", + "required": false, + "help": "Input file format", "choices": [ - "fast", - "speed", - "instant", - "balanced", - "balance", - "medium", - "advanced", - "high", - "thinking", - "very-high", - "ultra", - "xhigh", - "x-high", - "extra-high", - "very high", - "gpt-5.6-pro", - "gpt-5-6-pro", - "gpt-5.6-sol-pro", - "gpt-5-6-sol-pro", - "gpt-5.6", - "gpt-5-6", - "5.6-pro", - "5.6", - "pro", - "professional" + "markdown", + "storage" ] }, { - "name": "project", - "type": "str", + "name": "execute", + "type": "boolean", "required": false, - "valueRequired": true, - "help": "Open a ChatGPT project ID or /g/g-p- URL before switching intelligence level" + "help": "Actually create the remote page" } ], "columns": [ - "Status", - "Model" + "status", + "id", + "title", + "spaceId", + "spaceKey", + "version", + "url" ], "type": "js", - "modulePath": "chatgpt/model.js", - "sourceFile": "chatgpt/model.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "confluence/create.js", + "sourceFile": "confluence/create.js" }, { - "site": "chatgpt", - "name": "new", - "description": "Start a new ChatGPT web conversation", + "site": "confluence", + "name": "page", + "description": "Confluence page by id with storage and Markdown body", "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "project", + "name": "id", "type": "str", - "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + "required": true, + "positional": true, + "help": "Confluence page id" } ], "columns": [ - "Status" + "id", + "title", + "status", + "spaceId", + "spaceKey", + "version", + "url" ], "type": "js", - "modulePath": "chatgpt/new.js", - "sourceFile": "chatgpt/new.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "confluence/page.js", + "sourceFile": "confluence/page.js" }, { - "site": "chatgpt", - "name": "project-file-add", - "description": "Upload files to a ChatGPT project as project knowledge (not just conversation attachments)", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "site": "confluence", + "name": "search", + "description": "Search Confluence content with CQL", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "file", + "name": "cql", "type": "str", "required": true, "positional": true, - "help": "Local file path(s) to upload; comma-separated paths are supported", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "application/pdf", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } + "help": "CQL query, e.g. \"type = page and title ~ \\\"RCA\\\"\"" }, { - "name": "id", - "type": "str", - "required": true, - "help": "Project ID or /g/g-p- URL" - } - ], - "columns": [ - "Status", - "File" - ], - "type": "js", - "modulePath": "chatgpt/project-file-add.js", - "sourceFile": "chatgpt/project-file-add.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "project-list", - "description": "List visible ChatGPT projects from the sidebar", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ + "name": "space", + "type": "string", + "required": false, + "help": "Limit search to a Confluence space key" + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max projects to show" + "help": "Max results to return (1-100)" } ], "columns": [ - "Index", - "Id", - "Title", - "Url" - ], - "type": "js", - "modulePath": "chatgpt/project-list.js", - "sourceFile": "chatgpt/project-list.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "read", - "description": "Read messages in the current ChatGPT web conversation", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" - } + "id", + "title", + "type", + "spaceKey", + "status", + "lastModified", + "url" ], - "columns": [ - "Index", - "Role", - "Text" + "tags": [ + "search" ], "type": "js", - "modulePath": "chatgpt/read.js", - "sourceFile": "chatgpt/read.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "confluence/search.js", + "sourceFile": "confluence/search.js" }, { - "site": "chatgpt", - "name": "send", - "description": "Send a prompt to ChatGPT web without waiting for the response", + "site": "confluence", + "name": "update", + "description": "Update a Confluence page body from Markdown or storage XHTML", "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "prompt", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Prompt to send" + "help": "Confluence page id" }, { - "name": "new", - "type": "boolean", - "default": false, + "name": "file", + "type": "string", + "required": true, + "help": "Markdown file path" + }, + { + "name": "title", + "type": "string", "required": false, - "help": "Start a new chat before sending" + "help": "Optional replacement title; defaults to current title" }, { - "name": "conversation", - "type": "str", + "name": "version-message", + "type": "string", "required": false, - "valueRequired": true, - "help": "Continue an existing ChatGPT conversation ID or /c/ URL" + "help": "Confluence version message" }, { - "name": "project", - "type": "str", + "name": "representation", + "type": "string", + "default": "markdown", "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + "help": "Input file format", + "choices": [ + "markdown", + "storage" + ] + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually update the remote page" } ], "columns": [ - "Status", - "InjectedText" + "status", + "id", + "title", + "spaceId", + "spaceKey", + "version", + "url" ], "type": "js", - "modulePath": "chatgpt/send.js", - "sourceFile": "chatgpt/send.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "confluence/update.js", + "sourceFile": "confluence/update.js" }, { - "site": "chatgpt", - "name": "status", - "description": "Check ChatGPT web page availability and login state", + "site": "discord-app", + "name": "channels", + "description": "List channels in the current Discord server", "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [], "columns": [ - "Status", - "Login", - "Url" + "Index", + "Channel", + "Type", + "guild_id", + "channel_id", + "url" ], "type": "js", - "modulePath": "chatgpt/status.js", - "sourceFile": "chatgpt/status.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "discord-app/channels.js", + "sourceFile": "discord-app/channels.js", + "navigateBefore": true }, { - "site": "chatgpt", - "name": "whoami", - "description": "Show the current logged-in chatgpt account", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", + "site": "discord-app", + "name": "delete", + "description": "Delete a message by its ID in the active Discord channel", + "access": "write", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [], + "args": [ + { + "name": "message_id", + "type": "string", + "required": true, + "positional": true, + "help": "The ID of the message to delete (visible via Developer Mode or the read command)" + } + ], "columns": [ - "logged_in", - "site", - "user_id", - "name" + "status", + "message" ], "type": "js", - "modulePath": "chatgpt/auth.js", - "sourceFile": "chatgpt/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "discord-app/delete.js", + "sourceFile": "discord-app/delete.js", + "navigateBefore": true }, { - "site": "chatgpt-app", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", - "access": "write", + "site": "discord-app", + "name": "goto", + "description": "Open a Discord channel by id/name/url without sending messages", + "access": "read", "domain": "localhost", - "strategy": "public", - "browser": false, + "strategy": "ui", + "browser": true, "args": [ { - "name": "text", + "name": "guild", "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" + "required": false, + "help": "Guild/server id or visible name" }, { - "name": "model", + "name": "channel", "type": "str", "required": false, - "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] + "help": "Channel id or visible name" }, { - "name": "timeout", - "type": "int", - "default": 30, + "name": "url", + "type": "str", "required": false, - "help": "Max seconds to wait for response (default: 30)" + "help": "Discord channel URL" }, { - "name": "image", + "name": "timeout", "type": "str", + "default": "8", "required": false, - "help": "Path to local image to attach (optional)" + "help": "Seconds to wait for Discord to show the route (default: 8)" } ], "columns": [ - "Role", - "Text" + "Status", + "guild_id", + "channel_id", + "url" ], "type": "js", - "modulePath": "chatgpt-app/ask.js", - "sourceFile": "chatgpt-app/ask.js" + "modulePath": "discord-app/goto.js", + "sourceFile": "discord-app/goto.js", + "navigateBefore": true }, { - "site": "chatgpt-app", - "name": "model", - "description": "Switch ChatGPT Desktop model/mode (auto, instant, thinking, 5.2-instant, 5.2-thinking)", + "site": "discord-app", + "name": "members", + "description": "List online members in the current Discord channel", "access": "read", "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "model", - "type": "str", - "required": true, - "positional": true, - "help": "Model to switch to", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] - } - ], + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "Status", - "Model" + "Index", + "Name", + "Status" ], "type": "js", - "modulePath": "chatgpt-app/model.js", - "sourceFile": "chatgpt-app/model.js" + "modulePath": "discord-app/members.js", + "sourceFile": "discord-app/members.js", + "navigateBefore": true }, { - "site": "chatgpt-app", - "name": "new", - "description": "Open a new chat in ChatGPT Desktop App", - "access": "write", + "site": "discord-app", + "name": "read", + "description": "Read recent messages from the active or targeted Discord channel", + "access": "read", "domain": "localhost", - "strategy": "public", - "browser": false, + "strategy": "ui", + "browser": true, "args": [ { - "name": "temp", - "type": "boolean", - "default": false, + "name": "count", + "type": "str", + "default": "20", "required": false, - "help": "Open a temporary chat with privacy protection" + "help": "Number of messages to read (default: 20)" + }, + { + "name": "guild", + "type": "str", + "required": false, + "help": "Guild/server id or visible name for targeted reads" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Channel id or visible name for targeted reads" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Discord channel URL to open before reading" } ], "columns": [ - "Status" + "Author", + "Time", + "Message", + "channel_id", + "message_id" ], "type": "js", - "modulePath": "chatgpt-app/new.js", - "sourceFile": "chatgpt-app/new.js" + "modulePath": "discord-app/read.js", + "sourceFile": "discord-app/read.js", + "navigateBefore": true }, { - "site": "chatgpt-app", - "name": "read", - "description": "Read the last visible message from the focused ChatGPT Desktop window", + "site": "discord-app", + "name": "search", + "description": "Search messages in the current Discord server/channel (Cmd+F)", "access": "read", "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [], + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + } + ], "columns": [ - "Role", - "Text" + "Index", + "Author", + "Message" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "chatgpt-app/read.js", - "sourceFile": "chatgpt-app/read.js" + "modulePath": "discord-app/search.js", + "sourceFile": "discord-app/search.js", + "navigateBefore": true }, { - "site": "chatgpt-app", + "site": "discord-app", "name": "send", - "description": "Send a message to the active ChatGPT Desktop App window", + "description": "Send a message in the active Discord channel", "access": "write", "domain": "localhost", - "strategy": "public", - "browser": false, + "strategy": "ui", + "browser": true, "args": [ { "name": "text", @@ -1857,2184 +1686,1866 @@ "required": true, "positional": true, "help": "Message to send" - }, - { - "name": "model", - "type": "str", - "required": false, - "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] } ], "columns": [ "Status" ], "type": "js", - "modulePath": "chatgpt-app/send.js", - "sourceFile": "chatgpt-app/send.js" + "modulePath": "discord-app/send.js", + "sourceFile": "discord-app/send.js", + "navigateBefore": true }, { - "site": "chatgpt-app", - "name": "status", - "description": "Check if ChatGPT Desktop App is running natively on macOS", + "site": "discord-app", + "name": "servers", + "description": "List all Discord servers (guilds) in the sidebar", "access": "read", "domain": "localhost", - "strategy": "public", - "browser": false, + "strategy": "ui", + "browser": true, "args": [], "columns": [ - "Status" + "Index", + "Server", + "guild_id", + "url" ], "type": "js", - "modulePath": "chatgpt-app/status.js", - "sourceFile": "chatgpt-app/status.js" + "modulePath": "discord-app/servers.js", + "sourceFile": "discord-app/servers.js", + "navigateBefore": true }, { - "site": "claude", - "name": "ask", - "description": "Send a prompt to Claude and get the response", - "access": "write", - "domain": "claude.ai", - "strategy": "cookie", + "site": "discord-app", + "name": "status", + "description": "Check active CDP connection to Discord Desktop", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "discord-app/status.js", + "sourceFile": "discord-app/status.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "thread-read", + "description": "Read recent messages from a Discord thread/post by id or URL", + "access": "read", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "prompt", + "name": "thread", "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" + "required": false, + "help": "Thread/post id, or a full Discord thread/post URL" }, { - "name": "timeout", - "type": "int", - "default": 120, + "name": "count", + "type": "str", + "default": "20", "required": false, - "help": "Max seconds to wait for response" + "help": "Number of messages to read (default: 20)" }, { - "name": "new", - "type": "boolean", - "default": false, + "name": "guild", + "type": "str", "required": false, - "help": "Start a new chat before sending" + "help": "Parent guild/server id or visible name" }, { - "name": "model", + "name": "channel", "type": "str", - "default": "sonnet", "required": false, - "help": "Model to use: sonnet, opus, or haiku", - "choices": [ - "sonnet", - "opus", - "haiku" - ] + "help": "Parent forum/channel id or visible name" }, { - "name": "think", - "type": "boolean", - "default": false, + "name": "url", + "type": "str", + "required": false, + "help": "Discord thread/post URL" + } + ], + "columns": [ + "Author", + "Time", + "Message", + "channel_id", + "message_id" + ], + "type": "js", + "modulePath": "discord-app/thread-read.js", + "sourceFile": "discord-app/thread-read.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "threads", + "description": "List visible Discord forum/thread posts in the active or targeted channel", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "limit", + "type": "str", + "default": "30", "required": false, - "help": "Enable Adaptive thinking" + "help": "Maximum thread/post cards to return (default: 30)" }, { - "name": "file", + "name": "guild", "type": "str", "required": false, - "help": "Attach a file (image, PDF, text) with the prompt", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "application/pdf", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } + "help": "Guild/server id or visible name for targeted thread listing" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Forum/channel id or visible name for targeted thread listing" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Discord forum/channel URL to open before listing threads" } ], "columns": [ - "response" + "Index", + "Thread", + "Author", + "Updated", + "Preview", + "guild_id", + "channel_id", + "thread_id", + "url" ], "type": "js", - "modulePath": "claude/ask.js", - "sourceFile": "claude/ask.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "discord-app/threads.js", + "sourceFile": "discord-app/threads.js", + "navigateBefore": true }, { - "site": "claude", - "name": "detail", - "description": "Open a Claude conversation by ID and read its messages", - "access": "read", - "domain": "claude.ai", + "site": "facebook", + "name": "add-friend", + "description": "Send a friend request on Facebook", + "access": "write", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Conversation ID (UUID from /chat/)" + "help": "Facebook username or profile URL" } ], "columns": [ - "Index", - "Role", - "Text" + "status", + "username" ], "type": "js", - "modulePath": "claude/detail.js", - "sourceFile": "claude/detail.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "facebook/add-friend.js", + "sourceFile": "facebook/add-friend.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "claude", - "name": "history", - "description": "List conversation history from Claude /recents", + "site": "facebook", + "name": "events", + "description": "Browse Facebook event categories", "access": "read", - "domain": "claude.ai", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 15, "required": false, - "help": "Max conversations to show" + "help": "Number of categories" } ], "columns": [ - "Index", - "Id", - "Title", - "Url" + "index", + "name" ], "type": "js", - "modulePath": "claude/history.js", - "sourceFile": "claude/history.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "facebook/events.js", + "sourceFile": "facebook/events.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "claude", - "name": "login", - "description": "Open claude login", - "access": "write", - "domain": "claude.ai", + "site": "facebook", + "name": "feed", + "description": "Get your Facebook news feed", + "access": "read", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of posts" + } + ], "columns": [ - "status", - "logged_in", - "site", - "user_id", - "org_name", - "org_uuid", - "action", - "verify_command" + "index", + "author", + "content", + "likes", + "comments", + "shares" ], "type": "js", - "modulePath": "claude/auth.js", - "sourceFile": "claude/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "facebook/feed.js", + "sourceFile": "facebook/feed.js", + "navigateBefore": false }, { - "site": "claude", - "name": "new", - "description": "Start a new conversation in Claude", + "site": "facebook", + "name": "friends", + "description": "Get Facebook friend suggestions", "access": "read", - "domain": "claude.ai", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of friend suggestions" + } + ], "columns": [ - "Status" + "index", + "name", + "mutual" ], "type": "js", - "modulePath": "claude/new.js", - "sourceFile": "claude/new.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "facebook/friends.js", + "sourceFile": "facebook/friends.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "claude", - "name": "read", - "description": "Read the current Claude conversation", + "site": "facebook", + "name": "groups", + "description": "List your Facebook groups", "access": "read", - "domain": "claude.ai", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of groups" + } + ], "columns": [ - "Index", - "Role", - "Text" + "index", + "name", + "last_post", + "url" ], "type": "js", - "modulePath": "claude/read.js", - "sourceFile": "claude/read.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "send", - "description": "Send a prompt to Claude without waiting for the response", + "modulePath": "facebook/groups.js", + "sourceFile": "facebook/groups.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "join-group", + "description": "Join a Facebook group", "access": "write", - "domain": "claude.ai", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", + "name": "group", "type": "str", "required": true, "positional": true, - "help": "Prompt to send" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" + "help": "Group ID or URL path (e.g. '1876150192925481' or group name)" } ], "columns": [ - "Status", - "SubmittedBy", - "InjectedText" + "status", + "group" ], "type": "js", - "modulePath": "claude/send.js", - "sourceFile": "claude/send.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "facebook/join-group.js", + "sourceFile": "facebook/join-group.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "claude", - "name": "status", - "description": "Check Claude page availability and login state", - "access": "read", - "domain": "claude.ai", + "site": "facebook", + "name": "login", + "description": "Open facebook login", + "access": "write", + "domain": "facebook.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Status", - "Login", - "Url" + "status", + "logged_in", + "site", + "user_id", + "vanity", + "profile_url", + "action", + "verify_command" ], "type": "js", - "modulePath": "claude/status.js", - "sourceFile": "claude/status.js", + "modulePath": "facebook/auth.js", + "sourceFile": "facebook/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "claude", - "name": "whoami", - "description": "Show the current logged-in claude account", + "site": "facebook", + "name": "marketplace-inbox", + "description": "List recent Facebook Marketplace buyer/seller conversations", "access": "read", - "domain": "claude.ai", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of conversations to return" + } + ], "columns": [ - "logged_in", - "site", - "user_id", - "org_name", - "org_uuid" + "index", + "buyer", + "listing", + "snippet", + "time", + "unread" ], "type": "js", - "modulePath": "claude/auth.js", - "sourceFile": "claude/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "facebook/marketplace-inbox.js", + "sourceFile": "facebook/marketplace-inbox.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "confluence", - "name": "create", - "description": "Create a Confluence page from Markdown or storage XHTML", - "access": "write", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "site": "facebook", + "name": "marketplace-listings", + "description": "List your Facebook Marketplace seller listings", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "space", - "type": "string", - "required": true, - "help": "Cloud space id, or Data Center space key" - }, - { - "name": "title", - "type": "string", - "required": true, - "help": "Page title" - }, - { - "name": "file", - "type": "string", - "required": true, - "help": "Markdown file path" - }, - { - "name": "parent", - "type": "string", - "required": false, - "help": "Optional parent page id" - }, - { - "name": "representation", - "type": "string", - "default": "markdown", - "required": false, - "help": "Input file format", - "choices": [ - "markdown", - "storage" - ] - }, - { - "name": "execute", - "type": "boolean", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Actually create the remote page" + "help": "Number of listings to return" } ], "columns": [ - "status", - "id", + "index", "title", - "spaceId", - "spaceKey", - "version", - "url" + "price", + "status", + "listed", + "clicks", + "actions" ], "type": "js", - "modulePath": "confluence/create.js", - "sourceFile": "confluence/create.js" + "modulePath": "facebook/marketplace-listings.js", + "sourceFile": "facebook/marketplace-listings.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "confluence", - "name": "page", - "description": "Confluence page by id with storage and Markdown body", + "site": "facebook", + "name": "memories", + "description": "Get your Facebook memories (On This Day)", "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Confluence page id" + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of memories" } ], "columns": [ - "id", - "title", - "status", - "spaceId", - "spaceKey", - "version", - "url" + "index", + "source", + "content", + "time" ], "type": "js", - "modulePath": "confluence/page.js", - "sourceFile": "confluence/page.js" + "modulePath": "facebook/memories.js", + "sourceFile": "facebook/memories.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "confluence", - "name": "search", - "description": "Search Confluence content with CQL", + "site": "facebook", + "name": "notifications", + "description": "Get recent Facebook notifications (includes unread / time / url / notif_id / notif_type columns)", "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, "args": [ - { - "name": "cql", - "type": "str", - "required": true, - "positional": true, - "help": "CQL query, e.g. \"type = page and title ~ \\\"RCA\\\"\"" - }, - { - "name": "space", - "type": "string", - "required": false, - "help": "Limit search to a Confluence space key" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 15, "required": false, - "help": "Max results to return (1-100)" + "help": "Number of notifications (1-100)" } ], "columns": [ - "id", - "title", - "type", - "spaceKey", - "status", - "lastModified", - "url" - ], - "tags": [ - "search" + "index", + "unread", + "text", + "time", + "url", + "notif_id", + "notif_type" ], "type": "js", - "modulePath": "confluence/search.js", - "sourceFile": "confluence/search.js" + "modulePath": "facebook/notifications.js", + "sourceFile": "facebook/notifications.js", + "navigateBefore": false }, { - "site": "confluence", - "name": "update", - "description": "Update a Confluence page body from Markdown or storage XHTML", - "access": "write", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "site": "facebook", + "name": "profile", + "description": "Get Facebook user/page profile info", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Confluence page id" - }, - { - "name": "file", - "type": "string", - "required": true, - "help": "Markdown file path" - }, - { - "name": "title", - "type": "string", - "required": false, - "help": "Optional replacement title; defaults to current title" - }, - { - "name": "version-message", - "type": "string", - "required": false, - "help": "Confluence version message" - }, - { - "name": "representation", - "type": "string", - "default": "markdown", - "required": false, - "help": "Input file format", - "choices": [ - "markdown", - "storage" - ] - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually update the remote page" + "help": "Facebook username or page name" } ], "columns": [ - "status", - "id", - "title", - "spaceId", - "spaceKey", - "version", + "name", + "username", + "friends", + "followers", "url" ], "type": "js", - "modulePath": "confluence/update.js", - "sourceFile": "confluence/update.js" + "modulePath": "facebook/profile.js", + "sourceFile": "facebook/profile.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "discord-app", - "name": "channels", - "description": "List channels in the current Discord server", + "site": "facebook", + "name": "search", + "description": "Search Facebook for people, pages, or posts", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Channel", - "Type", - "guild_id", - "channel_id", - "url" - ], - "type": "js", - "modulePath": "discord-app/channels.js", - "sourceFile": "discord-app/channels.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "delete", - "description": "Delete a message by its ID in the active Discord channel", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "www.facebook.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "message_id", - "type": "string", + "name": "query", + "type": "str", "required": true, "positional": true, - "help": "The ID of the message to delete (visible via Developer Mode or the read command)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "discord-app/delete.js", - "sourceFile": "discord-app/delete.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "goto", - "description": "Open a Discord channel by id/name/url without sending messages", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "guild", - "type": "str", - "required": false, - "help": "Guild/server id or visible name" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Channel id or visible name" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Discord channel URL" + "help": "Search query" }, { - "name": "timeout", - "type": "str", - "default": "8", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Seconds to wait for Discord to show the route (default: 8)" + "help": "Number of results" } ], "columns": [ - "Status", - "guild_id", - "channel_id", + "index", + "title", + "text", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "discord-app/goto.js", - "sourceFile": "discord-app/goto.js", - "navigateBefore": true + "modulePath": "facebook/search.js", + "sourceFile": "facebook/search.js", + "navigateBefore": false }, { - "site": "discord-app", - "name": "members", - "description": "List online members in the current Discord channel", + "site": "facebook", + "name": "whoami", + "description": "Show the current logged-in facebook account", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "facebook.com", + "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Index", - "Name", - "Status" + "logged_in", + "site", + "user_id", + "vanity", + "profile_url" ], "type": "js", - "modulePath": "discord-app/members.js", - "sourceFile": "discord-app/members.js", - "navigateBefore": true + "modulePath": "facebook/auth.js", + "sourceFile": "facebook/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "read", - "description": "Read recent messages from the active or targeted Discord channel", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "geogebra", + "name": "add-circle", + "description": "Create a circle by center+radius or center+point", + "access": "write", + "example": "webcmd geogebra add-circle --center A --radius 3", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, "args": [ { - "name": "count", - "type": "str", - "default": "20", - "required": false, - "help": "Number of messages to read (default: 20)" - }, - { - "name": "guild", + "name": "center", "type": "str", - "required": false, - "help": "Guild/server id or visible name for targeted reads" + "required": true, + "help": "Center point label (e.g. A)" }, { - "name": "channel", + "name": "radius", "type": "str", "required": false, - "help": "Channel id or visible name for targeted reads" + "help": "Radius value (number) or a point label on the circle" }, { - "name": "url", + "name": "point", "type": "str", "required": false, - "help": "Discord channel URL to open before reading" + "help": "Alternative: a point label on the circle (use instead of --radius for Circle(center,point))" } ], "columns": [ - "Author", - "Time", - "Message", - "channel_id", - "message_id" + "label", + "center", + "radius" ], "type": "js", - "modulePath": "discord-app/read.js", - "sourceFile": "discord-app/read.js", - "navigateBefore": true + "modulePath": "geogebra/add-circle.js", + "sourceFile": "geogebra/add-circle.js", + "navigateBefore": false }, { - "site": "discord-app", - "name": "search", - "description": "Search messages in the current Discord server/channel (Cmd+F)", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "geogebra", + "name": "add-line", + "description": "Create a line through two points or a segment between two points", + "access": "write", + "example": "webcmd geogebra add-line --points A,B --type segment", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, "args": [ { - "name": "query", + "name": "points", "type": "str", "required": true, - "positional": true, - "help": "Search query" + "help": "Two point labels separated by comma (e.g. \"A,B\")" + }, + { + "name": "type", + "type": "str", + "default": "line", + "required": false, + "help": "Type: line, segment, or ray (default: line)", + "choices": [ + "line", + "segment", + "ray" + ] } ], "columns": [ - "Index", - "Author", - "Message" - ], - "tags": [ - "search" + "label", + "type", + "points" ], "type": "js", - "modulePath": "discord-app/search.js", - "sourceFile": "discord-app/search.js", - "navigateBefore": true + "modulePath": "geogebra/add-line.js", + "sourceFile": "geogebra/add-line.js", + "navigateBefore": false }, { - "site": "discord-app", - "name": "send", - "description": "Send a message in the active Discord channel", + "site": "geogebra", + "name": "add-point", + "description": "Create a point with given label and coordinates", "access": "write", - "domain": "localhost", - "strategy": "ui", + "example": "webcmd geogebra add-point --name A --coords 1,2", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, "args": [ { - "name": "text", + "name": "name", "type": "str", "required": true, - "positional": true, - "help": "Message to send" + "help": "Point label (e.g. A, B, P1)" + }, + { + "name": "coords", + "type": "str", + "required": true, + "help": "Coordinates as x,y (e.g. \"1,2\")" } ], "columns": [ - "Status" + "name", + "x", + "y" ], "type": "js", - "modulePath": "discord-app/send.js", - "sourceFile": "discord-app/send.js", - "navigateBefore": true + "modulePath": "geogebra/add-point.js", + "sourceFile": "geogebra/add-point.js", + "navigateBefore": false }, { - "site": "discord-app", - "name": "servers", - "description": "List all Discord servers (guilds) in the sidebar", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "geogebra", + "name": "add-polygon", + "description": "Create a polygon from a list of point labels", + "access": "write", + "example": "webcmd geogebra add-polygon --points A,B,C", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, - "args": [], - "columns": [ - "Index", - "Server", - "guild_id", - "url" + "args": [ + { + "name": "points", + "type": "str", + "required": true, + "help": "Comma-separated point labels (e.g. \"A,B,C\" or \"A,B,C,D\")" + } ], - "type": "js", - "modulePath": "discord-app/servers.js", - "sourceFile": "discord-app/servers.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "status", - "description": "Check active CDP connection to Discord Desktop", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], "columns": [ - "Status", - "Url", - "Title" + "label", + "vertices" ], "type": "js", - "modulePath": "discord-app/status.js", - "sourceFile": "discord-app/status.js", - "navigateBefore": true + "modulePath": "geogebra/add-polygon.js", + "sourceFile": "geogebra/add-polygon.js", + "navigateBefore": false }, { - "site": "discord-app", - "name": "thread-read", - "description": "Read recent messages from a Discord thread/post by id or URL", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "geogebra", + "name": "eval", + "description": "Execute one or more GeoGebra command strings (semicolon-separated)", + "access": "write", + "example": "webcmd geogebra eval \"A=(0,0);B=(4,0);c=Circle(A,B);d=Circle(B,A);C=Intersect(c,d,1);Polygon(A,B,C)\"", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, "args": [ { - "name": "thread", - "type": "str", - "required": false, - "help": "Thread/post id, or a full Discord thread/post URL" - }, - { - "name": "count", - "type": "str", - "default": "20", - "required": false, - "help": "Number of messages to read (default: 20)" - }, - { - "name": "guild", - "type": "str", - "required": false, - "help": "Parent guild/server id or visible name" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Parent forum/channel id or visible name" - }, - { - "name": "url", + "name": "command", "type": "str", - "required": false, - "help": "Discord thread/post URL" + "required": true, + "positional": true, + "help": "GeoGebra command string (use ; to chain multiple commands)" } ], "columns": [ - "Author", - "Time", - "Message", - "channel_id", - "message_id" + "command", + "result" ], "type": "js", - "modulePath": "discord-app/thread-read.js", - "sourceFile": "discord-app/thread-read.js", - "navigateBefore": true + "modulePath": "geogebra/eval.js", + "sourceFile": "geogebra/eval.js", + "navigateBefore": false }, { - "site": "discord-app", - "name": "threads", - "description": "List visible Discord forum/thread posts in the active or targeted channel", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "geogebra", + "name": "hexagon", + "description": "Draw a regular hexagon centered at the origin", + "access": "write", + "example": "webcmd geogebra hexagon --size 3", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, "args": [ { - "name": "limit", - "type": "str", - "default": "30", - "required": false, - "help": "Maximum thread/post cards to return (default: 30)" - }, - { - "name": "guild", - "type": "str", - "required": false, - "help": "Guild/server id or visible name for targeted thread listing" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Forum/channel id or visible name for targeted thread listing" - }, - { - "name": "url", + "name": "size", "type": "str", + "default": "2", "required": false, - "help": "Discord forum/channel URL to open before listing threads" + "help": "Radius of the hexagon (default: 2)" } ], "columns": [ - "Index", - "Thread", - "Author", - "Updated", - "Preview", - "guild_id", - "channel_id", - "thread_id", - "url" + "step", + "result" ], "type": "js", - "modulePath": "discord-app/threads.js", - "sourceFile": "discord-app/threads.js", - "navigateBefore": true + "modulePath": "geogebra/hexagon.js", + "sourceFile": "geogebra/hexagon.js", + "navigateBefore": false }, { - "site": "facebook", - "name": "add-friend", - "description": "Send a friend request on Facebook", - "access": "write", - "domain": "www.facebook.com", - "strategy": "cookie", + "site": "geogebra", + "name": "info", + "description": "Get detailed properties of a GeoGebra object", + "access": "read", + "example": "webcmd geogebra info --name A", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, "args": [ { - "name": "username", + "name": "name", "type": "str", "required": true, - "positional": true, - "help": "Facebook username or profile URL" + "help": "Object label (e.g. A, c1, poly1)" } ], "columns": [ - "status", - "username" + "property", + "value" ], "type": "js", - "modulePath": "facebook/add-friend.js", - "sourceFile": "facebook/add-friend.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "geogebra/info.js", + "sourceFile": "geogebra/info.js", + "navigateBefore": false }, { - "site": "facebook", - "name": "events", - "description": "Browse Facebook event categories", + "site": "geogebra", + "name": "list", + "description": "List all geometric objects on the GeoGebra canvas", "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 15, + "name": "type", + "type": "str", "required": false, - "help": "Number of categories" + "help": "Filter by object type (e.g. \"point\", \"line\", \"circle\")" } ], "columns": [ - "index", - "name" - ], - "type": "js", - "modulePath": "facebook/events.js", - "sourceFile": "facebook/events.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "feed", - "description": "Get your Facebook news feed", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of posts" - } - ], - "columns": [ - "index", - "author", - "content", - "likes", - "comments", - "shares" + "name", + "type", + "value", + "visible" ], "type": "js", - "modulePath": "facebook/feed.js", - "sourceFile": "facebook/feed.js", + "modulePath": "geogebra/list.js", + "sourceFile": "geogebra/list.js", "navigateBefore": false }, { - "site": "facebook", - "name": "friends", - "description": "Get Facebook friend suggestions", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", + "site": "geogebra", + "name": "triangle", + "description": "Draw an equilateral triangle from a horizontal base segment", + "access": "write", + "example": "webcmd geogebra triangle --size 4", + "domain": "www.geogebra.org", + "strategy": "public", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 10, + "name": "size", + "type": "str", + "default": "2", "required": false, - "help": "Number of friend suggestions" + "help": "Side length of the triangle (default: 2)" } ], "columns": [ - "index", - "name", - "mutual" + "step", + "result" ], "type": "js", - "modulePath": "facebook/friends.js", - "sourceFile": "facebook/friends.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "geogebra/triangle.js", + "sourceFile": "geogebra/triangle.js", + "navigateBefore": false }, { - "site": "facebook", - "name": "groups", - "description": "List your Facebook groups", - "access": "read", - "domain": "www.facebook.com", + "site": "grok", + "name": "ask", + "description": "Send a message to Grok and get response", + "access": "write", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", + "name": "prompt", + "type": "string", + "required": true, + "positional": true, + "help": "Prompt to send to Grok" + }, + { + "name": "timeout", "type": "int", - "default": 20, + "default": 120, "required": false, - "help": "Number of groups" + "help": "Max seconds to wait for response (default: 120)" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending (default: false)" } ], "columns": [ - "index", - "name", - "last_post", - "url" + "response" ], "type": "js", - "modulePath": "facebook/groups.js", - "sourceFile": "facebook/groups.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "grok/ask.js", + "sourceFile": "grok/ask.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" }, { - "site": "facebook", - "name": "join-group", - "description": "Join a Facebook group", + "site": "grok", + "name": "delete", + "description": "Delete a Grok conversation by ID. Grok takes effect immediately with no confirmation dialog — require --yes to actually delete.", "access": "write", - "domain": "www.facebook.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "group", - "type": "str", + "name": "id", + "type": "string", "required": true, "positional": true, - "help": "Group ID or URL path (e.g. '1876150192925481' or group name)" + "help": "Conversation UUID or grok.com/c/ URL" + }, + { + "name": "yes", + "type": "boolean", + "default": false, + "required": false, + "help": "Actually delete (default is a dry-run preview)" } ], "columns": [ "status", - "group" - ], - "type": "js", - "modulePath": "facebook/join-group.js", - "sourceFile": "facebook/join-group.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "login", - "description": "Open facebook login", - "access": "write", - "domain": "facebook.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "vanity", - "profile_url", - "action", - "verify_command" + "id" ], "type": "js", - "modulePath": "facebook/auth.js", - "sourceFile": "facebook/auth.js", - "navigateBefore": false, + "modulePath": "grok/delete.js", + "sourceFile": "grok/delete.js", + "navigateBefore": "https://grok.com", "siteSession": "persistent" }, { - "site": "facebook", - "name": "marketplace-inbox", - "description": "List recent Facebook Marketplace buyer/seller conversations", + "site": "grok", + "name": "detail", + "description": "Open a Grok conversation by ID and read its messages", "access": "read", - "domain": "www.facebook.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Session ID (UUID) or full https://grok.com/c/ URL" + }, + { + "name": "markdown", + "type": "boolean", + "default": false, "required": false, - "help": "Number of conversations to return" + "help": "Emit assistant replies as markdown" } ], "columns": [ - "index", - "buyer", - "listing", - "snippet", - "time", - "unread" + "Role", + "Text" ], "type": "js", - "modulePath": "facebook/marketplace-inbox.js", - "sourceFile": "facebook/marketplace-inbox.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "grok/detail.js", + "sourceFile": "grok/detail.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "facebook", - "name": "marketplace-listings", - "description": "List your Facebook Marketplace seller listings", + "site": "grok", + "name": "export", + "description": "Export all visible Grok conversation history metadata", "access": "read", - "domain": "www.facebook.com", + "example": "webcmd grok export -f yaml", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 0, "required": false, - "help": "Number of listings to return" + "help": "Max conversations to export; 0 means all loaded history" + }, + { + "name": "maxScrolls", + "type": "int", + "default": 80, + "required": false, + "help": "Max history-list scroll rounds when limit is 0 (max 500)" } ], "columns": [ "index", + "id", "title", - "price", - "status", - "listed", - "clicks", - "actions" + "date", + "url" ], "type": "js", - "modulePath": "facebook/marketplace-listings.js", - "sourceFile": "facebook/marketplace-listings.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "grok/export.js", + "sourceFile": "grok/export.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "facebook", - "name": "memories", - "description": "Get your Facebook memories (On This Day)", + "site": "grok", + "name": "export-all", + "description": "Export Grok conversation history and each conversation transcript", "access": "read", - "domain": "www.facebook.com", + "example": "webcmd grok export-all --limit 5 -f json", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 0, "required": false, - "help": "Number of memories" - } - ], - "columns": [ - "index", - "source", - "content", - "time" - ], - "type": "js", - "modulePath": "facebook/memories.js", - "sourceFile": "facebook/memories.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "notifications", - "description": "Get recent Facebook notifications (includes unread / time / url / notif_id / notif_type columns)", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ + "help": "Max conversations to export; 0 means all loaded history" + }, { - "name": "limit", + "name": "offset", "type": "int", - "default": 15, + "default": 0, "required": false, - "help": "Number of notifications (1-100)" + "help": "Skip this many conversations before exporting" + }, + { + "name": "manifestPath", + "type": "string", + "default": "", + "required": false, + "help": "Optional grok/export JSON manifest path; skips history dialog and visits listed /c pages directly" + }, + { + "name": "maxScrolls", + "type": "int", + "default": 80, + "required": false, + "help": "Max history-list scroll rounds when limit is 0 (max 500)" + }, + { + "name": "pageScrolls", + "type": "int", + "default": 30, + "required": false, + "help": "Max per-conversation scroll-to-bottom rounds (max 200)" + }, + { + "name": "pageTimeoutMs", + "type": "int", + "default": 30000, + "required": false, + "help": "Max wait for each conversation page to show messages" + }, + { + "name": "delayMinMs", + "type": "int", + "default": 0, + "required": false, + "help": "Minimum polite delay after a conversation page loads" + }, + { + "name": "delayMaxMs", + "type": "int", + "default": 5000, + "required": false, + "help": "Maximum polite delay after a conversation page loads" } ], "columns": [ "index", - "unread", - "text", - "time", + "id", + "title", + "date", "url", - "notif_id", - "notif_type" + "status", + "messageCount", + "error", + "messagesJson" ], "type": "js", - "modulePath": "facebook/notifications.js", - "sourceFile": "facebook/notifications.js", - "navigateBefore": false + "modulePath": "grok/export-all.js", + "sourceFile": "grok/export-all.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "facebook", - "name": "profile", - "description": "Get Facebook user/page profile info", + "site": "grok", + "name": "history", + "description": "List recent Grok conversations from the sidebar (requires login)", "access": "read", - "domain": "www.facebook.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Facebook username or page name" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max conversations to show (default 20, max 100)" } ], "columns": [ - "name", - "username", - "friends", - "followers", - "url" + "Index", + "Title", + "Url" ], "type": "js", - "modulePath": "facebook/profile.js", - "sourceFile": "facebook/profile.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "grok/history.js", + "sourceFile": "grok/history.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "facebook", - "name": "search", - "description": "Search Facebook for people, pages, or posts", - "access": "read", - "domain": "www.facebook.com", + "site": "grok", + "name": "image", + "description": "Generate images on grok.com and return image URLs", + "access": "write", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", - "type": "str", + "name": "prompt", + "type": "string", "required": true, "positional": true, - "help": "Search query" + "help": "Image generation prompt" }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 10, + "default": 240, "required": false, - "help": "Number of results" + "help": "Max seconds to wait for the image (default: 240)" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending (default: false)" + }, + { + "name": "count", + "type": "int", + "default": 1, + "required": false, + "help": "Minimum images to wait for before returning (default: 1)" + }, + { + "name": "out", + "type": "string", + "default": "", + "required": false, + "help": "Directory to save downloaded images (uses browser session to bypass auth)" } ], "columns": [ - "index", - "title", - "text", - "url" - ], - "tags": [ - "search" + "url", + "width", + "height", + "path" ], "type": "js", - "modulePath": "facebook/search.js", - "sourceFile": "facebook/search.js", - "navigateBefore": false + "modulePath": "grok/image.js", + "sourceFile": "grok/image.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" }, { - "site": "facebook", - "name": "whoami", - "description": "Show the current logged-in facebook account", - "access": "read", - "domain": "facebook.com", + "site": "grok", + "name": "login", + "description": "Open grok login", + "access": "write", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ + "status", "logged_in", "site", "user_id", - "vanity", - "profile_url" + "name", + "action", + "verify_command" ], "type": "js", - "modulePath": "facebook/auth.js", - "sourceFile": "facebook/auth.js", + "modulePath": "grok/auth.js", + "sourceFile": "grok/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", - "name": "ask", - "description": "Send a prompt to Gemini and return only the assistant response", + "site": "grok", + "name": "new", + "description": "Start a new conversation in Grok", "access": "write", - "domain": "gemini.google.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "model", - "type": "string", - "required": false, - "help": "Gemini model to use (e.g. \"2.5-flash\"). Use \"webcmd gemini models\" to list available values." - }, - { - "name": "timeout", - "type": "int", - "default": 60, - "required": false, - "help": "Max seconds to wait (default: 60)" - }, - { - "name": "new", - "type": "str", - "default": "false", - "required": false, - "help": "Start a new chat first (true/false, default: false)" - }, - { - "name": "thinking", - "type": "str", - "default": null, - "required": false, - "help": "Thinking level: standard or extended (omitted = leave unchanged)" - } - ], + "args": [], "columns": [ - "response" + "Status" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "gemini/ask.js", - "sourceFile": "gemini/ask.js", + "modulePath": "grok/new.js", + "sourceFile": "grok/new.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", - "name": "deep-research", - "description": "Start a Gemini Deep Research run and confirm it", + "site": "grok", + "name": "pin", + "description": "Pin a Grok conversation by ID", "access": "write", - "domain": "gemini.google.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", - "type": "str", + "name": "id", + "type": "string", "required": true, "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 180, - "required": false, - "help": "Max seconds for the overall command (default: 180; confirm-wait clamps internally to 6-20s)" - }, - { - "name": "tool", - "type": "str", - "required": false, - "help": "Override tool label (default: Deep Research)" - }, - { - "name": "confirm", - "type": "str", - "required": false, - "help": "Override confirm button label (default: Start research)" + "help": "Conversation UUID or grok.com/c/ URL" } ], "columns": [ "status", - "url" - ], - "tags": [ - "search" + "id" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "gemini/deep-research.js", - "sourceFile": "gemini/deep-research.js", - "navigateBefore": false, + "modulePath": "grok/pin.js", + "sourceFile": "grok/pin.js", + "navigateBefore": "https://grok.com", "siteSession": "persistent" }, { - "site": "gemini", - "name": "deep-research-result", - "description": "Export Deep Research report URL from a Gemini conversation", + "site": "grok", + "name": "read", + "description": "Read messages in the current Grok conversation", "access": "read", - "domain": "gemini.google.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", - "type": "str", - "required": false, - "positional": true, - "help": "Conversation title or URL (optional; defaults to latest conversation)" - }, - { - "name": "match", - "type": "str", - "default": "contains", - "required": false, - "help": "Match mode", - "choices": [ - "contains", - "exact" - ] - }, - { - "name": "timeout", - "type": "int", - "default": 120, + "name": "markdown", + "type": "boolean", + "default": false, "required": false, - "help": "Max seconds to wait for Docs export (default: 120)" + "help": "Emit assistant replies as markdown" } ], "columns": [ - "response" - ], - "tags": [ - "search" + "Role", + "Text" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "gemini/deep-research-result.js", - "sourceFile": "gemini/deep-research-result.js", + "modulePath": "grok/read.js", + "sourceFile": "grok/read.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", - "name": "detail", - "description": "Open a Gemini web conversation by id, URL, or sidebar title and read its turns", - "access": "read", - "domain": "gemini.google.com", + "site": "grok", + "name": "send", + "description": "Fire-and-forget: send a prompt to Grok without waiting for the reply", + "access": "write", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", + "name": "prompt", "type": "str", "required": true, "positional": true, - "help": "Conversation id, /app/ URL, or sidebar title" + "help": "Prompt to send to Grok" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" } ], "columns": [ - "Index", - "Role", - "Text" + "Status", + "Prompt" ], "type": "js", - "modulePath": "gemini/detail.js", - "sourceFile": "gemini/detail.js", + "modulePath": "grok/send.js", + "sourceFile": "grok/send.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", - "name": "history", - "description": "List visible Gemini web conversation history from the sidebar", + "site": "grok", + "name": "status", + "description": "Check Grok page availability, login state, current session and model", "access": "read", - "domain": "gemini.google.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max conversations to show" - } - ], + "args": [], "columns": [ - "Index", - "Id", - "Title", + "Status", + "Login", + "Model", + "SessionId", "Url" ], "type": "js", - "modulePath": "gemini/history.js", - "sourceFile": "gemini/history.js", + "modulePath": "grok/status.js", + "sourceFile": "grok/status.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", - "name": "image", - "description": "Generate images with Gemini web and save them locally", + "site": "grok", + "name": "unpin", + "description": "Unpin a Grok conversation by ID", "access": "write", - "domain": "gemini.google.com", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", - "type": "str", + "name": "id", + "type": "string", "required": true, "positional": true, - "help": "Image prompt to send to Gemini" - }, - { - "name": "rt", - "type": "str", - "default": "1:1", - "required": false, - "help": "Ratio shorthand for aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3)" - }, - { - "name": "st", - "type": "str", - "default": "", - "required": false, - "help": "Style shorthand, e.g. anime, icon, watercolor" - }, - { - "name": "op", - "type": "str", - "default": "~/tmp/gemini-images", - "required": false, - "help": "Output directory shorthand" - }, - { - "name": "sd", - "type": "boolean", - "default": false, - "required": false, - "help": "Skip download shorthand; only show Gemini page link" - }, - { - "name": "timeout", - "type": "int", - "default": 240, - "required": false, - "help": "Max seconds for the overall command (default: 240)" + "help": "Conversation UUID or grok.com/c/ URL" } ], "columns": [ "status", - "file", - "link" + "id" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "gemini/image.js", - "sourceFile": "gemini/image.js", - "navigateBefore": false, + "modulePath": "grok/pin.js", + "sourceFile": "grok/pin.js", + "navigateBefore": "https://grok.com", "siteSession": "persistent" }, { - "site": "gemini", - "name": "login", - "description": "Open gemini login", - "access": "write", - "domain": "gemini.google.com", + "site": "grok", + "name": "whoami", + "description": "Show the current logged-in grok account", + "access": "read", + "domain": "grok.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "status", "logged_in", "site", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "gemini/auth.js", - "sourceFile": "gemini/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "models", - "description": "List available Gemini models from the web UI", - "access": "read", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "model", - "thinkingValues" + "user_id", + "name" ], "type": "js", - "modulePath": "gemini/models.js", - "sourceFile": "gemini/models.js", + "modulePath": "grok/auth.js", + "sourceFile": "grok/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", - "name": "new", - "description": "Start a new conversation in Gemini web chat", - "access": "read", - "domain": "gemini.google.com", + "site": "instagram", + "name": "collection-create", + "description": "Create a new Instagram saved-posts collection (folder)", + "access": "write", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "Status", - "Action" + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Name of the collection to create" + } ], - "type": "js", - "modulePath": "gemini/new.js", - "sourceFile": "gemini/new.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "read", - "description": "Read the turns visible in the current Gemini web conversation", - "access": "read", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [], "columns": [ - "Index", - "Role", - "Text" + "status", + "collectionId", + "collectionName", + "mediaCount" ], "type": "js", - "modulePath": "gemini/read.js", - "sourceFile": "gemini/read.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/collection-create.js", + "sourceFile": "instagram/collection-create.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "gemini", - "name": "status", - "description": "Check Gemini web page availability and login state", - "access": "read", - "domain": "gemini.google.com", + "site": "instagram", + "name": "collection-delete", + "description": "Delete an Instagram saved-posts collection (folder) by name or id", + "access": "write", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "Status", - "Login", - "Url" + "args": [ + { + "name": "target", + "type": "str", + "required": true, + "positional": true, + "help": "Collection name (case-insensitive) or numeric collection_id" + } ], - "type": "js", - "modulePath": "gemini/status.js", - "sourceFile": "gemini/status.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "whoami", - "description": "Show the current logged-in gemini account", - "access": "read", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [], "columns": [ - "logged_in", - "site", - "name" + "status", + "collectionId", + "collectionName" ], "type": "js", - "modulePath": "gemini/auth.js", - "sourceFile": "gemini/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/collection-delete.js", + "sourceFile": "instagram/collection-delete.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "geogebra", - "name": "add-circle", - "description": "Create a circle by center+radius or center+point", + "site": "instagram", + "name": "comment", + "description": "Comment on an Instagram post", "access": "write", - "example": "webcmd geogebra add-circle --center A --radius 3", - "domain": "www.geogebra.org", - "strategy": "public", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "center", + "name": "username", "type": "str", "required": true, - "help": "Center point label (e.g. A)" + "positional": true, + "help": "Username of the post author" }, { - "name": "radius", + "name": "text", "type": "str", - "required": false, - "help": "Radius value (number) or a point label on the circle" + "required": true, + "positional": true, + "help": "Comment text" }, { - "name": "point", - "type": "str", + "name": "index", + "type": "int", + "default": 1, "required": false, - "help": "Alternative: a point label on the circle (use instead of --radius for Circle(center,point))" + "help": "Post index (1 = most recent)" } ], "columns": [ - "label", - "center", - "radius" + "status", + "user", + "text" ], "type": "js", - "modulePath": "geogebra/add-circle.js", - "sourceFile": "geogebra/add-circle.js", - "navigateBefore": false + "modulePath": "instagram/comment.js", + "sourceFile": "instagram/comment.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "geogebra", - "name": "add-line", - "description": "Create a line through two points or a segment between two points", - "access": "write", - "example": "webcmd geogebra add-line --points A,B --type segment", - "domain": "www.geogebra.org", - "strategy": "public", + "site": "instagram", + "name": "download", + "description": "Download images and videos from Instagram posts and reels", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "points", + "name": "url", "type": "str", "required": true, - "help": "Two point labels separated by comma (e.g. \"A,B\")" + "positional": true, + "help": "Instagram post / reel / tv URL" }, { - "name": "type", + "name": "path", "type": "str", - "default": "line", + "default": "~/Downloads/Instagram", "required": false, - "help": "Type: line, segment, or ray (default: line)", - "choices": [ - "line", - "segment", - "ray" - ] + "help": "Download directory" } ], - "columns": [ - "label", - "type", - "points" - ], "type": "js", - "modulePath": "geogebra/add-line.js", - "sourceFile": "geogebra/add-line.js", + "modulePath": "instagram/download.js", + "sourceFile": "instagram/download.js", "navigateBefore": false }, { - "site": "geogebra", - "name": "add-point", - "description": "Create a point with given label and coordinates", - "access": "write", - "example": "webcmd geogebra add-point --name A --coords 1,2", - "domain": "www.geogebra.org", - "strategy": "public", + "site": "instagram", + "name": "explore", + "description": "Instagram explore/discover trending posts", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "name", - "type": "str", - "required": true, - "help": "Point label (e.g. A, B, P1)" - }, - { - "name": "coords", - "type": "str", - "required": true, - "help": "Coordinates as x,y (e.g. \"1,2\")" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts" } ], "columns": [ - "name", - "x", - "y" + "rank", + "user", + "caption", + "likes", + "comments", + "type" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "geogebra/add-point.js", - "sourceFile": "geogebra/add-point.js", - "navigateBefore": false + "modulePath": "instagram/explore.js", + "sourceFile": "instagram/explore.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "geogebra", - "name": "add-polygon", - "description": "Create a polygon from a list of point labels", + "site": "instagram", + "name": "follow", + "description": "Follow an Instagram user", "access": "write", - "example": "webcmd geogebra add-polygon --points A,B,C", - "domain": "www.geogebra.org", - "strategy": "public", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "points", + "name": "username", "type": "str", "required": true, - "help": "Comma-separated point labels (e.g. \"A,B,C\" or \"A,B,C,D\")" + "positional": true, + "help": "Instagram username to follow" } ], "columns": [ - "label", - "vertices" + "status", + "username" ], "type": "js", - "modulePath": "geogebra/add-polygon.js", - "sourceFile": "geogebra/add-polygon.js", - "navigateBefore": false + "modulePath": "instagram/follow.js", + "sourceFile": "instagram/follow.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "geogebra", - "name": "eval", - "description": "Execute one or more GeoGebra command strings (semicolon-separated)", - "access": "write", - "example": "webcmd geogebra eval \"A=(0,0);B=(4,0);c=Circle(A,B);d=Circle(B,A);C=Intersect(c,d,1);Polygon(A,B,C)\"", - "domain": "www.geogebra.org", - "strategy": "public", + "site": "instagram", + "name": "followers", + "description": "List followers of an Instagram user", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "command", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "GeoGebra command string (use ; to chain multiple commands)" + "help": "Instagram username" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of followers" } ], "columns": [ - "command", - "result" + "rank", + "username", + "name", + "verified", + "private" ], "type": "js", - "modulePath": "geogebra/eval.js", - "sourceFile": "geogebra/eval.js", - "navigateBefore": false + "modulePath": "instagram/followers.js", + "sourceFile": "instagram/followers.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "geogebra", - "name": "hexagon", - "description": "Draw a regular hexagon centered at the origin", - "access": "write", - "example": "webcmd geogebra hexagon --size 3", - "domain": "www.geogebra.org", - "strategy": "public", + "site": "instagram", + "name": "following", + "description": "List accounts an Instagram user is following", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "size", + "name": "username", "type": "str", - "default": "2", + "required": true, + "positional": true, + "help": "Instagram username" + }, + { + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Radius of the hexagon (default: 2)" + "help": "Number of accounts" } ], "columns": [ - "step", - "result" + "rank", + "username", + "name", + "verified", + "private" ], "type": "js", - "modulePath": "geogebra/hexagon.js", - "sourceFile": "geogebra/hexagon.js", - "navigateBefore": false + "modulePath": "instagram/following.js", + "sourceFile": "instagram/following.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "geogebra", - "name": "info", - "description": "Get detailed properties of a GeoGebra object", - "access": "read", - "example": "webcmd geogebra info --name A", - "domain": "www.geogebra.org", - "strategy": "public", + "site": "instagram", + "name": "like", + "description": "Like an Instagram post", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "name", + "name": "username", "type": "str", "required": true, - "help": "Object label (e.g. A, c1, poly1)" + "positional": true, + "help": "Username of the post author" + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Post index (1 = most recent)" } ], "columns": [ - "property", - "value" + "status", + "user", + "post" ], "type": "js", - "modulePath": "geogebra/info.js", - "sourceFile": "geogebra/info.js", - "navigateBefore": false + "modulePath": "instagram/like.js", + "sourceFile": "instagram/like.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "geogebra", - "name": "list", - "description": "List all geometric objects on the GeoGebra canvas", - "access": "read", - "domain": "www.geogebra.org", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "type", - "type": "str", - "required": false, - "help": "Filter by object type (e.g. \"point\", \"line\", \"circle\")" - } - ], - "columns": [ - "name", - "type", - "value", - "visible" - ], - "type": "js", - "modulePath": "geogebra/list.js", - "sourceFile": "geogebra/list.js", - "navigateBefore": false - }, - { - "site": "geogebra", - "name": "triangle", - "description": "Draw an equilateral triangle from a horizontal base segment", + "site": "instagram", + "name": "login", + "description": "Open instagram login", "access": "write", - "example": "webcmd geogebra triangle --size 4", - "domain": "www.geogebra.org", - "strategy": "public", + "domain": "instagram.com", + "strategy": "cookie", "browser": true, - "args": [ - { - "name": "size", - "type": "str", - "default": "2", - "required": false, - "help": "Side length of the triangle (default: 2)" - } - ], + "args": [], "columns": [ - "step", - "result" + "status", + "logged_in", + "site", + "user_id", + "username", + "full_name", + "action", + "verify_command" ], "type": "js", - "modulePath": "geogebra/triangle.js", - "sourceFile": "geogebra/triangle.js", - "navigateBefore": false + "modulePath": "instagram/auth.js", + "sourceFile": "instagram/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "grok", - "name": "ask", - "description": "Send a message to Grok and get response", + "site": "instagram", + "name": "note", + "description": "Publish a text Instagram note", "access": "write", - "domain": "grok.com", - "strategy": "cookie", + "domain": "www.instagram.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "prompt", - "type": "string", + "name": "content", + "type": "str", "required": true, "positional": true, - "help": "Prompt to send to Grok" + "help": "Note text (max 60 characters)" }, { "name": "timeout", "type": "int", "default": 120, "required": false, - "help": "Max seconds to wait for response (default: 120)" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending (default: false)" + "help": "Max seconds for the overall command (default: 120)" } ], "columns": [ - "response" + "status", + "detail", + "noteId" ], "type": "js", - "modulePath": "grok/ask.js", - "sourceFile": "grok/ask.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" + "modulePath": "instagram/note.js", + "sourceFile": "instagram/note.js", + "navigateBefore": true }, { - "site": "grok", - "name": "delete", - "description": "Delete a Grok conversation by ID. Grok takes effect immediately with no confirmation dialog — require --yes to actually delete.", + "site": "instagram", + "name": "post", + "description": "Post an Instagram feed image or mixed-media carousel", "access": "write", - "domain": "grok.com", - "strategy": "cookie", + "domain": "www.instagram.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "id", - "type": "string", - "required": true, + "name": "media", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Comma-separated media paths (images/videos, up to 10)", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "image/jpeg", + "image/png", + "image/webp", + "video/mp4" + ], + "maxBytes": 262144000 + } + }, + { + "name": "content", + "type": "str", + "required": false, "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" + "help": "Caption text" }, { - "name": "yes", - "type": "boolean", - "default": false, + "name": "timeout", + "type": "int", + "default": 300, "required": false, - "help": "Actually delete (default is a dry-run preview)" + "help": "Max seconds for the overall command (default: 300)" } ], "columns": [ "status", - "id" + "detail", + "url" ], "type": "js", - "modulePath": "grok/delete.js", - "sourceFile": "grok/delete.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" + "modulePath": "instagram/post.js", + "sourceFile": "instagram/post.js", + "navigateBefore": true }, { - "site": "grok", - "name": "detail", - "description": "Open a Grok conversation by ID and read its messages", + "site": "instagram", + "name": "profile", + "description": "Get Instagram user profile info", "access": "read", - "domain": "grok.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Session ID (UUID) or full https://grok.com/c/ URL" - }, - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" + "help": "Instagram username" } ], "columns": [ - "Role", - "Text" + "username", + "name", + "followers", + "following", + "posts", + "verified", + "bio" ], "type": "js", - "modulePath": "grok/detail.js", - "sourceFile": "grok/detail.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/profile.js", + "sourceFile": "instagram/profile.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", - "name": "export", - "description": "Export all visible Grok conversation history metadata", - "access": "read", - "example": "webcmd grok export -f yaml", - "domain": "grok.com", - "strategy": "cookie", + "site": "instagram", + "name": "reel", + "description": "Post an Instagram reel video", + "access": "write", + "domain": "www.instagram.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 0, + "name": "video", + "type": "str", "required": false, - "help": "Max conversations to export; 0 means all loaded history" + "valueRequired": true, + "help": "Path to a single .mp4 video file", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "video/mp4" + ], + "maxBytes": 262144000 + } }, { - "name": "maxScrolls", + "name": "content", + "type": "str", + "required": false, + "positional": true, + "help": "Caption text" + }, + { + "name": "timeout", "type": "int", - "default": 80, + "default": 600, "required": false, - "help": "Max history-list scroll rounds when limit is 0 (max 500)" + "help": "Max seconds for the overall command (default: 600)" } ], "columns": [ - "index", - "id", - "title", - "date", + "status", + "detail", "url" ], "type": "js", - "modulePath": "grok/export.js", - "sourceFile": "grok/export.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/reel.js", + "sourceFile": "instagram/reel.js", + "navigateBefore": true }, { - "site": "grok", - "name": "export-all", - "description": "Export Grok conversation history and each conversation transcript", - "access": "read", - "example": "webcmd grok export-all --limit 5 -f json", - "domain": "grok.com", + "site": "instagram", + "name": "save", + "description": "Save (bookmark) an Instagram post", + "access": "write", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 0, - "required": false, - "help": "Max conversations to export; 0 means all loaded history" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Skip this many conversations before exporting" - }, - { - "name": "manifestPath", - "type": "string", - "default": "", - "required": false, - "help": "Optional grok/export JSON manifest path; skips history dialog and visits listed /c pages directly" - }, - { - "name": "maxScrolls", - "type": "int", - "default": 80, - "required": false, - "help": "Max history-list scroll rounds when limit is 0 (max 500)" - }, - { - "name": "pageScrolls", - "type": "int", - "default": 30, - "required": false, - "help": "Max per-conversation scroll-to-bottom rounds (max 200)" - }, - { - "name": "pageTimeoutMs", - "type": "int", - "default": 30000, - "required": false, - "help": "Max wait for each conversation page to show messages" - }, - { - "name": "delayMinMs", - "type": "int", - "default": 0, - "required": false, - "help": "Minimum polite delay after a conversation page loads" + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Username of the post author" }, { - "name": "delayMaxMs", + "name": "index", "type": "int", - "default": 5000, + "default": 1, "required": false, - "help": "Maximum polite delay after a conversation page loads" + "help": "Post index (1 = most recent)" } ], "columns": [ - "index", - "id", - "title", - "date", - "url", "status", - "messageCount", - "error", - "messagesJson" + "user", + "post" ], "type": "js", - "modulePath": "grok/export-all.js", - "sourceFile": "grok/export-all.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/save.js", + "sourceFile": "instagram/save.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", - "name": "history", - "description": "List recent Grok conversations from the sidebar (requires login)", + "site": "instagram", + "name": "saved", + "description": "Get your saved Instagram posts (optionally from a specific collection)", "access": "read", - "domain": "grok.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ @@ -4043,262 +3554,239 @@ "type": "int", "default": 20, "required": false, - "help": "Max conversations to show (default 20, max 100)" + "help": "Number of saved posts" + }, + { + "name": "collection", + "type": "str", + "required": false, + "help": "Collection name (case-insensitive). Omit for the default \"All posts\" feed." } ], "columns": [ - "Index", - "Title", - "Url" + "index", + "user", + "caption", + "likes", + "comments", + "type" ], "type": "js", - "modulePath": "grok/history.js", - "sourceFile": "grok/history.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/saved.js", + "sourceFile": "instagram/saved.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", - "name": "image", - "description": "Generate images on grok.com and return image URLs", - "access": "write", - "domain": "grok.com", + "site": "instagram", + "name": "search", + "description": "Search Instagram users", + "access": "read", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", - "type": "string", + "name": "query", + "type": "str", "required": true, "positional": true, - "help": "Image generation prompt" - }, - { - "name": "timeout", - "type": "int", - "default": 240, - "required": false, - "help": "Max seconds to wait for the image (default: 240)" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending (default: false)" + "help": "Search query" }, { - "name": "count", + "name": "limit", "type": "int", - "default": 1, - "required": false, - "help": "Minimum images to wait for before returning (default: 1)" - }, - { - "name": "out", - "type": "string", - "default": "", + "default": 10, "required": false, - "help": "Directory to save downloaded images (uses browser session to bypass auth)" + "help": "Number of results" } ], "columns": [ - "url", - "width", - "height", - "path" + "rank", + "username", + "name", + "verified", + "private", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "grok/image.js", - "sourceFile": "grok/image.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" + "modulePath": "instagram/search.js", + "sourceFile": "instagram/search.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", - "name": "login", - "description": "Open grok login", + "site": "instagram", + "name": "story", + "description": "Post a single Instagram story image or video", "access": "write", - "domain": "grok.com", - "strategy": "cookie", + "domain": "www.instagram.com", + "strategy": "ui", "browser": true, - "args": [], + "args": [ + { + "name": "media", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Path to a single story image or video file" + }, + { + "name": "timeout", + "type": "int", + "default": 300, + "required": false, + "help": "Max seconds for the overall command (default: 300)" + } + ], "columns": [ "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" + "detail", + "url" ], "type": "js", - "modulePath": "grok/auth.js", - "sourceFile": "grok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/story.js", + "sourceFile": "instagram/story.js", + "navigateBefore": true }, { - "site": "grok", - "name": "new", - "description": "Start a new conversation in Grok", + "site": "instagram", + "name": "unfollow", + "description": "Unfollow an Instagram user", "access": "write", - "domain": "grok.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram username to unfollow" + } + ], "columns": [ - "Status" + "status", + "username" ], "type": "js", - "modulePath": "grok/new.js", - "sourceFile": "grok/new.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/unfollow.js", + "sourceFile": "instagram/unfollow.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", - "name": "pin", - "description": "Pin a Grok conversation by ID", + "site": "instagram", + "name": "unlike", + "description": "Unlike an Instagram post", "access": "write", - "domain": "grok.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", - "type": "string", + "name": "username", + "type": "str", "required": true, "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" + "help": "Username of the post author" + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Post index (1 = most recent)" } ], "columns": [ "status", - "id" - ], - "type": "js", - "modulePath": "grok/pin.js", - "sourceFile": "grok/pin.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "read", - "description": "Read messages in the current Grok conversation", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" - } - ], - "columns": [ - "Role", - "Text" + "user", + "post" ], "type": "js", - "modulePath": "grok/read.js", - "sourceFile": "grok/read.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/unlike.js", + "sourceFile": "instagram/unlike.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", - "name": "send", - "description": "Fire-and-forget: send a prompt to Grok without waiting for the reply", + "site": "instagram", + "name": "unsave", + "description": "Unsave (remove bookmark) an Instagram post", "access": "write", - "domain": "grok.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Prompt to send to Grok" + "help": "Username of the post author" }, { - "name": "new", - "type": "boolean", - "default": false, + "name": "index", + "type": "int", + "default": 1, "required": false, - "help": "Start a new chat before sending" + "help": "Post index (1 = most recent)" } ], "columns": [ - "Status", - "Prompt" + "status", + "user", + "post" ], "type": "js", - "modulePath": "grok/send.js", - "sourceFile": "grok/send.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/unsave.js", + "sourceFile": "instagram/unsave.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", - "name": "status", - "description": "Check Grok page availability, login state, current session and model", + "site": "instagram", + "name": "user", + "description": "Get recent posts from an Instagram user", "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status", - "Login", - "Model", - "SessionId", - "Url" - ], - "type": "js", - "modulePath": "grok/status.js", - "sourceFile": "grok/status.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "unpin", - "description": "Unpin a Grok conversation by ID", - "access": "write", - "domain": "grok.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", - "type": "string", + "name": "username", + "type": "str", "required": true, "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" + "help": "Instagram username" + }, + { + "name": "limit", + "type": "int", + "default": 12, + "required": false, + "help": "Number of posts" } ], "columns": [ - "status", - "id" + "index", + "caption", + "likes", + "comments", + "type", + "date" ], "type": "js", - "modulePath": "grok/pin.js", - "sourceFile": "grok/pin.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" + "modulePath": "instagram/user.js", + "sourceFile": "instagram/user.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "grok", + "site": "instagram", "name": "whoami", - "description": "Show the current logged-in grok account", + "description": "Show the current logged-in instagram account", "access": "read", - "domain": "grok.com", + "domain": "instagram.com", "strategy": "cookie", "browser": true, "args": [], @@ -4306,1567 +3794,224 @@ "logged_in", "site", "user_id", - "name" + "username", + "full_name" ], "type": "js", - "modulePath": "grok/auth.js", - "sourceFile": "grok/auth.js", + "modulePath": "instagram/auth.js", + "sourceFile": "instagram/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "instagram", - "name": "collection-create", - "description": "Create a new Instagram saved-posts collection (folder)", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", + "site": "mercury", + "name": "check-login", + "description": "Open Mercury reimbursements and report whether the active browser profile is logged in", + "access": "read", + "example": "webcmd --profile mercury check-login -f json", + "domain": "app.mercury.com", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Name of the collection to create" - } - ], + "args": [], "columns": [ "status", - "collectionId", - "collectionName", - "mediaCount" + "loggedIn", + "url", + "hasSubmitExpense", + "hasReimbursements", + "title" ], "type": "js", - "modulePath": "instagram/collection-create.js", - "sourceFile": "instagram/collection-create.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "mercury/check-login.js", + "sourceFile": "mercury/check-login.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "instagram", - "name": "collection-delete", - "description": "Delete an Instagram saved-posts collection (folder) by name or id", + "site": "mercury", + "name": "reimbursement-draft", + "description": "Create a Mercury reimbursement draft from a local receipt, correct OCR fields, and stop at Review", "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", + "example": "webcmd --profile mercury reimbursement-draft --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", + "domain": "app.mercury.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "target", + "name": "receipt", "type": "str", "required": true, - "positional": true, - "help": "Collection name (case-insensitive) or numeric collection_id" - } - ], - "columns": [ - "status", - "collectionId", - "collectionName" - ], - "type": "js", - "modulePath": "instagram/collection-delete.js", - "sourceFile": "instagram/collection-delete.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "comment", - "description": "Comment on an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ + "help": "Local receipt/proof file path", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp", + "application/pdf" + ], + "maxBytes": 26214400 + } + }, { - "name": "username", + "name": "amount", "type": "str", "required": true, - "positional": true, - "help": "Username of the post author" + "help": "Original-currency amount, e.g. 140.00" }, { - "name": "text", + "name": "currency", + "type": "str", + "default": "CNY", + "required": false, + "help": "Original currency code" + }, + { + "name": "date", "type": "str", "required": true, - "positional": true, - "help": "Comment text" + "help": "Expense date as YYYY-MM-DD" }, { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "text" - ], - "type": "js", - "modulePath": "instagram/comment.js", - "sourceFile": "instagram/comment.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "download", - "description": "Download images and videos from Instagram posts and reels", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", + "name": "merchant", "type": "str", "required": true, - "positional": true, - "help": "Instagram post / reel / tv URL" + "help": "Merchant shown on the reimbursement" }, { - "name": "path", + "name": "category", "type": "str", - "default": "~/Downloads/Instagram", - "required": false, - "help": "Download directory" - } - ], - "type": "js", - "modulePath": "instagram/download.js", - "sourceFile": "instagram/download.js", - "navigateBefore": false - }, - { - "site": "instagram", - "name": "explore", - "description": "Instagram explore/discover trending posts", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, + "default": "Marketing & Advertising", "required": false, - "help": "Number of posts" - } - ], - "columns": [ - "rank", - "user", - "caption", - "likes", - "comments", - "type" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "instagram/explore.js", - "sourceFile": "instagram/explore.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "follow", - "description": "Follow an Instagram user", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ + "help": "Mercury expense category" + }, { - "name": "username", + "name": "notes", "type": "str", "required": true, - "positional": true, - "help": "Instagram username to follow" - } - ], - "columns": [ - "status", - "username" - ], - "type": "js", - "modulePath": "instagram/follow.js", - "sourceFile": "instagram/follow.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "followers", - "description": "List followers of an Instagram user", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ + "help": "Business purpose / reimbursement notes" + }, { - "name": "username", + "name": "ocr-wait-seconds", "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" + "default": "8", + "required": false, + "help": "Seconds to wait after receipt upload before correcting OCR-overwritten fields" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "close-after-review", + "type": "boolean", + "default": false, "required": false, - "help": "Number of followers" + "help": "Close the Review dialog after verification; final Submit is still never clicked" } ], "columns": [ - "rank", - "username", - "name", - "verified", - "private" + "status", + "url", + "receipt", + "uploaded", + "fieldsTouched", + "reviewReady", + "submitBlocked", + "warnings" ], "type": "js", - "modulePath": "instagram/followers.js", - "sourceFile": "instagram/followers.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "mercury/reimbursement-draft.js", + "sourceFile": "mercury/reimbursement-draft.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "instagram", - "name": "following", - "description": "List accounts an Instagram user is following", + "site": "mercury", + "name": "reimbursement-plan", + "description": "Validate Mercury reimbursement inputs and print the draft plan without opening a browser", "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, + "example": "webcmd mercury reimbursement-plan --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", + "strategy": "local", + "browser": false, "args": [ { - "name": "username", + "name": "receipt", "type": "str", "required": true, - "positional": true, - "help": "Instagram username" + "help": "Local receipt/proof file path" }, { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of accounts" - } - ], - "columns": [ - "rank", - "username", - "name", - "verified", - "private" - ], - "type": "js", - "modulePath": "instagram/following.js", - "sourceFile": "instagram/following.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "like", - "description": "Like an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", + "name": "amount", "type": "str", "required": true, - "positional": true, - "help": "Username of the post author" + "help": "Original-currency amount, e.g. 140.00" }, { - "name": "index", - "type": "int", - "default": 1, + "name": "currency", + "type": "str", + "default": "CNY", "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "post" - ], - "type": "js", - "modulePath": "instagram/like.js", - "sourceFile": "instagram/like.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "login", - "description": "Open instagram login", - "access": "write", - "domain": "instagram.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "username", - "full_name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "instagram/auth.js", - "sourceFile": "instagram/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "instagram", - "name": "note", - "description": "Publish a text Instagram note", - "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Original currency code" + }, { - "name": "content", + "name": "date", "type": "str", "required": true, - "positional": true, - "help": "Note text (max 60 characters)" + "help": "Expense date as YYYY-MM-DD" }, { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds for the overall command (default: 120)" - } - ], - "columns": [ - "status", - "detail", - "noteId" - ], - "type": "js", - "modulePath": "instagram/note.js", - "sourceFile": "instagram/note.js", - "navigateBefore": true - }, - { - "site": "instagram", - "name": "post", - "description": "Post an Instagram feed image or mixed-media carousel", - "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "media", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Comma-separated media paths (images/videos, up to 10)", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "image/jpeg", - "image/png", - "image/webp", - "video/mp4" - ], - "maxBytes": 262144000 - } - }, - { - "name": "content", - "type": "str", - "required": false, - "positional": true, - "help": "Caption text" - }, - { - "name": "timeout", - "type": "int", - "default": 300, - "required": false, - "help": "Max seconds for the overall command (default: 300)" - } - ], - "columns": [ - "status", - "detail", - "url" - ], - "type": "js", - "modulePath": "instagram/post.js", - "sourceFile": "instagram/post.js", - "navigateBefore": true - }, - { - "site": "instagram", - "name": "profile", - "description": "Get Instagram user profile info", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", + "name": "merchant", "type": "str", "required": true, - "positional": true, - "help": "Instagram username" - } - ], - "columns": [ - "username", - "name", - "followers", - "following", - "posts", - "verified", - "bio" - ], - "type": "js", - "modulePath": "instagram/profile.js", - "sourceFile": "instagram/profile.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "reel", - "description": "Post an Instagram reel video", - "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "video", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Path to a single .mp4 video file", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "video/mp4" - ], - "maxBytes": 262144000 - } + "help": "Merchant shown on the reimbursement" }, { - "name": "content", + "name": "category", "type": "str", + "default": "Marketing & Advertising", "required": false, - "positional": true, - "help": "Caption text" + "help": "Mercury expense category" }, { - "name": "timeout", - "type": "int", - "default": 600, - "required": false, - "help": "Max seconds for the overall command (default: 600)" - } - ], - "columns": [ - "status", - "detail", - "url" - ], - "type": "js", - "modulePath": "instagram/reel.js", - "sourceFile": "instagram/reel.js", - "navigateBefore": true - }, - { - "site": "instagram", - "name": "save", - "description": "Save (bookmark) an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", + "name": "notes", "type": "str", "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "post" - ], - "type": "js", - "modulePath": "instagram/save.js", - "sourceFile": "instagram/save.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "saved", - "description": "Get your saved Instagram posts (optionally from a specific collection)", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of saved posts" + "help": "Business purpose / reimbursement notes" }, { - "name": "collection", + "name": "ocr-wait-seconds", "type": "str", + "default": "8", "required": false, - "help": "Collection name (case-insensitive). Omit for the default \"All posts\" feed." - } - ], - "columns": [ - "index", - "user", - "caption", - "likes", - "comments", - "type" - ], - "type": "js", - "modulePath": "instagram/saved.js", - "sourceFile": "instagram/saved.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "search", - "description": "Search Instagram users", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" + "help": "Seconds the draft command waits after receipt upload before correcting OCR fields" }, { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "username", - "name", - "verified", - "private", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "instagram/search.js", - "sourceFile": "instagram/search.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "story", - "description": "Post a single Instagram story image or video", - "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "media", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Path to a single story image or video file" - }, - { - "name": "timeout", - "type": "int", - "default": 300, - "required": false, - "help": "Max seconds for the overall command (default: 300)" - } - ], - "columns": [ - "status", - "detail", - "url" - ], - "type": "js", - "modulePath": "instagram/story.js", - "sourceFile": "instagram/story.js", - "navigateBefore": true - }, - { - "site": "instagram", - "name": "unfollow", - "description": "Unfollow an Instagram user", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username to unfollow" - } - ], - "columns": [ - "status", - "username" - ], - "type": "js", - "modulePath": "instagram/unfollow.js", - "sourceFile": "instagram/unfollow.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "unlike", - "description": "Unlike an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "post" - ], - "type": "js", - "modulePath": "instagram/unlike.js", - "sourceFile": "instagram/unlike.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "unsave", - "description": "Unsave (remove bookmark) an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "post" - ], - "type": "js", - "modulePath": "instagram/unsave.js", - "sourceFile": "instagram/unsave.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "user", - "description": "Get recent posts from an Instagram user", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" - }, - { - "name": "limit", - "type": "int", - "default": 12, - "required": false, - "help": "Number of posts" - } - ], - "columns": [ - "index", - "caption", - "likes", - "comments", - "type", - "date" - ], - "type": "js", - "modulePath": "instagram/user.js", - "sourceFile": "instagram/user.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "whoami", - "description": "Show the current logged-in instagram account", - "access": "read", - "domain": "instagram.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "username", - "full_name" - ], - "type": "js", - "modulePath": "instagram/auth.js", - "sourceFile": "instagram/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "mercury", - "name": "check-login", - "description": "Open Mercury reimbursements and report whether the active browser profile is logged in", - "access": "read", - "example": "webcmd --profile mercury check-login -f json", - "domain": "app.mercury.com", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "status", - "loggedIn", - "url", - "hasSubmitExpense", - "hasReimbursements", - "title" - ], - "type": "js", - "modulePath": "mercury/check-login.js", - "sourceFile": "mercury/check-login.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "mercury", - "name": "reimbursement-draft", - "description": "Create a Mercury reimbursement draft from a local receipt, correct OCR fields, and stop at Review", - "access": "write", - "example": "webcmd --profile mercury reimbursement-draft --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", - "domain": "app.mercury.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "receipt", - "type": "str", - "required": true, - "help": "Local receipt/proof file path", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp", - "application/pdf" - ], - "maxBytes": 26214400 - } - }, - { - "name": "amount", - "type": "str", - "required": true, - "help": "Original-currency amount, e.g. 140.00" - }, - { - "name": "currency", - "type": "str", - "default": "CNY", - "required": false, - "help": "Original currency code" - }, - { - "name": "date", - "type": "str", - "required": true, - "help": "Expense date as YYYY-MM-DD" - }, - { - "name": "merchant", - "type": "str", - "required": true, - "help": "Merchant shown on the reimbursement" - }, - { - "name": "category", - "type": "str", - "default": "Marketing & Advertising", - "required": false, - "help": "Mercury expense category" - }, - { - "name": "notes", - "type": "str", - "required": true, - "help": "Business purpose / reimbursement notes" - }, - { - "name": "ocr-wait-seconds", - "type": "str", - "default": "8", - "required": false, - "help": "Seconds to wait after receipt upload before correcting OCR-overwritten fields" - }, - { - "name": "close-after-review", - "type": "boolean", - "default": false, - "required": false, - "help": "Close the Review dialog after verification; final Submit is still never clicked" - } - ], - "columns": [ - "status", - "url", - "receipt", - "uploaded", - "fieldsTouched", - "reviewReady", - "submitBlocked", - "warnings" - ], - "type": "js", - "modulePath": "mercury/reimbursement-draft.js", - "sourceFile": "mercury/reimbursement-draft.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "mercury", - "name": "reimbursement-plan", - "description": "Validate Mercury reimbursement inputs and print the draft plan without opening a browser", - "access": "read", - "example": "webcmd mercury reimbursement-plan --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "receipt", - "type": "str", - "required": true, - "help": "Local receipt/proof file path" - }, - { - "name": "amount", - "type": "str", - "required": true, - "help": "Original-currency amount, e.g. 140.00" - }, - { - "name": "currency", - "type": "str", - "default": "CNY", - "required": false, - "help": "Original currency code" - }, - { - "name": "date", - "type": "str", - "required": true, - "help": "Expense date as YYYY-MM-DD" - }, - { - "name": "merchant", - "type": "str", - "required": true, - "help": "Merchant shown on the reimbursement" - }, - { - "name": "category", - "type": "str", - "default": "Marketing & Advertising", - "required": false, - "help": "Mercury expense category" - }, - { - "name": "notes", - "type": "str", - "required": true, - "help": "Business purpose / reimbursement notes" - }, - { - "name": "ocr-wait-seconds", - "type": "str", - "default": "8", - "required": false, - "help": "Seconds the draft command waits after receipt upload before correcting OCR fields" - }, - { - "name": "close-after-review", - "type": "boolean", - "default": false, - "required": false, - "help": "For draft command: close the Review dialog after verification" - } - ], - "columns": [ - "status", - "receipt", - "amount", - "currency", - "date", - "merchant", - "category", - "notes", - "safety" - ], - "type": "js", - "modulePath": "mercury/reimbursement-plan.js", - "sourceFile": "mercury/reimbursement-plan.js" - }, - { - "site": "notebooklm", - "name": "add-source", - "description": "Add a URL, text, or local file source to an existing NotebookLM notebook", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Source URL to add (http/https). Pass exactly one of --url, --content, --file." - }, - { - "name": "content", - "type": "str", - "required": false, - "help": "Raw text content to add as a Text source (max 10 MB)." - }, - { - "name": "file", - "type": "str", - "required": false, - "help": "Local file path to upload as a source (max 52428800 bytes; pdf / txt / md / html / docx / etc.). Uses Google Drive's 3-step resumable upload protocol." - }, - { - "name": "title", - "type": "str", - "required": false, - "help": "Title for the text source (default \"Text Source\"). Ignored for --url and --file." - }, - { - "name": "mime-type", - "type": "str", - "required": false, - "help": "Override the auto-detected MIME type when --file is given." - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually add the remote source to the NotebookLM notebook" - } - ], - "columns": [ - "notebook_id", - "source_id", - "kind", - "identifier", - "notebook_url" - ], - "type": "js", - "modulePath": "notebooklm/add-source.js", - "sourceFile": "notebooklm/add-source.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "create", - "description": "Create a new NotebookLM notebook with the given title", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "title", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook title (1-200 chars)" - }, - { - "name": "emoji", - "type": "str", - "required": false, - "help": "Notebook emoji icon (default 📒)" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually create the remote NotebookLM notebook" - } - ], - "columns": [ - "id", - "title", - "emoji", - "url" - ], - "type": "js", - "modulePath": "notebooklm/create.js", - "sourceFile": "notebooklm/create.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "current", - "description": "Show metadata for the currently opened NotebookLM notebook tab", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", - "title", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/current.js", - "sourceFile": "notebooklm/current.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "generate-audio", - "description": "Trigger an Audio Overview (Deep Dive podcast) generation for a NotebookLM notebook, using all of its sources", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually trigger remote NotebookLM audio generation" - } - ], - "columns": [ - "notebook_id", - "audio_id", - "source_count", - "status", - "notebook_url" - ], - "type": "js", - "modulePath": "notebooklm/generate-audio.js", - "sourceFile": "notebooklm/generate-audio.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "generate-slides", - "description": "Trigger a Slide Deck (AI presentation) generation for a NotebookLM notebook, using all of its sources", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "length", - "type": "str", - "required": false, - "help": "Slide deck length: 1=Short, 3=Default (default 3)" - }, - { - "name": "language", - "type": "str", - "required": false, - "help": "Language code (default en)" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually trigger remote NotebookLM slide deck generation" - } - ], - "columns": [ - "notebook_id", - "slides_id", - "source_count", - "status", - "notebook_url" - ], - "type": "js", - "modulePath": "notebooklm/generate-slides.js", - "sourceFile": "notebooklm/generate-slides.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "get", - "aliases": [ - "metadata" - ], - "description": "Get rich metadata for the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", - "title", - "emoji", - "source_count", - "created_at", - "updated_at", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/get.js", - "sourceFile": "notebooklm/get.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "history", - "description": "List NotebookLM conversation history threads in the current notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "thread_id", - "item_count", - "preview", - "source", - "notebook_id", - "url" - ], - "type": "js", - "modulePath": "notebooklm/history.js", - "sourceFile": "notebooklm/history.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "list", - "description": "List NotebookLM notebooks via in-page batchexecute RPC in the current logged-in session", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "id", - "is_owner", - "created_at", - "source", - "url" - ], - "type": "js", - "modulePath": "notebooklm/list.js", - "sourceFile": "notebooklm/list.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "login", - "description": "Open notebooklm login", - "access": "write", - "domain": "google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "name", - "authuser", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "notebooklm/auth.js", - "sourceFile": "notebooklm/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "notebooklm", - "name": "note-list", - "aliases": [ - "notes-list" - ], - "description": "List saved notes from the Studio panel of the current NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "created_at", - "source", - "url" - ], - "type": "js", - "modulePath": "notebooklm/note-list.js", - "sourceFile": "notebooklm/note-list.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "notes-get", - "description": "Get one note from the current NotebookLM notebook by title from the visible note editor", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "note", - "type": "str", - "required": true, - "positional": true, - "help": "Note title or id from the current notebook" - } - ], - "columns": [ - "title", - "content", - "source", - "url" - ], - "type": "js", - "modulePath": "notebooklm/notes-get.js", - "sourceFile": "notebooklm/notes-get.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "open", - "aliases": [ - "select" - ], - "description": "Open one NotebookLM notebook in the adapter session by id or URL", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from list output, or a full NotebookLM notebook URL" - } - ], - "columns": [ - "id", - "title", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/open.js", - "sourceFile": "notebooklm/open.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-fulltext", - "description": "Get the extracted fulltext for one source in the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "source", - "type": "str", - "required": true, - "positional": true, - "help": "Source id or title from the current notebook" - } - ], - "columns": [ - "title", - "kind", - "char_count", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/source-fulltext.js", - "sourceFile": "notebooklm/source-fulltext.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-get", - "description": "Get one source from the currently opened NotebookLM notebook by id or title", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "source", - "type": "str", - "required": true, - "positional": true, - "help": "Source id or title from the current notebook" - } - ], - "columns": [ - "title", - "id", - "type", - "size", - "created_at", - "updated_at", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/source-get.js", - "sourceFile": "notebooklm/source-get.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-guide", - "description": "Get the guide summary and keywords for one source in the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "source", - "type": "str", - "required": true, - "positional": true, - "help": "Source id or title from the current notebook" - } - ], - "columns": [ - "source_id", - "notebook_id", - "title", - "type", - "summary", - "keywords", - "source" - ], - "type": "js", - "modulePath": "notebooklm/source-guide.js", - "sourceFile": "notebooklm/source-guide.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-list", - "description": "List sources for the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "id", - "type", - "size", - "created_at", - "updated_at", - "url", - "source" + "name": "close-after-review", + "type": "boolean", + "default": false, + "required": false, + "help": "For draft command: close the Review dialog after verification" + } ], - "type": "js", - "modulePath": "notebooklm/source-list.js", - "sourceFile": "notebooklm/source-list.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "status", - "description": "Check NotebookLM page availability and login state in the current Chrome session", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], "columns": [ "status", - "login", - "page", - "url", - "title", - "notebooks" - ], - "type": "js", - "modulePath": "notebooklm/status.js", - "sourceFile": "notebooklm/status.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "summary", - "description": "Get the summary block from the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "summary", - "source", - "url" - ], - "type": "js", - "modulePath": "notebooklm/summary.js", - "sourceFile": "notebooklm/summary.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "whoami", - "description": "Show the current logged-in notebooklm account", - "access": "read", - "domain": "google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name", - "authuser" + "receipt", + "amount", + "currency", + "date", + "merchant", + "category", + "notes", + "safety" ], "type": "js", - "modulePath": "notebooklm/auth.js", - "sourceFile": "notebooklm/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "mercury/reimbursement-plan.js", + "sourceFile": "mercury/reimbursement-plan.js" }, { "site": "notebooklm", - "name": "write-note", - "description": "Create a Studio note in an existing NotebookLM notebook with the given title and Markdown content", + "name": "add-source", + "description": "Add a URL, text, or local file source to an existing NotebookLM notebook", "access": "write", "domain": "notebooklm.google.com", "strategy": "cookie", @@ -5880,317 +4025,273 @@ "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "title", + "name": "url", "type": "str", - "required": true, - "help": "Note title (1-200 chars)" + "required": false, + "help": "Source URL to add (http/https). Pass exactly one of --url, --content, --file." }, { "name": "content", "type": "str", - "required": true, - "help": "Note body as Markdown" + "required": false, + "help": "Raw text content to add as a Text source (max 10 MB)." + }, + { + "name": "file", + "type": "str", + "required": false, + "help": "Local file path to upload as a source (max 52428800 bytes; pdf / txt / md / html / docx / etc.). Uses Google Drive's 3-step resumable upload protocol." + }, + { + "name": "title", + "type": "str", + "required": false, + "help": "Title for the text source (default \"Text Source\"). Ignored for --url and --file." + }, + { + "name": "mime-type", + "type": "str", + "required": false, + "help": "Override the auto-detected MIME type when --file is given." }, { "name": "execute", "type": "boolean", "required": false, - "help": "Actually create the remote NotebookLM note" + "help": "Actually add the remote source to the NotebookLM notebook" } ], "columns": [ "notebook_id", - "note_id", - "title", + "source_id", + "kind", + "identifier", "notebook_url" ], "type": "js", - "modulePath": "notebooklm/write-note.js", - "sourceFile": "notebooklm/write-note.js", + "modulePath": "notebooklm/add-source.js", + "sourceFile": "notebooklm/add-source.js", "navigateBefore": false }, { - "site": "paperreview", - "name": "feedback", - "description": "Submit feedback for a paperreview.ai review token", + "site": "notebooklm", + "name": "create", + "description": "Create a new NotebookLM notebook with the given title", "access": "write", - "domain": "paperreview.ai", - "strategy": "public", - "browser": false, + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "token", + "name": "title", "type": "str", "required": true, "positional": true, - "help": "Review token returned by paperreview.ai" - }, - { - "name": "helpfulness", - "type": "int", - "required": true, - "help": "Helpfulness score from 1 to 5" - }, - { - "name": "critical-error", - "type": "str", - "required": true, - "help": "Whether the review contains a critical error", - "choices": [ - "yes", - "no" - ] - }, - { - "name": "actionable-suggestions", - "type": "str", - "required": true, - "help": "Whether the review contains actionable suggestions", - "choices": [ - "yes", - "no" - ] + "help": "Notebook title (1-200 chars)" }, { - "name": "additional-comments", + "name": "emoji", "type": "str", "required": false, - "help": "Optional free-text feedback" + "help": "Notebook emoji icon (default 📒)" }, { - "name": "timeout", - "type": "int", - "default": 30, + "name": "execute", + "type": "boolean", "required": false, - "help": "Max seconds for the overall command (default: 30)" + "help": "Actually create the remote NotebookLM notebook" } ], "columns": [ - "status", - "token", - "helpfulness", - "critical_error", - "actionable_suggestions", - "message" + "id", + "title", + "emoji", + "url" ], "type": "js", - "modulePath": "paperreview/feedback.js", - "sourceFile": "paperreview/feedback.js" + "modulePath": "notebooklm/create.js", + "sourceFile": "notebooklm/create.js", + "navigateBefore": false }, { - "site": "paperreview", - "name": "review", - "description": "Fetch a paperreview.ai review by token", + "site": "notebooklm", + "name": "current", + "description": "Show metadata for the currently opened NotebookLM notebook tab", "access": "read", - "domain": "paperreview.ai", - "strategy": "public", - "browser": false, + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "id", + "title", + "url", + "source" + ], + "type": "js", + "modulePath": "notebooklm/current.js", + "sourceFile": "notebooklm/current.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "generate-audio", + "description": "Trigger an Audio Overview (Deep Dive podcast) generation for a NotebookLM notebook, using all of its sources", + "access": "write", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "token", + "name": "notebook", "type": "str", "required": true, "positional": true, - "help": "Review token returned by paperreview.ai" + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "timeout", - "type": "int", - "default": 30, + "name": "execute", + "type": "boolean", "required": false, - "help": "Max seconds for the overall command (default: 30)" + "help": "Actually trigger remote NotebookLM audio generation" } ], "columns": [ + "notebook_id", + "audio_id", + "source_count", "status", - "title", - "venue", - "numerical_score", - "has_feedback", - "review_url" + "notebook_url" ], "type": "js", - "modulePath": "paperreview/review.js", - "sourceFile": "paperreview/review.js" + "modulePath": "notebooklm/generate-audio.js", + "sourceFile": "notebooklm/generate-audio.js", + "navigateBefore": false }, { - "site": "paperreview", - "name": "submit", - "description": "Submit a PDF to paperreview.ai for review", + "site": "notebooklm", + "name": "generate-slides", + "description": "Trigger a Slide Deck (AI presentation) generation for a NotebookLM notebook, using all of its sources", "access": "write", - "domain": "paperreview.ai", - "strategy": "public", - "browser": false, + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "pdf", + "name": "notebook", "type": "str", "required": true, "positional": true, - "help": "Path to the paper PDF" - }, - { - "name": "email", - "type": "str", - "required": true, - "help": "Email address for the submission" + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "venue", + "name": "length", "type": "str", "required": false, - "help": "Optional target venue such as ICLR or NeurIPS" - }, - { - "name": "dry-run", - "type": "bool", - "default": false, - "required": false, - "help": "Validate the input and stop before remote submission" + "help": "Slide deck length: 1=Short, 3=Default (default 3)" }, { - "name": "prepare-only", - "type": "bool", - "default": false, + "name": "language", + "type": "str", "required": false, - "help": "Request an upload slot but stop before uploading the PDF" + "help": "Language code (default en)" }, { - "name": "timeout", - "type": "int", - "default": 120, + "name": "execute", + "type": "boolean", "required": false, - "help": "Max seconds for the overall command (default: 120)" + "help": "Actually trigger remote NotebookLM slide deck generation" } ], "columns": [ + "notebook_id", + "slides_id", + "source_count", "status", - "file", - "email", - "venue", - "token", - "review_url", - "message" + "notebook_url" ], "type": "js", - "modulePath": "paperreview/submit.js", - "sourceFile": "paperreview/submit.js" + "modulePath": "notebooklm/generate-slides.js", + "sourceFile": "notebooklm/generate-slides.js", + "navigateBefore": false }, { - "site": "pixiv", - "name": "detail", - "description": "View illustration details (tags, stats, URLs)", + "site": "notebooklm", + "name": "get", + "aliases": [ + "metadata" + ], + "description": "Get rich metadata for the currently opened NotebookLM notebook", "access": "read", - "domain": "www.pixiv.net", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Illustration ID" - } - ], + "args": [], "columns": [ - "illust_id", + "id", "title", - "author", - "type", - "pages", - "bookmarks", - "likes", - "views", - "tags", - "created", - "url" + "emoji", + "source_count", + "created_at", + "updated_at", + "url", + "source" ], "type": "js", - "modulePath": "pixiv/detail.js", - "sourceFile": "pixiv/detail.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "notebooklm/get.js", + "sourceFile": "notebooklm/get.js", + "navigateBefore": false }, { - "site": "pixiv", - "name": "download", - "description": "Download illustration images from Pixiv", + "site": "notebooklm", + "name": "history", + "description": "List NotebookLM conversation history threads in the current notebook", "access": "read", - "domain": "www.pixiv.net", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "illust-id", - "type": "str", - "required": true, - "positional": true, - "help": "Illustration ID" - }, - { - "name": "output", - "type": "str", - "default": "./pixiv-downloads", - "required": false, - "help": "Output directory" - } - ], + "args": [], "columns": [ - "index", - "type", - "status", - "size" + "thread_id", + "item_count", + "preview", + "source", + "notebook_id", + "url" ], "type": "js", - "modulePath": "pixiv/download.js", - "sourceFile": "pixiv/download.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "notebooklm/history.js", + "sourceFile": "notebooklm/history.js", + "navigateBefore": false }, { - "site": "pixiv", - "name": "illusts", - "description": "List a Pixiv artist's illustrations", + "site": "notebooklm", + "name": "list", + "description": "List NotebookLM notebooks via in-page batchexecute RPC in the current logged-in session", "access": "read", - "domain": "www.pixiv.net", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "user-id", - "type": "str", - "required": true, - "positional": true, - "help": "Pixiv user ID" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], + "args": [], "columns": [ - "rank", "title", - "illust_id", - "pages", - "bookmarks", - "tags", - "created", + "id", + "is_owner", + "created_at", + "source", "url" ], "type": "js", - "modulePath": "pixiv/illusts.js", - "sourceFile": "pixiv/illusts.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "notebooklm/list.js", + "sourceFile": "notebooklm/list.js", + "navigateBefore": false }, { - "site": "pixiv", + "site": "notebooklm", "name": "login", - "description": "Open pixiv login", + "description": "Open notebooklm login", "access": "write", - "domain": "pixiv.net", + "domain": "google.com", "strategy": "cookie", "browser": true, "args": [], @@ -6198,617 +4299,490 @@ "status", "logged_in", "site", - "user_id", "name", + "authuser", "action", "verify_command" ], "type": "js", - "modulePath": "pixiv/auth.js", - "sourceFile": "pixiv/auth.js", + "modulePath": "notebooklm/auth.js", + "sourceFile": "notebooklm/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "pixiv", - "name": "ranking", - "description": "Pixiv illustration rankings (daily/weekly/monthly)", + "site": "notebooklm", + "name": "note-list", + "aliases": [ + "notes-list" + ], + "description": "List saved notes from the Studio panel of the current NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "title", + "created_at", + "source", + "url" + ], + "type": "js", + "modulePath": "notebooklm/note-list.js", + "sourceFile": "notebooklm/note-list.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "notes-get", + "description": "Get one note from the current NotebookLM notebook by title from the visible note editor", "access": "read", - "domain": "www.pixiv.net", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "mode", + "name": "note", "type": "str", - "default": "daily", - "required": false, - "help": "Ranking mode", - "choices": [ - "daily", - "weekly", - "monthly", - "rookie", - "original", - "male", - "female", - "daily_r18", - "weekly_r18" - ] - }, + "required": true, + "positional": true, + "help": "Note title or id from the current notebook" + } + ], + "columns": [ + "title", + "content", + "source", + "url" + ], + "type": "js", + "modulePath": "notebooklm/notes-get.js", + "sourceFile": "notebooklm/notes-get.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "open", + "aliases": [ + "select" + ], + "description": "Open one NotebookLM notebook in the adapter session by id or URL", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number" - }, + "name": "notebook", + "type": "str", + "required": true, + "positional": true, + "help": "Notebook id from list output, or a full NotebookLM notebook URL" + } + ], + "columns": [ + "id", + "title", + "url", + "source" + ], + "type": "js", + "modulePath": "notebooklm/open.js", + "sourceFile": "notebooklm/open.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "source-fulltext", + "description": "Get the extracted fulltext for one source in the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" + "name": "source", + "type": "str", + "required": true, + "positional": true, + "help": "Source id or title from the current notebook" } ], "columns": [ - "rank", "title", - "author", - "user_id", - "illust_id", - "pages", - "bookmarks", - "url" + "kind", + "char_count", + "url", + "source" ], "type": "js", - "modulePath": "pixiv/ranking.js", - "sourceFile": "pixiv/ranking.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "notebooklm/source-fulltext.js", + "sourceFile": "notebooklm/source-fulltext.js", + "navigateBefore": false }, { - "site": "pixiv", - "name": "search", - "description": "Search Pixiv illustrations by keyword", + "site": "notebooklm", + "name": "source-get", + "description": "Get one source from the currently opened NotebookLM notebook by id or title", "access": "read", - "domain": "www.pixiv.net", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", + "name": "source", "type": "str", "required": true, "positional": true, - "help": "Search keyword or tag" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - }, - { - "name": "order", - "type": "str", - "default": "date_d", - "required": false, - "help": "Sort order", - "choices": [ - "date_d", - "date", - "popular_d", - "popular_male_d", - "popular_female_d" - ] - }, - { - "name": "mode", - "type": "str", - "default": "all", - "required": false, - "help": "Search mode", - "choices": [ - "all", - "safe", - "r18" - ] - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number" + "help": "Source id or title from the current notebook" } ], "columns": [ - "rank", "title", - "author", - "user_id", - "illust_id", - "pages", - "bookmarks", - "tags", - "url" - ], - "tags": [ - "search" + "id", + "type", + "size", + "created_at", + "updated_at", + "url", + "source" ], "type": "js", - "modulePath": "pixiv/search.js", - "sourceFile": "pixiv/search.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "notebooklm/source-get.js", + "sourceFile": "notebooklm/source-get.js", + "navigateBefore": false }, { - "site": "pixiv", - "name": "user", - "description": "View Pixiv artist profile", + "site": "notebooklm", + "name": "source-guide", + "description": "Get the guide summary and keywords for one source in the currently opened NotebookLM notebook", "access": "read", - "domain": "www.pixiv.net", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "uid", + "name": "source", "type": "str", "required": true, "positional": true, - "help": "Pixiv user ID" + "help": "Source id or title from the current notebook" } ], "columns": [ - "user_id", - "name", - "premium", - "following", - "illusts", - "manga", - "novels", - "comment", - "url" + "source_id", + "notebook_id", + "title", + "type", + "summary", + "keywords", + "source" ], "type": "js", - "modulePath": "pixiv/user.js", - "sourceFile": "pixiv/user.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "notebooklm/source-guide.js", + "sourceFile": "notebooklm/source-guide.js", + "navigateBefore": false }, { - "site": "pixiv", - "name": "whoami", - "description": "Show the current logged-in pixiv account", + "site": "notebooklm", + "name": "source-list", + "description": "List sources for the currently opened NotebookLM notebook", "access": "read", - "domain": "pixiv.net", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "user_id", - "name" + "title", + "id", + "type", + "size", + "created_at", + "updated_at", + "url", + "source" ], "type": "js", - "modulePath": "pixiv/auth.js", - "sourceFile": "pixiv/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/source-list.js", + "sourceFile": "notebooklm/source-list.js", + "navigateBefore": false }, { - "site": "practo", - "name": "appointment", - "description": "Show logged-in Practo Drive appointment details", + "site": "notebooklm", + "name": "status", + "description": "Check NotebookLM page availability and login state in the current Chrome session", "access": "read", - "domain": "drive.practo.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "appointment_id", - "type": "str", - "required": true, - "positional": true, - "help": "Appointment id from `practo appointments`" - } - ], + "args": [], "columns": [ - "appointment_id", "status", - "summary" + "login", + "page", + "url", + "title", + "notebooks" ], "type": "js", - "modulePath": "practo/appointment.js", - "sourceFile": "practo/appointment.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/status.js", + "sourceFile": "notebooklm/status.js", + "navigateBefore": false }, { - "site": "practo", - "name": "appointments", - "description": "List logged-in Practo Drive appointments", + "site": "notebooklm", + "name": "summary", + "description": "Get the summary block from the currently opened NotebookLM notebook", "access": "read", - "domain": "drive.practo.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "appointment_id", - "doctor", - "practice", - "time", - "status" + "title", + "summary", + "source", + "url" + ], + "type": "js", + "modulePath": "notebooklm/summary.js", + "sourceFile": "notebooklm/summary.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "whoami", + "description": "Show the current logged-in notebooklm account", + "access": "read", + "domain": "google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name", + "authuser" ], "type": "js", - "modulePath": "practo/appointments.js", - "sourceFile": "practo/appointments.js", + "modulePath": "notebooklm/auth.js", + "sourceFile": "notebooklm/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "practo", - "name": "book-confirm", - "description": "Confirm a Practo clinic visit booking after explicit confirmation", + "site": "notebooklm", + "name": "write-note", + "description": "Create a Studio note in an existing NotebookLM notebook with the given title and Markdown content", "access": "write", - "domain": "www.practo.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "practice_doctor_id", + "name": "notebook", "type": "str", "required": true, "positional": true, - "help": "Practo practice_doctor_id" + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "time", + "name": "title", "type": "str", "required": true, - "help": "Slot time YYYY-MM-DD HH:mm:ss" + "help": "Note title (1-200 chars)" }, { - "name": "profile-url", + "name": "content", "type": "str", - "required": false, - "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" + "required": true, + "help": "Note body as Markdown" }, { - "name": "confirm", + "name": "execute", "type": "boolean", - "default": false, "required": false, - "help": "Required. Set --confirm true to create the appointment." + "help": "Actually create the remote NotebookLM note" } ], "columns": [ - "status", - "practice_doctor_id", - "time", - "url" + "notebook_id", + "note_id", + "title", + "notebook_url" ], "type": "js", - "modulePath": "practo/book-confirm.js", - "sourceFile": "practo/book-confirm.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/write-note.js", + "sourceFile": "notebooklm/write-note.js", + "navigateBefore": false }, { - "site": "practo", - "name": "book-preview", - "description": "Preview Practo booking details for a selected slot without confirming", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, + "site": "paperreview", + "name": "feedback", + "description": "Submit feedback for a paperreview.ai review token", + "access": "write", + "domain": "paperreview.ai", + "strategy": "public", + "browser": false, "args": [ { - "name": "practice_doctor_id", + "name": "token", "type": "str", "required": true, "positional": true, - "help": "Practo practice_doctor_id" + "help": "Review token returned by paperreview.ai" }, { - "name": "time", - "type": "str", + "name": "helpfulness", + "type": "int", "required": true, - "help": "Slot time YYYY-MM-DD HH:mm:ss" + "help": "Helpfulness score from 1 to 5" }, { - "name": "profile-url", - "type": "str", - "required": false, - "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" - } - ], - "columns": [ - "practice_doctor_id", - "time", - "amount", - "prepaid", - "payment_mode", - "requires_payment", - "confirm_button", - "booking_url" - ], - "type": "js", - "modulePath": "practo/book-preview.js", - "sourceFile": "practo/book-preview.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "practo", - "name": "booking-link", - "description": "Build a Practo booking URL for a selected slot without confirming it", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "practice_doctor_id", + "name": "critical-error", "type": "str", "required": true, - "positional": true, - "help": "Practo practice_doctor_id" + "help": "Whether the review contains a critical error", + "choices": [ + "yes", + "no" + ] }, { - "name": "time", + "name": "actionable-suggestions", "type": "str", "required": true, - "help": "Slot time YYYY-MM-DD HH:mm:ss" + "help": "Whether the review contains actionable suggestions", + "choices": [ + "yes", + "no" + ] }, { - "name": "profile-url", - "type": "str", - "required": false, - "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" - } - ], - "columns": [ - "practice_doctor_id", - "time", - "booking_url" - ], - "type": "js", - "modulePath": "practo/booking-link.js", - "sourceFile": "practo/booking-link.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "cancel", - "description": "Cancel a logged-in Practo Drive appointment after explicit confirmation", - "access": "write", - "domain": "drive.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "appointment_id", + "name": "additional-comments", "type": "str", - "required": true, - "positional": true, - "help": "Appointment id from `practo appointments`" + "required": false, + "help": "Optional free-text feedback" }, { - "name": "confirm", - "type": "boolean", - "default": false, + "name": "timeout", + "type": "int", + "default": 30, "required": false, - "help": "Required. Set --confirm true to cancel the appointment." + "help": "Max seconds for the overall command (default: 30)" } ], "columns": [ "status", - "appointment_id" + "token", + "helpfulness", + "critical_error", + "actionable_suggestions", + "message" ], "type": "js", - "modulePath": "practo/cancel.js", - "sourceFile": "practo/cancel.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "paperreview/feedback.js", + "sourceFile": "paperreview/feedback.js" }, { - "site": "practo", - "name": "contact", - "description": "Get Practo virtual contact number for a practice_doctor_id", + "site": "paperreview", + "name": "review", + "description": "Fetch a paperreview.ai review by token", "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, + "domain": "paperreview.ai", + "strategy": "public", + "browser": false, "args": [ { - "name": "practice_doctor_id", + "name": "token", "type": "str", "required": true, "positional": true, - "help": "Practo practice_doctor_id from search results" + "help": "Review token returned by paperreview.ai" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds for the overall command (default: 30)" } ], - "columns": [ - "practice_doctor_id", - "phone", - "raw" - ], - "type": "js", - "modulePath": "practo/contact.js", - "sourceFile": "practo/contact.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "login", - "description": "Open practo login", - "access": "write", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [], "columns": [ "status", - "logged_in", - "site", - "name", - "action", - "verify_command" + "title", + "venue", + "numerical_score", + "has_feedback", + "review_url" ], "type": "js", - "modulePath": "practo/login.js", - "sourceFile": "practo/login.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "paperreview/review.js", + "sourceFile": "paperreview/review.js" }, { - "site": "practo", - "name": "profile", - "description": "Read public details from a Practo doctor profile URL", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, + "site": "paperreview", + "name": "submit", + "description": "Submit a PDF to paperreview.ai for review", + "access": "write", + "domain": "paperreview.ai", + "strategy": "public", + "browser": false, "args": [ { - "name": "url", + "name": "pdf", "type": "str", "required": true, "positional": true, - "help": "Practo doctor profile URL" - } - ], - "columns": [ - "name", - "specialty", - "experience", - "fee", - "profile_url" - ], - "type": "js", - "modulePath": "practo/profile.js", - "sourceFile": "practo/profile.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "search", - "description": "Search Practo doctors by specialty, city, and optional locality", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ + "help": "Path to the paper PDF" + }, { - "name": "specialty", + "name": "email", "type": "str", "required": true, - "positional": true, - "help": "Doctor specialty, e.g. orthopedist or dermatologist" + "help": "Email address for the submission" }, { - "name": "city", + "name": "venue", "type": "str", - "default": "bangalore", "required": false, - "help": "City, e.g. bangalore" + "help": "Optional target venue such as ICLR or NeurIPS" }, { - "name": "locality", - "type": "str", + "name": "dry-run", + "type": "bool", + "default": false, "required": false, - "help": "Optional locality, e.g. indiranagar" + "help": "Validate the input and stop before remote submission" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "prepare-only", + "type": "bool", + "default": false, "required": false, - "help": "Max doctors to return (1-25)" - } - ], - "columns": [ - "rank", - "practice_doctor_id", - "doctor_id", - "practice_id", - "name", - "specialty", - "experience_years", - "locality", - "clinic", - "fee", - "next_available", - "profile_url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "practo/search.js", - "sourceFile": "practo/search.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "slots", - "description": "List available Practo appointment slots for a practice_doctor_id", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "practice_doctor_id", - "type": "str", - "required": true, - "positional": true, - "help": "Practo practice_doctor_id from search results" + "help": "Request an upload slot but stop before uploading the PDF" }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 20, + "default": 120, "required": false, - "help": "Max slots to return (1-25)" + "help": "Max seconds for the overall command (default: 120)" } ], "columns": [ - "practice_doctor_id", - "time", - "available", - "amount", - "prepaid", - "appointment_token" - ], - "type": "js", - "modulePath": "practo/slots.js", - "sourceFile": "practo/slots.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "whoami", - "aliases": [ - "auth-status" - ], - "description": "Show the current logged-in practo account", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name" + "status", + "file", + "email", + "venue", + "token", + "review_url", + "message" ], "type": "js", - "modulePath": "practo/login.js", - "sourceFile": "practo/login.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "paperreview/submit.js", + "sourceFile": "paperreview/submit.js" }, { "site": "qoder", @@ -7950,159 +5924,40 @@ { "name": "limit", "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "score", - "comments", - "url" - ], - "type": "js", - "modulePath": "reddit/user-posts.js", - "sourceFile": "reddit/user-posts.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "whoami", - "description": "Show the currently logged-in Reddit user", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "reddit/whoami.js", - "sourceFile": "reddit/whoami.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reuters", - "name": "article-detail", - "description": "Reuters Reuters article detail:title/author/body text", - "access": "read", - "domain": "www.reuters.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "Reuters article URL (must be on reuters.com)" - } - ], - "columns": [ - "title", - "date", - "section", - "section_path", - "authors", - "description", - "word_count", - "url", - "body" - ], - "type": "js", - "modulePath": "reuters/article-detail.js", - "sourceFile": "reuters/article-detail.js", - "navigateBefore": "https://www.reuters.com" - }, - { - "site": "reuters", - "name": "login", - "description": "Open reuters login", - "access": "write", - "domain": "reuters.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "subscribed", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "reuters/auth.js", - "sourceFile": "reuters/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "reuters", - "name": "search", - "description": "Reuters Reuters news search", - "access": "read", - "domain": "www.reuters.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, + "default": 15, "required": false, - "help": "Number of results (1-40)" + "help": "" } ], "columns": [ - "rank", "title", - "date", - "section", - "section_path", - "authors", + "subreddit", + "score", + "comments", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "reuters/search.js", - "sourceFile": "reuters/search.js", - "navigateBefore": "https://www.reuters.com" + "modulePath": "reddit/user-posts.js", + "sourceFile": "reddit/user-posts.js", + "navigateBefore": "https://reddit.com" }, { - "site": "reuters", + "site": "reddit", "name": "whoami", - "description": "Show the current logged-in reuters account", + "description": "Show the currently logged-in Reddit user", "access": "read", - "domain": "reuters.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "user_id", - "subscribed" + "field", + "value" ], "type": "js", - "modulePath": "reuters/auth.js", - "sourceFile": "reuters/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "reddit/whoami.js", + "sourceFile": "reddit/whoami.js", + "navigateBefore": "https://reddit.com" }, { "site": "slock", @@ -9867,401 +7722,133 @@ "strategy": "local", "browser": false, "args": [ - { - "name": "mode", - "type": "str", - "default": "context", - "required": false, - "positional": true, - "help": "off / track / context", - "choices": [ - "off", - "track", - "context" - ] - } - ], - "columns": [ - "repeat" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "search", - "description": "Search for tracks", - "access": "read", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results (default: 10)" - } - ], - "columns": [ - "track", - "artist", - "album", - "uri" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "shuffle", - "description": "Toggle shuffle on/off", - "access": "write", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "state", - "type": "str", - "default": "on", - "required": false, - "positional": true, - "help": "on or off", - "choices": [ - "on", - "off" - ] - } - ], - "columns": [ - "shuffle" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "status", - "description": "Show current playback status", - "access": "read", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "track", - "artist", - "album", - "status", - "progress" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "volume", - "description": "Set playback volume (0-100)", - "access": "write", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "level", - "type": "int", - "default": 50, - "required": true, - "positional": true, - "help": "Volume 0–100" - } - ], - "columns": [ - "volume" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "suno", - "name": "download", - "description": "Download an existing Suno clip (MP3 + optional WAV/M4A/video) by id", - "access": "write", - "domain": "suno.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "clip", - "type": "str", - "required": true, - "positional": true, - "help": "Clip UUID or https://suno.com/song/ URL" - }, - { - "name": "formats", - "type": "str", - "required": false, - "help": "Comma-separated formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" - }, - { - "name": "op", - "type": "str", - "required": false, - "help": "Output directory (default: ~/Music/suno)" - }, - { - "name": "confirm-paid", - "type": "boolean", - "default": false, - "required": false, - "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." - } - ], - "columns": [ - "status", - "clip", - "title", - "files", - "link" - ], - "defaultFormat": "plain", - "type": "js", - "modulePath": "suno/download.js", - "sourceFile": "suno/download.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "suno", - "name": "generate", - "description": "Generate music with Suno (V5.5 chirp-fenix by default) and download clips locally", - "access": "write", - "domain": "suno.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": false, - "positional": true, - "help": "Simple-mode description (ignored when --lyrics is provided)" - }, - { - "name": "lyrics", - "type": "str", - "required": false, - "help": "Custom-mode lyrics (with [Verse]/[Chorus] metatags). Triggers Custom mode." - }, - { - "name": "tags", - "type": "str", - "required": false, - "help": "Custom-mode style tags (genre, BPM, instruments...). Used with --lyrics." - }, - { - "name": "negative-tags", - "type": "str", - "required": false, - "help": "Custom-mode style exclusions (e.g. \"no vocals, no autotune\"). Used with --lyrics." - }, - { - "name": "title", - "type": "str", - "required": false, - "help": "Song title (default: auto-derived from prompt)" - }, - { - "name": "instrumental", - "type": "boolean", - "default": false, - "required": false, - "help": "No vocals" - }, - { - "name": "model", - "type": "str", - "required": false, - "help": "Model id: chirp-fenix, chirp-bluejay, chirp-v4, chirp-v3-5. Default: chirp-fenix" - }, - { - "name": "weirdness", - "type": "str", - "required": false, - "help": "Creative weirdness slider (0..1). Default: 0.5" - }, - { - "name": "style-weight", - "type": "str", - "required": false, - "help": "Style adherence slider (0..1). Default: 0.5" - }, - { - "name": "formats", - "type": "str", - "required": false, - "help": "Comma-separated download formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" - }, - { - "name": "op", - "type": "str", - "required": false, - "help": "Output directory (default: ~/Music/suno)" - }, - { - "name": "timeout", - "type": "int", - "default": 300, - "required": false, - "help": "Max seconds to wait for clips to finish (default: 300)" - }, - { - "name": "sd", - "type": "boolean", - "default": false, - "required": false, - "help": "Skip download; only print clip ids and Suno URLs" - }, - { - "name": "confirm-paid", - "type": "boolean", - "default": false, + { + "name": "mode", + "type": "str", + "default": "context", "required": false, - "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." + "positional": true, + "help": "off / track / context", + "choices": [ + "off", + "track", + "context" + ] } ], "columns": [ - "status", - "clip", - "title", - "files", - "link" + "repeat" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "suno/generate.js", - "sourceFile": "suno/generate.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "suno", - "name": "list", - "description": "List recent Suno clips in your library (id, title, status, created_at, link)", + "site": "spotify", + "name": "search", + "description": "Search for tracks", "access": "read", - "domain": "suno.com", - "strategy": "cookie", - "browser": true, + "strategy": "local", + "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max clips to list (default: 20)" + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" }, { - "name": "page", + "name": "limit", "type": "int", - "default": 0, + "default": 10, "required": false, - "help": "Pagination offset, 0-based (default: 0)" + "help": "Number of results (default: 10)" } ], "columns": [ - "rank", - "clip", - "title", - "status", - "created", - "link" + "track", + "artist", + "album", + "uri" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "suno/list.js", - "sourceFile": "suno/list.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "suno", - "name": "login", - "description": "Open suno login", + "site": "spotify", + "name": "shuffle", + "description": "Toggle shuffle on/off", "access": "write", - "domain": "suno.com", - "strategy": "cookie", - "browser": true, - "args": [], + "strategy": "local", + "browser": false, + "args": [ + { + "name": "state", + "type": "str", + "default": "on", + "required": false, + "positional": true, + "help": "on or off", + "choices": [ + "on", + "off" + ] + } + ], "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" + "shuffle" ], "type": "js", - "modulePath": "suno/auth.js", - "sourceFile": "suno/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "suno", + "site": "spotify", "name": "status", - "description": "Check Suno login, plan, credit balance, and captcha readiness", + "description": "Show current playback status", "access": "read", - "domain": "suno.com", - "strategy": "cookie", - "browser": true, + "strategy": "local", + "browser": false, "args": [], "columns": [ - "Status", - "Plan", - "Credits", - "Monthly", - "Captcha" + "track", + "artist", + "album", + "status", + "progress" ], "type": "js", - "modulePath": "suno/status.js", - "sourceFile": "suno/status.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { - "site": "suno", - "name": "whoami", - "description": "Show the current logged-in suno account", - "access": "read", - "domain": "suno.com", - "strategy": "cookie", - "browser": true, - "args": [], + "site": "spotify", + "name": "volume", + "description": "Set playback volume (0-100)", + "access": "write", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "level", + "type": "int", + "default": 50, + "required": true, + "positional": true, + "help": "Volume 0–100" + } + ], "columns": [ - "logged_in", - "site", - "user_id", - "name" + "volume" ], "type": "js", - "modulePath": "suno/auth.js", - "sourceFile": "suno/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "spotify/spotify.js", + "sourceFile": "spotify/spotify.js" }, { "site": "tiktok", @@ -13062,262 +10649,52 @@ "type": "js", "modulePath": "twitter/unlike.js", "sourceFile": "twitter/unlike.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "unretweet", - "description": "Undo a retweet on a specific tweet", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to unretweet" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/unretweet.js", - "sourceFile": "twitter/unretweet.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "whoami", - "description": "Show the current logged-in twitter account", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "username", - "url" - ], - "type": "js", - "modulePath": "twitter/auth.js", - "sourceFile": "twitter/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "upwork", - "name": "detail", - "aliases": [ - "job", - "view" - ], - "description": "Read the full Upwork job posting by ciphertext id (e.g. ~022054964136512093518)", - "access": "read", - "domain": "www.upwork.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Job ciphertext id (~01… / ~02…) or full /jobs/~02… URL" - } - ], - "columns": [ - "id", - "title", - "type", - "budget", - "experienceLevel", - "workload", - "category", - "skills", - "description", - "clientCountry", - "clientSpent", - "clientHires", - "clientRating", - "proposalsCount", - "publishedOn", - "url" - ], - "type": "js", - "modulePath": "upwork/detail.js", - "sourceFile": "upwork/detail.js", - "navigateBefore": false - }, - { - "site": "upwork", - "name": "feed", - "aliases": [ - "best-matches" - ], - "description": "Upwork personalized jobs feed (best-matches | most-recent) — requires login", - "access": "read", - "domain": "www.upwork.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "tab", - "type": "str", - "default": "best-matches", - "required": false, - "positional": true, - "help": "Feed tab: best-matches | most-recent" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max rows to return (1-50, capped at one page)" - } - ], - "columns": [ - "rank", - "id", - "title", - "type", - "budget", - "experienceLevel", - "proposalsTier", - "skills", - "clientCountry", - "clientRating", - "publishedOn", - "url" - ], - "type": "js", - "modulePath": "upwork/feed.js", - "sourceFile": "upwork/feed.js", - "navigateBefore": false - }, - { - "site": "upwork", - "name": "login", - "description": "Open upwork login", - "access": "write", - "domain": "upwork.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "ciphertext", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "upwork/auth.js", - "sourceFile": "upwork/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "navigateBefore": true }, { - "site": "upwork", - "name": "search", - "description": "Upwork keyword job search (logged-in browser session, US site)", - "access": "read", - "domain": "www.upwork.com", - "strategy": "cookie", + "site": "twitter", + "name": "unretweet", + "description": "Undo a retweet on a specific tweet", + "access": "write", + "domain": "x.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "query", - "type": "str", + "name": "url", + "type": "string", "required": true, "positional": true, - "help": "Job keyword (skill / title / company)" - }, - { - "name": "location", - "type": "string", - "default": "", - "required": false, - "help": "Country/city filter (e.g. \"United States\", \"Remote\")" - }, - { - "name": "category", - "type": "string", - "default": "", - "required": false, - "help": "Category uid filter (advanced; from job detail `category` slug)" - }, - { - "name": "sort", - "type": "string", - "default": "recency", - "required": false, - "help": "Sort: recency | relevance | client_total_charge | client_total_reviews" - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number (1-based)" - }, - { - "name": "per_page", - "type": "int", - "default": 10, - "required": false, - "help": "Rows per page (10-50, capped at one page)" + "help": "The URL of the tweet to unretweet" } ], "columns": [ - "rank", - "id", - "title", - "type", - "budget", - "experienceLevel", - "proposalsTier", - "skills", - "clientCountry", - "clientRating", - "publishedOn", - "url" - ], - "tags": [ - "search" + "status", + "message" ], "type": "js", - "modulePath": "upwork/search.js", - "sourceFile": "upwork/search.js", - "navigateBefore": false + "modulePath": "twitter/unretweet.js", + "sourceFile": "twitter/unretweet.js", + "navigateBefore": true }, { - "site": "upwork", + "site": "twitter", "name": "whoami", - "description": "Show the current logged-in upwork account", + "description": "Show the current logged-in twitter account", "access": "read", - "domain": "upwork.com", + "domain": "x.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ "logged_in", "site", - "user_id", - "ciphertext" + "username", + "url" ], "type": "js", - "modulePath": "upwork/auth.js", - "sourceFile": "upwork/auth.js", + "modulePath": "twitter/auth.js", + "sourceFile": "twitter/auth.js", "navigateBefore": false, "siteSession": "persistent" }, @@ -14423,252 +11800,5 @@ "sourceFile": "youtube/auth.js", "navigateBefore": false, "siteSession": "persistent" - }, - { - "site": "zepto", - "name": "add-to-cart", - "description": "Add a Zepto product to cart", - "access": "write", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "product", - "type": "str", - "required": true, - "positional": true, - "help": "Product URL from Zepto search results" - }, - { - "name": "quantity", - "type": "int", - "default": 1, - "required": false, - "help": "Quantity to add (max 12)" - } - ], - "columns": [ - "ok", - "product_id", - "quantity", - "item_count", - "message" - ], - "type": "js", - "modulePath": "zepto/add-to-cart.js", - "sourceFile": "zepto/add-to-cart.js", - "navigateBefore": false - }, - { - "site": "zepto", - "name": "cart", - "description": "Read Zepto cart line items", - "access": "read", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "rank", - "product_id", - "title", - "pack_size", - "quantity", - "price", - "mrp", - "availability" - ], - "type": "js", - "modulePath": "zepto/cart.js", - "sourceFile": "zepto/cart.js", - "navigateBefore": false - }, - { - "site": "zepto", - "name": "checkout", - "description": "Open Zepto checkout review without placing an order", - "access": "write", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "ok", - "stage", - "item_count", - "next_action", - "url" - ], - "type": "js", - "modulePath": "zepto/checkout.js", - "sourceFile": "zepto/checkout.js", - "navigateBefore": false - }, - { - "site": "zepto", - "name": "location", - "description": "Show the selected Zepto delivery location", - "access": "read", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "selected", - "label", - "area", - "city", - "pincode", - "hasCoordinates", - "source" - ], - "type": "js", - "modulePath": "zepto/location.js", - "sourceFile": "zepto/location.js", - "navigateBefore": false - }, - { - "site": "zepto", - "name": "login", - "description": "Open zepto login", - "access": "write", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "zepto/auth.js", - "sourceFile": "zepto/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "zepto", - "name": "place-order", - "description": "Submit a real Zepto order only when --confirm true is passed", - "access": "write", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "confirm", - "type": "boolean", - "default": false, - "required": false, - "help": "Required. Set true to submit a real Zepto order/payment action." - } - ], - "columns": [ - "status", - "confirmed", - "message" - ], - "type": "js", - "modulePath": "zepto/place-order.js", - "sourceFile": "zepto/place-order.js", - "navigateBefore": false - }, - { - "site": "zepto", - "name": "product", - "description": "Read Zepto product details", - "access": "read", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "product", - "type": "str", - "required": true, - "positional": true, - "help": "Product URL from Zepto search results" - } - ], - "columns": [ - "product_id", - "title", - "brand", - "pack_size", - "price", - "mrp", - "availability", - "url" - ], - "type": "js", - "modulePath": "zepto/product.js", - "sourceFile": "zepto/product.js", - "navigateBefore": false - }, - { - "site": "zepto", - "name": "search", - "description": "Search Zepto products", - "access": "read", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum products to return (max 50)" - } - ], - "columns": [ - "rank", - "product_id", - "title", - "brand", - "pack_size", - "price", - "mrp", - "availability", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "zepto/search.js", - "sourceFile": "zepto/search.js", - "navigateBefore": false - }, - { - "site": "zepto", - "name": "whoami", - "description": "Show the current logged-in zepto account", - "access": "read", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site" - ], - "type": "js", - "modulePath": "zepto/auth.js", - "sourceFile": "zepto/auth.js", - "navigateBefore": false, - "siteSession": "persistent" } ] diff --git a/plugin-command-manifest.json b/plugin-command-manifest.json index 15317902..ccf05fbf 100644 --- a/plugin-command-manifest.json +++ b/plugin-command-manifest.json @@ -2958,16 +2958,16 @@ "sourceFile": "plugins/brave/search.js" }, { - "site": "chatwise", + "site": "chatgpt", "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", + "description": "Send a prompt to ChatGPT web and wait for the response", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "chatgpt.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "text", + "name": "prompt", "type": "str", "required": true, "positional": true, @@ -2976,2941 +2976,4715 @@ { "name": "timeout", "type": "int", - "default": 30, + "default": 120, "required": false, - "help": "Max seconds to wait (default: 30)" + "help": "Max seconds to wait for response" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Continue an existing ChatGPT conversation ID or /c/ URL" + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + }, + { + "name": "wait", + "type": "boolean", + "default": true, + "required": false, + "help": "Wait for the assistant response after sending" + }, + { + "name": "deep-research", + "type": "boolean", + "default": false, + "required": false, + "help": "Enable ChatGPT Deep Research (Deep Research)" + }, + { + "name": "web-search", + "type": "boolean", + "default": false, + "required": false, + "help": "Enable ChatGPT Web Search (Web Search)" } ], "columns": [ - "Role", - "Text" + "conversationId", + "conversationUrl", + "tool", + "response" ], "type": "js", - "modulePath": "plugins/chatwise/ask.js", - "sourceFile": "plugins/chatwise/ask.js", - "navigateBefore": true + "modulePath": "plugins/chatgpt/ask.js", + "sourceFile": "plugins/chatgpt/ask.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "chatwise", - "name": "export", - "description": "Export the current ChatWise conversation to a Markdown file", + "site": "chatgpt", + "name": "deep-research-result", + "description": "Read a ChatGPT Deep Research report or progress from the conversation payload", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "chatgpt.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "output", + "name": "id", "type": "str", + "required": true, + "positional": true, + "help": "Conversation ID or full /c/ URL" + }, + { + "name": "wait", + "type": "boolean", + "default": false, "required": false, - "help": "Output file (default: /tmp/chatwise-export.md)" + "help": "Wait until Deep Research completes or becomes extractable" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait when --wait is true" + }, + { + "name": "stable", + "type": "int", + "default": 6, + "required": false, + "help": "Seconds the report text must remain unchanged when --wait is true" } ], "columns": [ - "Status", - "File", - "Messages" + "conversationId", + "status", + "report", + "sources", + "progress", + "asyncTaskConversationId", + "widgetSessionId", + "asyncStatus", + "venusMessageType", + "venusStatus", + "waitingForUserUntil", + "planTitle", + "planId", + "url", + "method", + "diagnostics" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/chatwise/export.js", - "sourceFile": "plugins/chatwise/export.js", - "navigateBefore": true + "modulePath": "plugins/chatgpt/deep-research-result.js", + "sourceFile": "plugins/chatgpt/deep-research-result.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "chatwise", - "name": "history", - "description": "List conversation history in ChatWise sidebar", + "site": "chatgpt", + "name": "detail", + "description": "Open a ChatGPT web conversation by ID and read its messages", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "chatgpt.com", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Conversation ID or full /c/ URL" + }, + { + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" + }, + { + "name": "wait", + "type": "boolean", + "default": false, + "required": false, + "help": "Wait until the conversation stops generating and stabilizes" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait when --wait is true" + }, + { + "name": "stable", + "type": "int", + "default": 6, + "required": false, + "help": "Seconds the final messages must remain unchanged when --wait is true" + } + ], "columns": [ "Index", - "Title" + "Role", + "Text", + "Generating", + "StableSeconds" ], "type": "js", - "modulePath": "plugins/chatwise/history.js", - "sourceFile": "plugins/chatwise/history.js", - "navigateBefore": true + "modulePath": "plugins/chatgpt/detail.js", + "sourceFile": "plugins/chatgpt/detail.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "chatwise", - "name": "model", - "description": "Get or switch the active AI model in ChatWise", + "site": "chatgpt", + "name": "history", + "description": "List visible ChatGPT web conversation history from the sidebar", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "chatgpt.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "model-name", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "positional": true, - "help": "Model to switch to (e.g. gpt-4, claude-3)" + "help": "Max conversations to show" } ], "columns": [ - "Status", - "Model" + "Index", + "Id", + "Title", + "Url" ], "type": "js", - "modulePath": "plugins/chatwise/model.js", - "sourceFile": "plugins/chatwise/model.js", - "navigateBefore": true + "modulePath": "plugins/chatgpt/history.js", + "sourceFile": "plugins/chatgpt/history.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "chatwise", - "name": "new", - "description": "Start a new ChatWise conversation session", + "site": "chatgpt", + "name": "image", + "description": "Generate images with ChatGPT web and save them locally", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "chatgpt.com", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Image prompt to send to ChatGPT" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Local image path to attach before prompting; comma-separated paths are supported", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start image generation inside a ChatGPT project ID or /g/g-p- URL" + }, + { + "name": "op", + "type": "str", + "required": false, + "help": "Output directory (default: ~/Pictures/chatgpt)", + "file": { + "direction": "output", + "pathKind": "directory", + "multiple": false, + "defaultPath": "~/Pictures/chatgpt" + } + }, + { + "name": "sd", + "type": "boolean", + "default": false, + "required": false, + "help": "Skip download shorthand; only show ChatGPT link" + }, + { + "name": "timeout", + "type": "int", + "default": 240, + "required": false, + "help": "Max seconds for the overall command (default: 240)" + } + ], "columns": [ - "Status" + "status", + "file", + "link" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/chatwise/new.js", - "sourceFile": "plugins/chatwise/new.js", - "navigateBefore": true + "modulePath": "plugins/chatgpt/image.js", + "sourceFile": "plugins/chatgpt/image.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "chatwise", - "name": "read", - "description": "Read the current ChatWise conversation history", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "chatgpt", + "name": "login", + "description": "Open chatgpt login", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Content" + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/chatwise/read.js", - "sourceFile": "plugins/chatwise/read.js", - "navigateBefore": true + "modulePath": "plugins/chatgpt/auth.js", + "sourceFile": "plugins/chatgpt/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "chatwise", - "name": "screenshot", - "description": "Capture a snapshot of the current ChatWise window (DOM + Accessibility tree)", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "chatgpt", + "name": "model", + "description": "Switch ChatGPT web model or intelligence level (GPT-5.6 Pro, fast, balanced, advanced, very-high, pro)", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "output", + "name": "model", + "type": "str", + "required": true, + "positional": true, + "help": "ChatGPT model or intelligence level to switch to", + "choices": [ + "fast", + "speed", + "instant", + "balanced", + "balance", + "medium", + "advanced", + "high", + "thinking", + "very-high", + "ultra", + "xhigh", + "x-high", + "extra-high", + "very high", + "gpt-5.6-pro", + "gpt-5-6-pro", + "gpt-5.6-sol-pro", + "gpt-5-6-sol-pro", + "gpt-5.6", + "gpt-5-6", + "5.6-pro", + "5.6", + "pro", + "professional" + ] + }, + { + "name": "project", "type": "str", "required": false, - "help": "Output file path (default: /tmp/chatwise-snapshot.txt)" + "valueRequired": true, + "help": "Open a ChatGPT project ID or /g/g-p- URL before switching intelligence level" } ], "columns": [ "Status", - "File" + "Model" ], "type": "js", - "modulePath": "plugins/chatwise/screenshot.js", - "sourceFile": "plugins/chatwise/screenshot.js", - "navigateBefore": true + "modulePath": "plugins/chatgpt/model.js", + "sourceFile": "plugins/chatgpt/model.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "chatwise", - "name": "send", - "description": "Send a message to the active ChatWise conversation", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "site": "chatgpt", + "name": "new", + "description": "Start a new ChatGPT web conversation", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "text", + "name": "project", "type": "str", - "required": true, - "positional": true, - "help": "Message to send" + "required": false, + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" } ], "columns": [ - "Status", - "InjectedText" + "Status" ], "type": "js", - "modulePath": "plugins/chatwise/send.js", - "sourceFile": "plugins/chatwise/send.js", - "navigateBefore": true + "modulePath": "plugins/chatgpt/new.js", + "sourceFile": "plugins/chatgpt/new.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "chatwise", - "name": "status", - "description": "Check active CDP connection to ChatWise Desktop", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "chatgpt", + "name": "project-file-add", + "description": "Upload files to a ChatGPT project as project knowledge (not just conversation attachments)", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "file", + "type": "str", + "required": true, + "positional": true, + "help": "Local file path(s) to upload; comma-separated paths are supported", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "application/pdf", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + }, + { + "name": "id", + "type": "str", + "required": true, + "help": "Project ID or /g/g-p- URL" + } + ], "columns": [ "Status", - "Url", - "Title" + "File" ], "type": "js", - "modulePath": "plugins/chatwise/status.js", - "sourceFile": "plugins/chatwise/status.js", - "navigateBefore": true + "modulePath": "plugins/chatgpt/project-file-add.js", + "sourceFile": "plugins/chatgpt/project-file-add.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "chess", - "name": "analyze", - "description": "Open a Chess.com game in the browser analysis board", + "site": "chatgpt", + "name": "project-list", + "description": "List visible ChatGPT projects from the sidebar", "access": "read", - "domain": "www.chess.com", - "strategy": "ui", + "domain": "chatgpt.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "game-url", - "type": "string", - "required": true, - "positional": true, - "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max projects to show" } ], "columns": [ - "kind", - "game_id", - "analysis_url" + "Index", + "Id", + "Title", + "Url" ], "type": "js", - "modulePath": "plugins/chess/analyze.js", - "sourceFile": "plugins/chess/analyze.js", - "navigateBefore": false + "modulePath": "plugins/chatgpt/project-list.js", + "sourceFile": "plugins/chatgpt/project-list.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "chess", - "name": "game", - "description": "Chess.com single-game detail (white, black, result, ECO, time control) by full game URL", + "site": "chatgpt", + "name": "read", + "description": "Read messages in the current ChatGPT web conversation", "access": "read", - "domain": "www.chess.com", - "strategy": "public", - "browser": false, + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "game-url", - "type": "string", - "required": true, - "positional": true, - "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" } ], "columns": [ - "kind", - "game_id", - "date", - "white", - "white_rating", - "black", - "black_rating", - "result", - "winner_color", - "termination", - "eco", - "time_control", - "rated", - "ply_count", - "url" + "Index", + "Role", + "Text" ], "type": "js", - "modulePath": "plugins/chess/game.js", - "sourceFile": "plugins/chess/game.js" + "modulePath": "plugins/chatgpt/read.js", + "sourceFile": "plugins/chatgpt/read.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "chess", - "name": "games", - "description": "Chess.com recent games for a player, newest first", - "access": "read", - "domain": "api.chess.com", - "strategy": "public", - "browser": false, + "site": "chatgpt", + "name": "send", + "description": "Send a prompt to ChatGPT web without waiting for the response", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "username", - "type": "string", + "name": "prompt", + "type": "str", "required": true, "positional": true, - "help": "Chess.com username" + "help": "Prompt to send" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "new", + "type": "boolean", + "default": false, "required": false, - "help": "Number of recent games (1-100)" + "help": "Start a new chat before sending" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Continue an existing ChatGPT conversation ID or /c/ URL" + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" } ], "columns": [ - "date", - "time_class", - "rated", - "my_color", - "my_rating", - "my_result", - "opponent", - "opponent_rating", - "accuracy_white", - "accuracy_black", - "eco", - "opening_name", - "url" + "Status", + "InjectedText" ], "type": "js", - "modulePath": "plugins/chess/games.js", - "sourceFile": "plugins/chess/games.js" + "modulePath": "plugins/chatgpt/send.js", + "sourceFile": "plugins/chatgpt/send.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "chess", - "name": "stats", - "description": "Chess.com player ratings + win/loss record across game kinds", + "site": "chatgpt", + "name": "status", + "description": "Check ChatGPT web page availability and login state", "access": "read", - "domain": "api.chess.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Chess.com username (case-insensitive)" - } - ], + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "kind", - "rating_current", - "rating_best", - "wins", - "losses", - "draws" + "Status", + "Login", + "Url" ], "type": "js", - "modulePath": "plugins/chess/stats.js", - "sourceFile": "plugins/chess/stats.js" + "modulePath": "plugins/chatgpt/status.js", + "sourceFile": "plugins/chatgpt/status.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "cincinnati", - "name": "export-postgraduate-courses", - "description": "Export University of Cincinnati graduate and professional programs from official public sources.", + "site": "chatgpt", + "name": "whoami", + "description": "Show the current logged-in chatgpt account", "access": "read", - "example": "webcmd cincinnati export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.grad.uc.edu", - "strategy": "public", - "browser": false, + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" + ], + "type": "js", + "modulePath": "plugins/chatgpt/auth.js", + "sourceFile": "plugins/chatgpt/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatwise", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" }, { - "name": "count", + "name": "timeout", "type": "int", + "default": 30, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Max seconds to wait (default: 30)" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" - ], - "type": "js", - "modulePath": "plugins/cincinnati/export-postgraduate-courses.js", - "sourceFile": "plugins/cincinnati/export-postgraduate-courses.js" - }, - { - "site": "codex", - "name": "archive", - "description": "Archive (Codex's term for delete) the selected conversation via the Chat actions header menu. No confirmation in UI — pass --yes to actually archive.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "yes", - "type": "boolean", - "default": false, - "required": false, - "help": "Actually archive (default: dry-run preview)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "status", - "thread_id", - "project", - "conversation" + "Role", + "Text" ], "type": "js", - "modulePath": "plugins/codex/archive.js", - "sourceFile": "plugins/codex/archive.js", + "modulePath": "plugins/chatwise/ask.js", + "sourceFile": "plugins/chatwise/ask.js", "navigateBefore": true }, { - "site": "codex", - "name": "ask", - "description": "Send a prompt to the current or selected Codex conversation and wait for the AI response", - "access": "write", + "site": "chatwise", + "name": "export", + "description": "Export the current ChatWise conversation to a Markdown file", + "access": "read", "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 60, - "required": false, - "help": "Max seconds to wait for response (default: 60)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", + "name": "output", "type": "str", "required": false, - "help": "Exact Codex thread id to select" + "help": "Output file (default: /tmp/chatwise-export.md)" } ], "columns": [ - "Role", - "Project", - "Conversation", - "Text" + "Status", + "File", + "Messages" ], "type": "js", - "modulePath": "plugins/codex/ask.js", - "sourceFile": "plugins/codex/ask.js", + "modulePath": "plugins/chatwise/export.js", + "sourceFile": "plugins/chatwise/export.js", "navigateBefore": true }, { - "site": "codex", - "name": "dump", - "description": "Dump the DOM and Accessibility tree of codex for reverse-engineering", + "site": "chatwise", + "name": "history", + "description": "List conversation history in ChatWise sidebar", "access": "read", "domain": "localhost", "strategy": "ui", "browser": true, "args": [], "columns": [ - "action", - "files" + "Index", + "Title" ], "type": "js", - "modulePath": "plugins/codex/dump.js", - "sourceFile": "plugins/codex/dump.js", + "modulePath": "plugins/chatwise/history.js", + "sourceFile": "plugins/chatwise/history.js", "navigateBefore": true }, { - "site": "codex", - "name": "export", - "description": "Export the current Codex conversation to a Markdown file", + "site": "chatwise", + "name": "model", + "description": "Get or switch the active AI model in ChatWise", "access": "read", "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "output", + "name": "model-name", "type": "str", "required": false, - "help": "Output file (default: /tmp/codex-export.md)" + "positional": true, + "help": "Model to switch to (e.g. gpt-4, claude-3)" } ], "columns": [ "Status", - "File", - "Messages" + "Model" ], "type": "js", - "modulePath": "plugins/codex/export.js", - "sourceFile": "plugins/codex/export.js", + "modulePath": "plugins/chatwise/model.js", + "sourceFile": "plugins/chatwise/model.js", "navigateBefore": true }, { - "site": "codex", - "name": "extract-diff", - "description": "Extract visual code review diff patches from Codex", + "site": "chatwise", + "name": "new", + "description": "Start a new ChatWise conversation session", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/chatwise/new.js", + "sourceFile": "plugins/chatwise/new.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "read", + "description": "Read the current ChatWise conversation history", "access": "read", "domain": "localhost", "strategy": "ui", "browser": true, "args": [], "columns": [ - "File", - "Diff" + "Content" ], "type": "js", - "modulePath": "plugins/codex/extract-diff.js", - "sourceFile": "plugins/codex/extract-diff.js", + "modulePath": "plugins/chatwise/read.js", + "sourceFile": "plugins/chatwise/read.js", "navigateBefore": true }, { - "site": "codex", - "name": "history", - "description": "List visible Codex conversation threads grouped by project", + "site": "chatwise", + "name": "screenshot", + "description": "Capture a snapshot of the current ChatWise window (DOM + Accessibility tree)", "access": "read", "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "project", - "type": "str", - "required": false, - "help": "Filter by project label or path" - }, - { - "name": "limit", + "name": "output", "type": "str", "required": false, - "help": "Max conversations per project" + "help": "Output file path (default: /tmp/chatwise-snapshot.txt)" } ], "columns": [ - "Project", - "Index", - "Title", - "Updated", - "Active" + "Status", + "File" ], "type": "js", - "modulePath": "plugins/codex/history.js", - "sourceFile": "plugins/codex/history.js", + "modulePath": "plugins/chatwise/screenshot.js", + "sourceFile": "plugins/chatwise/screenshot.js", "navigateBefore": true }, { - "site": "codex", - "name": "model", - "description": "Read, list, or switch the active model / reasoning level in Codex Desktop. The composer toolbar button toggles a menu that mixes model variants (GPT-5.5, Speed) with reasoning levels (Low/Medium/High/Extra High).", + "site": "chatwise", + "name": "send", + "description": "Send a message to the active ChatWise conversation", "access": "write", "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "name", + "name": "text", "type": "str", - "required": false, + "required": true, "positional": true, - "help": "Substring (case-insensitive) of a model / reasoning level to switch to. Omit to read current." - }, - { - "name": "list", - "type": "boolean", - "default": false, - "required": false, - "help": "List all menu options (does not switch)" + "help": "Message to send" } ], "columns": [ "Status", - "Model" + "InjectedText" ], "type": "js", - "modulePath": "plugins/codex/model.js", - "sourceFile": "plugins/codex/model.js", + "modulePath": "plugins/chatwise/send.js", + "sourceFile": "plugins/chatwise/send.js", "navigateBefore": true }, { - "site": "codex", - "name": "new", - "description": "Start a new Codex conversation session", - "access": "write", + "site": "chatwise", + "name": "status", + "description": "Check active CDP connection to ChatWise Desktop", + "access": "read", "domain": "localhost", "strategy": "ui", "browser": true, "args": [], "columns": [ - "Status" + "Status", + "Url", + "Title" ], "type": "js", - "modulePath": "plugins/codex/new.js", - "sourceFile": "plugins/codex/new.js", + "modulePath": "plugins/chatwise/status.js", + "sourceFile": "plugins/chatwise/status.js", "navigateBefore": true }, { - "site": "codex", - "name": "pin", - "description": "Pin the selected Codex conversation via the Chat actions header menu.", - "access": "write", - "domain": "localhost", + "site": "chess", + "name": "analyze", + "description": "Open a Chess.com game in the browser analysis board", + "access": "read", + "domain": "www.chess.com", "strategy": "ui", "browser": true, "args": [ { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" + "name": "game-url", + "type": "string", + "required": true, + "positional": true, + "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" } ], "columns": [ - "status", - "thread_id", - "project", - "conversation" + "kind", + "game_id", + "analysis_url" ], "type": "js", - "modulePath": "plugins/codex/pin.js", - "sourceFile": "plugins/codex/pin.js", - "navigateBefore": true + "modulePath": "plugins/chess/analyze.js", + "sourceFile": "plugins/chess/analyze.js", + "navigateBefore": false }, { - "site": "codex", - "name": "projects", - "description": "List Codex projects and visible conversations from the sidebar", + "site": "chess", + "name": "game", + "description": "Chess.com single-game detail (white, black, result, ECO, time control) by full game URL", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "domain": "www.chess.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "project", - "type": "str", - "required": false, - "help": "Filter by project label or path" - }, - { - "name": "limit", - "type": "str", - "required": false, - "help": "Max conversations per project" + "name": "game-url", + "type": "string", + "required": true, + "positional": true, + "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" } ], "columns": [ - "Project", - "Index", - "Title", - "Updated", - "Active" + "kind", + "game_id", + "date", + "white", + "white_rating", + "black", + "black_rating", + "result", + "winner_color", + "termination", + "eco", + "time_control", + "rated", + "ply_count", + "url" ], "type": "js", - "modulePath": "plugins/codex/projects.js", - "sourceFile": "plugins/codex/projects.js", - "navigateBefore": true + "modulePath": "plugins/chess/game.js", + "sourceFile": "plugins/chess/game.js" }, { - "site": "codex", - "name": "read", - "description": "Read the contents of the current or selected Codex conversation thread", + "site": "chess", + "name": "games", + "description": "Chess.com recent games for a player, newest first", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "domain": "api.chess.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Chess.com username" }, { - "name": "thread-id", - "type": "str", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Exact Codex thread id to select" + "help": "Number of recent games (1-100)" } ], "columns": [ - "Project", - "Conversation", - "Content" + "date", + "time_class", + "rated", + "my_color", + "my_rating", + "my_result", + "opponent", + "opponent_rating", + "accuracy_white", + "accuracy_black", + "eco", + "opening_name", + "url" ], "type": "js", - "modulePath": "plugins/codex/read.js", - "sourceFile": "plugins/codex/read.js", - "navigateBefore": true + "modulePath": "plugins/chess/games.js", + "sourceFile": "plugins/chess/games.js" }, { - "site": "codex", - "name": "rename", - "description": "Rename the selected Codex conversation. Opens the Chat actions menu → \"Rename chat\", then types the new title.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "site": "chess", + "name": "stats", + "description": "Chess.com player ratings + win/loss record across game kinds", + "access": "read", + "domain": "api.chess.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "title", - "type": "str", + "name": "username", + "type": "string", "required": true, "positional": true, - "help": "New title (single line, no newlines)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" + "help": "Chess.com username (case-insensitive)" } ], "columns": [ - "status", - "title", - "thread_id", - "project" + "kind", + "rating_current", + "rating_best", + "wins", + "losses", + "draws" ], "type": "js", - "modulePath": "plugins/codex/rename.js", - "sourceFile": "plugins/codex/rename.js", - "navigateBefore": true + "modulePath": "plugins/chess/stats.js", + "sourceFile": "plugins/chess/stats.js" }, { - "site": "codex", - "name": "screenshot", - "description": "Capture a snapshot of the current Codex window (DOM + Accessibility tree)", + "site": "cincinnati", + "name": "export-postgraduate-courses", + "description": "Export University of Cincinnati graduate and professional programs from official public sources.", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "output", - "type": "str", + "example": "webcmd cincinnati export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.grad.uc.edu", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", "required": false, - "help": "Output file path (default: /tmp/codex-snapshot.txt)" + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" } ], "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", "Status", - "File" + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/codex/screenshot.js", - "sourceFile": "plugins/codex/screenshot.js", - "navigateBefore": true + "modulePath": "plugins/cincinnati/export-postgraduate-courses.js", + "sourceFile": "plugins/cincinnati/export-postgraduate-courses.js" }, { - "site": "codex", - "name": "send", - "description": "Send text/commands to the current or selected Codex AI composer", + "site": "claude", + "name": "ask", + "description": "Send a prompt to Claude and get the response", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "claude.ai", + "strategy": "cookie", "browser": true, "args": [ { - "name": "text", + "name": "prompt", "type": "str", "required": true, "positional": true, - "help": "Text, command (e.g. /review), or skill (e.g. $imagegen)" + "help": "Prompt to send" }, { - "name": "project", - "type": "str", + "name": "timeout", + "type": "int", + "default": 120, "required": false, - "help": "Project label or path to select before running the command" + "help": "Max seconds to wait for response" }, { - "name": "conversation", - "type": "str", + "name": "new", + "type": "boolean", + "default": false, "required": false, - "help": "Conversation title to select within --project" + "help": "Start a new chat before sending" }, { - "name": "index", + "name": "model", "type": "str", + "default": "sonnet", "required": false, - "help": "1-based conversation index within --project" + "help": "Model to use: sonnet, opus, or haiku", + "choices": [ + "sonnet", + "opus", + "haiku" + ] }, { - "name": "thread-id", + "name": "think", + "type": "boolean", + "default": false, + "required": false, + "help": "Enable Adaptive thinking" + }, + { + "name": "file", "type": "str", "required": false, - "help": "Exact Codex thread id to select" + "help": "Attach a file (image, PDF, text) with the prompt", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "application/pdf", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } } ], "columns": [ - "Status", - "Project", - "Conversation", - "InjectedText" + "response" ], "type": "js", - "modulePath": "plugins/codex/send.js", - "sourceFile": "plugins/codex/send.js", - "navigateBefore": true + "modulePath": "plugins/claude/ask.js", + "sourceFile": "plugins/claude/ask.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "codex", - "name": "status", - "description": "Check active CDP connection to OpenAI Codex App", + "site": "claude", + "name": "detail", + "description": "Open a Claude conversation by ID and read its messages", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "claude.ai", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Conversation ID (UUID from /chat/)" + } + ], "columns": [ - "Status", - "Url", - "Title" + "Index", + "Role", + "Text" ], "type": "js", - "modulePath": "plugins/codex/status.js", - "sourceFile": "plugins/codex/status.js", - "navigateBefore": true + "modulePath": "plugins/claude/detail.js", + "sourceFile": "plugins/claude/detail.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "codex", - "name": "unpin", - "description": "Unpin the selected Codex conversation via the Chat actions header menu.", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "site": "claude", + "name": "history", + "description": "List conversation history from Claude /recents", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", "browser": true, "args": [ { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Exact Codex thread id to select" + "help": "Max conversations to show" } ], + "columns": [ + "Index", + "Id", + "Title", + "Url" + ], + "type": "js", + "modulePath": "plugins/claude/history.js", + "sourceFile": "plugins/claude/history.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "login", + "description": "Open claude login", + "access": "write", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ "status", - "thread_id", - "project", - "conversation" + "logged_in", + "site", + "user_id", + "org_name", + "org_uuid", + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/codex/pin.js", - "sourceFile": "plugins/codex/pin.js", - "navigateBefore": true + "modulePath": "plugins/claude/auth.js", + "sourceFile": "plugins/claude/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "coingecko", - "name": "categories", - "description": "Crypto categories ranked by aggregated market cap", + "site": "claude", + "name": "new", + "description": "Start a new conversation in Claude", "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/claude/new.js", + "sourceFile": "plugins/claude/new.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "read", + "description": "Read the current Claude conversation", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/claude/read.js", + "sourceFile": "plugins/claude/read.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "send", + "description": "Send a prompt to Claude without waiting for the response", + "access": "write", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "sort", + "name": "prompt", "type": "str", - "default": "market_cap_desc", - "required": false, - "help": "Sort order (market_cap_desc / market_cap_asc / name_desc / name_asc / market_cap_change_24h_desc / market_cap_change_24h_asc)" + "required": true, + "positional": true, + "help": "Prompt to send" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "new", + "type": "boolean", + "default": false, "required": false, - "help": "Number of categories (1-100; CoinGecko returns ~120 max)" + "help": "Start a new chat before sending" } ], "columns": [ - "rank", - "id", - "name", - "marketCap", - "volume24h", - "marketCapChange24hPct", - "top3Coins" + "Status", + "SubmittedBy", + "InjectedText" ], "type": "js", - "modulePath": "plugins/coingecko/categories.js", - "sourceFile": "plugins/coingecko/categories.js" + "modulePath": "plugins/claude/send.js", + "sourceFile": "plugins/claude/send.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "coingecko", - "name": "coin", - "description": "Fetch a single cryptocurrency's market data by CoinGecko id (e.g. bitcoin, ethereum).", + "site": "claude", + "name": "status", + "description": "Check Claude page availability and login state", "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "CoinGecko coin id (lowercase, e.g. bitcoin / ethereum / solana)." - }, - { - "name": "currency", - "type": "string", - "default": "usd", - "required": false, - "help": "Quote currency (usd, cny, eur, jpy, ...)." - } - ], + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "id", - "symbol", - "name", - "rank", - "price", - "marketCap", - "volume24h", - "change24hPct", - "change7dPct", - "change30dPct", - "ath", - "athDate", - "atl", - "atlDate", - "circulatingSupply", - "totalSupply", - "maxSupply", - "genesisDate", - "homepage" + "Status", + "Login", + "Url" ], "type": "js", - "modulePath": "plugins/coingecko/coin.js", - "sourceFile": "plugins/coingecko/coin.js" + "modulePath": "plugins/claude/status.js", + "sourceFile": "plugins/claude/status.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "coingecko", - "name": "derivatives", - "description": "Top crypto derivative (perpetual / futures) markets by 24h volume", + "site": "claude", + "name": "whoami", + "description": "Show the current logged-in claude account", "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "org_name", + "org_uuid" + ], + "type": "js", + "modulePath": "plugins/claude/auth.js", + "sourceFile": "plugins/claude/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "codex", + "name": "archive", + "description": "Archive (Codex's term for delete) the selected conversation via the Chat actions header menu. No confirmation in UI — pass --yes to actually archive.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "yes", + "type": "boolean", + "default": false, "required": false, - "help": "Max rows to return (1-500; CoinGecko returns one large page)." + "help": "Actually archive (default: dry-run preview)" }, { - "name": "symbol", - "type": "string", + "name": "project", + "type": "str", "required": false, - "help": "Optional symbol substring filter (e.g. \"BTC\", \"ETHUSDT\")." + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "rank", - "market", - "symbol", - "indexId", - "contractType", - "price", - "change24hPct", - "fundingRate", - "openInterestUsd", - "volume24hUsd", - "expired" + "status", + "thread_id", + "project", + "conversation" ], "type": "js", - "modulePath": "plugins/coingecko/derivatives.js", - "sourceFile": "plugins/coingecko/derivatives.js" + "modulePath": "plugins/codex/archive.js", + "sourceFile": "plugins/codex/archive.js", + "navigateBefore": true }, { - "site": "coingecko", - "name": "exchanges", - "description": "Top crypto exchanges by 24h BTC trading volume", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, + "site": "codex", + "name": "ask", + "description": "Send a prompt to the current or selected Codex conversation and wait for the AI response", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", "type": "int", - "default": 20, + "default": 60, "required": false, - "help": "Number of exchanges (1-250, CoinGecko per_page upper bound)" + "help": "Max seconds to wait for response (default: 60)" }, { - "name": "page", - "type": "int", - "default": 1, + "name": "project", + "type": "str", "required": false, - "help": "Page number (1-based)" + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "rank", - "id", - "name", - "trustScore", - "volume24hBtc", - "country", - "yearEstablished", - "url" + "Role", + "Project", + "Conversation", + "Text" ], "type": "js", - "modulePath": "plugins/coingecko/exchanges.js", - "sourceFile": "plugins/coingecko/exchanges.js" + "modulePath": "plugins/codex/ask.js", + "sourceFile": "plugins/codex/ask.js", + "navigateBefore": true }, { - "site": "coingecko", - "name": "global", - "description": "Aggregate crypto market stats: total market cap, volume, dominance", + "site": "codex", + "name": "dump", + "description": "Dump the DOM and Accessibility tree of codex for reverse-engineering", "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "currency", - "type": "string", - "default": "usd", - "required": false, - "help": "Quote currency for total market cap / volume (usd, cny, eur, jpy, ...)" - } - ], + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "currency", - "totalMarketCap", - "totalVolume24h", - "marketCapChange24hPct", - "btcDominancePct", - "ethDominancePct", - "activeCryptocurrencies", - "markets", - "ongoingIcos", - "updatedAt" + "action", + "files" ], "type": "js", - "modulePath": "plugins/coingecko/global.js", - "sourceFile": "plugins/coingecko/global.js" + "modulePath": "plugins/codex/dump.js", + "sourceFile": "plugins/codex/dump.js", + "navigateBefore": true }, { - "site": "coingecko", - "name": "top", - "description": "Cryptocurrency quotes by market cap (default USD)", + "site": "codex", + "name": "export", + "description": "Export the current Codex conversation to a Markdown file", "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "currency", - "type": "string", - "default": "usd", - "required": false, - "help": "quote currency (usd / cny / eur / jpy ...)" - }, + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ { - "name": "limit", - "type": "int", - "default": 10, + "name": "output", + "type": "str", "required": false, - "help": "Number to return (default 10, maximum 250)" + "help": "Output file (default: /tmp/codex-export.md)" } ], "columns": [ - "rank", - "symbol", - "name", - "price", - "change24hPct", - "marketCap", - "volume24h", - "high24h", - "low24h" + "Status", + "File", + "Messages" ], "type": "js", - "modulePath": "plugins/coingecko/top.js", - "sourceFile": "plugins/coingecko/top.js" + "modulePath": "plugins/codex/export.js", + "sourceFile": "plugins/codex/export.js", + "navigateBefore": true }, { - "site": "coingecko", - "name": "trending", - "description": "Top trending cryptocurrencies on CoinGecko in the last 24h (search-volume based).", + "site": "codex", + "name": "extract-diff", + "description": "Extract visual code review diff patches from Codex", "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [], "columns": [ - "rank", - "id", - "symbol", - "name", - "marketCapRank", - "priceBtc", - "thumb" + "File", + "Diff" ], "type": "js", - "modulePath": "plugins/coingecko/trending.js", - "sourceFile": "plugins/coingecko/trending.js" + "modulePath": "plugins/codex/extract-diff.js", + "sourceFile": "plugins/codex/extract-diff.js", + "navigateBefore": true }, { - "site": "concordia", - "name": "export-postgraduate-courses", - "description": "Export Concordia University Montreal postgraduate programs using official public sources.", + "site": "codex", + "name": "history", + "description": "List visible Codex conversation threads grouped by project", "access": "read", - "example": "webcmd concordia export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.concordia.ca", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", + "name": "project", + "type": "str", "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "help": "Filter by project label or path" }, { - "name": "count", - "type": "int", + "name": "limit", + "type": "str", "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Max conversations per project" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "Project", + "Index", + "Title", + "Updated", + "Active" ], "type": "js", - "modulePath": "plugins/concordia/export-postgraduate-courses.js", - "sourceFile": "plugins/concordia/export-postgraduate-courses.js" + "modulePath": "plugins/codex/history.js", + "sourceFile": "plugins/codex/history.js", + "navigateBefore": true }, { - "site": "coupang", - "name": "add-to-cart", - "description": "Add a Coupang product to cart using logged-in browser session", + "site": "codex", + "name": "model", + "description": "Read, list, or switch the active model / reasoning level in Codex Desktop. The composer toolbar button toggles a menu that mixes model variants (GPT-5.5, Speed) with reasoning levels (Low/Medium/High/Extra High).", "access": "write", - "domain": "www.coupang.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "product-id", + "name": "name", "type": "str", "required": false, "positional": true, - "help": "Coupang product ID" + "help": "Substring (case-insensitive) of a model / reasoning level to switch to. Omit to read current." }, { - "name": "url", - "type": "str", + "name": "list", + "type": "boolean", + "default": false, "required": false, - "help": "Canonical product URL" + "help": "List all menu options (does not switch)" } ], "columns": [ - "ok", - "product_id", - "url", - "message" + "Status", + "Model" ], "type": "js", - "modulePath": "plugins/coupang/add-to-cart.js", - "sourceFile": "plugins/coupang/add-to-cart.js", - "navigateBefore": "https://www.coupang.com" + "modulePath": "plugins/codex/model.js", + "sourceFile": "plugins/codex/model.js", + "navigateBefore": true }, { - "site": "coupang", - "name": "login", - "description": "Open coupang login", + "site": "codex", + "name": "new", + "description": "Start a new Codex conversation session", "access": "write", - "domain": "coupang.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [], "columns": [ - "status", - "logged_in", - "site", - "name", - "action", - "verify_command" + "Status" ], "type": "js", - "modulePath": "plugins/coupang/auth.js", - "sourceFile": "plugins/coupang/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/codex/new.js", + "sourceFile": "plugins/codex/new.js", + "navigateBefore": true }, { - "site": "coupang", - "name": "product", - "description": "Read full product detail (price, rating, seller, delivery) for a Coupang product", - "access": "read", - "domain": "www.coupang.com", - "strategy": "cookie", + "site": "codex", + "name": "pin", + "description": "Pin the selected Codex conversation via the Chat actions header menu.", + "access": "write", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "product-id", + "name": "project", "type": "str", "required": false, - "positional": true, - "help": "Coupang product ID (digits only)" + "help": "Project label or path to select before running the command" }, { - "name": "url", + "name": "conversation", "type": "str", "required": false, - "help": "Canonical Coupang product URL (alternative to --product-id)" + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "product_id", - "title", - "price", - "original_price", - "discount_rate", - "rating", - "review_count", - "seller", - "brand", - "rocket", - "delivery_promise", - "image_url", - "url" + "status", + "thread_id", + "project", + "conversation" ], "type": "js", - "modulePath": "plugins/coupang/product.js", - "sourceFile": "plugins/coupang/product.js", - "navigateBefore": "https://www.coupang.com" + "modulePath": "plugins/codex/pin.js", + "sourceFile": "plugins/codex/pin.js", + "navigateBefore": true }, { - "site": "coupang", - "name": "search", - "description": "Search Coupang products with logged-in browser session", + "site": "codex", + "name": "projects", + "description": "List Codex projects and visible conversations from the sidebar", "access": "read", - "domain": "www.coupang.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "query", + "name": "project", "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "page", - "type": "int", - "default": 1, "required": false, - "help": "Search result page number" + "help": "Filter by project label or path" }, { "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (max 50)" - }, - { - "name": "filter", "type": "str", "required": false, - "help": "Optional search filter (currently supports: rocket)" + "help": "Max conversations per project" } ], "columns": [ - "rank", - "product_id", - "title", - "price", - "unit_price", - "rating", - "review_count", - "rocket", - "delivery_type", - "delivery_promise", - "url" - ], - "tags": [ - "search" + "Project", + "Index", + "Title", + "Updated", + "Active" ], "type": "js", - "modulePath": "plugins/coupang/search.js", - "sourceFile": "plugins/coupang/search.js", - "navigateBefore": "https://www.coupang.com" + "modulePath": "plugins/codex/projects.js", + "sourceFile": "plugins/codex/projects.js", + "navigateBefore": true }, { - "site": "coupang", - "name": "whoami", - "description": "Show the current logged-in coupang account", + "site": "codex", + "name": "read", + "description": "Read the contents of the current or selected Codex conversation thread", "access": "read", - "domain": "coupang.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name" - ], - "type": "js", - "modulePath": "plugins/coupang/auth.js", - "sourceFile": "plugins/coupang/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "crates", - "name": "crate", - "description": "Single crates.io crate metadata (latest version, downloads, license, repo)", - "access": "read", - "domain": "crates.io", - "strategy": "public", - "browser": false, "args": [ { - "name": "name", + "name": "project", "type": "str", - "required": true, - "positional": true, - "help": "crates.io crate name (e.g. \"serde\", \"tokio\")" - } - ], - "columns": [ - "name", - "latestVersion", - "description", - "downloads", - "recentDownloads", - "versions", - "license", - "homepage", - "documentation", - "repository", - "keywords", - "categories", - "created", - "updated", - "url" - ], - "type": "js", - "modulePath": "plugins/crates/crate.js", - "sourceFile": "plugins/crates/crate.js" - }, - { - "site": "crates", - "name": "search", - "description": "Search the public crates.io registry by keyword", - "access": "read", - "domain": "crates.io", - "strategy": "public", - "browser": false, - "args": [ + "required": false, + "help": "Project label or path to select before running the command" + }, { - "name": "query", + "name": "conversation", "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"serde\", \"async runtime\")" + "required": false, + "help": "Conversation title to select within --project" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "index", + "type": "str", "required": false, - "help": "Max results (1-100)" + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "rank", - "name", - "latestVersion", - "description", - "downloads", - "recentDownloads", - "repository", - "updated", - "url" - ], - "tags": [ - "search" + "Project", + "Conversation", + "Content" ], "type": "js", - "modulePath": "plugins/crates/search.js", - "sourceFile": "plugins/crates/search.js" + "modulePath": "plugins/codex/read.js", + "sourceFile": "plugins/codex/read.js", + "navigateBefore": true }, { - "site": "cursor", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", + "site": "codex", + "name": "rename", + "description": "Rename the selected Codex conversation. Opens the Chat actions menu → \"Rename chat\", then types the new title.", "access": "write", "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "text", + "name": "title", "type": "str", "required": true, "positional": true, - "help": "Prompt to send" + "help": "New title (single line, no newlines)" }, { - "name": "timeout", - "type": "int", - "default": 30, + "name": "project", + "type": "str", "required": false, - "help": "Max seconds to wait for response (default: 30)" + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "Role", - "Text" + "status", + "title", + "thread_id", + "project" ], "type": "js", - "modulePath": "plugins/cursor/ask.js", - "sourceFile": "plugins/cursor/ask.js", + "modulePath": "plugins/codex/rename.js", + "sourceFile": "plugins/codex/rename.js", "navigateBefore": true }, { - "site": "cursor", - "name": "composer", - "description": "Send a prompt directly into Cursor Composer (Cmd+I shortcut)", - "access": "write", + "site": "codex", + "name": "screenshot", + "description": "Capture a snapshot of the current Codex window (DOM + Accessibility tree)", + "access": "read", "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "text", + "name": "output", "type": "str", - "required": true, - "positional": true, - "help": "Text to send into Composer" + "required": false, + "help": "Output file path (default: /tmp/codex-snapshot.txt)" } ], "columns": [ "Status", - "InjectedText" + "File" ], "type": "js", - "modulePath": "plugins/cursor/composer.js", - "sourceFile": "plugins/cursor/composer.js", + "modulePath": "plugins/codex/screenshot.js", + "sourceFile": "plugins/codex/screenshot.js", "navigateBefore": true }, { - "site": "cursor", - "name": "dump", - "description": "Dump the DOM and Accessibility tree of cursor for reverse-engineering", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "action", - "files" - ], - "type": "js", - "modulePath": "plugins/cursor/dump.js", - "sourceFile": "plugins/cursor/dump.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "export", - "description": "Export the current cursor conversation to a Markdown file", - "access": "read", + "site": "codex", + "name": "send", + "description": "Send text/commands to the current or selected Codex AI composer", + "access": "write", "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "output", + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Text, command (e.g. /review), or skill (e.g. $imagegen)" + }, + { + "name": "project", "type": "str", "required": false, - "help": "Output file (default: /tmp/cursor-export.md)" + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ "Status", - "File", - "Messages" - ], - "type": "js", - "modulePath": "plugins/cursor/export.js", - "sourceFile": "plugins/cursor/export.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "extract-code", - "description": "Extract multi-line code blocks from the current Cursor conversation", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Code" + "Project", + "Conversation", + "InjectedText" ], "type": "js", - "modulePath": "plugins/cursor/extract-code.js", - "sourceFile": "plugins/cursor/extract-code.js", + "modulePath": "plugins/codex/send.js", + "sourceFile": "plugins/codex/send.js", "navigateBefore": true }, { - "site": "cursor", - "name": "history", - "description": "List recent chat sessions from the Cursor sidebar", + "site": "codex", + "name": "status", + "description": "Check active CDP connection to OpenAI Codex App", "access": "read", "domain": "localhost", "strategy": "ui", "browser": true, "args": [], "columns": [ - "Index", + "Status", + "Url", "Title" ], "type": "js", - "modulePath": "plugins/cursor/history.js", - "sourceFile": "plugins/cursor/history.js", + "modulePath": "plugins/codex/status.js", + "sourceFile": "plugins/codex/status.js", "navigateBefore": true }, { - "site": "cursor", - "name": "model", - "description": "Get or switch the currently active AI model in Cursor", - "access": "read", + "site": "codex", + "name": "unpin", + "description": "Unpin the selected Codex conversation via the Chat actions header menu.", + "access": "write", "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "model-name", + "name": "project", "type": "str", "required": false, - "positional": true, - "help": "The ID of the model to switch to (e.g. claude-3.5-sonnet)" - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "plugins/cursor/model.js", - "sourceFile": "plugins/cursor/model.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "new", - "description": "Start a new Cursor chat or Composer session", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/cursor/new.js", - "sourceFile": "plugins/cursor/new.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "read", - "description": "Read the current Cursor chat/composer conversation history", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "plugins/cursor/read.js", - "sourceFile": "plugins/cursor/read.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "screenshot", - "description": "Capture a snapshot of the current cursor window (DOM + Accessibility tree)", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Project label or path to select before running the command" + }, { - "name": "output", + "name": "conversation", "type": "str", "required": false, - "help": "Output file path (default: /tmp/cursor-snapshot.txt)" - } - ], - "columns": [ - "Status", - "File" - ], - "type": "js", - "modulePath": "plugins/cursor/screenshot.js", - "sourceFile": "plugins/cursor/screenshot.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "send", - "description": "Send a prompt directly into Cursor Composer/Chat", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Conversation title to select within --project" + }, { - "name": "text", + "name": "index", "type": "str", - "required": true, - "positional": true, - "help": "Text to send into Cursor" + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" } ], "columns": [ - "Status", - "InjectedText" - ], - "type": "js", - "modulePath": "plugins/cursor/send.js", - "sourceFile": "plugins/cursor/send.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "status", - "description": "Check active CDP connection to Cursor AI Editor", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" + "status", + "thread_id", + "project", + "conversation" ], "type": "js", - "modulePath": "plugins/cursor/status.js", - "sourceFile": "plugins/cursor/status.js", + "modulePath": "plugins/codex/pin.js", + "sourceFile": "plugins/codex/pin.js", "navigateBefore": true }, { - "site": "dblp", - "name": "author", - "description": "List dblp publications by a given author (newest first; resolves to top PID match)", + "site": "coingecko", + "name": "categories", + "description": "Crypto categories ranked by aggregated market cap", "access": "read", - "domain": "dblp.org", + "domain": "api.coingecko.com", "strategy": "public", "browser": false, "args": [ { - "name": "author", - "type": "str", - "required": false, - "positional": true, - "help": "Author name (e.g. \"Yoshua Bengio\"). Optional when --pid is given." - }, - { - "name": "pid", + "name": "sort", "type": "str", + "default": "market_cap_desc", "required": false, - "help": "Canonical dblp PID (e.g. \"56/953\"). Bypasses author search." + "help": "Sort order (market_cap_desc / market_cap_asc / name_desc / name_asc / market_cap_change_24h_desc / market_cap_change_24h_asc)" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max publications (1-200)" + "help": "Number of categories (1-100; CoinGecko returns ~120 max)" } ], "columns": [ "rank", - "key", - "title", - "authors", - "venue", - "year", - "type", - "doi", - "pid", - "url" + "id", + "name", + "marketCap", + "volume24h", + "marketCapChange24hPct", + "top3Coins" ], "type": "js", - "modulePath": "plugins/dblp/author.js", - "sourceFile": "plugins/dblp/author.js" + "modulePath": "plugins/coingecko/categories.js", + "sourceFile": "plugins/coingecko/categories.js" }, { - "site": "dblp", - "name": "paper", - "aliases": [ - "detail", - "view" - ], - "description": "Fetch a dblp record by canonical key (e.g. conf/nips/VaswaniSPUJGKP17)", + "site": "coingecko", + "name": "coin", + "description": "Fetch a single cryptocurrency's market data by CoinGecko id (e.g. bitcoin, ethereum).", "access": "read", - "domain": "dblp.org", + "domain": "api.coingecko.com", "strategy": "public", "browser": false, "args": [ { - "name": "key", - "type": "str", + "name": "id", + "type": "string", "required": true, "positional": true, - "help": "dblp record key (round-tripped from the `key` column of `dblp search`)" + "help": "CoinGecko coin id (lowercase, e.g. bitcoin / ethereum / solana)." + }, + { + "name": "currency", + "type": "string", + "default": "usd", + "required": false, + "help": "Quote currency (usd, cny, eur, jpy, ...)." } ], "columns": [ - "key", - "type", - "title", - "authors", - "venue", - "year", - "pages", - "doi", - "open_access_url", - "dblp_url" + "id", + "symbol", + "name", + "rank", + "price", + "marketCap", + "volume24h", + "change24hPct", + "change7dPct", + "change30dPct", + "ath", + "athDate", + "atl", + "atlDate", + "circulatingSupply", + "totalSupply", + "maxSupply", + "genesisDate", + "homepage" ], "type": "js", - "modulePath": "plugins/dblp/paper.js", - "sourceFile": "plugins/dblp/paper.js" + "modulePath": "plugins/coingecko/coin.js", + "sourceFile": "plugins/coingecko/coin.js" }, { - "site": "dblp", - "name": "search", - "description": "Search dblp computer-science bibliography by free-text query", + "site": "coingecko", + "name": "derivatives", + "description": "Top crypto derivative (perpetual / futures) markets by 24h volume", "access": "read", - "domain": "dblp.org", + "domain": "api.coingecko.com", "strategy": "public", "browser": false, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (title / author / venue, e.g. \"attention is all you need\")" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max results (1-100, single dblp page)" + "help": "Max rows to return (1-500; CoinGecko returns one large page)." + }, + { + "name": "symbol", + "type": "string", + "required": false, + "help": "Optional symbol substring filter (e.g. \"BTC\", \"ETHUSDT\")." } ], "columns": [ "rank", - "key", - "title", - "authors", - "venue", - "year", - "type", - "doi", - "url" - ], - "tags": [ - "search" + "market", + "symbol", + "indexId", + "contractType", + "price", + "change24hPct", + "fundingRate", + "openInterestUsd", + "volume24hUsd", + "expired" ], "type": "js", - "modulePath": "plugins/dblp/search.js", - "sourceFile": "plugins/dblp/search.js" + "modulePath": "plugins/coingecko/derivatives.js", + "sourceFile": "plugins/coingecko/derivatives.js" }, { - "site": "dblp", - "name": "venue", - "description": "Search dblp venue registry (conferences / journals) by name or acronym", + "site": "coingecko", + "name": "exchanges", + "description": "Top crypto exchanges by 24h BTC trading volume", "access": "read", - "domain": "dblp.org", + "domain": "api.coingecko.com", "strategy": "public", "browser": false, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Venue name or acronym (e.g. \"ICLR\", \"neural networks\")" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max venues (1-100, single dblp page)" + "help": "Number of exchanges (1-250, CoinGecko per_page upper bound)" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number (1-based)" } ], "columns": [ "rank", - "acronym", - "venue", - "type", + "id", + "name", + "trustScore", + "volume24hBtc", + "country", + "yearEstablished", "url" ], "type": "js", - "modulePath": "plugins/dblp/venue.js", - "sourceFile": "plugins/dblp/venue.js" + "modulePath": "plugins/coingecko/exchanges.js", + "sourceFile": "plugins/coingecko/exchanges.js" }, { - "site": "defillama", - "name": "protocol", - "description": "Single DefiLlama protocol details (current TVL, mcap, chains, twitter, github, description)", + "site": "coingecko", + "name": "global", + "description": "Aggregate crypto market stats: total market cap, volume, dominance", "access": "read", - "domain": "defillama.com", + "domain": "api.coingecko.com", "strategy": "public", "browser": false, "args": [ { - "name": "slug", + "name": "currency", "type": "string", - "required": true, - "positional": true, - "help": "DefiLlama protocol slug (e.g. \"aave\", \"lido\")" + "default": "usd", + "required": false, + "help": "Quote currency for total market cap / volume (usd, cny, eur, jpy, ...)" } ], "columns": [ - "slug", - "name", - "category", - "isParent", - "tvl", - "tvlAt", - "mcap", - "chains", - "twitter", - "github", - "audits", - "listedAt", - "description", - "website", - "url" + "currency", + "totalMarketCap", + "totalVolume24h", + "marketCapChange24hPct", + "btcDominancePct", + "ethDominancePct", + "activeCryptocurrencies", + "markets", + "ongoingIcos", + "updatedAt" ], "type": "js", - "modulePath": "plugins/defillama/protocol.js", - "sourceFile": "plugins/defillama/protocol.js" + "modulePath": "plugins/coingecko/global.js", + "sourceFile": "plugins/coingecko/global.js" }, { - "site": "defillama", - "name": "protocols", - "description": "Top DeFi protocols on DefiLlama by current TVL (slug, name, category, TVL, mcap, change_1d/7d, chains)", + "site": "coingecko", + "name": "top", + "description": "Cryptocurrency quotes by market cap (default USD)", "access": "read", - "domain": "defillama.com", + "domain": "api.coingecko.com", "strategy": "public", "browser": false, "args": [ + { + "name": "currency", + "type": "string", + "default": "usd", + "required": false, + "help": "quote currency (usd / cny / eur / jpy ...)" + }, { "name": "limit", "type": "int", - "default": 30, + "default": 10, "required": false, - "help": "Number of rows to return (1-500)" + "help": "Number to return (default 10, maximum 250)" } ], "columns": [ "rank", - "slug", + "symbol", "name", - "category", - "tvl", - "mcap", - "change_1d", - "change_7d", - "chains", - "listedAt", - "url" + "price", + "change24hPct", + "marketCap", + "volume24h", + "high24h", + "low24h" ], "type": "js", - "modulePath": "plugins/defillama/protocols.js", - "sourceFile": "plugins/defillama/protocols.js" + "modulePath": "plugins/coingecko/top.js", + "sourceFile": "plugins/coingecko/top.js" }, { - "site": "devto", - "name": "latest", - "description": "Newest dev.to articles (firehose, all tags)", + "site": "coingecko", + "name": "trending", + "description": "Top trending cryptocurrencies on CoinGecko in the last 24h (search-volume based).", "access": "read", - "domain": "dev.to", + "domain": "api.coingecko.com", "strategy": "public", "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Articles per page (1-100)" - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number (1-based)" - } - ], + "args": [], "columns": [ "rank", "id", - "title", - "author", - "tags", - "reactions", - "comments", - "published", - "url" + "symbol", + "name", + "marketCapRank", + "priceBtc", + "thumb" ], "type": "js", - "modulePath": "plugins/devto/latest.js", - "sourceFile": "plugins/devto/latest.js" + "modulePath": "plugins/coingecko/trending.js", + "sourceFile": "plugins/coingecko/trending.js" }, { - "site": "devto", - "name": "read", - "description": "Read a DEV.to article body by id", + "site": "concordia", + "name": "export-postgraduate-courses", + "description": "Export Concordia University Montreal postgraduate programs using official public sources.", "access": "read", - "domain": "dev.to", + "example": "webcmd concordia export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.concordia.ca", "strategy": "public", "browser": false, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "DEV.to article id (numeric, e.g. 3605688)" + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" }, { - "name": "max-length", + "name": "count", "type": "int", - "default": 20000, "required": false, - "help": "Max characters of body to return (min 100)" + "help": "Positive maximum number of programs after filtering and deduplication" } ], "columns": [ - "id", - "title", - "author", - "reactions", - "reading_time", - "tags", - "published_at", - "body", - "url" + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/devto/read.js", - "sourceFile": "plugins/devto/read.js" + "modulePath": "plugins/concordia/export-postgraduate-courses.js", + "sourceFile": "plugins/concordia/export-postgraduate-courses.js" }, { - "site": "devto", - "name": "tag", - "description": "Latest DEV.to articles for a specific tag", - "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, + "site": "coupang", + "name": "add-to-cart", + "description": "Add a Coupang product to cart using logged-in browser session", + "access": "write", + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "tag", + "name": "product-id", "type": "str", - "required": true, + "required": false, "positional": true, - "help": "Tag name (e.g. javascript, python, webdev)" + "help": "Coupang product ID" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "url", + "type": "str", "required": false, - "help": "Number of articles" + "help": "Canonical product URL" } ], "columns": [ - "rank", - "id", - "title", - "author", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", - "url" + "ok", + "product_id", + "url", + "message" ], "type": "js", - "modulePath": "plugins/devto/tag.js", - "sourceFile": "plugins/devto/tag.js" + "modulePath": "plugins/coupang/add-to-cart.js", + "sourceFile": "plugins/coupang/add-to-cart.js", + "navigateBefore": "https://www.coupang.com" }, { - "site": "devto", - "name": "top", - "description": "Top DEV.to articles of the day", + "site": "coupang", + "name": "login", + "description": "Open coupang login", + "access": "write", + "domain": "coupang.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/coupang/auth.js", + "sourceFile": "plugins/coupang/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "coupang", + "name": "product", + "description": "Read full product detail (price, rating, seller, delivery) for a Coupang product", "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "product-id", + "type": "str", "required": false, - "help": "Number of articles" + "positional": true, + "help": "Coupang product ID (digits only)" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Canonical Coupang product URL (alternative to --product-id)" } ], "columns": [ - "rank", - "id", + "product_id", "title", - "author", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", + "price", + "original_price", + "discount_rate", + "rating", + "review_count", + "seller", + "brand", + "rocket", + "delivery_promise", + "image_url", "url" ], "type": "js", - "modulePath": "plugins/devto/top.js", - "sourceFile": "plugins/devto/top.js" + "modulePath": "plugins/coupang/product.js", + "sourceFile": "plugins/coupang/product.js", + "navigateBefore": "https://www.coupang.com" }, { - "site": "devto", - "name": "user", - "description": "Recent DEV.to articles from a specific user", + "site": "coupang", + "name": "search", + "description": "Search Coupang products with logged-in browser session", "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "username", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "DEV.to username (e.g. ben, thepracticaldev)" + "help": "Search keyword" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Search result page number" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of articles" + "help": "Max results (max 50)" + }, + { + "name": "filter", + "type": "str", + "required": false, + "help": "Optional search filter (currently supports: rocket)" } ], "columns": [ "rank", - "id", + "product_id", "title", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", - "url" + "price", + "unit_price", + "rating", + "review_count", + "rocket", + "delivery_type", + "delivery_promise", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/devto/user.js", - "sourceFile": "plugins/devto/user.js" + "modulePath": "plugins/coupang/search.js", + "sourceFile": "plugins/coupang/search.js", + "navigateBefore": "https://www.coupang.com" }, { - "site": "dictionary", - "name": "examples", - "description": "Read real-world example sentences utilizing the word", + "site": "coupang", + "name": "whoami", + "description": "Show the current logged-in coupang account", "access": "read", - "domain": "api.dictionaryapi.dev", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "word", - "type": "string", - "required": true, - "positional": true, - "help": "Word to get example sentences for" - } - ], + "domain": "coupang.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "word", - "example" + "logged_in", + "site", + "name" ], "type": "js", - "modulePath": "plugins/dictionary/examples.js", - "sourceFile": "plugins/dictionary/examples.js" + "modulePath": "plugins/coupang/auth.js", + "sourceFile": "plugins/coupang/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "dictionary", - "name": "search", - "description": "Search the Free Dictionary API for definitions, parts of speech, and pronunciations.", + "site": "crates", + "name": "crate", + "description": "Single crates.io crate metadata (latest version, downloads, license, repo)", "access": "read", - "domain": "api.dictionaryapi.dev", + "domain": "crates.io", "strategy": "public", "browser": false, "args": [ { - "name": "word", - "type": "string", + "name": "name", + "type": "str", "required": true, "positional": true, - "help": "Word to define (e.g., serendipity)" + "help": "crates.io crate name (e.g. \"serde\", \"tokio\")" } ], "columns": [ - "word", - "phonetic", - "type", - "definition" - ], - "tags": [ - "search" + "name", + "latestVersion", + "description", + "downloads", + "recentDownloads", + "versions", + "license", + "homepage", + "documentation", + "repository", + "keywords", + "categories", + "created", + "updated", + "url" ], "type": "js", - "modulePath": "plugins/dictionary/search.js", - "sourceFile": "plugins/dictionary/search.js" + "modulePath": "plugins/crates/crate.js", + "sourceFile": "plugins/crates/crate.js" }, { - "site": "dictionary", - "name": "synonyms", - "description": "Find synonyms for a specific word", + "site": "crates", + "name": "search", + "description": "Search the public crates.io registry by keyword", "access": "read", - "domain": "api.dictionaryapi.dev", + "domain": "crates.io", "strategy": "public", "browser": false, "args": [ { - "name": "word", - "type": "string", + "name": "query", + "type": "str", "required": true, "positional": true, - "help": "Word to find synonyms for (e.g., serendipity)" + "help": "Search keyword (e.g. \"serde\", \"async runtime\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" } ], "columns": [ - "word", - "synonyms" + "rank", + "name", + "latestVersion", + "description", + "downloads", + "recentDownloads", + "repository", + "updated", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/dictionary/synonyms.js", - "sourceFile": "plugins/dictionary/synonyms.js" + "modulePath": "plugins/crates/search.js", + "sourceFile": "plugins/crates/search.js" }, { - "site": "district", - "name": "checkout", - "description": "Select District movie seats and open the UPI QR payment scanner", + "site": "cursor", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", "access": "write", - "domain": "www.district.in", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "show", + "name": "text", "type": "str", "required": true, "positional": true, - "help": "District seat-layout URL or showId from district showtimes" - }, - { - "name": "seats", - "type": "str", - "required": true, - "help": "Comma-separated seat labels to select, e.g. I22,I21" - }, - { - "name": "format-id", - "type": "str", - "required": false, - "help": "District formatId from showtimes; required when show is a showId" - }, - { - "name": "content-id", - "type": "str", - "required": false, - "help": "District content id; required when show is a showId" + "help": "Prompt to send" }, { "name": "timeout", "type": "int", - "default": 45, - "required": false, - "help": "Maximum seconds to wait for selection, review page, and payment handoff" - }, - { - "name": "payment", - "type": "str", - "default": "upi-qr", + "default": 30, "required": false, - "help": "Payment handoff target: upi-qr opens the scanner; review stops on order review" + "help": "Max seconds to wait for response (default: 30)" } ], "columns": [ - "status", - "movie", - "cinema", - "date", - "time", - "seats", - "ticketCount", - "orderAmount", - "bookingCharge", - "total", - "paymentMethod", - "paymentState", - "upiQrVisible", - "paymentAmount", - "paymentUrl", - "showId" + "Role", + "Text" ], "type": "js", - "modulePath": "plugins/district/checkout.js", - "sourceFile": "plugins/district/checkout.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "modulePath": "plugins/cursor/ask.js", + "sourceFile": "plugins/cursor/ask.js", + "navigateBefore": true }, { - "site": "district", - "name": "listings", - "aliases": [ - "ls" - ], - "description": "List public District by Zomato movies, events, and nearby going-out cards", - "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, + "site": "cursor", + "name": "composer", + "description": "Send a prompt directly into Cursor Composer (Cmd+I shortcut)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "input", + "name": "text", "type": "str", - "default": "home", - "required": false, + "required": true, "positional": true, - "help": "home, movies, events, a district.in URL, or a District path" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum rows to return (1-100)" + "help": "Text to send into Composer" } ], "columns": [ - "rank", - "title", - "category", - "date", - "venue", - "price", - "url" + "Status", + "InjectedText" ], "type": "js", - "modulePath": "plugins/district/listings.js", - "sourceFile": "plugins/district/listings.js" + "modulePath": "plugins/cursor/composer.js", + "sourceFile": "plugins/cursor/composer.js", + "navigateBefore": true }, { - "site": "district", - "name": "locations", - "aliases": [ - "location-search" - ], - "description": "Search District-supported cities, areas, malls, and places for booking filters", + "site": "cursor", + "name": "dump", + "description": "Dump the DOM and Accessibility tree of cursor for reverse-engineering", "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, - "args": [ - { + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "action", + "files" + ], + "type": "js", + "modulePath": "plugins/cursor/dump.js", + "sourceFile": "plugins/cursor/dump.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "export", + "description": "Export the current cursor conversation to a Markdown file", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file (default: /tmp/cursor-export.md)" + } + ], + "columns": [ + "Status", + "File", + "Messages" + ], + "type": "js", + "modulePath": "plugins/cursor/export.js", + "sourceFile": "plugins/cursor/export.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "extract-code", + "description": "Extract multi-line code blocks from the current Cursor conversation", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Code" + ], + "type": "js", + "modulePath": "plugins/cursor/extract-code.js", + "sourceFile": "plugins/cursor/extract-code.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "history", + "description": "List recent chat sessions from the Cursor sidebar", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Title" + ], + "type": "js", + "modulePath": "plugins/cursor/history.js", + "sourceFile": "plugins/cursor/history.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "model", + "description": "Get or switch the currently active AI model in Cursor", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "model-name", + "type": "str", + "required": false, + "positional": true, + "help": "The ID of the model to switch to (e.g. claude-3.5-sonnet)" + } + ], + "columns": [ + "Status", + "Model" + ], + "type": "js", + "modulePath": "plugins/cursor/model.js", + "sourceFile": "plugins/cursor/model.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "new", + "description": "Start a new Cursor chat or Composer session", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/cursor/new.js", + "sourceFile": "plugins/cursor/new.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "read", + "description": "Read the current Cursor chat/composer conversation history", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/cursor/read.js", + "sourceFile": "plugins/cursor/read.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "screenshot", + "description": "Capture a snapshot of the current cursor window (DOM + Accessibility tree)", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file path (default: /tmp/cursor-snapshot.txt)" + } + ], + "columns": [ + "Status", + "File" + ], + "type": "js", + "modulePath": "plugins/cursor/screenshot.js", + "sourceFile": "plugins/cursor/screenshot.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "send", + "description": "Send a prompt directly into Cursor Composer/Chat", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Text to send into Cursor" + } + ], + "columns": [ + "Status", + "InjectedText" + ], + "type": "js", + "modulePath": "plugins/cursor/send.js", + "sourceFile": "plugins/cursor/send.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "status", + "description": "Check active CDP connection to Cursor AI Editor", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "plugins/cursor/status.js", + "sourceFile": "plugins/cursor/status.js", + "navigateBefore": true + }, + { + "site": "dblp", + "name": "author", + "description": "List dblp publications by a given author (newest first; resolves to top PID match)", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "author", + "type": "str", + "required": false, + "positional": true, + "help": "Author name (e.g. \"Yoshua Bengio\"). Optional when --pid is given." + }, + { + "name": "pid", + "type": "str", + "required": false, + "help": "Canonical dblp PID (e.g. \"56/953\"). Bypasses author search." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max publications (1-200)" + } + ], + "columns": [ + "rank", + "key", + "title", + "authors", + "venue", + "year", + "type", + "doi", + "pid", + "url" + ], + "type": "js", + "modulePath": "plugins/dblp/author.js", + "sourceFile": "plugins/dblp/author.js" + }, + { + "site": "dblp", + "name": "paper", + "aliases": [ + "detail", + "view" + ], + "description": "Fetch a dblp record by canonical key (e.g. conf/nips/VaswaniSPUJGKP17)", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "dblp record key (round-tripped from the `key` column of `dblp search`)" + } + ], + "columns": [ + "key", + "type", + "title", + "authors", + "venue", + "year", + "pages", + "doi", + "open_access_url", + "dblp_url" + ], + "type": "js", + "modulePath": "plugins/dblp/paper.js", + "sourceFile": "plugins/dblp/paper.js" + }, + { + "site": "dblp", + "name": "search", + "description": "Search dblp computer-science bibliography by free-text query", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (title / author / venue, e.g. \"attention is all you need\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100, single dblp page)" + } + ], + "columns": [ + "rank", + "key", + "title", + "authors", + "venue", + "year", + "type", + "doi", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/dblp/search.js", + "sourceFile": "plugins/dblp/search.js" + }, + { + "site": "dblp", + "name": "venue", + "description": "Search dblp venue registry (conferences / journals) by name or acronym", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Venue name or acronym (e.g. \"ICLR\", \"neural networks\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max venues (1-100, single dblp page)" + } + ], + "columns": [ + "rank", + "acronym", + "venue", + "type", + "url" + ], + "type": "js", + "modulePath": "plugins/dblp/venue.js", + "sourceFile": "plugins/dblp/venue.js" + }, + { + "site": "defillama", + "name": "protocol", + "description": "Single DefiLlama protocol details (current TVL, mcap, chains, twitter, github, description)", + "access": "read", + "domain": "defillama.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "slug", + "type": "string", + "required": true, + "positional": true, + "help": "DefiLlama protocol slug (e.g. \"aave\", \"lido\")" + } + ], + "columns": [ + "slug", + "name", + "category", + "isParent", + "tvl", + "tvlAt", + "mcap", + "chains", + "twitter", + "github", + "audits", + "listedAt", + "description", + "website", + "url" + ], + "type": "js", + "modulePath": "plugins/defillama/protocol.js", + "sourceFile": "plugins/defillama/protocol.js" + }, + { + "site": "defillama", + "name": "protocols", + "description": "Top DeFi protocols on DefiLlama by current TVL (slug, name, category, TVL, mcap, change_1d/7d, chains)", + "access": "read", + "domain": "defillama.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Number of rows to return (1-500)" + } + ], + "columns": [ + "rank", + "slug", + "name", + "category", + "tvl", + "mcap", + "change_1d", + "change_7d", + "chains", + "listedAt", + "url" + ], + "type": "js", + "modulePath": "plugins/defillama/protocols.js", + "sourceFile": "plugins/defillama/protocols.js" + }, + { + "site": "devto", + "name": "latest", + "description": "Newest dev.to articles (firehose, all tags)", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Articles per page (1-100)" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number (1-based)" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "tags", + "reactions", + "comments", + "published", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/latest.js", + "sourceFile": "plugins/devto/latest.js" + }, + { + "site": "devto", + "name": "read", + "description": "Read a DEV.to article body by id", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "DEV.to article id (numeric, e.g. 3605688)" + }, + { + "name": "max-length", + "type": "int", + "default": 20000, + "required": false, + "help": "Max characters of body to return (min 100)" + } + ], + "columns": [ + "id", + "title", + "author", + "reactions", + "reading_time", + "tags", + "published_at", + "body", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/read.js", + "sourceFile": "plugins/devto/read.js" + }, + { + "site": "devto", + "name": "tag", + "description": "Latest DEV.to articles for a specific tag", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "tag", + "type": "str", + "required": true, + "positional": true, + "help": "Tag name (e.g. javascript, python, webdev)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of articles" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/tag.js", + "sourceFile": "plugins/devto/tag.js" + }, + { + "site": "devto", + "name": "top", + "description": "Top DEV.to articles of the day", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of articles" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/top.js", + "sourceFile": "plugins/devto/top.js" + }, + { + "site": "devto", + "name": "user", + "description": "Recent DEV.to articles from a specific user", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "DEV.to username (e.g. ben, thepracticaldev)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of articles" + } + ], + "columns": [ + "rank", + "id", + "title", + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/user.js", + "sourceFile": "plugins/devto/user.js" + }, + { + "site": "dictionary", + "name": "examples", + "description": "Read real-world example sentences utilizing the word", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to get example sentences for" + } + ], + "columns": [ + "word", + "example" + ], + "type": "js", + "modulePath": "plugins/dictionary/examples.js", + "sourceFile": "plugins/dictionary/examples.js" + }, + { + "site": "dictionary", + "name": "search", + "description": "Search the Free Dictionary API for definitions, parts of speech, and pronunciations.", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to define (e.g., serendipity)" + } + ], + "columns": [ + "word", + "phonetic", + "type", + "definition" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/dictionary/search.js", + "sourceFile": "plugins/dictionary/search.js" + }, + { + "site": "dictionary", + "name": "synonyms", + "description": "Find synonyms for a specific word", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to find synonyms for (e.g., serendipity)" + } + ], + "columns": [ + "word", + "synonyms" + ], + "type": "js", + "modulePath": "plugins/dictionary/synonyms.js", + "sourceFile": "plugins/dictionary/synonyms.js" + }, + { + "site": "district", + "name": "checkout", + "description": "Select District movie seats and open the UPI QR payment scanner", + "access": "write", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "show", + "type": "str", + "required": true, + "positional": true, + "help": "District seat-layout URL or showId from district showtimes" + }, + { + "name": "seats", + "type": "str", + "required": true, + "help": "Comma-separated seat labels to select, e.g. I22,I21" + }, + { + "name": "format-id", + "type": "str", + "required": false, + "help": "District formatId from showtimes; required when show is a showId" + }, + { + "name": "content-id", + "type": "str", + "required": false, + "help": "District content id; required when show is a showId" + }, + { + "name": "timeout", + "type": "int", + "default": 45, + "required": false, + "help": "Maximum seconds to wait for selection, review page, and payment handoff" + }, + { + "name": "payment", + "type": "str", + "default": "upi-qr", + "required": false, + "help": "Payment handoff target: upi-qr opens the scanner; review stops on order review" + } + ], + "columns": [ + "status", + "movie", + "cinema", + "date", + "time", + "seats", + "ticketCount", + "orderAmount", + "bookingCharge", + "total", + "paymentMethod", + "paymentState", + "upiQrVisible", + "paymentAmount", + "paymentUrl", + "showId" + ], + "type": "js", + "modulePath": "plugins/district/checkout.js", + "sourceFile": "plugins/district/checkout.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true + }, + { + "site": "district", + "name": "listings", + "aliases": [ + "ls" + ], + "description": "List public District by Zomato movies, events, and nearby going-out cards", + "access": "read", + "domain": "www.district.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "input", + "type": "str", + "default": "home", + "required": false, + "positional": true, + "help": "home, movies, events, a district.in URL, or a District path" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum rows to return (1-100)" + } + ], + "columns": [ + "rank", + "title", + "category", + "date", + "venue", + "price", + "url" + ], + "type": "js", + "modulePath": "plugins/district/listings.js", + "sourceFile": "plugins/district/listings.js" + }, + { + "site": "district", + "name": "locations", + "aliases": [ + "location-search" + ], + "description": "Search District-supported cities, areas, malls, and places for booking filters", + "access": "read", + "domain": "www.district.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "City, area, mall, or locality, for example \"bangalore\" or \"indiranagar\"" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum location rows to return (1-50)" + } + ], + "columns": [ + "rank", + "name", + "kind", + "city", + "state", + "cityKey", + "cityId", + "placeId", + "lat", + "lng", + "distanceKm", + "source" + ], + "type": "js", + "modulePath": "plugins/district/locations.js", + "sourceFile": "plugins/district/locations.js" + }, + { + "site": "district", + "name": "login", + "description": "Open district login", + "access": "write", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "phone_number", + "email", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/district/auth.js", + "sourceFile": "plugins/district/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "district", + "name": "search", + "aliases": [ + "s" + ], + "description": "Search District by Zomato across movies, events, dining, stores, activities, and play", + "access": "read", + "domain": "www.district.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query, for example \"hamlet\" or \"arijit\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum rows to return (1-100)" + }, + { + "name": "tab", + "type": "str", + "default": "all", + "required": false, + "help": "Search tab: all, dining, events, movies, stores, activities, or play" + } + ], + "columns": [ + "rank", + "title", + "category", + "date", + "venue", + "price", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/district/search.js", + "sourceFile": "plugins/district/search.js" + }, + { + "site": "district", + "name": "seats", + "description": "List available seats for a District movie showtime", + "access": "read", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "show", + "type": "str", + "required": true, + "positional": true, + "help": "District seat-layout URL or showId from district showtimes" + }, + { + "name": "format-id", + "type": "str", + "required": false, + "help": "District formatId from showtimes; required when show is a showId" + }, + { + "name": "content-id", + "type": "str", + "required": false, + "help": "District content id; required when show is a showId" + }, + { + "name": "class", + "type": "str", + "required": false, + "help": "Optional seat class filter, e.g. premium, premium xl, or recliner" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Number of seats to choose (1-10); without count, seats are listed normally" + }, + { + "name": "together", + "type": "str", + "required": false, + "help": "Require selected seats to be adjacent when count is provided" + }, + { + "name": "max-price", + "type": "float", + "required": false, + "help": "Maximum price per seat" + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Maximum seats to return (1-300)" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Maximum seconds to wait for the seat map to render" + } + ], + "columns": [ + "rank", + "seat", + "row", + "number", + "column", + "seatClass", + "price", + "status", + "flags", + "showId", + "formatId", + "url" + ], + "type": "js", + "modulePath": "plugins/district/seats.js", + "sourceFile": "plugins/district/seats.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "district", + "name": "set-location", + "aliases": [ + "setlocation" + ], + "description": "Set the District browser session location for movie booking filters", + "access": "write", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "location", + "type": "str", + "required": true, + "positional": true, + "help": "City, area, mall, or locality, for example \"Bangalore\" or \"Indiranagar\"" + }, + { + "name": "rank", + "type": "int", + "default": 1, + "required": false, + "help": "Pick the Nth District location result (1-20), default: 1" + }, + { + "name": "timeout", + "type": "int", + "default": 45, + "required": false, + "help": "Maximum seconds to wait for the picker and location change" + } + ], + "columns": [ + "status", + "name", + "city", + "state", + "cityKey", + "cityId", + "placeId", + "subzoneId", + "lat", + "lng", + "availableTabs", + "source" + ], + "type": "js", + "modulePath": "plugins/district/set-location.js", + "sourceFile": "plugins/district/set-location.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "district", + "name": "showtimes", + "aliases": [ + "shows" + ], + "description": "List District movie showtimes with location, time, cinema, language, price, and format filters", + "access": "read", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "movie", + "type": "str", + "required": true, + "positional": true, + "help": "Movie name or District movie URL" + }, + { + "name": "date", + "type": "str", + "required": false, + "help": "Show date in YYYY-MM-DD format; defaults to District selected date" + }, + { + "name": "city", + "type": "str", + "required": false, + "help": "District city name/key, for example Bangalore or Bengaluru" + }, + { + "name": "near", + "type": "str", + "required": false, + "help": "Area, mall, or locality to search near, for example Indiranagar" + }, + { + "name": "city-key", + "type": "str", + "required": false, + "help": "Legacy District city key override, for example bengaluru" + }, + { + "name": "after", + "type": "str", + "required": false, + "help": "Only shows at or after HH:MM, 24-hour time" + }, + { + "name": "before", + "type": "str", + "required": false, + "help": "Only shows at or before HH:MM, 24-hour time" + }, + { + "name": "cinema", + "type": "str", + "required": false, + "help": "Filter cinema/theatre name, for example PVR, INOX, Orion" + }, + { + "name": "language", + "type": "str", + "required": false, + "help": "Filter movie language, for example English, Hindi, Kannada" + }, + { + "name": "max-price", + "type": "float", + "required": false, + "help": "Only shows with at least one ticket class at or below this price" + }, + { + "name": "quality", + "type": "str", + "required": false, + "help": "Generic format/quality filter, for example 2D, 3D, IMAX, IMAX 3D, 4DX" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Maximum showtime rows to return (1-200)" + } + ], + "columns": [ + "rank", + "movie", + "language", + "date", + "time", + "cinema", + "format", + "priceRange", + "available", + "showId", + "formatId", + "url" + ], + "type": "js", + "modulePath": "plugins/district/showtimes.js", + "sourceFile": "plugins/district/showtimes.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "district", + "name": "whoami", + "description": "Show the current logged-in district account", + "access": "read", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name", + "phone_number", + "email" + ], + "type": "js", + "modulePath": "plugins/district/auth.js", + "sourceFile": "plugins/district/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "dockerhub", + "name": "image", + "description": "Fetch a Docker Hub repository's public metadata (stars, pulls, last updated, status)", + "access": "read", + "domain": "hub.docker.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image name (e.g. \"nginx\", \"library/nginx\", \"bitnami/redis\")" + } + ], + "columns": [ + "image", + "official", + "stars", + "pulls", + "description", + "lastUpdated", + "lastModified", + "registered", + "status", + "url" + ], + "type": "js", + "modulePath": "plugins/dockerhub/image.js", + "sourceFile": "plugins/dockerhub/image.js" + }, + { + "site": "dockerhub", + "name": "search", + "description": "Search Docker Hub repositories by keyword", + "access": "read", + "domain": "hub.docker.com", + "strategy": "public", + "browser": false, + "args": [ + { "name": "query", "type": "str", "required": true, "positional": true, - "help": "City, area, mall, or locality, for example \"bangalore\" or \"indiranagar\"" + "help": "Search keyword (e.g. \"nginx\", \"bitnami redis\")" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max repositories (1-100, single Docker Hub page)" + } + ], + "columns": [ + "rank", + "image", + "official", + "stars", + "pulls", + "description", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/dockerhub/search.js", + "sourceFile": "plugins/dockerhub/search.js" + }, + { + "site": "duckduckgo", + "name": "search", + "description": "Search DuckDuckGo", + "access": "read", + "domain": "html.duckduckgo.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Maximum location rows to return (1-50)" + "help": "Number of results per page (1-10). For multi-page, use --offset" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Result offset for pagination (0, 10, 20...). Uses XHR POST internally" + }, + { + "name": "region", + "type": "str", + "required": false, + "help": "Region code (e.g. jp-jp, us-en, cn-zh). Default: all regions" + }, + { + "name": "time", + "type": "str", + "required": false, + "help": "Time range: d (day), w (week), m (month), y (year)" + } + ], + "columns": [ + "rank", + "title", + "url", + "snippet", + "displayUrl", + "icon", + "resultType" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/duckduckgo/search.js", + "sourceFile": "plugins/duckduckgo/search.js" + }, + { + "site": "duckduckgo", + "name": "suggest", + "description": "DuckDuckGo search suggestions", + "access": "read", + "domain": "duckduckgo.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query prefix" + }, + { + "name": "limit", + "type": "int", + "default": 8, + "required": false, + "help": "Max number of suggestions" + } + ], + "columns": [ + "phrase" + ], + "type": "js", + "modulePath": "plugins/duckduckgo/suggest.js", + "sourceFile": "plugins/duckduckgo/suggest.js" + }, + { + "site": "endoflife", + "name": "product", + "description": "Release cycles + EOL / LTS / support dates for one product on endoflife.date", + "access": "read", + "domain": "endoflife.date", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "product", + "type": "string", + "required": true, + "positional": true, + "help": "endoflife.date product slug (e.g. \"nodejs\", \"python\", \"ubuntu\")" + } + ], + "columns": [ + "product", + "cycle", + "releaseDate", + "latest", + "latestReleaseDate", + "lts", + "support", + "eol", + "extendedSupport", + "eolStatus", + "url" + ], + "type": "js", + "modulePath": "plugins/endoflife/product.js", + "sourceFile": "plugins/endoflife/product.js" + }, + { + "site": "flathub", + "name": "app", + "description": "Full Flathub appstream metadata for an app id (license, categories, latest release)", + "access": "read", + "domain": "flathub.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "appId", + "type": "str", + "required": true, + "positional": true, + "help": "AppStream id (e.g. \"org.mozilla.firefox\", \"org.gnome.Calculator\")" + } + ], + "columns": [ + "appId", + "name", + "summary", + "developer", + "license", + "isFreeLicense", + "isEol", + "categories", + "keywords", + "latestVersion", + "latestReleaseDate", + "homepage", + "bugtracker", + "donation", + "url" + ], + "type": "js", + "modulePath": "plugins/flathub/app.js", + "sourceFile": "plugins/flathub/app.js" + }, + { + "site": "flathub", + "name": "search", + "description": "Search Flathub apps by keyword", + "access": "read", + "domain": "flathub.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max apps (1-100)" } ], "columns": [ "rank", + "appId", "name", - "kind", - "city", - "state", - "cityKey", - "cityId", - "placeId", - "lat", - "lng", - "distanceKm", - "source" + "summary", + "developer", + "license", + "isFreeLicense", + "mainCategories", + "installsLastMonth", + "updatedAt", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/district/locations.js", - "sourceFile": "plugins/district/locations.js" + "modulePath": "plugins/flathub/search.js", + "sourceFile": "plugins/flathub/search.js" }, { - "site": "district", - "name": "login", - "description": "Open district login", + "site": "gemini", + "name": "ask", + "description": "Send a prompt to Gemini and return only the assistant response", "access": "write", - "domain": "www.district.in", + "domain": "gemini.google.com", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "phone_number", - "email", - "action", - "verify_command" + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "model", + "type": "string", + "required": false, + "help": "Gemini model to use (e.g. \"2.5-flash\"). Use \"webcmd gemini models\" to list available values." + }, + { + "name": "timeout", + "type": "int", + "default": 60, + "required": false, + "help": "Max seconds to wait (default: 60)" + }, + { + "name": "new", + "type": "str", + "default": "false", + "required": false, + "help": "Start a new chat first (true/false, default: false)" + }, + { + "name": "thinking", + "type": "str", + "default": null, + "required": false, + "help": "Thinking level: standard or extended (omitted = leave unchanged)" + } + ], + "columns": [ + "response" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/district/auth.js", - "sourceFile": "plugins/district/auth.js", + "modulePath": "plugins/gemini/ask.js", + "sourceFile": "plugins/gemini/ask.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "district", - "name": "search", - "aliases": [ - "s" - ], - "description": "Search District by Zomato across movies, events, dining, stores, activities, and play", - "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, + "site": "gemini", + "name": "deep-research", + "description": "Start a Gemini Deep Research run and confirm it", + "access": "write", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "prompt", "type": "str", "required": true, "positional": true, - "help": "Search query, for example \"hamlet\" or \"arijit\"" + "help": "Prompt to send" }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 20, + "default": 180, "required": false, - "help": "Maximum rows to return (1-100)" + "help": "Max seconds for the overall command (default: 180; confirm-wait clamps internally to 6-20s)" }, { - "name": "tab", + "name": "tool", "type": "str", - "default": "all", "required": false, - "help": "Search tab: all, dining, events, movies, stores, activities, or play" + "help": "Override tool label (default: Deep Research)" + }, + { + "name": "confirm", + "type": "str", + "required": false, + "help": "Override confirm button label (default: Start research)" } ], "columns": [ - "rank", - "title", - "category", - "date", - "venue", - "price", + "status", "url" ], "tags": [ "search" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/district/search.js", - "sourceFile": "plugins/district/search.js" + "modulePath": "plugins/gemini/deep-research.js", + "sourceFile": "plugins/gemini/deep-research.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "district", - "name": "seats", - "description": "List available seats for a District movie showtime", + "site": "gemini", + "name": "deep-research-result", + "description": "Export Deep Research report URL from a Gemini conversation", "access": "read", - "domain": "www.district.in", + "domain": "gemini.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "show", + "name": "query", "type": "str", - "required": true, + "required": false, "positional": true, - "help": "District seat-layout URL or showId from district showtimes" + "help": "Conversation title or URL (optional; defaults to latest conversation)" }, { - "name": "format-id", + "name": "match", "type": "str", + "default": "contains", "required": false, - "help": "District formatId from showtimes; required when show is a showId" + "help": "Match mode", + "choices": [ + "contains", + "exact" + ] }, { - "name": "content-id", - "type": "str", + "name": "timeout", + "type": "int", + "default": 120, "required": false, - "help": "District content id; required when show is a showId" - }, + "help": "Max seconds to wait for Docs export (default: 120)" + } + ], + "columns": [ + "response" + ], + "tags": [ + "search" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "plugins/gemini/deep-research-result.js", + "sourceFile": "plugins/gemini/deep-research-result.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "detail", + "description": "Open a Gemini web conversation by id, URL, or sidebar title and read its turns", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "class", + "name": "id", "type": "str", + "required": true, + "positional": true, + "help": "Conversation id, /app/ URL, or sidebar title" + } + ], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/gemini/detail.js", + "sourceFile": "plugins/gemini/detail.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "history", + "description": "List visible Gemini web conversation history from the sidebar", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Optional seat class filter, e.g. premium, premium xl, or recliner" + "help": "Max conversations to show" + } + ], + "columns": [ + "Index", + "Id", + "Title", + "Url" + ], + "type": "js", + "modulePath": "plugins/gemini/history.js", + "sourceFile": "plugins/gemini/history.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "image", + "description": "Generate images with Gemini web and save them locally", + "access": "write", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Image prompt to send to Gemini" }, { - "name": "count", - "type": "int", + "name": "rt", + "type": "str", + "default": "1:1", "required": false, - "help": "Number of seats to choose (1-10); without count, seats are listed normally" + "help": "Ratio shorthand for aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3)" }, { - "name": "together", + "name": "st", "type": "str", + "default": "", "required": false, - "help": "Require selected seats to be adjacent when count is provided" + "help": "Style shorthand, e.g. anime, icon, watercolor" }, { - "name": "max-price", - "type": "float", + "name": "op", + "type": "str", + "default": "~/tmp/gemini-images", "required": false, - "help": "Maximum price per seat" + "help": "Output directory shorthand" }, { - "name": "limit", - "type": "int", - "default": 100, + "name": "sd", + "type": "boolean", + "default": false, "required": false, - "help": "Maximum seats to return (1-300)" + "help": "Skip download shorthand; only show Gemini page link" }, { "name": "timeout", "type": "int", - "default": 30, + "default": 240, "required": false, - "help": "Maximum seconds to wait for the seat map to render" + "help": "Max seconds for the overall command (default: 240)" } ], "columns": [ - "rank", - "seat", - "row", - "number", - "column", - "seatClass", - "price", "status", - "flags", - "showId", - "formatId", - "url" + "file", + "link" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "plugins/gemini/image.js", + "sourceFile": "plugins/gemini/image.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "login", + "description": "Open gemini login", + "access": "write", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/gemini/auth.js", + "sourceFile": "plugins/gemini/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "models", + "description": "List available Gemini models from the web UI", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "model", + "thinkingValues" + ], + "type": "js", + "modulePath": "plugins/gemini/models.js", + "sourceFile": "plugins/gemini/models.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "new", + "description": "Start a new conversation in Gemini web chat", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status", + "Action" + ], + "type": "js", + "modulePath": "plugins/gemini/new.js", + "sourceFile": "plugins/gemini/new.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "read", + "description": "Read the turns visible in the current Gemini web conversation", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/gemini/read.js", + "sourceFile": "plugins/gemini/read.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "status", + "description": "Check Gemini web page availability and login state", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status", + "Login", + "Url" + ], + "type": "js", + "modulePath": "plugins/gemini/status.js", + "sourceFile": "plugins/gemini/status.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "whoami", + "description": "Show the current logged-in gemini account", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name" + ], + "type": "js", + "modulePath": "plugins/gemini/auth.js", + "sourceFile": "plugins/gemini/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "github", + "name": "login", + "description": "Open github login", + "access": "write", + "domain": "github.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "id", + "username", + "name", + "url", + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/district/seats.js", - "sourceFile": "plugins/district/seats.js", + "modulePath": "plugins/github/auth.js", + "sourceFile": "plugins/github/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "district", - "name": "set-location", - "aliases": [ - "setlocation" - ], - "description": "Set the District browser session location for movie booking filters", - "access": "write", - "domain": "www.district.in", + "site": "github", + "name": "whoami", + "description": "Show the current logged-in github account", + "access": "read", + "domain": "github.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "location", - "type": "str", - "required": true, - "positional": true, - "help": "City, area, mall, or locality, for example \"Bangalore\" or \"Indiranagar\"" - }, - { - "name": "rank", - "type": "int", - "default": 1, - "required": false, - "help": "Pick the Nth District location result (1-20), default: 1" - }, - { - "name": "timeout", - "type": "int", - "default": 45, - "required": false, - "help": "Maximum seconds to wait for the picker and location change" - } - ], + "args": [], "columns": [ - "status", + "logged_in", + "site", + "id", + "username", "name", - "city", - "state", - "cityKey", - "cityId", - "placeId", - "subzoneId", - "lat", - "lng", - "availableTabs", - "source" + "url" ], "type": "js", - "modulePath": "plugins/district/set-location.js", - "sourceFile": "plugins/district/set-location.js", + "modulePath": "plugins/github/auth.js", + "sourceFile": "plugins/github/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "district", - "name": "showtimes", - "aliases": [ - "shows" - ], - "description": "List District movie showtimes with location, time, cinema, language, price, and format filters", + "site": "github-trending", + "name": "repos", + "description": "GitHub Trending repositories (public, no login). Filter by --language and --since.", "access": "read", - "domain": "www.district.in", - "strategy": "cookie", - "browser": true, + "domain": "github.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "movie", - "type": "str", - "required": true, - "positional": true, - "help": "Movie name or District movie URL" - }, - { - "name": "date", - "type": "str", - "required": false, - "help": "Show date in YYYY-MM-DD format; defaults to District selected date" - }, - { - "name": "city", - "type": "str", - "required": false, - "help": "District city name/key, for example Bangalore or Bengaluru" - }, - { - "name": "near", - "type": "str", - "required": false, - "help": "Area, mall, or locality to search near, for example Indiranagar" - }, - { - "name": "city-key", - "type": "str", - "required": false, - "help": "Legacy District city key override, for example bengaluru" - }, - { - "name": "after", - "type": "str", - "required": false, - "help": "Only shows at or after HH:MM, 24-hour time" - }, - { - "name": "before", - "type": "str", - "required": false, - "help": "Only shows at or before HH:MM, 24-hour time" - }, - { - "name": "cinema", - "type": "str", + "name": "since", + "type": "string", + "default": "daily", "required": false, - "help": "Filter cinema/theatre name, for example PVR, INOX, Orion" + "help": "Time range: daily / weekly / monthly" }, { "name": "language", - "type": "str", - "required": false, - "help": "Filter movie language, for example English, Hindi, Kannada" - }, - { - "name": "max-price", - "type": "float", - "required": false, - "help": "Only shows with at least one ticket class at or below this price" - }, - { - "name": "quality", - "type": "str", + "type": "string", + "default": "", "required": false, - "help": "Generic format/quality filter, for example 2D, 3D, IMAX, IMAX 3D, 4DX" + "help": "Filter by programming language slug, e.g. python, rust, \"c++\"" }, { "name": "limit", "type": "int", - "default": 50, + "default": 25, "required": false, - "help": "Maximum showtime rows to return (1-200)" + "help": "Number of repositories to return (max 25)" } ], "columns": [ "rank", - "movie", + "repo", + "description", "language", - "date", - "time", - "cinema", - "format", - "priceRange", - "available", - "showId", - "formatId", + "stars", + "forks", + "starsSince", "url" ], "type": "js", - "modulePath": "plugins/district/showtimes.js", - "sourceFile": "plugins/district/showtimes.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/github-trending/repos.js", + "sourceFile": "plugins/github-trending/repos.js" }, { - "site": "district", - "name": "whoami", - "description": "Show the current logged-in district account", + "site": "goettingen", + "name": "export-postgraduate-courses", + "description": "Export University of Göttingen postgraduate programmes from the official A-Z API.", "access": "read", - "domain": "www.district.in", - "strategy": "cookie", - "browser": true, - "args": [], + "example": "webcmd goettingen export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.uni-goettingen.de", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programmes after filtering and deduplication" + } + ], "columns": [ - "logged_in", - "site", - "user_id", - "name", - "phone_number", - "email" + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/district/auth.js", - "sourceFile": "plugins/district/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/goettingen/export-postgraduate-courses.js", + "sourceFile": "plugins/goettingen/export-postgraduate-courses.js" }, { - "site": "dockerhub", - "name": "image", - "description": "Fetch a Docker Hub repository's public metadata (stars, pulls, last updated, status)", + "site": "google", + "name": "images", + "description": "Search Google Images for photos and image results", "access": "read", - "domain": "hub.docker.com", + "domain": "google.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "image", + "name": "keyword", "type": "str", "required": true, "positional": true, - "help": "Image name (e.g. \"nginx\", \"library/nginx\", \"bitnami/redis\")" + "help": "Image search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of image results (1-100)" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language short code (e.g. en, zh)" + }, + { + "name": "resolve", + "type": "bool", + "default": true, + "required": false, + "help": "Click image previews to resolve original imgurl values" } ], "columns": [ - "image", - "official", - "stars", - "pulls", - "description", - "lastUpdated", - "lastModified", - "registered", - "status", - "url" + "rank", + "title", + "imageUrl", + "thumbnailUrl", + "sourceUrl", + "source", + "width", + "height" ], "type": "js", - "modulePath": "plugins/dockerhub/image.js", - "sourceFile": "plugins/dockerhub/image.js" + "modulePath": "plugins/google/images.js", + "sourceFile": "plugins/google/images.js", + "navigateBefore": false }, { - "site": "dockerhub", - "name": "search", - "description": "Search Docker Hub repositories by keyword", + "site": "google", + "name": "news", + "description": "Get Google News headlines", "access": "read", - "domain": "hub.docker.com", "strategy": "public", "browser": false, "args": [ { - "name": "query", + "name": "keyword", "type": "str", - "required": true, + "required": false, "positional": true, - "help": "Search keyword (e.g. \"nginx\", \"bitnami redis\")" + "help": "Search query (omit for top stories)" }, { "name": "limit", "type": "int", - "default": 25, + "default": 10, "required": false, - "help": "Max repositories (1-100, single Docker Hub page)" + "help": "Number of results" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language short code (e.g. en, zh)" + }, + { + "name": "region", + "type": "str", + "default": "US", + "required": false, + "help": "Region code (e.g. US, CN)" } ], "columns": [ - "rank", - "image", - "official", - "stars", - "pulls", - "description", + "title", + "source", + "date", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/dockerhub/search.js", - "sourceFile": "plugins/dockerhub/search.js" + "modulePath": "plugins/google/news.js", + "sourceFile": "plugins/google/news.js" }, { - "site": "duckduckgo", + "site": "google", "name": "search", - "description": "Search DuckDuckGo", + "description": "Search Google", "access": "read", - "domain": "html.duckduckgo.com", + "domain": "google.com", "strategy": "public", "browser": true, "args": [ @@ -5926,50 +7700,34 @@ "type": "int", "default": 10, "required": false, - "help": "Number of results per page (1-10). For multi-page, use --offset" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Result offset for pagination (0, 10, 20...). Uses XHR POST internally" - }, - { - "name": "region", - "type": "str", - "required": false, - "help": "Region code (e.g. jp-jp, us-en, cn-zh). Default: all regions" + "help": "Number of results (1-100)" }, { - "name": "time", + "name": "lang", "type": "str", + "default": "en", "required": false, - "help": "Time range: d (day), w (week), m (month), y (year)" + "help": "Language short code (e.g. en, zh)" } ], "columns": [ - "rank", + "type", "title", "url", - "snippet", - "displayUrl", - "icon", - "resultType" + "snippet" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/duckduckgo/search.js", - "sourceFile": "plugins/duckduckgo/search.js" + "modulePath": "plugins/google/search.js", + "sourceFile": "plugins/google/search.js" }, { - "site": "duckduckgo", + "site": "google", "name": "suggest", - "description": "DuckDuckGo search suggestions", + "description": "Get Google search suggestions", "access": "read", - "domain": "duckduckgo.com", "strategy": "public", "browser": false, "args": [ @@ -5978,503 +7736,437 @@ "type": "str", "required": true, "positional": true, - "help": "Search query prefix" + "help": "Search query" }, { - "name": "limit", - "type": "int", - "default": 8, - "required": false, - "help": "Max number of suggestions" - } - ], - "columns": [ - "phrase" - ], - "type": "js", - "modulePath": "plugins/duckduckgo/suggest.js", - "sourceFile": "plugins/duckduckgo/suggest.js" - }, - { - "site": "endoflife", - "name": "product", - "description": "Release cycles + EOL / LTS / support dates for one product on endoflife.date", - "access": "read", - "domain": "endoflife.date", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "product", - "type": "string", - "required": true, - "positional": true, - "help": "endoflife.date product slug (e.g. \"nodejs\", \"python\", \"ubuntu\")" - } - ], - "columns": [ - "product", - "cycle", - "releaseDate", - "latest", - "latestReleaseDate", - "lts", - "support", - "eol", - "extendedSupport", - "eolStatus", - "url" - ], - "type": "js", - "modulePath": "plugins/endoflife/product.js", - "sourceFile": "plugins/endoflife/product.js" - }, - { - "site": "flathub", - "name": "app", - "description": "Full Flathub appstream metadata for an app id (license, categories, latest release)", - "access": "read", - "domain": "flathub.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "appId", + "name": "lang", "type": "str", - "required": true, - "positional": true, - "help": "AppStream id (e.g. \"org.mozilla.firefox\", \"org.gnome.Calculator\")" + "default": "zh-CN", + "required": false, + "help": "Language code" } ], "columns": [ - "appId", - "name", - "summary", - "developer", - "license", - "isFreeLicense", - "isEol", - "categories", - "keywords", - "latestVersion", - "latestReleaseDate", - "homepage", - "bugtracker", - "donation", - "url" + "suggestion" ], "type": "js", - "modulePath": "plugins/flathub/app.js", - "sourceFile": "plugins/flathub/app.js" + "modulePath": "plugins/google/suggest.js", + "sourceFile": "plugins/google/suggest.js" }, { - "site": "flathub", - "name": "search", - "description": "Search Flathub apps by keyword", + "site": "google", + "name": "trends", + "description": "Get Google Trends daily trending searches", "access": "read", - "domain": "flathub.org", "strategy": "public", "browser": false, "args": [ { - "name": "query", + "name": "region", "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" + "default": "US", + "required": false, + "help": "Region code (e.g. US, CN, JP)" }, { "name": "limit", "type": "int", - "default": 25, + "default": 20, "required": false, - "help": "Max apps (1-100)" + "help": "Number of results" } ], "columns": [ - "rank", - "appId", - "name", - "summary", - "developer", - "license", - "isFreeLicense", - "mainCategories", - "installsLastMonth", - "updatedAt", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/flathub/search.js", - "sourceFile": "plugins/flathub/search.js" - }, - { - "site": "github", - "name": "login", - "description": "Open github login", - "access": "write", - "domain": "github.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "id", - "username", - "name", - "url", - "action", - "verify_command" + "title", + "traffic", + "date" ], "type": "js", - "modulePath": "plugins/github/auth.js", - "sourceFile": "plugins/github/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/google/trends.js", + "sourceFile": "plugins/google/trends.js" }, { - "site": "github", - "name": "whoami", - "description": "Show the current logged-in github account", + "site": "google-scholar", + "name": "cite", + "description": "Get citation for a Google Scholar paper", "access": "read", - "domain": "github.com", - "strategy": "cookie", + "domain": "scholar.google.com", + "strategy": "public", "browser": true, - "args": [], + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Paper title to search for" + }, + { + "name": "style", + "type": "str", + "default": "bibtex", + "required": false, + "help": "Citation format", + "choices": [ + "bibtex", + "endnote", + "refman", + "refworks" + ] + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Which search result to cite (1-based)" + } + ], "columns": [ - "logged_in", - "site", - "id", - "username", - "name", - "url" + "title", + "format", + "citation" ], "type": "js", - "modulePath": "plugins/github/auth.js", - "sourceFile": "plugins/github/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/google-scholar/cite.js", + "sourceFile": "plugins/google-scholar/cite.js" }, { - "site": "github-trending", - "name": "repos", - "description": "GitHub Trending repositories (public, no login). Filter by --language and --since.", + "site": "google-scholar", + "name": "profile", + "description": "View a Google Scholar author profile", "access": "read", - "domain": "github.com", + "domain": "scholar.google.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "since", - "type": "string", - "default": "daily", - "required": false, - "help": "Time range: daily / weekly / monthly" - }, - { - "name": "language", - "type": "string", - "default": "", - "required": false, - "help": "Filter by programming language slug, e.g. python, rust, \"c++\"" + "name": "author", + "type": "str", + "required": true, + "positional": true, + "help": "Author name or Scholar user ID (e.g. JicYPdAAAAAJ)" }, { "name": "limit", "type": "int", - "default": 25, + "default": 10, "required": false, - "help": "Number of repositories to return (max 25)" + "help": "Max papers to show (max 20)" } ], "columns": [ "rank", - "repo", - "description", - "language", - "stars", - "forks", - "starsSince", - "url" + "title", + "cited", + "year" ], "type": "js", - "modulePath": "plugins/github-trending/repos.js", - "sourceFile": "plugins/github-trending/repos.js" + "modulePath": "plugins/google-scholar/profile.js", + "sourceFile": "plugins/google-scholar/profile.js" }, { - "site": "goettingen", - "name": "export-postgraduate-courses", - "description": "Export University of Göttingen postgraduate programmes from the official A-Z API.", + "site": "google-scholar", + "name": "search", + "description": "Google Scholar scholar search", "access": "read", - "example": "webcmd goettingen export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.uni-goettingen.de", + "domain": "scholar.google.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" }, { - "name": "count", + "name": "limit", "type": "int", + "default": 10, "required": false, - "help": "Positive maximum number of programmes after filtering and deduplication" + "help": "Number of results to return (max 20)" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "rank", + "title", + "authors", + "source", + "year", + "cited", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/google-scholar/search.js", + "sourceFile": "plugins/google-scholar/search.js" + }, + { + "site": "goproxy", + "name": "module", + "description": "Latest version + VCS origin metadata for a Go module on proxy.golang.org", + "access": "read", + "domain": "proxy.golang.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "module", + "type": "string", + "required": true, + "positional": true, + "help": "Go module path (e.g. \"github.com/gin-gonic/gin\", \"golang.org/x/net\")" + } + ], + "columns": [ + "module", + "version", + "publishedAt", + "vcs", + "repository", + "commit", + "ref", + "pkgGoDevUrl", + "url" ], "type": "js", - "modulePath": "plugins/goettingen/export-postgraduate-courses.js", - "sourceFile": "plugins/goettingen/export-postgraduate-courses.js" + "modulePath": "plugins/goproxy/module.js", + "sourceFile": "plugins/goproxy/module.js" }, { - "site": "google", - "name": "images", - "description": "Search Google Images for photos and image results", + "site": "goproxy", + "name": "versions", + "description": "Published version tags for a Go module (newest first), optionally with publish times", "access": "read", - "domain": "google.com", + "domain": "proxy.golang.org", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "keyword", - "type": "str", + "name": "module", + "type": "string", "required": true, "positional": true, - "help": "Image search query" + "help": "Go module path (e.g. \"github.com/gin-gonic/gin\")" }, { "name": "limit", "type": "int", - "default": 20, + "default": 30, "required": false, - "help": "Number of image results (1-100)" + "help": "Max rows to return (1-200)" }, { - "name": "lang", - "type": "str", - "default": "en", + "name": "with-time", + "type": "boolean", + "default": false, "required": false, - "help": "Language short code (e.g. en, zh)" - }, + "help": "Fetch each version's publish time (one extra request per row)" + } + ], + "columns": [ + "rank", + "module", + "version", + "publishedAt", + "url" + ], + "type": "js", + "modulePath": "plugins/goproxy/versions.js", + "sourceFile": "plugins/goproxy/versions.js" + }, + { + "site": "hackernews", + "name": "ask", + "description": "Hacker News Ask HN posts", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ { - "name": "resolve", - "type": "bool", - "default": true, + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Click image previews to resolve original imgurl values" + "help": "Number of stories" } ], "columns": [ "rank", + "id", "title", - "imageUrl", - "thumbnailUrl", - "sourceUrl", - "source", - "width", - "height" + "score", + "author", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/google/images.js", - "sourceFile": "plugins/google/images.js", - "navigateBefore": false + "modulePath": "plugins/hackernews/ask.js", + "sourceFile": "plugins/hackernews/ask.js" }, { - "site": "google", - "name": "news", - "description": "Get Google News headlines", + "site": "hackernews", + "name": "best", + "description": "Hacker News best stories", "access": "read", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ - { - "name": "keyword", - "type": "str", - "required": false, - "positional": true, - "help": "Search query (omit for top stories)" - }, { "name": "limit", "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - }, - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language short code (e.g. en, zh)" - }, - { - "name": "region", - "type": "str", - "default": "US", + "default": 20, "required": false, - "help": "Region code (e.g. US, CN)" + "help": "Number of stories" } ], "columns": [ + "rank", + "id", "title", - "source", - "date", + "score", + "author", + "comments", "url" ], "type": "js", - "modulePath": "plugins/google/news.js", - "sourceFile": "plugins/google/news.js" + "modulePath": "plugins/hackernews/best.js", + "sourceFile": "plugins/hackernews/best.js" }, { - "site": "google", - "name": "search", - "description": "Search Google", + "site": "hackernews", + "name": "jobs", + "description": "Hacker News job postings", "access": "read", - "domain": "google.com", + "domain": "news.ycombinator.com", "strategy": "public", - "browser": true, + "browser": false, "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results (1-100)" - }, + "help": "Number of job postings" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "url" + ], + "type": "js", + "modulePath": "plugins/hackernews/jobs.js", + "sourceFile": "plugins/hackernews/jobs.js" + }, + { + "site": "hackernews", + "name": "new", + "description": "Hacker News newest stories", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ { - "name": "lang", - "type": "str", - "default": "en", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Language short code (e.g. en, zh)" + "help": "Number of stories" } ], "columns": [ - "type", + "rank", + "id", "title", - "url", - "snippet" - ], - "tags": [ - "search" + "score", + "author", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/google/search.js", - "sourceFile": "plugins/google/search.js" + "modulePath": "plugins/hackernews/new.js", + "sourceFile": "plugins/hackernews/new.js" }, { - "site": "google", - "name": "suggest", - "description": "Get Google search suggestions", + "site": "hackernews", + "name": "read", + "description": "Read a Hacker News story and its comment tree", "access": "read", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ { - "name": "keyword", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "HN item ID (e.g. 39847301)" }, { - "name": "lang", - "type": "str", - "default": "zh-CN", + "name": "limit", + "type": "int", + "default": 25, "required": false, - "help": "Language code" + "help": "Max top-level comments" + }, + { + "name": "depth", + "type": "int", + "default": 2, + "required": false, + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + }, + { + "name": "replies", + "type": "int", + "default": 5, + "required": false, + "help": "Max replies shown per comment at each level" + }, + { + "name": "max-length", + "type": "int", + "default": 2000, + "required": false, + "help": "Max characters per comment body (min 100)" } ], "columns": [ - "suggestion" + "type", + "author", + "score", + "text" ], "type": "js", - "modulePath": "plugins/google/suggest.js", - "sourceFile": "plugins/google/suggest.js" + "modulePath": "plugins/hackernews/read.js", + "sourceFile": "plugins/hackernews/read.js" }, { - "site": "google", - "name": "trends", - "description": "Get Google Trends daily trending searches", + "site": "hackernews", + "name": "search", + "description": "Search Hacker News stories", "access": "read", + "domain": "news.ycombinator.com", "strategy": "public", "browser": false, "args": [ { - "name": "region", + "name": "query", "type": "str", - "default": "US", - "required": false, - "help": "Region code (e.g. US, CN, JP)" + "required": true, + "positional": true, + "help": "Search query" }, { "name": "limit", @@ -6482,416 +8174,445 @@ "default": 20, "required": false, "help": "Number of results" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance or date", + "choices": [ + "relevance", + "date" + ] } ], "columns": [ + "rank", + "id", "title", - "traffic", - "date" + "score", + "author", + "comments", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/google/trends.js", - "sourceFile": "plugins/google/trends.js" + "modulePath": "plugins/hackernews/search.js", + "sourceFile": "plugins/hackernews/search.js" }, { - "site": "google-scholar", - "name": "cite", - "description": "Get citation for a Google Scholar paper", + "site": "hackernews", + "name": "show", + "description": "Hacker News Show HN posts", "access": "read", - "domain": "scholar.google.com", + "domain": "news.ycombinator.com", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Paper title to search for" - }, - { - "name": "style", - "type": "str", - "default": "bibtex", - "required": false, - "help": "Citation format", - "choices": [ - "bibtex", - "endnote", - "refman", - "refworks" - ] - }, - { - "name": "index", + "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Which search result to cite (1-based)" + "help": "Number of stories" } ], "columns": [ + "rank", + "id", "title", - "format", - "citation" + "score", + "author", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/google-scholar/cite.js", - "sourceFile": "plugins/google-scholar/cite.js" + "modulePath": "plugins/hackernews/show.js", + "sourceFile": "plugins/hackernews/show.js" }, { - "site": "google-scholar", - "name": "profile", - "description": "View a Google Scholar author profile", + "site": "hackernews", + "name": "top", + "description": "Hacker News top stories", "access": "read", - "domain": "scholar.google.com", + "domain": "news.ycombinator.com", "strategy": "public", - "browser": true, + "browser": false, "args": [ - { - "name": "author", - "type": "str", - "required": true, - "positional": true, - "help": "Author name or Scholar user ID (e.g. JicYPdAAAAAJ)" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Max papers to show (max 20)" + "help": "Number of stories" } ], "columns": [ "rank", + "id", "title", - "cited", - "year" + "score", + "author", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/google-scholar/profile.js", - "sourceFile": "plugins/google-scholar/profile.js" + "modulePath": "plugins/hackernews/top.js", + "sourceFile": "plugins/hackernews/top.js" }, { - "site": "google-scholar", - "name": "search", - "description": "Google Scholar scholar search", + "site": "hackernews", + "name": "user", + "description": "Hacker News user profile", "access": "read", - "domain": "scholar.google.com", + "domain": "news.ycombinator.com", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "query", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results to return (max 20)" + "help": "HN username" } ], "columns": [ - "rank", - "title", - "authors", - "source", - "year", - "cited", - "url" - ], - "tags": [ - "search" + "username", + "karma", + "created", + "about" ], "type": "js", - "modulePath": "plugins/google-scholar/search.js", - "sourceFile": "plugins/google-scholar/search.js" + "modulePath": "plugins/hackernews/user.js", + "sourceFile": "plugins/hackernews/user.js" }, { - "site": "goproxy", - "name": "module", - "description": "Latest version + VCS origin metadata for a Go module on proxy.golang.org", + "site": "heidelberg", + "name": "export-postgraduate-courses", + "description": "Export Heidelberg University non-bachelor/postgraduate programs using the official study finder.", "access": "read", - "domain": "proxy.golang.org", + "example": "webcmd heidelberg export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.uni-heidelberg.de", "strategy": "public", "browser": false, "args": [ { - "name": "module", + "name": "degree-level", "type": "string", - "required": true, - "positional": true, - "help": "Go module path (e.g. \"github.com/gin-gonic/gin\", \"golang.org/x/net\")" + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" } ], "columns": [ - "module", - "version", - "publishedAt", - "vcs", - "repository", - "commit", - "ref", - "pkgGoDevUrl", - "url" + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/goproxy/module.js", - "sourceFile": "plugins/goproxy/module.js" + "modulePath": "plugins/heidelberg/export-postgraduate-courses.js", + "sourceFile": "plugins/heidelberg/export-postgraduate-courses.js" }, { - "site": "goproxy", - "name": "versions", - "description": "Published version tags for a Go module (newest first), optionally with publish times", + "site": "hf", + "name": "datasets", + "description": "Top Hugging Face datasets (downloads / likes / trending / freshness).", "access": "read", - "domain": "proxy.golang.org", + "domain": "huggingface.co", "strategy": "public", "browser": false, "args": [ { - "name": "module", + "name": "sort", "type": "string", - "required": true, - "positional": true, - "help": "Go module path (e.g. \"github.com/gin-gonic/gin\")" - }, - { - "name": "limit", - "type": "int", - "default": 30, + "default": "downloads", "required": false, - "help": "Max rows to return (1-200)" + "help": "Sort key: downloads, likes, trending, created_at, last_modified" }, { - "name": "with-time", - "type": "boolean", - "default": false, + "name": "search", + "type": "string", "required": false, - "help": "Fetch each version's publish time (one extra request per row)" - } - ], - "columns": [ - "rank", - "module", - "version", - "publishedAt", - "url" - ], - "type": "js", - "modulePath": "plugins/goproxy/versions.js", - "sourceFile": "plugins/goproxy/versions.js" - }, - { - "site": "hackernews", - "name": "ask", - "description": "Hacker News Ask HN posts", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ + "help": "Optional name/owner substring filter." + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of stories" + "help": "Max datasets (max 100; one API page)." } ], "columns": [ "rank", "id", - "title", - "score", "author", - "comments", + "downloads", + "likes", + "tags", + "lastModified", "url" ], "type": "js", - "modulePath": "plugins/hackernews/ask.js", - "sourceFile": "plugins/hackernews/ask.js" + "modulePath": "plugins/hf/datasets.js", + "sourceFile": "plugins/hf/datasets.js" }, { - "site": "hackernews", - "name": "best", - "description": "Hacker News best stories", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], + "site": "hf", + "name": "login", + "description": "Open hf login", + "access": "write", + "domain": "huggingface.co", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" + "status", + "logged_in", + "site", + "username", + "fullname", + "type", + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/hackernews/best.js", - "sourceFile": "plugins/hackernews/best.js" + "modulePath": "plugins/hf/auth.js", + "sourceFile": "plugins/hf/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "hackernews", - "name": "jobs", - "description": "Hacker News job postings", + "site": "hf", + "name": "models", + "description": "Top Hugging Face models (downloads / likes / trending / freshness).", "access": "read", - "domain": "news.ycombinator.com", + "domain": "huggingface.co", "strategy": "public", "browser": false, "args": [ + { + "name": "sort", + "type": "string", + "default": "downloads", + "required": false, + "help": "Sort key: downloads, likes, trending, created_at, last_modified" + }, + { + "name": "search", + "type": "string", + "required": false, + "help": "Optional name/owner substring filter (e.g. \"llama\", \"mistralai/\")" + }, + { + "name": "pipeline", + "type": "string", + "required": false, + "help": "Filter by pipeline tag (e.g. text-generation, image-classification)" + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of job postings" + "help": "Max models (max 100; one API page)." } ], "columns": [ "rank", "id", - "title", "author", + "pipelineTag", + "downloads", + "likes", + "tags", + "lastModified", "url" ], "type": "js", - "modulePath": "plugins/hackernews/jobs.js", - "sourceFile": "plugins/hackernews/jobs.js" + "modulePath": "plugins/hf/models.js", + "sourceFile": "plugins/hf/models.js" }, { - "site": "hackernews", - "name": "new", - "description": "Hacker News newest stories", + "site": "hf", + "name": "paper", + "description": "Hugging Face paper detail by arXiv id (full title / summary / authors / AI keywords)", "access": "read", - "domain": "news.ycombinator.com", + "domain": "huggingface.co", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "arXiv id (e.g. \"1706.03762\") — same value HF uses to mirror the paper" } ], "columns": [ - "rank", "id", "title", - "score", - "author", - "comments", + "authors", + "publishedAt", + "upvotes", + "aiKeywords", + "summary", + "aiSummary", "url" ], "type": "js", - "modulePath": "plugins/hackernews/new.js", - "sourceFile": "plugins/hackernews/new.js" + "modulePath": "plugins/hf/paper.js", + "sourceFile": "plugins/hf/paper.js" }, { - "site": "hackernews", - "name": "read", - "description": "Read a Hacker News story and its comment tree", + "site": "hf", + "name": "spaces", + "description": "Top Hugging Face Spaces (likes / created_at / last_modified).", "access": "read", - "domain": "news.ycombinator.com", + "domain": "huggingface.co", "strategy": "public", "browser": false, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "HN item ID (e.g. 39847301)" - }, - { - "name": "limit", - "type": "int", - "default": 25, + "name": "sort", + "type": "string", + "default": "likes", "required": false, - "help": "Max top-level comments" + "help": "Sort key: likes, created_at, last_modified" }, { - "name": "depth", - "type": "int", - "default": 2, + "name": "search", + "type": "string", "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + "help": "Optional name/owner substring filter (e.g. \"stability\", \"openai/\")" }, { - "name": "replies", - "type": "int", - "default": 5, + "name": "sdk", + "type": "string", "required": false, - "help": "Max replies shown per comment at each level" + "help": "Filter by Space SDK: gradio / streamlit / docker / static" }, { - "name": "max-length", + "name": "limit", "type": "int", - "default": 2000, + "default": 20, "required": false, - "help": "Max characters per comment body (min 100)" + "help": "Max spaces (max 100; one API page)." } ], "columns": [ - "type", + "rank", + "id", "author", - "score", - "text" + "sdk", + "likes", + "tags", + "lastModified", + "url" ], "type": "js", - "modulePath": "plugins/hackernews/read.js", - "sourceFile": "plugins/hackernews/read.js" + "modulePath": "plugins/hf/spaces.js", + "sourceFile": "plugins/hf/spaces.js" }, { - "site": "hackernews", - "name": "search", - "description": "Search Hacker News stories", + "site": "hf", + "name": "top", + "description": "Top upvoted Hugging Face papers", "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of results" + "help": "Number of papers" }, { - "name": "sort", + "name": "all", + "type": "bool", + "default": false, + "required": false, + "help": "Return all papers (ignore limit)" + }, + { + "name": "date", "type": "str", - "default": "relevance", "required": false, - "help": "Sort by relevance or date", + "help": "Date (YYYY-MM-DD), defaults to most recent" + }, + { + "name": "period", + "type": "str", + "default": "daily", + "required": false, + "help": "Time period: daily, weekly, or monthly", "choices": [ - "relevance", - "date" + "daily", + "weekly", + "monthly" ] } ], @@ -6899,112 +8620,235 @@ "rank", "id", "title", - "score", - "author", - "comments", - "url" + "upvotes", + "authors" ], - "tags": [ - "search" + "type": "js", + "modulePath": "plugins/hf/top.js", + "sourceFile": "plugins/hf/top.js" + }, + { + "site": "hf", + "name": "whoami", + "description": "Show the current logged-in hf account", + "access": "read", + "domain": "huggingface.co", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "username", + "fullname", + "type" ], "type": "js", - "modulePath": "plugins/hackernews/search.js", - "sourceFile": "plugins/hackernews/search.js" + "modulePath": "plugins/hf/auth.js", + "sourceFile": "plugins/hf/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "hackernews", - "name": "show", - "description": "Hacker News Show HN posts", + "site": "hft", + "name": "export-postgraduate-courses", + "description": "Export HFT Stuttgart postgraduate programs using the official public programme pages.", "access": "read", - "domain": "news.ycombinator.com", + "example": "webcmd hft export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.hft-stuttgart.de", "strategy": "public", "browser": false, "args": [ { - "name": "limit", + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", "type": "int", - "default": 20, "required": false, - "help": "Number of stories" + "help": "Positive maximum number of programs after filtering and deduplication" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/hackernews/show.js", - "sourceFile": "plugins/hackernews/show.js" + "modulePath": "plugins/hft/export-postgraduate-courses.js", + "sourceFile": "plugins/hft/export-postgraduate-courses.js" }, { - "site": "hackernews", - "name": "top", - "description": "Hacker News top stories", + "site": "homebrew", + "name": "cask", + "description": "Fetch a Homebrew cask's metadata (version, homepage, deprecation, download URL)", "access": "read", - "domain": "news.ycombinator.com", + "domain": "formulae.brew.sh", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" + "name": "token", + "type": "str", + "required": true, + "positional": true, + "help": "Cask token (e.g. \"firefox\", \"visual-studio-code\", \"google-chrome\")" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", + "cask", + "tap", + "name", + "version", + "description", + "homepage", + "deprecated", + "disabled", + "download", "url" ], "type": "js", - "modulePath": "plugins/hackernews/top.js", - "sourceFile": "plugins/hackernews/top.js" + "modulePath": "plugins/homebrew/cask.js", + "sourceFile": "plugins/homebrew/cask.js" }, { - "site": "hackernews", - "name": "user", - "description": "Hacker News user profile", + "site": "homebrew", + "name": "formula", + "description": "Fetch a Homebrew formula's metadata (version, license, deps, deprecation, source)", "access": "read", - "domain": "news.ycombinator.com", + "domain": "formulae.brew.sh", "strategy": "public", "browser": false, "args": [ { - "name": "username", + "name": "name", "type": "str", "required": true, "positional": true, - "help": "HN username" + "help": "Formula name (e.g. \"wget\", \"gcc@13\", \"imagemagick\")" } ], "columns": [ - "username", - "karma", - "created", - "about" + "formula", + "tap", + "version", + "license", + "description", + "homepage", + "dependencies", + "deprecated", + "disabled", + "source", + "url" ], "type": "js", - "modulePath": "plugins/hackernews/user.js", - "sourceFile": "plugins/hackernews/user.js" + "modulePath": "plugins/homebrew/formula.js", + "sourceFile": "plugins/homebrew/formula.js" + }, + { + "site": "homebrew", + "name": "popular", + "description": "List most-installed Homebrew formulae or casks (Homebrew's analytics ranking)", + "access": "read", + "domain": "formulae.brew.sh", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "type", + "type": "str", + "default": "formula", + "required": false, + "help": "Package type (formula / cask)" + }, + { + "name": "window", + "type": "str", + "default": "30d", + "required": false, + "help": "Time window (30d / 90d / 365d)" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max rows (1-500)" + } + ], + "columns": [ + "rank", + "token", + "type", + "installs", + "percent", + "window", + "url" + ], + "type": "js", + "modulePath": "plugins/homebrew/popular.js", + "sourceFile": "plugins/homebrew/popular.js" }, { - "site": "heidelberg", + "site": "iit", "name": "export-postgraduate-courses", - "description": "Export Heidelberg University non-bachelor/postgraduate programs using the official study finder.", + "description": "Export Illinois Tech postgraduate programs using official public sources.", "access": "read", - "example": "webcmd heidelberg export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.uni-heidelberg.de", + "example": "webcmd iit export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.iit.edu", "strategy": "public", "browser": false, "args": [ @@ -7077,486 +8921,311 @@ "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/heidelberg/export-postgraduate-courses.js", - "sourceFile": "plugins/heidelberg/export-postgraduate-courses.js" + "modulePath": "plugins/iit/export-postgraduate-courses.js", + "sourceFile": "plugins/iit/export-postgraduate-courses.js" }, { - "site": "hf", - "name": "datasets", - "description": "Top Hugging Face datasets (downloads / likes / trending / freshness).", + "site": "imdb", + "name": "person", + "description": "Get actor or director info", "access": "read", - "domain": "huggingface.co", + "domain": "www.imdb.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "sort", - "type": "string", - "default": "downloads", - "required": false, - "help": "Sort key: downloads, likes, trending, created_at, last_modified" - }, - { - "name": "search", - "type": "string", - "required": false, - "help": "Optional name/owner substring filter." + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "IMDb person ID (nm0634240) or URL" }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Max datasets (max 100; one API page)." + "help": "Max filmography entries" } ], "columns": [ - "rank", - "id", - "author", - "downloads", - "likes", - "tags", - "lastModified", - "url" - ], - "type": "js", - "modulePath": "plugins/hf/datasets.js", - "sourceFile": "plugins/hf/datasets.js" - }, - { - "site": "hf", - "name": "login", - "description": "Open hf login", - "access": "write", - "domain": "huggingface.co", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "username", - "fullname", - "type", - "action", - "verify_command" + "field", + "value" ], "type": "js", - "modulePath": "plugins/hf/auth.js", - "sourceFile": "plugins/hf/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/imdb/person.js", + "sourceFile": "plugins/imdb/person.js" }, { - "site": "hf", - "name": "models", - "description": "Top Hugging Face models (downloads / likes / trending / freshness).", + "site": "imdb", + "name": "reviews", + "description": "Get user reviews for a movie or TV show", "access": "read", - "domain": "huggingface.co", + "domain": "www.imdb.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "sort", - "type": "string", - "default": "downloads", - "required": false, - "help": "Sort key: downloads, likes, trending, created_at, last_modified" - }, - { - "name": "search", - "type": "string", - "required": false, - "help": "Optional name/owner substring filter (e.g. \"llama\", \"mistralai/\")" - }, - { - "name": "pipeline", - "type": "string", - "required": false, - "help": "Filter by pipeline tag (e.g. text-generation, image-classification)" + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "IMDb title ID (tt1375666) or URL" }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Max models (max 100; one API page)." + "help": "Number of reviews" } ], "columns": [ "rank", - "id", + "title", + "rating", "author", - "pipelineTag", - "downloads", - "likes", - "tags", - "lastModified", - "url" + "date", + "text" ], "type": "js", - "modulePath": "plugins/hf/models.js", - "sourceFile": "plugins/hf/models.js" + "modulePath": "plugins/imdb/reviews.js", + "sourceFile": "plugins/imdb/reviews.js" }, { - "site": "hf", - "name": "paper", - "description": "Hugging Face paper detail by arXiv id (full title / summary / authors / AI keywords)", + "site": "imdb", + "name": "search", + "description": "Search IMDb for movies, TV shows, and people", "access": "read", - "domain": "huggingface.co", + "domain": "www.imdb.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "id", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "arXiv id (e.g. \"1706.03762\") — same value HF uses to mirror the paper" - } - ], - "columns": [ - "id", - "title", - "authors", - "publishedAt", - "upvotes", - "aiKeywords", - "summary", - "aiSummary", - "url" - ], - "type": "js", - "modulePath": "plugins/hf/paper.js", - "sourceFile": "plugins/hf/paper.js" - }, - { - "site": "hf", - "name": "spaces", - "description": "Top Hugging Face Spaces (likes / created_at / last_modified).", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "sort", - "type": "string", - "default": "likes", - "required": false, - "help": "Sort key: likes, created_at, last_modified" - }, - { - "name": "search", - "type": "string", - "required": false, - "help": "Optional name/owner substring filter (e.g. \"stability\", \"openai/\")" - }, - { - "name": "sdk", - "type": "string", - "required": false, - "help": "Filter by Space SDK: gradio / streamlit / docker / static" + "help": "Search query" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max spaces (max 100; one API page)." + "help": "Number of results" } ], "columns": [ "rank", "id", - "author", - "sdk", - "likes", - "tags", - "lastModified", + "title", + "year", + "type", "url" ], - "type": "js", - "modulePath": "plugins/hf/spaces.js", - "sourceFile": "plugins/hf/spaces.js" - }, - { - "site": "hf", - "name": "top", - "description": "Top upvoted Hugging Face papers", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of papers" - }, - { - "name": "all", - "type": "bool", - "default": false, - "required": false, - "help": "Return all papers (ignore limit)" - }, - { - "name": "date", - "type": "str", - "required": false, - "help": "Date (YYYY-MM-DD), defaults to most recent" - }, - { - "name": "period", - "type": "str", - "default": "daily", - "required": false, - "help": "Time period: daily, weekly, or monthly", - "choices": [ - "daily", - "weekly", - "monthly" - ] - } - ], - "columns": [ - "rank", - "id", - "title", - "upvotes", - "authors" + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/hf/top.js", - "sourceFile": "plugins/hf/top.js" + "modulePath": "plugins/imdb/search.js", + "sourceFile": "plugins/imdb/search.js" }, { - "site": "hf", - "name": "whoami", - "description": "Show the current logged-in hf account", + "site": "imdb", + "name": "title", + "description": "Get movie or TV show details", "access": "read", - "domain": "huggingface.co", - "strategy": "cookie", + "domain": "www.imdb.com", + "strategy": "public", "browser": true, - "args": [], + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "IMDb title ID (tt1375666) or URL" + } + ], "columns": [ - "logged_in", - "site", - "username", - "fullname", - "type" + "field", + "value" ], "type": "js", - "modulePath": "plugins/hf/auth.js", - "sourceFile": "plugins/hf/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/imdb/title.js", + "sourceFile": "plugins/imdb/title.js" }, { - "site": "hft", - "name": "export-postgraduate-courses", - "description": "Export HFT Stuttgart postgraduate programs using the official public programme pages.", + "site": "imdb", + "name": "top", + "description": "IMDb Top 250 Movies", "access": "read", - "example": "webcmd hft export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.hft-stuttgart.de", + "domain": "www.imdb.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", + "name": "limit", "type": "int", + "default": 20, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Number of results" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "rank", + "title", + "rating", + "votes", + "genre", + "url" ], "type": "js", - "modulePath": "plugins/hft/export-postgraduate-courses.js", - "sourceFile": "plugins/hft/export-postgraduate-courses.js" + "modulePath": "plugins/imdb/top.js", + "sourceFile": "plugins/imdb/top.js" }, { - "site": "homebrew", - "name": "cask", - "description": "Fetch a Homebrew cask's metadata (version, homepage, deprecation, download URL)", + "site": "imdb", + "name": "trending", + "description": "IMDb Most Popular Movies", "access": "read", - "domain": "formulae.brew.sh", + "domain": "www.imdb.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "token", - "type": "str", - "required": true, - "positional": true, - "help": "Cask token (e.g. \"firefox\", \"visual-studio-code\", \"google-chrome\")" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" } ], "columns": [ - "cask", - "tap", - "name", - "version", - "description", - "homepage", - "deprecated", - "disabled", - "download", + "rank", + "title", + "rating", + "genre", "url" ], "type": "js", - "modulePath": "plugins/homebrew/cask.js", - "sourceFile": "plugins/homebrew/cask.js" + "modulePath": "plugins/imdb/trending.js", + "sourceFile": "plugins/imdb/trending.js" }, { - "site": "homebrew", - "name": "formula", - "description": "Fetch a Homebrew formula's metadata (version, license, deps, deprecation, source)", + "site": "indeed", + "name": "job", + "aliases": [ + "detail", + "view" + ], + "description": "Read the full Indeed job posting by jk (job key)", "access": "read", - "domain": "formulae.brew.sh", - "strategy": "public", - "browser": false, + "domain": "www.indeed.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "name", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Formula name (e.g. \"wget\", \"gcc@13\", \"imagemagick\")" + "help": "Job key (16-char hex from `indeed search`, e.g. \"dccc07ac5a6a3683\")" } ], "columns": [ - "formula", - "tap", - "version", - "license", + "id", + "title", + "company", + "location", + "salary", + "job_type", "description", - "homepage", - "dependencies", - "deprecated", - "disabled", - "source", "url" ], "type": "js", - "modulePath": "plugins/homebrew/formula.js", - "sourceFile": "plugins/homebrew/formula.js" + "modulePath": "plugins/indeed/job.js", + "sourceFile": "plugins/indeed/job.js", + "navigateBefore": false }, { - "site": "homebrew", - "name": "popular", - "description": "List most-installed Homebrew formulae or casks (Homebrew's analytics ranking)", + "site": "indeed", + "name": "search", + "description": "Indeed keyword job search (rendered DOM via browser session, US site)", "access": "read", - "domain": "formulae.brew.sh", - "strategy": "public", - "browser": false, + "domain": "www.indeed.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "type", + "name": "query", "type": "str", - "default": "formula", + "required": true, + "positional": true, + "help": "Job keyword (title / skill / company)" + }, + { + "name": "location", + "type": "string", + "default": "", "required": false, - "help": "Package type (formula / cask)" + "help": "Location filter (e.g. \"remote\", \"New York, NY\", \"San Francisco\")" }, { - "name": "window", - "type": "str", - "default": "30d", + "name": "fromage", + "type": "string", + "default": "", "required": false, - "help": "Time window (30d / 90d / 365d)" + "help": "Recency filter, days back: 1 / 3 / 7 / 14" + }, + { + "name": "sort", + "type": "string", + "default": "relevance", + "required": false, + "help": "Sort order: relevance | date" + }, + { + "name": "start", + "type": "int", + "default": 0, + "required": false, + "help": "Pagination offset (multiple of 10, 0-based)" }, { "name": "limit", "type": "int", - "default": 30, + "default": 15, "required": false, - "help": "Max rows (1-500)" + "help": "Max rows to return (1-25, capped at one page)" } ], "columns": [ - "rank", - "token", - "type", - "installs", - "percent", - "window", + "rank", + "id", + "title", + "company", + "location", + "salary", + "tags", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/homebrew/popular.js", - "sourceFile": "plugins/homebrew/popular.js" + "modulePath": "plugins/indeed/search.js", + "sourceFile": "plugins/indeed/search.js", + "navigateBefore": false }, { - "site": "iit", + "site": "jhu", "name": "export-postgraduate-courses", - "description": "Export Illinois Tech postgraduate programs using official public sources.", + "description": "Export Johns Hopkins University postgraduate programs using the official Academic Catalogue.", "access": "read", - "example": "webcmd iit export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.iit.edu", + "example": "webcmd jhu export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "e-catalogue.jhu.edu", "strategy": "public", "browser": false, "args": [ @@ -7629,592 +9298,478 @@ "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/iit/export-postgraduate-courses.js", - "sourceFile": "plugins/iit/export-postgraduate-courses.js" + "modulePath": "plugins/jhu/export-postgraduate-courses.js", + "sourceFile": "plugins/jhu/export-postgraduate-courses.js" }, { - "site": "imdb", - "name": "person", - "description": "Get actor or director info", + "site": "jira", + "name": "attachments", + "description": "Jira issue attachment metadata", "access": "read", - "domain": "www.imdb.com", + "domain": "atlassian.net", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "id", + "name": "key", "type": "str", "required": true, "positional": true, - "help": "IMDb person ID (nm0634240) or URL" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max filmography entries" + "help": "Jira issue key, e.g. PROJ-123" } ], "columns": [ - "field", - "value" + "id", + "filename", + "mimeType", + "size", + "url" ], "type": "js", - "modulePath": "plugins/imdb/person.js", - "sourceFile": "plugins/imdb/person.js" + "modulePath": "plugins/jira/attachments.js", + "sourceFile": "plugins/jira/attachments.js" }, { - "site": "imdb", - "name": "reviews", - "description": "Get user reviews for a movie or TV show", + "site": "jira", + "name": "comments", + "description": "Jira issue comments as Markdown", "access": "read", - "domain": "www.imdb.com", + "domain": "atlassian.net", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "id", + "name": "key", "type": "str", "required": true, "positional": true, - "help": "IMDb title ID (tt1375666) or URL" + "help": "Jira issue key, e.g. PROJ-123" }, { "name": "limit", "type": "int", - "default": 10, + "default": 50, "required": false, - "help": "Number of reviews" + "help": "Max comments to return (1-100)" } ], "columns": [ - "rank", - "title", - "rating", + "id", "author", - "date", - "text" + "created", + "updated", + "markdown" ], "type": "js", - "modulePath": "plugins/imdb/reviews.js", - "sourceFile": "plugins/imdb/reviews.js" + "modulePath": "plugins/jira/comments.js", + "sourceFile": "plugins/jira/comments.js" }, { - "site": "imdb", - "name": "search", - "description": "Search IMDb for movies, TV shows, and people", + "site": "jira", + "name": "issue", + "description": "Jira issue detail normalized for agents (description, comments, attachments, links)", "access": "read", - "domain": "www.imdb.com", + "domain": "atlassian.net", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "query", + "name": "key", "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "Jira issue key, e.g. PROJ-123" }, { - "name": "limit", + "name": "comments-limit", "type": "int", - "default": 20, + "default": 100, "required": false, - "help": "Number of results" + "help": "Max comments to include (1-100)" } ], "columns": [ - "rank", - "id", - "title", - "year", - "type", + "key", + "summary", + "issueType", + "status", + "priority", + "assignee", + "updated", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/imdb/search.js", - "sourceFile": "plugins/imdb/search.js" + "modulePath": "plugins/jira/issue.js", + "sourceFile": "plugins/jira/issue.js" }, { - "site": "imdb", - "name": "title", - "description": "Get movie or TV show details", + "site": "jira", + "name": "links", + "description": "Jira issue links", "access": "read", - "domain": "www.imdb.com", + "domain": "atlassian.net", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "id", + "name": "key", "type": "str", "required": true, "positional": true, - "help": "IMDb title ID (tt1375666) or URL" + "help": "Jira issue key, e.g. PROJ-123" } ], "columns": [ - "field", - "value" + "key", + "type", + "direction" ], "type": "js", - "modulePath": "plugins/imdb/title.js", - "sourceFile": "plugins/imdb/title.js" + "modulePath": "plugins/jira/links.js", + "sourceFile": "plugins/jira/links.js" }, { - "site": "imdb", - "name": "top", - "description": "IMDb Top 250 Movies", + "site": "jira", + "name": "search", + "description": "Search Jira issues with JQL", "access": "read", - "domain": "www.imdb.com", + "domain": "atlassian.net", "strategy": "public", - "browser": true, + "browser": false, "args": [ + { + "name": "jql", + "type": "str", + "required": true, + "positional": true, + "help": "JQL query, e.g. \"project = PROJ order by updated desc\"" + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of results" + "help": "Max issues to return (1-100)" } ], "columns": [ - "rank", - "title", - "rating", - "votes", - "genre", + "key", + "summary", + "issueType", + "status", + "priority", + "assignee", + "updated", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/imdb/top.js", - "sourceFile": "plugins/imdb/top.js" + "modulePath": "plugins/jira/search.js", + "sourceFile": "plugins/jira/search.js" }, { - "site": "imdb", - "name": "trending", - "description": "IMDb Most Popular Movies", + "site": "lesswrong", + "name": "comments", + "description": "Top comments on a post", "access": "read", - "domain": "www.imdb.com", + "domain": "www.lesswrong.com", "strategy": "public", - "browser": true, + "browser": false, "args": [ + { + "name": "url-or-id", + "type": "string", + "required": true, + "positional": true, + "help": "Post URL or LessWrong post ID" + }, { "name": "limit", "type": "int", - "default": 20, + "default": 5, "required": false, - "help": "Number of results" + "help": "Number of comments" } ], "columns": [ "rank", - "title", - "rating", - "genre", - "url" + "score", + "author", + "text" ], "type": "js", - "modulePath": "plugins/imdb/trending.js", - "sourceFile": "plugins/imdb/trending.js" + "modulePath": "plugins/lesswrong/comments.js", + "sourceFile": "plugins/lesswrong/comments.js" }, { - "site": "indeed", - "name": "job", - "aliases": [ - "detail", - "view" - ], - "description": "Read the full Indeed job posting by jk (job key)", + "site": "lesswrong", + "name": "curated", + "description": "Curated editor's picks", "access": "read", - "domain": "www.indeed.com", - "strategy": "cookie", - "browser": true, + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Job key (16-char hex from `indeed search`, e.g. \"dccc07ac5a6a3683\")" + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" } ], - "columns": [ - "id", - "title", - "company", - "location", - "salary", - "job_type", - "description", + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", "url" ], "type": "js", - "modulePath": "plugins/indeed/job.js", - "sourceFile": "plugins/indeed/job.js", - "navigateBefore": false + "modulePath": "plugins/lesswrong/curated.js", + "sourceFile": "plugins/lesswrong/curated.js" }, { - "site": "indeed", - "name": "search", - "description": "Indeed keyword job search (rendered DOM via browser session, US site)", + "site": "lesswrong", + "name": "frontpage", + "description": "Algorithmic frontpage", "access": "read", - "domain": "www.indeed.com", - "strategy": "cookie", - "browser": true, + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Job keyword (title / skill / company)" - }, - { - "name": "location", - "type": "string", - "default": "", - "required": false, - "help": "Location filter (e.g. \"remote\", \"New York, NY\", \"San Francisco\")" - }, - { - "name": "fromage", - "type": "string", - "default": "", - "required": false, - "help": "Recency filter, days back: 1 / 3 / 7 / 14" - }, - { - "name": "sort", - "type": "string", - "default": "relevance", - "required": false, - "help": "Sort order: relevance | date" - }, - { - "name": "start", - "type": "int", - "default": 0, - "required": false, - "help": "Pagination offset (multiple of 10, 0-based)" - }, { "name": "limit", "type": "int", - "default": 15, + "default": 10, "required": false, - "help": "Max rows to return (1-25, capped at one page)" + "help": "Number of results" } ], "columns": [ "rank", - "id", "title", - "company", - "location", - "salary", - "tags", + "author", + "karma", + "comments", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/indeed/search.js", - "sourceFile": "plugins/indeed/search.js", - "navigateBefore": false + "modulePath": "plugins/lesswrong/frontpage.js", + "sourceFile": "plugins/lesswrong/frontpage.js" }, { - "site": "jhu", - "name": "export-postgraduate-courses", - "description": "Export Johns Hopkins University postgraduate programs using the official Academic Catalogue.", + "site": "lesswrong", + "name": "new", + "description": "Latest posts", "access": "read", - "example": "webcmd jhu export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "e-catalogue.jhu.edu", + "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", + "name": "limit", "type": "int", + "default": 10, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Number of results" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "rank", + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/jhu/export-postgraduate-courses.js", - "sourceFile": "plugins/jhu/export-postgraduate-courses.js" + "modulePath": "plugins/lesswrong/new.js", + "sourceFile": "plugins/lesswrong/new.js" }, { - "site": "jira", - "name": "attachments", - "description": "Jira issue attachment metadata", + "site": "lesswrong", + "name": "read", + "description": "Read full post by URL or ID", "access": "read", - "domain": "atlassian.net", + "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ { - "name": "key", - "type": "str", + "name": "url-or-id", + "type": "string", "required": true, "positional": true, - "help": "Jira issue key, e.g. PROJ-123" + "help": "Post URL or LessWrong post ID" } ], "columns": [ - "id", - "filename", - "mimeType", - "size", + "title", + "author", + "karma", + "comments", + "tags", + "content", "url" ], "type": "js", - "modulePath": "plugins/jira/attachments.js", - "sourceFile": "plugins/jira/attachments.js" + "modulePath": "plugins/lesswrong/read.js", + "sourceFile": "plugins/lesswrong/read.js" }, { - "site": "jira", - "name": "comments", - "description": "Jira issue comments as Markdown", + "site": "lesswrong", + "name": "sequences", + "description": "List post collections", "access": "read", - "domain": "atlassian.net", + "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Jira issue key, e.g. PROJ-123" - }, { "name": "limit", "type": "int", - "default": 50, + "default": 10, "required": false, - "help": "Max comments to return (1-100)" + "help": "Number of results" } ], "columns": [ - "id", - "author", - "created", - "updated", - "markdown" + "rank", + "title", + "author" ], "type": "js", - "modulePath": "plugins/jira/comments.js", - "sourceFile": "plugins/jira/comments.js" + "modulePath": "plugins/lesswrong/sequences.js", + "sourceFile": "plugins/lesswrong/sequences.js" }, { - "site": "jira", - "name": "issue", - "description": "Jira issue detail normalized for agents (description, comments, attachments, links)", + "site": "lesswrong", + "name": "shortform", + "description": "Quick takes / shortform posts", "access": "read", - "domain": "atlassian.net", + "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Jira issue key, e.g. PROJ-123" - }, - { - "name": "comments-limit", + "name": "limit", "type": "int", - "default": 100, + "default": 10, "required": false, - "help": "Max comments to include (1-100)" + "help": "Number of results" } ], "columns": [ - "key", - "summary", - "issueType", - "status", - "priority", - "assignee", - "updated", + "rank", + "title", + "author", + "karma", + "comments", "url" ], "type": "js", - "modulePath": "plugins/jira/issue.js", - "sourceFile": "plugins/jira/issue.js" + "modulePath": "plugins/lesswrong/shortform.js", + "sourceFile": "plugins/lesswrong/shortform.js" }, { - "site": "jira", - "name": "links", - "description": "Jira issue links", + "site": "lesswrong", + "name": "tag", + "description": "Posts by tag", "access": "read", - "domain": "atlassian.net", + "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ { - "name": "key", - "type": "str", + "name": "tag", + "type": "string", "required": true, "positional": true, - "help": "Jira issue key, e.g. PROJ-123" + "help": "Tag slug or name" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" } ], "columns": [ - "key", - "type", - "direction" + "rank", + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/jira/links.js", - "sourceFile": "plugins/jira/links.js" + "modulePath": "plugins/lesswrong/tag.js", + "sourceFile": "plugins/lesswrong/tag.js" }, { - "site": "jira", - "name": "search", - "description": "Search Jira issues with JQL", + "site": "lesswrong", + "name": "tags", + "description": "List popular tags", "access": "read", - "domain": "atlassian.net", + "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ - { - "name": "jql", - "type": "str", - "required": true, - "positional": true, - "help": "JQL query, e.g. \"project = PROJ order by updated desc\"" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max issues to return (1-100)" + "help": "Number of results" } ], "columns": [ - "key", - "summary", - "issueType", - "status", - "priority", - "assignee", - "updated", - "url" - ], - "tags": [ - "search" + "rank", + "name", + "posts" ], "type": "js", - "modulePath": "plugins/jira/search.js", - "sourceFile": "plugins/jira/search.js" + "modulePath": "plugins/lesswrong/tags.js", + "sourceFile": "plugins/lesswrong/tags.js" }, { "site": "lesswrong", - "name": "comments", - "description": "Top comments on a post", + "name": "top", + "description": "Top all-time", "access": "read", "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ - { - "name": "url-or-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post URL or LessWrong post ID" - }, { "name": "limit", "type": "int", - "default": 5, + "default": 10, "required": false, - "help": "Number of comments" + "help": "Number of results" } ], "columns": [ "rank", - "score", + "title", "author", - "text" + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/lesswrong/comments.js", - "sourceFile": "plugins/lesswrong/comments.js" + "modulePath": "plugins/lesswrong/top.js", + "sourceFile": "plugins/lesswrong/top.js" }, { "site": "lesswrong", - "name": "curated", - "description": "Curated editor's picks", + "name": "top-month", + "description": "Top this month", "access": "read", "domain": "www.lesswrong.com", "strategy": "public", @@ -8237,13 +9792,13 @@ "url" ], "type": "js", - "modulePath": "plugins/lesswrong/curated.js", - "sourceFile": "plugins/lesswrong/curated.js" + "modulePath": "plugins/lesswrong/top-month.js", + "sourceFile": "plugins/lesswrong/top-month.js" }, { "site": "lesswrong", - "name": "frontpage", - "description": "Algorithmic frontpage", + "name": "top-week", + "description": "Top this week", "access": "read", "domain": "www.lesswrong.com", "strategy": "public", @@ -8266,13 +9821,13 @@ "url" ], "type": "js", - "modulePath": "plugins/lesswrong/frontpage.js", - "sourceFile": "plugins/lesswrong/frontpage.js" + "modulePath": "plugins/lesswrong/top-week.js", + "sourceFile": "plugins/lesswrong/top-week.js" }, { "site": "lesswrong", - "name": "new", - "description": "Latest posts", + "name": "top-year", + "description": "Top this year", "access": "read", "domain": "www.lesswrong.com", "strategy": "public", @@ -8295,48 +9850,50 @@ "url" ], "type": "js", - "modulePath": "plugins/lesswrong/new.js", - "sourceFile": "plugins/lesswrong/new.js" + "modulePath": "plugins/lesswrong/top-year.js", + "sourceFile": "plugins/lesswrong/top-year.js" }, { "site": "lesswrong", - "name": "read", - "description": "Read full post by URL or ID", + "name": "user", + "description": "User profile", "access": "read", "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ { - "name": "url-or-id", + "name": "username", "type": "string", "required": true, "positional": true, - "help": "Post URL or LessWrong post ID" + "help": "LessWrong username or slug" } ], "columns": [ - "title", - "author", - "karma", - "comments", - "tags", - "content", - "url" + "field", + "value" ], "type": "js", - "modulePath": "plugins/lesswrong/read.js", - "sourceFile": "plugins/lesswrong/read.js" + "modulePath": "plugins/lesswrong/user.js", + "sourceFile": "plugins/lesswrong/user.js" }, { "site": "lesswrong", - "name": "sequences", - "description": "List post collections", + "name": "user-posts", + "description": "List a user's posts", "access": "read", "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "LessWrong username or slug" + }, { "name": "limit", "type": "int", @@ -8348,422 +9905,678 @@ "columns": [ "rank", "title", - "author" + "karma", + "comments", + "date", + "url" ], "type": "js", - "modulePath": "plugins/lesswrong/sequences.js", - "sourceFile": "plugins/lesswrong/sequences.js" + "modulePath": "plugins/lesswrong/user-posts.js", + "sourceFile": "plugins/lesswrong/user-posts.js" }, { - "site": "lesswrong", - "name": "shortform", - "description": "Quick takes / shortform posts", + "site": "lichess", + "name": "top", + "description": "Top-N Lichess leaderboard for a perf type (bullet/blitz/rapid/classical/...)", "access": "read", - "domain": "www.lesswrong.com", + "domain": "lichess.org", "strategy": "public", "browser": false, "args": [ + { + "name": "perf", + "type": "str", + "required": true, + "positional": true, + "help": "Perf type (bullet, blitz, rapid, classical, ultraBullet, chess960, ...)" + }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Number of results" + "help": "Top-N rows (1-200)" } ], "columns": [ "rank", + "username", + "id", "title", - "author", - "karma", - "comments", + "rating", + "progress", + "patron", + "url" + ], + "type": "js", + "modulePath": "plugins/lichess/top.js", + "sourceFile": "plugins/lichess/top.js" + }, + { + "site": "lichess", + "name": "user", + "description": "Fetch a Lichess player profile by username (rating, perfs, counts)", + "access": "read", + "domain": "lichess.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Lichess username (case-insensitive)" + } + ], + "columns": [ + "username", + "id", + "title", + "patron", + "online", + "tosViolation", + "createdAt", + "seenAt", + "gamesAll", + "gamesWin", + "gamesLoss", + "gamesDraw", + "topPerfName", + "topPerfRating", + "topPerfGames", + "fideRating", + "country", + "bio", + "url" + ], + "type": "js", + "modulePath": "plugins/lichess/user.js", + "sourceFile": "plugins/lichess/user.js" + }, + { + "site": "linkedin", + "name": "company", + "description": "Read a LinkedIn company page: industry, size, HQ, founded, website, followers, and about text", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "company", + "type": "string", + "required": true, + "positional": true, + "help": "Company universal name, /company/ path, or full URL" + } + ], + "columns": [ + "name", + "industry", + "size", + "headquarters", + "founded", + "website", + "specialties", + "followers", + "about", "url" ], "type": "js", - "modulePath": "plugins/lesswrong/shortform.js", - "sourceFile": "plugins/lesswrong/shortform.js" + "modulePath": "plugins/linkedin/company.js", + "sourceFile": "plugins/linkedin/company.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "lesswrong", - "name": "tag", - "description": "Posts by tag", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "site": "linkedin", + "name": "connect", + "description": "Fail-closed LinkedIn connection request sender that verifies the exact profile before optionally sending a note", + "access": "write", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "tag", + "name": "profile-url", "type": "string", "required": true, "positional": true, - "help": "Tag slug or name" + "help": "Exact LinkedIn profile URL to open and verify" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "expected-name", + "type": "string", + "required": true, + "help": "Expected visible profile name" + }, + { + "name": "note", + "type": "string", + "default": "", "required": false, - "help": "Number of results" + "help": "Optional connection note, max 300 chars" + }, + { + "name": "send", + "type": "bool", + "default": false, + "required": false, + "help": "Actually click Send. Default is dry-run verification only." } ], "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" + "status", + "recipient", + "reason", + "profile_url", + "note_chars", + "connectable", + "delivery_verified", + "matched_invitation_name", + "matched_invitation_url", + "actualValue", + "blockReason", + "expectedValue", + "observedUrl", + "safety" ], "type": "js", - "modulePath": "plugins/lesswrong/tag.js", - "sourceFile": "plugins/lesswrong/tag.js" + "modulePath": "plugins/linkedin/connect.js", + "sourceFile": "plugins/linkedin/connect.js", + "navigateBefore": true }, { - "site": "lesswrong", - "name": "tags", - "description": "List popular tags", + "site": "linkedin", + "name": "connections", + "description": "List your LinkedIn first-degree connections with names, headlines, and profile URLs", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of results" + "help": "Number of connections to return (max 500)" } ], "columns": [ "rank", "name", - "posts" + "occupation", + "public_id", + "connected_at", + "url" ], "type": "js", - "modulePath": "plugins/lesswrong/tags.js", - "sourceFile": "plugins/lesswrong/tags.js" + "modulePath": "plugins/linkedin/connections.js", + "sourceFile": "plugins/linkedin/connections.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "lesswrong", - "name": "top", - "description": "Top all-time", + "site": "linkedin", + "name": "inbox", + "description": "List LinkedIn messaging inbox conversations and unread messages", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 40, "required": false, - "help": "Number of results" + "help": "Maximum conversations to return (1-100)" + }, + { + "name": "unread-only", + "type": "bool", + "default": false, + "required": false, + "help": "Return only conversations with unread messages" } ], "columns": [ "rank", - "title", - "author", - "karma", - "comments", - "url" + "thread_url", + "thread_id", + "person_name", + "last_message_preview", + "unread", + "counterparty_type", + "category", + "timestamp" ], "type": "js", - "modulePath": "plugins/lesswrong/top.js", - "sourceFile": "plugins/lesswrong/top.js" + "modulePath": "plugins/linkedin/inbox.js", + "sourceFile": "plugins/linkedin/inbox.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "lesswrong", - "name": "top-month", - "description": "Top this month", + "site": "linkedin", + "name": "job-detail", + "description": "Read one LinkedIn job page with description, apply URL, workplace type, applicants, and company metadata", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" + "name": "job-url", + "type": "string", + "required": true, + "positional": true, + "help": "Exact LinkedIn job URL, e.g. https://www.linkedin.com/jobs/view/123/" } ], "columns": [ - "rank", "title", - "author", - "karma", - "comments", - "url" + "company", + "location", + "workplace_type", + "job_type", + "applicants", + "listed", + "apply_url", + "company_url", + "url", + "description" ], "type": "js", - "modulePath": "plugins/lesswrong/top-month.js", - "sourceFile": "plugins/lesswrong/top-month.js" + "modulePath": "plugins/linkedin/job-detail.js", + "sourceFile": "plugins/linkedin/job-detail.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "lesswrong", - "name": "top-week", - "description": "Top this week", + "site": "linkedin", + "name": "jobs-preferences", + "description": "Read visible LinkedIn Jobs preferences and alert settings without changing them", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "open_to_work", + "job_titles", + "locations", + "job_alerts", + "preferences_url", + "alerts_url", + "raw_preferences" + ], + "type": "js", + "modulePath": "plugins/linkedin/jobs-preferences.js", + "sourceFile": "plugins/linkedin/jobs-preferences.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "login", + "description": "Open linkedin login", + "access": "write", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "public_id", + "plain_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/linkedin/auth.js", + "sourceFile": "plugins/linkedin/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "linkedin", + "name": "people-search", + "description": "Search standard LinkedIn (not Sales Navigator) for people by keyword. Each invocation consumes against LinkedIn's monthly Commercial Use Limit on people search; throttle accordingly.", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ + { + "name": "keywords", + "type": "string", + "required": true, + "positional": true, + "help": "People search keywords, e.g. \"site reliability engineer berlin\"" + }, { "name": "limit", "type": "int", - "default": 10, + "default": 5, "required": false, - "help": "Number of results" + "help": "Maximum people to return (1-10); each query counts toward LinkedIn's monthly CUL" } ], - "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" + "columns": [ + "rank", + "name", + "headline", + "location", + "profile_url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/lesswrong/top-week.js", - "sourceFile": "plugins/lesswrong/top-week.js" + "modulePath": "plugins/linkedin/people-search.js", + "sourceFile": "plugins/linkedin/people-search.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "lesswrong", - "name": "top-year", - "description": "Top this year", + "site": "linkedin", + "name": "post-analytics", + "description": "Summarize raw visible LinkedIn post counters without custom scoring or classification", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + }, { "name": "limit", "type": "int", - "default": 10, + "default": 30, "required": false, - "help": "Number of results" + "help": "Maximum posts to summarize (1-100)" } ], "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" + "posts_analyzed", + "total_reactions", + "total_comments", + "total_reposts", + "total_impressions", + "posts_with_media", + "posts_with_urls", + "latest_posted_at", + "latest_reactions", + "latest_comments", + "latest_reposts", + "latest_impressions", + "latest_url" ], "type": "js", - "modulePath": "plugins/lesswrong/top-year.js", - "sourceFile": "plugins/lesswrong/top-year.js" + "modulePath": "plugins/linkedin/post-analytics.js", + "sourceFile": "plugins/linkedin/post-analytics.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "lesswrong", - "name": "user", - "description": "User profile", + "site": "linkedin", + "name": "post-comments", + "description": "List unique commenters and reply authors from one exact LinkedIn post URL", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "username", + "name": "post-url", "type": "string", "required": true, "positional": true, - "help": "LessWrong username or slug" + "help": "Exact LinkedIn post URL" + }, + { + "name": "limit", + "type": "int", + "required": false, + "help": "Maximum unique commenters to return; omit to fetch all" } ], "columns": [ - "field", - "value" + "rank", + "name", + "headline", + "profile_url", + "comment_count", + "sample_comment", + "commented_at", + "source_post" ], "type": "js", - "modulePath": "plugins/lesswrong/user.js", - "sourceFile": "plugins/lesswrong/user.js" + "modulePath": "plugins/linkedin/post-comments.js", + "sourceFile": "plugins/linkedin/post-comments.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "lesswrong", - "name": "user-posts", - "description": "List a user's posts", + "site": "linkedin", + "name": "posts", + "description": "Export visible posts from a LinkedIn profile activity page with engagement metrics", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "username", + "name": "profile-url", "type": "string", - "required": true, - "positional": true, - "help": "LessWrong username or slug" + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results" + "help": "Maximum posts to return (1-100)" } ], "columns": [ "rank", - "title", - "karma", + "author", + "posted_at", + "body", + "reactions", "comments", - "date", - "url" + "reposts", + "impressions", + "media", + "media_urls", + "url", + "raw_text" ], "type": "js", - "modulePath": "plugins/lesswrong/user-posts.js", - "sourceFile": "plugins/lesswrong/user-posts.js" + "modulePath": "plugins/linkedin/posts.js", + "sourceFile": "plugins/linkedin/posts.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "lichess", - "name": "top", - "description": "Top-N Lichess leaderboard for a perf type (bullet/blitz/rapid/classical/...)", + "site": "linkedin", + "name": "profile-analytics", + "description": "Read visible LinkedIn profile dashboard metrics such as profile views, post impressions, and search appearances", "access": "read", - "domain": "lichess.org", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "perf", - "type": "str", - "required": true, - "positional": true, - "help": "Perf type (bullet, blitz, rapid, classical, ultraBullet, chess960, ...)" - }, - { - "name": "limit", - "type": "int", - "default": 10, + "name": "profile-url", + "type": "string", "required": false, - "help": "Top-N rows (1-200)" + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." } ], "columns": [ - "rank", - "username", - "id", - "title", - "rating", - "progress", - "patron", - "url" + "profile_url", + "profile_views", + "post_impressions", + "search_appearances", + "followers", + "connections", + "raw_analytics" ], "type": "js", - "modulePath": "plugins/lichess/top.js", - "sourceFile": "plugins/lichess/top.js" + "modulePath": "plugins/linkedin/profile-analytics.js", + "sourceFile": "plugins/linkedin/profile-analytics.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "lichess", - "name": "user", - "description": "Fetch a Lichess player profile by username (rating, perfs, counts)", + "site": "linkedin", + "name": "profile-experience", + "description": "Read visible LinkedIn profile experience entries with titles, dates, locations, skills, media, and URLs", "access": "read", - "domain": "lichess.org", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Lichess username (case-insensitive)" + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." } ], "columns": [ - "username", - "id", + "rank", + "total_count", "title", - "patron", - "online", - "tosViolation", - "createdAt", - "seenAt", - "gamesAll", - "gamesWin", - "gamesLoss", - "gamesDraw", - "topPerfName", - "topPerfRating", - "topPerfGames", - "fideRating", - "country", - "bio", - "url" + "employment_type", + "company", + "date_range", + "start_date", + "end_date", + "location", + "location_type", + "description", + "skills", + "media", + "urls", + "skill_url", + "media_url", + "profile_url", + "raw_text" + ], + "type": "js", + "modulePath": "plugins/linkedin/profile-experience.js", + "sourceFile": "plugins/linkedin/profile-experience.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "profile-projects", + "description": "Read visible LinkedIn profile projects with descriptions, dates, skills, media, and URLs", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], + "columns": [ + "rank", + "title", + "date_range", + "associated_with", + "description", + "skills", + "media", + "urls", + "profile_url", + "raw_text" ], "type": "js", - "modulePath": "plugins/lichess/user.js", - "sourceFile": "plugins/lichess/user.js" + "modulePath": "plugins/linkedin/profile-projects.js", + "sourceFile": "plugins/linkedin/profile-projects.js", + "navigateBefore": "https://www.linkedin.com" }, { "site": "linkedin", - "name": "company", - "description": "Read a LinkedIn company page: industry, size, HQ, founded, website, followers, and about text", + "name": "profile-read", + "description": "Read visible LinkedIn profile sections: headline, About, experience, education, services, and featured sections", "access": "read", "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "company", + "name": "profile-url", "type": "string", - "required": true, - "positional": true, - "help": "Company universal name, /company/ path, or full URL" + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." } ], "columns": [ + "profile_url", "name", - "industry", - "size", - "headquarters", - "founded", - "website", - "specialties", - "followers", + "headline", + "location", "about", - "url" + "about_character_count", + "about_skills", + "experience", + "education", + "services", + "featured" ], "type": "js", - "modulePath": "plugins/linkedin/company.js", - "sourceFile": "plugins/linkedin/company.js", + "modulePath": "plugins/linkedin/profile-read.js", + "sourceFile": "plugins/linkedin/profile-read.js", "navigateBefore": "https://www.linkedin.com" }, { "site": "linkedin", - "name": "connect", - "description": "Fail-closed LinkedIn connection request sender that verifies the exact profile before optionally sending a note", + "name": "safe-send", + "description": "Fail-closed LinkedIn message sender that verifies exact thread, recipient, and latest message before filling/sending", "access": "write", "domain": "www.linkedin.com", "strategy": "ui", "browser": true, "args": [ { - "name": "profile-url", - "type": "string", + "name": "thread-url", + "type": "str", "required": true, - "positional": true, - "help": "Exact LinkedIn profile URL to open and verify" + "help": "Exact LinkedIn messaging thread URL to open and verify" }, { "name": "expected-name", - "type": "string", + "type": "str", "required": true, - "help": "Expected visible profile name" + "help": "Expected visible recipient name in the active thread header" }, { - "name": "note", - "type": "string", - "default": "", + "name": "message", + "type": "str", + "required": true, + "help": "Message body to send or dry-run" + }, + { + "name": "expected-last-text", + "type": "str", "required": false, - "help": "Optional connection note, max 300 chars" + "help": "Substring expected in the currently visible latest conversation context" + }, + { + "name": "expected-last-hash", + "type": "str", + "required": false, + "help": "SHA-256 hash of expected latest visible message text" }, { "name": "send", @@ -8771,189 +10584,149 @@ "default": false, "required": false, "help": "Actually click Send. Default is dry-run verification only." + }, + { + "name": "screenshot", + "type": "bool", + "default": false, + "required": false, + "help": "Capture a screenshot during verification" } ], "columns": [ "status", "recipient", "reason", - "profile_url", - "note_chars", - "connectable", - "delivery_verified", - "matched_invitation_name", - "matched_invitation_url", - "actualValue", - "blockReason", - "expectedValue", - "observedUrl", - "safety" + "thread_url", + "message_chars", + "screenshot" ], "type": "js", - "modulePath": "plugins/linkedin/connect.js", - "sourceFile": "plugins/linkedin/connect.js", + "modulePath": "plugins/linkedin/safe-send.js", + "sourceFile": "plugins/linkedin/safe-send.js", "navigateBefore": true }, { "site": "linkedin", - "name": "connections", - "description": "List your LinkedIn first-degree connections with names, headlines, and profile URLs", + "name": "salesnav-inbox", + "description": "List LinkedIn Sales Navigator message conversations with API pagination", "access": "read", "domain": "www.linkedin.com", - "strategy": "cookie", + "strategy": "ui", "browser": true, "args": [ { "name": "limit", - "type": "int", - "default": 20, + "type": "number", + "default": 40, "required": false, - "help": "Number of connections to return (max 500)" - } - ], - "columns": [ - "rank", - "name", - "occupation", - "public_id", - "connected_at", - "url" - ], - "type": "js", - "modulePath": "plugins/linkedin/connections.js", - "sourceFile": "plugins/linkedin/connections.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "inbox", - "description": "List LinkedIn messaging inbox conversations and unread messages", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ + "help": "Maximum conversations to return (1-500)" + }, { - "name": "limit", - "type": "int", - "default": 40, + "name": "max-pages", + "type": "number", + "default": 30, "required": false, - "help": "Maximum conversations to return (1-100)" + "help": "Maximum Sales Navigator API pages to fetch" }, { "name": "unread-only", "type": "bool", "default": false, "required": false, - "help": "Return only conversations with unread messages" + "help": "Return only unread conversations" } ], "columns": [ "rank", - "thread_url", "thread_id", + "thread_url", "person_name", - "last_message_preview", + "last_message_snippet", + "last_activity_time", "unread", - "counterparty_type", - "category", - "timestamp" + "unread_count", + "total_message_count", + "archived", + "participants", + "next_page_starts_at" ], "type": "js", - "modulePath": "plugins/linkedin/inbox.js", - "sourceFile": "plugins/linkedin/inbox.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/linkedin/salesnav-inbox.js", + "sourceFile": "plugins/linkedin/salesnav-inbox.js", + "navigateBefore": true }, { "site": "linkedin", - "name": "job-detail", - "description": "Read one LinkedIn job page with description, apply URL, workplace type, applicants, and company metadata", - "access": "read", + "name": "salesnav-message", + "description": "Send or dry-run a LinkedIn Sales Navigator InMail to a lead using the Sales Navigator messaging API", + "access": "write", "domain": "www.linkedin.com", - "strategy": "cookie", + "strategy": "ui", "browser": true, "args": [ { - "name": "job-url", + "name": "recipient", "type": "string", "required": true, "positional": true, - "help": "Exact LinkedIn job URL, e.g. https://www.linkedin.com/jobs/view/123/" - } - ], - "columns": [ - "title", - "company", - "location", - "workplace_type", - "job_type", - "applicants", - "listed", - "apply_url", - "company_url", - "url", - "description" - ], - "type": "js", - "modulePath": "plugins/linkedin/job-detail.js", - "sourceFile": "plugins/linkedin/job-detail.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "jobs-preferences", - "description": "Read visible LinkedIn Jobs preferences and alert settings without changing them", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "open_to_work", - "job_titles", - "locations", - "job_alerts", - "preferences_url", - "alerts_url", - "raw_preferences" + "help": "Sales Navigator lead URL, LinkedIn /in/ URL from salesnav-search, or urn:li:fs_salesProfile:(...)" + }, + { + "name": "subject", + "type": "string", + "required": true, + "help": "InMail subject" + }, + { + "name": "body", + "type": "string", + "required": true, + "help": "InMail body" + }, + { + "name": "send", + "type": "bool", + "default": false, + "required": false, + "help": "Actually send the InMail. Default is dry-run validation only." + }, + { + "name": "copy-to-crm", + "type": "bool", + "default": false, + "required": false, + "help": "Set Sales Navigator copyToCrm on the message request" + } ], - "type": "js", - "modulePath": "plugins/linkedin/jobs-preferences.js", - "sourceFile": "plugins/linkedin/jobs-preferences.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "login", - "description": "Open linkedin login", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], "columns": [ "status", - "logged_in", - "site", - "public_id", - "plain_id", - "name", - "action", - "verify_command" + "recipient", + "title", + "company", + "credits_remaining", + "credits_before", + "credits_after", + "sent_in_salesnav", + "message_chars", + "subject_chars", + "recipient_urn", + "degree", + "inmail_restriction", + "open_link" ], "type": "js", - "modulePath": "plugins/linkedin/auth.js", - "sourceFile": "plugins/linkedin/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/linkedin/salesnav-message.js", + "sourceFile": "plugins/linkedin/salesnav-message.js", + "navigateBefore": true }, { "site": "linkedin", - "name": "people-search", - "description": "Search standard LinkedIn (not Sales Navigator) for people by keyword. Each invocation consumes against LinkedIn's monthly Commercial Use Limit on people search; throttle accordingly.", + "name": "salesnav-search", + "description": "Search LinkedIn Sales Navigator for people leads by keyword", "access": "read", "domain": "www.linkedin.com", - "strategy": "cookie", + "strategy": "ui", "browser": true, "args": [ { @@ -8961,855 +10734,948 @@ "type": "string", "required": true, "positional": true, - "help": "People search keywords, e.g. \"site reliability engineer berlin\"" + "help": "People search keywords, e.g. \"quality manager food manufacturing\"" }, { "name": "limit", - "type": "int", - "default": 5, + "type": "number", + "default": 25, "required": false, - "help": "Maximum people to return (1-10); each query counts toward LinkedIn's monthly CUL" + "help": "Maximum leads to return (1-500, fetched 25 per request)" } ], "columns": [ "rank", "name", - "headline", + "title", + "company", "location", - "profile_url" + "degree", + "profile_url", + "lead_url", + "recipient_urn" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/linkedin/people-search.js", - "sourceFile": "plugins/linkedin/people-search.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/linkedin/salesnav-search.js", + "sourceFile": "plugins/linkedin/salesnav-search.js", + "navigateBefore": true }, { "site": "linkedin", - "name": "post-analytics", - "description": "Summarize raw visible LinkedIn post counters without custom scoring or classification", + "name": "salesnav-thread", + "description": "Return full Sales Navigator message history for a thread id, Sales Navigator inbox URL, lead URL, recipient urn, or exact recipient name", "access": "read", "domain": "www.linkedin.com", - "strategy": "cookie", + "strategy": "ui", "browser": true, "args": [ { - "name": "profile-url", + "name": "thread-or-recipient", "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "required": true, + "positional": true, + "help": "Sales Navigator inbox URL/thread id, Sales Navigator lead URL, recipient urn, or exact participant name" }, { "name": "limit", - "type": "int", + "type": "number", + "default": 200, + "required": false, + "help": "Maximum messages to return (1-500)" + }, + { + "name": "max-pages", + "type": "number", "default": 30, "required": false, - "help": "Maximum posts to summarize (1-100)" + "help": "Maximum inbox pages to scan when resolving a recipient" } ], "columns": [ - "posts_analyzed", - "total_reactions", - "total_comments", - "total_reposts", - "total_impressions", - "posts_with_media", - "posts_with_urls", - "latest_posted_at", - "latest_reactions", - "latest_comments", - "latest_reposts", - "latest_impressions", - "latest_url" + "index", + "thread_id", + "thread_url", + "sender", + "text", + "timestamp", + "subject", + "message_id", + "sender_urn", + "delivered_at", + "type", + "total_message_count" ], "type": "js", - "modulePath": "plugins/linkedin/post-analytics.js", - "sourceFile": "plugins/linkedin/post-analytics.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/linkedin/salesnav-thread.js", + "sourceFile": "plugins/linkedin/salesnav-thread.js", + "navigateBefore": true }, { "site": "linkedin", - "name": "post-comments", - "description": "List unique commenters and reply authors from one exact LinkedIn post URL", + "name": "search", + "description": "Search LinkedIn jobs", "access": "read", "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "post-url", + "name": "query", "type": "string", "required": true, "positional": true, - "help": "Exact LinkedIn post URL" + "help": "Job search keywords" + }, + { + "name": "location", + "type": "string", + "required": false, + "help": "Location text such as San Francisco Bay Area" }, { "name": "limit", "type": "int", + "default": 10, "required": false, - "help": "Maximum unique commenters to return; omit to fetch all" + "help": "Number of jobs to return (max 100)" + }, + { + "name": "start", + "type": "int", + "default": 0, + "required": false, + "help": "Result offset for pagination" + }, + { + "name": "details", + "type": "bool", + "default": false, + "required": false, + "help": "Include full job description and apply URL (slower)" + }, + { + "name": "company", + "type": "string", + "required": false, + "help": "Comma-separated company names or LinkedIn company IDs" + }, + { + "name": "experience-level", + "type": "string", + "required": false, + "help": "Comma-separated: internship, entry, associate, mid-senior, director, executive" + }, + { + "name": "job-type", + "type": "string", + "required": false, + "help": "Comma-separated: full-time, part-time, contract, temporary, volunteer, internship, other" + }, + { + "name": "date-posted", + "type": "string", + "required": false, + "help": "One of: any, month, week, 24h" + }, + { + "name": "remote", + "type": "string", + "required": false, + "help": "Comma-separated: on-site, hybrid, remote" } ], + "columns": [ + "rank", + "title", + "company", + "location", + "listed", + "salary", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/linkedin/search.js", + "sourceFile": "plugins/linkedin/search.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "sent-invitations", + "description": "List pending LinkedIn sent invitations for CRM reconciliation", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ "rank", "name", - "headline", "profile_url", - "comment_count", - "sample_comment", - "commented_at", - "source_post" + "invited_date_text" + ], + "type": "js", + "modulePath": "plugins/linkedin/sent-invitations.js", + "sourceFile": "plugins/linkedin/sent-invitations.js", + "navigateBefore": true + }, + { + "site": "linkedin", + "name": "services-read", + "description": "Read LinkedIn Services page details including services, overview, availability, pricing, and media titles/descriptions", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + }, + { + "name": "services-url", + "type": "string", + "required": false, + "help": "LinkedIn /services/page// URL. If omitted, it is discovered from the profile." + } + ], + "columns": [ + "service_url", + "page_title", + "overview", + "availability", + "work_locations", + "pricing", + "services_provided", + "services_count", + "media", + "media_count", + "messages", + "reviews_visibility" ], "type": "js", - "modulePath": "plugins/linkedin/post-comments.js", - "sourceFile": "plugins/linkedin/post-comments.js", + "modulePath": "plugins/linkedin/services-read.js", + "sourceFile": "plugins/linkedin/services-read.js", "navigateBefore": "https://www.linkedin.com" }, { "site": "linkedin", - "name": "posts", - "description": "Export visible posts from a LinkedIn profile activity page with engagement metrics", + "name": "thread-snapshot", + "description": "Load a LinkedIn messaging thread, scroll for available history, and return a full context snapshot", "access": "read", "domain": "www.linkedin.com", - "strategy": "cookie", + "strategy": "ui", "browser": true, "args": [ { - "name": "profile-url", - "type": "string", + "name": "thread-url", + "type": "str", + "required": true, + "help": "Exact LinkedIn messaging thread URL to open and snapshot" + }, + { + "name": "max-scrolls", + "type": "number", + "default": 30, "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "Maximum upward scroll attempts to load older messages" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "json", + "type": "bool", + "default": false, "required": false, - "help": "Maximum posts to return (1-100)" + "help": "Return only JSON snapshot string in the snapshot_json field" } ], "columns": [ - "rank", - "author", - "posted_at", - "body", - "reactions", - "comments", - "reposts", - "impressions", - "media", - "media_urls", - "url", - "raw_text" + "thread_url", + "recipient", + "message_count", + "latest_text", + "snapshot_json" ], "type": "js", - "modulePath": "plugins/linkedin/posts.js", - "sourceFile": "plugins/linkedin/posts.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/linkedin/thread-snapshot.js", + "sourceFile": "plugins/linkedin/thread-snapshot.js", + "navigateBefore": true }, { "site": "linkedin", - "name": "profile-analytics", - "description": "Read visible LinkedIn profile dashboard metrics such as profile views, post impressions, and search appearances", + "name": "timeline", + "description": "Read LinkedIn home timeline posts", "access": "read", "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "profile-url", - "type": "string", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "Number of posts to return (max 100)" } ], "columns": [ - "profile_url", - "profile_views", - "post_impressions", - "search_appearances", - "followers", - "connections", - "raw_analytics" + "rank", + "author", + "author_url", + "headline", + "text", + "posted_at", + "reactions", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/profile-analytics.js", - "sourceFile": "plugins/linkedin/profile-analytics.js", + "modulePath": "plugins/linkedin/timeline.js", + "sourceFile": "plugins/linkedin/timeline.js", "navigateBefore": "https://www.linkedin.com" }, { "site": "linkedin", - "name": "profile-experience", - "description": "Read visible LinkedIn profile experience entries with titles, dates, locations, skills, media, and URLs", + "name": "whoami", + "description": "Show the current logged-in linkedin account", "access": "read", "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - } - ], + "args": [], "columns": [ - "rank", - "total_count", - "title", - "employment_type", - "company", - "date_range", - "start_date", - "end_date", - "location", - "location_type", - "description", - "skills", - "media", - "urls", - "skill_url", - "media_url", - "profile_url", - "raw_text" + "logged_in", + "site", + "public_id", + "plain_id", + "name" ], "type": "js", - "modulePath": "plugins/linkedin/profile-experience.js", - "sourceFile": "plugins/linkedin/profile-experience.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/linkedin/auth.js", + "sourceFile": "plugins/linkedin/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "linkedin", - "name": "profile-projects", - "description": "Read visible LinkedIn profile projects with descriptions, dates, skills, media, and URLs", + "site": "linkedin-learning", + "name": "course", + "description": "Get LinkedIn Learning course detail by slug or course URL", "access": "read", "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "profile-url", + "name": "slug", "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "required": true, + "positional": true, + "help": "Course slug (e.g. agentic-ai-build-your-first-agentic-ai-system) or full /learning/ URL" } ], "columns": [ - "rank", "title", - "date_range", - "associated_with", + "slug", "description", - "skills", - "media", - "urls", - "profile_url", - "raw_text" + "difficulty", + "duration_sec", + "videos_count", + "rating", + "rating_count", + "released", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/profile-projects.js", - "sourceFile": "plugins/linkedin/profile-projects.js", + "modulePath": "plugins/linkedin-learning/course.js", + "sourceFile": "plugins/linkedin-learning/course.js", "navigateBefore": "https://www.linkedin.com" }, { - "site": "linkedin", - "name": "profile-read", - "description": "Read visible LinkedIn profile sections: headline, About, experience, education, services, and featured sections", - "access": "read", - "domain": "www.linkedin.com", + "site": "linkedin-learning", + "name": "login", + "description": "Open linkedin-learning login", + "access": "write", + "domain": "linkedin.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - } - ], + "args": [], "columns": [ - "profile_url", + "status", + "logged_in", + "site", + "public_id", + "plain_id", "name", - "headline", - "location", - "about", - "about_character_count", - "about_skills", - "experience", - "education", - "services", - "featured" + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/linkedin/profile-read.js", - "sourceFile": "plugins/linkedin/profile-read.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/linkedin-learning/auth.js", + "sourceFile": "plugins/linkedin-learning/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "linkedin", - "name": "safe-send", - "description": "Fail-closed LinkedIn message sender that verifies exact thread, recipient, and latest message before filling/sending", - "access": "write", + "site": "linkedin-learning", + "name": "search", + "description": "Search LinkedIn Learning courses, videos, and learning paths by keyword", + "access": "read", "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "thread-url", - "type": "str", - "required": true, - "help": "Exact LinkedIn messaging thread URL to open and verify" - }, - { - "name": "expected-name", - "type": "str", - "required": true, - "help": "Expected visible recipient name in the active thread header" - }, - { - "name": "message", - "type": "str", - "required": true, - "help": "Message body to send or dry-run" - }, - { - "name": "expected-last-text", - "type": "str", - "required": false, - "help": "Substring expected in the currently visible latest conversation context" - }, - { - "name": "expected-last-hash", - "type": "str", - "required": false, - "help": "SHA-256 hash of expected latest visible message text" - }, - { - "name": "send", - "type": "bool", - "default": false, - "required": false, - "help": "Actually click Send. Default is dry-run verification only." + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "keywords", + "type": "string", + "required": true, + "positional": true, + "help": "Search keywords, e.g. \"AI agent\"" }, { - "name": "screenshot", - "type": "bool", - "default": false, + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Capture a screenshot during verification" + "help": "Maximum results to return (1-50)" } ], "columns": [ - "status", - "recipient", - "reason", - "thread_url", - "message_chars", - "screenshot" + "rank", + "type", + "title", + "instructor", + "difficulty", + "duration_sec", + "rating", + "rating_count", + "viewers", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/linkedin/safe-send.js", - "sourceFile": "plugins/linkedin/safe-send.js", - "navigateBefore": true + "modulePath": "plugins/linkedin-learning/search.js", + "sourceFile": "plugins/linkedin-learning/search.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "linkedin", - "name": "salesnav-inbox", - "description": "List LinkedIn Sales Navigator message conversations with API pagination", + "site": "linkedin-learning", + "name": "trending", + "description": "Browse LinkedIn Learning recommended courses across personalized carousels", "access": "read", "domain": "www.linkedin.com", - "strategy": "ui", + "strategy": "cookie", "browser": true, "args": [ { "name": "limit", - "type": "number", - "default": 40, - "required": false, - "help": "Maximum conversations to return (1-500)" - }, - { - "name": "max-pages", - "type": "number", - "default": 30, + "type": "int", + "default": 10, "required": false, - "help": "Maximum Sales Navigator API pages to fetch" - }, + "help": "Maximum results to return (1-50)" + } + ], + "columns": [ + "rank", + "group", + "type", + "title", + "difficulty", + "viewers", + "url" + ], + "type": "js", + "modulePath": "plugins/linkedin-learning/trending.js", + "sourceFile": "plugins/linkedin-learning/trending.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin-learning", + "name": "whoami", + "description": "Show the current logged-in linkedin-learning account", + "access": "read", + "domain": "linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "public_id", + "plain_id", + "name" + ], + "type": "js", + "modulePath": "plugins/linkedin-learning/auth.js", + "sourceFile": "plugins/linkedin-learning/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "lobsters", + "name": "active", + "description": "Lobste.rs most active discussions", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ { - "name": "unread-only", - "type": "bool", - "default": false, + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Return only unread conversations" + "help": "Number of stories" } ], "columns": [ "rank", - "thread_id", - "thread_url", - "person_name", - "last_message_snippet", - "last_activity_time", - "unread", - "unread_count", - "total_message_count", - "archived", - "participants", - "next_page_starts_at" + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-inbox.js", - "sourceFile": "plugins/linkedin/salesnav-inbox.js", - "navigateBefore": true + "modulePath": "plugins/lobsters/active.js", + "sourceFile": "plugins/lobsters/active.js" }, { - "site": "linkedin", - "name": "salesnav-message", - "description": "Send or dry-run a LinkedIn Sales Navigator InMail to a lead using the Sales Navigator messaging API", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, + "site": "lobsters", + "name": "domain", + "description": "Lobste.rs stories submitted from a specific domain", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, "args": [ { - "name": "recipient", - "type": "string", + "name": "domain", + "type": "str", "required": true, "positional": true, - "help": "Sales Navigator lead URL, LinkedIn /in/ URL from salesnav-search, or urn:li:fs_salesProfile:(...)" - }, - { - "name": "subject", - "type": "string", - "required": true, - "help": "InMail subject" - }, - { - "name": "body", - "type": "string", - "required": true, - "help": "InMail body" + "help": "Source domain (e.g. github.com, arxiv.org, blog.cloudflare.com)" }, { - "name": "send", - "type": "bool", - "default": false, + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Actually send the InMail. Default is dry-run validation only." - }, + "help": "Number of stories (1-25 — single page)" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "submission_url", + "comments_url" + ], + "type": "js", + "modulePath": "plugins/lobsters/domain.js", + "sourceFile": "plugins/lobsters/domain.js" + }, + { + "site": "lobsters", + "name": "hot", + "description": "Lobste.rs hottest stories", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ { - "name": "copy-to-crm", - "type": "bool", - "default": false, + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Set Sales Navigator copyToCrm on the message request" + "help": "Number of stories" } ], "columns": [ - "status", - "recipient", + "rank", + "id", "title", - "company", - "credits_remaining", - "credits_before", - "credits_after", - "sent_in_salesnav", - "message_chars", - "subject_chars", - "recipient_urn", - "degree", - "inmail_restriction", - "open_link" + "score", + "author", + "comments", + "created_at", + "tags", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-message.js", - "sourceFile": "plugins/linkedin/salesnav-message.js", - "navigateBefore": true + "modulePath": "plugins/lobsters/hot.js", + "sourceFile": "plugins/lobsters/hot.js" }, { - "site": "linkedin", - "name": "salesnav-search", - "description": "Search LinkedIn Sales Navigator for people leads by keyword", + "site": "lobsters", + "name": "newest", + "description": "Lobste.rs newest stories", "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, + "domain": "lobste.rs", + "strategy": "public", + "browser": false, "args": [ - { - "name": "keywords", - "type": "string", - "required": true, - "positional": true, - "help": "People search keywords, e.g. \"quality manager food manufacturing\"" - }, { "name": "limit", - "type": "number", - "default": 25, + "type": "int", + "default": 20, "required": false, - "help": "Maximum leads to return (1-500, fetched 25 per request)" + "help": "Number of stories" } ], "columns": [ "rank", - "name", + "id", "title", - "company", - "location", - "degree", - "profile_url", - "lead_url", - "recipient_urn" - ], - "tags": [ - "search" + "score", + "author", + "comments", + "created_at", + "tags", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-search.js", - "sourceFile": "plugins/linkedin/salesnav-search.js", - "navigateBefore": true + "modulePath": "plugins/lobsters/newest.js", + "sourceFile": "plugins/lobsters/newest.js" }, { - "site": "linkedin", - "name": "salesnav-thread", - "description": "Return full Sales Navigator message history for a thread id, Sales Navigator inbox URL, lead URL, recipient urn, or exact recipient name", + "site": "lobsters", + "name": "read", + "description": "Read a Lobste.rs story and its comment tree", "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, + "domain": "lobste.rs", + "strategy": "public", + "browser": false, "args": [ { - "name": "thread-or-recipient", - "type": "string", + "name": "id", + "type": "str", "required": true, "positional": true, - "help": "Sales Navigator inbox URL/thread id, Sales Navigator lead URL, recipient urn, or exact participant name" + "help": "Lobste.rs short_id (e.g. 6cmh6h)" }, { "name": "limit", - "type": "number", - "default": 200, + "type": "int", + "default": 25, "required": false, - "help": "Maximum messages to return (1-500)" + "help": "Max top-level comments" }, { - "name": "max-pages", - "type": "number", - "default": 30, + "name": "depth", + "type": "int", + "default": 2, "required": false, - "help": "Maximum inbox pages to scan when resolving a recipient" + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + }, + { + "name": "replies", + "type": "int", + "default": 5, + "required": false, + "help": "Max replies shown per comment at each level" + }, + { + "name": "max-length", + "type": "int", + "default": 2000, + "required": false, + "help": "Max characters per comment body (min 100)" } ], "columns": [ - "index", - "thread_id", - "thread_url", - "sender", - "text", - "timestamp", - "subject", - "message_id", - "sender_urn", - "delivered_at", "type", - "total_message_count" + "author", + "score", + "text" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-thread.js", - "sourceFile": "plugins/linkedin/salesnav-thread.js", - "navigateBefore": true + "modulePath": "plugins/lobsters/read.js", + "sourceFile": "plugins/lobsters/read.js" }, { - "site": "linkedin", - "name": "search", - "description": "Search LinkedIn jobs", + "site": "lobsters", + "name": "tag", + "description": "Lobste.rs stories by tag", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "lobste.rs", + "strategy": "public", + "browser": false, "args": [ { - "name": "query", - "type": "string", + "name": "tag", + "type": "str", "required": true, "positional": true, - "help": "Job search keywords" - }, - { - "name": "location", - "type": "string", - "required": false, - "help": "Location text such as San Francisco Bay Area" + "help": "Tag name (e.g. programming, rust, security, ai)" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of jobs to return (max 100)" - }, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/lobsters/tag.js", + "sourceFile": "plugins/lobsters/tag.js" + }, + { + "site": "luma", + "name": "create-event", + "description": "Create a free single-session Luma event", + "access": "write", + "domain": "luma.com", + "strategy": "ui", + "browser": true, + "args": [ { - "name": "start", - "type": "int", - "default": 0, - "required": false, - "help": "Result offset for pagination" + "name": "name", + "type": "str", + "required": true, + "help": "" }, { - "name": "details", - "type": "bool", - "default": false, - "required": false, - "help": "Include full job description and apply URL (slower)" + "name": "start", + "type": "str", + "required": true, + "help": "" }, { - "name": "company", - "type": "string", - "required": false, - "help": "Comma-separated company names or LinkedIn company IDs" + "name": "end", + "type": "str", + "required": true, + "help": "" }, { - "name": "experience-level", - "type": "string", - "required": false, - "help": "Comma-separated: internship, entry, associate, mid-senior, director, executive" + "name": "timezone", + "type": "str", + "required": true, + "help": "" }, { - "name": "job-type", - "type": "string", + "name": "calendar", + "type": "str", "required": false, - "help": "Comma-separated: full-time, part-time, contract, temporary, volunteer, internship, other" + "help": "" }, { - "name": "date-posted", - "type": "string", + "name": "description", + "type": "str", "required": false, - "help": "One of: any, month, week, 24h" + "help": "" }, { - "name": "remote", - "type": "string", - "required": false, - "help": "Comma-separated: on-site, hybrid, remote" - } - ], - "columns": [ - "rank", - "title", - "company", - "location", - "listed", - "salary", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/linkedin/search.js", - "sourceFile": "plugins/linkedin/search.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "sent-invitations", - "description": "List pending LinkedIn sent invitations for CRM reconciliation", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "rank", - "name", - "profile_url", - "invited_date_text" - ], - "type": "js", - "modulePath": "plugins/linkedin/sent-invitations.js", - "sourceFile": "plugins/linkedin/sent-invitations.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "services-read", - "description": "Read LinkedIn Services page details including services, overview, availability, pricing, and media titles/descriptions", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", + "name": "location", + "type": "str", "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "" }, { - "name": "services-url", - "type": "string", + "name": "virtual-url", + "type": "str", "required": false, - "help": "LinkedIn /services/page// URL. If omitted, it is discovered from the profile." - } - ], - "columns": [ - "service_url", - "page_title", - "overview", - "availability", - "work_locations", - "pricing", - "services_provided", - "services_count", - "media", - "media_count", - "messages", - "reviews_visibility" - ], - "type": "js", - "modulePath": "plugins/linkedin/services-read.js", - "sourceFile": "plugins/linkedin/services-read.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "thread-snapshot", - "description": "Load a LinkedIn messaging thread, scroll for available history, and return a full context snapshot", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ + "help": "" + }, { - "name": "thread-url", + "name": "visibility", "type": "str", - "required": true, - "help": "Exact LinkedIn messaging thread URL to open and snapshot" + "default": "public", + "required": false, + "help": "", + "choices": [ + "public", + "private", + "members-only" + ] }, { - "name": "max-scrolls", - "type": "number", - "default": 30, + "name": "capacity", + "type": "int", "required": false, - "help": "Maximum upward scroll attempts to load older messages" + "help": "" }, { - "name": "json", - "type": "bool", + "name": "require-approval", + "type": "boolean", "default": false, "required": false, - "help": "Return only JSON snapshot string in the snapshot_json field" + "help": "" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "" } ], "columns": [ - "thread_url", - "recipient", - "message_count", - "latest_text", - "snapshot_json" + "eventId", + "name", + "startsAt", + "endsAt", + "timezone", + "visibility", + "requireApproval", + "capacity", + "eventUrl", + "manageUrl" ], "type": "js", - "modulePath": "plugins/linkedin/thread-snapshot.js", - "sourceFile": "plugins/linkedin/thread-snapshot.js", - "navigateBefore": true + "modulePath": "plugins/luma/create-event.js", + "sourceFile": "plugins/luma/create-event.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true }, { - "site": "linkedin", - "name": "timeline", - "description": "Read LinkedIn home timeline posts", + "site": "luma", + "name": "events", + "description": "List upcoming or past Luma events managed by the logged-in account", "access": "read", - "domain": "www.linkedin.com", + "example": "webcmd luma events --period future --limit 25 -f json", + "domain": "luma.com", "strategy": "cookie", "browser": true, "args": [ + { + "name": "period", + "type": "str", + "default": "future", + "required": false, + "help": "List future or past events", + "choices": [ + "future", + "past" + ] + }, { "name": "limit", "type": "int", - "default": 20, + "default": 25, "required": false, - "help": "Number of posts to return (max 100)" + "help": "Maximum number of events to request" } ], "columns": [ - "rank", - "author", - "author_url", - "headline", - "text", - "posted_at", - "reactions", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/linkedin/timeline.js", - "sourceFile": "plugins/linkedin/timeline.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "whoami", - "description": "Show the current logged-in linkedin account", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "public_id", - "plain_id", - "name" + "eventId", + "name", + "startsAt", + "endsAt", + "timezone", + "guestCount", + "requireApproval", + "managerLevel", + "location", + "manageUrl", + "eventUrl" ], "type": "js", - "modulePath": "plugins/linkedin/auth.js", - "sourceFile": "plugins/linkedin/auth.js", + "modulePath": "plugins/luma/events.js", + "sourceFile": "plugins/luma/events.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "linkedin-learning", - "name": "course", - "description": "Get LinkedIn Learning course detail by slug or course URL", + "site": "luma", + "name": "guests", + "description": "List guests and all custom registration answers for a managed Luma event", "access": "read", - "domain": "www.linkedin.com", + "example": "webcmd luma guests evt-abc --status pending_approval --limit 100 -f json", + "domain": "luma.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "slug", - "type": "string", + "name": "eventId", + "type": "str", "required": true, "positional": true, - "help": "Course slug (e.g. agentic-ai-build-your-first-agentic-ai-system) or full /learning/ URL" + "help": "Luma event ID returned by webcmd luma events" + }, + { + "name": "status", + "type": "str", + "default": "all", + "required": false, + "help": "Filter by guest approval status", + "choices": [ + "all", + "approved", + "pending_approval", + "declined", + "waitlist", + "invited" + ] + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Maximum matching guests to return" + }, + { + "name": "query", + "type": "str", + "default": "", + "required": false, + "help": "Search text passed to Luma guest search" } ], "columns": [ - "title", - "slug", - "description", - "difficulty", - "duration_sec", - "videos_count", - "rating", - "rating_count", - "released", - "url" + "eventId", + "guestId", + "userId", + "name", + "email", + "phone", + "status", + "registeredAt", + "profiles", + "answers" ], "type": "js", - "modulePath": "plugins/linkedin-learning/course.js", - "sourceFile": "plugins/linkedin-learning/course.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/luma/guests.js", + "sourceFile": "plugins/luma/guests.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "linkedin-learning", + "site": "luma", "name": "login", - "description": "Open linkedin-learning login", + "description": "Open Luma sign in", "access": "write", - "domain": "linkedin.com", + "domain": "luma.com", "strategy": "cookie", "browser": true, "args": [], @@ -9817,1017 +11683,952 @@ "status", "logged_in", "site", - "public_id", - "plain_id", "name", + "email", + "url", "action", "verify_command" ], "type": "js", - "modulePath": "plugins/linkedin-learning/auth.js", - "sourceFile": "plugins/linkedin-learning/auth.js", + "modulePath": "plugins/luma/auth.js", + "sourceFile": "plugins/luma/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "linkedin-learning", - "name": "search", - "description": "Search LinkedIn Learning courses, videos, and learning paths by keyword", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", + "site": "luma", + "name": "set-registration-questions", + "description": "Append or replace custom registration questions on a managed Luma event", + "access": "write", + "domain": "luma.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "keywords", - "type": "string", + "name": "eventId", + "type": "str", "required": true, "positional": true, - "help": "Search keywords, e.g. \"AI agent\"" + "help": "" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "questions-file", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "mode", + "type": "str", + "required": true, + "help": "", + "choices": [ + "append", + "replace" + ] + }, + { + "name": "confirm", + "type": "boolean", + "default": false, "required": false, - "help": "Maximum results to return (1-50)" + "help": "" } ], "columns": [ - "rank", - "type", - "title", - "instructor", - "difficulty", - "duration_sec", - "rating", - "rating_count", - "viewers", - "url" - ], - "tags": [ - "search" + "eventId", + "mode", + "previousCount", + "questionCount", + "questions", + "registrationUrl" ], "type": "js", - "modulePath": "plugins/linkedin-learning/search.js", - "sourceFile": "plugins/linkedin-learning/search.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/luma/set-registration-questions.js", + "sourceFile": "plugins/luma/set-registration-questions.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true }, { - "site": "linkedin-learning", - "name": "trending", - "description": "Browse LinkedIn Learning recommended courses across personalized carousels", - "access": "read", - "domain": "www.linkedin.com", + "site": "luma", + "name": "update-guest-status", + "description": "Approve or decline a pending Luma guest after explicit confirmation", + "access": "write", + "example": "webcmd luma update-guest-status evt-abc gst-abc --status approved --confirm true -f json", + "domain": "luma.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 10, + "name": "eventId", + "type": "str", + "required": true, + "positional": true, + "help": "Luma event ID returned by webcmd luma events" + }, + { + "name": "guestId", + "type": "str", + "required": true, + "positional": true, + "help": "Luma guest ID returned by webcmd luma guests" + }, + { + "name": "status", + "type": "str", + "required": true, + "help": "New guest status", + "choices": [ + "approved", + "declined" + ] + }, + { + "name": "suppress-email", + "type": "boolean", + "default": false, "required": false, - "help": "Maximum results to return (1-50)" + "help": "Set true to prevent Luma from emailing the guest" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "Required. Set --confirm true to change the real guest status" } ], "columns": [ - "rank", - "group", - "type", - "title", - "difficulty", - "viewers", - "url" + "eventId", + "guestId", + "name", + "email", + "previousStatus", + "status", + "emailSuppressed" ], "type": "js", - "modulePath": "plugins/linkedin-learning/trending.js", - "sourceFile": "plugins/linkedin-learning/trending.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/luma/update-guest-status.js", + "sourceFile": "plugins/luma/update-guest-status.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true }, { - "site": "linkedin-learning", + "site": "luma", "name": "whoami", - "description": "Show the current logged-in linkedin-learning account", + "description": "Show the current logged-in Luma account", "access": "read", - "domain": "linkedin.com", + "domain": "luma.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ "logged_in", "site", - "public_id", - "plain_id", - "name" + "name", + "email", + "url" ], "type": "js", - "modulePath": "plugins/linkedin-learning/auth.js", - "sourceFile": "plugins/linkedin-learning/auth.js", + "modulePath": "plugins/luma/auth.js", + "sourceFile": "plugins/luma/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "lobsters", - "name": "active", - "description": "Lobste.rs most active discussions", - "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "url" - ], - "type": "js", - "modulePath": "plugins/lobsters/active.js", - "sourceFile": "plugins/lobsters/active.js" - }, - { - "site": "lobsters", - "name": "domain", - "description": "Lobste.rs stories submitted from a specific domain", + "site": "manus", + "name": "connectors", + "description": "List available Manus connectors (integrations).", "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, + "domain": "manus.im", + "strategy": "cookie", + "browser": true, "args": [ - { - "name": "domain", - "type": "str", - "required": true, - "positional": true, - "help": "Source domain (e.g. github.com, arxiv.org, blog.cloudflare.com)" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 50, "required": false, - "help": "Number of stories (1-25 — single page)" + "help": "Max connectors to return" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "submission_url", - "comments_url" + "UID", + "Name", + "Brief" ], "type": "js", - "modulePath": "plugins/lobsters/domain.js", - "sourceFile": "plugins/lobsters/domain.js" + "modulePath": "plugins/manus/connectors.js", + "sourceFile": "plugins/manus/connectors.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "lobsters", - "name": "hot", - "description": "Lobste.rs hottest stories", + "site": "manus", + "name": "credits", + "description": "Show Manus credit balance and refresh details.", "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "url" + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/lobsters/hot.js", - "sourceFile": "plugins/lobsters/hot.js" + "modulePath": "plugins/manus/credits.js", + "sourceFile": "plugins/manus/credits.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "lobsters", - "name": "newest", - "description": "Lobste.rs newest stories", + "site": "manus", + "name": "list", + "description": "List Manus sessions (tasks).", "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, + "domain": "manus.im", + "strategy": "cookie", + "browser": true, "args": [ { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of stories" + "help": "Max sessions to return" + }, + { + "name": "archived", + "type": "bool", + "default": false, + "required": false, + "help": "Include archived sessions" } ], "columns": [ - "rank", "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "url" + "Title", + "Status", + "Last Message", + "Last Updated", + "Credits" ], "type": "js", - "modulePath": "plugins/lobsters/newest.js", - "sourceFile": "plugins/lobsters/newest.js" + "modulePath": "plugins/manus/list.js", + "sourceFile": "plugins/manus/list.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "lobsters", + "site": "manus", + "name": "login", + "description": "Open manus login", + "access": "write", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/manus/auth.js", + "sourceFile": "plugins/manus/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "manus", "name": "read", - "description": "Read a Lobste.rs story and its comment tree", + "description": "Show details for a specific Manus session.", "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, + "domain": "manus.im", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "uid", "type": "str", "required": true, "positional": true, - "help": "Lobste.rs short_id (e.g. 6cmh6h)" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max top-level comments" - }, - { - "name": "depth", - "type": "int", - "default": 2, - "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" - }, - { - "name": "replies", - "type": "int", - "default": 5, - "required": false, - "help": "Max replies shown per comment at each level" - }, - { - "name": "max-length", - "type": "int", - "default": 2000, - "required": false, - "help": "Max characters per comment body (min 100)" + "help": "Session UID" } ], "columns": [ - "type", - "author", - "score", - "text" + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/lobsters/read.js", - "sourceFile": "plugins/lobsters/read.js" + "modulePath": "plugins/manus/read.js", + "sourceFile": "plugins/manus/read.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "lobsters", - "name": "tag", - "description": "Lobste.rs stories by tag", + "site": "manus", + "name": "skills", + "description": "List Manus skills (user-added and system).", "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "tag", - "type": "str", - "required": true, - "positional": true, - "help": "Tag name (e.g. programming, rust, security, ai)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "ID", + "Name", + "Description", + "Source" ], + "type": "js", + "modulePath": "plugins/manus/skills.js", + "sourceFile": "plugins/manus/skills.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "status", + "description": "Show current Manus user profile and credit summary.", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "url" + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/lobsters/tag.js", - "sourceFile": "plugins/lobsters/tag.js" + "modulePath": "plugins/manus/status.js", + "sourceFile": "plugins/manus/status.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "luma", - "name": "create-event", - "description": "Create a free single-session Luma event", - "access": "write", - "domain": "luma.com", - "strategy": "ui", + "site": "manus", + "name": "whoami", + "description": "Show the current logged-in manus account", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "start", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "end", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "timezone", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "calendar", - "type": "str", - "required": false, - "help": "" - }, - { - "name": "description", - "type": "str", - "required": false, - "help": "" - }, - { - "name": "location", - "type": "str", - "required": false, - "help": "" - }, - { - "name": "virtual-url", - "type": "str", - "required": false, - "help": "" - }, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" + ], + "type": "js", + "modulePath": "plugins/manus/auth.js", + "sourceFile": "plugins/manus/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "maven", + "name": "artifact", + "description": "Fetch a Maven Central artifact's version history (groupId:artifactId[:version])", + "access": "read", + "domain": "search.maven.org", + "strategy": "public", + "browser": false, + "args": [ { - "name": "visibility", + "name": "coordinate", "type": "str", - "default": "public", - "required": false, - "help": "", - "choices": [ - "public", - "private", - "members-only" - ] + "required": true, + "positional": true, + "help": "Maven coord \"groupId:artifactId\" or \"groupId:artifactId:version\"" }, { - "name": "capacity", + "name": "limit", "type": "int", + "default": 20, "required": false, - "help": "" - }, - { - "name": "require-approval", - "type": "boolean", - "default": false, - "required": false, - "help": "" - }, - { - "name": "confirm", - "type": "boolean", - "default": false, - "required": false, - "help": "" + "help": "Max versions (1-200, ignored when version is pinned)" } ], "columns": [ - "eventId", - "name", - "startsAt", - "endsAt", - "timezone", - "visibility", - "requireApproval", - "capacity", - "eventUrl", - "manageUrl" + "groupId", + "artifactId", + "version", + "packaging", + "publishedAt", + "tags", + "url" ], "type": "js", - "modulePath": "plugins/luma/create-event.js", - "sourceFile": "plugins/luma/create-event.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "modulePath": "plugins/maven/artifact.js", + "sourceFile": "plugins/maven/artifact.js" }, { - "site": "luma", - "name": "events", - "description": "List upcoming or past Luma events managed by the logged-in account", + "site": "maven", + "name": "search", + "description": "Search Maven Central by keyword (artifact name, groupId, tag)", "access": "read", - "example": "webcmd luma events --period future --limit 25 -f json", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, + "domain": "search.maven.org", + "strategy": "public", + "browser": false, "args": [ { - "name": "period", + "name": "query", "type": "str", - "default": "future", - "required": false, - "help": "List future or past events", - "choices": [ - "future", - "past" - ] + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"jackson\", \"guava\", \"ai.koog\")" }, { "name": "limit", "type": "int", - "default": 25, + "default": 30, "required": false, - "help": "Maximum number of events to request" + "help": "Max artifacts (1-200)" } ], "columns": [ - "eventId", - "name", - "startsAt", - "endsAt", - "timezone", - "guestCount", - "requireApproval", - "managerLevel", - "location", - "manageUrl", - "eventUrl" + "rank", + "coordinate", + "groupId", + "artifactId", + "latestVersion", + "packaging", + "versions", + "lastPublished", + "repository", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/luma/events.js", - "sourceFile": "plugins/luma/events.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/maven/search.js", + "sourceFile": "plugins/maven/search.js" }, { - "site": "luma", - "name": "guests", - "description": "List guests and all custom registration answers for a managed Luma event", + "site": "mdn", + "name": "search", + "description": "Search MDN Web Docs by keyword", "access": "read", - "example": "webcmd luma guests evt-abc --status pending_approval --limit 100 -f json", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, + "domain": "developer.mozilla.org", + "strategy": "public", + "browser": false, "args": [ { - "name": "eventId", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Luma event ID returned by webcmd luma events" - }, - { - "name": "status", - "type": "str", - "default": "all", - "required": false, - "help": "Filter by guest approval status", - "choices": [ - "all", - "approved", - "pending_approval", - "declined", - "waitlist", - "invited" - ] + "help": "Search keyword (e.g. \"fetch\", \"flexbox\", \"Array.prototype.map\")" }, { "name": "limit", "type": "int", - "default": 100, + "default": 10, "required": false, - "help": "Maximum matching guests to return" + "help": "Max results (1-50)" }, { - "name": "query", + "name": "locale", "type": "str", - "default": "", + "default": "en-US", "required": false, - "help": "Search text passed to Luma guest search" + "help": "Doc locale (en-US default; de / es / fr / ja / ko / pt-BR / ru / zh-CN / zh-TW)" } ], "columns": [ - "eventId", - "guestId", - "userId", - "name", - "email", - "phone", - "status", - "registeredAt", - "profiles", - "answers" + "rank", + "title", + "slug", + "locale", + "summary", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/luma/guests.js", - "sourceFile": "plugins/luma/guests.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/mdn/search.js", + "sourceFile": "plugins/mdn/search.js" }, { - "site": "luma", - "name": "login", - "description": "Open Luma sign in", - "access": "write", - "domain": "luma.com", + "site": "medium", + "name": "feed", + "description": "Medium popular posts Feed", + "access": "read", + "domain": "medium.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "topic", + "type": "str", + "default": "", + "required": false, + "help": "Topic (for example technology, programming, ai)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" + } + ], "columns": [ - "status", - "logged_in", - "site", - "name", - "email", - "url", - "action", - "verify_command" + "rank", + "title", + "author", + "date", + "readTime", + "claps" ], "type": "js", - "modulePath": "plugins/luma/auth.js", - "sourceFile": "plugins/luma/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/medium/feed.js", + "sourceFile": "plugins/medium/feed.js", + "navigateBefore": "https://medium.com" }, { - "site": "luma", - "name": "set-registration-questions", - "description": "Append or replace custom registration questions on a managed Luma event", - "access": "write", - "domain": "luma.com", - "strategy": "ui", + "site": "medium", + "name": "search", + "description": "Search Medium posts", + "access": "read", + "domain": "medium.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "eventId", + "name": "keyword", "type": "str", "required": true, "positional": true, - "help": "" + "help": "Search keyword" }, { - "name": "questions-file", - "type": "str", - "required": true, - "help": "" - }, + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" + } + ], + "columns": [ + "rank", + "title", + "author", + "date", + "readTime", + "claps", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/medium/search.js", + "sourceFile": "plugins/medium/search.js", + "navigateBefore": "https://medium.com" + }, + { + "site": "medium", + "name": "tag", + "description": "Latest Medium articles tagged with a given keyword (RSS feed)", + "access": "read", + "domain": "medium.com", + "strategy": "public", + "browser": false, + "args": [ { - "name": "mode", + "name": "tag", "type": "str", "required": true, - "help": "", - "choices": [ - "append", - "replace" - ] + "positional": true, + "help": "Lowercase tag slug (e.g. \"programming\", \"machine-learning\")" }, { - "name": "confirm", - "type": "boolean", - "default": false, + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "" + "help": "Max articles (1-25 — single RSS page)" } ], "columns": [ - "eventId", - "mode", - "previousCount", - "questionCount", - "questions", - "registrationUrl" + "rank", + "title", + "author", + "description", + "categories", + "published", + "url" ], "type": "js", - "modulePath": "plugins/luma/set-registration-questions.js", - "sourceFile": "plugins/luma/set-registration-questions.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "modulePath": "plugins/medium/tag.js", + "sourceFile": "plugins/medium/tag.js" }, { - "site": "luma", - "name": "update-guest-status", - "description": "Approve or decline a pending Luma guest after explicit confirmation", - "access": "write", - "example": "webcmd luma update-guest-status evt-abc gst-abc --status approved --confirm true -f json", - "domain": "luma.com", + "site": "medium", + "name": "user", + "description": "Get Medium user posts", + "access": "read", + "domain": "medium.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "eventId", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Luma event ID returned by webcmd luma events" + "help": "Medium username(for example @username or username)" }, { - "name": "guestId", + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" + } + ], + "columns": [ + "rank", + "title", + "date", + "readTime", + "claps", + "url" + ], + "type": "js", + "modulePath": "plugins/medium/user.js", + "sourceFile": "plugins/medium/user.js", + "navigateBefore": "https://medium.com" + }, + { + "site": "npm", + "name": "downloads", + "description": "Daily download counts for an npm package over a window", + "access": "read", + "domain": "api.npmjs.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", "type": "str", "required": true, "positional": true, - "help": "Luma guest ID returned by webcmd luma guests" + "help": "npm package name (e.g. \"react\", \"@vercel/og\")" }, { - "name": "status", + "name": "period", "type": "str", - "required": true, - "help": "New guest status", - "choices": [ - "approved", - "declined" - ] - }, - { - "name": "suppress-email", - "type": "boolean", - "default": false, - "required": false, - "help": "Set true to prevent Luma from emailing the guest" - }, - { - "name": "confirm", - "type": "boolean", - "default": false, + "default": "last-week", "required": false, - "help": "Required. Set --confirm true to change the real guest status" + "help": "last-day / last-week / last-month / last-year, or YYYY-MM-DD:YYYY-MM-DD" } ], "columns": [ - "eventId", - "guestId", - "name", - "email", - "previousStatus", - "status", - "emailSuppressed" + "rank", + "package", + "day", + "downloads" ], "type": "js", - "modulePath": "plugins/luma/update-guest-status.js", - "sourceFile": "plugins/luma/update-guest-status.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "modulePath": "plugins/npm/downloads.js", + "sourceFile": "plugins/npm/downloads.js" }, { - "site": "luma", - "name": "whoami", - "description": "Show the current logged-in Luma account", + "site": "npm", + "name": "package", + "description": "Single npm package metadata (latest version, license, homepage, repository). Use `npm downloads` for stats.", "access": "read", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, - "args": [], + "domain": "registry.npmjs.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "npm package name (e.g. \"react\", \"@vercel/og\")" + } + ], "columns": [ - "logged_in", - "site", "name", - "email", + "latestVersion", + "description", + "license", + "homepage", + "repository", + "bugs", + "maintainers", + "keywords", + "created", + "modified", "url" ], "type": "js", - "modulePath": "plugins/luma/auth.js", - "sourceFile": "plugins/luma/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/npm/package.js", + "sourceFile": "plugins/npm/package.js" }, { - "site": "manus", - "name": "connectors", - "description": "List available Manus connectors (integrations).", + "site": "npm", + "name": "search", + "description": "Search the public npm registry by keyword", "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, + "domain": "registry.npmjs.org", + "strategy": "public", + "browser": false, "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"react\", \"graphql client\")" + }, { "name": "limit", "type": "int", - "default": 50, + "default": 20, "required": false, - "help": "Max connectors to return" + "help": "Max results (1-250)" } ], "columns": [ - "UID", - "Name", - "Brief" + "rank", + "name", + "version", + "description", + "weeklyDownloads", + "dependents", + "license", + "publisher", + "updated", + "url" ], - "type": "js", - "modulePath": "plugins/manus/connectors.js", - "sourceFile": "plugins/manus/connectors.js", - "navigateBefore": true, - "siteSession": "persistent" + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/npm/search.js", + "sourceFile": "plugins/npm/search.js" }, { - "site": "manus", - "name": "credits", - "description": "Show Manus credit balance and refresh details.", + "site": "nuget", + "name": "package", + "description": "Full NuGet package version history (catalogEntry per release)", "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], + "domain": "api.nuget.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "NuGet package id (e.g. \"Newtonsoft.Json\", case-insensitive)" + } + ], "columns": [ - "Field", - "Value" + "rank", + "id", + "version", + "title", + "authors", + "tags", + "language", + "licenseExpression", + "projectUrl", + "published", + "listed", + "url" ], "type": "js", - "modulePath": "plugins/manus/credits.js", - "sourceFile": "plugins/manus/credits.js", - "navigateBefore": true, - "siteSession": "persistent" + "modulePath": "plugins/nuget/package.js", + "sourceFile": "plugins/nuget/package.js" }, { - "site": "manus", - "name": "list", - "description": "List Manus sessions (tasks).", + "site": "nuget", + "name": "search", + "description": "Search NuGet packages by keyword", "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, + "domain": "api.nuget.org", + "strategy": "public", + "browser": false, "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max sessions to return" + "help": "Max packages (1-1000)" }, { - "name": "archived", - "type": "bool", + "name": "prerelease", + "type": "boolean", "default": false, "required": false, - "help": "Include archived sessions" + "help": "Include prerelease versions" } ], "columns": [ + "rank", "id", - "Title", - "Status", - "Last Message", - "Last Updated", - "Credits" + "version", + "title", + "description", + "authors", + "tags", + "totalDownloads", + "verified", + "projectUrl", + "url" ], - "type": "js", - "modulePath": "plugins/manus/list.js", - "sourceFile": "plugins/manus/list.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "login", - "description": "Open manus login", - "access": "write", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/manus/auth.js", - "sourceFile": "plugins/manus/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/nuget/search.js", + "sourceFile": "plugins/nuget/search.js" }, { - "site": "manus", - "name": "read", - "description": "Show details for a specific Manus session.", + "site": "nvd", + "name": "cve", + "description": "NIST NVD CVE detail (description, CVSS, CWE, KEV flag)", "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, + "domain": "services.nvd.nist.gov", + "strategy": "public", + "browser": false, "args": [ { - "name": "uid", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Session UID" + "help": "CVE identifier (e.g. \"CVE-2021-44228\")" } ], "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/manus/read.js", - "sourceFile": "plugins/manus/read.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "skills", - "description": "List Manus skills (user-added and system).", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "ID", - "Name", - "Description", - "Source" - ], - "type": "js", - "modulePath": "plugins/manus/skills.js", - "sourceFile": "plugins/manus/skills.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "status", - "description": "Show current Manus user profile and credit summary.", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/manus/status.js", - "sourceFile": "plugins/manus/status.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "whoami", - "description": "Show the current logged-in manus account", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" + "id", + "published", + "lastModified", + "vulnStatus", + "baseScore", + "severity", + "attackVector", + "cwe", + "kevAdded", + "description", + "url" ], "type": "js", - "modulePath": "plugins/manus/auth.js", - "sourceFile": "plugins/manus/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/nvd/cve.js", + "sourceFile": "plugins/nvd/cve.js" }, { - "site": "maven", - "name": "artifact", - "description": "Fetch a Maven Central artifact's version history (groupId:artifactId[:version])", + "site": "oeis", + "name": "search", + "description": "Search OEIS sequences by keyword or numeric pattern", "access": "read", - "domain": "search.maven.org", + "domain": "oeis.org", "strategy": "public", "browser": false, "args": [ { - "name": "coordinate", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Maven coord \"groupId:artifactId\" or \"groupId:artifactId:version\"" + "help": "Search keyword or comma-separated terms (e.g. \"fibonacci\", \"1,1,2,3,5,8\")" }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Max versions (1-200, ignored when version is pinned)" + "help": "Max sequences (1-100)" } ], "columns": [ - "groupId", - "artifactId", - "version", - "packaging", - "publishedAt", - "tags", + "rank", + "id", + "name", + "keywords", + "preview", + "author", + "created", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/maven/artifact.js", - "sourceFile": "plugins/maven/artifact.js" + "modulePath": "plugins/oeis/search.js", + "sourceFile": "plugins/oeis/search.js" }, { - "site": "maven", - "name": "search", - "description": "Search Maven Central by keyword (artifact name, groupId, tag)", + "site": "oeis", + "name": "sequence", + "description": "Full OEIS sequence detail by A-number (terms, name, keywords, formula counts)", "access": "read", - "domain": "search.maven.org", + "domain": "oeis.org", "strategy": "public", "browser": false, "args": [ { - "name": "query", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"jackson\", \"guava\", \"ai.koog\")" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max artifacts (1-200)" + "help": "OEIS sequence id (e.g. \"A000045\" for Fibonacci)" } ], "columns": [ - "rank", - "coordinate", - "groupId", - "artifactId", - "latestVersion", - "packaging", - "versions", - "lastPublished", - "repository", + "id", + "name", + "keywords", + "preview", + "termCount", + "offset", + "author", + "created", + "revision", + "commentCount", + "formulaCount", + "referenceCount", + "xrefCount", + "linkCount", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/maven/search.js", - "sourceFile": "plugins/maven/search.js" + "modulePath": "plugins/oeis/sequence.js", + "sourceFile": "plugins/oeis/sequence.js" }, { - "site": "mdn", + "site": "openalex", "name": "search", - "description": "Search MDN Web Docs by keyword", + "description": "Search OpenAlex Works (papers, books, preprints) by keyword", "access": "read", - "domain": "developer.mozilla.org", + "domain": "api.openalex.org", "strategy": "public", "browser": false, "args": [ @@ -10836,265 +12637,287 @@ "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"fetch\", \"flexbox\", \"Array.prototype.map\")" + "help": "Search text (e.g. \"transformers\", \"open access scholarly\")" }, { "name": "limit", "type": "int", - "default": 10, - "required": false, - "help": "Max results (1-50)" - }, - { - "name": "locale", - "type": "str", - "default": "en-US", + "default": 20, "required": false, - "help": "Doc locale (en-US default; de / es / fr / ja / ko / pt-BR / ru / zh-CN / zh-TW)" + "help": "Max works (1-200, single OpenAlex page)" } ], "columns": [ "rank", + "id", "title", - "slug", - "locale", - "summary", + "year", + "citations", + "firstAuthor", + "venue", + "openAccess", + "type", + "doi", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/mdn/search.js", - "sourceFile": "plugins/mdn/search.js" + "modulePath": "plugins/openalex/search.js", + "sourceFile": "plugins/openalex/search.js" }, { - "site": "medium", - "name": "feed", - "description": "Medium popular posts Feed", + "site": "openalex", + "name": "work", + "description": "Fetch a single OpenAlex Work (paper / preprint / book) — metadata + abstract", "access": "read", - "domain": "medium.com", - "strategy": "cookie", - "browser": true, + "domain": "api.openalex.org", + "strategy": "public", + "browser": false, "args": [ { - "name": "topic", + "name": "id", "type": "str", - "default": "", - "required": false, - "help": "Topic (for example technology, programming, ai)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" + "required": true, + "positional": true, + "help": "OpenAlex Work id (\"W2741809807\"), DOI (\"10.7717/peerj.4375\"), or full URL" } ], "columns": [ - "rank", + "id", "title", - "author", + "type", + "year", "date", - "readTime", - "claps" + "language", + "authors", + "venue", + "citations", + "openAccess", + "openAccessUrl", + "referencedCount", + "doi", + "abstract", + "url" ], "type": "js", - "modulePath": "plugins/medium/feed.js", - "sourceFile": "plugins/medium/feed.js", - "navigateBefore": "https://medium.com" + "modulePath": "plugins/openalex/work.js", + "sourceFile": "plugins/openalex/work.js" }, { - "site": "medium", - "name": "search", - "description": "Search Medium posts", + "site": "openfda", + "name": "drug-label", + "description": "Search FDA-approved drug labels (brand or generic name)", "access": "read", - "domain": "medium.com", - "strategy": "cookie", - "browser": true, + "domain": "fda.gov", + "strategy": "public", + "browser": false, "args": [ { - "name": "keyword", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Search keyword" + "help": "Brand or generic drug name (e.g. \"aspirin\", \"lisinopril\")" }, { "name": "limit", "type": "int", - "default": 20, + "default": 5, "required": false, - "help": "Number of posts to return" + "help": "Max rows (1-25, default 5; openFDA caps anonymous tier at 25/page)" } ], "columns": [ "rank", - "title", - "author", - "date", - "readTime", - "claps", - "url" - ], - "tags": [ - "search" + "id", + "brandName", + "genericName", + "manufacturer", + "productType", + "route", + "productNdc", + "pharmClass", + "purpose", + "indications", + "warnings", + "dosage", + "effectiveTime" ], "type": "js", - "modulePath": "plugins/medium/search.js", - "sourceFile": "plugins/medium/search.js", - "navigateBefore": "https://medium.com" + "modulePath": "plugins/openfda/drug-label.js", + "sourceFile": "plugins/openfda/drug-label.js" }, { - "site": "medium", - "name": "tag", - "description": "Latest Medium articles tagged with a given keyword (RSS feed)", + "site": "openfda", + "name": "food-recall", + "description": "FDA food recall and enforcement actions (most recent first)", "access": "read", - "domain": "medium.com", + "domain": "fda.gov", "strategy": "public", "browser": false, "args": [ { - "name": "tag", + "name": "query", "type": "str", - "required": true, - "positional": true, - "help": "Lowercase tag slug (e.g. \"programming\", \"machine-learning\")" + "required": false, + "help": "Free-text Lucene query (e.g. \"salmonella\", \"listeria\"); default: all recent recalls" + }, + { + "name": "status", + "type": "str", + "required": false, + "help": "Filter by status: \"Ongoing\", \"Completed\", \"Terminated\"" + }, + { + "name": "classification", + "type": "str", + "required": false, + "help": "Filter by class: \"Class I\" (most serious), \"Class II\", \"Class III\"" }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Max articles (1-25 — single RSS page)" + "help": "Max rows (1-100, default 10; openFDA caps anonymous tier at 100/page)" } ], "columns": [ "rank", - "title", - "author", - "description", - "categories", - "published", - "url" + "recallNumber", + "status", + "classification", + "voluntary", + "recallingFirm", + "city", + "state", + "country", + "productDescription", + "reasonForRecall", + "productQuantity", + "distributionPattern", + "reportDate", + "recallInitiationDate", + "terminationDate" ], "type": "js", - "modulePath": "plugins/medium/tag.js", - "sourceFile": "plugins/medium/tag.js" + "modulePath": "plugins/openfda/food-recall.js", + "sourceFile": "plugins/openfda/food-recall.js" }, { - "site": "medium", - "name": "user", - "description": "Get Medium user posts", + "site": "openreview", + "name": "author", + "description": "List OpenReview submissions by an author profile id (newest first)", "access": "read", - "domain": "medium.com", - "strategy": "cookie", - "browser": true, + "domain": "openreview.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "username", + "name": "profile", "type": "str", "required": true, "positional": true, - "help": "Medium username(for example @username or username)" + "help": "OpenReview profile id (e.g. \"~Yoshua_Bengio1\"). Find it on the author profile URL on openreview.net." }, { "name": "limit", "type": "int", - "default": 20, + "default": 50, "required": false, - "help": "Number of posts to return" + "help": "Max submissions (1-1000)" } ], "columns": [ "rank", - "title", - "date", - "readTime", - "claps", + "id", + "title", + "authors", + "venue", + "pdate", "url" ], "type": "js", - "modulePath": "plugins/medium/user.js", - "sourceFile": "plugins/medium/user.js", - "navigateBefore": "https://medium.com" + "modulePath": "plugins/openreview/author.js", + "sourceFile": "plugins/openreview/author.js" }, { - "site": "npm", - "name": "downloads", - "description": "Daily download counts for an npm package over a window", + "site": "openreview", + "name": "paper", + "description": "Show full metadata for a single OpenReview paper", "access": "read", - "domain": "api.npmjs.org", + "domain": "openreview.net", "strategy": "public", "browser": false, "args": [ { - "name": "name", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "npm package name (e.g. \"react\", \"@vercel/og\")" - }, - { - "name": "period", - "type": "str", - "default": "last-week", - "required": false, - "help": "last-day / last-week / last-month / last-year, or YYYY-MM-DD:YYYY-MM-DD" + "help": "OpenReview note id (e.g. \"5sRnsubyAK\")" } ], "columns": [ - "rank", - "package", - "day", - "downloads" + "id", + "title", + "authors", + "keywords", + "venue", + "venueid", + "primary_area", + "abstract", + "pdate", + "pdf", + "url" ], "type": "js", - "modulePath": "plugins/npm/downloads.js", - "sourceFile": "plugins/npm/downloads.js" + "modulePath": "plugins/openreview/paper.js", + "sourceFile": "plugins/openreview/paper.js" }, { - "site": "npm", - "name": "package", - "description": "Single npm package metadata (latest version, license, homepage, repository). Use `npm downloads` for stats.", + "site": "openreview", + "name": "reviews", + "description": "Show full review thread (paper + reviews + decisions) for an OpenReview forum", "access": "read", - "domain": "registry.npmjs.org", + "domain": "openreview.net", "strategy": "public", "browser": false, "args": [ { - "name": "name", + "name": "forum", "type": "str", "required": true, "positional": true, - "help": "npm package name (e.g. \"react\", \"@vercel/og\")" + "help": "OpenReview forum id (same as paper id)" + }, + { + "name": "max-length", + "type": "int", + "default": 4000, + "required": false, + "help": "Per-row text truncation (min 200)" } ], "columns": [ - "name", - "latestVersion", - "description", - "license", - "homepage", - "repository", - "bugs", - "maintainers", - "keywords", - "created", - "modified", - "url" + "type", + "author", + "rating", + "confidence", + "text" ], "type": "js", - "modulePath": "plugins/npm/package.js", - "sourceFile": "plugins/npm/package.js" + "modulePath": "plugins/openreview/reviews.js", + "sourceFile": "plugins/openreview/reviews.js" }, { - "site": "npm", + "site": "openreview", "name": "search", - "description": "Search the public npm registry by keyword", + "description": "Search OpenReview papers by free-text query", "access": "read", - "domain": "registry.npmjs.org", + "domain": "openreview.net", "strategy": "public", "browser": false, "args": [ @@ -11103,161 +12926,206 @@ "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"react\", \"graphql client\")" + "help": "Search keyword (e.g. \"diffusion model\")" }, { "name": "limit", "type": "int", - "default": 20, + "default": 25, "required": false, - "help": "Max results (1-250)" + "help": "Max results (max 50)" } ], "columns": [ "rank", - "name", - "version", - "description", - "weeklyDownloads", - "dependents", - "license", - "publisher", - "updated", + "id", + "title", + "authors", + "venue", + "pdate", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/npm/search.js", - "sourceFile": "plugins/npm/search.js" + "modulePath": "plugins/openreview/search.js", + "sourceFile": "plugins/openreview/search.js" }, { - "site": "nuget", - "name": "package", - "description": "Full NuGet package version history (catalogEntry per release)", + "site": "openreview", + "name": "venue", + "description": "List papers at an OpenReview venue (e.g. \"ICLR 2024 oral\" or full invitation id)", "access": "read", - "domain": "api.nuget.org", + "domain": "openreview.net", "strategy": "public", "browser": false, "args": [ { - "name": "id", + "name": "venue", "type": "str", "required": true, "positional": true, - "help": "NuGet package id (e.g. \"Newtonsoft.Json\", case-insensitive)" + "help": "Venue name (\"ICLR 2024 oral\") or invitation (\"ICLR.cc/2025/Conference/-/Submission\")" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max results (max 200)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Pagination offset" } ], "columns": [ "rank", "id", - "version", "title", "authors", - "tags", - "language", - "licenseExpression", - "projectUrl", - "published", - "listed", + "keywords", + "primary_area", + "pdate", + "pdf", "url" ], "type": "js", - "modulePath": "plugins/nuget/package.js", - "sourceFile": "plugins/nuget/package.js" + "modulePath": "plugins/openreview/venue.js", + "sourceFile": "plugins/openreview/venue.js" }, { - "site": "nuget", - "name": "search", - "description": "Search NuGet packages by keyword", + "site": "osv", + "name": "query", + "description": "OSV.dev vulnerabilities affecting a package (optionally pinned to a version)", "access": "read", - "domain": "api.nuget.org", + "domain": "osv.dev", "strategy": "public", "browser": false, "args": [ { - "name": "query", - "type": "str", + "name": "package", + "type": "string", "required": true, "positional": true, - "help": "Search keyword" + "help": "Package name (e.g. \"lodash\", \"django\")" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "ecosystem", + "type": "string", + "required": true, + "help": "OSV ecosystem (npm / PyPI / Go / Maven / NuGet / RubyGems / crates.io / Packagist / ...)" + }, + { + "name": "version", + "type": "string", "required": false, - "help": "Max packages (1-1000)" + "help": "Pin to a specific version (e.g. \"4.17.20\"); omit for all known vulns" }, { - "name": "prerelease", - "type": "boolean", - "default": false, + "name": "limit", + "type": "int", + "default": 30, "required": false, - "help": "Include prerelease versions" + "help": "Max rows to return (1-200)" } ], "columns": [ "rank", "id", - "version", - "title", - "description", - "authors", - "tags", - "totalDownloads", - "verified", - "projectUrl", + "summary", + "severity", + "aliases", + "published", + "modified", + "affectedPackages", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/nuget/search.js", - "sourceFile": "plugins/nuget/search.js" + "modulePath": "plugins/osv/query.js", + "sourceFile": "plugins/osv/query.js" }, { - "site": "nvd", - "name": "cve", - "description": "NIST NVD CVE detail (description, CVSS, CWE, KEV flag)", + "site": "osv", + "name": "vulnerability", + "description": "Single OSV.dev vulnerability detail (severity, affected packages, CVE/GHSA aliases)", "access": "read", - "domain": "services.nvd.nist.gov", + "domain": "osv.dev", "strategy": "public", "browser": false, "args": [ { "name": "id", - "type": "str", + "type": "string", "required": true, "positional": true, - "help": "CVE identifier (e.g. \"CVE-2021-44228\")" + "help": "OSV vulnerability id (e.g. \"GHSA-29mw-wpgm-hmr9\", \"CVE-2020-28500\")" } ], "columns": [ "id", - "published", - "lastModified", - "vulnStatus", - "baseScore", + "summary", "severity", - "attackVector", - "cwe", - "kevAdded", + "aliases", + "published", + "modified", + "affectedPackages", + "cwes", + "referenceCount", + "url" + ], + "type": "js", + "modulePath": "plugins/osv/vulnerability.js", + "sourceFile": "plugins/osv/vulnerability.js" + }, + { + "site": "packagist", + "name": "package", + "description": "Fetch a Packagist package's metadata (version, downloads, license, repo, GitHub stars)", + "access": "read", + "domain": "packagist.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Composer package \"/\" (e.g. \"symfony/console\", \"monolog/monolog\")" + } + ], + "columns": [ + "package", + "version", + "releasedAt", + "license", "description", + "repository", + "githubStars", + "favers", + "downloads", + "monthlyDownloads", + "dailyDownloads", "url" ], "type": "js", - "modulePath": "plugins/nvd/cve.js", - "sourceFile": "plugins/nvd/cve.js" + "modulePath": "plugins/packagist/package.js", + "sourceFile": "plugins/packagist/package.js" }, { - "site": "oeis", + "site": "packagist", "name": "search", - "description": "Search OEIS sequences by keyword or numeric pattern", + "description": "Search Packagist (PHP / Composer) packages by keyword", "access": "read", - "domain": "oeis.org", + "domain": "packagist.org", "strategy": "public", "browser": false, "args": [ @@ -11266,607 +13134,765 @@ "type": "str", "required": true, "positional": true, - "help": "Search keyword or comma-separated terms (e.g. \"fibonacci\", \"1,1,2,3,5,8\")" + "help": "Search keyword (e.g. \"symfony\", \"laravel http\")" }, { "name": "limit", "type": "int", - "default": 10, + "default": 30, "required": false, - "help": "Max sequences (1-100)" + "help": "Max packages (1-100, single Packagist page)" } ], "columns": [ "rank", - "id", - "name", - "keywords", - "preview", - "author", - "created", + "package", + "description", + "downloads", + "favers", + "repository", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/oeis/search.js", - "sourceFile": "plugins/oeis/search.js" + "modulePath": "plugins/packagist/search.js", + "sourceFile": "plugins/packagist/search.js" }, { - "site": "oeis", - "name": "sequence", - "description": "Full OEIS sequence detail by A-number (terms, name, keywords, formula counts)", + "site": "pixiv", + "name": "detail", + "description": "View illustration details (tags, stats, URLs)", "access": "read", - "domain": "oeis.org", - "strategy": "public", - "browser": false, + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, "args": [ { "name": "id", "type": "str", "required": true, "positional": true, - "help": "OEIS sequence id (e.g. \"A000045\" for Fibonacci)" + "help": "Illustration ID" } ], "columns": [ - "id", - "name", - "keywords", - "preview", - "termCount", - "offset", + "illust_id", + "title", "author", + "type", + "pages", + "bookmarks", + "likes", + "views", + "tags", "created", - "revision", - "commentCount", - "formulaCount", - "referenceCount", - "xrefCount", - "linkCount", "url" ], "type": "js", - "modulePath": "plugins/oeis/sequence.js", - "sourceFile": "plugins/oeis/sequence.js" + "modulePath": "plugins/pixiv/detail.js", + "sourceFile": "plugins/pixiv/detail.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "openalex", - "name": "search", - "description": "Search OpenAlex Works (papers, books, preprints) by keyword", + "site": "pixiv", + "name": "download", + "description": "Download illustration images from Pixiv", "access": "read", - "domain": "api.openalex.org", - "strategy": "public", - "browser": false, + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "illust-id", "type": "str", "required": true, "positional": true, - "help": "Search text (e.g. \"transformers\", \"open access scholarly\")" + "help": "Illustration ID" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "output", + "type": "str", + "default": "./pixiv-downloads", "required": false, - "help": "Max works (1-200, single OpenAlex page)" + "help": "Output directory" } ], "columns": [ - "rank", - "id", - "title", - "year", - "citations", - "firstAuthor", - "venue", - "openAccess", + "index", "type", - "doi", - "url" - ], - "tags": [ - "search" + "status", + "size" ], "type": "js", - "modulePath": "plugins/openalex/search.js", - "sourceFile": "plugins/openalex/search.js" + "modulePath": "plugins/pixiv/download.js", + "sourceFile": "plugins/pixiv/download.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "openalex", - "name": "work", - "description": "Fetch a single OpenAlex Work (paper / preprint / book) — metadata + abstract", + "site": "pixiv", + "name": "illusts", + "description": "List a Pixiv artist's illustrations", "access": "read", - "domain": "api.openalex.org", - "strategy": "public", - "browser": false, + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "user-id", "type": "str", "required": true, "positional": true, - "help": "OpenAlex Work id (\"W2741809807\"), DOI (\"10.7717/peerj.4375\"), or full URL" + "help": "Pixiv user ID" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" } ], "columns": [ - "id", + "rank", "title", - "type", - "year", - "date", - "language", - "authors", - "venue", - "citations", - "openAccess", - "openAccessUrl", - "referencedCount", - "doi", - "abstract", + "illust_id", + "pages", + "bookmarks", + "tags", + "created", "url" ], "type": "js", - "modulePath": "plugins/openalex/work.js", - "sourceFile": "plugins/openalex/work.js" + "modulePath": "plugins/pixiv/illusts.js", + "sourceFile": "plugins/pixiv/illusts.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "openfda", - "name": "drug-label", - "description": "Search FDA-approved drug labels (brand or generic name)", - "access": "read", - "domain": "fda.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Brand or generic drug name (e.g. \"aspirin\", \"lisinopril\")" - }, - { - "name": "limit", - "type": "int", - "default": 5, - "required": false, - "help": "Max rows (1-25, default 5; openFDA caps anonymous tier at 25/page)" - } - ], + "site": "pixiv", + "name": "login", + "description": "Open pixiv login", + "access": "write", + "domain": "pixiv.net", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "id", - "brandName", - "genericName", - "manufacturer", - "productType", - "route", - "productNdc", - "pharmClass", - "purpose", - "indications", - "warnings", - "dosage", - "effectiveTime" + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/openfda/drug-label.js", - "sourceFile": "plugins/openfda/drug-label.js" + "modulePath": "plugins/pixiv/auth.js", + "sourceFile": "plugins/pixiv/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "openfda", - "name": "food-recall", - "description": "FDA food recall and enforcement actions (most recent first)", + "site": "pixiv", + "name": "ranking", + "description": "Pixiv illustration rankings (daily/weekly/monthly)", "access": "read", - "domain": "fda.gov", - "strategy": "public", - "browser": false, + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", - "type": "str", - "required": false, - "help": "Free-text Lucene query (e.g. \"salmonella\", \"listeria\"); default: all recent recalls" - }, - { - "name": "status", + "name": "mode", "type": "str", + "default": "daily", "required": false, - "help": "Filter by status: \"Ongoing\", \"Completed\", \"Terminated\"" + "help": "Ranking mode", + "choices": [ + "daily", + "weekly", + "monthly", + "rookie", + "original", + "male", + "female", + "daily_r18", + "weekly_r18" + ] }, { - "name": "classification", - "type": "str", + "name": "page", + "type": "int", + "default": 1, "required": false, - "help": "Filter by class: \"Class I\" (most serious), \"Class II\", \"Class III\"" + "help": "Page number" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Max rows (1-100, default 10; openFDA caps anonymous tier at 100/page)" + "help": "Number of results" } ], "columns": [ "rank", - "recallNumber", - "status", - "classification", - "voluntary", - "recallingFirm", - "city", - "state", - "country", - "productDescription", - "reasonForRecall", - "productQuantity", - "distributionPattern", - "reportDate", - "recallInitiationDate", - "terminationDate" + "title", + "author", + "user_id", + "illust_id", + "pages", + "bookmarks", + "url" ], "type": "js", - "modulePath": "plugins/openfda/food-recall.js", - "sourceFile": "plugins/openfda/food-recall.js" - }, - { - "site": "openreview", - "name": "author", - "description": "List OpenReview submissions by an author profile id (newest first)", + "modulePath": "plugins/pixiv/ranking.js", + "sourceFile": "plugins/pixiv/ranking.js", + "navigateBefore": "https://www.pixiv.net" + }, + { + "site": "pixiv", + "name": "search", + "description": "Search Pixiv illustrations by keyword", "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "profile", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "OpenReview profile id (e.g. \"~Yoshua_Bengio1\"). Find it on the author profile URL on openreview.net." + "help": "Search keyword or tag" }, { "name": "limit", "type": "int", - "default": 50, + "default": 20, "required": false, - "help": "Max submissions (1-1000)" + "help": "Number of results" + }, + { + "name": "order", + "type": "str", + "default": "date_d", + "required": false, + "help": "Sort order", + "choices": [ + "date_d", + "date", + "popular_d", + "popular_male_d", + "popular_female_d" + ] + }, + { + "name": "mode", + "type": "str", + "default": "all", + "required": false, + "help": "Search mode", + "choices": [ + "all", + "safe", + "r18" + ] + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number" } ], "columns": [ "rank", - "id", "title", - "authors", - "venue", - "pdate", + "author", + "user_id", + "illust_id", + "pages", + "bookmarks", + "tags", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/openreview/author.js", - "sourceFile": "plugins/openreview/author.js" + "modulePath": "plugins/pixiv/search.js", + "sourceFile": "plugins/pixiv/search.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "openreview", - "name": "paper", - "description": "Show full metadata for a single OpenReview paper", + "site": "pixiv", + "name": "user", + "description": "View Pixiv artist profile", "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "uid", "type": "str", "required": true, "positional": true, - "help": "OpenReview note id (e.g. \"5sRnsubyAK\")" + "help": "Pixiv user ID" } ], "columns": [ - "id", - "title", - "authors", - "keywords", - "venue", - "venueid", - "primary_area", - "abstract", - "pdate", - "pdf", + "user_id", + "name", + "premium", + "following", + "illusts", + "manga", + "novels", + "comment", "url" ], "type": "js", - "modulePath": "plugins/openreview/paper.js", - "sourceFile": "plugins/openreview/paper.js" + "modulePath": "plugins/pixiv/user.js", + "sourceFile": "plugins/pixiv/user.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "openreview", - "name": "reviews", - "description": "Show full review thread (paper + reviews + decisions) for an OpenReview forum", + "site": "pixiv", + "name": "whoami", + "description": "Show the current logged-in pixiv account", "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, + "domain": "pixiv.net", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" + ], + "type": "js", + "modulePath": "plugins/pixiv/auth.js", + "sourceFile": "plugins/pixiv/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "practo", + "name": "appointment", + "description": "Show logged-in Practo Drive appointment details", + "access": "read", + "domain": "drive.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "forum", + "name": "appointment_id", "type": "str", "required": true, "positional": true, - "help": "OpenReview forum id (same as paper id)" - }, - { - "name": "max-length", - "type": "int", - "default": 4000, - "required": false, - "help": "Per-row text truncation (min 200)" + "help": "Appointment id from `practo appointments`" } ], "columns": [ - "type", - "author", - "rating", - "confidence", - "text" + "appointment_id", + "status", + "summary" ], "type": "js", - "modulePath": "plugins/openreview/reviews.js", - "sourceFile": "plugins/openreview/reviews.js" + "modulePath": "plugins/practo/appointment.js", + "sourceFile": "plugins/practo/appointment.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "openreview", - "name": "search", - "description": "Search OpenReview papers by free-text query", + "site": "practo", + "name": "appointments", + "description": "List logged-in Practo Drive appointments", "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, + "domain": "drive.practo.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "appointment_id", + "doctor", + "practice", + "time", + "status" + ], + "type": "js", + "modulePath": "plugins/practo/appointments.js", + "sourceFile": "plugins/practo/appointments.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "practo", + "name": "book-confirm", + "description": "Confirm a Practo clinic visit booking after explicit confirmation", + "access": "write", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "practice_doctor_id", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"diffusion model\")" + "help": "Practo practice_doctor_id" }, { - "name": "limit", - "type": "int", - "default": 25, + "name": "time", + "type": "str", + "required": true, + "help": "Slot time YYYY-MM-DD HH:mm:ss" + }, + { + "name": "profile-url", + "type": "str", "required": false, - "help": "Max results (max 50)" + "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "Required. Set --confirm true to create the appointment." } ], "columns": [ - "rank", - "id", - "title", - "authors", - "venue", - "pdate", + "status", + "practice_doctor_id", + "time", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/openreview/search.js", - "sourceFile": "plugins/openreview/search.js" + "modulePath": "plugins/practo/book-confirm.js", + "sourceFile": "plugins/practo/book-confirm.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "openreview", - "name": "venue", - "description": "List papers at an OpenReview venue (e.g. \"ICLR 2024 oral\" or full invitation id)", + "site": "practo", + "name": "book-preview", + "description": "Preview Practo booking details for a selected slot without confirming", "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "venue", + "name": "practice_doctor_id", "type": "str", "required": true, "positional": true, - "help": "Venue name (\"ICLR 2024 oral\") or invitation (\"ICLR.cc/2025/Conference/-/Submission\")" + "help": "Practo practice_doctor_id" }, { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max results (max 200)" + "name": "time", + "type": "str", + "required": true, + "help": "Slot time YYYY-MM-DD HH:mm:ss" }, { - "name": "offset", - "type": "int", - "default": 0, + "name": "profile-url", + "type": "str", "required": false, - "help": "Pagination offset" + "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" } ], "columns": [ - "rank", - "id", - "title", - "authors", - "keywords", - "primary_area", - "pdate", - "pdf", - "url" + "practice_doctor_id", + "time", + "amount", + "prepaid", + "payment_mode", + "requires_payment", + "confirm_button", + "booking_url" ], "type": "js", - "modulePath": "plugins/openreview/venue.js", - "sourceFile": "plugins/openreview/venue.js" + "modulePath": "plugins/practo/book-preview.js", + "sourceFile": "plugins/practo/book-preview.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "osv", - "name": "query", - "description": "OSV.dev vulnerabilities affecting a package (optionally pinned to a version)", + "site": "practo", + "name": "booking-link", + "description": "Build a Practo booking URL for a selected slot without confirming it", "access": "read", - "domain": "osv.dev", - "strategy": "public", - "browser": false, + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "package", - "type": "string", + "name": "practice_doctor_id", + "type": "str", "required": true, "positional": true, - "help": "Package name (e.g. \"lodash\", \"django\")" + "help": "Practo practice_doctor_id" }, { - "name": "ecosystem", - "type": "string", + "name": "time", + "type": "str", "required": true, - "help": "OSV ecosystem (npm / PyPI / Go / Maven / NuGet / RubyGems / crates.io / Packagist / ...)" + "help": "Slot time YYYY-MM-DD HH:mm:ss" }, { - "name": "version", - "type": "string", + "name": "profile-url", + "type": "str", "required": false, - "help": "Pin to a specific version (e.g. \"4.17.20\"); omit for all known vulns" - }, + "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" + } + ], + "columns": [ + "practice_doctor_id", + "time", + "booking_url" + ], + "type": "js", + "modulePath": "plugins/practo/booking-link.js", + "sourceFile": "plugins/practo/booking-link.js", + "navigateBefore": false + }, + { + "site": "practo", + "name": "cancel", + "description": "Cancel a logged-in Practo Drive appointment after explicit confirmation", + "access": "write", + "domain": "drive.practo.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "limit", - "type": "int", - "default": 30, + "name": "appointment_id", + "type": "str", + "required": true, + "positional": true, + "help": "Appointment id from `practo appointments`" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, "required": false, - "help": "Max rows to return (1-200)" + "help": "Required. Set --confirm true to cancel the appointment." } ], "columns": [ - "rank", - "id", - "summary", - "severity", - "aliases", - "published", - "modified", - "affectedPackages", - "url" - ], - "tags": [ - "search" + "status", + "appointment_id" ], "type": "js", - "modulePath": "plugins/osv/query.js", - "sourceFile": "plugins/osv/query.js" + "modulePath": "plugins/practo/cancel.js", + "sourceFile": "plugins/practo/cancel.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "osv", - "name": "vulnerability", - "description": "Single OSV.dev vulnerability detail (severity, affected packages, CVE/GHSA aliases)", + "site": "practo", + "name": "contact", + "description": "Get Practo virtual contact number for a practice_doctor_id", "access": "read", - "domain": "osv.dev", - "strategy": "public", - "browser": false, + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", - "type": "string", + "name": "practice_doctor_id", + "type": "str", "required": true, "positional": true, - "help": "OSV vulnerability id (e.g. \"GHSA-29mw-wpgm-hmr9\", \"CVE-2020-28500\")" + "help": "Practo practice_doctor_id from search results" } ], "columns": [ - "id", - "summary", - "severity", - "aliases", - "published", - "modified", - "affectedPackages", - "cwes", - "referenceCount", - "url" + "practice_doctor_id", + "phone", + "raw" ], "type": "js", - "modulePath": "plugins/osv/vulnerability.js", - "sourceFile": "plugins/osv/vulnerability.js" + "modulePath": "plugins/practo/contact.js", + "sourceFile": "plugins/practo/contact.js", + "navigateBefore": false }, { - "site": "packagist", - "name": "package", - "description": "Fetch a Packagist package's metadata (version, downloads, license, repo, GitHub stars)", + "site": "practo", + "name": "login", + "description": "Open practo login", + "access": "write", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/practo/login.js", + "sourceFile": "plugins/practo/login.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "practo", + "name": "profile", + "description": "Read public details from a Practo doctor profile URL", "access": "read", - "domain": "packagist.org", - "strategy": "public", - "browser": false, + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "name", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Composer package \"/\" (e.g. \"symfony/console\", \"monolog/monolog\")" + "help": "Practo doctor profile URL" } ], "columns": [ - "package", - "version", - "releasedAt", - "license", - "description", - "repository", - "githubStars", - "favers", - "downloads", - "monthlyDownloads", - "dailyDownloads", - "url" + "name", + "specialty", + "experience", + "fee", + "profile_url" ], "type": "js", - "modulePath": "plugins/packagist/package.js", - "sourceFile": "plugins/packagist/package.js" + "modulePath": "plugins/practo/profile.js", + "sourceFile": "plugins/practo/profile.js", + "navigateBefore": false }, { - "site": "packagist", + "site": "practo", "name": "search", - "description": "Search Packagist (PHP / Composer) packages by keyword", + "description": "Search Practo doctors by specialty, city, and optional locality", "access": "read", - "domain": "packagist.org", - "strategy": "public", - "browser": false, + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "specialty", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"symfony\", \"laravel http\")" + "help": "Doctor specialty, e.g. orthopedist or dermatologist" + }, + { + "name": "city", + "type": "str", + "default": "bangalore", + "required": false, + "help": "City, e.g. bangalore" + }, + { + "name": "locality", + "type": "str", + "required": false, + "help": "Optional locality, e.g. indiranagar" }, { "name": "limit", "type": "int", - "default": 30, + "default": 10, "required": false, - "help": "Max packages (1-100, single Packagist page)" + "help": "Max doctors to return (1-25)" } ], "columns": [ "rank", - "package", - "description", - "downloads", - "favers", - "repository", - "url" + "practice_doctor_id", + "doctor_id", + "practice_id", + "name", + "specialty", + "experience_years", + "locality", + "clinic", + "fee", + "next_available", + "profile_url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/packagist/search.js", - "sourceFile": "plugins/packagist/search.js" + "modulePath": "plugins/practo/search.js", + "sourceFile": "plugins/practo/search.js", + "navigateBefore": false + }, + { + "site": "practo", + "name": "slots", + "description": "List available Practo appointment slots for a practice_doctor_id", + "access": "read", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "practice_doctor_id", + "type": "str", + "required": true, + "positional": true, + "help": "Practo practice_doctor_id from search results" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max slots to return (1-25)" + } + ], + "columns": [ + "practice_doctor_id", + "time", + "available", + "amount", + "prepaid", + "appointment_token" + ], + "type": "js", + "modulePath": "plugins/practo/slots.js", + "sourceFile": "plugins/practo/slots.js", + "navigateBefore": false + }, + { + "site": "practo", + "name": "whoami", + "aliases": [ + "auth-status" + ], + "description": "Show the current logged-in practo account", + "access": "read", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name" + ], + "type": "js", + "modulePath": "plugins/practo/login.js", + "sourceFile": "plugins/practo/login.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "producthunt", @@ -12765,46 +14791,165 @@ "browser": false, "args": [ { - "name": "region", + "name": "region", + "type": "str", + "required": true, + "positional": true, + "help": "Region name (case-insensitive)" + }, + { + "name": "limit", + "type": "int", + "default": 250, + "required": false, + "help": "Max rows (1-250)" + } + ], + "columns": [ + "rank", + "commonName", + "officialName", + "cca2", + "cca3", + "ccn3", + "capital", + "region", + "subregion", + "population", + "area", + "languages", + "currencies", + "latitude", + "longitude", + "timezones", + "independent", + "unMember", + "landlocked", + "flag", + "url" + ], + "type": "js", + "modulePath": "plugins/rest-countries/region.js", + "sourceFile": "plugins/rest-countries/region.js" + }, + { + "site": "reuters", + "name": "article-detail", + "description": "Reuters Reuters article detail:title/author/body text", + "access": "read", + "domain": "www.reuters.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "Reuters article URL (must be on reuters.com)" + } + ], + "columns": [ + "title", + "date", + "section", + "section_path", + "authors", + "description", + "word_count", + "url", + "body" + ], + "type": "js", + "modulePath": "plugins/reuters/article-detail.js", + "sourceFile": "plugins/reuters/article-detail.js", + "navigateBefore": "https://www.reuters.com" + }, + { + "site": "reuters", + "name": "login", + "description": "Open reuters login", + "access": "write", + "domain": "reuters.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "subscribed", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/reuters/auth.js", + "sourceFile": "plugins/reuters/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "reuters", + "name": "search", + "description": "Reuters Reuters news search", + "access": "read", + "domain": "www.reuters.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Region name (case-insensitive)" + "help": "Search query" }, { "name": "limit", "type": "int", - "default": 250, + "default": 10, "required": false, - "help": "Max rows (1-250)" + "help": "Number of results (1-40)" } ], "columns": [ "rank", - "commonName", - "officialName", - "cca2", - "cca3", - "ccn3", - "capital", - "region", - "subregion", - "population", - "area", - "languages", - "currencies", - "latitude", - "longitude", - "timezones", - "independent", - "unMember", - "landlocked", - "flag", + "title", + "date", + "section", + "section_path", + "authors", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/rest-countries/region.js", - "sourceFile": "plugins/rest-countries/region.js" + "modulePath": "plugins/reuters/search.js", + "sourceFile": "plugins/reuters/search.js", + "navigateBefore": "https://www.reuters.com" + }, + { + "site": "reuters", + "name": "whoami", + "description": "Show the current logged-in reuters account", + "access": "read", + "domain": "reuters.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "subscribed" + ], + "type": "js", + "modulePath": "plugins/reuters/auth.js", + "sourceFile": "plugins/reuters/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "rfc", @@ -13601,118 +15746,386 @@ "browser": true, "args": [ { - "name": "category", - "type": "str", - "default": "all", + "name": "category", + "type": "str", + "default": "all", + "required": false, + "help": "Post category: all, tech, business, culture, politics, science, health" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" + } + ], + "columns": [ + "rank", + "title", + "author", + "date", + "readTime", + "url" + ], + "type": "js", + "modulePath": "plugins/substack/feed.js", + "sourceFile": "plugins/substack/feed.js", + "navigateBefore": "https://substack.com" + }, + { + "site": "substack", + "name": "publication", + "description": "Get a specific Substack Newsletter latest posts", + "access": "read", + "domain": "substack.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "Newsletter URL(for example https://example.substack.com)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" + } + ], + "columns": [ + "rank", + "title", + "date", + "description", + "url" + ], + "type": "js", + "modulePath": "plugins/substack/publication.js", + "sourceFile": "plugins/substack/publication.js", + "navigateBefore": "https://substack.com" + }, + { + "site": "substack", + "name": "search", + "description": "Search Substack posts and newsletters", + "access": "read", + "domain": "substack.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "type", + "type": "str", + "default": "posts", + "required": false, + "help": "Search type(posts=posts, publications=Newsletter)", + "choices": [ + "posts", + "publications" + ] + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results to return" + } + ], + "columns": [ + "rank", + "title", + "author", + "date", + "description", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/substack/search.js", + "sourceFile": "plugins/substack/search.js" + }, + { + "site": "suno", + "name": "download", + "description": "Download an existing Suno clip (MP3 + optional WAV/M4A/video) by id", + "access": "write", + "domain": "suno.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "clip", + "type": "str", + "required": true, + "positional": true, + "help": "Clip UUID or https://suno.com/song/ URL" + }, + { + "name": "formats", + "type": "str", + "required": false, + "help": "Comma-separated formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" + }, + { + "name": "op", + "type": "str", + "required": false, + "help": "Output directory (default: ~/Music/suno)" + }, + { + "name": "confirm-paid", + "type": "boolean", + "default": false, + "required": false, + "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." + } + ], + "columns": [ + "status", + "clip", + "title", + "files", + "link" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "plugins/suno/download.js", + "sourceFile": "plugins/suno/download.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "suno", + "name": "generate", + "description": "Generate music with Suno (V5.5 chirp-fenix by default) and download clips locally", + "access": "write", + "domain": "suno.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": false, + "positional": true, + "help": "Simple-mode description (ignored when --lyrics is provided)" + }, + { + "name": "lyrics", + "type": "str", + "required": false, + "help": "Custom-mode lyrics (with [Verse]/[Chorus] metatags). Triggers Custom mode." + }, + { + "name": "tags", + "type": "str", + "required": false, + "help": "Custom-mode style tags (genre, BPM, instruments...). Used with --lyrics." + }, + { + "name": "negative-tags", + "type": "str", + "required": false, + "help": "Custom-mode style exclusions (e.g. \"no vocals, no autotune\"). Used with --lyrics." + }, + { + "name": "title", + "type": "str", + "required": false, + "help": "Song title (default: auto-derived from prompt)" + }, + { + "name": "instrumental", + "type": "boolean", + "default": false, + "required": false, + "help": "No vocals" + }, + { + "name": "model", + "type": "str", + "required": false, + "help": "Model id: chirp-fenix, chirp-bluejay, chirp-v4, chirp-v3-5. Default: chirp-fenix" + }, + { + "name": "weirdness", + "type": "str", + "required": false, + "help": "Creative weirdness slider (0..1). Default: 0.5" + }, + { + "name": "style-weight", + "type": "str", + "required": false, + "help": "Style adherence slider (0..1). Default: 0.5" + }, + { + "name": "formats", + "type": "str", + "required": false, + "help": "Comma-separated download formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" + }, + { + "name": "op", + "type": "str", + "required": false, + "help": "Output directory (default: ~/Music/suno)" + }, + { + "name": "timeout", + "type": "int", + "default": 300, + "required": false, + "help": "Max seconds to wait for clips to finish (default: 300)" + }, + { + "name": "sd", + "type": "boolean", + "default": false, "required": false, - "help": "Post category: all, tech, business, culture, politics, science, health" + "help": "Skip download; only print clip ids and Suno URLs" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "confirm-paid", + "type": "boolean", + "default": false, "required": false, - "help": "Number of posts to return" + "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." } ], "columns": [ - "rank", + "status", + "clip", "title", - "author", - "date", - "readTime", - "url" + "files", + "link" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/substack/feed.js", - "sourceFile": "plugins/substack/feed.js", - "navigateBefore": "https://substack.com" + "modulePath": "plugins/suno/generate.js", + "sourceFile": "plugins/suno/generate.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "substack", - "name": "publication", - "description": "Get a specific Substack Newsletter latest posts", + "site": "suno", + "name": "list", + "description": "List recent Suno clips in your library (id, title, status, created_at, link)", "access": "read", - "domain": "substack.com", + "domain": "suno.com", "strategy": "cookie", "browser": true, "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "Newsletter URL(for example https://example.substack.com)" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of posts to return" + "help": "Max clips to list (default: 20)" + }, + { + "name": "page", + "type": "int", + "default": 0, + "required": false, + "help": "Pagination offset, 0-based (default: 0)" } ], "columns": [ "rank", + "clip", "title", - "date", - "description", - "url" + "status", + "created", + "link" ], "type": "js", - "modulePath": "plugins/substack/publication.js", - "sourceFile": "plugins/substack/publication.js", - "navigateBefore": "https://substack.com" + "modulePath": "plugins/suno/list.js", + "sourceFile": "plugins/suno/list.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "substack", - "name": "search", - "description": "Search Substack posts and newsletters", - "access": "read", - "domain": "substack.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "type", - "type": "str", - "default": "posts", - "required": false, - "help": "Search type(posts=posts, publications=Newsletter)", - "choices": [ - "posts", - "publications" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results to return" - } + "site": "suno", + "name": "login", + "description": "Open suno login", + "access": "write", + "domain": "suno.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" ], + "type": "js", + "modulePath": "plugins/suno/auth.js", + "sourceFile": "plugins/suno/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "suno", + "name": "status", + "description": "Check Suno login, plan, credit balance, and captcha readiness", + "access": "read", + "domain": "suno.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "title", - "author", - "date", - "description", - "url" + "Status", + "Plan", + "Credits", + "Monthly", + "Captcha" ], - "tags": [ - "search" + "type": "js", + "modulePath": "plugins/suno/status.js", + "sourceFile": "plugins/suno/status.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "suno", + "name": "whoami", + "description": "Show the current logged-in suno account", + "access": "read", + "domain": "suno.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" ], "type": "js", - "modulePath": "plugins/substack/search.js", - "sourceFile": "plugins/substack/search.js" + "modulePath": "plugins/suno/auth.js", + "sourceFile": "plugins/suno/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "techcrunch", @@ -14768,42 +17181,252 @@ "name": "preview", "description": "Capture a screenshot of the Uiverse preview element", "access": "read", - "domain": "uiverse.io", - "strategy": "public", + "domain": "uiverse.io", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "input", + "type": "str", + "required": true, + "positional": true, + "help": "Uiverse URL or author/slug identifier" + }, + { + "name": "output", + "type": "str", + "required": false, + "help": "Output image path (defaults to a temp file)" + }, + { + "name": "padding", + "type": "int", + "default": 8, + "required": false, + "help": "Extra padding around the captured preview in pixels" + } + ], + "columns": [ + "username", + "slug", + "width", + "height", + "output" + ], + "type": "js", + "modulePath": "plugins/uiverse/preview.js", + "sourceFile": "plugins/uiverse/preview.js", + "navigateBefore": "https://uiverse.io" + }, + { + "site": "upwork", + "name": "detail", + "aliases": [ + "job", + "view" + ], + "description": "Read the full Upwork job posting by ciphertext id (e.g. ~022054964136512093518)", + "access": "read", + "domain": "www.upwork.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Job ciphertext id (~01… / ~02…) or full /jobs/~02… URL" + } + ], + "columns": [ + "id", + "title", + "type", + "budget", + "experienceLevel", + "workload", + "category", + "skills", + "description", + "clientCountry", + "clientSpent", + "clientHires", + "clientRating", + "proposalsCount", + "publishedOn", + "url" + ], + "type": "js", + "modulePath": "plugins/upwork/detail.js", + "sourceFile": "plugins/upwork/detail.js", + "navigateBefore": false + }, + { + "site": "upwork", + "name": "feed", + "aliases": [ + "best-matches" + ], + "description": "Upwork personalized jobs feed (best-matches | most-recent) — requires login", + "access": "read", + "domain": "www.upwork.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "tab", + "type": "str", + "default": "best-matches", + "required": false, + "positional": true, + "help": "Feed tab: best-matches | most-recent" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max rows to return (1-50, capped at one page)" + } + ], + "columns": [ + "rank", + "id", + "title", + "type", + "budget", + "experienceLevel", + "proposalsTier", + "skills", + "clientCountry", + "clientRating", + "publishedOn", + "url" + ], + "type": "js", + "modulePath": "plugins/upwork/feed.js", + "sourceFile": "plugins/upwork/feed.js", + "navigateBefore": false + }, + { + "site": "upwork", + "name": "login", + "description": "Open upwork login", + "access": "write", + "domain": "upwork.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "ciphertext", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/upwork/auth.js", + "sourceFile": "plugins/upwork/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "upwork", + "name": "search", + "description": "Upwork keyword job search (logged-in browser session, US site)", + "access": "read", + "domain": "www.upwork.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "input", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Uiverse URL or author/slug identifier" + "help": "Job keyword (skill / title / company)" }, { - "name": "output", - "type": "str", + "name": "location", + "type": "string", + "default": "", "required": false, - "help": "Output image path (defaults to a temp file)" + "help": "Country/city filter (e.g. \"United States\", \"Remote\")" }, { - "name": "padding", + "name": "category", + "type": "string", + "default": "", + "required": false, + "help": "Category uid filter (advanced; from job detail `category` slug)" + }, + { + "name": "sort", + "type": "string", + "default": "recency", + "required": false, + "help": "Sort: recency | relevance | client_total_charge | client_total_reviews" + }, + { + "name": "page", "type": "int", - "default": 8, + "default": 1, "required": false, - "help": "Extra padding around the captured preview in pixels" + "help": "Page number (1-based)" + }, + { + "name": "per_page", + "type": "int", + "default": 10, + "required": false, + "help": "Rows per page (10-50, capped at one page)" } ], "columns": [ - "username", - "slug", - "width", - "height", - "output" + "rank", + "id", + "title", + "type", + "budget", + "experienceLevel", + "proposalsTier", + "skills", + "clientCountry", + "clientRating", + "publishedOn", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/uiverse/preview.js", - "sourceFile": "plugins/uiverse/preview.js", - "navigateBefore": "https://uiverse.io" + "modulePath": "plugins/upwork/search.js", + "sourceFile": "plugins/upwork/search.js", + "navigateBefore": false + }, + { + "site": "upwork", + "name": "whoami", + "description": "Show the current logged-in upwork account", + "access": "read", + "domain": "upwork.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "ciphertext" + ], + "type": "js", + "modulePath": "plugins/upwork/auth.js", + "sourceFile": "plugins/upwork/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "web", @@ -15501,6 +18124,253 @@ "sourceFile": "plugins/ycombinator/company.js", "navigateBefore": false }, + { + "site": "zepto", + "name": "add-to-cart", + "description": "Add a Zepto product to cart", + "access": "write", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product", + "type": "str", + "required": true, + "positional": true, + "help": "Product URL from Zepto search results" + }, + { + "name": "quantity", + "type": "int", + "default": 1, + "required": false, + "help": "Quantity to add (max 12)" + } + ], + "columns": [ + "ok", + "product_id", + "quantity", + "item_count", + "message" + ], + "type": "js", + "modulePath": "plugins/zepto/add-to-cart.js", + "sourceFile": "plugins/zepto/add-to-cart.js", + "navigateBefore": false + }, + { + "site": "zepto", + "name": "cart", + "description": "Read Zepto cart line items", + "access": "read", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "rank", + "product_id", + "title", + "pack_size", + "quantity", + "price", + "mrp", + "availability" + ], + "type": "js", + "modulePath": "plugins/zepto/cart.js", + "sourceFile": "plugins/zepto/cart.js", + "navigateBefore": false + }, + { + "site": "zepto", + "name": "checkout", + "description": "Open Zepto checkout review without placing an order", + "access": "write", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "ok", + "stage", + "item_count", + "next_action", + "url" + ], + "type": "js", + "modulePath": "plugins/zepto/checkout.js", + "sourceFile": "plugins/zepto/checkout.js", + "navigateBefore": false + }, + { + "site": "zepto", + "name": "location", + "description": "Show the selected Zepto delivery location", + "access": "read", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "selected", + "label", + "area", + "city", + "pincode", + "hasCoordinates", + "source" + ], + "type": "js", + "modulePath": "plugins/zepto/location.js", + "sourceFile": "plugins/zepto/location.js", + "navigateBefore": false + }, + { + "site": "zepto", + "name": "login", + "description": "Open zepto login", + "access": "write", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/zepto/auth.js", + "sourceFile": "plugins/zepto/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "zepto", + "name": "place-order", + "description": "Submit a real Zepto order only when --confirm true is passed", + "access": "write", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "Required. Set true to submit a real Zepto order/payment action." + } + ], + "columns": [ + "status", + "confirmed", + "message" + ], + "type": "js", + "modulePath": "plugins/zepto/place-order.js", + "sourceFile": "plugins/zepto/place-order.js", + "navigateBefore": false + }, + { + "site": "zepto", + "name": "product", + "description": "Read Zepto product details", + "access": "read", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product", + "type": "str", + "required": true, + "positional": true, + "help": "Product URL from Zepto search results" + } + ], + "columns": [ + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "availability", + "url" + ], + "type": "js", + "modulePath": "plugins/zepto/product.js", + "sourceFile": "plugins/zepto/product.js", + "navigateBefore": false + }, + { + "site": "zepto", + "name": "search", + "description": "Search Zepto products", + "access": "read", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum products to return (max 50)" + } + ], + "columns": [ + "rank", + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "availability", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/zepto/search.js", + "sourceFile": "plugins/zepto/search.js", + "navigateBefore": false + }, + { + "site": "zepto", + "name": "whoami", + "description": "Show the current logged-in zepto account", + "access": "read", + "domain": "www.zepto.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site" + ], + "type": "js", + "modulePath": "plugins/zepto/auth.js", + "sourceFile": "plugins/zepto/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, { "site": "zlibrary", "name": "info", diff --git a/plugins/chatgpt/README.md b/plugins/chatgpt/README.md new file mode 100644 index 00000000..a1bcd75c --- /dev/null +++ b/plugins/chatgpt/README.md @@ -0,0 +1,28 @@ +# webcmd-plugin-chatgpt + +Webcmd commands for chatgpt. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/chatgpt +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd chatgpt ask` | Send a prompt to ChatGPT web and wait for the response | +| `webcmd chatgpt deep-research-result` | Read a ChatGPT Deep Research report or progress from the conversation payload | +| `webcmd chatgpt detail` | Open a ChatGPT web conversation by ID and read its messages | +| `webcmd chatgpt history` | List visible ChatGPT web conversation history from the sidebar | +| `webcmd chatgpt image` | Generate images with ChatGPT web and save them locally | +| `webcmd chatgpt login` | Open chatgpt login | +| `webcmd chatgpt model` | Switch ChatGPT web model or intelligence level (GPT-5.6 Pro, fast, balanced, advanced, very-high, pro) | +| `webcmd chatgpt new` | Start a new ChatGPT web conversation | +| `webcmd chatgpt project-file-add` | Upload files to a ChatGPT project as project knowledge (not just conversation attachments) | +| `webcmd chatgpt project-list` | List visible ChatGPT projects from the sidebar | +| `webcmd chatgpt read` | Read messages in the current ChatGPT web conversation | +| `webcmd chatgpt send` | Send a prompt to ChatGPT web without waiting for the response | +| `webcmd chatgpt status` | Check ChatGPT web page availability and login state | +| `webcmd chatgpt whoami` | Show the current logged-in chatgpt account | diff --git a/clis/chatgpt/ask.js b/plugins/chatgpt/ask.js similarity index 100% rename from clis/chatgpt/ask.js rename to plugins/chatgpt/ask.js diff --git a/clis/chatgpt/auth.js b/plugins/chatgpt/auth.js similarity index 96% rename from clis/chatgpt/auth.js rename to plugins/chatgpt/auth.js index 708480f2..71501f6a 100644 --- a/clis/chatgpt/auth.js +++ b/plugins/chatgpt/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasChatgptSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://chatgpt.com' }); diff --git a/clis/chatgpt/deep-research-result.js b/plugins/chatgpt/deep-research-result.js similarity index 100% rename from clis/chatgpt/deep-research-result.js rename to plugins/chatgpt/deep-research-result.js diff --git a/clis/chatgpt/detail.js b/plugins/chatgpt/detail.js similarity index 100% rename from clis/chatgpt/detail.js rename to plugins/chatgpt/detail.js diff --git a/clis/chatgpt/history.js b/plugins/chatgpt/history.js similarity index 100% rename from clis/chatgpt/history.js rename to plugins/chatgpt/history.js diff --git a/clis/chatgpt/image.js b/plugins/chatgpt/image.js similarity index 100% rename from clis/chatgpt/image.js rename to plugins/chatgpt/image.js diff --git a/clis/chatgpt/model.js b/plugins/chatgpt/model.js similarity index 100% rename from clis/chatgpt/model.js rename to plugins/chatgpt/model.js diff --git a/clis/chatgpt/new.js b/plugins/chatgpt/new.js similarity index 100% rename from clis/chatgpt/new.js rename to plugins/chatgpt/new.js diff --git a/plugins/chatgpt/package.json b/plugins/chatgpt/package.json new file mode 100644 index 00000000..62dd1668 --- /dev/null +++ b/plugins/chatgpt/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-chatgpt", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for chatgpt", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/chatgpt/project-file-add.js b/plugins/chatgpt/project-file-add.js similarity index 100% rename from clis/chatgpt/project-file-add.js rename to plugins/chatgpt/project-file-add.js diff --git a/clis/chatgpt/project-list.js b/plugins/chatgpt/project-list.js similarity index 100% rename from clis/chatgpt/project-list.js rename to plugins/chatgpt/project-list.js diff --git a/clis/chatgpt/read.js b/plugins/chatgpt/read.js similarity index 100% rename from clis/chatgpt/read.js rename to plugins/chatgpt/read.js diff --git a/clis/chatgpt/send.js b/plugins/chatgpt/send.js similarity index 100% rename from clis/chatgpt/send.js rename to plugins/chatgpt/send.js diff --git a/clis/chatgpt/status.js b/plugins/chatgpt/status.js similarity index 100% rename from clis/chatgpt/status.js rename to plugins/chatgpt/status.js diff --git a/clis/chatgpt/ask.test.js b/plugins/chatgpt/test/ask.test.js similarity index 89% rename from clis/chatgpt/ask.test.js rename to plugins/chatgpt/test/ask.test.js index 30d405b1..baa56831 100644 --- a/clis/chatgpt/ask.test.js +++ b/plugins/chatgpt/test/ask.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { askCommand } from './ask.js'; +import { askCommand } from '../ask.js'; describe('chatgpt ask polling', () => { it('uses pure sleep while waiting for an active generation to finish', () => { diff --git a/clis/chatgpt/commands.test.js b/plugins/chatgpt/test/commands.test.js similarity index 98% rename from clis/chatgpt/commands.test.js rename to plugins/chatgpt/test/commands.test.js index bed7c4dd..3884781e 100644 --- a/clis/chatgpt/commands.test.js +++ b/plugins/chatgpt/test/commands.test.js @@ -4,18 +4,18 @@ import path from 'node:path'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './ask.js'; -import './send.js'; -import './read.js'; -import './history.js'; -import './detail.js'; -import './deep-research-result.js'; -import './new.js'; -import './status.js'; -import './image.js'; -import './model.js'; -import './project-list.js'; -import './project-file-add.js'; +import '../ask.js'; +import '../send.js'; +import '../read.js'; +import '../history.js'; +import '../detail.js'; +import '../deep-research-result.js'; +import '../new.js'; +import '../status.js'; +import '../image.js'; +import '../model.js'; +import '../project-list.js'; +import '../project-file-add.js'; const tempDirs = []; diff --git a/clis/chatgpt/envelope.test.js b/plugins/chatgpt/test/envelope.test.js similarity index 99% rename from clis/chatgpt/envelope.test.js rename to plugins/chatgpt/test/envelope.test.js index 5ff39e32..fea05b97 100644 --- a/clis/chatgpt/envelope.test.js +++ b/plugins/chatgpt/test/envelope.test.js @@ -5,7 +5,7 @@ import { requireBooleanEvaluateResult, requireObjectEvaluateResult, unwrapEvaluateResult, -} from './utils.js'; +} from '../utils.js'; describe('chatgpt page.evaluate envelope helpers', () => { describe('unwrapEvaluateResult', () => { diff --git a/clis/chatgpt/image.test.js b/plugins/chatgpt/test/image.test.js similarity index 99% rename from clis/chatgpt/image.test.js rename to plugins/chatgpt/test/image.test.js index 7466121a..b4ebed2e 100644 --- a/clis/chatgpt/image.test.js +++ b/plugins/chatgpt/test/image.test.js @@ -14,7 +14,7 @@ const mocks = vi.hoisted(() => ({ saveBase64ToFile: vi.fn(), })); -vi.mock('./utils.js', () => ({ +vi.mock('../utils.js', () => ({ clearChatGPTDraft: mocks.clearChatGPTDraft, getChatGPTVisibleImageUrls: mocks.getChatGPTVisibleImageUrls, navigateToProject: mocks.navigateToProject, @@ -41,7 +41,7 @@ vi.mock('@agentrhq/webcmd/utils', () => ({ saveBase64ToFile: mocks.saveBase64ToFile, })); -const { imageCommand, nextAvailablePath, parseImagePaths, resolveOutputDir } = await import('./image.js'); +const { imageCommand, nextAvailablePath, parseImagePaths, resolveOutputDir } = await import('../image.js'); function createPage() { return { diff --git a/clis/chatgpt/model.test.js b/plugins/chatgpt/test/model.test.js similarity index 94% rename from clis/chatgpt/model.test.js rename to plugins/chatgpt/test/model.test.js index 6635159c..be7002cf 100644 --- a/clis/chatgpt/model.test.js +++ b/plugins/chatgpt/test/model.test.js @@ -5,14 +5,14 @@ const mocks = vi.hoisted(() => ({ selectChatGPTModel: vi.fn(), })); -vi.mock('./utils.js', () => ({ +vi.mock('../utils.js', () => ({ CHATGPT_DOMAIN: 'chatgpt.com', CHATGPT_MODEL_CHOICES: ['fast', 'speed', 'instant', 'balanced', 'advanced', 'high', 'thinking', 'very-high', 'pro', 'gpt-5.6-pro'], navigateToProject: mocks.navigateToProject, selectChatGPTModel: mocks.selectChatGPTModel, })); -const { modelCommand } = await import('./model.js'); +const { modelCommand } = await import('../model.js'); beforeEach(() => { vi.restoreAllMocks(); diff --git a/clis/chatgpt/utils.test.js b/plugins/chatgpt/test/utils.test.js similarity index 99% rename from clis/chatgpt/utils.test.js rename to plugins/chatgpt/test/utils.test.js index 12531984..6104f096 100644 --- a/clis/chatgpt/utils.test.js +++ b/plugins/chatgpt/test/utils.test.js @@ -4,7 +4,7 @@ import path from 'node:path'; import { JSDOM } from 'jsdom'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { CHATGPT_MODEL_CHOICES, __test__, getChatGPTDetailRows, getChatGPTImageAssets, getChatGPTResponsePairCounts, getChatGPTVisibleImageUrls, getCurrentChatGPTModel, getCurrentChatGPTTool, getVisibleMessages, isGenerating, navigateToProject, openChatGPTConversation, prepareChatGPTImagePaths, selectChatGPTModel, selectChatGPTTool, sendChatGPTMessage, uploadChatGPTImages, waitForChatGPTDeepResearchResult, waitForChatGPTDetailRows, waitForChatGPTImages, waitForChatGPTResponse } from './utils.js'; +import { CHATGPT_MODEL_CHOICES, __test__, getChatGPTDetailRows, getChatGPTImageAssets, getChatGPTResponsePairCounts, getChatGPTVisibleImageUrls, getCurrentChatGPTModel, getCurrentChatGPTTool, getVisibleMessages, isGenerating, navigateToProject, openChatGPTConversation, prepareChatGPTImagePaths, selectChatGPTModel, selectChatGPTTool, sendChatGPTMessage, uploadChatGPTImages, waitForChatGPTDeepResearchResult, waitForChatGPTDetailRows, waitForChatGPTImages, waitForChatGPTResponse } from '../utils.js'; const tempDirs = []; @@ -1745,7 +1745,7 @@ describe('chatgpt file path validation', () => { const docxPath = path.join(dir, 'notes.docx'); fs.writeFileSync(docxPath, 'fake-docx'); - const { prepareChatGPTFilePaths } = await import('./utils.js'); + const { prepareChatGPTFilePaths } = await import('../utils.js'); await expect(prepareChatGPTFilePaths([pdfPath])).resolves.toEqual({ ok: true, paths: [pdfPath] }); await expect(prepareChatGPTFilePaths([pdfPath, docxPath])).resolves.toEqual({ ok: true, paths: [pdfPath, docxPath] }); await expect(prepareChatGPTFilePaths([path.join(dir, 'missing.txt')])).resolves.toMatchObject({ @@ -1806,7 +1806,7 @@ describe('chatgpt project file upload helper', () => { evaluate: vi.fn((script) => Promise.resolve(dom.window.eval(String(script)))), }; - const { getProjectList } = await import('./utils.js'); + const { getProjectList } = await import('../utils.js'); await expect(getProjectList(page)).resolves.toEqual([ { Index: 1, @@ -1838,7 +1838,7 @@ describe('chatgpt project file upload helper', () => { }), }; - const { openProjectKnowledgeDialog } = await import('./utils.js'); + const { openProjectKnowledgeDialog } = await import('../utils.js'); const result = await openProjectKnowledgeDialog(page); expect(result).toBe(true); }); @@ -1850,7 +1850,7 @@ describe('chatgpt project file upload helper', () => { evaluate: vi.fn().mockResolvedValue(false), }; - const { openProjectKnowledgeDialog } = await import('./utils.js'); + const { openProjectKnowledgeDialog } = await import('../utils.js'); const result = await openProjectKnowledgeDialog(page); expect(result).toBe(false); }); @@ -1877,7 +1877,7 @@ describe('chatgpt project file upload helper', () => { evaluate: vi.fn((script) => Promise.resolve(dom.window.eval(String(script)))), }; - const { openProjectKnowledgeDialog } = await import('./utils.js'); + const { openProjectKnowledgeDialog } = await import('../utils.js'); await expect(openProjectKnowledgeDialog(page)).resolves.toBe(true); expect(sourcesTab.dataset.clicked).toBe('true'); }); @@ -1911,7 +1911,7 @@ describe('chatgpt project file upload helper', () => { }), }; - const { uploadChatGPTProjectFiles } = await import('./utils.js'); + const { uploadChatGPTProjectFiles } = await import('../utils.js'); const result = await uploadChatGPTProjectFiles(page, '12345678', [filePath]); expect(result).toEqual({ ok: true, files: [filePath] }); @@ -1944,7 +1944,7 @@ describe('chatgpt project file upload helper', () => { }), }; - const { uploadChatGPTProjectFiles } = await import('./utils.js'); + const { uploadChatGPTProjectFiles } = await import('../utils.js'); const result = await uploadChatGPTProjectFiles(page, '12345678', [filePath]); expect(result).toMatchObject({ @@ -1990,7 +1990,7 @@ describe('chatgpt project file upload helper', () => { }), }; - const { uploadChatGPTProjectFiles } = await import('./utils.js'); + const { uploadChatGPTProjectFiles } = await import('../utils.js'); const result = await uploadChatGPTProjectFiles(page, '12345678', [filePath]); expect(result).toMatchObject({ diff --git a/clis/chatgpt/utils.js b/plugins/chatgpt/utils.js similarity index 100% rename from clis/chatgpt/utils.js rename to plugins/chatgpt/utils.js diff --git a/plugins/chatgpt/webcmd-plugin.json b/plugins/chatgpt/webcmd-plugin.json new file mode 100644 index 00000000..c076c1d7 --- /dev/null +++ b/plugins/chatgpt/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "chatgpt", + "version": "0.1.0", + "description": "Webcmd commands for chatgpt", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/claude/README.md b/plugins/claude/README.md new file mode 100644 index 00000000..dbf47738 --- /dev/null +++ b/plugins/claude/README.md @@ -0,0 +1,23 @@ +# webcmd-plugin-claude + +Webcmd commands for claude. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/claude +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd claude ask` | Send a prompt to Claude and get the response | +| `webcmd claude detail` | Open a Claude conversation by ID and read its messages | +| `webcmd claude history` | List conversation history from Claude /recents | +| `webcmd claude login` | Open claude login | +| `webcmd claude new` | Start a new conversation in Claude | +| `webcmd claude read` | Read the current Claude conversation | +| `webcmd claude send` | Send a prompt to Claude without waiting for the response | +| `webcmd claude status` | Check Claude page availability and login state | +| `webcmd claude whoami` | Show the current logged-in claude account | diff --git a/clis/claude/ask.js b/plugins/claude/ask.js similarity index 100% rename from clis/claude/ask.js rename to plugins/claude/ask.js diff --git a/clis/claude/auth.js b/plugins/claude/auth.js similarity index 97% rename from clis/claude/auth.js rename to plugins/claude/auth.js index e6f82fb4..173eee0c 100644 --- a/clis/claude/auth.js +++ b/plugins/claude/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasClaudeSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://claude.ai' }); diff --git a/clis/claude/detail.js b/plugins/claude/detail.js similarity index 100% rename from clis/claude/detail.js rename to plugins/claude/detail.js diff --git a/clis/claude/history.js b/plugins/claude/history.js similarity index 100% rename from clis/claude/history.js rename to plugins/claude/history.js diff --git a/clis/claude/new.js b/plugins/claude/new.js similarity index 100% rename from clis/claude/new.js rename to plugins/claude/new.js diff --git a/plugins/claude/package.json b/plugins/claude/package.json new file mode 100644 index 00000000..f36d07bc --- /dev/null +++ b/plugins/claude/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-claude", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for claude", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/claude/read.js b/plugins/claude/read.js similarity index 100% rename from clis/claude/read.js rename to plugins/claude/read.js diff --git a/clis/claude/send.js b/plugins/claude/send.js similarity index 100% rename from clis/claude/send.js rename to plugins/claude/send.js diff --git a/clis/claude/status.js b/plugins/claude/status.js similarity index 100% rename from clis/claude/status.js rename to plugins/claude/status.js diff --git a/clis/claude/ask.test.js b/plugins/claude/test/ask.test.js similarity index 99% rename from clis/claude/ask.test.js rename to plugins/claude/test/ask.test.js index 021dc1c3..0eb425bc 100644 --- a/clis/claude/ask.test.js +++ b/plugins/claude/test/ask.test.js @@ -29,7 +29,7 @@ const { mockWithRetry: vi.fn(async (fn) => fn()), })); -vi.mock('./utils.js', () => ({ +vi.mock('../utils.js', () => ({ CLAUDE_DOMAIN: 'claude.ai', CLAUDE_URL: 'https://claude.ai/new', ensureOnClaude: mockEnsureOnClaude, @@ -46,7 +46,7 @@ vi.mock('./utils.js', () => ({ withRetry: mockWithRetry, })); -import { askCommand } from './ask.js'; +import { askCommand } from '../ask.js'; describe('claude ask basic flow', () => { const page = { diff --git a/clis/claude/commands.test.js b/plugins/claude/test/commands.test.js similarity index 94% rename from clis/claude/commands.test.js rename to plugins/claude/test/commands.test.js index fbf59f45..70d8e72e 100644 --- a/clis/claude/commands.test.js +++ b/plugins/claude/test/commands.test.js @@ -27,7 +27,7 @@ const { mockWithRetry: vi.fn(async (fn) => fn()), })); -vi.mock('./utils.js', () => ({ +vi.mock('../utils.js', () => ({ CLAUDE_DOMAIN: 'claude.ai', CLAUDE_URL: 'https://claude.ai/new', ensureOnClaude: mockEnsureOnClaude, @@ -43,11 +43,11 @@ vi.mock('./utils.js', () => ({ withRetry: mockWithRetry, })); -import { sendCommand } from './send.js'; -import { newCommand } from './new.js'; -import { readCommand } from './read.js'; -import { historyCommand } from './history.js'; -import { detailCommand } from './detail.js'; +import { sendCommand } from '../send.js'; +import { newCommand } from '../new.js'; +import { readCommand } from '../read.js'; +import { historyCommand } from '../history.js'; +import { detailCommand } from '../detail.js'; describe('claude command-level fail-fast contracts', () => { const page = { diff --git a/clis/claude/utils.test.js b/plugins/claude/test/utils.test.js similarity index 98% rename from clis/claude/utils.test.js rename to plugins/claude/test/utils.test.js index fb3e8a58..3e44de98 100644 --- a/clis/claude/utils.test.js +++ b/plugins/claude/test/utils.test.js @@ -3,7 +3,7 @@ import os from 'node:os'; import path from 'node:path'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; -import { parseBoolFlag, sendWithFile, selectModel, requireConversationId, requireNonEmptyPrompt, requirePositiveInt } from './utils.js'; +import { parseBoolFlag, sendWithFile, selectModel, requireConversationId, requireNonEmptyPrompt, requirePositiveInt } from '../utils.js'; describe('claude parseBoolFlag', () => { it('returns booleans unchanged', () => { diff --git a/clis/claude/utils.js b/plugins/claude/utils.js similarity index 100% rename from clis/claude/utils.js rename to plugins/claude/utils.js diff --git a/plugins/claude/webcmd-plugin.json b/plugins/claude/webcmd-plugin.json new file mode 100644 index 00000000..3dadcd7e --- /dev/null +++ b/plugins/claude/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "claude", + "version": "0.1.0", + "description": "Webcmd commands for claude", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/gemini/README.md b/plugins/gemini/README.md new file mode 100644 index 00000000..b3f77ade --- /dev/null +++ b/plugins/gemini/README.md @@ -0,0 +1,26 @@ +# webcmd-plugin-gemini + +Webcmd commands for gemini. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/gemini +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd gemini ask` | Send a prompt to Gemini and return only the assistant response | +| `webcmd gemini deep-research` | Start a Gemini Deep Research run and confirm it | +| `webcmd gemini deep-research-result` | Export Deep Research report URL from a Gemini conversation | +| `webcmd gemini detail` | Open a Gemini web conversation by id, URL, or sidebar title and read its turns | +| `webcmd gemini history` | List visible Gemini web conversation history from the sidebar | +| `webcmd gemini image` | Generate images with Gemini web and save them locally | +| `webcmd gemini login` | Open gemini login | +| `webcmd gemini models` | List available Gemini models from the web UI | +| `webcmd gemini new` | Start a new conversation in Gemini web chat | +| `webcmd gemini read` | Read the turns visible in the current Gemini web conversation | +| `webcmd gemini status` | Check Gemini web page availability and login state | +| `webcmd gemini whoami` | Show the current logged-in gemini account | diff --git a/clis/gemini/ask.js b/plugins/gemini/ask.js similarity index 100% rename from clis/gemini/ask.js rename to plugins/gemini/ask.js diff --git a/clis/gemini/auth.js b/plugins/gemini/auth.js similarity index 95% rename from clis/gemini/auth.js rename to plugins/gemini/auth.js index 1ac10153..7ef2044a 100644 --- a/clis/gemini/auth.js +++ b/plugins/gemini/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasGoogleSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://gemini.google.com' }); diff --git a/clis/gemini/deep-research-result.js b/plugins/gemini/deep-research-result.js similarity index 100% rename from clis/gemini/deep-research-result.js rename to plugins/gemini/deep-research-result.js diff --git a/clis/gemini/deep-research.js b/plugins/gemini/deep-research.js similarity index 100% rename from clis/gemini/deep-research.js rename to plugins/gemini/deep-research.js diff --git a/clis/gemini/detail.js b/plugins/gemini/detail.js similarity index 100% rename from clis/gemini/detail.js rename to plugins/gemini/detail.js diff --git a/clis/gemini/history.js b/plugins/gemini/history.js similarity index 100% rename from clis/gemini/history.js rename to plugins/gemini/history.js diff --git a/clis/gemini/image.js b/plugins/gemini/image.js similarity index 100% rename from clis/gemini/image.js rename to plugins/gemini/image.js diff --git a/clis/gemini/models.js b/plugins/gemini/models.js similarity index 100% rename from clis/gemini/models.js rename to plugins/gemini/models.js diff --git a/clis/gemini/new.js b/plugins/gemini/new.js similarity index 100% rename from clis/gemini/new.js rename to plugins/gemini/new.js diff --git a/plugins/gemini/package.json b/plugins/gemini/package.json new file mode 100644 index 00000000..d21448d7 --- /dev/null +++ b/plugins/gemini/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-gemini", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for gemini", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/gemini/read.js b/plugins/gemini/read.js similarity index 100% rename from clis/gemini/read.js rename to plugins/gemini/read.js diff --git a/clis/gemini/status.js b/plugins/gemini/status.js similarity index 100% rename from clis/gemini/status.js rename to plugins/gemini/status.js diff --git a/clis/gemini/ask.test.js b/plugins/gemini/test/ask.test.js similarity index 99% rename from clis/gemini/ask.test.js rename to plugins/gemini/test/ask.test.js index a0223ecb..56195a7b 100644 --- a/clis/gemini/ask.test.js +++ b/plugins/gemini/test/ask.test.js @@ -43,8 +43,8 @@ const mocks = vi.hoisted(() => ({ waitForGeminiResponse: vi.fn(), })); -vi.mock('./utils.js', async () => { - const actual = await vi.importActual('./utils.js'); +vi.mock('../utils.js', async () => { + const actual = await vi.importActual('../utils.js'); return { ...actual, ensureGeminiPage: mocks.ensureGeminiPage, @@ -66,7 +66,7 @@ const modelsMock = vi.hoisted(() => ({ clickThinkingToggleScript: vi.fn().mockReturnValue('__TOGGLE_SCRIPT__'), extractThinkingScript: vi.fn().mockReturnValue('__EXTRACT_THINKING_SCRIPT__'), })); -vi.mock('./models.js', () => ({ +vi.mock('../models.js', () => ({ pickModelPickerScript: modelsMock.pickModelPickerScript, readMenuModelsScript: modelsMock.readMenuModelsScript, clickThinkingToggleScript: modelsMock.clickThinkingToggleScript, @@ -79,7 +79,7 @@ vi.mock('./models.js', () => ({ }, })); -import { askCommand, __test__ } from './ask.js'; +import { askCommand, __test__ } from '../ask.js'; const { validateAskModelValue } = __test__; diff --git a/clis/gemini/commands.test.js b/plugins/gemini/test/commands.test.js similarity index 96% rename from clis/gemini/commands.test.js rename to plugins/gemini/test/commands.test.js index 06119996..245d2ea6 100644 --- a/clis/gemini/commands.test.js +++ b/plugins/gemini/test/commands.test.js @@ -9,8 +9,8 @@ const mocks = vi.hoisted(() => ({ resolveGeminiConversationForQuery: vi.fn(), })); -vi.mock('./utils.js', async () => { - const actual = await vi.importActual('./utils.js'); +vi.mock('../utils.js', async () => { + const actual = await vi.importActual('../utils.js'); return { ...actual, ensureGeminiPage: mocks.ensureGeminiPage, @@ -21,10 +21,10 @@ vi.mock('./utils.js', async () => { }; }); -import { statusCommand } from './status.js'; -import { historyCommand, extractGeminiId } from './history.js'; -import { detailCommand } from './detail.js'; -import { readCommand } from './read.js'; +import { statusCommand } from '../status.js'; +import { historyCommand, extractGeminiId } from '../history.js'; +import { detailCommand } from '../detail.js'; +import { readCommand } from '../read.js'; function makePage() { return { diff --git a/clis/gemini/deep-research-result.test.js b/plugins/gemini/test/deep-research-result.test.js similarity index 98% rename from clis/gemini/deep-research-result.test.js rename to plugins/gemini/test/deep-research-result.test.js index 4ddebabc..609c0707 100644 --- a/clis/gemini/deep-research-result.test.js +++ b/plugins/gemini/test/deep-research-result.test.js @@ -9,7 +9,7 @@ const { mockClickGeminiConversationByTitle, mockExportGeminiDeepResearchReport, mockResolveGeminiConversationForQuery: vi.fn(), mockWaitForGeminiTranscript: vi.fn(), })); -vi.mock('./utils.js', () => ({ +vi.mock('../utils.js', () => ({ GEMINI_DOMAIN: 'gemini.google.com', clickGeminiConversationByTitle: mockClickGeminiConversationByTitle, exportGeminiDeepResearchReport: mockExportGeminiDeepResearchReport, @@ -30,7 +30,7 @@ vi.mock('./utils.js', () => ({ resolveGeminiConversationForQuery: mockResolveGeminiConversationForQuery, waitForGeminiTranscript: mockWaitForGeminiTranscript, })); -import { deepResearchResultCommand } from './deep-research-result.js'; +import { deepResearchResultCommand } from '../deep-research-result.js'; describe('gemini/deep-research-result', () => { const page = { goto: vi.fn().mockResolvedValue(undefined), diff --git a/clis/gemini/deep-research.test.js b/plugins/gemini/test/deep-research.test.js similarity index 99% rename from clis/gemini/deep-research.test.js rename to plugins/gemini/test/deep-research.test.js index af56660d..46eee08a 100644 --- a/clis/gemini/deep-research.test.js +++ b/plugins/gemini/test/deep-research.test.js @@ -9,7 +9,7 @@ const { mockGetCurrentGeminiUrl, mockReadGeminiSnapshot, mockSelectGeminiTool, m mockWaitForGeminiConfirmButton: vi.fn(), mockGetLatestGeminiAssistantResponse: vi.fn(), })); -vi.mock('./utils.js', () => ({ +vi.mock('../utils.js', () => ({ GEMINI_DOMAIN: 'gemini.google.com', GEMINI_APP_URL: 'https://gemini.google.com/app', GEMINI_DEEP_RESEARCH_DEFAULT_TOOL_LABELS: ['Deep Research', 'Deep research', '\u6df1\u5ea6\u7814\u7a76'], @@ -41,7 +41,7 @@ vi.mock('./utils.js', () => ({ waitForGeminiSubmission: mockWaitForGeminiSubmission, waitForGeminiConfirmButton: mockWaitForGeminiConfirmButton, })); -import { deepResearchCommand } from './deep-research.js'; +import { deepResearchCommand } from '../deep-research.js'; describe('gemini/deep-research', () => { const page = {}; const runCommand = (kwargs) => deepResearchCommand.func(page, { timeout: 180, ...kwargs }); diff --git a/clis/gemini/models.test.js b/plugins/gemini/test/models.test.js similarity index 99% rename from clis/gemini/models.test.js rename to plugins/gemini/test/models.test.js index d341120b..850c9112 100644 --- a/clis/gemini/models.test.js +++ b/plugins/gemini/test/models.test.js @@ -6,15 +6,15 @@ const mocks = vi.hoisted(() => ({ ensureGeminiPage: vi.fn(), })); -vi.mock('./utils.js', async () => { - const actual = await vi.importActual('./utils.js'); +vi.mock('../utils.js', async () => { + const actual = await vi.importActual('../utils.js'); return { ...actual, ensureGeminiPage: mocks.ensureGeminiPage, }; }); -import { modelsCommand, __test__ } from './models.js'; +import { modelsCommand, __test__ } from '../models.js'; function createPageMock() { return { diff --git a/clis/gemini/reply-state.test.js b/plugins/gemini/test/reply-state.test.js similarity index 99% rename from clis/gemini/reply-state.test.js rename to plugins/gemini/test/reply-state.test.js index a6b906be..2f39619e 100644 --- a/clis/gemini/reply-state.test.js +++ b/plugins/gemini/test/reply-state.test.js @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from 'vitest'; -import { __test__, waitForGeminiResponse, waitForGeminiSubmission } from './utils.js'; +import { __test__, waitForGeminiResponse, waitForGeminiSubmission } from '../utils.js'; function snapshot(overrides = {}) { return { turns: [], diff --git a/clis/gemini/utils.test.js b/plugins/gemini/test/utils.test.js similarity index 99% rename from clis/gemini/utils.test.js rename to plugins/gemini/test/utils.test.js index ef917dc5..21b76d6e 100644 --- a/clis/gemini/utils.test.js +++ b/plugins/gemini/test/utils.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { JSDOM } from 'jsdom'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { __test__, collectGeminiTranscriptAdditions, getGeminiConversationList, getGeminiPageState, getGeminiVisibleTurns, pickGeminiDeepResearchExportUrl, readGeminiSnapshot, sanitizeGeminiResponseText, selectGeminiModel, selectGeminiThinking, sendGeminiMessage, } from './utils.js'; +import { __test__, collectGeminiTranscriptAdditions, getGeminiConversationList, getGeminiPageState, getGeminiVisibleTurns, pickGeminiDeepResearchExportUrl, readGeminiSnapshot, sanitizeGeminiResponseText, selectGeminiModel, selectGeminiThinking, sendGeminiMessage, } from '../utils.js'; function createPageMock() { return { goto: vi.fn().mockResolvedValue(undefined), diff --git a/clis/gemini/utils.js b/plugins/gemini/utils.js similarity index 100% rename from clis/gemini/utils.js rename to plugins/gemini/utils.js diff --git a/plugins/gemini/webcmd-plugin.json b/plugins/gemini/webcmd-plugin.json new file mode 100644 index 00000000..eb6d0b61 --- /dev/null +++ b/plugins/gemini/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "gemini", + "version": "0.1.0", + "description": "Webcmd commands for gemini", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/pixiv/README.md b/plugins/pixiv/README.md new file mode 100644 index 00000000..b7a371fb --- /dev/null +++ b/plugins/pixiv/README.md @@ -0,0 +1,22 @@ +# webcmd-plugin-pixiv + +Webcmd commands for pixiv. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/pixiv +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd pixiv detail` | View illustration details (tags, stats, URLs) | +| `webcmd pixiv download` | Download illustration images from Pixiv | +| `webcmd pixiv illusts` | List a Pixiv artist's illustrations | +| `webcmd pixiv login` | Open pixiv login | +| `webcmd pixiv ranking` | Pixiv illustration rankings (daily/weekly/monthly) | +| `webcmd pixiv search` | Search Pixiv illustrations by keyword | +| `webcmd pixiv user` | View Pixiv artist profile | +| `webcmd pixiv whoami` | Show the current logged-in pixiv account | diff --git a/clis/pixiv/auth.js b/plugins/pixiv/auth.js similarity index 97% rename from clis/pixiv/auth.js rename to plugins/pixiv/auth.js index dd479467..7277a13d 100644 --- a/clis/pixiv/auth.js +++ b/plugins/pixiv/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasPixivSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://www.pixiv.net' }); diff --git a/clis/pixiv/detail.js b/plugins/pixiv/detail.js similarity index 100% rename from clis/pixiv/detail.js rename to plugins/pixiv/detail.js diff --git a/clis/pixiv/download.js b/plugins/pixiv/download.js similarity index 100% rename from clis/pixiv/download.js rename to plugins/pixiv/download.js diff --git a/clis/pixiv/illusts.js b/plugins/pixiv/illusts.js similarity index 100% rename from clis/pixiv/illusts.js rename to plugins/pixiv/illusts.js diff --git a/plugins/pixiv/package.json b/plugins/pixiv/package.json new file mode 100644 index 00000000..49cf60a3 --- /dev/null +++ b/plugins/pixiv/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-pixiv", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for pixiv", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/pixiv/ranking.js b/plugins/pixiv/ranking.js similarity index 100% rename from clis/pixiv/ranking.js rename to plugins/pixiv/ranking.js diff --git a/clis/pixiv/search.js b/plugins/pixiv/search.js similarity index 100% rename from clis/pixiv/search.js rename to plugins/pixiv/search.js diff --git a/clis/pixiv/detail.test.js b/plugins/pixiv/test/detail.test.js similarity index 98% rename from clis/pixiv/detail.test.js rename to plugins/pixiv/test/detail.test.js index 5892d1c6..c9d8b6fb 100644 --- a/clis/pixiv/detail.test.js +++ b/plugins/pixiv/test/detail.test.js @@ -1,8 +1,8 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { createPageMock } from '../test-utils.js'; -import './detail.js'; +import { createPageMock } from './page-mock.js'; +import '../detail.js'; let cmd; beforeAll(() => { cmd = getRegistry().get('pixiv/detail'); diff --git a/clis/pixiv/download.test.js b/plugins/pixiv/test/download.test.js similarity index 98% rename from clis/pixiv/download.test.js rename to plugins/pixiv/test/download.test.js index a3d07860..fbd639c4 100644 --- a/clis/pixiv/download.test.js +++ b/plugins/pixiv/test/download.test.js @@ -1,7 +1,7 @@ import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { createPageMock } from '../test-utils.js'; +import { createPageMock } from './page-mock.js'; // Mock download dependencies before importing the adapter const { mockHttpDownload, mockMkdirSync } = vi.hoisted(() => ({ mockHttpDownload: vi.fn(), @@ -15,7 +15,7 @@ vi.mock('node:fs', () => ({ mkdirSync: mockMkdirSync, })); // Now import the adapter (after mocks are set up) -await import('./download.js'); +await import('../download.js'); let cmd; beforeAll(() => { cmd = getRegistry().get('pixiv/download'); diff --git a/clis/pixiv/illusts.test.js b/plugins/pixiv/test/illusts.test.js similarity index 98% rename from clis/pixiv/illusts.test.js rename to plugins/pixiv/test/illusts.test.js index 604312e2..fe428423 100644 --- a/clis/pixiv/illusts.test.js +++ b/plugins/pixiv/test/illusts.test.js @@ -1,8 +1,8 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { createPageMock } from '../test-utils.js'; -import './illusts.js'; +import { createPageMock } from './page-mock.js'; +import '../illusts.js'; let cmd; beforeAll(() => { cmd = getRegistry().get('pixiv/illusts'); diff --git a/plugins/pixiv/test/page-mock.js b/plugins/pixiv/test/page-mock.js new file mode 100644 index 00000000..f88b7b1e --- /dev/null +++ b/plugins/pixiv/test/page-mock.js @@ -0,0 +1,13 @@ +import { vi } from 'vitest'; + +export function createPageMock(evaluateResults = [], overrides = {}) { + const evaluate = vi.fn(); + for (const result of evaluateResults) evaluate.mockResolvedValueOnce(result); + return { + evaluate, + getCookies: vi.fn().mockResolvedValue([]), + goto: vi.fn().mockResolvedValue(undefined), + wait: vi.fn().mockResolvedValue(undefined), + ...overrides, + }; +} diff --git a/clis/pixiv/search.test.js b/plugins/pixiv/test/search.test.js similarity index 97% rename from clis/pixiv/search.test.js rename to plugins/pixiv/test/search.test.js index cb3c453b..d2622280 100644 --- a/clis/pixiv/search.test.js +++ b/plugins/pixiv/test/search.test.js @@ -1,8 +1,8 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { createPageMock } from '../test-utils.js'; -import './search.js'; +import { createPageMock } from './page-mock.js'; +import '../search.js'; let cmd; beforeAll(() => { cmd = getRegistry().get('pixiv/search'); diff --git a/clis/pixiv/user.test.js b/plugins/pixiv/test/user.test.js similarity index 98% rename from clis/pixiv/user.test.js rename to plugins/pixiv/test/user.test.js index 0df269f3..15f9fb70 100644 --- a/clis/pixiv/user.test.js +++ b/plugins/pixiv/test/user.test.js @@ -1,8 +1,8 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { createPageMock } from '../test-utils.js'; -import './user.js'; +import { createPageMock } from './page-mock.js'; +import '../user.js'; let cmd; beforeAll(() => { cmd = getRegistry().get('pixiv/user'); diff --git a/clis/pixiv/user.js b/plugins/pixiv/user.js similarity index 100% rename from clis/pixiv/user.js rename to plugins/pixiv/user.js diff --git a/clis/pixiv/utils.js b/plugins/pixiv/utils.js similarity index 100% rename from clis/pixiv/utils.js rename to plugins/pixiv/utils.js diff --git a/plugins/pixiv/webcmd-plugin.json b/plugins/pixiv/webcmd-plugin.json new file mode 100644 index 00000000..5f6e3d53 --- /dev/null +++ b/plugins/pixiv/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "pixiv", + "version": "0.1.0", + "description": "Webcmd commands for pixiv", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/practo/README.md b/plugins/practo/README.md new file mode 100644 index 00000000..bd8237aa --- /dev/null +++ b/plugins/practo/README.md @@ -0,0 +1,26 @@ +# webcmd-plugin-practo + +Webcmd commands for practo. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/practo +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd practo appointment` | Show logged-in Practo Drive appointment details | +| `webcmd practo appointments` | List logged-in Practo Drive appointments | +| `webcmd practo book-confirm` | Confirm a Practo clinic visit booking after explicit confirmation | +| `webcmd practo book-preview` | Preview Practo booking details for a selected slot without confirming | +| `webcmd practo booking-link` | Build a Practo booking URL for a selected slot without confirming it | +| `webcmd practo cancel` | Cancel a logged-in Practo Drive appointment after explicit confirmation | +| `webcmd practo contact` | Get Practo virtual contact number for a practice_doctor_id | +| `webcmd practo login` | Open practo login | +| `webcmd practo profile` | Read public details from a Practo doctor profile URL | +| `webcmd practo search` | Search Practo doctors by specialty, city, and optional locality | +| `webcmd practo slots` | List available Practo appointment slots for a practice_doctor_id | +| `webcmd practo whoami` | Show the current logged-in practo account | diff --git a/clis/practo/appointment.js b/plugins/practo/appointment.js similarity index 100% rename from clis/practo/appointment.js rename to plugins/practo/appointment.js diff --git a/clis/practo/appointments.js b/plugins/practo/appointments.js similarity index 100% rename from clis/practo/appointments.js rename to plugins/practo/appointments.js diff --git a/clis/practo/book-confirm.js b/plugins/practo/book-confirm.js similarity index 100% rename from clis/practo/book-confirm.js rename to plugins/practo/book-confirm.js diff --git a/clis/practo/book-preview.js b/plugins/practo/book-preview.js similarity index 100% rename from clis/practo/book-preview.js rename to plugins/practo/book-preview.js diff --git a/clis/practo/booking-link.js b/plugins/practo/booking-link.js similarity index 100% rename from clis/practo/booking-link.js rename to plugins/practo/booking-link.js diff --git a/clis/practo/cancel.js b/plugins/practo/cancel.js similarity index 100% rename from clis/practo/cancel.js rename to plugins/practo/cancel.js diff --git a/clis/practo/contact.js b/plugins/practo/contact.js similarity index 100% rename from clis/practo/contact.js rename to plugins/practo/contact.js diff --git a/clis/practo/login.js b/plugins/practo/login.js similarity index 88% rename from clis/practo/login.js rename to plugins/practo/login.js index 6c1b9dec..cc772b41 100644 --- a/clis/practo/login.js +++ b/plugins/practo/login.js @@ -1,4 +1,4 @@ -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; import { PRACTO, probeIdentity } from './utils.js'; registerSiteAuthCommands({ diff --git a/plugins/practo/package.json b/plugins/practo/package.json new file mode 100644 index 00000000..6ea20f33 --- /dev/null +++ b/plugins/practo/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-practo", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for practo", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/practo/profile.js b/plugins/practo/profile.js similarity index 100% rename from clis/practo/profile.js rename to plugins/practo/profile.js diff --git a/clis/practo/search.js b/plugins/practo/search.js similarity index 100% rename from clis/practo/search.js rename to plugins/practo/search.js diff --git a/clis/practo/slots.js b/plugins/practo/slots.js similarity index 100% rename from clis/practo/slots.js rename to plugins/practo/slots.js diff --git a/clis/practo/practo.test.js b/plugins/practo/test/practo.test.js similarity index 94% rename from clis/practo/practo.test.js rename to plugins/practo/test/practo.test.js index 52b22081..42425fce 100644 --- a/clis/practo/practo.test.js +++ b/plugins/practo/test/practo.test.js @@ -1,18 +1,18 @@ import { describe, expect, it, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './appointment.js'; -import './appointments.js'; -import './book-confirm.js'; -import './book-preview.js'; -import './booking-link.js'; -import './cancel.js'; -import './contact.js'; -import './login.js'; -import './profile.js'; -import './search.js'; -import './slots.js'; -import { __test__ } from './utils.js'; +import '../appointment.js'; +import '../appointments.js'; +import '../book-confirm.js'; +import '../book-preview.js'; +import '../booking-link.js'; +import '../cancel.js'; +import '../contact.js'; +import '../login.js'; +import '../profile.js'; +import '../search.js'; +import '../slots.js'; +import { __test__ } from '../utils.js'; const { buildSearchUrl, diff --git a/clis/practo/utils.js b/plugins/practo/utils.js similarity index 100% rename from clis/practo/utils.js rename to plugins/practo/utils.js diff --git a/plugins/practo/webcmd-plugin.json b/plugins/practo/webcmd-plugin.json new file mode 100644 index 00000000..176f73e0 --- /dev/null +++ b/plugins/practo/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "practo", + "version": "0.1.0", + "description": "Webcmd commands for practo", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/reuters/README.md b/plugins/reuters/README.md new file mode 100644 index 00000000..0f3e6b27 --- /dev/null +++ b/plugins/reuters/README.md @@ -0,0 +1,18 @@ +# webcmd-plugin-reuters + +Webcmd commands for reuters. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/reuters +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd reuters article-detail` | Reuters Reuters article detail:title/author/body text | +| `webcmd reuters login` | Open reuters login | +| `webcmd reuters search` | Reuters Reuters news search | +| `webcmd reuters whoami` | Show the current logged-in reuters account | diff --git a/clis/reuters/article-detail.js b/plugins/reuters/article-detail.js similarity index 100% rename from clis/reuters/article-detail.js rename to plugins/reuters/article-detail.js diff --git a/clis/reuters/auth.js b/plugins/reuters/auth.js similarity index 96% rename from clis/reuters/auth.js rename to plugins/reuters/auth.js index 8aa161d4..2c0d6296 100644 --- a/clis/reuters/auth.js +++ b/plugins/reuters/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function verifyReutersIdentity(page) { await page.goto('https://www.reuters.com/'); diff --git a/plugins/reuters/package.json b/plugins/reuters/package.json new file mode 100644 index 00000000..b74d92f5 --- /dev/null +++ b/plugins/reuters/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-reuters", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for reuters", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/reuters/search.js b/plugins/reuters/search.js similarity index 100% rename from clis/reuters/search.js rename to plugins/reuters/search.js diff --git a/clis/reuters/reuters.test.js b/plugins/reuters/test/reuters.test.js similarity index 99% rename from clis/reuters/reuters.test.js rename to plugins/reuters/test/reuters.test.js index 14a2d9cc..f9b59211 100644 --- a/clis/reuters/reuters.test.js +++ b/plugins/reuters/test/reuters.test.js @@ -1,9 +1,9 @@ import { describe, expect, it, vi } from 'vitest'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './search.js'; -import './article-detail.js'; -import { buildArticleDetailScript, buildSearchScript, isAuthStatus, looksAuthWallText, mapArticleDetail, mapSearchArticles, parseLimit } from './utils.js'; +import '../search.js'; +import '../article-detail.js'; +import { buildArticleDetailScript, buildSearchScript, isAuthStatus, looksAuthWallText, mapArticleDetail, mapSearchArticles, parseLimit } from '../utils.js'; function makePage(evaluateResult) { return { diff --git a/clis/reuters/utils.js b/plugins/reuters/utils.js similarity index 100% rename from clis/reuters/utils.js rename to plugins/reuters/utils.js diff --git a/plugins/reuters/webcmd-plugin.json b/plugins/reuters/webcmd-plugin.json new file mode 100644 index 00000000..ee835d3d --- /dev/null +++ b/plugins/reuters/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "reuters", + "version": "0.1.0", + "description": "Webcmd commands for reuters", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/suno/README.md b/plugins/suno/README.md new file mode 100644 index 00000000..0bc39d84 --- /dev/null +++ b/plugins/suno/README.md @@ -0,0 +1,20 @@ +# webcmd-plugin-suno + +Webcmd commands for suno. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/suno +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd suno download` | Download an existing Suno clip (MP3 + optional WAV/M4A/video) by id | +| `webcmd suno generate` | Generate music with Suno (V5.5 chirp-fenix by default) and download clips locally | +| `webcmd suno list` | List recent Suno clips in your library (id, title, status, created_at, link) | +| `webcmd suno login` | Open suno login | +| `webcmd suno status` | Check Suno login, plan, credit balance, and captcha readiness | +| `webcmd suno whoami` | Show the current logged-in suno account | diff --git a/clis/suno/auth.js b/plugins/suno/auth.js similarity index 97% rename from clis/suno/auth.js rename to plugins/suno/auth.js index 09ce4296..555d6939 100644 --- a/clis/suno/auth.js +++ b/plugins/suno/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasSunoClerkCookie(page) { const cookies = await page.getCookies({ url: 'https://clerk.suno.com' }); diff --git a/clis/suno/download.js b/plugins/suno/download.js similarity index 100% rename from clis/suno/download.js rename to plugins/suno/download.js diff --git a/clis/suno/generate.js b/plugins/suno/generate.js similarity index 100% rename from clis/suno/generate.js rename to plugins/suno/generate.js diff --git a/clis/suno/list.js b/plugins/suno/list.js similarity index 100% rename from clis/suno/list.js rename to plugins/suno/list.js diff --git a/plugins/suno/package.json b/plugins/suno/package.json new file mode 100644 index 00000000..a3fc80b5 --- /dev/null +++ b/plugins/suno/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-suno", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for suno", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/suno/status.js b/plugins/suno/status.js similarity index 100% rename from clis/suno/status.js rename to plugins/suno/status.js diff --git a/clis/suno/commands.test.js b/plugins/suno/test/commands.test.js similarity index 98% rename from clis/suno/commands.test.js rename to plugins/suno/test/commands.test.js index 37d09751..a4ba911c 100644 --- a/clis/suno/commands.test.js +++ b/plugins/suno/test/commands.test.js @@ -6,7 +6,7 @@ const mocks = vi.hoisted(() => ({ checkSunoCaptcha: vi.fn(), })); -vi.mock('./utils.js', () => ({ +vi.mock('../utils.js', () => ({ STUDIO_API: 'https://studio-api-prod.suno.com', SUNO_DOMAIN: 'suno.com', SUNO_URL: 'https://suno.com', @@ -25,8 +25,8 @@ vi.mock('./utils.js', () => ({ unwrapEvaluateResult: (value) => value && typeof value === 'object' && 'session' in value && 'data' in value ? value.data : value, })); -const { statusCommand } = await import('./status.js'); -const { listCommand } = await import('./list.js'); +const { statusCommand } = await import('../status.js'); +const { listCommand } = await import('../list.js'); function createPage(evaluateImpl) { return { diff --git a/clis/suno/download.test.js b/plugins/suno/test/download.test.js similarity index 98% rename from clis/suno/download.test.js rename to plugins/suno/test/download.test.js index eb966576..b9edf0b4 100644 --- a/clis/suno/download.test.js +++ b/plugins/suno/test/download.test.js @@ -5,7 +5,7 @@ const mocks = vi.hoisted(() => ({ downloadSunoClip: vi.fn(), })); -vi.mock('./utils.js', () => ({ +vi.mock('../utils.js', () => ({ STUDIO_API: 'https://studio-api-prod.suno.com', SUNO_DOMAIN: 'suno.com', SUNO_URL: 'https://suno.com', @@ -25,7 +25,7 @@ vi.mock('./utils.js', () => ({ unwrapEvaluateResult: (value) => value && typeof value === 'object' && 'session' in value && 'data' in value ? value.data : value, })); -const { downloadCommand } = await import('./download.js'); +const { downloadCommand } = await import('../download.js'); const okSession = { ok: true, deviceId: 'device-uuid' }; const okCompleteClip = { id: '11111111-2222-3333-4444-555555555555', status: 'complete', title: 'Probe', audio_url: 'https://cdn1.suno.ai/x.mp3' }; diff --git a/clis/suno/generate.test.js b/plugins/suno/test/generate.test.js similarity index 99% rename from clis/suno/generate.test.js rename to plugins/suno/test/generate.test.js index ce9f31c0..dc2e01ff 100644 --- a/clis/suno/generate.test.js +++ b/plugins/suno/test/generate.test.js @@ -8,7 +8,7 @@ const mocks = vi.hoisted(() => ({ downloadSunoClip: vi.fn(), })); -vi.mock('./utils.js', () => ({ +vi.mock('../utils.js', () => ({ DEFAULT_SUNO_MODEL: 'chirp-fenix', SUNO_DOMAIN: 'suno.com', SUNO_MODELS: ['chirp-fenix', 'chirp-bluejay', 'chirp-v4', 'chirp-v3-5'], @@ -38,7 +38,7 @@ vi.mock('./utils.js', () => ({ resolveSunoOutputDir: (value) => value || '/tmp/suno-test', })); -const { generateCommand } = await import('./generate.js'); +const { generateCommand } = await import('../generate.js'); function createPage() { return { diff --git a/clis/suno/utils.test.js b/plugins/suno/test/utils.test.js similarity index 99% rename from clis/suno/utils.test.js rename to plugins/suno/test/utils.test.js index a7c732f1..1f9d5d44 100644 --- a/clis/suno/utils.test.js +++ b/plugins/suno/test/utils.test.js @@ -17,7 +17,7 @@ import { pollSunoClips, ensureSunoSession, parseSunoBillingInfo, -} from './utils.js'; +} from '../utils.js'; describe('suno utils — parseFormats', () => { it('returns the default format set when input is empty or missing', () => { diff --git a/clis/suno/utils.js b/plugins/suno/utils.js similarity index 100% rename from clis/suno/utils.js rename to plugins/suno/utils.js diff --git a/plugins/suno/webcmd-plugin.json b/plugins/suno/webcmd-plugin.json new file mode 100644 index 00000000..5ed86569 --- /dev/null +++ b/plugins/suno/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "suno", + "version": "0.1.0", + "description": "Webcmd commands for suno", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/upwork/README.md b/plugins/upwork/README.md new file mode 100644 index 00000000..ea314e05 --- /dev/null +++ b/plugins/upwork/README.md @@ -0,0 +1,19 @@ +# webcmd-plugin-upwork + +Webcmd commands for upwork. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/upwork +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd upwork detail` | Read the full Upwork job posting by ciphertext id (e.g. ~022054964136512093518) | +| `webcmd upwork feed` | Upwork personalized jobs feed (best-matches \| most-recent) — requires login | +| `webcmd upwork login` | Open upwork login | +| `webcmd upwork search` | Upwork keyword job search (logged-in browser session, US site) | +| `webcmd upwork whoami` | Show the current logged-in upwork account | diff --git a/clis/upwork/auth.js b/plugins/upwork/auth.js similarity index 96% rename from clis/upwork/auth.js rename to plugins/upwork/auth.js index ad2c9e90..156a87df 100644 --- a/clis/upwork/auth.js +++ b/plugins/upwork/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasUpworkSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://www.upwork.com' }); diff --git a/clis/upwork/detail.js b/plugins/upwork/detail.js similarity index 100% rename from clis/upwork/detail.js rename to plugins/upwork/detail.js diff --git a/clis/upwork/feed.js b/plugins/upwork/feed.js similarity index 100% rename from clis/upwork/feed.js rename to plugins/upwork/feed.js diff --git a/plugins/upwork/package.json b/plugins/upwork/package.json new file mode 100644 index 00000000..82001dd4 --- /dev/null +++ b/plugins/upwork/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-upwork", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for upwork", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/upwork/search.js b/plugins/upwork/search.js similarity index 100% rename from clis/upwork/search.js rename to plugins/upwork/search.js diff --git a/clis/upwork/upwork.test.js b/plugins/upwork/test/upwork.test.js similarity index 99% rename from clis/upwork/upwork.test.js rename to plugins/upwork/test/upwork.test.js index 9dfb0337..69cf4b0b 100644 --- a/clis/upwork/upwork.test.js +++ b/plugins/upwork/test/upwork.test.js @@ -29,10 +29,10 @@ import { jobType, formatSkills, jobToListRow, -} from './utils.js'; -import './search.js'; -import './feed.js'; -import './detail.js'; +} from '../utils.js'; +import '../search.js'; +import '../feed.js'; +import '../detail.js'; function createPageMock(evaluateResult) { const evaluate = typeof evaluateResult === 'function' diff --git a/clis/upwork/utils.js b/plugins/upwork/utils.js similarity index 100% rename from clis/upwork/utils.js rename to plugins/upwork/utils.js diff --git a/plugins/upwork/webcmd-plugin.json b/plugins/upwork/webcmd-plugin.json new file mode 100644 index 00000000..92dfeca3 --- /dev/null +++ b/plugins/upwork/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "upwork", + "version": "0.1.0", + "description": "Webcmd commands for upwork", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/zepto/README.md b/plugins/zepto/README.md new file mode 100644 index 00000000..03d47366 --- /dev/null +++ b/plugins/zepto/README.md @@ -0,0 +1,23 @@ +# webcmd-plugin-zepto + +Webcmd commands for zepto. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/zepto +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd zepto add-to-cart` | Add a Zepto product to cart | +| `webcmd zepto cart` | Read Zepto cart line items | +| `webcmd zepto checkout` | Open Zepto checkout review without placing an order | +| `webcmd zepto location` | Show the selected Zepto delivery location | +| `webcmd zepto login` | Open zepto login | +| `webcmd zepto place-order` | Submit a real Zepto order only when --confirm true is passed | +| `webcmd zepto product` | Read Zepto product details | +| `webcmd zepto search` | Search Zepto products | +| `webcmd zepto whoami` | Show the current logged-in zepto account | diff --git a/clis/zepto/add-to-cart.js b/plugins/zepto/add-to-cart.js similarity index 100% rename from clis/zepto/add-to-cart.js rename to plugins/zepto/add-to-cart.js diff --git a/clis/zepto/auth.js b/plugins/zepto/auth.js similarity index 95% rename from clis/zepto/auth.js rename to plugins/zepto/auth.js index 2bc235c4..30b2ff06 100644 --- a/clis/zepto/auth.js +++ b/plugins/zepto/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; import { DOMAIN, HOME_URL, SITE, ZEPTO_NAV_OPTIONS, safeGoto } from './utils.js'; function authEvaluate() { diff --git a/clis/zepto/cart.js b/plugins/zepto/cart.js similarity index 100% rename from clis/zepto/cart.js rename to plugins/zepto/cart.js diff --git a/clis/zepto/checkout.js b/plugins/zepto/checkout.js similarity index 100% rename from clis/zepto/checkout.js rename to plugins/zepto/checkout.js diff --git a/clis/zepto/location.js b/plugins/zepto/location.js similarity index 100% rename from clis/zepto/location.js rename to plugins/zepto/location.js diff --git a/plugins/zepto/package.json b/plugins/zepto/package.json new file mode 100644 index 00000000..ae8caed7 --- /dev/null +++ b/plugins/zepto/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-zepto", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for zepto", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/zepto/place-order.js b/plugins/zepto/place-order.js similarity index 100% rename from clis/zepto/place-order.js rename to plugins/zepto/place-order.js diff --git a/clis/zepto/product.js b/plugins/zepto/product.js similarity index 100% rename from clis/zepto/product.js rename to plugins/zepto/product.js diff --git a/clis/zepto/search.js b/plugins/zepto/search.js similarity index 100% rename from clis/zepto/search.js rename to plugins/zepto/search.js diff --git a/clis/zepto/zepto.test.js b/plugins/zepto/test/zepto.test.js similarity index 97% rename from clis/zepto/zepto.test.js rename to plugins/zepto/test/zepto.test.js index 1db4c55d..2ad9baa0 100644 --- a/clis/zepto/zepto.test.js +++ b/plugins/zepto/test/zepto.test.js @@ -2,14 +2,14 @@ import { describe, expect, it, vi } from 'vitest'; import { JSDOM } from 'jsdom'; import { ArgumentError, AuthRequiredError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './auth.js'; -import './location.js'; -import './search.js'; -import './product.js'; -import './add-to-cart.js'; -import './cart.js'; -import './checkout.js'; -import './place-order.js'; +import '../auth.js'; +import '../location.js'; +import '../search.js'; +import '../product.js'; +import '../add-to-cart.js'; +import '../cart.js'; +import '../checkout.js'; +import '../place-order.js'; import { CART_EVALUATE, buildSearchUrl, @@ -20,7 +20,7 @@ import { productCardsEvaluate, resolveProductInput, safeGoto, -} from './utils.js'; +} from '../utils.js'; describe('zepto helpers', () => { it('builds search URLs and validates numeric args', () => { diff --git a/clis/zepto/utils.js b/plugins/zepto/utils.js similarity index 100% rename from clis/zepto/utils.js rename to plugins/zepto/utils.js diff --git a/plugins/zepto/webcmd-plugin.json b/plugins/zepto/webcmd-plugin.json new file mode 100644 index 00000000..cb17ce5c --- /dev/null +++ b/plugins/zepto/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "zepto", + "version": "0.1.0", + "description": "Webcmd commands for zepto", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/src/hosted/file-contract.test.ts b/src/hosted/file-contract.test.ts index a0761e3f..22968d92 100644 --- a/src/hosted/file-contract.test.ts +++ b/src/hosted/file-contract.test.ts @@ -166,7 +166,10 @@ function contractCommand(contract: HostedContract, command: string) { describe('hosted file argument contract', () => { it('declares every real local path argument in generated artifacts', () => { - const manifest = readJson('cli-manifest.json'); + const manifest = [ + ...readJson('cli-manifest.json'), + ...readJson('plugin-command-manifest.json'), + ]; const contract = readJson('hosted-contract.json'); for (const [command, expected] of Object.entries(EXPECTED_FILE_ARGUMENTS)) { diff --git a/webcmd-plugin.json b/webcmd-plugin.json index a3986ca4..964de6af 100644 --- a/webcmd-plugin.json +++ b/webcmd-plugin.json @@ -154,6 +154,16 @@ "handle": "agentrhq" } }, + "chatgpt": { + "path": "plugins/chatgpt", + "version": "0.1.0", + "description": "Webcmd commands for chatgpt", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "chatwise": { "path": "plugins/chatwise", "version": "0.1.0", @@ -184,6 +194,16 @@ "handle": "agentrhq" } }, + "claude": { + "path": "plugins/claude", + "version": "0.1.0", + "description": "Webcmd commands for claude", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "codex": { "path": "plugins/codex", "version": "0.1.0", @@ -334,6 +354,16 @@ "handle": "agentrhq" } }, + "gemini": { + "path": "plugins/gemini", + "version": "0.1.0", + "description": "Webcmd commands for gemini", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "github": { "path": "plugins/github", "version": "0.1.0", @@ -684,6 +714,26 @@ "handle": "agentrhq" } }, + "pixiv": { + "path": "plugins/pixiv", + "version": "0.1.0", + "description": "Webcmd commands for pixiv", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "practo": { + "path": "plugins/practo", + "version": "0.1.0", + "description": "Webcmd commands for practo", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "producthunt": { "path": "plugins/producthunt", "version": "0.1.0", @@ -724,6 +774,16 @@ "handle": "agentrhq" } }, + "reuters": { + "path": "plugins/reuters", + "version": "0.1.0", + "description": "Webcmd commands for reuters", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "rfc": { "path": "plugins/rfc", "version": "0.1.0", @@ -794,6 +854,16 @@ "handle": "agentrhq" } }, + "suno": { + "path": "plugins/suno", + "version": "0.1.0", + "description": "Webcmd commands for suno", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "techcrunch": { "path": "plugins/techcrunch", "version": "0.1.0", @@ -844,6 +914,16 @@ "handle": "agentrhq" } }, + "upwork": { + "path": "plugins/upwork", + "version": "0.1.0", + "description": "Webcmd commands for upwork", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "web": { "path": "plugins/web", "version": "0.1.0", @@ -924,6 +1004,16 @@ "handle": "agentrhq" } }, + "zepto": { + "path": "plugins/zepto", + "version": "0.1.0", + "description": "Webcmd commands for zepto", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "zlibrary": { "path": "plugins/zlibrary", "version": "0.1.0", From 66d6a4edffc7302b40f05c082dbd517453a9248d Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 17:35:31 +0530 Subject: [PATCH 18/39] refactor: migrate document and messaging adapters to plugins --- cli-manifest.json | 12168 ++++++---------- clis/_atlassian/shared.test.js | 170 - plugin-command-manifest.json | 2894 +++- plugins/bigbasket/README.md | 21 + {clis => plugins}/bigbasket/add-to-cart.js | 0 {clis => plugins}/bigbasket/cart.js | 0 {clis => plugins}/bigbasket/category.js | 0 {clis => plugins}/bigbasket/checkout.js | 0 {clis => plugins}/bigbasket/location.js | 0 plugins/bigbasket/package.json | 9 + {clis => plugins}/bigbasket/product.js | 0 {clis => plugins}/bigbasket/search.js | 0 .../bigbasket/test}/bigbasket.test.js | 20 +- {clis => plugins}/bigbasket/utils.js | 0 plugins/bigbasket/webcmd-plugin.json | 10 + plugins/chatgpt-app/README.md | 20 + {clis => plugins}/chatgpt-app/ask.js | 0 {clis => plugins}/chatgpt-app/ax.js | 0 {clis => plugins}/chatgpt-app/model.js | 0 {clis => plugins}/chatgpt-app/new.js | 0 plugins/chatgpt-app/package.json | 9 + {clis => plugins}/chatgpt-app/read.js | 0 {clis => plugins}/chatgpt-app/send.js | 0 {clis => plugins}/chatgpt-app/status.js | 0 .../chatgpt-app/test}/ax.test.js | 2 +- .../chatgpt-app/test}/commands.test.js | 12 +- plugins/chatgpt-app/webcmd-plugin.json | 10 + plugins/confluence/README.md | 18 + .../confluence/atlassian.js | 334 +- {clis => plugins}/confluence/create.js | 4 +- plugins/confluence/package.json | 9 + {clis => plugins}/confluence/page.js | 2 +- {clis => plugins}/confluence/search.js | 2 +- {clis => plugins}/confluence/shared.js | 2 +- plugins/confluence/test/atlassian.test.js | 101 + .../confluence/test}/commands.test.js | 8 +- {clis => plugins}/confluence/update.js | 2 +- plugins/confluence/webcmd-plugin.json | 10 + plugins/discord-app/README.md | 25 + {clis => plugins}/discord-app/channels.js | 0 {clis => plugins}/discord-app/delete.js | 0 {clis => plugins}/discord-app/goto.js | 0 {clis => plugins}/discord-app/members.js | 0 plugins/discord-app/package.json | 9 + {clis => plugins}/discord-app/read.js | 0 {clis => plugins}/discord-app/search.js | 0 {clis => plugins}/discord-app/send.js | 0 {clis => plugins}/discord-app/servers.js | 0 {clis => plugins}/discord-app/status.js | 0 .../discord-app/test}/commands.test.js | 16 +- {clis => plugins}/discord-app/thread-read.js | 0 {clis => plugins}/discord-app/threads.js | 0 {clis => plugins}/discord-app/utils.js | 0 plugins/discord-app/webcmd-plugin.json | 10 + plugins/geogebra/README.md | 23 + {clis => plugins}/geogebra/add-circle.js | 0 {clis => plugins}/geogebra/add-line.js | 0 {clis => plugins}/geogebra/add-point.js | 0 {clis => plugins}/geogebra/add-polygon.js | 0 {clis => plugins}/geogebra/eval.js | 0 {clis => plugins}/geogebra/hexagon.js | 0 {clis => plugins}/geogebra/info.js | 0 {clis => plugins}/geogebra/list.js | 0 plugins/geogebra/package.json | 9 + .../geogebra/test}/geogebra.test.js | 20 +- {clis => plugins}/geogebra/triangle.js | 0 {clis => plugins}/geogebra/utils.js | 0 plugins/geogebra/webcmd-plugin.json | 10 + plugins/mercury/README.md | 17 + {clis => plugins}/mercury/check-login.js | 0 plugins/mercury/package.json | 9 + .../mercury/reimbursement-draft.js | 0 .../mercury/reimbursement-plan.js | 0 .../mercury/test}/mercury.test.js | 8 +- {clis => plugins}/mercury/utils.js | 0 plugins/mercury/webcmd-plugin.json | 10 + plugins/paperreview/README.md | 17 + {clis => plugins}/paperreview/feedback.js | 0 plugins/paperreview/package.json | 9 + {clis => plugins}/paperreview/review.js | 0 {clis => plugins}/paperreview/submit.js | 0 .../paperreview/test}/commands.test.js | 10 +- .../paperreview/test}/utils.test.js | 2 +- {clis => plugins}/paperreview/utils.js | 0 plugins/paperreview/webcmd-plugin.json | 10 + plugins/spotify/README.md | 25 + plugins/spotify/package.json | 9 + {clis => plugins}/spotify/spotify.js | 0 .../spotify/test}/utils.test.js | 2 +- {clis => plugins}/spotify/utils.js | 0 plugins/spotify/webcmd-plugin.json | 10 + plugins/yollomi/README.md | 26 + {clis => plugins}/yollomi/background.js | 0 {clis => plugins}/yollomi/edit.js | 0 {clis => plugins}/yollomi/face-swap.js | 0 {clis => plugins}/yollomi/generate.js | 0 {clis => plugins}/yollomi/models.js | 0 {clis => plugins}/yollomi/object-remover.js | 0 plugins/yollomi/package.json | 9 + {clis => plugins}/yollomi/remove-bg.js | 0 {clis => plugins}/yollomi/restore.js | 0 {clis => plugins}/yollomi/try-on.js | 0 {clis => plugins}/yollomi/upload.js | 0 {clis => plugins}/yollomi/upscale.js | 0 {clis => plugins}/yollomi/utils.js | 0 {clis => plugins}/yollomi/video.js | 0 plugins/yollomi/webcmd-plugin.json | 10 + scripts/silent-column-drop-baseline.json | 4 +- scripts/typed-error-lint-baseline.json | 34 +- webcmd-plugin.json | 90 + 110 files changed, 8252 insertions(+), 8018 deletions(-) delete mode 100644 clis/_atlassian/shared.test.js create mode 100644 plugins/bigbasket/README.md rename {clis => plugins}/bigbasket/add-to-cart.js (100%) rename {clis => plugins}/bigbasket/cart.js (100%) rename {clis => plugins}/bigbasket/category.js (100%) rename {clis => plugins}/bigbasket/checkout.js (100%) rename {clis => plugins}/bigbasket/location.js (100%) create mode 100644 plugins/bigbasket/package.json rename {clis => plugins}/bigbasket/product.js (100%) rename {clis => plugins}/bigbasket/search.js (100%) rename {clis/bigbasket => plugins/bigbasket/test}/bigbasket.test.js (97%) rename {clis => plugins}/bigbasket/utils.js (100%) create mode 100644 plugins/bigbasket/webcmd-plugin.json create mode 100644 plugins/chatgpt-app/README.md rename {clis => plugins}/chatgpt-app/ask.js (100%) rename {clis => plugins}/chatgpt-app/ax.js (100%) rename {clis => plugins}/chatgpt-app/model.js (100%) rename {clis => plugins}/chatgpt-app/new.js (100%) create mode 100644 plugins/chatgpt-app/package.json rename {clis => plugins}/chatgpt-app/read.js (100%) rename {clis => plugins}/chatgpt-app/send.js (100%) rename {clis => plugins}/chatgpt-app/status.js (100%) rename {clis/chatgpt-app => plugins/chatgpt-app/test}/ax.test.js (99%) rename {clis/chatgpt-app => plugins/chatgpt-app/test}/commands.test.js (92%) create mode 100644 plugins/chatgpt-app/webcmd-plugin.json create mode 100644 plugins/confluence/README.md rename clis/_atlassian/shared.js => plugins/confluence/atlassian.js (54%) rename {clis => plugins}/confluence/create.js (95%) create mode 100644 plugins/confluence/package.json rename {clis => plugins}/confluence/page.js (93%) rename {clis => plugins}/confluence/search.js (96%) rename {clis => plugins}/confluence/shared.js (99%) create mode 100644 plugins/confluence/test/atlassian.test.js rename {clis/confluence => plugins/confluence/test}/commands.test.js (98%) rename {clis => plugins}/confluence/update.js (97%) create mode 100644 plugins/confluence/webcmd-plugin.json create mode 100644 plugins/discord-app/README.md rename {clis => plugins}/discord-app/channels.js (100%) rename {clis => plugins}/discord-app/delete.js (100%) rename {clis => plugins}/discord-app/goto.js (100%) rename {clis => plugins}/discord-app/members.js (100%) create mode 100644 plugins/discord-app/package.json rename {clis => plugins}/discord-app/read.js (100%) rename {clis => plugins}/discord-app/search.js (100%) rename {clis => plugins}/discord-app/send.js (100%) rename {clis => plugins}/discord-app/servers.js (100%) rename {clis => plugins}/discord-app/status.js (100%) rename {clis/discord-app => plugins/discord-app/test}/commands.test.js (98%) rename {clis => plugins}/discord-app/thread-read.js (100%) rename {clis => plugins}/discord-app/threads.js (100%) rename {clis => plugins}/discord-app/utils.js (100%) create mode 100644 plugins/discord-app/webcmd-plugin.json create mode 100644 plugins/geogebra/README.md rename {clis => plugins}/geogebra/add-circle.js (100%) rename {clis => plugins}/geogebra/add-line.js (100%) rename {clis => plugins}/geogebra/add-point.js (100%) rename {clis => plugins}/geogebra/add-polygon.js (100%) rename {clis => plugins}/geogebra/eval.js (100%) rename {clis => plugins}/geogebra/hexagon.js (100%) rename {clis => plugins}/geogebra/info.js (100%) rename {clis => plugins}/geogebra/list.js (100%) create mode 100644 plugins/geogebra/package.json rename {clis/geogebra => plugins/geogebra/test}/geogebra.test.js (96%) rename {clis => plugins}/geogebra/triangle.js (100%) rename {clis => plugins}/geogebra/utils.js (100%) create mode 100644 plugins/geogebra/webcmd-plugin.json create mode 100644 plugins/mercury/README.md rename {clis => plugins}/mercury/check-login.js (100%) create mode 100644 plugins/mercury/package.json rename {clis => plugins}/mercury/reimbursement-draft.js (100%) rename {clis => plugins}/mercury/reimbursement-plan.js (100%) rename {clis/mercury => plugins/mercury/test}/mercury.test.js (98%) rename {clis => plugins}/mercury/utils.js (100%) create mode 100644 plugins/mercury/webcmd-plugin.json create mode 100644 plugins/paperreview/README.md rename {clis => plugins}/paperreview/feedback.js (100%) create mode 100644 plugins/paperreview/package.json rename {clis => plugins}/paperreview/review.js (100%) rename {clis => plugins}/paperreview/submit.js (100%) rename {clis/paperreview => plugins/paperreview/test}/commands.test.js (98%) rename {clis/paperreview => plugins/paperreview/test}/utils.test.js (97%) rename {clis => plugins}/paperreview/utils.js (100%) create mode 100644 plugins/paperreview/webcmd-plugin.json create mode 100644 plugins/spotify/README.md create mode 100644 plugins/spotify/package.json rename {clis => plugins}/spotify/spotify.js (100%) rename {clis/spotify => plugins/spotify/test}/utils.test.js (97%) rename {clis => plugins}/spotify/utils.js (100%) create mode 100644 plugins/spotify/webcmd-plugin.json create mode 100644 plugins/yollomi/README.md rename {clis => plugins}/yollomi/background.js (100%) rename {clis => plugins}/yollomi/edit.js (100%) rename {clis => plugins}/yollomi/face-swap.js (100%) rename {clis => plugins}/yollomi/generate.js (100%) rename {clis => plugins}/yollomi/models.js (100%) rename {clis => plugins}/yollomi/object-remover.js (100%) create mode 100644 plugins/yollomi/package.json rename {clis => plugins}/yollomi/remove-bg.js (100%) rename {clis => plugins}/yollomi/restore.js (100%) rename {clis => plugins}/yollomi/try-on.js (100%) rename {clis => plugins}/yollomi/upload.js (100%) rename {clis => plugins}/yollomi/upscale.js (100%) rename {clis => plugins}/yollomi/utils.js (100%) rename {clis => plugins}/yollomi/video.js (100%) create mode 100644 plugins/yollomi/webcmd-plugin.json diff --git a/cli-manifest.json b/cli-manifest.json index d7233e70..febbc261 100644 --- a/cli-manifest.json +++ b/cli-manifest.json @@ -875,1139 +875,1213 @@ "sourceFile": "antigravity/storage.js" }, { - "site": "bigbasket", - "name": "add-to-cart", - "description": "Add a BigBasket product to cart", + "site": "facebook", + "name": "add-friend", + "description": "Send a friend request on Facebook", "access": "write", - "domain": "www.bigbasket.com", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "product", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Product ID or URL" - }, + "help": "Facebook username or profile URL" + } + ], + "columns": [ + "status", + "username" + ], + "type": "js", + "modulePath": "facebook/add-friend.js", + "sourceFile": "facebook/add-friend.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "events", + "description": "Browse Facebook event categories", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "quantity", + "name": "limit", "type": "int", - "default": 1, + "default": 15, "required": false, - "help": "Quantity to add (max 20)" + "help": "Number of categories" } ], "columns": [ - "ok", - "product_id", - "quantity", - "url", - "message" + "index", + "name" ], "type": "js", - "modulePath": "bigbasket/add-to-cart.js", - "sourceFile": "bigbasket/add-to-cart.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "facebook/events.js", + "sourceFile": "facebook/events.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "bigbasket", - "name": "cart", - "description": "Read BigBasket cart line items", + "site": "facebook", + "name": "feed", + "description": "Get your Facebook news feed", "access": "read", - "domain": "www.bigbasket.com", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of posts" + } + ], "columns": [ - "product_id", - "title", - "quantity", - "price", - "line_total", - "availability", - "url" + "index", + "author", + "content", + "likes", + "comments", + "shares" ], "type": "js", - "modulePath": "bigbasket/cart.js", - "sourceFile": "bigbasket/cart.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "facebook/feed.js", + "sourceFile": "facebook/feed.js", + "navigateBefore": false }, { - "site": "bigbasket", - "name": "category", - "description": "Read BigBasket category product cards", + "site": "facebook", + "name": "friends", + "description": "Get Facebook friend suggestions", "access": "read", - "domain": "www.bigbasket.com", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "category", - "type": "str", - "required": true, - "positional": true, - "help": "Category URL or slug" - }, + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of friend suggestions" + } + ], + "columns": [ + "index", + "name", + "mutual" + ], + "type": "js", + "modulePath": "facebook/friends.js", + "sourceFile": "facebook/friends.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "groups", + "description": "List your Facebook groups", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Maximum products to return (max 50)" + "help": "Number of groups" } ], "columns": [ - "rank", - "product_id", - "title", - "brand", - "pack_size", - "price", - "mrp", - "discount", - "availability", + "index", + "name", + "last_post", "url" ], "type": "js", - "modulePath": "bigbasket/category.js", - "sourceFile": "bigbasket/category.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "facebook/groups.js", + "sourceFile": "facebook/groups.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "bigbasket", - "name": "checkout", - "description": "Open BigBasket checkout review without placing an order", + "site": "facebook", + "name": "join-group", + "description": "Join a Facebook group", "access": "write", - "domain": "www.bigbasket.com", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "group", + "type": "str", + "required": true, + "positional": true, + "help": "Group ID or URL path (e.g. '1876150192925481' or group name)" + } + ], "columns": [ - "ok", - "stage", - "cart_total", - "address_ready", - "delivery_ready", - "payment_ready", - "next_action", - "url" + "status", + "group" ], "type": "js", - "modulePath": "bigbasket/checkout.js", - "sourceFile": "bigbasket/checkout.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "facebook/join-group.js", + "sourceFile": "facebook/join-group.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "bigbasket", - "name": "location", - "description": "Show the selected BigBasket delivery location", - "access": "read", - "domain": "www.bigbasket.com", + "site": "facebook", + "name": "login", + "description": "Open facebook login", + "access": "write", + "domain": "facebook.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "selected", - "label", - "area", - "city", - "pincode", - "source" + "status", + "logged_in", + "site", + "user_id", + "vanity", + "profile_url", + "action", + "verify_command" ], "type": "js", - "modulePath": "bigbasket/location.js", - "sourceFile": "bigbasket/location.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "facebook/auth.js", + "sourceFile": "facebook/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "bigbasket", - "name": "product", - "description": "Read BigBasket product details", + "site": "facebook", + "name": "marketplace-inbox", + "description": "List recent Facebook Marketplace buyer/seller conversations", "access": "read", - "domain": "www.bigbasket.com", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "product", - "type": "str", - "required": true, - "positional": true, - "help": "Product ID or URL" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of conversations to return" } ], "columns": [ - "product_id", - "title", - "brand", - "pack_size", - "price", - "mrp", - "discount", - "availability", - "delivery", - "image_url", - "url" + "index", + "buyer", + "listing", + "snippet", + "time", + "unread" ], "type": "js", - "modulePath": "bigbasket/product.js", - "sourceFile": "bigbasket/product.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "facebook/marketplace-inbox.js", + "sourceFile": "facebook/marketplace-inbox.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "bigbasket", - "name": "search", - "description": "Search BigBasket products", + "site": "facebook", + "name": "marketplace-listings", + "description": "List your Facebook Marketplace seller listings", "access": "read", - "domain": "www.bigbasket.com", + "domain": "www.facebook.com", "strategy": "cookie", "browser": true, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Maximum products to return (max 50)" + "help": "Number of listings to return" } ], "columns": [ - "rank", - "product_id", + "index", "title", - "brand", - "pack_size", "price", - "mrp", - "discount", - "availability", - "url" - ], - "tags": [ - "search" + "status", + "listed", + "clicks", + "actions" ], "type": "js", - "modulePath": "bigbasket/search.js", - "sourceFile": "bigbasket/search.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "facebook/marketplace-listings.js", + "sourceFile": "facebook/marketplace-listings.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "chatgpt-app", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", - "access": "write", - "domain": "localhost", - "strategy": "public", - "browser": false, + "site": "facebook", + "name": "memories", + "description": "Get your Facebook memories (On This Day)", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "model", - "type": "str", - "required": false, - "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] - }, - { - "name": "timeout", + "name": "limit", "type": "int", - "default": 30, - "required": false, - "help": "Max seconds to wait for response (default: 30)" - }, - { - "name": "image", - "type": "str", + "default": 10, "required": false, - "help": "Path to local image to attach (optional)" + "help": "Number of memories" } ], "columns": [ - "Role", - "Text" + "index", + "source", + "content", + "time" ], "type": "js", - "modulePath": "chatgpt-app/ask.js", - "sourceFile": "chatgpt-app/ask.js" + "modulePath": "facebook/memories.js", + "sourceFile": "facebook/memories.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "chatgpt-app", - "name": "model", - "description": "Switch ChatGPT Desktop model/mode (auto, instant, thinking, 5.2-instant, 5.2-thinking)", + "site": "facebook", + "name": "notifications", + "description": "Get recent Facebook notifications (includes unread / time / url / notif_id / notif_type columns)", "access": "read", - "domain": "localhost", - "strategy": "public", - "browser": false, + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "model", - "type": "str", - "required": true, - "positional": true, - "help": "Model to switch to", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Number of notifications (1-100)" } ], "columns": [ - "Status", - "Model" + "index", + "unread", + "text", + "time", + "url", + "notif_id", + "notif_type" ], "type": "js", - "modulePath": "chatgpt-app/model.js", - "sourceFile": "chatgpt-app/model.js" + "modulePath": "facebook/notifications.js", + "sourceFile": "facebook/notifications.js", + "navigateBefore": false }, { - "site": "chatgpt-app", - "name": "new", - "description": "Open a new chat in ChatGPT Desktop App", - "access": "write", - "domain": "localhost", - "strategy": "public", - "browser": false, + "site": "facebook", + "name": "profile", + "description": "Get Facebook user/page profile info", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "temp", - "type": "boolean", - "default": false, - "required": false, - "help": "Open a temporary chat with privacy protection" + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Facebook username or page name" } ], "columns": [ - "Status" + "name", + "username", + "friends", + "followers", + "url" ], "type": "js", - "modulePath": "chatgpt-app/new.js", - "sourceFile": "chatgpt-app/new.js" + "modulePath": "facebook/profile.js", + "sourceFile": "facebook/profile.js", + "navigateBefore": "https://www.facebook.com" }, { - "site": "chatgpt-app", - "name": "read", - "description": "Read the last visible message from the focused ChatGPT Desktop window", + "site": "facebook", + "name": "search", + "description": "Search Facebook for people, pages, or posts", "access": "read", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "chatgpt-app/read.js", - "sourceFile": "chatgpt-app/read.js" - }, - { - "site": "chatgpt-app", - "name": "send", - "description": "Send a message to the active ChatGPT Desktop App window", - "access": "write", - "domain": "localhost", - "strategy": "public", - "browser": false, + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "text", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Message to send" + "help": "Search query" }, { - "name": "model", - "type": "str", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] + "help": "Number of results" } ], "columns": [ - "Status" + "index", + "title", + "text", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "chatgpt-app/send.js", - "sourceFile": "chatgpt-app/send.js" + "modulePath": "facebook/search.js", + "sourceFile": "facebook/search.js", + "navigateBefore": false }, { - "site": "chatgpt-app", - "name": "status", - "description": "Check if ChatGPT Desktop App is running natively on macOS", + "site": "facebook", + "name": "whoami", + "description": "Show the current logged-in facebook account", "access": "read", - "domain": "localhost", - "strategy": "public", - "browser": false, + "domain": "facebook.com", + "strategy": "cookie", + "browser": true, "args": [], "columns": [ - "Status" + "logged_in", + "site", + "user_id", + "vanity", + "profile_url" ], "type": "js", - "modulePath": "chatgpt-app/status.js", - "sourceFile": "chatgpt-app/status.js" + "modulePath": "facebook/auth.js", + "sourceFile": "facebook/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "confluence", - "name": "create", - "description": "Create a Confluence page from Markdown or storage XHTML", + "site": "grok", + "name": "ask", + "description": "Send a message to Grok and get response", "access": "write", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "domain": "grok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "space", - "type": "string", - "required": true, - "help": "Cloud space id, or Data Center space key" - }, - { - "name": "title", + "name": "prompt", "type": "string", "required": true, - "help": "Page title" + "positional": true, + "help": "Prompt to send to Grok" }, { - "name": "file", - "type": "string", - "required": true, - "help": "Markdown file path" + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait for response (default: 120)" }, { - "name": "parent", - "type": "string", + "name": "new", + "type": "boolean", + "default": false, "required": false, - "help": "Optional parent page id" - }, + "help": "Start a new chat before sending (default: false)" + } + ], + "columns": [ + "response" + ], + "type": "js", + "modulePath": "grok/ask.js", + "sourceFile": "grok/ask.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "delete", + "description": "Delete a Grok conversation by ID. Grok takes effect immediately with no confirmation dialog — require --yes to actually delete.", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "representation", + "name": "id", "type": "string", - "default": "markdown", - "required": false, - "help": "Input file format", - "choices": [ - "markdown", - "storage" - ] + "required": true, + "positional": true, + "help": "Conversation UUID or grok.com/c/ URL" }, { - "name": "execute", + "name": "yes", "type": "boolean", + "default": false, "required": false, - "help": "Actually create the remote page" + "help": "Actually delete (default is a dry-run preview)" } ], "columns": [ "status", - "id", - "title", - "spaceId", - "spaceKey", - "version", - "url" + "id" ], "type": "js", - "modulePath": "confluence/create.js", - "sourceFile": "confluence/create.js" + "modulePath": "grok/delete.js", + "sourceFile": "grok/delete.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" }, { - "site": "confluence", - "name": "page", - "description": "Confluence page by id with storage and Markdown body", + "site": "grok", + "name": "detail", + "description": "Open a Grok conversation by ID and read its messages", "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "domain": "grok.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "id", "type": "str", "required": true, "positional": true, - "help": "Confluence page id" + "help": "Session ID (UUID) or full https://grok.com/c/ URL" + }, + { + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" } ], "columns": [ - "id", - "title", - "status", - "spaceId", - "spaceKey", - "version", - "url" + "Role", + "Text" ], "type": "js", - "modulePath": "confluence/page.js", - "sourceFile": "confluence/page.js" + "modulePath": "grok/detail.js", + "sourceFile": "grok/detail.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "confluence", - "name": "search", - "description": "Search Confluence content with CQL", + "site": "grok", + "name": "export", + "description": "Export all visible Grok conversation history metadata", "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "example": "webcmd grok export -f yaml", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "cql", - "type": "str", - "required": true, - "positional": true, - "help": "CQL query, e.g. \"type = page and title ~ \\\"RCA\\\"\"" - }, - { - "name": "space", - "type": "string", + "name": "limit", + "type": "int", + "default": 0, "required": false, - "help": "Limit search to a Confluence space key" + "help": "Max conversations to export; 0 means all loaded history" }, { - "name": "limit", + "name": "maxScrolls", "type": "int", - "default": 20, + "default": 80, "required": false, - "help": "Max results to return (1-100)" + "help": "Max history-list scroll rounds when limit is 0 (max 500)" } ], "columns": [ + "index", "id", "title", - "type", - "spaceKey", - "status", - "lastModified", + "date", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "confluence/search.js", - "sourceFile": "confluence/search.js" + "modulePath": "grok/export.js", + "sourceFile": "grok/export.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "confluence", - "name": "update", - "description": "Update a Confluence page body from Markdown or storage XHTML", - "access": "write", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Confluence page id" - }, + "site": "grok", + "name": "export-all", + "description": "Export Grok conversation history and each conversation transcript", + "access": "read", + "example": "webcmd grok export-all --limit 5 -f json", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "file", - "type": "string", - "required": true, - "help": "Markdown file path" + "name": "limit", + "type": "int", + "default": 0, + "required": false, + "help": "Max conversations to export; 0 means all loaded history" }, { - "name": "title", - "type": "string", + "name": "offset", + "type": "int", + "default": 0, "required": false, - "help": "Optional replacement title; defaults to current title" + "help": "Skip this many conversations before exporting" }, { - "name": "version-message", + "name": "manifestPath", "type": "string", + "default": "", "required": false, - "help": "Confluence version message" + "help": "Optional grok/export JSON manifest path; skips history dialog and visits listed /c pages directly" }, { - "name": "representation", - "type": "string", - "default": "markdown", + "name": "maxScrolls", + "type": "int", + "default": 80, "required": false, - "help": "Input file format", - "choices": [ - "markdown", - "storage" - ] + "help": "Max history-list scroll rounds when limit is 0 (max 500)" }, { - "name": "execute", - "type": "boolean", + "name": "pageScrolls", + "type": "int", + "default": 30, + "required": false, + "help": "Max per-conversation scroll-to-bottom rounds (max 200)" + }, + { + "name": "pageTimeoutMs", + "type": "int", + "default": 30000, + "required": false, + "help": "Max wait for each conversation page to show messages" + }, + { + "name": "delayMinMs", + "type": "int", + "default": 0, + "required": false, + "help": "Minimum polite delay after a conversation page loads" + }, + { + "name": "delayMaxMs", + "type": "int", + "default": 5000, "required": false, - "help": "Actually update the remote page" + "help": "Maximum polite delay after a conversation page loads" } ], "columns": [ - "status", + "index", "id", "title", - "spaceId", - "spaceKey", - "version", - "url" + "date", + "url", + "status", + "messageCount", + "error", + "messagesJson" ], "type": "js", - "modulePath": "confluence/update.js", - "sourceFile": "confluence/update.js" + "modulePath": "grok/export-all.js", + "sourceFile": "grok/export-all.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "channels", - "description": "List channels in the current Discord server", + "site": "grok", + "name": "history", + "description": "List recent Grok conversations from the sidebar (requires login)", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "grok.com", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max conversations to show (default 20, max 100)" + } + ], "columns": [ "Index", - "Channel", - "Type", - "guild_id", - "channel_id", - "url" + "Title", + "Url" ], "type": "js", - "modulePath": "discord-app/channels.js", - "sourceFile": "discord-app/channels.js", - "navigateBefore": true + "modulePath": "grok/history.js", + "sourceFile": "grok/history.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "delete", - "description": "Delete a message by its ID in the active Discord channel", + "site": "grok", + "name": "image", + "description": "Generate images on grok.com and return image URLs", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "grok.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "message_id", + "name": "prompt", "type": "string", "required": true, "positional": true, - "help": "The ID of the message to delete (visible via Developer Mode or the read command)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "discord-app/delete.js", - "sourceFile": "discord-app/delete.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "goto", - "description": "Open a Discord channel by id/name/url without sending messages", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Image generation prompt" + }, { - "name": "guild", - "type": "str", + "name": "timeout", + "type": "int", + "default": 240, "required": false, - "help": "Guild/server id or visible name" + "help": "Max seconds to wait for the image (default: 240)" }, { - "name": "channel", - "type": "str", + "name": "new", + "type": "boolean", + "default": false, "required": false, - "help": "Channel id or visible name" + "help": "Start a new chat before sending (default: false)" }, { - "name": "url", - "type": "str", + "name": "count", + "type": "int", + "default": 1, "required": false, - "help": "Discord channel URL" + "help": "Minimum images to wait for before returning (default: 1)" }, { - "name": "timeout", - "type": "str", - "default": "8", + "name": "out", + "type": "string", + "default": "", "required": false, - "help": "Seconds to wait for Discord to show the route (default: 8)" + "help": "Directory to save downloaded images (uses browser session to bypass auth)" } ], "columns": [ - "Status", - "guild_id", - "channel_id", - "url" + "url", + "width", + "height", + "path" ], "type": "js", - "modulePath": "discord-app/goto.js", - "sourceFile": "discord-app/goto.js", - "navigateBefore": true + "modulePath": "grok/image.js", + "sourceFile": "grok/image.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "members", - "description": "List online members in the current Discord channel", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "grok", + "name": "login", + "description": "Open grok login", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Index", - "Name", - "Status" + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" ], "type": "js", - "modulePath": "discord-app/members.js", - "sourceFile": "discord-app/members.js", - "navigateBefore": true + "modulePath": "grok/auth.js", + "sourceFile": "grok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "read", - "description": "Read recent messages from the active or targeted Discord channel", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "grok", + "name": "new", + "description": "Start a new conversation in Grok", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", "browser": true, - "args": [ - { - "name": "count", - "type": "str", - "default": "20", - "required": false, - "help": "Number of messages to read (default: 20)" - }, - { - "name": "guild", - "type": "str", - "required": false, - "help": "Guild/server id or visible name for targeted reads" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Channel id or visible name for targeted reads" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Discord channel URL to open before reading" - } - ], + "args": [], "columns": [ - "Author", - "Time", - "Message", - "channel_id", - "message_id" + "Status" ], "type": "js", - "modulePath": "discord-app/read.js", - "sourceFile": "discord-app/read.js", - "navigateBefore": true + "modulePath": "grok/new.js", + "sourceFile": "grok/new.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "search", - "description": "Search messages in the current Discord server/channel (Cmd+F)", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "grok", + "name": "pin", + "description": "Pin a Grok conversation by ID", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "query", - "type": "str", + "name": "id", + "type": "string", "required": true, "positional": true, - "help": "Search query" + "help": "Conversation UUID or grok.com/c/ URL" } ], "columns": [ - "Index", - "Author", - "Message" - ], - "tags": [ - "search" + "status", + "id" ], "type": "js", - "modulePath": "discord-app/search.js", - "sourceFile": "discord-app/search.js", - "navigateBefore": true + "modulePath": "grok/pin.js", + "sourceFile": "grok/pin.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "send", - "description": "Send a message in the active Discord channel", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Message to send" + "site": "grok", + "name": "read", + "description": "Read messages in the current Grok conversation", + "access": "read", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" } ], "columns": [ - "Status" + "Role", + "Text" ], "type": "js", - "modulePath": "discord-app/send.js", - "sourceFile": "discord-app/send.js", - "navigateBefore": true + "modulePath": "grok/read.js", + "sourceFile": "grok/read.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "servers", - "description": "List all Discord servers (guilds) in the sidebar", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "grok", + "name": "send", + "description": "Fire-and-forget: send a prompt to Grok without waiting for the reply", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send to Grok" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" + } + ], "columns": [ - "Index", - "Server", - "guild_id", - "url" + "Status", + "Prompt" ], "type": "js", - "modulePath": "discord-app/servers.js", - "sourceFile": "discord-app/servers.js", - "navigateBefore": true + "modulePath": "grok/send.js", + "sourceFile": "grok/send.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", + "site": "grok", "name": "status", - "description": "Check active CDP connection to Discord Desktop", + "description": "Check Grok page availability, login state, current session and model", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "grok.com", + "strategy": "cookie", "browser": true, "args": [], "columns": [ "Status", - "Url", - "Title" + "Login", + "Model", + "SessionId", + "Url" ], "type": "js", - "modulePath": "discord-app/status.js", - "sourceFile": "discord-app/status.js", - "navigateBefore": true + "modulePath": "grok/status.js", + "sourceFile": "grok/status.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "thread-read", - "description": "Read recent messages from a Discord thread/post by id or URL", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "grok", + "name": "unpin", + "description": "Unpin a Grok conversation by ID", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "thread", - "type": "str", - "required": false, - "help": "Thread/post id, or a full Discord thread/post URL" - }, - { - "name": "count", - "type": "str", - "default": "20", - "required": false, - "help": "Number of messages to read (default: 20)" - }, - { - "name": "guild", - "type": "str", - "required": false, - "help": "Parent guild/server id or visible name" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Parent forum/channel id or visible name" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Discord thread/post URL" + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID or grok.com/c/ URL" } ], "columns": [ - "Author", - "Time", - "Message", - "channel_id", - "message_id" + "status", + "id" ], "type": "js", - "modulePath": "discord-app/thread-read.js", - "sourceFile": "discord-app/thread-read.js", - "navigateBefore": true + "modulePath": "grok/pin.js", + "sourceFile": "grok/pin.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "threads", - "description": "List visible Discord forum/thread posts in the active or targeted channel", + "site": "grok", + "name": "whoami", + "description": "Show the current logged-in grok account", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" + ], + "type": "js", + "modulePath": "grok/auth.js", + "sourceFile": "grok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "instagram", + "name": "collection-create", + "description": "Create a new Instagram saved-posts collection (folder)", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "str", - "default": "30", - "required": false, - "help": "Maximum thread/post cards to return (default: 30)" - }, - { - "name": "guild", - "type": "str", - "required": false, - "help": "Guild/server id or visible name for targeted thread listing" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Forum/channel id or visible name for targeted thread listing" - }, - { - "name": "url", + "name": "name", "type": "str", - "required": false, - "help": "Discord forum/channel URL to open before listing threads" + "required": true, + "positional": true, + "help": "Name of the collection to create" } ], "columns": [ - "Index", - "Thread", - "Author", - "Updated", - "Preview", - "guild_id", - "channel_id", - "thread_id", - "url" + "status", + "collectionId", + "collectionName", + "mediaCount" ], "type": "js", - "modulePath": "discord-app/threads.js", - "sourceFile": "discord-app/threads.js", - "navigateBefore": true + "modulePath": "instagram/collection-create.js", + "sourceFile": "instagram/collection-create.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "facebook", - "name": "add-friend", - "description": "Send a friend request on Facebook", + "site": "instagram", + "name": "collection-delete", + "description": "Delete an Instagram saved-posts collection (folder) by name or id", "access": "write", - "domain": "www.facebook.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "target", "type": "str", "required": true, "positional": true, - "help": "Facebook username or profile URL" + "help": "Collection name (case-insensitive) or numeric collection_id" } ], "columns": [ "status", - "username" + "collectionId", + "collectionName" ], "type": "js", - "modulePath": "facebook/add-friend.js", - "sourceFile": "facebook/add-friend.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "instagram/collection-delete.js", + "sourceFile": "instagram/collection-delete.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "facebook", - "name": "events", - "description": "Browse Facebook event categories", - "access": "read", - "domain": "www.facebook.com", + "site": "instagram", + "name": "comment", + "description": "Comment on an Instagram post", + "access": "write", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Username of the post author" + }, + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Comment text" + }, + { + "name": "index", "type": "int", - "default": 15, + "default": 1, "required": false, - "help": "Number of categories" + "help": "Post index (1 = most recent)" } ], "columns": [ - "index", - "name" + "status", + "user", + "text" ], "type": "js", - "modulePath": "facebook/events.js", - "sourceFile": "facebook/events.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "instagram/comment.js", + "sourceFile": "instagram/comment.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "facebook", - "name": "feed", - "description": "Get your Facebook news feed", + "site": "instagram", + "name": "download", + "description": "Download images and videos from Instagram posts and reels", "access": "read", - "domain": "www.facebook.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of posts" - } - ], + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram post / reel / tv URL" + }, + { + "name": "path", + "type": "str", + "default": "~/Downloads/Instagram", + "required": false, + "help": "Download directory" + } + ], + "type": "js", + "modulePath": "instagram/download.js", + "sourceFile": "instagram/download.js", + "navigateBefore": false + }, + { + "site": "instagram", + "name": "explore", + "description": "Instagram explore/discover trending posts", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts" + } + ], "columns": [ - "index", - "author", - "content", + "rank", + "user", + "caption", "likes", "comments", - "shares" + "type" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "facebook/feed.js", - "sourceFile": "facebook/feed.js", - "navigateBefore": false + "modulePath": "instagram/explore.js", + "sourceFile": "instagram/explore.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "facebook", - "name": "friends", - "description": "Get Facebook friend suggestions", + "site": "instagram", + "name": "follow", + "description": "Follow an Instagram user", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram username to follow" + } + ], + "columns": [ + "status", + "username" + ], + "type": "js", + "modulePath": "instagram/follow.js", + "sourceFile": "instagram/follow.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "followers", + "description": "List followers of an Instagram user", "access": "read", - "domain": "www.facebook.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram username" + }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of friend suggestions" + "help": "Number of followers" } ], "columns": [ - "index", + "rank", + "username", "name", - "mutual" + "verified", + "private" ], "type": "js", - "modulePath": "facebook/friends.js", - "sourceFile": "facebook/friends.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "instagram/followers.js", + "sourceFile": "instagram/followers.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "facebook", - "name": "groups", - "description": "List your Facebook groups", + "site": "instagram", + "name": "following", + "description": "List accounts an Instagram user is following", "access": "read", - "domain": "www.facebook.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram username" + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of groups" + "help": "Number of accounts" } ], "columns": [ - "index", + "rank", + "username", "name", - "last_post", - "url" + "verified", + "private" ], "type": "js", - "modulePath": "facebook/groups.js", - "sourceFile": "facebook/groups.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "instagram/following.js", + "sourceFile": "instagram/following.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "facebook", - "name": "join-group", - "description": "Join a Facebook group", + "site": "instagram", + "name": "like", + "description": "Like an Instagram post", "access": "write", - "domain": "www.facebook.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "group", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Group ID or URL path (e.g. '1876150192925481' or group name)" + "help": "Username of the post author" + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Post index (1 = most recent)" } ], "columns": [ "status", - "group" + "user", + "post" ], "type": "js", - "modulePath": "facebook/join-group.js", - "sourceFile": "facebook/join-group.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "instagram/like.js", + "sourceFile": "instagram/like.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "facebook", + "site": "instagram", "name": "login", - "description": "Open facebook login", + "description": "Open instagram login", "access": "write", - "domain": "facebook.com", + "domain": "instagram.com", "strategy": "cookie", "browser": true, "args": [], @@ -2016,143 +2090,111 @@ "logged_in", "site", "user_id", - "vanity", - "profile_url", + "username", + "full_name", "action", "verify_command" ], "type": "js", - "modulePath": "facebook/auth.js", - "sourceFile": "facebook/auth.js", + "modulePath": "instagram/auth.js", + "sourceFile": "instagram/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "facebook", - "name": "marketplace-inbox", - "description": "List recent Facebook Marketplace buyer/seller conversations", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", + "site": "instagram", + "name": "note", + "description": "Publish a text Instagram note", + "access": "write", + "domain": "www.instagram.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "limit", + "name": "content", + "type": "str", + "required": true, + "positional": true, + "help": "Note text (max 60 characters)" + }, + { + "name": "timeout", "type": "int", - "default": 20, + "default": 120, "required": false, - "help": "Number of conversations to return" + "help": "Max seconds for the overall command (default: 120)" } ], "columns": [ - "index", - "buyer", - "listing", - "snippet", - "time", - "unread" + "status", + "detail", + "noteId" ], "type": "js", - "modulePath": "facebook/marketplace-inbox.js", - "sourceFile": "facebook/marketplace-inbox.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "instagram/note.js", + "sourceFile": "instagram/note.js", + "navigateBefore": true }, { - "site": "facebook", - "name": "marketplace-listings", - "description": "List your Facebook Marketplace seller listings", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", + "site": "instagram", + "name": "post", + "description": "Post an Instagram feed image or mixed-media carousel", + "access": "write", + "domain": "www.instagram.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "media", + "type": "str", "required": false, - "help": "Number of listings to return" - } - ], - "columns": [ - "index", - "title", - "price", - "status", - "listed", - "clicks", - "actions" - ], - "type": "js", - "modulePath": "facebook/marketplace-listings.js", - "sourceFile": "facebook/marketplace-listings.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "memories", - "description": "Get your Facebook memories (On This Day)", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ + "valueRequired": true, + "help": "Comma-separated media paths (images/videos, up to 10)", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "image/jpeg", + "image/png", + "image/webp", + "video/mp4" + ], + "maxBytes": 262144000 + } + }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "content", + "type": "str", "required": false, - "help": "Number of memories" - } - ], - "columns": [ - "index", - "source", - "content", - "time" - ], - "type": "js", - "modulePath": "facebook/memories.js", - "sourceFile": "facebook/memories.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "notifications", - "description": "Get recent Facebook notifications (includes unread / time / url / notif_id / notif_type columns)", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ + "positional": true, + "help": "Caption text" + }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 15, + "default": 300, "required": false, - "help": "Number of notifications (1-100)" + "help": "Max seconds for the overall command (default: 300)" } ], "columns": [ - "index", - "unread", - "text", - "time", - "url", - "notif_id", - "notif_type" + "status", + "detail", + "url" ], "type": "js", - "modulePath": "facebook/notifications.js", - "sourceFile": "facebook/notifications.js", - "navigateBefore": false + "modulePath": "instagram/post.js", + "sourceFile": "instagram/post.js", + "navigateBefore": true }, { - "site": "facebook", + "site": "instagram", "name": "profile", - "description": "Get Facebook user/page profile info", + "description": "Get Instagram user profile info", "access": "read", - "domain": "www.facebook.com", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ @@ -2161,673 +2203,654 @@ "type": "str", "required": true, "positional": true, - "help": "Facebook username or page name" + "help": "Instagram username" } ], "columns": [ - "name", "username", - "friends", + "name", "followers", - "url" + "following", + "posts", + "verified", + "bio" ], "type": "js", - "modulePath": "facebook/profile.js", - "sourceFile": "facebook/profile.js", - "navigateBefore": "https://www.facebook.com" + "modulePath": "instagram/profile.js", + "sourceFile": "instagram/profile.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "facebook", - "name": "search", - "description": "Search Facebook for people, pages, or posts", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", + "site": "instagram", + "name": "reel", + "description": "Post an Instagram reel video", + "access": "write", + "domain": "www.instagram.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "query", + "name": "video", "type": "str", - "required": true, + "required": false, + "valueRequired": true, + "help": "Path to a single .mp4 video file", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "video/mp4" + ], + "maxBytes": 262144000 + } + }, + { + "name": "content", + "type": "str", + "required": false, "positional": true, - "help": "Search query" + "help": "Caption text" }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 10, + "default": 600, "required": false, - "help": "Number of results" + "help": "Max seconds for the overall command (default: 600)" } ], "columns": [ - "index", - "title", - "text", + "status", + "detail", "url" ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "facebook/search.js", - "sourceFile": "facebook/search.js", - "navigateBefore": false - }, - { - "site": "facebook", - "name": "whoami", - "description": "Show the current logged-in facebook account", - "access": "read", - "domain": "facebook.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "vanity", - "profile_url" - ], "type": "js", - "modulePath": "facebook/auth.js", - "sourceFile": "facebook/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "instagram/reel.js", + "sourceFile": "instagram/reel.js", + "navigateBefore": true }, { - "site": "geogebra", - "name": "add-circle", - "description": "Create a circle by center+radius or center+point", + "site": "instagram", + "name": "save", + "description": "Save (bookmark) an Instagram post", "access": "write", - "example": "webcmd geogebra add-circle --center A --radius 3", - "domain": "www.geogebra.org", - "strategy": "public", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "center", + "name": "username", "type": "str", "required": true, - "help": "Center point label (e.g. A)" - }, - { - "name": "radius", - "type": "str", - "required": false, - "help": "Radius value (number) or a point label on the circle" + "positional": true, + "help": "Username of the post author" }, { - "name": "point", - "type": "str", + "name": "index", + "type": "int", + "default": 1, "required": false, - "help": "Alternative: a point label on the circle (use instead of --radius for Circle(center,point))" + "help": "Post index (1 = most recent)" } ], "columns": [ - "label", - "center", - "radius" + "status", + "user", + "post" ], "type": "js", - "modulePath": "geogebra/add-circle.js", - "sourceFile": "geogebra/add-circle.js", - "navigateBefore": false + "modulePath": "instagram/save.js", + "sourceFile": "instagram/save.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "geogebra", - "name": "add-line", - "description": "Create a line through two points or a segment between two points", - "access": "write", - "example": "webcmd geogebra add-line --points A,B --type segment", - "domain": "www.geogebra.org", - "strategy": "public", + "site": "instagram", + "name": "saved", + "description": "Get your saved Instagram posts (optionally from a specific collection)", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "points", - "type": "str", - "required": true, - "help": "Two point labels separated by comma (e.g. \"A,B\")" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of saved posts" }, { - "name": "type", + "name": "collection", "type": "str", - "default": "line", "required": false, - "help": "Type: line, segment, or ray (default: line)", - "choices": [ - "line", - "segment", - "ray" - ] + "help": "Collection name (case-insensitive). Omit for the default \"All posts\" feed." } ], "columns": [ - "label", - "type", - "points" + "index", + "user", + "caption", + "likes", + "comments", + "type" ], "type": "js", - "modulePath": "geogebra/add-line.js", - "sourceFile": "geogebra/add-line.js", - "navigateBefore": false + "modulePath": "instagram/saved.js", + "sourceFile": "instagram/saved.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "geogebra", - "name": "add-point", - "description": "Create a point with given label and coordinates", - "access": "write", - "example": "webcmd geogebra add-point --name A --coords 1,2", - "domain": "www.geogebra.org", - "strategy": "public", + "site": "instagram", + "name": "search", + "description": "Search Instagram users", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "name", + "name": "query", "type": "str", "required": true, - "help": "Point label (e.g. A, B, P1)" + "positional": true, + "help": "Search query" }, { - "name": "coords", - "type": "str", - "required": true, - "help": "Coordinates as x,y (e.g. \"1,2\")" + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" } ], "columns": [ + "rank", + "username", "name", - "x", - "y" + "verified", + "private", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "geogebra/add-point.js", - "sourceFile": "geogebra/add-point.js", - "navigateBefore": false + "modulePath": "instagram/search.js", + "sourceFile": "instagram/search.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "geogebra", - "name": "add-polygon", - "description": "Create a polygon from a list of point labels", + "site": "instagram", + "name": "story", + "description": "Post a single Instagram story image or video", "access": "write", - "example": "webcmd geogebra add-polygon --points A,B,C", - "domain": "www.geogebra.org", - "strategy": "public", + "domain": "www.instagram.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "points", + "name": "media", "type": "str", - "required": true, - "help": "Comma-separated point labels (e.g. \"A,B,C\" or \"A,B,C,D\")" + "required": false, + "valueRequired": true, + "help": "Path to a single story image or video file" + }, + { + "name": "timeout", + "type": "int", + "default": 300, + "required": false, + "help": "Max seconds for the overall command (default: 300)" } ], "columns": [ - "label", - "vertices" + "status", + "detail", + "url" ], "type": "js", - "modulePath": "geogebra/add-polygon.js", - "sourceFile": "geogebra/add-polygon.js", - "navigateBefore": false + "modulePath": "instagram/story.js", + "sourceFile": "instagram/story.js", + "navigateBefore": true }, { - "site": "geogebra", - "name": "eval", - "description": "Execute one or more GeoGebra command strings (semicolon-separated)", + "site": "instagram", + "name": "unfollow", + "description": "Unfollow an Instagram user", "access": "write", - "example": "webcmd geogebra eval \"A=(0,0);B=(4,0);c=Circle(A,B);d=Circle(B,A);C=Intersect(c,d,1);Polygon(A,B,C)\"", - "domain": "www.geogebra.org", - "strategy": "public", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "command", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "GeoGebra command string (use ; to chain multiple commands)" + "help": "Instagram username to unfollow" } ], "columns": [ - "command", - "result" + "status", + "username" ], "type": "js", - "modulePath": "geogebra/eval.js", - "sourceFile": "geogebra/eval.js", - "navigateBefore": false + "modulePath": "instagram/unfollow.js", + "sourceFile": "instagram/unfollow.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "geogebra", - "name": "hexagon", - "description": "Draw a regular hexagon centered at the origin", + "site": "instagram", + "name": "unlike", + "description": "Unlike an Instagram post", "access": "write", - "example": "webcmd geogebra hexagon --size 3", - "domain": "www.geogebra.org", - "strategy": "public", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "size", + "name": "username", "type": "str", - "default": "2", + "required": true, + "positional": true, + "help": "Username of the post author" + }, + { + "name": "index", + "type": "int", + "default": 1, "required": false, - "help": "Radius of the hexagon (default: 2)" + "help": "Post index (1 = most recent)" } ], "columns": [ - "step", - "result" + "status", + "user", + "post" ], "type": "js", - "modulePath": "geogebra/hexagon.js", - "sourceFile": "geogebra/hexagon.js", - "navigateBefore": false + "modulePath": "instagram/unlike.js", + "sourceFile": "instagram/unlike.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "geogebra", - "name": "info", - "description": "Get detailed properties of a GeoGebra object", - "access": "read", - "example": "webcmd geogebra info --name A", - "domain": "www.geogebra.org", - "strategy": "public", + "site": "instagram", + "name": "unsave", + "description": "Unsave (remove bookmark) an Instagram post", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "name", + "name": "username", "type": "str", "required": true, - "help": "Object label (e.g. A, c1, poly1)" + "positional": true, + "help": "Username of the post author" + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Post index (1 = most recent)" } ], "columns": [ - "property", - "value" + "status", + "user", + "post" ], "type": "js", - "modulePath": "geogebra/info.js", - "sourceFile": "geogebra/info.js", - "navigateBefore": false + "modulePath": "instagram/unsave.js", + "sourceFile": "instagram/unsave.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "geogebra", - "name": "list", - "description": "List all geometric objects on the GeoGebra canvas", + "site": "instagram", + "name": "user", + "description": "Get recent posts from an Instagram user", "access": "read", - "domain": "www.geogebra.org", - "strategy": "public", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "type", + "name": "username", "type": "str", + "required": true, + "positional": true, + "help": "Instagram username" + }, + { + "name": "limit", + "type": "int", + "default": 12, "required": false, - "help": "Filter by object type (e.g. \"point\", \"line\", \"circle\")" + "help": "Number of posts" } ], "columns": [ - "name", + "index", + "caption", + "likes", + "comments", "type", - "value", - "visible" + "date" ], "type": "js", - "modulePath": "geogebra/list.js", - "sourceFile": "geogebra/list.js", - "navigateBefore": false + "modulePath": "instagram/user.js", + "sourceFile": "instagram/user.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "geogebra", - "name": "triangle", - "description": "Draw an equilateral triangle from a horizontal base segment", - "access": "write", - "example": "webcmd geogebra triangle --size 4", - "domain": "www.geogebra.org", - "strategy": "public", + "site": "instagram", + "name": "whoami", + "description": "Show the current logged-in instagram account", + "access": "read", + "domain": "instagram.com", + "strategy": "cookie", "browser": true, - "args": [ - { - "name": "size", - "type": "str", - "default": "2", - "required": false, - "help": "Side length of the triangle (default: 2)" - } - ], + "args": [], "columns": [ - "step", - "result" + "logged_in", + "site", + "user_id", + "username", + "full_name" ], "type": "js", - "modulePath": "geogebra/triangle.js", - "sourceFile": "geogebra/triangle.js", - "navigateBefore": false + "modulePath": "instagram/auth.js", + "sourceFile": "instagram/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "grok", - "name": "ask", - "description": "Send a message to Grok and get response", + "site": "notebooklm", + "name": "add-source", + "description": "Add a URL, text, or local file source to an existing NotebookLM notebook", "access": "write", - "domain": "grok.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", - "type": "string", + "name": "notebook", + "type": "str", "required": true, "positional": true, - "help": "Prompt to send to Grok" + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "timeout", - "type": "int", - "default": 120, + "name": "url", + "type": "str", "required": false, - "help": "Max seconds to wait for response (default: 120)" + "help": "Source URL to add (http/https). Pass exactly one of --url, --content, --file." }, { - "name": "new", + "name": "content", + "type": "str", + "required": false, + "help": "Raw text content to add as a Text source (max 10 MB)." + }, + { + "name": "file", + "type": "str", + "required": false, + "help": "Local file path to upload as a source (max 52428800 bytes; pdf / txt / md / html / docx / etc.). Uses Google Drive's 3-step resumable upload protocol." + }, + { + "name": "title", + "type": "str", + "required": false, + "help": "Title for the text source (default \"Text Source\"). Ignored for --url and --file." + }, + { + "name": "mime-type", + "type": "str", + "required": false, + "help": "Override the auto-detected MIME type when --file is given." + }, + { + "name": "execute", "type": "boolean", - "default": false, "required": false, - "help": "Start a new chat before sending (default: false)" + "help": "Actually add the remote source to the NotebookLM notebook" } ], "columns": [ - "response" + "notebook_id", + "source_id", + "kind", + "identifier", + "notebook_url" ], "type": "js", - "modulePath": "grok/ask.js", - "sourceFile": "grok/ask.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" + "modulePath": "notebooklm/add-source.js", + "sourceFile": "notebooklm/add-source.js", + "navigateBefore": false }, { - "site": "grok", - "name": "delete", - "description": "Delete a Grok conversation by ID. Grok takes effect immediately with no confirmation dialog — require --yes to actually delete.", + "site": "notebooklm", + "name": "create", + "description": "Create a new NotebookLM notebook with the given title", "access": "write", - "domain": "grok.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", - "type": "string", + "name": "title", + "type": "str", "required": true, "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" + "help": "Notebook title (1-200 chars)" }, { - "name": "yes", + "name": "emoji", + "type": "str", + "required": false, + "help": "Notebook emoji icon (default 📒)" + }, + { + "name": "execute", "type": "boolean", - "default": false, "required": false, - "help": "Actually delete (default is a dry-run preview)" + "help": "Actually create the remote NotebookLM notebook" } ], "columns": [ - "status", - "id" + "id", + "title", + "emoji", + "url" ], "type": "js", - "modulePath": "grok/delete.js", - "sourceFile": "grok/delete.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" + "modulePath": "notebooklm/create.js", + "sourceFile": "notebooklm/create.js", + "navigateBefore": false }, { - "site": "grok", - "name": "detail", - "description": "Open a Grok conversation by ID and read its messages", + "site": "notebooklm", + "name": "current", + "description": "Show metadata for the currently opened NotebookLM notebook tab", "access": "read", - "domain": "grok.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Session ID (UUID) or full https://grok.com/c/ URL" - }, - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" - } - ], + "args": [], "columns": [ - "Role", - "Text" + "id", + "title", + "url", + "source" ], "type": "js", - "modulePath": "grok/detail.js", - "sourceFile": "grok/detail.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/current.js", + "sourceFile": "notebooklm/current.js", + "navigateBefore": false }, { - "site": "grok", - "name": "export", - "description": "Export all visible Grok conversation history metadata", - "access": "read", - "example": "webcmd grok export -f yaml", - "domain": "grok.com", + "site": "notebooklm", + "name": "generate-audio", + "description": "Trigger an Audio Overview (Deep Dive podcast) generation for a NotebookLM notebook, using all of its sources", + "access": "write", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 0, - "required": false, - "help": "Max conversations to export; 0 means all loaded history" + "name": "notebook", + "type": "str", + "required": true, + "positional": true, + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "maxScrolls", - "type": "int", - "default": 80, + "name": "execute", + "type": "boolean", "required": false, - "help": "Max history-list scroll rounds when limit is 0 (max 500)" + "help": "Actually trigger remote NotebookLM audio generation" } ], "columns": [ - "index", - "id", - "title", - "date", - "url" + "notebook_id", + "audio_id", + "source_count", + "status", + "notebook_url" ], "type": "js", - "modulePath": "grok/export.js", - "sourceFile": "grok/export.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/generate-audio.js", + "sourceFile": "notebooklm/generate-audio.js", + "navigateBefore": false }, { - "site": "grok", - "name": "export-all", - "description": "Export Grok conversation history and each conversation transcript", - "access": "read", - "example": "webcmd grok export-all --limit 5 -f json", - "domain": "grok.com", + "site": "notebooklm", + "name": "generate-slides", + "description": "Trigger a Slide Deck (AI presentation) generation for a NotebookLM notebook, using all of its sources", + "access": "write", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 0, - "required": false, - "help": "Max conversations to export; 0 means all loaded history" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Skip this many conversations before exporting" - }, - { - "name": "manifestPath", - "type": "string", - "default": "", - "required": false, - "help": "Optional grok/export JSON manifest path; skips history dialog and visits listed /c pages directly" - }, - { - "name": "maxScrolls", - "type": "int", - "default": 80, - "required": false, - "help": "Max history-list scroll rounds when limit is 0 (max 500)" - }, - { - "name": "pageScrolls", - "type": "int", - "default": 30, - "required": false, - "help": "Max per-conversation scroll-to-bottom rounds (max 200)" + "name": "notebook", + "type": "str", + "required": true, + "positional": true, + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "pageTimeoutMs", - "type": "int", - "default": 30000, + "name": "length", + "type": "str", "required": false, - "help": "Max wait for each conversation page to show messages" + "help": "Slide deck length: 1=Short, 3=Default (default 3)" }, { - "name": "delayMinMs", - "type": "int", - "default": 0, + "name": "language", + "type": "str", "required": false, - "help": "Minimum polite delay after a conversation page loads" + "help": "Language code (default en)" }, { - "name": "delayMaxMs", - "type": "int", - "default": 5000, + "name": "execute", + "type": "boolean", "required": false, - "help": "Maximum polite delay after a conversation page loads" + "help": "Actually trigger remote NotebookLM slide deck generation" } ], "columns": [ - "index", + "notebook_id", + "slides_id", + "source_count", + "status", + "notebook_url" + ], + "type": "js", + "modulePath": "notebooklm/generate-slides.js", + "sourceFile": "notebooklm/generate-slides.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "get", + "aliases": [ + "metadata" + ], + "description": "Get rich metadata for the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ "id", "title", - "date", + "emoji", + "source_count", + "created_at", + "updated_at", "url", - "status", - "messageCount", - "error", - "messagesJson" + "source" ], "type": "js", - "modulePath": "grok/export-all.js", - "sourceFile": "grok/export-all.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/get.js", + "sourceFile": "notebooklm/get.js", + "navigateBefore": false }, { - "site": "grok", + "site": "notebooklm", "name": "history", - "description": "List recent Grok conversations from the sidebar (requires login)", + "description": "List NotebookLM conversation history threads in the current notebook", "access": "read", - "domain": "grok.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max conversations to show (default 20, max 100)" - } + "args": [], + "columns": [ + "thread_id", + "item_count", + "preview", + "source", + "notebook_id", + "url" ], + "type": "js", + "modulePath": "notebooklm/history.js", + "sourceFile": "notebooklm/history.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "list", + "description": "List NotebookLM notebooks via in-page batchexecute RPC in the current logged-in session", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "Index", - "Title", - "Url" + "title", + "id", + "is_owner", + "created_at", + "source", + "url" ], "type": "js", - "modulePath": "grok/history.js", - "sourceFile": "grok/history.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/list.js", + "sourceFile": "notebooklm/list.js", + "navigateBefore": false }, { - "site": "grok", - "name": "image", - "description": "Generate images on grok.com and return image URLs", + "site": "notebooklm", + "name": "login", + "description": "Open notebooklm login", "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "string", - "required": true, - "positional": true, - "help": "Image generation prompt" - }, - { - "name": "timeout", - "type": "int", - "default": 240, - "required": false, - "help": "Max seconds to wait for the image (default: 240)" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending (default: false)" - }, - { - "name": "count", - "type": "int", - "default": 1, - "required": false, - "help": "Minimum images to wait for before returning (default: 1)" - }, - { - "name": "out", - "type": "string", - "default": "", - "required": false, - "help": "Directory to save downloaded images (uses browser session to bypass auth)" - } - ], - "columns": [ - "url", - "width", - "height", - "path" - ], - "type": "js", - "modulePath": "grok/image.js", - "sourceFile": "grok/image.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "login", - "description": "Open grok login", - "access": "write", - "domain": "grok.com", + "domain": "google.com", "strategy": "cookie", "browser": true, "args": [], @@ -2835,754 +2858,598 @@ "status", "logged_in", "site", - "user_id", "name", + "authuser", "action", "verify_command" ], "type": "js", - "modulePath": "grok/auth.js", - "sourceFile": "grok/auth.js", + "modulePath": "notebooklm/auth.js", + "sourceFile": "notebooklm/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "grok", - "name": "new", - "description": "Start a new conversation in Grok", - "access": "write", - "domain": "grok.com", + "site": "notebooklm", + "name": "note-list", + "aliases": [ + "notes-list" + ], + "description": "List saved notes from the Studio panel of the current NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Status" + "title", + "created_at", + "source", + "url" ], "type": "js", - "modulePath": "grok/new.js", - "sourceFile": "grok/new.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/note-list.js", + "sourceFile": "notebooklm/note-list.js", + "navigateBefore": false }, { - "site": "grok", - "name": "pin", - "description": "Pin a Grok conversation by ID", - "access": "write", - "domain": "grok.com", + "site": "notebooklm", + "name": "notes-get", + "description": "Get one note from the current NotebookLM notebook by title from the visible note editor", + "access": "read", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", - "type": "string", + "name": "note", + "type": "str", "required": true, "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" + "help": "Note title or id from the current notebook" } ], "columns": [ - "status", - "id" + "title", + "content", + "source", + "url" ], "type": "js", - "modulePath": "grok/pin.js", - "sourceFile": "grok/pin.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" + "modulePath": "notebooklm/notes-get.js", + "sourceFile": "notebooklm/notes-get.js", + "navigateBefore": false }, { - "site": "grok", - "name": "read", - "description": "Read messages in the current Grok conversation", + "site": "notebooklm", + "name": "open", + "aliases": [ + "select" + ], + "description": "Open one NotebookLM notebook in the adapter session by id or URL", "access": "read", - "domain": "grok.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" + "name": "notebook", + "type": "str", + "required": true, + "positional": true, + "help": "Notebook id from list output, or a full NotebookLM notebook URL" } ], "columns": [ - "Role", - "Text" + "id", + "title", + "url", + "source" ], "type": "js", - "modulePath": "grok/read.js", - "sourceFile": "grok/read.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/open.js", + "sourceFile": "notebooklm/open.js", + "navigateBefore": false }, { - "site": "grok", - "name": "send", - "description": "Fire-and-forget: send a prompt to Grok without waiting for the reply", - "access": "write", - "domain": "grok.com", + "site": "notebooklm", + "name": "source-fulltext", + "description": "Get the extracted fulltext for one source in the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", + "name": "source", "type": "str", "required": true, "positional": true, - "help": "Prompt to send to Grok" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" + "help": "Source id or title from the current notebook" } ], "columns": [ - "Status", - "Prompt" + "title", + "kind", + "char_count", + "url", + "source" ], "type": "js", - "modulePath": "grok/send.js", - "sourceFile": "grok/send.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/source-fulltext.js", + "sourceFile": "notebooklm/source-fulltext.js", + "navigateBefore": false }, { - "site": "grok", - "name": "status", - "description": "Check Grok page availability, login state, current session and model", + "site": "notebooklm", + "name": "source-get", + "description": "Get one source from the currently opened NotebookLM notebook by id or title", "access": "read", - "domain": "grok.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "source", + "type": "str", + "required": true, + "positional": true, + "help": "Source id or title from the current notebook" + } + ], "columns": [ - "Status", - "Login", - "Model", - "SessionId", - "Url" + "title", + "id", + "type", + "size", + "created_at", + "updated_at", + "url", + "source" ], "type": "js", - "modulePath": "grok/status.js", - "sourceFile": "grok/status.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/source-get.js", + "sourceFile": "notebooklm/source-get.js", + "navigateBefore": false }, { - "site": "grok", - "name": "unpin", - "description": "Unpin a Grok conversation by ID", - "access": "write", - "domain": "grok.com", + "site": "notebooklm", + "name": "source-guide", + "description": "Get the guide summary and keywords for one source in the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", - "type": "string", + "name": "source", + "type": "str", "required": true, "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" + "help": "Source id or title from the current notebook" } ], "columns": [ - "status", - "id" + "source_id", + "notebook_id", + "title", + "type", + "summary", + "keywords", + "source" ], "type": "js", - "modulePath": "grok/pin.js", - "sourceFile": "grok/pin.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" + "modulePath": "notebooklm/source-guide.js", + "sourceFile": "notebooklm/source-guide.js", + "navigateBefore": false }, { - "site": "grok", - "name": "whoami", - "description": "Show the current logged-in grok account", - "access": "read", - "domain": "grok.com", + "site": "notebooklm", + "name": "source-list", + "description": "List sources for the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "user_id", - "name" + "title", + "id", + "type", + "size", + "created_at", + "updated_at", + "url", + "source" ], "type": "js", - "modulePath": "grok/auth.js", - "sourceFile": "grok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "notebooklm/source-list.js", + "sourceFile": "notebooklm/source-list.js", + "navigateBefore": false }, { - "site": "instagram", - "name": "collection-create", - "description": "Create a new Instagram saved-posts collection (folder)", - "access": "write", - "domain": "www.instagram.com", + "site": "notebooklm", + "name": "status", + "description": "Check NotebookLM page availability and login state in the current Chrome session", + "access": "read", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Name of the collection to create" - } - ], + "args": [], "columns": [ "status", - "collectionId", - "collectionName", - "mediaCount" + "login", + "page", + "url", + "title", + "notebooks" ], "type": "js", - "modulePath": "instagram/collection-create.js", - "sourceFile": "instagram/collection-create.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "notebooklm/status.js", + "sourceFile": "notebooklm/status.js", + "navigateBefore": false }, { - "site": "instagram", - "name": "collection-delete", - "description": "Delete an Instagram saved-posts collection (folder) by name or id", - "access": "write", - "domain": "www.instagram.com", + "site": "notebooklm", + "name": "summary", + "description": "Get the summary block from the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "target", - "type": "str", - "required": true, - "positional": true, - "help": "Collection name (case-insensitive) or numeric collection_id" - } + "args": [], + "columns": [ + "title", + "summary", + "source", + "url" ], + "type": "js", + "modulePath": "notebooklm/summary.js", + "sourceFile": "notebooklm/summary.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "whoami", + "description": "Show the current logged-in notebooklm account", + "access": "read", + "domain": "google.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "status", - "collectionId", - "collectionName" + "logged_in", + "site", + "name", + "authuser" ], "type": "js", - "modulePath": "instagram/collection-delete.js", - "sourceFile": "instagram/collection-delete.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "notebooklm/auth.js", + "sourceFile": "notebooklm/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "instagram", - "name": "comment", - "description": "Comment on an Instagram post", + "site": "notebooklm", + "name": "write-note", + "description": "Create a Studio note in an existing NotebookLM notebook with the given title and Markdown content", "access": "write", - "domain": "www.instagram.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "notebook", "type": "str", "required": true, "positional": true, - "help": "Username of the post author" + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "text", + "name": "title", "type": "str", "required": true, - "positional": true, - "help": "Comment text" + "help": "Note title (1-200 chars)" }, { - "name": "index", - "type": "int", - "default": 1, + "name": "content", + "type": "str", + "required": true, + "help": "Note body as Markdown" + }, + { + "name": "execute", + "type": "boolean", "required": false, - "help": "Post index (1 = most recent)" + "help": "Actually create the remote NotebookLM note" } ], "columns": [ - "status", - "user", - "text" + "notebook_id", + "note_id", + "title", + "notebook_url" ], "type": "js", - "modulePath": "instagram/comment.js", - "sourceFile": "instagram/comment.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "notebooklm/write-note.js", + "sourceFile": "notebooklm/write-note.js", + "navigateBefore": false }, { - "site": "instagram", - "name": "download", - "description": "Download images and videos from Instagram posts and reels", + "site": "qoder", + "name": "account", + "description": "Click the account button (username) in the Qoder sidebar and return the visible account dropdown items.", "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram post / reel / tv URL" - }, - { - "name": "path", + "name": "username", "type": "str", - "default": "~/Downloads/Instagram", "required": false, - "help": "Download directory" + "help": "Username text shown in the sidebar (default: tries common short labels)" } ], + "columns": [ + "Field", + "Value" + ], "type": "js", - "modulePath": "instagram/download.js", - "sourceFile": "instagram/download.js", - "navigateBefore": false + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "instagram", - "name": "explore", - "description": "Instagram explore/discover trending posts", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", + "site": "qoder", + "name": "add-workspace", + "description": "Click \"Add Workspace\" — opens the folder picker. Note: this opens a system file-picker dialog that Qoder controls; the actual folder selection must be done in the UI by the user.", + "access": "write", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts" - } - ], + "args": [], "columns": [ - "rank", - "user", - "caption", - "likes", - "comments", - "type" - ], - "tags": [ - "search" + "Status" ], "type": "js", - "modulePath": "instagram/explore.js", - "sourceFile": "instagram/explore.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "instagram", - "name": "follow", - "description": "Follow an Instagram user", + "site": "qoder", + "name": "ask", + "description": "Send a prompt to Qoder and wait up to --timeout seconds for the reply (best-effort: polls for the chat turn count to grow + stabilize).", "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "username", + "name": "text", "type": "str", "required": true, "positional": true, - "help": "Instagram username to follow" + "help": "Prompt text" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait" } ], "columns": [ - "status", - "username" + "Role", + "Text", + "WaitedSeconds" ], "type": "js", - "modulePath": "instagram/follow.js", - "sourceFile": "instagram/follow.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "qoder/quest.js", + "sourceFile": "qoder/quest.js", + "navigateBefore": true }, { - "site": "instagram", - "name": "followers", - "description": "List followers of an Instagram user", + "site": "qoder", + "name": "credits", + "description": "Click \"Credits Usage\" and return the credits-usage display text.", "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of followers" - } - ], + "args": [], "columns": [ - "rank", - "username", - "name", - "verified", - "private" + "Field", + "Value" ], "type": "js", - "modulePath": "instagram/followers.js", - "sourceFile": "instagram/followers.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "instagram", - "name": "following", - "description": "List accounts an Instagram user is following", + "site": "qoder", + "name": "history", + "description": "List Quests visible in the Qoder sidebar. Returns title + visible metadata.", "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 50, "required": false, - "help": "Number of accounts" + "help": "" } ], "columns": [ - "rank", - "username", - "name", - "verified", - "private" + "Index", + "Title" ], "type": "js", - "modulePath": "instagram/following.js", - "sourceFile": "instagram/following.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "qoder/history.js", + "sourceFile": "qoder/history.js", + "navigateBefore": true }, { - "site": "instagram", - "name": "like", - "description": "Like an Instagram post", + "site": "qoder", + "name": "knowledge", + "description": "Open the Knowledge view (Qoder's personal/team knowledge base).", "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], + "args": [], "columns": [ - "status", - "user", - "post" + "Status" ], "type": "js", - "modulePath": "instagram/like.js", - "sourceFile": "instagram/like.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "instagram", - "name": "login", - "description": "Open instagram login", + "site": "qoder", + "name": "marketplace", + "description": "Open the Qoder Marketplace.", "access": "write", - "domain": "instagram.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [], "columns": [ - "status", - "logged_in", - "site", - "user_id", - "username", - "full_name", - "action", - "verify_command" + "Status" ], "type": "js", - "modulePath": "instagram/auth.js", - "sourceFile": "instagram/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "instagram", - "name": "note", - "description": "Publish a text Instagram note", - "access": "write", - "domain": "www.instagram.com", + "site": "qoder", + "name": "more-actions", + "description": "Click the \"More Actions\" button and list its menu items.", + "access": "read", + "domain": "localhost", "strategy": "ui", "browser": true, - "args": [ - { - "name": "content", - "type": "str", - "required": true, - "positional": true, - "help": "Note text (max 60 characters)" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds for the overall command (default: 120)" - } - ], + "args": [], "columns": [ - "status", - "detail", - "noteId" + "Index", + "Item" ], "type": "js", - "modulePath": "instagram/note.js", - "sourceFile": "instagram/note.js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", "navigateBefore": true }, { - "site": "instagram", - "name": "post", - "description": "Post an Instagram feed image or mixed-media carousel", + "site": "qoder", + "name": "new", + "description": "Start a new Qoder Quest (conversation). Clicks the \"New Quest\" button in the sidebar (or its ⌘N variant).", "access": "write", - "domain": "www.instagram.com", + "domain": "localhost", "strategy": "ui", "browser": true, - "args": [ - { - "name": "media", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Comma-separated media paths (images/videos, up to 10)", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "image/jpeg", - "image/png", - "image/webp", - "video/mp4" - ], - "maxBytes": 262144000 - } - }, - { - "name": "content", - "type": "str", - "required": false, - "positional": true, - "help": "Caption text" - }, - { - "name": "timeout", - "type": "int", - "default": 300, - "required": false, - "help": "Max seconds for the overall command (default: 300)" - } - ], + "args": [], "columns": [ - "status", - "detail", - "url" + "Status" ], "type": "js", - "modulePath": "instagram/post.js", - "sourceFile": "instagram/post.js", + "modulePath": "qoder/quest.js", + "sourceFile": "qoder/quest.js", "navigateBefore": true }, { - "site": "instagram", - "name": "profile", - "description": "Get Instagram user profile info", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", + "site": "qoder", + "name": "open-editor", + "description": "Click \"Open Editor\" — opens the current draft in a full editor pane.", + "access": "write", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" - } - ], + "args": [], "columns": [ - "username", - "name", - "followers", - "following", - "posts", - "verified", - "bio" + "Status" ], "type": "js", - "modulePath": "instagram/profile.js", - "sourceFile": "instagram/profile.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "qoder/composer.js", + "sourceFile": "qoder/composer.js", + "navigateBefore": true }, { - "site": "instagram", - "name": "reel", - "description": "Post an Instagram reel video", + "site": "qoder", + "name": "open-panel", + "description": "Open / close the Qoder bottom panel (Output / Terminal / Debug Console). ⌥⌘B equivalent.", "access": "write", - "domain": "www.instagram.com", + "domain": "localhost", "strategy": "ui", "browser": true, - "args": [ - { - "name": "video", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Path to a single .mp4 video file", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "video/mp4" - ], - "maxBytes": 262144000 - } - }, - { - "name": "content", - "type": "str", - "required": false, - "positional": true, - "help": "Caption text" - }, - { - "name": "timeout", - "type": "int", - "default": 600, - "required": false, - "help": "Max seconds for the overall command (default: 600)" - } - ], + "args": [], "columns": [ - "status", - "detail", - "url" + "Status" ], "type": "js", - "modulePath": "instagram/reel.js", - "sourceFile": "instagram/reel.js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", "navigateBefore": true }, { - "site": "instagram", - "name": "save", - "description": "Save (bookmark) an Instagram post", + "site": "qoder", + "name": "prompt-enhance", + "description": "Click \"Prompt Enhance\" — Qoder rewrites the current composer draft for better LLM consumption.", "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], + "args": [], "columns": [ - "status", - "user", - "post" + "Status" ], "type": "js", - "modulePath": "instagram/save.js", - "sourceFile": "instagram/save.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "qoder/composer.js", + "sourceFile": "qoder/composer.js", + "navigateBefore": true }, { - "site": "instagram", - "name": "saved", - "description": "Get your saved Instagram posts (optionally from a specific collection)", + "site": "qoder", + "name": "read", + "description": "Read messages in the current Qoder Quest. Returns role + text for each visible turn.", "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 20, - "required": false, - "help": "Number of saved posts" - }, - { - "name": "collection", - "type": "str", + "default": 30, "required": false, - "help": "Collection name (case-insensitive). Omit for the default \"All posts\" feed." + "help": "" } ], "columns": [ - "index", - "user", - "caption", - "likes", - "comments", - "type" + "Index", + "Role", + "Text" ], "type": "js", - "modulePath": "instagram/saved.js", - "sourceFile": "instagram/saved.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "qoder/read.js", + "sourceFile": "qoder/read.js", + "navigateBefore": true }, { - "site": "instagram", + "site": "qoder", "name": "search", - "description": "Search Instagram users", + "description": "Open Qoder Search palette (⌘P), type a query, return matched options.", "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { @@ -3590,1751 +3457,1648 @@ "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "Search text" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results" + "help": "" } ], "columns": [ - "rank", - "username", - "name", - "verified", - "private", - "url" + "Index", + "Item" ], "tags": [ "search" ], "type": "js", - "modulePath": "instagram/search.js", - "sourceFile": "instagram/search.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "instagram", - "name": "story", - "description": "Post a single Instagram story image or video", + "site": "qoder", + "name": "send", + "description": "Type text into the Qoder composer and click \"Send message\" (fire-and-forget).", "access": "write", - "domain": "www.instagram.com", + "domain": "localhost", "strategy": "ui", "browser": true, "args": [ { - "name": "media", + "name": "text", "type": "str", - "required": false, - "valueRequired": true, - "help": "Path to a single story image or video file" - }, - { - "name": "timeout", - "type": "int", - "default": 300, - "required": false, - "help": "Max seconds for the overall command (default: 300)" + "required": true, + "positional": true, + "help": "Text to send" } ], "columns": [ - "status", - "detail", - "url" + "Status", + "Length" ], "type": "js", - "modulePath": "instagram/story.js", - "sourceFile": "instagram/story.js", + "modulePath": "qoder/quest.js", + "sourceFile": "qoder/quest.js", "navigateBefore": true }, { - "site": "instagram", - "name": "unfollow", - "description": "Unfollow an Instagram user", + "site": "qoder", + "name": "settings", + "description": "Click the Settings button in the Qoder sidebar.", "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username to unfollow" - } - ], + "args": [], "columns": [ - "status", - "username" + "Status" ], "type": "js", - "modulePath": "instagram/unfollow.js", - "sourceFile": "instagram/unfollow.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "instagram", - "name": "unlike", - "description": "Unlike an Instagram post", + "site": "qoder", + "name": "sidebar-toggle", + "description": "Collapse / Expand the Qoder Quest List sidebar (⌘B).", "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], + "args": [], "columns": [ - "status", - "user", - "post" + "Status" ], "type": "js", - "modulePath": "instagram/unlike.js", - "sourceFile": "instagram/unlike.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true }, { - "site": "instagram", - "name": "unsave", - "description": "Unsave (remove bookmark) an Instagram post", + "site": "qoder", + "name": "status", + "description": "Check Qoder CDP connection and report the current renderer URL + title.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "qoder/status.js", + "sourceFile": "qoder/status.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "view-all", + "description": "Click \"View all\" to show all Quests.", "access": "write", - "domain": "www.instagram.com", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "qoder/ui.js", + "sourceFile": "qoder/ui.js", + "navigateBefore": true + }, + { + "site": "reddit", + "name": "comment", + "description": "Post a comment on a Reddit post", + "access": "write", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", - "type": "str", + "name": "post-id", + "type": "string", "required": true, "positional": true, - "help": "Username of the post author" + "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" }, { - "name": "index", + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "Comment text" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "reddit/comment.js", + "sourceFile": "reddit/comment.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "frontpage", + "description": "Reddit Frontpage / r/all", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", "type": "int", - "default": 1, + "default": 15, "required": false, - "help": "Post index (1 = most recent)" + "help": "" } ], "columns": [ - "status", - "user", - "post" + "title", + "subreddit", + "author", + "upvotes", + "comments", + "url", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "instagram/unsave.js", - "sourceFile": "instagram/unsave.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "reddit/frontpage.js", + "sourceFile": "reddit/frontpage.js", + "navigateBefore": "https://reddit.com" }, { - "site": "instagram", - "name": "user", - "description": "Get recent posts from an Instagram user", + "site": "reddit", + "name": "home", + "description": "Reddit personalized home feed (Best, requires login)", "access": "read", - "domain": "www.instagram.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Number of posts (1–100)" + } + ], + "columns": [ + "rank", + "title", + "subreddit", + "score", + "comments", + "postId", + "author", + "url", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" + ], + "type": "js", + "modulePath": "reddit/home.js", + "sourceFile": "reddit/home.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "hot", + "description": "Reddit hot posts", + "access": "read", + "domain": "www.reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "subreddit", "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" + "default": "", + "required": false, + "help": "Subreddit name (e.g. programming). Empty for frontpage" }, { "name": "limit", "type": "int", - "default": 12, + "default": 20, "required": false, "help": "Number of posts" } ], "columns": [ - "index", - "caption", - "likes", + "rank", + "title", + "subreddit", + "score", "comments", - "type", - "date" + "postId", + "author", + "url", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "instagram/user.js", - "sourceFile": "instagram/user.js", - "navigateBefore": "https://www.instagram.com" + "modulePath": "reddit/hot.js", + "sourceFile": "reddit/hot.js", + "navigateBefore": "https://www.reddit.com" }, { - "site": "instagram", - "name": "whoami", - "description": "Show the current logged-in instagram account", - "access": "read", - "domain": "instagram.com", + "site": "reddit", + "name": "login", + "description": "Open reddit login", + "access": "write", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ + "status", "logged_in", "site", - "user_id", "username", - "full_name" + "id", + "action", + "verify_command" ], "type": "js", - "modulePath": "instagram/auth.js", - "sourceFile": "instagram/auth.js", + "modulePath": "reddit/auth.js", + "sourceFile": "reddit/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "mercury", - "name": "check-login", - "description": "Open Mercury reimbursements and report whether the active browser profile is logged in", + "site": "reddit", + "name": "popular", + "description": "Reddit Popular posts (/r/popular)", "access": "read", - "example": "webcmd --profile mercury check-login -f json", - "domain": "app.mercury.com", - "strategy": "ui", + "domain": "reddit.com", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "" + } + ], "columns": [ - "status", - "loggedIn", + "rank", + "id", + "title", + "subreddit", + "score", + "comments", + "author", "url", - "hasSubmitExpense", - "hasReimbursements", - "title" + "created_utc", + "selftext", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "mercury/check-login.js", - "sourceFile": "mercury/check-login.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "reddit/popular.js", + "sourceFile": "reddit/popular.js", + "navigateBefore": "https://reddit.com" }, { - "site": "mercury", - "name": "reimbursement-draft", - "description": "Create a Mercury reimbursement draft from a local receipt, correct OCR fields, and stop at Review", - "access": "write", - "example": "webcmd --profile mercury reimbursement-draft --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", - "domain": "app.mercury.com", - "strategy": "ui", + "site": "reddit", + "name": "read", + "description": "Read a Reddit post and its comments", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "receipt", + "name": "post-id", "type": "str", "required": true, - "help": "Local receipt/proof file path", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp", - "application/pdf" - ], - "maxBytes": 26214400 - } + "positional": true, + "help": "Post ID (e.g. 1abc123) or full URL" }, { - "name": "amount", + "name": "sort", "type": "str", - "required": true, - "help": "Original-currency amount, e.g. 140.00" - }, - { - "name": "currency", - "type": "str", - "default": "CNY", + "default": "best", "required": false, - "help": "Original currency code" + "help": "Comment sort: best, top, new, controversial, old, qa" }, { - "name": "date", - "type": "str", - "required": true, - "help": "Expense date as YYYY-MM-DD" + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Number of top-level comments" }, { - "name": "merchant", - "type": "str", - "required": true, - "help": "Merchant shown on the reimbursement" + "name": "depth", + "type": "int", + "default": 2, + "required": false, + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" }, { - "name": "category", - "type": "str", - "default": "Marketing & Advertising", + "name": "replies", + "type": "int", + "default": 5, "required": false, - "help": "Mercury expense category" + "help": "Max replies shown per comment at each level (sorted by score)" }, { - "name": "notes", - "type": "str", - "required": true, - "help": "Business purpose / reimbursement notes" + "name": "max-length", + "type": "int", + "default": 2000, + "required": false, + "help": "Max characters per comment body (min 100)" }, { - "name": "ocr-wait-seconds", - "type": "str", - "default": "8", + "name": "expand-more", + "type": "bool", + "default": false, "required": false, - "help": "Seconds to wait after receipt upload before correcting OCR-overwritten fields" + "help": "Follow Reddit \"more comments\" stubs by calling /api/morechildren.json" }, { - "name": "close-after-review", - "type": "boolean", - "default": false, + "name": "expand-rounds", + "type": "int", + "default": 2, "required": false, - "help": "Close the Review dialog after verification; final Submit is still never clicked" + "help": "Max expansion passes when --expand-more is on (1–5; each round can fan out new \"more\" stubs)" } ], "columns": [ - "status", - "url", - "receipt", - "uploaded", - "fieldsTouched", - "reviewReady", - "submitBlocked", - "warnings" + "type", + "author", + "score", + "text", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "mercury/reimbursement-draft.js", - "sourceFile": "mercury/reimbursement-draft.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "reddit/read.js", + "sourceFile": "reddit/read.js", + "navigateBefore": "https://reddit.com" }, { - "site": "mercury", - "name": "reimbursement-plan", - "description": "Validate Mercury reimbursement inputs and print the draft plan without opening a browser", - "access": "read", - "example": "webcmd mercury reimbursement-plan --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", - "strategy": "local", - "browser": false, + "site": "reddit", + "name": "reply", + "description": "Reply to a Reddit comment", + "access": "write", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "receipt", - "type": "str", - "required": true, - "help": "Local receipt/proof file path" - }, - { - "name": "amount", - "type": "str", - "required": true, - "help": "Original-currency amount, e.g. 140.00" - }, - { - "name": "currency", - "type": "str", - "default": "CNY", - "required": false, - "help": "Original currency code" - }, - { - "name": "date", - "type": "str", - "required": true, - "help": "Expense date as YYYY-MM-DD" - }, - { - "name": "merchant", - "type": "str", + "name": "comment-id", + "type": "string", "required": true, - "help": "Merchant shown on the reimbursement" - }, - { - "name": "category", - "type": "str", - "default": "Marketing & Advertising", - "required": false, - "help": "Mercury expense category" + "positional": true, + "help": "Comment ID (e.g. okf3s7u) or fullname (t1_xxx)" }, { - "name": "notes", - "type": "str", + "name": "text", + "type": "string", "required": true, - "help": "Business purpose / reimbursement notes" - }, - { - "name": "ocr-wait-seconds", - "type": "str", - "default": "8", - "required": false, - "help": "Seconds the draft command waits after receipt upload before correcting OCR fields" - }, - { - "name": "close-after-review", - "type": "boolean", - "default": false, - "required": false, - "help": "For draft command: close the Review dialog after verification" + "positional": true, + "help": "Reply text" } ], "columns": [ "status", - "receipt", - "amount", - "currency", - "date", - "merchant", - "category", - "notes", - "safety" + "message" ], "type": "js", - "modulePath": "mercury/reimbursement-plan.js", - "sourceFile": "mercury/reimbursement-plan.js" + "modulePath": "reddit/reply.js", + "sourceFile": "reddit/reply.js", + "navigateBefore": "https://reddit.com" }, { - "site": "notebooklm", - "name": "add-source", - "description": "Add a URL, text, or local file source to an existing NotebookLM notebook", + "site": "reddit", + "name": "save", + "description": "Save or unsave a Reddit post", "access": "write", - "domain": "notebooklm.google.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "notebook", - "type": "str", + "name": "post-id", + "type": "string", "required": true, "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Source URL to add (http/https). Pass exactly one of --url, --content, --file." - }, - { - "name": "content", - "type": "str", - "required": false, - "help": "Raw text content to add as a Text source (max 10 MB)." - }, - { - "name": "file", - "type": "str", - "required": false, - "help": "Local file path to upload as a source (max 52428800 bytes; pdf / txt / md / html / docx / etc.). Uses Google Drive's 3-step resumable upload protocol." - }, - { - "name": "title", - "type": "str", - "required": false, - "help": "Title for the text source (default \"Text Source\"). Ignored for --url and --file." - }, - { - "name": "mime-type", - "type": "str", - "required": false, - "help": "Override the auto-detected MIME type when --file is given." + "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" }, { - "name": "execute", + "name": "undo", "type": "boolean", + "default": false, "required": false, - "help": "Actually add the remote source to the NotebookLM notebook" + "help": "Unsave instead of save" } ], "columns": [ - "notebook_id", - "source_id", - "kind", - "identifier", - "notebook_url" + "status", + "message" ], "type": "js", - "modulePath": "notebooklm/add-source.js", - "sourceFile": "notebooklm/add-source.js", - "navigateBefore": false + "modulePath": "reddit/save.js", + "sourceFile": "reddit/save.js", + "navigateBefore": "https://reddit.com" }, { - "site": "notebooklm", - "name": "create", - "description": "Create a new NotebookLM notebook with the given title", - "access": "write", - "domain": "notebooklm.google.com", + "site": "reddit", + "name": "saved", + "description": "Browse your saved Reddit posts", + "access": "read", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "title", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook title (1-200 chars)" - }, - { - "name": "emoji", - "type": "str", - "required": false, - "help": "Notebook emoji icon (default 📒)" - }, - { - "name": "execute", - "type": "boolean", + "name": "limit", + "type": "int", + "default": 15, "required": false, - "help": "Actually create the remote NotebookLM notebook" + "help": "" } ], "columns": [ - "id", "title", - "emoji", + "subreddit", + "score", + "comments", "url" ], "type": "js", - "modulePath": "notebooklm/create.js", - "sourceFile": "notebooklm/create.js", - "navigateBefore": false + "modulePath": "reddit/saved.js", + "sourceFile": "reddit/saved.js", + "navigateBefore": "https://reddit.com" }, { - "site": "notebooklm", - "name": "current", - "description": "Show metadata for the currently opened NotebookLM notebook tab", + "site": "reddit", + "name": "search", + "description": "Search Reddit Posts", "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", - "title", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/current.js", - "sourceFile": "notebooklm/current.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "generate-audio", - "description": "Trigger an Audio Overview (Deep Dive podcast) generation for a NotebookLM notebook, using all of its sources", - "access": "write", - "domain": "notebooklm.google.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "notebook", - "type": "str", + "name": "query", + "type": "string", "required": true, "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" + "help": "Reddit search query" }, { - "name": "execute", - "type": "boolean", + "name": "subreddit", + "type": "string", + "default": "", "required": false, - "help": "Actually trigger remote NotebookLM audio generation" + "help": "Search within a specific subreddit" + }, + { + "name": "sort", + "type": "string", + "default": "relevance", + "required": false, + "help": "Sort order: relevance, hot, top, new, comments" + }, + { + "name": "time", + "type": "string", + "default": "all", + "required": false, + "help": "Time filter: hour, day, week, month, year, all" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "" } ], "columns": [ - "notebook_id", - "audio_id", - "source_count", - "status", - "notebook_url" + "id", + "title", + "subreddit", + "author", + "score", + "comments", + "url", + "created_utc", + "selftext", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "notebooklm/generate-audio.js", - "sourceFile": "notebooklm/generate-audio.js", - "navigateBefore": false + "modulePath": "reddit/search.js", + "sourceFile": "reddit/search.js", + "navigateBefore": "https://reddit.com" }, { - "site": "notebooklm", - "name": "generate-slides", - "description": "Trigger a Slide Deck (AI presentation) generation for a NotebookLM notebook, using all of its sources", - "access": "write", - "domain": "notebooklm.google.com", + "site": "reddit", + "name": "subreddit", + "description": "Get posts from a specific Subreddit", + "access": "read", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "notebook", - "type": "str", + "name": "name", + "type": "string", "required": true, "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" + "help": "Subreddit name (no `r/` prefix; e.g. `python`)" }, { - "name": "length", - "type": "str", + "name": "sort", + "type": "string", + "default": "hot", "required": false, - "help": "Slide deck length: 1=Short, 3=Default (default 3)" + "help": "Sorting method: hot, new, top, rising, controversial" }, { - "name": "language", - "type": "str", + "name": "time", + "type": "string", + "default": "all", "required": false, - "help": "Language code (default en)" + "help": "Time filter for top/controversial: hour, day, week, month, year, all" }, { - "name": "execute", - "type": "boolean", + "name": "limit", + "type": "int", + "default": 15, "required": false, - "help": "Actually trigger remote NotebookLM slide deck generation" + "help": "" } ], "columns": [ - "notebook_id", - "slides_id", - "source_count", - "status", - "notebook_url" + "id", + "title", + "subreddit", + "author", + "upvotes", + "comments", + "url", + "created_utc", + "selftext", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "notebooklm/generate-slides.js", - "sourceFile": "notebooklm/generate-slides.js", - "navigateBefore": false + "modulePath": "reddit/subreddit.js", + "sourceFile": "reddit/subreddit.js", + "navigateBefore": "https://reddit.com" }, { - "site": "notebooklm", - "name": "get", - "aliases": [ - "metadata" - ], - "description": "Get rich metadata for the currently opened NotebookLM notebook", + "site": "reddit", + "name": "subreddit-info", + "description": "Show metadata for a Reddit subreddit (subscribers, description, created date, NSFW)", "access": "read", - "domain": "notebooklm.google.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "name", + "type": "string", + "required": true, + "positional": true, + "help": "Subreddit name (no `r/` prefix needed)" + } + ], "columns": [ - "id", - "title", - "emoji", - "source_count", - "created_at", - "updated_at", - "url", - "source" + "field", + "value" ], "type": "js", - "modulePath": "notebooklm/get.js", - "sourceFile": "notebooklm/get.js", - "navigateBefore": false + "modulePath": "reddit/subreddit-info.js", + "sourceFile": "reddit/subreddit-info.js", + "navigateBefore": "https://reddit.com" }, { - "site": "notebooklm", - "name": "history", - "description": "List NotebookLM conversation history threads in the current notebook", - "access": "read", - "domain": "notebooklm.google.com", + "site": "reddit", + "name": "subscribe", + "description": "Subscribe or unsubscribe to a subreddit", + "access": "write", + "domain": "reddit.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "subreddit", + "type": "string", + "required": true, + "positional": true, + "help": "Subreddit name (e.g. python)" + }, + { + "name": "undo", + "type": "boolean", + "default": false, + "required": false, + "help": "Unsubscribe instead of subscribe" + } + ], "columns": [ - "thread_id", - "item_count", - "preview", - "source", - "notebook_id", - "url" + "status", + "message" ], "type": "js", - "modulePath": "notebooklm/history.js", - "sourceFile": "notebooklm/history.js", - "navigateBefore": false + "modulePath": "reddit/subscribe.js", + "sourceFile": "reddit/subscribe.js", + "navigateBefore": "https://reddit.com" }, { - "site": "notebooklm", - "name": "list", - "description": "List NotebookLM notebooks via in-page batchexecute RPC in the current logged-in session", + "site": "reddit", + "name": "subscribed", + "description": "List subreddits you are subscribed to", "access": "read", - "domain": "notebooklm.google.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Max subreddits to return (1-1000, auto-paginates)" + } + ], "columns": [ - "title", "id", - "is_owner", - "created_at", - "source", + "subreddit", + "title", + "subscribers", + "description", "url" ], "type": "js", - "modulePath": "notebooklm/list.js", - "sourceFile": "notebooklm/list.js", - "navigateBefore": false + "modulePath": "reddit/subscribed.js", + "sourceFile": "reddit/subscribed.js", + "navigateBefore": "https://reddit.com" }, { - "site": "notebooklm", - "name": "login", - "description": "Open notebooklm login", + "site": "reddit", + "name": "upvote", + "description": "Upvote or downvote a Reddit post", "access": "write", - "domain": "google.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "post-id", + "type": "string", + "required": true, + "positional": true, + "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" + }, + { + "name": "direction", + "type": "string", + "default": "up", + "required": false, + "help": "Vote direction: up, down, none" + } + ], "columns": [ "status", - "logged_in", - "site", - "name", - "authuser", - "action", - "verify_command" + "message" ], "type": "js", - "modulePath": "notebooklm/auth.js", - "sourceFile": "notebooklm/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "reddit/upvote.js", + "sourceFile": "reddit/upvote.js", + "navigateBefore": "https://reddit.com" }, { - "site": "notebooklm", - "name": "note-list", - "aliases": [ - "notes-list" - ], - "description": "List saved notes from the Studio panel of the current NotebookLM notebook", + "site": "reddit", + "name": "upvoted", + "description": "Browse your upvoted Reddit posts", "access": "read", - "domain": "notebooklm.google.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "" + } + ], "columns": [ "title", - "created_at", - "source", + "subreddit", + "score", + "comments", "url" ], "type": "js", - "modulePath": "notebooklm/note-list.js", - "sourceFile": "notebooklm/note-list.js", - "navigateBefore": false + "modulePath": "reddit/upvoted.js", + "sourceFile": "reddit/upvoted.js", + "navigateBefore": "https://reddit.com" }, { - "site": "notebooklm", - "name": "notes-get", - "description": "Get one note from the current NotebookLM notebook by title from the visible note editor", + "site": "reddit", + "name": "user", + "description": "View a Reddit user profile", "access": "read", - "domain": "notebooklm.google.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "note", - "type": "str", + "name": "username", + "type": "string", "required": true, "positional": true, - "help": "Note title or id from the current notebook" + "help": "Reddit username (no `u/` prefix needed)" } ], "columns": [ - "title", - "content", - "source", - "url" + "field", + "value" ], "type": "js", - "modulePath": "notebooklm/notes-get.js", - "sourceFile": "notebooklm/notes-get.js", - "navigateBefore": false + "modulePath": "reddit/user.js", + "sourceFile": "reddit/user.js", + "navigateBefore": "https://reddit.com" }, { - "site": "notebooklm", - "name": "open", - "aliases": [ - "select" - ], - "description": "Open one NotebookLM notebook in the adapter session by id or URL", + "site": "reddit", + "name": "user-comments", + "description": "View a Reddit user's comment history", "access": "read", - "domain": "notebooklm.google.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "notebook", - "type": "str", + "name": "username", + "type": "string", "required": true, "positional": true, - "help": "Notebook id from list output, or a full NotebookLM notebook URL" + "help": "Reddit username (no `u/` prefix needed)" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "" } ], "columns": [ - "id", - "title", - "url", - "source" + "subreddit", + "score", + "body", + "url" ], "type": "js", - "modulePath": "notebooklm/open.js", - "sourceFile": "notebooklm/open.js", - "navigateBefore": false + "modulePath": "reddit/user-comments.js", + "sourceFile": "reddit/user-comments.js", + "navigateBefore": "https://reddit.com" }, { - "site": "notebooklm", - "name": "source-fulltext", - "description": "Get the extracted fulltext for one source in the currently opened NotebookLM notebook", + "site": "reddit", + "name": "user-posts", + "description": "View a Reddit user's submitted posts", "access": "read", - "domain": "notebooklm.google.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "source", - "type": "str", + "name": "username", + "type": "string", "required": true, "positional": true, - "help": "Source id or title from the current notebook" + "help": "Reddit username (no `u/` prefix needed)" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "" } ], "columns": [ "title", - "kind", - "char_count", - "url", - "source" + "subreddit", + "score", + "comments", + "url" ], "type": "js", - "modulePath": "notebooklm/source-fulltext.js", - "sourceFile": "notebooklm/source-fulltext.js", - "navigateBefore": false + "modulePath": "reddit/user-posts.js", + "sourceFile": "reddit/user-posts.js", + "navigateBefore": "https://reddit.com" }, { - "site": "notebooklm", - "name": "source-get", - "description": "Get one source from the currently opened NotebookLM notebook by id or title", + "site": "reddit", + "name": "whoami", + "description": "Show the currently logged-in Reddit user", "access": "read", - "domain": "notebooklm.google.com", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "reddit/whoami.js", + "sourceFile": "reddit/whoami.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "slock", + "name": "attachment-download", + "description": "Download an attachment to a local file. Resolves a signed CDN URL in the page, then fetches bytes node-side (no CORS).", + "access": "read", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "source", + "name": "attachmentId", "type": "str", "required": true, "positional": true, - "help": "Source id or title from the current notebook" + "help": "Attachment UUID" + }, + { + "name": "out", + "type": "str", + "required": false, + "help": "Local path to write to. Defaults to ./.bin" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server slug" } ], "columns": [ - "title", - "id", - "type", - "size", - "created_at", - "updated_at", - "url", - "source" + "attachmentId", + "out", + "sizeBytes" ], "type": "js", - "modulePath": "notebooklm/source-get.js", - "sourceFile": "notebooklm/source-get.js", - "navigateBefore": false + "modulePath": "slock/attachment-download.js", + "sourceFile": "slock/attachment-download.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "notebooklm", - "name": "source-guide", - "description": "Get the guide summary and keywords for one source in the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", + "site": "slock", + "name": "attachment-upload", + "description": "Upload a local file to Slock attachments. Prints the attachmentId for use with `message-send --attach`.", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "source", + "name": "file", "type": "str", "required": true, "positional": true, - "help": "Source id or title from the current notebook" + "help": "Local file path to upload (single file; max 50 MB)" + }, + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name — server requires the attachment be scoped to a channel" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server slug" } ], "columns": [ - "source_id", - "notebook_id", - "title", - "type", - "summary", - "keywords", - "source" + "attachmentId", + "filename", + "mimeType", + "sizeBytes" ], "type": "js", - "modulePath": "notebooklm/source-guide.js", - "sourceFile": "notebooklm/source-guide.js", - "navigateBefore": false + "modulePath": "slock/attachment-upload.js", + "sourceFile": "slock/attachment-upload.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "notebooklm", - "name": "source-list", - "description": "List sources for the currently opened NotebookLM notebook", + "site": "slock", + "name": "attachment-url", + "description": "Get a short-lived signed CDN URL for an attachment (does not download bytes).", "access": "read", - "domain": "notebooklm.google.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "title", - "id", - "type", - "size", - "created_at", - "updated_at", - "url", - "source" + "args": [ + { + "name": "attachmentId", + "type": "str", + "required": true, + "positional": true, + "help": "Attachment UUID" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server slug" + } ], - "type": "js", - "modulePath": "notebooklm/source-list.js", - "sourceFile": "notebooklm/source-list.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "status", - "description": "Check NotebookLM page availability and login state in the current Chrome session", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], "columns": [ - "status", - "login", - "page", + "attachmentId", "url", - "title", - "notebooks" + "expiresAt" ], "type": "js", - "modulePath": "notebooklm/status.js", - "sourceFile": "notebooklm/status.js", - "navigateBefore": false + "modulePath": "slock/attachment-url.js", + "sourceFile": "slock/attachment-url.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "notebooklm", - "name": "summary", - "description": "Get the summary block from the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", + "site": "slock", + "name": "bookmark-add", + "description": "Bookmark a message (POST /channels/saved). Requires full messageId UUID.", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "title", - "summary", - "source", - "url" + "args": [ + { + "name": "messageId", + "type": "str", + "required": true, + "positional": true, + "help": "Full messageId UUID (short ids rejected)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } ], - "type": "js", - "modulePath": "notebooklm/summary.js", - "sourceFile": "notebooklm/summary.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "whoami", - "description": "Show the current logged-in notebooklm account", - "access": "read", - "domain": "google.com", - "strategy": "cookie", - "browser": true, - "args": [], "columns": [ - "logged_in", - "site", - "name", - "authuser" + "messageId", + "saved" ], "type": "js", - "modulePath": "notebooklm/auth.js", - "sourceFile": "notebooklm/auth.js", - "navigateBefore": false, + "modulePath": "slock/bookmark-add.js", + "sourceFile": "slock/bookmark-add.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "notebooklm", - "name": "write-note", - "description": "Create a Studio note in an existing NotebookLM notebook with the given title and Markdown content", - "access": "write", - "domain": "notebooklm.google.com", + "site": "slock", + "name": "bookmark-list", + "description": "List bookmarks (saved messages) in the active server", + "access": "read", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max results" }, { - "name": "title", - "type": "str", - "required": true, - "help": "Note title (1-200 chars)" + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Offset" }, { - "name": "content", + "name": "server", "type": "str", - "required": true, - "help": "Note body as Markdown" - }, - { - "name": "execute", - "type": "boolean", "required": false, - "help": "Actually create the remote NotebookLM note" + "help": "Override active server" } ], "columns": [ - "notebook_id", - "note_id", - "title", - "notebook_url" + "id", + "messageId", + "content", + "savedAt" ], "type": "js", - "modulePath": "notebooklm/write-note.js", - "sourceFile": "notebooklm/write-note.js", - "navigateBefore": false + "modulePath": "slock/bookmark-list.js", + "sourceFile": "slock/bookmark-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "paperreview", - "name": "feedback", - "description": "Submit feedback for a paperreview.ai review token", + "site": "slock", + "name": "bookmark-remove", + "description": "Remove a bookmark (DELETE /channels/saved/:messageId). 404 is treated as already-removed.", "access": "write", - "domain": "paperreview.ai", - "strategy": "public", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "token", + "name": "messageId", "type": "str", "required": true, "positional": true, - "help": "Review token returned by paperreview.ai" - }, - { - "name": "helpfulness", - "type": "int", - "required": true, - "help": "Helpfulness score from 1 to 5" - }, - { - "name": "critical-error", - "type": "str", - "required": true, - "help": "Whether the review contains a critical error", - "choices": [ - "yes", - "no" - ] - }, - { - "name": "actionable-suggestions", - "type": "str", - "required": true, - "help": "Whether the review contains actionable suggestions", - "choices": [ - "yes", - "no" - ] + "help": "Full messageId UUID" }, { - "name": "additional-comments", + "name": "server", "type": "str", "required": false, - "help": "Optional free-text feedback" - }, - { - "name": "timeout", - "type": "int", - "default": 30, - "required": false, - "help": "Max seconds for the overall command (default: 30)" + "help": "Override active server" } ], "columns": [ - "status", - "token", - "helpfulness", - "critical_error", - "actionable_suggestions", - "message" + "messageId", + "removed", + "note" ], "type": "js", - "modulePath": "paperreview/feedback.js", - "sourceFile": "paperreview/feedback.js" + "modulePath": "slock/bookmark-remove.js", + "sourceFile": "slock/bookmark-remove.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "paperreview", - "name": "review", - "description": "Fetch a paperreview.ai review by token", - "access": "read", - "domain": "paperreview.ai", - "strategy": "public", - "browser": false, + "site": "slock", + "name": "channel-archive", + "description": "Archive a channel — admin only (POST /channels/:id/archive)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "token", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Review token returned by paperreview.ai" + "help": "channelId UUID or #name" }, { - "name": "timeout", - "type": "int", - "default": 30, + "name": "server", + "type": "str", "required": false, - "help": "Max seconds for the overall command (default: 30)" + "help": "Override active server" } ], "columns": [ - "status", - "title", - "venue", - "numerical_score", - "has_feedback", - "review_url" + "channel", + "id", + "archivedAt", + "result" ], "type": "js", - "modulePath": "paperreview/review.js", - "sourceFile": "paperreview/review.js" + "modulePath": "slock/channel-archive.js", + "sourceFile": "slock/channel-archive.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "paperreview", - "name": "submit", - "description": "Submit a PDF to paperreview.ai for review", + "site": "slock", + "name": "channel-create", + "description": "Create a channel — admin only (POST /channels/). Public unless --private.", "access": "write", - "domain": "paperreview.ai", - "strategy": "public", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "pdf", + "name": "name", "type": "str", "required": true, "positional": true, - "help": "Path to the paper PDF" - }, - { - "name": "email", - "type": "str", - "required": true, - "help": "Email address for the submission" + "help": "Channel name" }, { - "name": "venue", + "name": "description", "type": "str", "required": false, - "help": "Optional target venue such as ICLR or NeurIPS" - }, - { - "name": "dry-run", - "type": "bool", - "default": false, - "required": false, - "help": "Validate the input and stop before remote submission" + "help": "Channel description / topic (≤500 chars)" }, { - "name": "prepare-only", + "name": "private", "type": "bool", "default": false, "required": false, - "help": "Request an upload slot but stop before uploading the PDF" + "help": "Create a private channel instead of public" }, { - "name": "timeout", - "type": "int", - "default": 120, + "name": "server", + "type": "str", "required": false, - "help": "Max seconds for the overall command (default: 120)" + "help": "Override active server" } ], "columns": [ - "status", - "file", - "email", - "venue", - "token", - "review_url", - "message" + "id", + "name", + "type", + "result" ], "type": "js", - "modulePath": "paperreview/submit.js", - "sourceFile": "paperreview/submit.js" + "modulePath": "slock/channel-create.js", + "sourceFile": "slock/channel-create.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "qoder", - "name": "account", - "description": "Click the account button (username) in the Qoder sidebar and return the visible account dropdown items.", + "site": "slock", + "name": "channel-files", + "description": "List files shared in a channel (GET /channels/:id/files)", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "channel", "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "limit", + "type": "int", + "default": 50, "required": false, - "help": "Username text shown in the sidebar (default: tries common short labels)" + "help": "Max files" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "Field", - "Value" + "id", + "filename", + "mimeType", + "sizeBytes", + "messageId", + "createdAt" ], "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true + "modulePath": "slock/channel-files.js", + "sourceFile": "slock/channel-files.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "qoder", - "name": "add-workspace", - "description": "Click \"Add Workspace\" — opens the folder picker. Note: this opens a system file-picker dialog that Qoder controls; the actual folder selection must be done in the UI by the user.", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "site": "slock", + "name": "channel-info", + "description": "Show one channel's details (GET /channels/:id)", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], "columns": [ - "Status" + "id", + "name", + "type", + "topic", + "joined", + "archivedAt" ], "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true + "modulePath": "slock/channel-info.js", + "sourceFile": "slock/channel-info.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "qoder", - "name": "ask", - "description": "Send a prompt to Qoder and wait up to --timeout seconds for the reply (best-effort: polls for the chat turn count to grow + stabilize).", + "site": "slock", + "name": "channel-join", + "description": "Join a public channel (POST /channels/:id/join)", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, "args": [ { - "name": "text", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Prompt text" + "help": "channelId UUID or #name" }, { - "name": "timeout", - "type": "int", - "default": 120, + "name": "server", + "type": "str", "required": false, - "help": "Max seconds to wait" + "help": "Override active server" } ], "columns": [ - "Role", - "Text", - "WaitedSeconds" + "channel", + "id", + "archivedAt", + "result" ], "type": "js", - "modulePath": "qoder/quest.js", - "sourceFile": "qoder/quest.js", - "navigateBefore": true + "modulePath": "slock/channel-join.js", + "sourceFile": "slock/channel-join.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "qoder", - "name": "credits", - "description": "Click \"Credits Usage\" and return the credits-usage display text.", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "slock", + "name": "channel-leave", + "description": "Leave a channel (POST /channels/:id/leave)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], "columns": [ - "Field", - "Value" + "channel", + "id", + "archivedAt", + "result" ], "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true + "modulePath": "slock/channel-leave.js", + "sourceFile": "slock/channel-leave.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "qoder", - "name": "history", - "description": "List Quests visible in the Qoder sidebar. Returns title + visible metadata.", + "site": "slock", + "name": "channel-list", + "description": "List channels in the active slock server", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 50, + "name": "server", + "type": "str", "required": false, - "help": "" + "help": "Override active server (slug or id) for this call" } ], "columns": [ - "Index", - "Title" + "id", + "name", + "topic" ], "type": "js", - "modulePath": "qoder/history.js", - "sourceFile": "qoder/history.js", - "navigateBefore": true + "modulePath": "slock/channel-list.js", + "sourceFile": "slock/channel-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "qoder", - "name": "knowledge", - "description": "Open the Knowledge view (Qoder's personal/team knowledge base).", + "site": "slock", + "name": "channel-mark", + "description": "Mark a channel read (default), read up to --seq, or --unread.", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "seq", + "type": "int", + "required": false, + "help": "Mark read up to this seq (omit for read-all)" + }, + { + "name": "unread", + "type": "bool", + "default": false, + "required": false, + "help": "Mark the channel unread instead of read" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], "columns": [ - "Status" + "channel", + "action", + "result" ], "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true + "modulePath": "slock/channel-mark.js", + "sourceFile": "slock/channel-mark.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "qoder", - "name": "marketplace", - "description": "Open the Qoder Marketplace.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "more-actions", - "description": "Click the \"More Actions\" button and list its menu items.", + "site": "slock", + "name": "channel-members", + "description": "List members of a channel", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "Index", - "Item" + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server (slug or id)" + } ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "new", - "description": "Start a new Qoder Quest (conversation). Clicks the \"New Quest\" button in the sidebar (or its ⌘N variant).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], "columns": [ - "Status" + "userId", + "name", + "kind", + "role" ], "type": "js", - "modulePath": "qoder/quest.js", - "sourceFile": "qoder/quest.js", - "navigateBefore": true + "modulePath": "slock/channel-members.js", + "sourceFile": "slock/channel-members.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "qoder", - "name": "open-editor", - "description": "Click \"Open Editor\" — opens the current draft in a full editor pane.", + "site": "slock", + "name": "channel-unarchive", + "description": "Unarchive a channel — admin only (POST /channels/:id/unarchive). #name lookups exclude archived channels; pass the channelId UUID for archived ones.", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "Status" + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } ], - "type": "js", - "modulePath": "qoder/composer.js", - "sourceFile": "qoder/composer.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "open-panel", - "description": "Open / close the Qoder bottom panel (Output / Terminal / Debug Console). ⌥⌘B equivalent.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], "columns": [ - "Status" + "channel", + "id", + "archivedAt", + "result" ], "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true + "modulePath": "slock/channel-unarchive.js", + "sourceFile": "slock/channel-unarchive.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "qoder", - "name": "prompt-enhance", - "description": "Click \"Prompt Enhance\" — Qoder rewrites the current composer draft for better LLM consumption.", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "site": "slock", + "name": "dm-list", + "description": "List DM channels in the active server (GET /channels/dm)", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server (slug or id)" + } + ], "columns": [ - "Status" + "channelId", + "peerName", + "peerId", + "createdAt" ], "type": "js", - "modulePath": "qoder/composer.js", - "sourceFile": "qoder/composer.js", - "navigateBefore": true + "modulePath": "slock/dm-list.js", + "sourceFile": "slock/dm-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "qoder", - "name": "read", - "description": "Read messages in the current Qoder Quest. Returns role + text for each visible turn.", + "site": "slock", + "name": "inbox", + "description": "List unified inbox items (channels, DMs, followed threads) that need attention.", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, "args": [ + { + "name": "filter", + "type": "str", + "default": "all", + "required": false, + "help": "all | unread | mentions" + }, { "name": "limit", "type": "int", "default": 30, "required": false, - "help": "" + "help": "Max items (server caps at 100)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Pagination offset" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "Index", - "Role", - "Text" + "kind", + "id", + "name", + "unreadCount", + "hasMention", + "lastActivityAt", + "preview" ], "type": "js", - "modulePath": "qoder/read.js", - "sourceFile": "qoder/read.js", - "navigateBefore": true + "modulePath": "slock/inbox.js", + "sourceFile": "slock/inbox.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "qoder", - "name": "search", - "description": "Open Qoder Search palette (⌘P), type a query, return matched options.", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "slock", + "name": "inbox-done", + "description": "Mark one chat as done / clear it from the inbox (POST /channels/inbox/done)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, "args": [ { - "name": "query", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Search text" + "help": "channelId UUID or #name" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "server", + "type": "str", "required": false, - "help": "" + "help": "Override active server" } ], "columns": [ - "Index", - "Item" - ], - "tags": [ - "search" + "channel", + "result" ], "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true + "modulePath": "slock/inbox-done.js", + "sourceFile": "slock/inbox-done.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "qoder", - "name": "send", - "description": "Type text into the Qoder composer and click \"Send message\" (fire-and-forget).", + "site": "slock", + "name": "inbox-read-all", + "description": "Mark the entire inbox as read (POST /channels/inbox/read-all)", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, "args": [ { - "name": "text", + "name": "server", "type": "str", - "required": true, - "positional": true, - "help": "Text to send" + "required": false, + "help": "Override active server" } ], "columns": [ - "Status", - "Length" - ], - "type": "js", - "modulePath": "qoder/quest.js", - "sourceFile": "qoder/quest.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "settings", - "description": "Click the Settings button in the Qoder sidebar.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "sidebar-toggle", - "description": "Collapse / Expand the Qoder Quest List sidebar (⌘B).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "status", - "description": "Check Qoder CDP connection and report the current renderer URL + title.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" - ], - "type": "js", - "modulePath": "qoder/status.js", - "sourceFile": "qoder/status.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "view-all", - "description": "Click \"View all\" to show all Quests.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "reddit", - "name": "comment", - "description": "Post a comment on a Reddit post", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" - }, - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "Comment text" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "reddit/comment.js", - "sourceFile": "reddit/comment.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "frontpage", - "description": "Reddit Frontpage / r/all", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "author", - "upvotes", - "comments", - "url", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/frontpage.js", - "sourceFile": "reddit/frontpage.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "home", - "description": "Reddit personalized home feed (Best, requires login)", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Number of posts (1–100)" - } - ], - "columns": [ - "rank", - "title", - "subreddit", - "score", - "comments", - "postId", - "author", - "url", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/home.js", - "sourceFile": "reddit/home.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "hot", - "description": "Reddit hot posts", - "access": "read", - "domain": "www.reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "subreddit", - "type": "str", - "default": "", - "required": false, - "help": "Subreddit name (e.g. programming). Empty for frontpage" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts" - } - ], - "columns": [ - "rank", - "title", - "subreddit", - "score", - "comments", - "postId", - "author", - "url", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" + "result", + "markedCount" ], "type": "js", - "modulePath": "reddit/hot.js", - "sourceFile": "reddit/hot.js", - "navigateBefore": "https://www.reddit.com" + "modulePath": "slock/inbox-read-all.js", + "sourceFile": "slock/inbox-read-all.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "reddit", + "site": "slock", "name": "login", - "description": "Open reddit login", + "description": "Open slock login", "access": "write", - "domain": "reddit.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [], @@ -5342,2173 +5106,59 @@ "status", "logged_in", "site", - "username", "id", + "name", + "email", "action", "verify_command" ], "type": "js", - "modulePath": "reddit/auth.js", - "sourceFile": "reddit/auth.js", + "modulePath": "slock/whoami.js", + "sourceFile": "slock/whoami.js", "navigateBefore": false, "siteSession": "persistent" }, - { - "site": "reddit", - "name": "popular", - "description": "Reddit Popular posts (/r/popular)", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "" - } - ], - "columns": [ - "rank", - "id", - "title", - "subreddit", - "score", - "comments", - "author", - "url", - "created_utc", - "selftext", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/popular.js", - "sourceFile": "reddit/popular.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "read", - "description": "Read a Reddit post and its comments", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "str", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or full URL" - }, - { - "name": "sort", - "type": "str", - "default": "best", - "required": false, - "help": "Comment sort: best, top, new, controversial, old, qa" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Number of top-level comments" - }, - { - "name": "depth", - "type": "int", - "default": 2, - "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" - }, - { - "name": "replies", - "type": "int", - "default": 5, - "required": false, - "help": "Max replies shown per comment at each level (sorted by score)" - }, - { - "name": "max-length", - "type": "int", - "default": 2000, - "required": false, - "help": "Max characters per comment body (min 100)" - }, - { - "name": "expand-more", - "type": "bool", - "default": false, - "required": false, - "help": "Follow Reddit \"more comments\" stubs by calling /api/morechildren.json" - }, - { - "name": "expand-rounds", - "type": "int", - "default": 2, - "required": false, - "help": "Max expansion passes when --expand-more is on (1–5; each round can fan out new \"more\" stubs)" - } - ], - "columns": [ - "type", - "author", - "score", - "text", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/read.js", - "sourceFile": "reddit/read.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "reply", - "description": "Reply to a Reddit comment", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "comment-id", - "type": "string", - "required": true, - "positional": true, - "help": "Comment ID (e.g. okf3s7u) or fullname (t1_xxx)" - }, - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "Reply text" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "reddit/reply.js", - "sourceFile": "reddit/reply.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "save", - "description": "Save or unsave a Reddit post", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" - }, - { - "name": "undo", - "type": "boolean", - "default": false, - "required": false, - "help": "Unsave instead of save" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "reddit/save.js", - "sourceFile": "reddit/save.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "saved", - "description": "Browse your saved Reddit posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "score", - "comments", - "url" - ], - "type": "js", - "modulePath": "reddit/saved.js", - "sourceFile": "reddit/saved.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "search", - "description": "Search Reddit Posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit search query" - }, - { - "name": "subreddit", - "type": "string", - "default": "", - "required": false, - "help": "Search within a specific subreddit" - }, - { - "name": "sort", - "type": "string", - "default": "relevance", - "required": false, - "help": "Sort order: relevance, hot, top, new, comments" - }, - { - "name": "time", - "type": "string", - "default": "all", - "required": false, - "help": "Time filter: hour, day, week, month, year, all" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "id", - "title", - "subreddit", - "author", - "score", - "comments", - "url", - "created_utc", - "selftext", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "reddit/search.js", - "sourceFile": "reddit/search.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "subreddit", - "description": "Get posts from a specific Subreddit", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "name", - "type": "string", - "required": true, - "positional": true, - "help": "Subreddit name (no `r/` prefix; e.g. `python`)" - }, - { - "name": "sort", - "type": "string", - "default": "hot", - "required": false, - "help": "Sorting method: hot, new, top, rising, controversial" - }, - { - "name": "time", - "type": "string", - "default": "all", - "required": false, - "help": "Time filter for top/controversial: hour, day, week, month, year, all" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "id", - "title", - "subreddit", - "author", - "upvotes", - "comments", - "url", - "created_utc", - "selftext", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/subreddit.js", - "sourceFile": "reddit/subreddit.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "subreddit-info", - "description": "Show metadata for a Reddit subreddit (subscribers, description, created date, NSFW)", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "name", - "type": "string", - "required": true, - "positional": true, - "help": "Subreddit name (no `r/` prefix needed)" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "reddit/subreddit-info.js", - "sourceFile": "reddit/subreddit-info.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "subscribe", - "description": "Subscribe or unsubscribe to a subreddit", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "subreddit", - "type": "string", - "required": true, - "positional": true, - "help": "Subreddit name (e.g. python)" - }, - { - "name": "undo", - "type": "boolean", - "default": false, - "required": false, - "help": "Unsubscribe instead of subscribe" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "reddit/subscribe.js", - "sourceFile": "reddit/subscribe.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "subscribed", - "description": "List subreddits you are subscribed to", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Max subreddits to return (1-1000, auto-paginates)" - } - ], - "columns": [ - "id", - "subreddit", - "title", - "subscribers", - "description", - "url" - ], - "type": "js", - "modulePath": "reddit/subscribed.js", - "sourceFile": "reddit/subscribed.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "upvote", - "description": "Upvote or downvote a Reddit post", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" - }, - { - "name": "direction", - "type": "string", - "default": "up", - "required": false, - "help": "Vote direction: up, down, none" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "reddit/upvote.js", - "sourceFile": "reddit/upvote.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "upvoted", - "description": "Browse your upvoted Reddit posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "score", - "comments", - "url" - ], - "type": "js", - "modulePath": "reddit/upvoted.js", - "sourceFile": "reddit/upvoted.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "user", - "description": "View a Reddit user profile", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit username (no `u/` prefix needed)" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "reddit/user.js", - "sourceFile": "reddit/user.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "user-comments", - "description": "View a Reddit user's comment history", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit username (no `u/` prefix needed)" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "subreddit", - "score", - "body", - "url" - ], - "type": "js", - "modulePath": "reddit/user-comments.js", - "sourceFile": "reddit/user-comments.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "user-posts", - "description": "View a Reddit user's submitted posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit username (no `u/` prefix needed)" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "score", - "comments", - "url" - ], - "type": "js", - "modulePath": "reddit/user-posts.js", - "sourceFile": "reddit/user-posts.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "whoami", - "description": "Show the currently logged-in Reddit user", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "reddit/whoami.js", - "sourceFile": "reddit/whoami.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "slock", - "name": "attachment-download", - "description": "Download an attachment to a local file. Resolves a signed CDN URL in the page, then fetches bytes node-side (no CORS).", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "attachmentId", - "type": "str", - "required": true, - "positional": true, - "help": "Attachment UUID" - }, - { - "name": "out", - "type": "str", - "required": false, - "help": "Local path to write to. Defaults to ./.bin" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server slug" - } - ], - "columns": [ - "attachmentId", - "out", - "sizeBytes" - ], - "type": "js", - "modulePath": "slock/attachment-download.js", - "sourceFile": "slock/attachment-download.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "attachment-upload", - "description": "Upload a local file to Slock attachments. Prints the attachmentId for use with `message-send --attach`.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "file", - "type": "str", - "required": true, - "positional": true, - "help": "Local file path to upload (single file; max 50 MB)" - }, - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name — server requires the attachment be scoped to a channel" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server slug" - } - ], - "columns": [ - "attachmentId", - "filename", - "mimeType", - "sizeBytes" - ], - "type": "js", - "modulePath": "slock/attachment-upload.js", - "sourceFile": "slock/attachment-upload.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "attachment-url", - "description": "Get a short-lived signed CDN URL for an attachment (does not download bytes).", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "attachmentId", - "type": "str", - "required": true, - "positional": true, - "help": "Attachment UUID" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server slug" - } - ], - "columns": [ - "attachmentId", - "url", - "expiresAt" - ], - "type": "js", - "modulePath": "slock/attachment-url.js", - "sourceFile": "slock/attachment-url.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "bookmark-add", - "description": "Bookmark a message (POST /channels/saved). Requires full messageId UUID.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full messageId UUID (short ids rejected)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "messageId", - "saved" - ], - "type": "js", - "modulePath": "slock/bookmark-add.js", - "sourceFile": "slock/bookmark-add.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "bookmark-list", - "description": "List bookmarks (saved messages) in the active server", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max results" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Offset" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "messageId", - "content", - "savedAt" - ], - "type": "js", - "modulePath": "slock/bookmark-list.js", - "sourceFile": "slock/bookmark-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "bookmark-remove", - "description": "Remove a bookmark (DELETE /channels/saved/:messageId). 404 is treated as already-removed.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full messageId UUID" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "messageId", - "removed", - "note" - ], - "type": "js", - "modulePath": "slock/bookmark-remove.js", - "sourceFile": "slock/bookmark-remove.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-archive", - "description": "Archive a channel — admin only (POST /channels/:id/archive)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "id", - "archivedAt", - "result" - ], - "type": "js", - "modulePath": "slock/channel-archive.js", - "sourceFile": "slock/channel-archive.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-create", - "description": "Create a channel — admin only (POST /channels/). Public unless --private.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Channel name" - }, - { - "name": "description", - "type": "str", - "required": false, - "help": "Channel description / topic (≤500 chars)" - }, - { - "name": "private", - "type": "bool", - "default": false, - "required": false, - "help": "Create a private channel instead of public" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "name", - "type", - "result" - ], - "type": "js", - "modulePath": "slock/channel-create.js", - "sourceFile": "slock/channel-create.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-files", - "description": "List files shared in a channel (GET /channels/:id/files)", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max files" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "filename", - "mimeType", - "sizeBytes", - "messageId", - "createdAt" - ], - "type": "js", - "modulePath": "slock/channel-files.js", - "sourceFile": "slock/channel-files.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-info", - "description": "Show one channel's details (GET /channels/:id)", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "name", - "type", - "topic", - "joined", - "archivedAt" - ], - "type": "js", - "modulePath": "slock/channel-info.js", - "sourceFile": "slock/channel-info.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-join", - "description": "Join a public channel (POST /channels/:id/join)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "id", - "archivedAt", - "result" - ], - "type": "js", - "modulePath": "slock/channel-join.js", - "sourceFile": "slock/channel-join.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-leave", - "description": "Leave a channel (POST /channels/:id/leave)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "id", - "archivedAt", - "result" - ], - "type": "js", - "modulePath": "slock/channel-leave.js", - "sourceFile": "slock/channel-leave.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-list", - "description": "List channels in the active slock server", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id) for this call" - } - ], - "columns": [ - "id", - "name", - "topic" - ], - "type": "js", - "modulePath": "slock/channel-list.js", - "sourceFile": "slock/channel-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-mark", - "description": "Mark a channel read (default), read up to --seq, or --unread.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "seq", - "type": "int", - "required": false, - "help": "Mark read up to this seq (omit for read-all)" - }, - { - "name": "unread", - "type": "bool", - "default": false, - "required": false, - "help": "Mark the channel unread instead of read" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "action", - "result" - ], - "type": "js", - "modulePath": "slock/channel-mark.js", - "sourceFile": "slock/channel-mark.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-members", - "description": "List members of a channel", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id)" - } - ], - "columns": [ - "userId", - "name", - "kind", - "role" - ], - "type": "js", - "modulePath": "slock/channel-members.js", - "sourceFile": "slock/channel-members.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-unarchive", - "description": "Unarchive a channel — admin only (POST /channels/:id/unarchive). #name lookups exclude archived channels; pass the channelId UUID for archived ones.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "id", - "archivedAt", - "result" - ], - "type": "js", - "modulePath": "slock/channel-unarchive.js", - "sourceFile": "slock/channel-unarchive.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "dm-list", - "description": "List DM channels in the active server (GET /channels/dm)", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id)" - } - ], - "columns": [ - "channelId", - "peerName", - "peerId", - "createdAt" - ], - "type": "js", - "modulePath": "slock/dm-list.js", - "sourceFile": "slock/dm-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "inbox", - "description": "List unified inbox items (channels, DMs, followed threads) that need attention.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "filter", - "type": "str", - "default": "all", - "required": false, - "help": "all | unread | mentions" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max items (server caps at 100)" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Pagination offset" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "kind", - "id", - "name", - "unreadCount", - "hasMention", - "lastActivityAt", - "preview" - ], - "type": "js", - "modulePath": "slock/inbox.js", - "sourceFile": "slock/inbox.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "inbox-done", - "description": "Mark one chat as done / clear it from the inbox (POST /channels/inbox/done)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "result" - ], - "type": "js", - "modulePath": "slock/inbox-done.js", - "sourceFile": "slock/inbox-done.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "inbox-read-all", - "description": "Mark the entire inbox as read (POST /channels/inbox/read-all)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "result", - "markedCount" - ], - "type": "js", - "modulePath": "slock/inbox-read-all.js", - "sourceFile": "slock/inbox-read-all.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "login", - "description": "Open slock login", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "id", - "name", - "email", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "slock/whoami.js", - "sourceFile": "slock/whoami.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "message-read", - "description": "Read messages in a channel or thread. Thread form: \"#channel:msgIdOrShort\". Use --after seq|UUID for cursor.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID, \"#name\", or \"#channel:msgIdOrShort\"" - }, - { - "name": "after", - "type": "str", - "required": false, - "help": "Cursor: seq number or messageId UUID (exclusive)" - }, - { - "name": "before", - "type": "str", - "required": false, - "help": "seq to page before" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max messages" - }, - { - "name": "no-threads", - "type": "bool", - "default": false, - "required": false, - "help": "Skip /threads enrichment" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "seq", - "createdAt", - "senderName", - "content", - "threadChannelId", - "replyCount", - "unreadCount", - "lastReplyAt" - ], - "type": "js", - "modulePath": "slock/message-read.js", - "sourceFile": "slock/message-read.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "message-search", - "description": "Search messages", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Restrict to a channel (UUID or #name)" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max results" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "channelId", - "createdAt", - "senderName", - "content" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "slock/message-search.js", - "sourceFile": "slock/message-search.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "message-send", - "description": "Send a message to a channel, DM, or thread (content sent verbatim)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "target", - "type": "str", - "required": true, - "positional": true, - "help": "\"#channel\", \"#channel:msgIdOrShort\", \"dm:@name\", \"dm:\", or channel UUID" - }, - { - "name": "content", - "type": "str", - "required": true, - "positional": true, - "help": "Message body (sent verbatim, no marker)" - }, - { - "name": "dry-run", - "type": "bool", - "default": false, - "required": false, - "help": "Print the planned payload without sending" - }, - { - "name": "as-task", - "type": "bool", - "default": false, - "required": false, - "help": "Create the message as a task (asTask)" - }, - { - "name": "attach", - "type": "str", - "required": false, - "help": "Comma-separated attachmentId UUIDs (upload separately first)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id)" - } - ], - "columns": [ - "target", - "channelId", - "content", - "result", - "messageId" - ], - "type": "js", - "modulePath": "slock/message-send.js", - "sourceFile": "slock/message-send.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "reaction-add", - "description": "Add an emoji reaction to a message (POST /messages/:id/reactions). Idempotent server-side.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full messageId UUID (short ids rejected)" - }, - { - "name": "emoji", - "type": "str", - "required": true, - "positional": true, - "help": "A single unicode emoji, e.g. 👍" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "messageId", - "emoji", - "result" - ], - "type": "js", - "modulePath": "slock/reaction-add.js", - "sourceFile": "slock/reaction-add.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "reaction-remove", - "description": "Remove your emoji reaction from a message (DELETE /messages/:id/reactions).", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full messageId UUID (short ids rejected)" - }, - { - "name": "emoji", - "type": "str", - "required": true, - "positional": true, - "help": "The unicode emoji to remove, e.g. 👍" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "messageId", - "emoji", - "result" - ], - "type": "js", - "modulePath": "slock/reaction-remove.js", - "sourceFile": "slock/reaction-remove.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "server-list", - "description": "List slock servers you belong to; marks active per localStorage slug", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", - "slug", - "name", - "active" - ], - "type": "js", - "modulePath": "slock/server-list.js", - "sourceFile": "slock/server-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "server-use", - "description": "Set the active slock server (writes localStorage.slock_last_server_slug)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "server slug, \"#slug\", or UUID id" - } - ], - "columns": [ - "id", - "slug", - "name", - "written" - ], - "type": "js", - "modulePath": "slock/server-use.js", - "sourceFile": "slock/server-use.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-claim", - "description": "Claim a chat task (PATCH /tasks/:id/claim). Requires full task UUID (= message id).", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "taskId", - "type": "str", - "required": true, - "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "taskId", - "taskStatus", - "assigneeId", - "taskNumber" - ], - "type": "js", - "modulePath": "slock/task-claim.js", - "sourceFile": "slock/task-claim.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-convert", - "description": "Convert a message into a chat task (POST /tasks/convert-message). Accepts a message UUID or \"#channel:shortId\".", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full message UUID, or \"#channel:shortId\" (short id expanded via /messages/context)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "channelId" - ], - "type": "js", - "modulePath": "slock/task-convert.js", - "sourceFile": "slock/task-convert.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-create", - "description": "Create a task in a channel (single title; batch 1-50 is server-supported but client surface is single — see backlog R4).", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "title", - "type": "str", - "required": true, - "positional": true, - "help": "Task title (single; batch TODO via R4)" - }, - { - "name": "desc", - "type": "str", - "required": false, - "help": "Optional description body for the task" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "channelId" - ], - "type": "js", - "modulePath": "slock/task-create.js", - "sourceFile": "slock/task-create.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-delete", - "description": "Delete a chat task (DELETE /tasks/:taskId). Requires --confirm — destructive, irreversible.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "taskId", - "type": "str", - "required": true, - "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" - }, - { - "name": "confirm", - "type": "bool", - "default": false, - "required": false, - "help": "Required acknowledgement: deletion is irreversible" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "taskId", - "deleted" - ], - "type": "js", - "modulePath": "slock/task-delete.js", - "sourceFile": "slock/task-delete.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-get", - "description": "Fetch a task by channel + taskNumber (GET /tasks/channel/:channelId/number/:taskNumber).", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "number", - "type": "str", - "required": true, - "positional": true, - "help": "taskNumber (per-channel integer, as shown in \"task #N\")" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "assigneeId" - ], - "type": "js", - "modulePath": "slock/task-get.js", - "sourceFile": "slock/task-get.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-list", - "description": "List tasks (chat tasks = messages with task fields) attached to a channel. Optional --status filter.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "status", - "type": "str", - "required": false, - "help": "Filter by status: todo|in_progress|in_review|done|closed" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "assigneeId" - ], - "type": "js", - "modulePath": "slock/task-list.js", - "sourceFile": "slock/task-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-list-server", - "description": "List tasks across all channels in the active server (GET /tasks/server). Optional --status filter.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "status", - "type": "str", - "required": false, - "help": "Filter by status: todo|in_progress|in_review|done|closed" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "channelId", - "assigneeId" - ], - "type": "js", - "modulePath": "slock/task-list-server.js", - "sourceFile": "slock/task-list-server.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-status", - "description": "Set a task's status (PATCH /tasks/:taskId/status, body {status}). One of todo|in_progress|in_review|done|closed.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "taskId", - "type": "str", - "required": true, - "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" - }, - { - "name": "status", - "type": "str", - "required": true, - "positional": true, - "help": "One of: todo|in_progress|in_review|done|closed" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "taskId", - "taskStatus", - "assigneeId", - "taskNumber" - ], - "type": "js", - "modulePath": "slock/task-status.js", - "sourceFile": "slock/task-status.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-unclaim", - "description": "Release ownership of a chat task (PATCH /tasks/:id/unclaim).", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "taskId", - "type": "str", - "required": true, - "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "taskId", - "taskStatus", - "assigneeId", - "taskNumber" - ], - "type": "js", - "modulePath": "slock/task-unclaim.js", - "sourceFile": "slock/task-unclaim.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "thread-done", - "description": "Mark a thread as done / hide it from the active list (POST /channels/threads/done)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "threadChannelId", - "type": "str", - "required": true, - "positional": true, - "help": "Thread channel UUID (from thread-list / message-read)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "threadChannelId", - "result" - ], - "type": "js", - "modulePath": "slock/thread-done.js", - "sourceFile": "slock/thread-done.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "thread-follow", - "description": "Follow the thread on a parent message (POST /channels/threads/follow)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "parentMessageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full parent messageId UUID (short ids rejected)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "parentMessageId", - "threadChannelId", - "result" - ], - "type": "js", - "modulePath": "slock/thread-follow.js", - "sourceFile": "slock/thread-follow.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "thread-list", - "description": "List followed threads in the active server (GET /channels/threads/followed)", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "threadChannelId", - "parentMessageId", - "parentChannelName", - "unreadCount", - "replyCount", - "lastReplyAt" - ], - "type": "js", - "modulePath": "slock/thread-list.js", - "sourceFile": "slock/thread-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, { "site": "slock", - "name": "thread-undone", - "description": "Restore a done thread to the active list (POST /channels/threads/undone)", - "access": "write", + "name": "message-read", + "description": "Read messages in a channel or thread. Thread form: \"#channel:msgIdOrShort\". Use --after seq|UUID for cursor.", + "access": "read", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "threadChannelId", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Thread channel UUID (from thread-list / message-read)" + "help": "channelId UUID, \"#name\", or \"#channel:msgIdOrShort\"" + }, + { + "name": "after", + "type": "str", + "required": false, + "help": "Cursor: seq number or messageId UUID (exclusive)" + }, + { + "name": "before", + "type": "str", + "required": false, + "help": "seq to page before" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max messages" + }, + { + "name": "no-threads", + "type": "bool", + "default": false, + "required": false, + "help": "Skip /threads enrichment" }, { "name": "server", @@ -7518,30 +5168,50 @@ } ], "columns": [ + "id", + "seq", + "createdAt", + "senderName", + "content", "threadChannelId", - "result" + "replyCount", + "unreadCount", + "lastReplyAt" ], "type": "js", - "modulePath": "slock/thread-undone.js", - "sourceFile": "slock/thread-undone.js", + "modulePath": "slock/message-read.js", + "sourceFile": "slock/message-read.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "thread-unfollow", - "description": "Stop following a thread (POST /channels/threads/unfollow)", - "access": "write", + "name": "message-search", + "description": "Search messages", + "access": "read", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "threadChannelId", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Thread channel UUID (from thread-list / message-read)" + "help": "Search query" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Restrict to a channel (UUID or #name)" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max results" }, { "name": "server", @@ -7551,794 +5221,784 @@ } ], "columns": [ - "threadChannelId", - "result" + "id", + "channelId", + "createdAt", + "senderName", + "content" ], - "type": "js", - "modulePath": "slock/thread-unfollow.js", - "sourceFile": "slock/thread-unfollow.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "unread-summary", - "description": "Global unread counts across every server you belong to.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "serverId", - "slug", - "name", - "unreadCount" + "tags": [ + "search" ], "type": "js", - "modulePath": "slock/unread-summary.js", - "sourceFile": "slock/unread-summary.js", + "modulePath": "slock/message-search.js", + "sourceFile": "slock/message-search.js", "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { "site": "slock", - "name": "whoami", - "description": "Show the current logged-in slock account", - "access": "read", + "name": "message-send", + "description": "Send a message to a channel, DM, or thread (content sent verbatim)", + "access": "write", "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "id", - "name", - "email" - ], - "type": "js", - "modulePath": "slock/whoami.js", - "sourceFile": "slock/whoami.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "spotify", - "name": "auth", - "description": "Authenticate with Spotify (OAuth — run once)", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "next", - "description": "Skip to next track", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "pause", - "description": "Pause playback", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "play", - "description": "Resume playback or search and play a track/artist", - "access": "write", - "strategy": "local", - "browser": false, "args": [ { - "name": "query", + "name": "target", "type": "str", - "default": "", - "required": false, + "required": true, "positional": true, - "help": "Track or artist to play (optional)" - } - ], - "columns": [ - "track", - "artist", - "status" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "prev", - "description": "Skip to previous track", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "queue", - "description": "Add a track to the playback queue", - "access": "write", - "strategy": "local", - "browser": false, - "args": [ + "help": "\"#channel\", \"#channel:msgIdOrShort\", \"dm:@name\", \"dm:\", or channel UUID" + }, { - "name": "query", + "name": "content", "type": "str", "required": true, "positional": true, - "help": "Track to add to queue" + "help": "Message body (sent verbatim, no marker)" + }, + { + "name": "dry-run", + "type": "bool", + "default": false, + "required": false, + "help": "Print the planned payload without sending" + }, + { + "name": "as-task", + "type": "bool", + "default": false, + "required": false, + "help": "Create the message as a task (asTask)" + }, + { + "name": "attach", + "type": "str", + "required": false, + "help": "Comma-separated attachmentId UUIDs (upload separately first)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server (slug or id)" } ], "columns": [ - "track", - "artist", - "status" + "target", + "channelId", + "content", + "result", + "messageId" ], "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" + "modulePath": "slock/message-send.js", + "sourceFile": "slock/message-send.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "spotify", - "name": "repeat", - "description": "Set repeat mode (off / track / context)", + "site": "slock", + "name": "reaction-add", + "description": "Add an emoji reaction to a message (POST /messages/:id/reactions). Idempotent server-side.", "access": "write", - "strategy": "local", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "mode", + "name": "messageId", "type": "str", - "default": "context", - "required": false, + "required": true, "positional": true, - "help": "off / track / context", - "choices": [ - "off", - "track", - "context" - ] - } - ], - "columns": [ - "repeat" - ], - "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" - }, - { - "site": "spotify", - "name": "search", - "description": "Search for tracks", - "access": "read", - "strategy": "local", - "browser": false, - "args": [ + "help": "Full messageId UUID (short ids rejected)" + }, { - "name": "query", + "name": "emoji", "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "A single unicode emoji, e.g. 👍" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "server", + "type": "str", "required": false, - "help": "Number of results (default: 10)" + "help": "Override active server" } ], "columns": [ - "track", - "artist", - "album", - "uri" - ], - "tags": [ - "search" + "messageId", + "emoji", + "result" ], "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" + "modulePath": "slock/reaction-add.js", + "sourceFile": "slock/reaction-add.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "spotify", - "name": "shuffle", - "description": "Toggle shuffle on/off", + "site": "slock", + "name": "reaction-remove", + "description": "Remove your emoji reaction from a message (DELETE /messages/:id/reactions).", "access": "write", - "strategy": "local", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "state", + "name": "messageId", "type": "str", - "default": "on", - "required": false, + "required": true, "positional": true, - "help": "on or off", - "choices": [ - "on", - "off" - ] + "help": "Full messageId UUID (short ids rejected)" + }, + { + "name": "emoji", + "type": "str", + "required": true, + "positional": true, + "help": "The unicode emoji to remove, e.g. 👍" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "shuffle" + "messageId", + "emoji", + "result" ], "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" + "modulePath": "slock/reaction-remove.js", + "sourceFile": "slock/reaction-remove.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "spotify", - "name": "status", - "description": "Show current playback status", + "site": "slock", + "name": "server-list", + "description": "List slock servers you belong to; marks active per localStorage slug", "access": "read", - "strategy": "local", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [], "columns": [ - "track", - "artist", - "album", - "status", - "progress" + "id", + "slug", + "name", + "active" ], "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" + "modulePath": "slock/server-list.js", + "sourceFile": "slock/server-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "spotify", - "name": "volume", - "description": "Set playback volume (0-100)", + "site": "slock", + "name": "server-use", + "description": "Set the active slock server (writes localStorage.slock_last_server_slug)", "access": "write", - "strategy": "local", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "level", - "type": "int", - "default": 50, + "name": "input", + "type": "str", "required": true, "positional": true, - "help": "Volume 0–100" + "help": "server slug, \"#slug\", or UUID id" } ], "columns": [ - "volume" + "id", + "slug", + "name", + "written" ], "type": "js", - "modulePath": "spotify/spotify.js", - "sourceFile": "spotify/spotify.js" + "modulePath": "slock/server-use.js", + "sourceFile": "slock/server-use.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "comment", - "description": "Post a comment on a TikTok video", + "site": "slock", + "name": "task-claim", + "description": "Claim a chat task (PATCH /tasks/:id/claim). Requires full task UUID (= message id).", "access": "write", - "domain": "www.tiktok.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "url", + "name": "taskId", "type": "str", "required": true, "positional": true, - "help": "TikTok video URL (https://www.tiktok.com/@user/video/)" + "help": "Full task UUID (= message id; short ids rejected)" }, { - "name": "text", + "name": "server", "type": "str", - "required": true, - "positional": true, - "help": "Comment text (≤150 chars)" + "required": false, + "help": "Override active server" } ], "columns": [ - "url", - "text", - "result" + "taskId", + "taskStatus", + "assigneeId", + "taskNumber" ], "type": "js", - "modulePath": "tiktok/comment.js", - "sourceFile": "tiktok/comment.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/task-claim.js", + "sourceFile": "slock/task-claim.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "creator-videos", - "description": "TikTok Studio creator content list (views/likes/comments/saves/shares)", - "access": "read", - "domain": "www.tiktok.com", + "site": "slock", + "name": "task-convert", + "description": "Convert a message into a chat task (POST /tasks/convert-message). Accepts a message UUID or \"#channel:shortId\".", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of creator videos to return (max 250)" + "name": "messageId", + "type": "str", + "required": true, + "positional": true, + "help": "Full message UUID, or \"#channel:shortId\" (short id expanded via /messages/context)" }, { - "name": "cursor", - "type": "string", - "default": "0", + "name": "server", + "type": "str", "required": false, - "help": "Non-negative TikTok Studio pagination cursor" + "help": "Override active server" } ], "columns": [ - "video_id", + "id", + "taskNumber", "title", - "date", - "views", - "likes", - "comments", - "saves", - "shares", - "url" + "taskStatus", + "channelId" ], "type": "js", - "modulePath": "tiktok/creator-videos.js", - "sourceFile": "tiktok/creator-videos.js", - "navigateBefore": "https://www.tiktok.com/tiktokstudio/content" + "modulePath": "slock/task-convert.js", + "sourceFile": "slock/task-convert.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "explore", - "description": "Get trending TikTok videos from the recommend feed via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", + "site": "slock", + "name": "task-create", + "description": "Create a task in a channel (single title; batch 1-50 is server-supported but client surface is single — see backlog R4).", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "title", + "type": "str", + "required": true, + "positional": true, + "help": "Task title (single; batch TODO via R4)" + }, + { + "name": "desc", + "type": "str", "required": false, - "help": "Number of videos to return (max 120)" + "help": "Optional description body for the task" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "index", "id", - "author", - "url", - "cover", + "taskNumber", "title", - "desc", - "plays", - "likes", - "comments", - "shares", - "createTime" + "taskStatus", + "channelId" + ], + "type": "js", + "modulePath": "slock/task-create.js", + "sourceFile": "slock/task-create.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-delete", + "description": "Delete a chat task (DELETE /tasks/:taskId). Requires --confirm — destructive, irreversible.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "taskId", + "type": "str", + "required": true, + "positional": true, + "help": "Full task UUID (= message id; short ids rejected)" + }, + { + "name": "confirm", + "type": "bool", + "default": false, + "required": false, + "help": "Required acknowledgement: deletion is irreversible" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } ], - "tags": [ - "search" + "columns": [ + "taskId", + "deleted" ], "type": "js", - "modulePath": "tiktok/explore.js", - "sourceFile": "tiktok/explore.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/task-delete.js", + "sourceFile": "slock/task-delete.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "follow", - "description": "Follow a TikTok user by username", - "access": "write", - "domain": "www.tiktok.com", + "site": "slock", + "name": "task-get", + "description": "Fetch a task by channel + taskNumber (GET /tasks/channel/:channelId/number/:taskNumber).", + "access": "read", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "TikTok username (without @)" + "help": "channelId UUID or #name" + }, + { + "name": "number", + "type": "str", + "required": true, + "positional": true, + "help": "taskNumber (per-channel integer, as shown in \"task #N\")" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "username", - "url", - "result" + "id", + "taskNumber", + "title", + "taskStatus", + "assigneeId" ], "type": "js", - "modulePath": "tiktok/follow.js", - "sourceFile": "tiktok/follow.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/task-get.js", + "sourceFile": "slock/task-get.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "following", - "description": "List accounts the logged-in user follows on TikTok via page-context APIs", + "site": "slock", + "name": "task-list", + "description": "List tasks (chat tasks = messages with task fields) attached to a channel. Optional --status filter.", "access": "read", - "domain": "www.tiktok.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "status", + "type": "str", "required": false, - "help": "Number of accounts (max 200)" + "help": "Filter by status: todo|in_progress|in_review|done|closed" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "index", - "username", - "name", - "secUid", - "verified", - "followers", - "following", - "url" + "id", + "taskNumber", + "title", + "taskStatus", + "assigneeId" ], "type": "js", - "modulePath": "tiktok/following.js", - "sourceFile": "tiktok/following.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/task-list.js", + "sourceFile": "slock/task-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "friends", - "description": "Get TikTok friend / who-to-follow suggestions via page-context APIs", + "site": "slock", + "name": "task-list-server", + "description": "List tasks across all channels in the active server (GET /tasks/server). Optional --status filter.", "access": "read", - "domain": "www.tiktok.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "status", + "type": "str", "required": false, - "help": "Number of suggestions (max 100)" + "help": "Filter by status: todo|in_progress|in_review|done|closed" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "index", - "username", - "name", - "secUid", - "verified", - "followers", - "following", - "url" + "id", + "taskNumber", + "title", + "taskStatus", + "channelId", + "assigneeId" ], "type": "js", - "modulePath": "tiktok/friends.js", - "sourceFile": "tiktok/friends.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/task-list-server.js", + "sourceFile": "slock/task-list-server.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "like", - "description": "Like a TikTok video", + "site": "slock", + "name": "task-status", + "description": "Set a task's status (PATCH /tasks/:taskId/status, body {status}). One of todo|in_progress|in_review|done|closed.", "access": "write", - "domain": "www.tiktok.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "url", + "name": "taskId", "type": "str", "required": true, "positional": true, - "help": "TikTok video URL" + "help": "Full task UUID (= message id; short ids rejected)" + }, + { + "name": "status", + "type": "str", + "required": true, + "positional": true, + "help": "One of: todo|in_progress|in_review|done|closed" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "status", - "likes", - "url" + "taskId", + "taskStatus", + "assigneeId", + "taskNumber" ], "type": "js", - "modulePath": "tiktok/like.js", - "sourceFile": "tiktok/like.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/task-status.js", + "sourceFile": "slock/task-status.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "live", - "description": "Browse TikTok live streams via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", + "site": "slock", + "name": "task-unclaim", + "description": "Release ownership of a chat task (PATCH /tasks/:id/unclaim).", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 10, + "name": "taskId", + "type": "str", + "required": true, + "positional": true, + "help": "Full task UUID (= message id; short ids rejected)" + }, + { + "name": "server", + "type": "str", "required": false, - "help": "Number of streams (max 60)" + "help": "Override active server" } ], "columns": [ - "index", - "streamer", - "name", - "title", - "viewers", - "likes", - "secUid", - "url" - ], - "type": "js", - "modulePath": "tiktok/live.js", - "sourceFile": "tiktok/live.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "login", - "description": "Open tiktok login", - "access": "write", - "domain": "tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "sec_uid", - "username", - "nickname", - "action", - "verify_command" + "taskId", + "taskStatus", + "assigneeId", + "taskNumber" ], "type": "js", - "modulePath": "tiktok/auth.js", - "sourceFile": "tiktok/auth.js", - "navigateBefore": false, + "modulePath": "slock/task-unclaim.js", + "sourceFile": "slock/task-unclaim.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, - { - "site": "tiktok", - "name": "notifications", - "description": "Read TikTok inbox notifications (likes, comments, mentions, followers) via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", + { + "site": "slock", + "name": "thread-done", + "description": "Mark a thread as done / hide it from the active list (POST /channels/threads/done)", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Number of notifications (max 100)" + "name": "threadChannelId", + "type": "str", + "required": true, + "positional": true, + "help": "Thread channel UUID (from thread-list / message-read)" }, { - "name": "type", + "name": "server", "type": "str", - "default": "all", "required": false, - "help": "Notification type", - "choices": [ - "all", - "likes", - "comments", - "mentions", - "followers" - ] + "help": "Override active server" } ], "columns": [ - "index", - "id", - "from", - "text", - "createTime" + "threadChannelId", + "result" ], "type": "js", - "modulePath": "tiktok/notifications.js", - "sourceFile": "tiktok/notifications.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/thread-done.js", + "sourceFile": "slock/thread-done.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "profile", - "description": "Get TikTok user profile info", - "access": "read", - "domain": "www.tiktok.com", + "site": "slock", + "name": "thread-follow", + "description": "Follow the thread on a parent message (POST /channels/threads/follow)", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "parentMessageId", "type": "str", "required": true, "positional": true, - "help": "TikTok username (without @)" + "help": "Full parent messageId UUID (short ids rejected)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "username", - "name", - "followers", - "following", - "likes", - "videos", - "verified", - "bio" + "parentMessageId", + "threadChannelId", + "result" ], "type": "js", - "modulePath": "tiktok/profile.js", - "sourceFile": "tiktok/profile.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/thread-follow.js", + "sourceFile": "slock/thread-follow.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "save", - "description": "Add a TikTok video to Favorites", - "access": "write", - "domain": "www.tiktok.com", + "site": "slock", + "name": "thread-list", + "description": "List followed threads in the active server (GET /channels/threads/followed)", + "access": "read", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "url", + "name": "server", "type": "str", - "required": true, - "positional": true, - "help": "TikTok video URL" + "required": false, + "help": "Override active server" } ], "columns": [ - "status", - "url" + "threadChannelId", + "parentMessageId", + "parentChannelName", + "unreadCount", + "replyCount", + "lastReplyAt" ], "type": "js", - "modulePath": "tiktok/save.js", - "sourceFile": "tiktok/save.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/thread-list.js", + "sourceFile": "slock/thread-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "search", - "description": "Search TikTok videos", - "access": "read", - "domain": "www.tiktok.com", + "site": "slock", + "name": "thread-undone", + "description": "Restore a done thread to the active list (POST /channels/threads/undone)", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", + "name": "threadChannelId", "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "Thread channel UUID (from thread-list / message-read)" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "server", + "type": "str", "required": false, - "help": "Number of results" + "help": "Override active server" } ], "columns": [ - "rank", - "desc", - "author", - "url", - "plays", - "likes", - "comments", - "shares" - ], - "tags": [ - "search" + "threadChannelId", + "result" ], "type": "js", - "modulePath": "tiktok/search.js", - "sourceFile": "tiktok/search.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/thread-undone.js", + "sourceFile": "slock/thread-undone.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "unfollow", - "description": "Unfollow a TikTok user by username", + "site": "slock", + "name": "thread-unfollow", + "description": "Stop following a thread (POST /channels/threads/unfollow)", "access": "write", - "domain": "www.tiktok.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "threadChannelId", "type": "str", "required": true, "positional": true, - "help": "TikTok username (without @)" + "help": "Thread channel UUID (from thread-list / message-read)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "username", - "url", + "threadChannelId", "result" ], "type": "js", - "modulePath": "tiktok/unfollow.js", - "sourceFile": "tiktok/unfollow.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/thread-unfollow.js", + "sourceFile": "slock/thread-unfollow.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "tiktok", - "name": "unlike", - "description": "Unlike a TikTok video", - "access": "write", - "domain": "www.tiktok.com", + "site": "slock", + "name": "unread-summary", + "description": "Global unread counts across every server you belong to.", + "access": "read", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok video URL" - } + "args": [], + "columns": [ + "serverId", + "slug", + "name", + "unreadCount" ], + "type": "js", + "modulePath": "slock/unread-summary.js", + "sourceFile": "slock/unread-summary.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "whoami", + "description": "Show the current logged-in slock account", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "status", - "likes", - "url" + "logged_in", + "site", + "id", + "name", + "email" ], "type": "js", - "modulePath": "tiktok/unlike.js", - "sourceFile": "tiktok/unlike.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "slock/whoami.js", + "sourceFile": "slock/whoami.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "tiktok", - "name": "unsave", - "description": "Remove a TikTok video from Favorites", + "name": "comment", + "description": "Post a comment on a TikTok video", "access": "write", "domain": "www.tiktok.com", "strategy": "cookie", @@ -8349,167 +6009,138 @@ "type": "str", "required": true, "positional": true, - "help": "TikTok video URL" + "help": "TikTok video URL (https://www.tiktok.com/@user/video/)" + }, + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Comment text (≤150 chars)" } ], "columns": [ - "status", - "url" + "url", + "text", + "result" ], - "type": "js", - "modulePath": "tiktok/unsave.js", - "sourceFile": "tiktok/unsave.js", + "type": "js", + "modulePath": "tiktok/comment.js", + "sourceFile": "tiktok/comment.js", "navigateBefore": "https://www.tiktok.com" }, { "site": "tiktok", - "name": "user", - "description": "Get recent videos from a TikTok user via page-context APIs", + "name": "creator-videos", + "description": "TikTok Studio creator content list (views/likes/comments/saves/shares)", "access": "read", "domain": "www.tiktok.com", "strategy": "cookie", "browser": true, "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok username (without @)" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of videos to return (max 120)" + "help": "Number of creator videos to return (max 250)" + }, + { + "name": "cursor", + "type": "string", + "default": "0", + "required": false, + "help": "Non-negative TikTok Studio pagination cursor" } ], "columns": [ - "index", - "id", - "source", - "author", - "url", - "cover", + "video_id", "title", - "desc", - "plays", + "date", + "views", "likes", "comments", + "saves", "shares", - "createTime" + "url" ], "type": "js", - "modulePath": "tiktok/user.js", - "sourceFile": "tiktok/user.js", - "navigateBefore": "https://www.tiktok.com" + "modulePath": "tiktok/creator-videos.js", + "sourceFile": "tiktok/creator-videos.js", + "navigateBefore": "https://www.tiktok.com/tiktokstudio/content" }, { "site": "tiktok", - "name": "whoami", - "description": "Show the current logged-in tiktok account", - "access": "read", - "domain": "tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "sec_uid", - "username", - "nickname" - ], - "type": "js", - "modulePath": "tiktok/auth.js", - "sourceFile": "tiktok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "trip", - "name": "attraction", - "description": "Search Trip.com attractions and experiences by destination keyword", + "name": "explore", + "description": "Get trending TikTok videos from the recommend feed via page-context APIs", "access": "read", - "domain": "trip.com", + "domain": "www.tiktok.com", "strategy": "cookie", "browser": true, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Destination or attraction keyword (e.g. Tokyo / Paris / Louvre)" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of results (1-50)" + "help": "Number of videos to return (max 120)" } ], "columns": [ - "rank", - "name", - "rating", - "reviews", - "booked", - "price", - "currency", - "url" + "index", + "id", + "author", + "url", + "cover", + "title", + "desc", + "plays", + "likes", + "comments", + "shares", + "createTime" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "trip/attraction.js", - "sourceFile": "trip/attraction.js", - "navigateBefore": false + "modulePath": "tiktok/explore.js", + "sourceFile": "tiktok/explore.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trip", - "name": "car", - "description": "List Trip.com car-rental vehicles for a city (category, model, seats, daily price)", - "access": "read", - "domain": "trip.com", + "site": "tiktok", + "name": "follow", + "description": "Follow a TikTok user by username", + "access": "write", + "domain": "www.tiktok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "city", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Numeric Trip.com carhire city id (discover via the carhire search box)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of vehicles (1-50)" + "help": "TikTok username (without @)" } ], "columns": [ - "rank", - "category", - "vehicle", - "seats", - "price", - "currency", - "url" + "username", + "url", + "result" ], "type": "js", - "modulePath": "trip/car.js", - "sourceFile": "trip/car.js", - "navigateBefore": false + "modulePath": "tiktok/follow.js", + "sourceFile": "tiktok/follow.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trip", - "name": "deals", - "description": "List Trip.com live promotions from the Top Deals hub: campaign title, offer, discount, and link", + "site": "tiktok", + "name": "following", + "description": "List accounts the logged-in user follows on TikTok via page-context APIs", "access": "read", - "domain": "trip.com", + "domain": "www.tiktok.com", "strategy": "cookie", "browser": true, "args": [ @@ -8518,979 +6149,1080 @@ "type": "int", "default": 20, "required": false, - "help": "Number of deals (1-50)" + "help": "Number of accounts (max 200)" } ], "columns": [ - "rank", - "title", - "offer", - "discount", + "index", + "username", + "name", + "secUid", + "verified", + "followers", + "following", "url" ], "type": "js", - "modulePath": "trip/deals.js", - "sourceFile": "trip/deals.js", - "navigateBefore": false + "modulePath": "tiktok/following.js", + "sourceFile": "tiktok/following.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trip", - "name": "flight", - "description": "Search Trip.com one-way flights by IATA route + departure date", + "site": "tiktok", + "name": "friends", + "description": "Get TikTok friend / who-to-follow suggestions via page-context APIs", "access": "read", - "domain": "trip.com", + "domain": "www.tiktok.com", "strategy": "cookie", "browser": true, "args": [ - { - "name": "from", - "type": "str", - "required": true, - "positional": true, - "help": "Departure IATA code (e.g. LON / LHR)" - }, - { - "name": "to", - "type": "str", - "required": true, - "positional": true, - "help": "Arrival IATA code (e.g. NYC / JFK)" - }, - { - "name": "date", - "type": "str", - "required": true, - "help": "Departure date (YYYY-MM-DD)" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of flights (1-50)" + "help": "Number of suggestions (max 100)" } ], "columns": [ - "rank", - "airline", - "departureTime", - "departureAirport", - "arrivalTime", - "arrivalAirport", - "duration", - "stops", - "price", - "currency", + "index", + "username", + "name", + "secUid", + "verified", + "followers", + "following", "url" ], "type": "js", - "modulePath": "trip/flight.js", - "sourceFile": "trip/flight.js", - "navigateBefore": false + "modulePath": "tiktok/friends.js", + "sourceFile": "tiktok/friends.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trip", - "name": "flight-round", - "description": "Search Trip.com round-trip flights by IATA route + depart/return dates", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "from", - "type": "str", - "required": true, - "positional": true, - "help": "Departure IATA code (e.g. LON / LHR)" - }, - { - "name": "to", - "type": "str", - "required": true, - "positional": true, - "help": "Arrival IATA code (e.g. NYC / JFK)" - }, - { - "name": "depart", - "type": "str", - "required": true, - "help": "Outbound date (YYYY-MM-DD)" - }, + "site": "tiktok", + "name": "like", + "description": "Like a TikTok video", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "return", + "name": "url", "type": "str", "required": true, - "help": "Return date (YYYY-MM-DD)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of flights (1-50)" + "positional": true, + "help": "TikTok video URL" } ], "columns": [ - "rank", - "airline", - "departureTime", - "departureAirport", - "arrivalTime", - "arrivalAirport", - "duration", - "stops", - "price", - "currency", + "status", + "likes", "url" ], "type": "js", - "modulePath": "trip/flight-round.js", - "sourceFile": "trip/flight-round.js", - "navigateBefore": false + "modulePath": "tiktok/like.js", + "sourceFile": "tiktok/like.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trip", - "name": "hotel", - "description": "Show a Trip.com hotel detail by id (rating breakdown, amenities, check-in/out policy)", + "site": "tiktok", + "name": "live", + "description": "Browse TikTok live streams via page-context APIs", "access": "read", - "domain": "trip.com", + "domain": "www.tiktok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Numeric Trip.com hotel id (discover via the hotels list; e.g. 715233)" + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of streams (max 60)" } ], "columns": [ - "hotelId", + "index", + "streamer", "name", - "enName", - "star", - "score", - "scoreLabel", - "reviewCount", - "ratingBreakdown", - "facilities", - "checkInOut", - "cityName", - "address", - "lat", - "lon", + "title", + "viewers", + "likes", + "secUid", "url" ], "type": "js", - "modulePath": "trip/hotel.js", - "sourceFile": "trip/hotel.js", - "navigateBefore": false + "modulePath": "tiktok/live.js", + "sourceFile": "tiktok/live.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trip", - "name": "hotel-search", - "description": "List Trip.com hotels for a city id + check-in/out date range", + "site": "tiktok", + "name": "login", + "description": "Open tiktok login", + "access": "write", + "domain": "tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "sec_uid", + "username", + "nickname", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "tiktok/auth.js", + "sourceFile": "tiktok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "tiktok", + "name": "notifications", + "description": "Read TikTok inbox notifications (likes, comments, mentions, followers) via page-context APIs", "access": "read", - "domain": "trip.com", + "domain": "www.tiktok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "city", - "type": "str", - "required": true, - "positional": true, - "help": "Numeric Trip.com city id (discover via the hotels search box; e.g. 338 for London)" - }, - { - "name": "checkin", - "type": "str", - "required": true, - "help": "Check-in date (YYYY-MM-DD)" + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Number of notifications (max 100)" }, { - "name": "checkout", + "name": "type", "type": "str", - "required": true, - "help": "Check-out date (YYYY-MM-DD)" - }, - { - "name": "limit", - "type": "int", - "default": 20, + "default": "all", "required": false, - "help": "Number of hotels (1-50)" + "help": "Notification type", + "choices": [ + "all", + "likes", + "comments", + "mentions", + "followers" + ] } ], "columns": [ - "rank", - "name", - "score", - "reviewLabel", - "reviews", - "location", - "room", - "price", - "currency", - "url" - ], - "tags": [ - "search" + "index", + "id", + "from", + "text", + "createTime" ], "type": "js", - "modulePath": "trip/hotel-search.js", - "sourceFile": "trip/hotel-search.js", - "navigateBefore": false + "modulePath": "tiktok/notifications.js", + "sourceFile": "tiktok/notifications.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trip", - "name": "package", - "description": "Search Trip.com flight+hotel packages by route + dates; lists the package flight options priced at the bundle rate", + "site": "tiktok", + "name": "profile", + "description": "Get TikTok user profile info", "access": "read", - "domain": "trip.com", - "strategy": "public", - "browser": false, + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "from", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Origin city keyword (e.g. Seoul / London / Bangkok)" - }, + "help": "TikTok username (without @)" + } + ], + "columns": [ + "username", + "name", + "followers", + "following", + "likes", + "videos", + "verified", + "bio" + ], + "type": "js", + "modulePath": "tiktok/profile.js", + "sourceFile": "tiktok/profile.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "save", + "description": "Add a TikTok video to Favorites", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "to", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Destination city keyword (e.g. Tokyo / Paris / Singapore)" - }, - { - "name": "depart", - "type": "str", - "required": true, - "help": "Outbound date (YYYY-MM-DD)" - }, - { - "name": "return", - "type": "str", - "required": true, - "help": "Return date (YYYY-MM-DD)" - }, - { - "name": "adults", - "type": "int", - "default": 2, - "required": false, - "help": "Number of adults (1-9, default 2)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of packages (1-50)" + "help": "TikTok video URL" } ], "columns": [ - "rank", - "airline", - "flightNo", - "from", - "to", - "departure", - "arrival", - "stops", - "price", - "currency" + "status", + "url" ], "type": "js", - "modulePath": "trip/package.js", - "sourceFile": "trip/package.js" + "modulePath": "tiktok/save.js", + "sourceFile": "tiktok/save.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trip", + "site": "tiktok", "name": "search", - "description": "Suggest Trip.com destinations (cities, airports) for a keyword; resolves the ids the other commands take", + "description": "Search TikTok videos", "access": "read", - "domain": "trip.com", - "strategy": "public", - "browser": false, + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "query", "type": "str", "required": true, "positional": true, - "help": "Destination keyword (e.g. Tokyo / Bali / London)" + "help": "Search query" }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Number of suggestions (1-50)" + "help": "Number of results" } ], "columns": [ "rank", - "name", - "type", - "cityId", - "airportCode", - "province", - "country" + "desc", + "author", + "url", + "plays", + "likes", + "comments", + "shares" ], "tags": [ "search" ], "type": "js", - "modulePath": "trip/search.js", - "sourceFile": "trip/search.js" + "modulePath": "tiktok/search.js", + "sourceFile": "tiktok/search.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trip", - "name": "tour", - "description": "Search Trip.com tour packages by destination keyword (private or group tours)", - "access": "read", - "domain": "trip.com", + "site": "tiktok", + "name": "unfollow", + "description": "Unfollow a TikTok user by username", + "access": "write", + "domain": "www.tiktok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Destination or tour keyword (e.g. Tokyo / Kyoto / Bali)" - }, - { - "name": "type", - "type": "str", - "default": "private", - "required": false, - "help": "Tour line: private or group (default private)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of tours (1-50)" + "help": "TikTok username (without @)" } ], "columns": [ - "rank", - "name", - "type", - "rating", - "reviews", - "price", - "currency", - "url" + "username", + "url", + "result" ], "type": "js", - "modulePath": "trip/tour.js", - "sourceFile": "trip/tour.js", - "navigateBefore": false + "modulePath": "tiktok/unfollow.js", + "sourceFile": "tiktok/unfollow.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "trip", - "name": "train", - "description": "Show a Trip.com train route timetable (departure/arrival times, duration, changes)", - "access": "read", - "domain": "trip.com", + "site": "tiktok", + "name": "unlike", + "description": "Unlike a TikTok video", + "access": "write", + "domain": "www.tiktok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "from", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Departure city (e.g. London / Paris / Shanghai)" - }, + "help": "TikTok video URL" + } + ], + "columns": [ + "status", + "likes", + "url" + ], + "type": "js", + "modulePath": "tiktok/unlike.js", + "sourceFile": "tiktok/unlike.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "unsave", + "description": "Remove a TikTok video from Favorites", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "to", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Arrival city (e.g. Manchester / Lyon / Beijing)" - }, + "help": "TikTok video URL" + } + ], + "columns": [ + "status", + "url" + ], + "type": "js", + "modulePath": "tiktok/unsave.js", + "sourceFile": "tiktok/unsave.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "user", + "description": "Get recent videos from a TikTok user via page-context APIs", + "access": "read", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "country", + "name": "username", "type": "str", "required": true, - "help": "Route country slug (e.g. uk / france / italy / spain / germany / china)" + "positional": true, + "help": "TikTok username (without @)" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of journeys (1-50)" + "help": "Number of videos to return (max 120)" } ], "columns": [ - "rank", - "departureTime", - "fromStation", - "arrivalTime", - "toStation", - "duration", - "changes", - "url" + "index", + "id", + "source", + "author", + "url", + "cover", + "title", + "desc", + "plays", + "likes", + "comments", + "shares", + "createTime" ], "type": "js", - "modulePath": "trip/train.js", - "sourceFile": "trip/train.js", - "navigateBefore": false + "modulePath": "tiktok/user.js", + "sourceFile": "tiktok/user.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "whoami", + "description": "Show the current logged-in tiktok account", + "access": "read", + "domain": "tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "sec_uid", + "username", + "nickname" + ], + "type": "js", + "modulePath": "tiktok/auth.js", + "sourceFile": "tiktok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "trip", - "name": "transfer", - "description": "List Trip.com airport-transfer vehicles for a city + airport (type, seats, from-price)", + "name": "attraction", + "description": "Search Trip.com attractions and experiences by destination keyword", "access": "read", "domain": "trip.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "city", - "type": "str", - "required": true, - "positional": true, - "help": "Airport city (e.g. Bangkok / Beijing / Da Nang)" - }, - { - "name": "airport", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "3-letter airport IATA code (e.g. DMK / PKX / DAD)" + "help": "Destination or attraction keyword (e.g. Tokyo / Paris / Louvre)" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of vehicles (1-50)" + "help": "Number of results (1-50)" } ], "columns": [ "rank", - "type", - "passengers", - "luggage", + "name", + "rating", + "reviews", + "booked", "price", "currency", "url" ], "type": "js", - "modulePath": "trip/transfer.js", - "sourceFile": "trip/transfer.js", + "modulePath": "trip/attraction.js", + "sourceFile": "trip/attraction.js", "navigateBefore": false }, { - "site": "twitter", - "name": "accept", - "description": "Auto-accept DM requests containing specific keywords", - "access": "write", - "domain": "x.com", - "strategy": "ui", + "site": "trip", + "name": "car", + "description": "List Trip.com car-rental vehicles for a city (category, model, seats, daily price)", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "query", - "type": "string", + "name": "city", + "type": "str", "required": true, "positional": true, - "help": "Keywords to match (comma-separated for OR, e.g. \"invoice,urgent\")" - }, - { - "name": "max", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of requests to accept (default: 20)" + "help": "Numeric Trip.com carhire city id (discover via the carhire search box)" }, { - "name": "timeout", + "name": "limit", "type": "int", - "default": 600, + "default": 20, "required": false, - "help": "Max seconds for the overall command (default: 600 — batch op)" + "help": "Number of vehicles (1-50)" } ], "columns": [ - "index", - "status", - "user", - "message" + "rank", + "category", + "vehicle", + "seats", + "price", + "currency", + "url" ], "type": "js", - "modulePath": "twitter/accept.js", - "sourceFile": "twitter/accept.js", - "navigateBefore": true + "modulePath": "trip/car.js", + "sourceFile": "trip/car.js", + "navigateBefore": false }, { - "site": "twitter", - "name": "article", - "description": "Fetch a Twitter Article (long-form content) and export as Markdown", + "site": "trip", + "name": "deals", + "description": "List Trip.com live promotions from the Top Deals hub: campaign title, offer, discount, and link", "access": "read", - "domain": "x.com", + "domain": "trip.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "tweet-id", - "type": "string", - "required": true, - "positional": true, - "help": "Tweet ID or URL containing the article" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of deals (1-50)" } ], "columns": [ + "rank", "title", - "author", - "content", + "offer", + "discount", "url" ], "type": "js", - "modulePath": "twitter/article.js", - "sourceFile": "twitter/article.js", - "navigateBefore": "https://x.com" + "modulePath": "trip/deals.js", + "sourceFile": "trip/deals.js", + "navigateBefore": false }, { - "site": "twitter", - "name": "block", - "description": "Block a Twitter user", - "access": "write", - "domain": "x.com", - "strategy": "ui", + "site": "trip", + "name": "flight", + "description": "Search Trip.com one-way flights by IATA route + departure date", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "username", - "type": "string", + "name": "from", + "type": "str", "required": true, "positional": true, - "help": "Twitter screen name (without @)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/block.js", - "sourceFile": "twitter/block.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "bookmark", - "description": "Bookmark a tweet", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ + "help": "Departure IATA code (e.g. LON / LHR)" + }, { - "name": "url", - "type": "string", + "name": "to", + "type": "str", "required": true, "positional": true, - "help": "Tweet URL to bookmark" + "help": "Arrival IATA code (e.g. NYC / JFK)" + }, + { + "name": "date", + "type": "str", + "required": true, + "help": "Departure date (YYYY-MM-DD)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of flights (1-50)" } ], "columns": [ - "status", - "message" + "rank", + "airline", + "departureTime", + "departureAirport", + "arrivalTime", + "arrivalAirport", + "duration", + "stops", + "price", + "currency", + "url" ], "type": "js", - "modulePath": "twitter/bookmark.js", - "sourceFile": "twitter/bookmark.js", - "navigateBefore": true + "modulePath": "trip/flight.js", + "sourceFile": "trip/flight.js", + "navigateBefore": false }, { - "site": "twitter", - "name": "bookmark-folder", - "description": "Read the tweets inside a single Twitter/X bookmark folder. Get the folder id from `webcmd twitter bookmark-folders`.", + "site": "trip", + "name": "flight-round", + "description": "Search Trip.com round-trip flights by IATA route + depart/return dates", "access": "read", - "domain": "x.com", + "domain": "trip.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "folder-id", - "type": "string", + "name": "from", + "type": "str", "required": true, "positional": true, - "help": "Folder id from `webcmd twitter bookmark-folders`." + "help": "Departure IATA code (e.g. LON / LHR)" }, { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of bookmarks to return (default 20)." + "name": "to", + "type": "str", + "required": true, + "positional": true, + "help": "Arrival IATA code (e.g. NYC / JFK)" }, { - "name": "top-by-engagement", + "name": "depart", + "type": "str", + "required": true, + "help": "Outbound date (YYYY-MM-DD)" + }, + { + "name": "return", + "type": "str", + "required": true, + "help": "Return date (YYYY-MM-DD)" + }, + { + "name": "limit", "type": "int", - "default": 0, + "default": 20, "required": false, - "help": "When set to N>0, re-rank the folder by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (saved-time) ordering." + "help": "Number of flights (1-50)" } ], "columns": [ - "id", - "author", - "text", - "likes", - "retweets", - "bookmarks", - "created_at", - "url", - "has_media", - "media_urls", - "media_posters" + "rank", + "airline", + "departureTime", + "departureAirport", + "arrivalTime", + "arrivalAirport", + "duration", + "stops", + "price", + "currency", + "url" ], "type": "js", - "modulePath": "twitter/bookmark-folder.js", - "sourceFile": "twitter/bookmark-folder.js", - "navigateBefore": "https://x.com" + "modulePath": "trip/flight-round.js", + "sourceFile": "trip/flight-round.js", + "navigateBefore": false }, { - "site": "twitter", - "name": "bookmark-folders", - "description": "List your Twitter/X bookmark folders (the user-created collections under Bookmarks). Returns folder id, name, item count, and created_at.", + "site": "trip", + "name": "hotel", + "description": "Show a Trip.com hotel detail by id (rating breakdown, amenities, check-in/out policy)", "access": "read", - "domain": "x.com", + "domain": "trip.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Numeric Trip.com hotel id (discover via the hotels list; e.g. 715233)" + } + ], "columns": [ - "id", + "hotelId", "name", - "items", - "created_at" + "enName", + "star", + "score", + "scoreLabel", + "reviewCount", + "ratingBreakdown", + "facilities", + "checkInOut", + "cityName", + "address", + "lat", + "lon", + "url" ], "type": "js", - "modulePath": "twitter/bookmark-folders.js", - "sourceFile": "twitter/bookmark-folders.js", - "navigateBefore": "https://x.com" + "modulePath": "trip/hotel.js", + "sourceFile": "trip/hotel.js", + "navigateBefore": false }, { - "site": "twitter", - "name": "bookmarks", - "description": "Fetch your Twitter/X bookmarks (the logged-in user's saved tweets, newest first)", + "site": "trip", + "name": "hotel-search", + "description": "List Trip.com hotels for a city id + check-in/out date range", "access": "read", - "domain": "x.com", + "domain": "trip.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of bookmarks to return (default 20)." + "name": "city", + "type": "str", + "required": true, + "positional": true, + "help": "Numeric Trip.com city id (discover via the hotels search box; e.g. 338 for London)" + }, + { + "name": "checkin", + "type": "str", + "required": true, + "help": "Check-in date (YYYY-MM-DD)" + }, + { + "name": "checkout", + "type": "str", + "required": true, + "help": "Check-out date (YYYY-MM-DD)" }, { - "name": "top-by-engagement", + "name": "limit", "type": "int", - "default": 0, + "default": 20, "required": false, - "help": "When set to N>0, re-rank the bookmarks by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (saved-time) ordering." + "help": "Number of hotels (1-50)" } ], "columns": [ - "id", - "author", - "text", - "likes", - "retweets", - "bookmarks", - "created_at", - "url", - "has_media", - "media_urls", - "media_posters" + "rank", + "name", + "score", + "reviewLabel", + "reviews", + "location", + "room", + "price", + "currency", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "twitter/bookmarks.js", - "sourceFile": "twitter/bookmarks.js", - "navigateBefore": "https://x.com" + "modulePath": "trip/hotel-search.js", + "sourceFile": "trip/hotel-search.js", + "navigateBefore": false }, { - "site": "twitter", - "name": "delete", - "description": "Delete a specific tweet by URL", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, + "site": "trip", + "name": "package", + "description": "Search Trip.com flight+hotel packages by route + dates; lists the package flight options priced at the bundle rate", + "access": "read", + "domain": "trip.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "url", - "type": "string", + "name": "from", + "type": "str", "required": true, "positional": true, - "help": "The URL of the tweet to delete" + "help": "Origin city keyword (e.g. Seoul / London / Bangkok)" + }, + { + "name": "to", + "type": "str", + "required": true, + "positional": true, + "help": "Destination city keyword (e.g. Tokyo / Paris / Singapore)" + }, + { + "name": "depart", + "type": "str", + "required": true, + "help": "Outbound date (YYYY-MM-DD)" + }, + { + "name": "return", + "type": "str", + "required": true, + "help": "Return date (YYYY-MM-DD)" + }, + { + "name": "adults", + "type": "int", + "default": 2, + "required": false, + "help": "Number of adults (1-9, default 2)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of packages (1-50)" } ], "columns": [ - "status", - "message" + "rank", + "airline", + "flightNo", + "from", + "to", + "departure", + "arrival", + "stops", + "price", + "currency" ], "type": "js", - "modulePath": "twitter/delete.js", - "sourceFile": "twitter/delete.js", - "navigateBefore": true + "modulePath": "trip/package.js", + "sourceFile": "trip/package.js" }, { - "site": "twitter", - "name": "device-follow", - "description": "Read the /i/timeline device-follow notification stream (tweets aggregated under a bell-icon \"new posts from @userA and N others\" notification)", + "site": "trip", + "name": "search", + "description": "Suggest Trip.com destinations (cities, airports) for a keyword; resolves the ids the other commands take", "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, + "domain": "trip.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of tweets to return (1-200, default 20)" + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Destination keyword (e.g. Tokyo / Bali / London)" }, { - "name": "top-by-engagement", + "name": "limit", "type": "int", - "default": 0, + "default": 20, "required": false, - "help": "When set to N>0, re-rank by weighted engagement and return the top N. Default 0 keeps upstream ordering." + "help": "Number of suggestions (1-50)" } ], "columns": [ - "id", - "author", - "text", - "likes", - "retweets", - "replies", - "views", - "created_at", - "url" + "rank", + "name", + "type", + "cityId", + "airportCode", + "province", + "country" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "twitter/device-follow.js", - "sourceFile": "twitter/device-follow.js", - "navigateBefore": "https://x.com" + "modulePath": "trip/search.js", + "sourceFile": "trip/search.js" }, { - "site": "twitter", - "name": "download", - "description": "Download Twitter/X media (images and videos). Provide either to fetch every media item from their profile via the GraphQL UserMedia endpoint with cursor pagination, or --tweet-url to download a single tweet.", + "site": "trip", + "name": "tour", + "description": "Search Trip.com tour packages by destination keyword (private or group tours)", "access": "read", - "domain": "x.com", + "domain": "trip.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "query", "type": "str", - "required": false, + "required": true, "positional": true, - "help": "Twitter username (with or without @) to scan their profile media. Either or --tweet-url is required." + "help": "Destination or tour keyword (e.g. Tokyo / Kyoto / Bali)" }, { - "name": "tweet-url", + "name": "type", "type": "str", + "default": "private", "required": false, - "help": "Single tweet URL to download. Use this OR , not both required at once." + "help": "Tour line: private or group (default private)" }, { "name": "limit", "type": "int", - "default": 10, - "required": false, - "help": "Maximum number of media items to download when scanning a profile (default 10). Ignored when --tweet-url is used." - }, - { - "name": "output", - "type": "str", - "default": "./twitter-downloads", + "default": 20, "required": false, - "help": "Output directory (default ./twitter-downloads). A per-source subdir is created inside.", - "file": { - "direction": "output", - "pathKind": "directory", - "multiple": false - } + "help": "Number of tours (1-50)" } ], "columns": [ - "index", - "tweet_id", - "url", + "rank", + "name", "type", - "status", - "size" + "rating", + "reviews", + "price", + "currency", + "url" ], "type": "js", - "modulePath": "twitter/download.js", - "sourceFile": "twitter/download.js", - "navigateBefore": "https://x.com" + "modulePath": "trip/tour.js", + "sourceFile": "trip/tour.js", + "navigateBefore": false }, { - "site": "twitter", - "name": "follow", - "description": "Follow a Twitter user", - "access": "write", - "domain": "x.com", - "strategy": "ui", + "site": "trip", + "name": "train", + "description": "Show a Trip.com train route timetable (departure/arrival times, duration, changes)", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "username", - "type": "string", + "name": "from", + "type": "str", "required": true, "positional": true, - "help": "Twitter screen name (without @)" + "help": "Departure city (e.g. London / Paris / Shanghai)" + }, + { + "name": "to", + "type": "str", + "required": true, + "positional": true, + "help": "Arrival city (e.g. Manchester / Lyon / Beijing)" + }, + { + "name": "country", + "type": "str", + "required": true, + "help": "Route country slug (e.g. uk / france / italy / spain / germany / china)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of journeys (1-50)" } ], "columns": [ - "status", - "message" + "rank", + "departureTime", + "fromStation", + "arrivalTime", + "toStation", + "duration", + "changes", + "url" ], "type": "js", - "modulePath": "twitter/follow.js", - "sourceFile": "twitter/follow.js", - "navigateBefore": true + "modulePath": "trip/train.js", + "sourceFile": "trip/train.js", + "navigateBefore": false }, { - "site": "twitter", - "name": "follow-batch", - "description": "Follow multiple Twitter/X users from a comma-separated username list", - "access": "write", - "domain": "x.com", - "strategy": "ui", + "site": "trip", + "name": "transfer", + "description": "List Trip.com airport-transfer vehicles for a city + airport (type, seats, from-price)", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "usernames", - "type": "string", + "name": "city", + "type": "str", "required": true, "positional": true, - "help": "Comma-separated Twitter/X screen names, with or without @" + "help": "Airport city (e.g. Bangkok / Beijing / Da Nang)" }, { - "name": "delay-ms", + "name": "airport", + "type": "str", + "required": true, + "positional": true, + "help": "3-letter airport IATA code (e.g. DMK / PKX / DAD)" + }, + { + "name": "limit", "type": "int", - "default": 3000, + "default": 20, "required": false, - "help": "Delay between follow attempts in milliseconds" + "help": "Number of vehicles (1-50)" } ], "columns": [ - "username", - "status", - "message" + "rank", + "type", + "passengers", + "luggage", + "price", + "currency", + "url" ], "type": "js", - "modulePath": "twitter/follow-batch.js", - "sourceFile": "twitter/follow-batch.js", - "navigateBefore": true + "modulePath": "trip/transfer.js", + "sourceFile": "trip/transfer.js", + "navigateBefore": false }, { "site": "twitter", - "name": "followers", - "description": "Get accounts following a Twitter/X user (defaults to the logged-in user when no user is given)", - "access": "read", + "name": "accept", + "description": "Auto-accept DM requests containing specific keywords", + "access": "write", "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "user", + "name": "query", "type": "string", - "required": false, + "required": true, "positional": true, - "help": "Twitter/X handle (with or without @). Omit to fetch followers of the currently logged-in account." + "help": "Keywords to match (comma-separated for OR, e.g. \"invoice,urgent\")" }, { - "name": "limit", + "name": "max", "type": "int", - "default": 50, + "default": 20, "required": false, - "help": "Maximum number of follower rows to return (default 50). Must be a positive integer." + "help": "Maximum number of requests to accept (default: 20)" + }, + { + "name": "timeout", + "type": "int", + "default": 600, + "required": false, + "help": "Max seconds for the overall command (default: 600 — batch op)" } ], "columns": [ - "screen_name", - "name", - "bio" + "index", + "status", + "user", + "message" ], "type": "js", - "modulePath": "twitter/followers.js", - "sourceFile": "twitter/followers.js", + "modulePath": "twitter/accept.js", + "sourceFile": "twitter/accept.js", "navigateBefore": true }, { "site": "twitter", - "name": "following", - "description": "Get accounts a Twitter/X user is following (defaults to the logged-in user when no user is given)", + "name": "article", + "description": "Fetch a Twitter Article (long-form content) and export as Markdown", "access": "read", "domain": "x.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "user", + "name": "tweet-id", "type": "string", - "required": false, + "required": true, "positional": true, - "help": "Twitter/X handle (with or without @). Omit to fetch the accounts the currently logged-in user follows." - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Maximum number of following rows to return (default 50). Must be a positive integer." + "help": "Tweet ID or URL containing the article" } ], "columns": [ - "screen_name", - "name", - "bio", - "followers" + "title", + "author", + "content", + "url" ], "type": "js", - "modulePath": "twitter/following.js", - "sourceFile": "twitter/following.js", + "modulePath": "twitter/article.js", + "sourceFile": "twitter/article.js", "navigateBefore": "https://x.com" }, { "site": "twitter", - "name": "hide-reply", - "description": "Hide a reply on your tweet (useful for hiding bot/spam replies)", + "name": "block", + "description": "Block a Twitter user", "access": "write", "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "url", + "name": "username", "type": "string", "required": true, "positional": true, - "help": "The URL of the reply tweet to hide" + "help": "Twitter screen name (without @)" } ], "columns": [ @@ -9498,14 +7230,14 @@ "message" ], "type": "js", - "modulePath": "twitter/hide-reply.js", - "sourceFile": "twitter/hide-reply.js", + "modulePath": "twitter/block.js", + "sourceFile": "twitter/block.js", "navigateBefore": true }, { "site": "twitter", - "name": "like", - "description": "Like a specific tweet", + "name": "bookmark", + "description": "Bookmark a tweet", "access": "write", "domain": "x.com", "strategy": "ui", @@ -9516,7 +7248,7 @@ "type": "string", "required": true, "positional": true, - "help": "The URL of the tweet to like" + "help": "Tweet URL to bookmark" } ], "columns": [ @@ -9524,48 +7256,48 @@ "message" ], "type": "js", - "modulePath": "twitter/like.js", - "sourceFile": "twitter/like.js", + "modulePath": "twitter/bookmark.js", + "sourceFile": "twitter/bookmark.js", "navigateBefore": true }, { "site": "twitter", - "name": "likes", - "description": "Fetch liked tweets of a Twitter user (defaults to the logged-in user when no username is given)", + "name": "bookmark-folder", + "description": "Read the tweets inside a single Twitter/X bookmark folder. Get the folder id from `webcmd twitter bookmark-folders`.", "access": "read", "domain": "x.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "folder-id", "type": "string", - "required": false, + "required": true, "positional": true, - "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." + "help": "Folder id from `webcmd twitter bookmark-folders`." }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Maximum number of liked tweets to return (default 20)." + "help": "Maximum number of bookmarks to return (default 20)." }, { "name": "top-by-engagement", "type": "int", "default": 0, "required": false, - "help": "When set to N>0, re-rank the liked tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (recency) ordering." + "help": "When set to N>0, re-rank the folder by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (saved-time) ordering." } ], "columns": [ "id", "author", - "name", "text", "likes", "retweets", + "bookmarks", "created_at", "url", "has_media", @@ -9573,454 +7305,378 @@ "media_posters" ], "type": "js", - "modulePath": "twitter/likes.js", - "sourceFile": "twitter/likes.js", + "modulePath": "twitter/bookmark-folder.js", + "sourceFile": "twitter/bookmark-folder.js", "navigateBefore": "https://x.com" }, { "site": "twitter", - "name": "list-add", - "description": "Add a user to a Twitter/X list you own (no-op if already a member)", - "access": "write", + "name": "bookmark-folders", + "description": "List your Twitter/X bookmark folders (the user-created collections under Bookmarks). Returns folder id, name, item count, and created_at.", + "access": "read", "domain": "x.com", - "strategy": "ui", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "id", + "name", + "items", + "created_at" + ], + "type": "js", + "modulePath": "twitter/bookmark-folders.js", + "sourceFile": "twitter/bookmark-folders.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "bookmarks", + "description": "Fetch your Twitter/X bookmarks (the logged-in user's saved tweets, newest first)", + "access": "read", + "domain": "x.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum number of bookmarks to return (default 20)." }, { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Twitter/X handle to add (with or without @)" + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank the bookmarks by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (saved-time) ordering." } ], "columns": [ - "listId", - "username", - "userId", - "status", - "message" + "id", + "author", + "text", + "likes", + "retweets", + "bookmarks", + "created_at", + "url", + "has_media", + "media_urls", + "media_posters" ], "type": "js", - "modulePath": "twitter/list-add.js", - "sourceFile": "twitter/list-add.js", - "navigateBefore": true + "modulePath": "twitter/bookmarks.js", + "sourceFile": "twitter/bookmarks.js", + "navigateBefore": "https://x.com" }, { "site": "twitter", - "name": "list-add-batch", - "description": "Add multiple users to a Twitter/X list you own from a comma-separated username list", + "name": "delete", + "description": "Delete a specific tweet by URL", "access": "write", "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" - }, - { - "name": "usernames", + "name": "url", "type": "string", "required": true, "positional": true, - "help": "Comma-separated Twitter/X handles to add (with or without @)" - }, - { - "name": "interval", - "type": "int", - "default": 5, - "required": false, - "help": "Seconds to wait between account additions (default: 5)" - }, - { - "name": "timeout", - "type": "int", - "default": 600, - "required": false, - "help": "Max seconds for the overall batch command (default: 600)" + "help": "The URL of the tweet to delete" } ], "columns": [ - "listId", - "username", - "userId", "status", "message" ], "type": "js", - "modulePath": "twitter/list-add-batch.js", - "sourceFile": "twitter/list-add-batch.js", + "modulePath": "twitter/delete.js", + "sourceFile": "twitter/delete.js", "navigateBefore": true }, { "site": "twitter", - "name": "list-create", - "description": "Create a new Twitter/X list (returns the new list id)", - "access": "write", + "name": "device-follow", + "description": "Read the /i/timeline device-follow notification stream (tweets aggregated under a bell-icon \"new posts from @userA and N others\" notification)", + "access": "read", "domain": "x.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "name", - "type": "string", - "required": true, - "positional": true, - "help": "List name (max 25 chars)" - }, - { - "name": "description", - "type": "string", - "default": "", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Optional list description (max 100 chars)" + "help": "Maximum number of tweets to return (1-200, default 20)" }, { - "name": "mode", - "type": "string", - "default": "public", + "name": "top-by-engagement", + "type": "int", + "default": 0, "required": false, - "help": "public | private" + "help": "When set to N>0, re-rank by weighted engagement and return the top N. Default 0 keeps upstream ordering." } ], "columns": [ "id", - "name", - "description", - "mode", - "status" + "author", + "text", + "likes", + "retweets", + "replies", + "views", + "created_at", + "url" ], "type": "js", - "modulePath": "twitter/list-create.js", - "sourceFile": "twitter/list-create.js", + "modulePath": "twitter/device-follow.js", + "sourceFile": "twitter/device-follow.js", "navigateBefore": "https://x.com" }, { "site": "twitter", - "name": "list-delete", - "description": "Delete a Twitter/X list you own after explicit confirmation", - "access": "write", + "name": "download", + "description": "Download Twitter/X media (images and videos). Provide either to fetch every media item from their profile via the GraphQL UserMedia endpoint with cursor pagination, or --tweet-url to download a single tweet.", + "access": "read", "domain": "x.com", - "strategy": "ui", + "strategy": "cookie", "browser": true, "args": [ { - "name": "listId", - "type": "string", - "required": true, + "name": "username", + "type": "str", + "required": false, "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" + "help": "Twitter username (with or without @) to scan their profile media. Either or --tweet-url is required." }, { - "name": "confirm", - "type": "boolean", - "default": false, + "name": "tweet-url", + "type": "str", "required": false, - "help": "Required. Set --confirm true to delete the list." + "help": "Single tweet URL to download. Use this OR , not both required at once." }, { - "name": "timeout", + "name": "limit", "type": "int", - "default": 300, + "default": 10, "required": false, - "help": "Max seconds for the overall delete command (default: 300)" + "help": "Maximum number of media items to download when scanning a profile (default 10). Ignored when --tweet-url is used." + }, + { + "name": "output", + "type": "str", + "default": "./twitter-downloads", + "required": false, + "help": "Output directory (default ./twitter-downloads). A per-source subdir is created inside.", + "file": { + "direction": "output", + "pathKind": "directory", + "multiple": false + } } ], "columns": [ - "listId", - "name", - "members", + "index", + "tweet_id", + "url", + "type", "status", - "message" + "size" ], "type": "js", - "modulePath": "twitter/list-delete.js", - "sourceFile": "twitter/list-delete.js", - "navigateBefore": true + "modulePath": "twitter/download.js", + "sourceFile": "twitter/download.js", + "navigateBefore": "https://x.com" }, { "site": "twitter", - "name": "list-remove", - "description": "Remove a user from a Twitter/X list you own (toggles via UI; no-op if not currently a member)", + "name": "follow", + "description": "Follow a Twitter user", "access": "write", "domain": "x.com", "strategy": "ui", "browser": true, "args": [ - { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" - }, { "name": "username", "type": "string", "required": true, "positional": true, - "help": "Twitter/X handle to remove (with or without @)" + "help": "Twitter screen name (without @)" } ], "columns": [ - "listId", - "username", - "userId", "status", "message" ], "type": "js", - "modulePath": "twitter/list-remove.js", - "sourceFile": "twitter/list-remove.js", + "modulePath": "twitter/follow.js", + "sourceFile": "twitter/follow.js", "navigateBefore": true }, { "site": "twitter", - "name": "list-remove-batch", - "description": "Remove multiple users from a Twitter/X list you own from a comma-separated username list", + "name": "follow-batch", + "description": "Follow multiple Twitter/X users from a comma-separated username list", "access": "write", "domain": "x.com", "strategy": "ui", "browser": true, "args": [ - { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" - }, { "name": "usernames", "type": "string", "required": true, "positional": true, - "help": "Comma-separated Twitter/X handles to remove (with or without @)" - }, - { - "name": "interval", - "type": "int", - "default": 5, - "required": false, - "help": "Seconds to wait between account removals (default: 5)" + "help": "Comma-separated Twitter/X screen names, with or without @" }, { - "name": "timeout", + "name": "delay-ms", "type": "int", - "default": 600, + "default": 3000, "required": false, - "help": "Max seconds for the overall batch command (default: 600)" + "help": "Delay between follow attempts in milliseconds" } ], "columns": [ - "listId", "username", - "userId", "status", "message" ], "type": "js", - "modulePath": "twitter/list-remove-batch.js", - "sourceFile": "twitter/list-remove-batch.js", + "modulePath": "twitter/follow-batch.js", + "sourceFile": "twitter/follow-batch.js", "navigateBefore": true }, { "site": "twitter", - "name": "list-tweets", - "description": "Fetch tweets from a Twitter/X list timeline", + "name": "followers", + "description": "Get accounts following a Twitter/X user (defaults to the logged-in user when no user is given)", "access": "read", "domain": "x.com", - "strategy": "cookie", + "strategy": "ui", "browser": true, "args": [ { - "name": "listId", + "name": "user", "type": "string", - "required": true, + "required": false, "positional": true, - "help": "Numeric ID of a Twitter/X list (e.g. from `webcmd twitter lists`)" + "help": "Twitter/X handle (with or without @). Omit to fetch followers of the currently logged-in account." }, { "name": "limit", "type": "int", "default": 50, "required": false, - "help": "" - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the list timeline by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the list's native (recency) ordering." + "help": "Maximum number of follower rows to return (default 50). Must be a positive integer." } ], "columns": [ - "id", - "author", - "bio", - "text", - "likes", - "retweets", - "replies", - "created_at", - "url", - "has_media", - "media_urls", - "media_posters", - "card", - "quoted_tweet" + "screen_name", + "name", + "bio" ], "type": "js", - "modulePath": "twitter/list-tweets.js", - "sourceFile": "twitter/list-tweets.js", - "navigateBefore": "https://x.com" + "modulePath": "twitter/followers.js", + "sourceFile": "twitter/followers.js", + "navigateBefore": true }, { "site": "twitter", - "name": "lists", - "description": "Get Twitter/X lists for the logged-in user (owned + subscribed)", + "name": "following", + "description": "Get accounts a Twitter/X user is following (defaults to the logged-in user when no user is given)", "access": "read", "domain": "x.com", "strategy": "cookie", "browser": true, "args": [ + { + "name": "user", + "type": "string", + "required": false, + "positional": true, + "help": "Twitter/X handle (with or without @). Omit to fetch the accounts the currently logged-in user follows." + }, { "name": "limit", "type": "int", "default": 50, "required": false, - "help": "Maximum number of lists to return (default 50)." + "help": "Maximum number of following rows to return (default 50). Must be a positive integer." } ], "columns": [ - "id", - "name", - "members", - "followers", - "mode" - ], - "type": "js", - "modulePath": "twitter/lists.js", - "sourceFile": "twitter/lists.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "login", - "description": "Open twitter login", - "access": "write", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "username", - "url", - "action", - "verify_command" + "screen_name", + "name", + "bio", + "followers" ], "type": "js", - "modulePath": "twitter/auth.js", - "sourceFile": "twitter/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "twitter/following.js", + "sourceFile": "twitter/following.js", + "navigateBefore": "https://x.com" }, { "site": "twitter", - "name": "notifications", - "description": "Get your Twitter/X notifications (the logged-in user's likes/replies/follows feed, newest first)", - "access": "read", + "name": "hide-reply", + "description": "Hide a reply on your tweet (useful for hiding bot/spam replies)", + "access": "write", "domain": "x.com", - "strategy": "intercept", + "strategy": "ui", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of notifications to return (default 20)." + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "The URL of the reply tweet to hide" } ], "columns": [ - "id", - "action", - "author", - "text", - "url" + "status", + "message" ], "type": "js", - "modulePath": "twitter/notifications.js", - "sourceFile": "twitter/notifications.js", + "modulePath": "twitter/hide-reply.js", + "sourceFile": "twitter/hide-reply.js", "navigateBefore": true }, { "site": "twitter", - "name": "post", - "description": "Post a new tweet/thread", + "name": "like", + "description": "Like a specific tweet", "access": "write", "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "text", + "name": "url", "type": "string", "required": true, "positional": true, - "help": "The text content of the tweet" - }, - { - "name": "images", - "type": "string", - "required": false, - "help": "Image paths, comma-separated, max 4 (jpg/png/gif/webp)", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } + "help": "The URL of the tweet to like" } ], "columns": [ "status", - "message", - "text", - "id", - "url" + "message" ], "type": "js", - "modulePath": "twitter/post.js", - "sourceFile": "twitter/post.js", + "modulePath": "twitter/like.js", + "sourceFile": "twitter/like.js", "navigateBefore": true }, { "site": "twitter", - "name": "profile", - "description": "Fetch a Twitter user profile — bio, stats, etc. (defaults to the logged-in user when no username is given)", + "name": "likes", + "description": "Fetch liked tweets of a Twitter user (defaults to the logged-in user when no username is given)", "access": "read", "domain": "x.com", "strategy": "cookie", @@ -10032,341 +7688,313 @@ "required": false, "positional": true, "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum number of liked tweets to return (default 20)." + }, + { + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank the liked tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (recency) ordering." } ], "columns": [ - "screen_name", + "id", + "author", "name", - "bio", - "location", - "url", - "followers", - "following", - "tweets", + "text", "likes", - "verified", - "created_at" + "retweets", + "created_at", + "url", + "has_media", + "media_urls", + "media_posters" ], "type": "js", - "modulePath": "twitter/profile.js", - "sourceFile": "twitter/profile.js", + "modulePath": "twitter/likes.js", + "sourceFile": "twitter/likes.js", "navigateBefore": "https://x.com" }, { "site": "twitter", - "name": "quote", - "description": "Quote-tweet a specific tweet with your own text, optionally with a local or remote image", + "name": "list-add", + "description": "Add a user to a Twitter/X list you own (no-op if already a member)", "access": "write", "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "url", + "name": "listId", "type": "string", "required": true, "positional": true, - "help": "The URL of the tweet to quote" + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" }, { - "name": "text", + "name": "username", "type": "string", "required": true, "positional": true, - "help": "The text content of your quote" - }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Optional local image path to attach to the quote tweet", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - }, - { - "name": "image-url", - "type": "str", - "required": false, - "help": "Optional remote image URL to download and attach to the quote tweet" + "help": "Twitter/X handle to add (with or without @)" } ], "columns": [ + "listId", + "username", + "userId", "status", - "message", - "text" + "message" ], "type": "js", - "modulePath": "twitter/quote.js", - "sourceFile": "twitter/quote.js", + "modulePath": "twitter/list-add.js", + "sourceFile": "twitter/list-add.js", "navigateBefore": true }, { "site": "twitter", - "name": "reply", - "description": "Reply to a specific tweet, optionally with a local or remote image", + "name": "list-add-batch", + "description": "Add multiple users to a Twitter/X list you own from a comma-separated username list", "access": "write", "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "url", + "name": "listId", "type": "string", "required": true, "positional": true, - "help": "The URL of the tweet to reply to" + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" }, { - "name": "text", + "name": "usernames", "type": "string", "required": true, "positional": true, - "help": "The text content of your reply" + "help": "Comma-separated Twitter/X handles to add (with or without @)" }, { - "name": "image", - "type": "str", + "name": "interval", + "type": "int", + "default": 5, "required": false, - "help": "Optional local image path to attach to the reply", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } + "help": "Seconds to wait between account additions (default: 5)" }, { - "name": "image-url", - "type": "str", + "name": "timeout", + "type": "int", + "default": 600, "required": false, - "help": "Optional remote image URL to download and attach to the reply" + "help": "Max seconds for the overall batch command (default: 600)" } ], "columns": [ + "listId", + "username", + "userId", "status", - "message", - "text", - "url" + "message" ], "type": "js", - "modulePath": "twitter/reply.js", - "sourceFile": "twitter/reply.js", + "modulePath": "twitter/list-add-batch.js", + "sourceFile": "twitter/list-add-batch.js", "navigateBefore": true }, { "site": "twitter", - "name": "reply-dm", - "description": "Send a message to recent DM conversations", + "name": "list-create", + "description": "Create a new Twitter/X list (returns the new list id)", + "access": "write", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "name", + "type": "string", + "required": true, + "positional": true, + "help": "List name (max 25 chars)" + }, + { + "name": "description", + "type": "string", + "default": "", + "required": false, + "help": "Optional list description (max 100 chars)" + }, + { + "name": "mode", + "type": "string", + "default": "public", + "required": false, + "help": "public | private" + } + ], + "columns": [ + "id", + "name", + "description", + "mode", + "status" + ], + "type": "js", + "modulePath": "twitter/list-create.js", + "sourceFile": "twitter/list-create.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "list-delete", + "description": "Delete a Twitter/X list you own after explicit confirmation", "access": "write", "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "text", + "name": "listId", "type": "string", "required": true, "positional": true, - "help": "Message text to send (e.g. \"my messaging handle wxkabi\")" - }, - { - "name": "max", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of conversations to reply to (default: 20)" + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" }, { - "name": "skip-replied", + "name": "confirm", "type": "boolean", - "default": true, + "default": false, "required": false, - "help": "Skip conversations where you already sent the same text (default: true)" + "help": "Required. Set --confirm true to delete the list." }, { "name": "timeout", "type": "int", - "default": 600, + "default": 300, "required": false, - "help": "Max seconds for the overall command (default: 600 — batch op)" + "help": "Max seconds for the overall delete command (default: 300)" } ], "columns": [ - "index", + "listId", + "name", + "members", "status", - "user", "message" ], "type": "js", - "modulePath": "twitter/reply-dm.js", - "sourceFile": "twitter/reply-dm.js", + "modulePath": "twitter/list-delete.js", + "sourceFile": "twitter/list-delete.js", "navigateBefore": true }, { "site": "twitter", - "name": "retweet", - "description": "Retweet a specific tweet", + "name": "list-remove", + "description": "Remove a user from a Twitter/X list you own (toggles via UI; no-op if not currently a member)", "access": "write", "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "url", + "name": "listId", "type": "string", "required": true, "positional": true, - "help": "The URL of the tweet to retweet" + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" + }, + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Twitter/X handle to remove (with or without @)" } ], "columns": [ + "listId", + "username", + "userId", "status", "message" ], "type": "js", - "modulePath": "twitter/retweet.js", - "sourceFile": "twitter/retweet.js", + "modulePath": "twitter/list-remove.js", + "sourceFile": "twitter/list-remove.js", "navigateBefore": true }, { "site": "twitter", - "name": "search", - "description": "Search Twitter/X for tweets, with optional --from / --has / --exclude / --product filters mapped to X's search operators", - "access": "read", + "name": "list-remove-batch", + "description": "Remove multiple users from a Twitter/X list you own from a comma-separated username list", + "access": "write", "domain": "x.com", - "strategy": "cookie", + "strategy": "ui", "browser": true, "args": [ { - "name": "query", + "name": "listId", "type": "string", "required": true, "positional": true, - "help": "Search query. Raw X operators (e.g. \"exact phrase\", #tag, OR, lang:en, since:YYYY-MM-DD, from:, since:) are passed through unchanged." - }, - { - "name": "filter", - "type": "string", - "default": "top", - "required": false, - "help": "Legacy alias for --product. Kept for backwards compatibility; if --product is set it wins.", - "choices": [ - "top", - "live" - ] - }, - { - "name": "product", - "type": "string", - "required": false, - "help": "Which X search tab to read: top (default), live (Latest), photos, videos. Maps to the f= URL param.", - "choices": [ - "top", - "live", - "photos", - "videos" - ] - }, - { - "name": "from", - "type": "string", - "required": false, - "help": "Restrict to tweets authored by . Leading @ is stripped. Equivalent to appending `from:` to the query." - }, - { - "name": "has", - "type": "string", - "required": false, - "help": "Restrict to tweets that have media|images|videos|links|replies. Maps to X's `filter:` operator.", - "choices": [ - "media", - "images", - "videos", - "links", - "replies" - ] + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" }, { - "name": "exclude", + "name": "usernames", "type": "string", - "required": false, - "help": "Exclude tweets matching : replies|retweets|media|links. Maps to X's `-filter:` operator (retweets → -filter:nativeretweets).", - "choices": [ - "replies", - "retweets", - "media", - "links" - ] + "required": true, + "positional": true, + "help": "Comma-separated Twitter/X handles to remove (with or without @)" }, { - "name": "limit", + "name": "interval", "type": "int", - "default": 15, + "default": 5, "required": false, - "help": "Maximum number of tweets to return (default 15). Result count after server-side filtering." + "help": "Seconds to wait between account removals (default: 5)" }, { - "name": "top-by-engagement", + "name": "timeout", "type": "int", - "default": 0, + "default": 600, "required": false, - "help": "When set to N>0, re-rank the results by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X's native ordering." + "help": "Max seconds for the overall batch command (default: 600)" } ], "columns": [ - "id", - "author", - "bio", - "text", - "created_at", - "likes", - "views", - "url", - "has_media", - "media_urls", - "media_posters", - "card", - "quoted_tweet" - ], - "tags": [ - "search" + "listId", + "username", + "userId", + "status", + "message" ], "type": "js", - "modulePath": "twitter/search.js", - "sourceFile": "twitter/search.js", - "navigateBefore": "https://x.com" + "modulePath": "twitter/list-remove-batch.js", + "sourceFile": "twitter/list-remove-batch.js", + "navigateBefore": true }, { "site": "twitter", - "name": "thread", - "description": "Get a tweet thread (original + all replies)", + "name": "list-tweets", + "description": "Fetch tweets from a Twitter/X list timeline", "access": "read", "domain": "x.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "tweet-id", + "name": "listId", "type": "string", "required": true, "positional": true, - "help": "Tweet numeric ID (e.g. 1234567890) or full status URL" + "help": "Numeric ID of a Twitter/X list (e.g. from `webcmd twitter lists`)" }, { "name": "limit", @@ -10380,7 +8008,7 @@ "type": "int", "default": 0, "required": false, - "help": "When set to N>0, re-rank the thread by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the conversation's structural ordering." + "help": "When set to N>0, re-rank the list timeline by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the list's native (recency) ordering." } ], "columns": [ @@ -10390,6 +8018,8 @@ "text", "likes", "retweets", + "replies", + "created_at", "url", "has_media", "media_urls", @@ -10398,76 +8028,70 @@ "quoted_tweet" ], "type": "js", - "modulePath": "twitter/thread.js", - "sourceFile": "twitter/thread.js", + "modulePath": "twitter/list-tweets.js", + "sourceFile": "twitter/list-tweets.js", "navigateBefore": "https://x.com" }, { "site": "twitter", - "name": "timeline", - "description": "Fetch the logged-in user's home timeline (for-you algorithmic feed by default; pass --type following for the chronological feed of accounts you follow)", + "name": "lists", + "description": "Get Twitter/X lists for the logged-in user (owned + subscribed)", "access": "read", "domain": "x.com", "strategy": "cookie", "browser": true, "args": [ - { - "name": "type", - "type": "str", - "default": "for-you", - "required": false, - "help": "Which home-timeline feed to read. Default for-you (algorithmic). Use following for the chronological feed of accounts you follow.", - "choices": [ - "for-you", - "following" - ] - }, { "name": "limit", "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of tweets to return (default 20)." - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, + "default": 50, "required": false, - "help": "When set to N>0, re-rank the timeline by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X's native ordering." + "help": "Maximum number of lists to return (default 50)." } ], "columns": [ "id", - "author", - "bio", - "text", - "likes", - "retweets", - "replies", - "quotes", - "bookmarks", - "views", - "created_at", - "url", - "has_media", - "media_urls", - "media_posters", - "card", - "quoted_tweet" + "name", + "members", + "followers", + "mode" ], "type": "js", - "modulePath": "twitter/timeline.js", - "sourceFile": "twitter/timeline.js", + "modulePath": "twitter/lists.js", + "sourceFile": "twitter/lists.js", "navigateBefore": "https://x.com" }, { "site": "twitter", - "name": "trending", - "description": "Twitter/X trending topics", + "name": "login", + "description": "Open twitter login", + "access": "write", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "username", + "url", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "twitter/auth.js", + "sourceFile": "twitter/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "twitter", + "name": "notifications", + "description": "Get your Twitter/X notifications (the logged-in user's likes/replies/follows feed, newest first)", "access": "read", "domain": "x.com", - "strategy": "cookie", + "strategy": "intercept", "browser": true, "args": [ { @@ -10475,108 +8099,108 @@ "type": "int", "default": 20, "required": false, - "help": "Number of trends to show" + "help": "Maximum number of notifications to return (default 20)." } ], "columns": [ - "rank", - "topic", - "category" + "id", + "action", + "author", + "text", + "url" ], "type": "js", - "modulePath": "twitter/trending.js", - "sourceFile": "twitter/trending.js", - "navigateBefore": "https://x.com" + "modulePath": "twitter/notifications.js", + "sourceFile": "twitter/notifications.js", + "navigateBefore": true }, { "site": "twitter", - "name": "tweets", - "description": "Fetch a Twitter user's most recent tweets (chronological, excludes pinned; defaults to the logged-in user when no username is given)", - "access": "read", + "name": "post", + "description": "Post a new tweet/thread", + "access": "write", "domain": "x.com", - "strategy": "cookie", + "strategy": "ui", "browser": true, "args": [ { - "name": "username", + "name": "text", "type": "string", - "required": false, + "required": true, "positional": true, - "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max tweets to return (1-10000; fetched across cursor pages)" - }, - { - "name": "page-delay", - "type": "int", - "default": 2, - "required": false, - "help": "Seconds to wait between paginated timeline requests to reduce rate-limit risk. Use 0 to disable." + "help": "The text content of the tweet" }, { - "name": "top-by-engagement", - "type": "int", - "default": 0, + "name": "images", + "type": "string", "required": false, - "help": "When set to N>0, re-rank the tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the chronological ordering." + "help": "Image paths, comma-separated, max 4 (jpg/png/gif/webp)", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } } ], "columns": [ - "id", - "author", - "created_at", - "is_retweet", + "status", + "message", "text", - "likes", - "retweets", - "replies", - "views", - "url", - "has_media", - "media_urls", - "media_posters", - "quoted_tweet" + "id", + "url" ], "type": "js", - "modulePath": "twitter/tweets.js", - "sourceFile": "twitter/tweets.js", - "navigateBefore": "https://x.com" + "modulePath": "twitter/post.js", + "sourceFile": "twitter/post.js", + "navigateBefore": true }, { "site": "twitter", - "name": "unblock", - "description": "Unblock a Twitter user", - "access": "write", + "name": "profile", + "description": "Fetch a Twitter user profile — bio, stats, etc. (defaults to the logged-in user when no username is given)", + "access": "read", "domain": "x.com", - "strategy": "ui", + "strategy": "cookie", "browser": true, "args": [ { "name": "username", "type": "string", - "required": true, + "required": false, "positional": true, - "help": "Twitter screen name (without @)" + "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." } ], "columns": [ - "status", - "message" + "screen_name", + "name", + "bio", + "location", + "url", + "followers", + "following", + "tweets", + "likes", + "verified", + "created_at" ], "type": "js", - "modulePath": "twitter/unblock.js", - "sourceFile": "twitter/unblock.js", - "navigateBefore": true + "modulePath": "twitter/profile.js", + "sourceFile": "twitter/profile.js", + "navigateBefore": "https://x.com" }, { "site": "twitter", - "name": "unbookmark", - "description": "Remove a tweet from bookmarks", + "name": "quote", + "description": "Quote-tweet a specific tweet with your own text, optionally with a local or remote image", "access": "write", "domain": "x.com", "strategy": "ui", @@ -10587,74 +8211,162 @@ "type": "string", "required": true, "positional": true, - "help": "Tweet URL to unbookmark" + "help": "The URL of the tweet to quote" + }, + { + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "The text content of your quote" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Optional local image path to attach to the quote tweet", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + }, + { + "name": "image-url", + "type": "str", + "required": false, + "help": "Optional remote image URL to download and attach to the quote tweet" } ], "columns": [ "status", - "message" + "message", + "text" ], "type": "js", - "modulePath": "twitter/unbookmark.js", - "sourceFile": "twitter/unbookmark.js", + "modulePath": "twitter/quote.js", + "sourceFile": "twitter/quote.js", "navigateBefore": true }, { "site": "twitter", - "name": "unfollow", - "description": "Unfollow a Twitter user", + "name": "reply", + "description": "Reply to a specific tweet, optionally with a local or remote image", "access": "write", "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "username", + "name": "url", "type": "string", "required": true, "positional": true, - "help": "Twitter screen name (without @)" + "help": "The URL of the tweet to reply to" + }, + { + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "The text content of your reply" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Optional local image path to attach to the reply", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + }, + { + "name": "image-url", + "type": "str", + "required": false, + "help": "Optional remote image URL to download and attach to the reply" } ], "columns": [ "status", - "message" + "message", + "text", + "url" ], "type": "js", - "modulePath": "twitter/unfollow.js", - "sourceFile": "twitter/unfollow.js", + "modulePath": "twitter/reply.js", + "sourceFile": "twitter/reply.js", "navigateBefore": true }, { "site": "twitter", - "name": "unlike", - "description": "Remove a like from a specific tweet", + "name": "reply-dm", + "description": "Send a message to recent DM conversations", "access": "write", "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "url", + "name": "text", "type": "string", "required": true, "positional": true, - "help": "The URL of the tweet to unlike" + "help": "Message text to send (e.g. \"my messaging handle wxkabi\")" + }, + { + "name": "max", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum number of conversations to reply to (default: 20)" + }, + { + "name": "skip-replied", + "type": "boolean", + "default": true, + "required": false, + "help": "Skip conversations where you already sent the same text (default: true)" + }, + { + "name": "timeout", + "type": "int", + "default": 600, + "required": false, + "help": "Max seconds for the overall command (default: 600 — batch op)" } ], "columns": [ + "index", "status", + "user", "message" ], "type": "js", - "modulePath": "twitter/unlike.js", - "sourceFile": "twitter/unlike.js", + "modulePath": "twitter/reply-dm.js", + "sourceFile": "twitter/reply-dm.js", "navigateBefore": true }, { "site": "twitter", - "name": "unretweet", - "description": "Undo a retweet on a specific tweet", + "name": "retweet", + "description": "Retweet a specific tweet", "access": "write", "domain": "x.com", "strategy": "ui", @@ -10665,7 +8377,7 @@ "type": "string", "required": true, "positional": true, - "help": "The URL of the tweet to unretweet" + "help": "The URL of the tweet to retweet" } ], "columns": [ @@ -10673,633 +8385,463 @@ "message" ], "type": "js", - "modulePath": "twitter/unretweet.js", - "sourceFile": "twitter/unretweet.js", + "modulePath": "twitter/retweet.js", + "sourceFile": "twitter/retweet.js", "navigateBefore": true }, { "site": "twitter", - "name": "whoami", - "description": "Show the current logged-in twitter account", + "name": "search", + "description": "Search Twitter/X for tweets, with optional --from / --has / --exclude / --product filters mapped to X's search operators", "access": "read", "domain": "x.com", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "username", - "url" - ], - "type": "js", - "modulePath": "twitter/auth.js", - "sourceFile": "twitter/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "yollomi", - "name": "background", - "description": "Generate AI background for a product/object image (5 credits)", - "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", - "browser": true, "args": [ { - "name": "image", - "type": "str", + "name": "query", + "type": "string", "required": true, "positional": true, - "help": "Image URL (upload via \"webcmd yollomi upload\" first)" + "help": "Search query. Raw X operators (e.g. \"exact phrase\", #tag, OR, lang:en, since:YYYY-MM-DD, from:, since:) are passed through unchanged." }, { - "name": "prompt", - "type": "str", - "default": "", + "name": "filter", + "type": "string", + "default": "top", "required": false, - "help": "Background description (optional)" + "help": "Legacy alias for --product. Kept for backwards compatibility; if --product is set it wins.", + "choices": [ + "top", + "live" + ] }, { - "name": "output", - "type": "str", - "default": "./yollomi-output", + "name": "product", + "type": "string", "required": false, - "help": "Output directory" + "help": "Which X search tab to read: top (default), live (Latest), photos, videos. Maps to the f= URL param.", + "choices": [ + "top", + "live", + "photos", + "videos" + ] }, { - "name": "no-download", - "type": "boolean", - "default": false, + "name": "from", + "type": "string", "required": false, - "help": "Only show URL" - } - ], - "columns": [ - "status", - "file", - "size", - "url" - ], - "type": "js", - "modulePath": "yollomi/background.js", - "sourceFile": "yollomi/background.js", - "navigateBefore": "https://yollomi.com" - }, - { - "site": "yollomi", - "name": "edit", - "description": "Edit images with AI text prompts (Qwen image edit)", - "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "image", - "type": "str", - "required": true, - "positional": true, - "help": "Input image URL (upload via \"webcmd yollomi upload\" first)" + "help": "Restrict to tweets authored by . Leading @ is stripped. Equivalent to appending `from:` to the query." }, { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Editing instruction (e.g. \"Make it look vintage\")" + "name": "has", + "type": "string", + "required": false, + "help": "Restrict to tweets that have media|images|videos|links|replies. Maps to X's `filter:` operator.", + "choices": [ + "media", + "images", + "videos", + "links", + "replies" + ] }, { - "name": "model", - "type": "str", - "default": "qwen-image-edit", + "name": "exclude", + "type": "string", "required": false, - "help": "Edit model", + "help": "Exclude tweets matching : replies|retweets|media|links. Maps to X's `-filter:` operator (retweets → -filter:nativeretweets).", "choices": [ - "qwen-image-edit", - "qwen-image-edit-plus" + "replies", + "retweets", + "media", + "links" ] }, { - "name": "output", - "type": "str", - "default": "./yollomi-output", + "name": "limit", + "type": "int", + "default": 15, "required": false, - "help": "Output directory" + "help": "Maximum number of tweets to return (default 15). Result count after server-side filtering." }, { - "name": "no-download", - "type": "boolean", - "default": false, + "name": "top-by-engagement", + "type": "int", + "default": 0, "required": false, - "help": "Only show URL" + "help": "When set to N>0, re-rank the results by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X's native ordering." } ], "columns": [ - "status", - "file", - "size", - "credits", - "url" + "id", + "author", + "bio", + "text", + "created_at", + "likes", + "views", + "url", + "has_media", + "media_urls", + "media_posters", + "card", + "quoted_tweet" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "yollomi/edit.js", - "sourceFile": "yollomi/edit.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "twitter/search.js", + "sourceFile": "twitter/search.js", + "navigateBefore": "https://x.com" }, { - "site": "yollomi", - "name": "face-swap", - "description": "Swap faces between two photos (3 credits)", - "access": "write", - "domain": "yollomi.com", + "site": "twitter", + "name": "thread", + "description": "Get a tweet thread (original + all replies)", + "access": "read", + "domain": "x.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "source", - "type": "str", - "required": true, - "help": "Source face image URL" - }, - { - "name": "target", - "type": "str", + "name": "tweet-id", + "type": "string", "required": true, - "help": "Target photo URL" + "positional": true, + "help": "Tweet numeric ID (e.g. 1234567890) or full status URL" }, { - "name": "output", - "type": "str", - "default": "./yollomi-output", + "name": "limit", + "type": "int", + "default": 50, "required": false, - "help": "Output directory" + "help": "" }, { - "name": "no-download", - "type": "boolean", - "default": false, + "name": "top-by-engagement", + "type": "int", + "default": 0, "required": false, - "help": "Only show URL" + "help": "When set to N>0, re-rank the thread by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the conversation's structural ordering." } ], "columns": [ - "status", - "file", - "size", - "url" + "id", + "author", + "bio", + "text", + "likes", + "retweets", + "url", + "has_media", + "media_urls", + "media_posters", + "card", + "quoted_tweet" ], "type": "js", - "modulePath": "yollomi/face-swap.js", - "sourceFile": "yollomi/face-swap.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "twitter/thread.js", + "sourceFile": "twitter/thread.js", + "navigateBefore": "https://x.com" }, { - "site": "yollomi", - "name": "generate", - "description": "Generate images with AI (text-to-image or image-to-image)", - "access": "write", - "domain": "yollomi.com", + "site": "twitter", + "name": "timeline", + "description": "Fetch the logged-in user's home timeline (for-you algorithmic feed by default; pass --type following for the chronological feed of accounts you follow)", + "access": "read", + "domain": "x.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Text prompt describing the image" - }, - { - "name": "model", - "type": "str", - "default": "z-image-turbo", - "required": false, - "help": "Model ID (z-image-turbo, flux-schnell, nano-banana, flux-2-pro, ...)" - }, - { - "name": "ratio", + "name": "type", "type": "str", - "default": "1:1", + "default": "for-you", "required": false, - "help": "Aspect ratio", + "help": "Which home-timeline feed to read. Default for-you (algorithmic). Use following for the chronological feed of accounts you follow.", "choices": [ - "1:1", - "16:9", - "9:16", - "4:3", - "3:4" + "for-you", + "following" ] }, { - "name": "image", - "type": "str", - "required": false, - "help": "Input image URL for image-to-image (upload via \"webcmd yollomi upload\" first)" - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Output directory" + "help": "Maximum number of tweets to return (default 20)." }, { - "name": "no-download", - "type": "boolean", - "default": false, + "name": "top-by-engagement", + "type": "int", + "default": 0, "required": false, - "help": "Only show URLs, skip download" + "help": "When set to N>0, re-rank the timeline by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X's native ordering." } ], "columns": [ - "index", - "status", - "file", - "size", - "url" + "id", + "author", + "bio", + "text", + "likes", + "retweets", + "replies", + "quotes", + "bookmarks", + "views", + "created_at", + "url", + "has_media", + "media_urls", + "media_posters", + "card", + "quoted_tweet" ], "type": "js", - "modulePath": "yollomi/generate.js", - "sourceFile": "yollomi/generate.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "twitter/timeline.js", + "sourceFile": "twitter/timeline.js", + "navigateBefore": "https://x.com" }, { - "site": "yollomi", - "name": "models", - "description": "List available Yollomi AI models (image, video, tools)", + "site": "twitter", + "name": "trending", + "description": "Twitter/X trending topics", "access": "read", - "strategy": "public", - "browser": false, + "domain": "x.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "type", - "type": "str", - "default": "all", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Filter by model type", - "choices": [ - "all", - "image", - "video", - "tool" - ] + "help": "Number of trends to show" } ], "columns": [ - "type", - "model", - "credits", - "description" + "rank", + "topic", + "category" ], "type": "js", - "modulePath": "yollomi/models.js", - "sourceFile": "yollomi/models.js" + "modulePath": "twitter/trending.js", + "sourceFile": "twitter/trending.js", + "navigateBefore": "https://x.com" }, { - "site": "yollomi", - "name": "object-remover", - "description": "Remove unwanted objects from images (3 credits)", - "access": "write", - "domain": "yollomi.com", + "site": "twitter", + "name": "tweets", + "description": "Fetch a Twitter user's most recent tweets (chronological, excludes pinned; defaults to the logged-in user when no username is given)", + "access": "read", + "domain": "x.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "image", - "type": "str", - "required": true, + "name": "username", + "type": "string", + "required": false, "positional": true, - "help": "Image URL" + "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." }, { - "name": "mask", - "type": "str", - "required": true, - "positional": true, - "help": "Mask image URL (white = area to remove)" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max tweets to return (1-10000; fetched across cursor pages)" }, { - "name": "output", - "type": "str", - "default": "./yollomi-output", + "name": "page-delay", + "type": "int", + "default": 2, "required": false, - "help": "Output directory" + "help": "Seconds to wait between paginated timeline requests to reduce rate-limit risk. Use 0 to disable." }, { - "name": "no-download", - "type": "boolean", - "default": false, + "name": "top-by-engagement", + "type": "int", + "default": 0, "required": false, - "help": "Only show URL" + "help": "When set to N>0, re-rank the tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the chronological ordering." } ], "columns": [ - "status", - "file", - "size", - "url" + "id", + "author", + "created_at", + "is_retweet", + "text", + "likes", + "retweets", + "replies", + "views", + "url", + "has_media", + "media_urls", + "media_posters", + "quoted_tweet" ], "type": "js", - "modulePath": "yollomi/object-remover.js", - "sourceFile": "yollomi/object-remover.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "twitter/tweets.js", + "sourceFile": "twitter/tweets.js", + "navigateBefore": "https://x.com" }, { - "site": "yollomi", - "name": "remove-bg", - "description": "Remove image background with AI (free)", + "site": "twitter", + "name": "unblock", + "description": "Unblock a Twitter user", "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", + "domain": "x.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "image", - "type": "str", + "name": "username", + "type": "string", "required": true, "positional": true, - "help": "Image URL to remove background from" - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL" + "help": "Twitter screen name (without @)" } ], "columns": [ "status", - "file", - "size", - "url" + "message" ], "type": "js", - "modulePath": "yollomi/remove-bg.js", - "sourceFile": "yollomi/remove-bg.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "twitter/unblock.js", + "sourceFile": "twitter/unblock.js", + "navigateBefore": true }, { - "site": "yollomi", - "name": "restore", - "description": "Restore old or damaged photos with AI (4 credits)", + "site": "twitter", + "name": "unbookmark", + "description": "Remove a tweet from bookmarks", "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", + "domain": "x.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "image", - "type": "str", + "name": "url", + "type": "string", "required": true, "positional": true, - "help": "Image URL to restore" - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL" + "help": "Tweet URL to unbookmark" } ], "columns": [ "status", - "file", - "size", - "url" + "message" ], "type": "js", - "modulePath": "yollomi/restore.js", - "sourceFile": "yollomi/restore.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "twitter/unbookmark.js", + "sourceFile": "twitter/unbookmark.js", + "navigateBefore": true }, { - "site": "yollomi", - "name": "try-on", - "description": "Virtual try-on — see how clothes look on a person (3 credits)", + "site": "twitter", + "name": "unfollow", + "description": "Unfollow a Twitter user", "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", + "domain": "x.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "person", - "type": "str", - "required": true, - "help": "Person photo URL (upload via \"webcmd yollomi upload\" first)" - }, - { - "name": "cloth", - "type": "str", + "name": "username", + "type": "string", "required": true, - "help": "Clothing image URL" - }, - { - "name": "cloth-type", - "type": "str", - "default": "upper", - "required": false, - "help": "Clothing type", - "choices": [ - "upper", - "lower", - "overall" - ] - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL" + "positional": true, + "help": "Twitter screen name (without @)" } ], "columns": [ "status", - "file", - "size", - "url" + "message" ], "type": "js", - "modulePath": "yollomi/try-on.js", - "sourceFile": "yollomi/try-on.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "twitter/unfollow.js", + "sourceFile": "twitter/unfollow.js", + "navigateBefore": true }, { - "site": "yollomi", - "name": "upload", - "description": "Upload an image or video to Yollomi (returns URL for other commands)", + "site": "twitter", + "name": "unlike", + "description": "Remove a like from a specific tweet", "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", + "domain": "x.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "file", - "type": "str", + "name": "url", + "type": "string", "required": true, "positional": true, - "help": "Local file path to upload" + "help": "The URL of the tweet to unlike" } ], "columns": [ "status", - "file", - "size", - "url" + "message" ], "type": "js", - "modulePath": "yollomi/upload.js", - "sourceFile": "yollomi/upload.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "twitter/unlike.js", + "sourceFile": "twitter/unlike.js", + "navigateBefore": true }, { - "site": "yollomi", - "name": "upscale", - "description": "Upscale image resolution with AI (1 credit)", + "site": "twitter", + "name": "unretweet", + "description": "Undo a retweet on a specific tweet", "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", + "domain": "x.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "image", - "type": "str", + "name": "url", + "type": "string", "required": true, "positional": true, - "help": "Image URL to upscale" - }, - { - "name": "scale", - "type": "str", - "default": "2", - "required": false, - "help": "Upscale factor (2 or 4)", - "choices": [ - "2", - "4" - ] - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL" + "help": "The URL of the tweet to unretweet" } ], "columns": [ "status", - "file", - "size", - "scale", - "url" + "message" ], "type": "js", - "modulePath": "yollomi/upscale.js", - "sourceFile": "yollomi/upscale.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "twitter/unretweet.js", + "sourceFile": "twitter/unretweet.js", + "navigateBefore": true }, { - "site": "yollomi", - "name": "video", - "description": "Generate videos with AI (text-to-video or image-to-video)", - "access": "write", - "domain": "yollomi.com", + "site": "twitter", + "name": "whoami", + "description": "Show the current logged-in twitter account", + "access": "read", + "domain": "x.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Text prompt describing the video" - }, - { - "name": "model", - "type": "str", - "default": "kling-2-1", - "required": false, - "help": "Model (kling-2-1, openai-sora-2, google-veo-3-1, wan-2-5-t2v, ...)" - }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Input image URL for image-to-video" - }, - { - "name": "ratio", - "type": "str", - "default": "16:9", - "required": false, - "help": "Aspect ratio", - "choices": [ - "1:1", - "16:9", - "9:16", - "4:3", - "3:4" - ] - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL, skip download" - } - ], + "args": [], "columns": [ - "status", - "file", - "size", - "credits", + "logged_in", + "site", + "username", "url" ], "type": "js", - "modulePath": "yollomi/video.js", - "sourceFile": "yollomi/video.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "twitter/auth.js", + "sourceFile": "twitter/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "youtube", diff --git a/clis/_atlassian/shared.test.js b/clis/_atlassian/shared.test.js deleted file mode 100644 index 1cfdd756..00000000 --- a/clis/_atlassian/shared.test.js +++ /dev/null @@ -1,170 +0,0 @@ -import { describe, expect, it, afterEach, vi } from 'vitest'; -import { __test__ } from './shared.js'; -import { CommandExecutionError } from '@agentrhq/webcmd/errors'; - -const ENV_KEYS = [ - 'ATLASSIAN_CONFLUENCE_BASE_URL', - 'ATLASSIAN_DEPLOYMENT', - 'ATLASSIAN_EMAIL', - 'ATLASSIAN_API_TOKEN', - 'ATLASSIAN_PAT', - 'ATLASSIAN_JIRA_BASE_URL', -]; - -function clearEnv() { - for (const key of ENV_KEYS) delete process.env[key]; -} - -afterEach(() => { - clearEnv(); - vi.unstubAllGlobals(); -}); - -describe('atlassian shared helpers', () => { - it('infers Confluence Cloud and appends /wiki', () => { - clearEnv(); - process.env.ATLASSIAN_CONFLUENCE_BASE_URL = 'https://example.atlassian.net'; - process.env.ATLASSIAN_EMAIL = 'bot@example.com'; - process.env.ATLASSIAN_API_TOKEN = 'secret'; - const config = __test__.getConfluenceConfig(); - expect(config.deployment).toBe('cloud'); - expect(config.baseUrl).toBe('https://example.atlassian.net/wiki'); - expect(config.authHeaders.Authorization).toMatch(/^Basic /); - }); - - it('uses Data Center PAT as bearer auth', () => { - clearEnv(); - process.env.ATLASSIAN_JIRA_BASE_URL = 'https://jira.example.com'; - process.env.ATLASSIAN_DEPLOYMENT = 'datacenter'; - process.env.ATLASSIAN_PAT = 'pat-123'; - const config = __test__.getJiraConfig(); - expect(config.deployment).toBe('datacenter'); - expect(config.authHeaders.Authorization).toBe('Bearer pat-123'); - }); - - it('converts Jira ADF to Markdown', () => { - const markdown = __test__.adfToMarkdown({ - type: 'doc', - content: [ - { - type: 'paragraph', - content: [ - { type: 'text', text: 'Broken ', marks: [{ type: 'strong' }] }, - { type: 'text', text: 'checkout', marks: [{ type: 'link', attrs: { href: 'https://example.com' } }] }, - ], - }, - { - type: 'bulletList', - content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'retry payment' }] }] }], - }, - ], - }); - expect(markdown).toContain('**Broken **'); - expect(markdown).toContain('[checkout](https://example.com)'); - expect(markdown).toContain('- retry payment'); - }); - - it('escapes pipe characters inside ADF table cells', () => { - const markdown = __test__.adfToMarkdown({ - type: 'doc', - content: [{ - type: 'table', - content: [ - { - type: 'tableRow', - content: [ - { type: 'tableHeader', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Service' }] }] }, - { type: 'tableHeader', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Notes' }] }] }, - ], - }, - { - type: 'tableRow', - content: [ - { type: 'tableCell', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'payments' }] }] }, - { type: 'tableCell', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'a | b' }] }] }, - ], - }, - ], - }], - }); - expect(markdown).toContain('Service | Notes'); - expect(markdown).toContain('--- | ---'); - expect(markdown).toContain('payments | a \\| b'); - }); - - it('converts nested HTML to Markdown through the shared Turndown converter', () => { - const markdown = __test__.htmlToMarkdown('
  • Root
    • Child
A
B
'); - expect(markdown).toContain('**Root**'); - expect(markdown).toContain('Child'); - expect(markdown).toContain('A'); - expect(markdown).toContain('B'); - }); - - it('converts Markdown to conservative Confluence storage XHTML', () => { - const storage = __test__.markdownToConfluenceStorage([ - '# RCA', - '', - '- Impacted checkout', - '', - '| Service | Status |', - '| --- | --- |', - '| payments | fixed |', - ].join('\n')); - expect(storage).toContain('

RCA

'); - expect(storage).toContain('
    '); - expect(storage).toContain(''); - expect(storage).toContain(''); - }); - - it('preserves nested Markdown lists in Confluence storage XHTML', () => { - const storage = __test__.markdownToConfluenceStorage([ - '- Parent', - ' - Child', - '- Next', - ].join('\n')); - const compact = storage.replace(/\s*\n\s*/g, ''); - expect(compact).toContain('
    • Parent
      • Child
    • Next
    '); - }); - - it('sends JSON requests with configured auth headers', async () => { - const fetchMock = vi.fn(async () => new Response(JSON.stringify({ ok: true }), { status: 200 })); - vi.stubGlobal('fetch', fetchMock); - const data = await __test__.atlassianRequest({ - product: 'jira', - baseUrl: 'https://jira.example.com', - deployment: 'datacenter', - authHeaders: { Authorization: 'Bearer token' }, - }, '/rest/api/2/myself', { label: 'jira myself' }); - expect(data).toEqual({ ok: true }); - expect(fetchMock.mock.calls[0][0]).toBe('https://jira.example.com/rest/api/2/myself'); - expect(fetchMock.mock.calls[0][1].headers.Authorization).toBe('Bearer token'); - }); - - it('maps auth and rate-limit responses to typed errors', async () => { - vi.stubGlobal('fetch', vi.fn(async () => new Response(JSON.stringify({ message: 'bad token' }), { status: 401 }))); - await expect(__test__.atlassianRequest({ - product: 'jira', - baseUrl: 'https://jira.example.com', - deployment: 'datacenter', - authHeaders: { Authorization: 'Bearer token' }, - }, '/rest/api/2/myself', { label: 'jira myself' })).rejects.toMatchObject({ code: 'AUTH_REQUIRED' }); - - vi.stubGlobal('fetch', vi.fn(async () => new Response(JSON.stringify({ message: 'slow down' }), { status: 429 }))); - await expect(__test__.atlassianRequest({ - product: 'jira', - baseUrl: 'https://jira.example.com', - deployment: 'datacenter', - authHeaders: { Authorization: 'Bearer token' }, - }, '/rest/api/2/myself', { label: 'jira myself' })).rejects.toMatchObject({ code: 'COMMAND_EXEC' }); - }); - - it('fails typed when a successful Atlassian REST response is not JSON', async () => { - vi.stubGlobal('fetch', vi.fn(async () => new Response('login', { status: 200, headers: { 'content-type': 'text/html' } }))); - await expect(__test__.atlassianRequest({ - product: 'jira', - baseUrl: 'https://jira.example.com', - deployment: 'datacenter', - authHeaders: { Authorization: 'Bearer token' }, - }, '/rest/api/2/myself', { label: 'jira myself' })).rejects.toBeInstanceOf(CommandExecutionError); - }); -}); diff --git a/plugin-command-manifest.json b/plugin-command-manifest.json index ccf05fbf..7bd8deb0 100644 --- a/plugin-command-manifest.json +++ b/plugin-command-manifest.json @@ -1425,6 +1425,231 @@ "modulePath": "plugins/bbc/topic.js", "sourceFile": "plugins/bbc/topic.js" }, + { + "site": "bigbasket", + "name": "add-to-cart", + "description": "Add a BigBasket product to cart", + "access": "write", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product", + "type": "str", + "required": true, + "positional": true, + "help": "Product ID or URL" + }, + { + "name": "quantity", + "type": "int", + "default": 1, + "required": false, + "help": "Quantity to add (max 20)" + } + ], + "columns": [ + "ok", + "product_id", + "quantity", + "url", + "message" + ], + "type": "js", + "modulePath": "plugins/bigbasket/add-to-cart.js", + "sourceFile": "plugins/bigbasket/add-to-cart.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "cart", + "description": "Read BigBasket cart line items", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "product_id", + "title", + "quantity", + "price", + "line_total", + "availability", + "url" + ], + "type": "js", + "modulePath": "plugins/bigbasket/cart.js", + "sourceFile": "plugins/bigbasket/cart.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "category", + "description": "Read BigBasket category product cards", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "category", + "type": "str", + "required": true, + "positional": true, + "help": "Category URL or slug" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum products to return (max 50)" + } + ], + "columns": [ + "rank", + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", + "url" + ], + "type": "js", + "modulePath": "plugins/bigbasket/category.js", + "sourceFile": "plugins/bigbasket/category.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "checkout", + "description": "Open BigBasket checkout review without placing an order", + "access": "write", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "ok", + "stage", + "cart_total", + "address_ready", + "delivery_ready", + "payment_ready", + "next_action", + "url" + ], + "type": "js", + "modulePath": "plugins/bigbasket/checkout.js", + "sourceFile": "plugins/bigbasket/checkout.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "location", + "description": "Show the selected BigBasket delivery location", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "selected", + "label", + "area", + "city", + "pincode", + "source" + ], + "type": "js", + "modulePath": "plugins/bigbasket/location.js", + "sourceFile": "plugins/bigbasket/location.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "product", + "description": "Read BigBasket product details", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product", + "type": "str", + "required": true, + "positional": true, + "help": "Product ID or URL" + } + ], + "columns": [ + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", + "delivery", + "image_url", + "url" + ], + "type": "js", + "modulePath": "plugins/bigbasket/product.js", + "sourceFile": "plugins/bigbasket/product.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "search", + "description": "Search BigBasket products", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum products to return (max 50)" + } + ], + "columns": [ + "rank", + "product_id", + "title", + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/bigbasket/search.js", + "sourceFile": "plugins/bigbasket/search.js", + "navigateBefore": "https://www.bigbasket.com" + }, { "site": "binance", "name": "asks", @@ -3577,13 +3802,13 @@ "siteSession": "persistent" }, { - "site": "chatwise", + "site": "chatgpt-app", "name": "ask", "description": "Send a prompt and wait for the AI response (send + wait + read)", "access": "write", "domain": "localhost", - "strategy": "ui", - "browser": true, + "strategy": "public", + "browser": false, "args": [ { "name": "text", @@ -3592,12 +3817,31 @@ "positional": true, "help": "Prompt to send" }, + { + "name": "model", + "type": "str", + "required": false, + "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] + }, { "name": "timeout", "type": "int", "default": 30, "required": false, - "help": "Max seconds to wait (default: 30)" + "help": "Max seconds to wait for response (default: 30)" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Path to local image to attach (optional)" } ], "columns": [ @@ -3605,68 +3849,226 @@ "Text" ], "type": "js", - "modulePath": "plugins/chatwise/ask.js", - "sourceFile": "plugins/chatwise/ask.js", - "navigateBefore": true + "modulePath": "plugins/chatgpt-app/ask.js", + "sourceFile": "plugins/chatgpt-app/ask.js" }, { - "site": "chatwise", - "name": "export", - "description": "Export the current ChatWise conversation to a Markdown file", + "site": "chatgpt-app", + "name": "model", + "description": "Switch ChatGPT Desktop model/mode (auto, instant, thinking, 5.2-instant, 5.2-thinking)", "access": "read", "domain": "localhost", - "strategy": "ui", - "browser": true, + "strategy": "public", + "browser": false, "args": [ { - "name": "output", + "name": "model", "type": "str", - "required": false, - "help": "Output file (default: /tmp/chatwise-export.md)" + "required": true, + "positional": true, + "help": "Model to switch to", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] } ], "columns": [ "Status", - "File", - "Messages" + "Model" ], "type": "js", - "modulePath": "plugins/chatwise/export.js", - "sourceFile": "plugins/chatwise/export.js", - "navigateBefore": true + "modulePath": "plugins/chatgpt-app/model.js", + "sourceFile": "plugins/chatgpt-app/model.js" }, { - "site": "chatwise", - "name": "history", - "description": "List conversation history in ChatWise sidebar", - "access": "read", + "site": "chatgpt-app", + "name": "new", + "description": "Open a new chat in ChatGPT Desktop App", + "access": "write", "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], + "strategy": "public", + "browser": false, + "args": [ + { + "name": "temp", + "type": "boolean", + "default": false, + "required": false, + "help": "Open a temporary chat with privacy protection" + } + ], "columns": [ - "Index", - "Title" + "Status" ], "type": "js", - "modulePath": "plugins/chatwise/history.js", - "sourceFile": "plugins/chatwise/history.js", - "navigateBefore": true + "modulePath": "plugins/chatgpt-app/new.js", + "sourceFile": "plugins/chatgpt-app/new.js" }, { - "site": "chatwise", - "name": "model", - "description": "Get or switch the active AI model in ChatWise", + "site": "chatgpt-app", + "name": "read", + "description": "Read the last visible message from the focused ChatGPT Desktop window", "access": "read", "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "model-name", - "type": "str", - "required": false, - "positional": true, + "strategy": "public", + "browser": false, + "args": [], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/chatgpt-app/read.js", + "sourceFile": "plugins/chatgpt-app/read.js" + }, + { + "site": "chatgpt-app", + "name": "send", + "description": "Send a message to the active ChatGPT Desktop App window", + "access": "write", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Message to send" + }, + { + "name": "model", + "type": "str", + "required": false, + "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] + } + ], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/chatgpt-app/send.js", + "sourceFile": "plugins/chatgpt-app/send.js" + }, + { + "site": "chatgpt-app", + "name": "status", + "description": "Check if ChatGPT Desktop App is running natively on macOS", + "access": "read", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/chatgpt-app/status.js", + "sourceFile": "plugins/chatgpt-app/status.js" + }, + { + "site": "chatwise", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds to wait (default: 30)" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/chatwise/ask.js", + "sourceFile": "plugins/chatwise/ask.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "export", + "description": "Export the current ChatWise conversation to a Markdown file", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file (default: /tmp/chatwise-export.md)" + } + ], + "columns": [ + "Status", + "File", + "Messages" + ], + "type": "js", + "modulePath": "plugins/chatwise/export.js", + "sourceFile": "plugins/chatwise/export.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "history", + "description": "List conversation history in ChatWise sidebar", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Title" + ], + "type": "js", + "modulePath": "plugins/chatwise/history.js", + "sourceFile": "plugins/chatwise/history.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "model", + "description": "Get or switch the active AI model in ChatWise", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "model-name", + "type": "str", + "required": false, + "positional": true, "help": "Model to switch to (e.g. gpt-4, claude-3)" } ], @@ -5195,93 +5597,298 @@ "sourceFile": "plugins/concordia/export-postgraduate-courses.js" }, { - "site": "coupang", - "name": "add-to-cart", - "description": "Add a Coupang product to cart using logged-in browser session", + "site": "confluence", + "name": "create", + "description": "Create a Confluence page from Markdown or storage XHTML", "access": "write", - "domain": "www.coupang.com", - "strategy": "cookie", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "product-id", - "type": "str", + "name": "space", + "type": "string", + "required": true, + "help": "Cloud space id, or Data Center space key" + }, + { + "name": "title", + "type": "string", + "required": true, + "help": "Page title" + }, + { + "name": "file", + "type": "string", + "required": true, + "help": "Markdown file path" + }, + { + "name": "parent", + "type": "string", "required": false, - "positional": true, - "help": "Coupang product ID" + "help": "Optional parent page id" }, { - "name": "url", - "type": "str", + "name": "representation", + "type": "string", + "default": "markdown", "required": false, - "help": "Canonical product URL" + "help": "Input file format", + "choices": [ + "markdown", + "storage" + ] + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually create the remote page" } ], "columns": [ - "ok", - "product_id", - "url", - "message" + "status", + "id", + "title", + "spaceId", + "spaceKey", + "version", + "url" ], "type": "js", - "modulePath": "plugins/coupang/add-to-cart.js", - "sourceFile": "plugins/coupang/add-to-cart.js", - "navigateBefore": "https://www.coupang.com" + "modulePath": "plugins/confluence/create.js", + "sourceFile": "plugins/confluence/create.js" }, { - "site": "coupang", - "name": "login", - "description": "Open coupang login", - "access": "write", - "domain": "coupang.com", - "strategy": "cookie", - "browser": true, - "args": [], + "site": "confluence", + "name": "page", + "description": "Confluence page by id with storage and Markdown body", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Confluence page id" + } + ], "columns": [ + "id", + "title", "status", - "logged_in", - "site", - "name", - "action", - "verify_command" + "spaceId", + "spaceKey", + "version", + "url" ], "type": "js", - "modulePath": "plugins/coupang/auth.js", - "sourceFile": "plugins/coupang/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/confluence/page.js", + "sourceFile": "plugins/confluence/page.js" }, { - "site": "coupang", - "name": "product", - "description": "Read full product detail (price, rating, seller, delivery) for a Coupang product", + "site": "confluence", + "name": "search", + "description": "Search Confluence content with CQL", "access": "read", - "domain": "www.coupang.com", - "strategy": "cookie", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "product-id", + "name": "cql", "type": "str", - "required": false, + "required": true, "positional": true, - "help": "Coupang product ID (digits only)" + "help": "CQL query, e.g. \"type = page and title ~ \\\"RCA\\\"\"" }, { - "name": "url", - "type": "str", + "name": "space", + "type": "string", "required": false, - "help": "Canonical Coupang product URL (alternative to --product-id)" + "help": "Limit search to a Confluence space key" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results to return (1-100)" } ], "columns": [ - "product_id", + "id", "title", - "price", - "original_price", - "discount_rate", - "rating", - "review_count", + "type", + "spaceKey", + "status", + "lastModified", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/confluence/search.js", + "sourceFile": "plugins/confluence/search.js" + }, + { + "site": "confluence", + "name": "update", + "description": "Update a Confluence page body from Markdown or storage XHTML", + "access": "write", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Confluence page id" + }, + { + "name": "file", + "type": "string", + "required": true, + "help": "Markdown file path" + }, + { + "name": "title", + "type": "string", + "required": false, + "help": "Optional replacement title; defaults to current title" + }, + { + "name": "version-message", + "type": "string", + "required": false, + "help": "Confluence version message" + }, + { + "name": "representation", + "type": "string", + "default": "markdown", + "required": false, + "help": "Input file format", + "choices": [ + "markdown", + "storage" + ] + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually update the remote page" + } + ], + "columns": [ + "status", + "id", + "title", + "spaceId", + "spaceKey", + "version", + "url" + ], + "type": "js", + "modulePath": "plugins/confluence/update.js", + "sourceFile": "plugins/confluence/update.js" + }, + { + "site": "coupang", + "name": "add-to-cart", + "description": "Add a Coupang product to cart using logged-in browser session", + "access": "write", + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product-id", + "type": "str", + "required": false, + "positional": true, + "help": "Coupang product ID" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Canonical product URL" + } + ], + "columns": [ + "ok", + "product_id", + "url", + "message" + ], + "type": "js", + "modulePath": "plugins/coupang/add-to-cart.js", + "sourceFile": "plugins/coupang/add-to-cart.js", + "navigateBefore": "https://www.coupang.com" + }, + { + "site": "coupang", + "name": "login", + "description": "Open coupang login", + "access": "write", + "domain": "coupang.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/coupang/auth.js", + "sourceFile": "plugins/coupang/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "coupang", + "name": "product", + "description": "Read full product detail (price, rating, seller, delivery) for a Coupang product", + "access": "read", + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product-id", + "type": "str", + "required": false, + "positional": true, + "help": "Coupang product ID (digits only)" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Canonical Coupang product URL (alternative to --product-id)" + } + ], + "columns": [ + "product_id", + "title", + "price", + "original_price", + "discount_rate", + "rating", + "review_count", "seller", "brand", "rocket", @@ -6224,98 +6831,456 @@ "sourceFile": "plugins/dictionary/synonyms.js" }, { - "site": "district", - "name": "checkout", - "description": "Select District movie seats and open the UPI QR payment scanner", + "site": "discord-app", + "name": "channels", + "description": "List channels in the current Discord server", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Channel", + "Type", + "guild_id", + "channel_id", + "url" + ], + "type": "js", + "modulePath": "plugins/discord-app/channels.js", + "sourceFile": "plugins/discord-app/channels.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "delete", + "description": "Delete a message by its ID in the active Discord channel", "access": "write", - "domain": "www.district.in", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "show", - "type": "str", + "name": "message_id", + "type": "string", "required": true, "positional": true, - "help": "District seat-layout URL or showId from district showtimes" - }, + "help": "The ID of the message to delete (visible via Developer Mode or the read command)" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "plugins/discord-app/delete.js", + "sourceFile": "plugins/discord-app/delete.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "goto", + "description": "Open a Discord channel by id/name/url without sending messages", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ { - "name": "seats", + "name": "guild", "type": "str", - "required": true, - "help": "Comma-separated seat labels to select, e.g. I22,I21" + "required": false, + "help": "Guild/server id or visible name" }, { - "name": "format-id", + "name": "channel", "type": "str", "required": false, - "help": "District formatId from showtimes; required when show is a showId" + "help": "Channel id or visible name" }, { - "name": "content-id", + "name": "url", "type": "str", "required": false, - "help": "District content id; required when show is a showId" + "help": "Discord channel URL" }, { "name": "timeout", - "type": "int", - "default": 45, - "required": false, - "help": "Maximum seconds to wait for selection, review page, and payment handoff" - }, - { - "name": "payment", "type": "str", - "default": "upi-qr", + "default": "8", "required": false, - "help": "Payment handoff target: upi-qr opens the scanner; review stops on order review" + "help": "Seconds to wait for Discord to show the route (default: 8)" } ], "columns": [ - "status", - "movie", - "cinema", - "date", - "time", - "seats", - "ticketCount", - "orderAmount", - "bookingCharge", - "total", - "paymentMethod", - "paymentState", - "upiQrVisible", - "paymentAmount", - "paymentUrl", - "showId" + "Status", + "guild_id", + "channel_id", + "url" ], "type": "js", - "modulePath": "plugins/district/checkout.js", - "sourceFile": "plugins/district/checkout.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "modulePath": "plugins/discord-app/goto.js", + "sourceFile": "plugins/discord-app/goto.js", + "navigateBefore": true }, { - "site": "district", - "name": "listings", - "aliases": [ - "ls" + "site": "discord-app", + "name": "members", + "description": "List online members in the current Discord channel", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Name", + "Status" ], - "description": "List public District by Zomato movies, events, and nearby going-out cards", + "type": "js", + "modulePath": "plugins/discord-app/members.js", + "sourceFile": "plugins/discord-app/members.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "read", + "description": "Read recent messages from the active or targeted Discord channel", "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "input", + "name": "count", "type": "str", - "default": "home", + "default": "20", "required": false, - "positional": true, - "help": "home, movies, events, a district.in URL, or a District path" + "help": "Number of messages to read (default: 20)" + }, + { + "name": "guild", + "type": "str", + "required": false, + "help": "Guild/server id or visible name for targeted reads" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Channel id or visible name for targeted reads" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Discord channel URL to open before reading" + } + ], + "columns": [ + "Author", + "Time", + "Message", + "channel_id", + "message_id" + ], + "type": "js", + "modulePath": "plugins/discord-app/read.js", + "sourceFile": "plugins/discord-app/read.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "search", + "description": "Search messages in the current Discord server/channel (Cmd+F)", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + } + ], + "columns": [ + "Index", + "Author", + "Message" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/discord-app/search.js", + "sourceFile": "plugins/discord-app/search.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "send", + "description": "Send a message in the active Discord channel", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Message to send" + } + ], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/discord-app/send.js", + "sourceFile": "plugins/discord-app/send.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "servers", + "description": "List all Discord servers (guilds) in the sidebar", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Server", + "guild_id", + "url" + ], + "type": "js", + "modulePath": "plugins/discord-app/servers.js", + "sourceFile": "plugins/discord-app/servers.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "status", + "description": "Check active CDP connection to Discord Desktop", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "plugins/discord-app/status.js", + "sourceFile": "plugins/discord-app/status.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "thread-read", + "description": "Read recent messages from a Discord thread/post by id or URL", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "thread", + "type": "str", + "required": false, + "help": "Thread/post id, or a full Discord thread/post URL" + }, + { + "name": "count", + "type": "str", + "default": "20", + "required": false, + "help": "Number of messages to read (default: 20)" + }, + { + "name": "guild", + "type": "str", + "required": false, + "help": "Parent guild/server id or visible name" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Parent forum/channel id or visible name" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Discord thread/post URL" + } + ], + "columns": [ + "Author", + "Time", + "Message", + "channel_id", + "message_id" + ], + "type": "js", + "modulePath": "plugins/discord-app/thread-read.js", + "sourceFile": "plugins/discord-app/thread-read.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "threads", + "description": "List visible Discord forum/thread posts in the active or targeted channel", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "limit", + "type": "str", + "default": "30", + "required": false, + "help": "Maximum thread/post cards to return (default: 30)" + }, + { + "name": "guild", + "type": "str", + "required": false, + "help": "Guild/server id or visible name for targeted thread listing" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Forum/channel id or visible name for targeted thread listing" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Discord forum/channel URL to open before listing threads" + } + ], + "columns": [ + "Index", + "Thread", + "Author", + "Updated", + "Preview", + "guild_id", + "channel_id", + "thread_id", + "url" + ], + "type": "js", + "modulePath": "plugins/discord-app/threads.js", + "sourceFile": "plugins/discord-app/threads.js", + "navigateBefore": true + }, + { + "site": "district", + "name": "checkout", + "description": "Select District movie seats and open the UPI QR payment scanner", + "access": "write", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "show", + "type": "str", + "required": true, + "positional": true, + "help": "District seat-layout URL or showId from district showtimes" + }, + { + "name": "seats", + "type": "str", + "required": true, + "help": "Comma-separated seat labels to select, e.g. I22,I21" + }, + { + "name": "format-id", + "type": "str", + "required": false, + "help": "District formatId from showtimes; required when show is a showId" + }, + { + "name": "content-id", + "type": "str", + "required": false, + "help": "District content id; required when show is a showId" + }, + { + "name": "timeout", + "type": "int", + "default": 45, + "required": false, + "help": "Maximum seconds to wait for selection, review page, and payment handoff" + }, + { + "name": "payment", + "type": "str", + "default": "upi-qr", + "required": false, + "help": "Payment handoff target: upi-qr opens the scanner; review stops on order review" + } + ], + "columns": [ + "status", + "movie", + "cinema", + "date", + "time", + "seats", + "ticketCount", + "orderAmount", + "bookingCharge", + "total", + "paymentMethod", + "paymentState", + "upiQrVisible", + "paymentAmount", + "paymentUrl", + "showId" + ], + "type": "js", + "modulePath": "plugins/district/checkout.js", + "sourceFile": "plugins/district/checkout.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true + }, + { + "site": "district", + "name": "listings", + "aliases": [ + "ls" + ], + "description": "List public District by Zomato movies, events, and nearby going-out cards", + "access": "read", + "domain": "www.district.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "input", + "type": "str", + "default": "home", + "required": false, + "positional": true, + "help": "home, movies, events, a district.in URL, or a District path" }, { "name": "limit", @@ -7404,7 +8369,278 @@ "siteSession": "persistent" }, { - "site": "github", + "site": "geogebra", + "name": "add-circle", + "description": "Create a circle by center+radius or center+point", + "access": "write", + "example": "webcmd geogebra add-circle --center A --radius 3", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "center", + "type": "str", + "required": true, + "help": "Center point label (e.g. A)" + }, + { + "name": "radius", + "type": "str", + "required": false, + "help": "Radius value (number) or a point label on the circle" + }, + { + "name": "point", + "type": "str", + "required": false, + "help": "Alternative: a point label on the circle (use instead of --radius for Circle(center,point))" + } + ], + "columns": [ + "label", + "center", + "radius" + ], + "type": "js", + "modulePath": "plugins/geogebra/add-circle.js", + "sourceFile": "plugins/geogebra/add-circle.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "add-line", + "description": "Create a line through two points or a segment between two points", + "access": "write", + "example": "webcmd geogebra add-line --points A,B --type segment", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "points", + "type": "str", + "required": true, + "help": "Two point labels separated by comma (e.g. \"A,B\")" + }, + { + "name": "type", + "type": "str", + "default": "line", + "required": false, + "help": "Type: line, segment, or ray (default: line)", + "choices": [ + "line", + "segment", + "ray" + ] + } + ], + "columns": [ + "label", + "type", + "points" + ], + "type": "js", + "modulePath": "plugins/geogebra/add-line.js", + "sourceFile": "plugins/geogebra/add-line.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "add-point", + "description": "Create a point with given label and coordinates", + "access": "write", + "example": "webcmd geogebra add-point --name A --coords 1,2", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "help": "Point label (e.g. A, B, P1)" + }, + { + "name": "coords", + "type": "str", + "required": true, + "help": "Coordinates as x,y (e.g. \"1,2\")" + } + ], + "columns": [ + "name", + "x", + "y" + ], + "type": "js", + "modulePath": "plugins/geogebra/add-point.js", + "sourceFile": "plugins/geogebra/add-point.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "add-polygon", + "description": "Create a polygon from a list of point labels", + "access": "write", + "example": "webcmd geogebra add-polygon --points A,B,C", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "points", + "type": "str", + "required": true, + "help": "Comma-separated point labels (e.g. \"A,B,C\" or \"A,B,C,D\")" + } + ], + "columns": [ + "label", + "vertices" + ], + "type": "js", + "modulePath": "plugins/geogebra/add-polygon.js", + "sourceFile": "plugins/geogebra/add-polygon.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "eval", + "description": "Execute one or more GeoGebra command strings (semicolon-separated)", + "access": "write", + "example": "webcmd geogebra eval \"A=(0,0);B=(4,0);c=Circle(A,B);d=Circle(B,A);C=Intersect(c,d,1);Polygon(A,B,C)\"", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "command", + "type": "str", + "required": true, + "positional": true, + "help": "GeoGebra command string (use ; to chain multiple commands)" + } + ], + "columns": [ + "command", + "result" + ], + "type": "js", + "modulePath": "plugins/geogebra/eval.js", + "sourceFile": "plugins/geogebra/eval.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "hexagon", + "description": "Draw a regular hexagon centered at the origin", + "access": "write", + "example": "webcmd geogebra hexagon --size 3", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "size", + "type": "str", + "default": "2", + "required": false, + "help": "Radius of the hexagon (default: 2)" + } + ], + "columns": [ + "step", + "result" + ], + "type": "js", + "modulePath": "plugins/geogebra/hexagon.js", + "sourceFile": "plugins/geogebra/hexagon.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "info", + "description": "Get detailed properties of a GeoGebra object", + "access": "read", + "example": "webcmd geogebra info --name A", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "help": "Object label (e.g. A, c1, poly1)" + } + ], + "columns": [ + "property", + "value" + ], + "type": "js", + "modulePath": "plugins/geogebra/info.js", + "sourceFile": "plugins/geogebra/info.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "list", + "description": "List all geometric objects on the GeoGebra canvas", + "access": "read", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "type", + "type": "str", + "required": false, + "help": "Filter by object type (e.g. \"point\", \"line\", \"circle\")" + } + ], + "columns": [ + "name", + "type", + "value", + "visible" + ], + "type": "js", + "modulePath": "plugins/geogebra/list.js", + "sourceFile": "plugins/geogebra/list.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "triangle", + "description": "Draw an equilateral triangle from a horizontal base segment", + "access": "write", + "example": "webcmd geogebra triangle --size 4", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "size", + "type": "str", + "default": "2", + "required": false, + "help": "Side length of the triangle (default: 2)" + } + ], + "columns": [ + "step", + "result" + ], + "type": "js", + "modulePath": "plugins/geogebra/triangle.js", + "sourceFile": "plugins/geogebra/triangle.js", + "navigateBefore": false + }, + { + "site": "github", "name": "login", "description": "Open github login", "access": "write", @@ -12313,46 +13549,251 @@ "navigateBefore": "https://medium.com" }, { - "site": "npm", - "name": "downloads", - "description": "Daily download counts for an npm package over a window", + "site": "mercury", + "name": "check-login", + "description": "Open Mercury reimbursements and report whether the active browser profile is logged in", "access": "read", - "domain": "api.npmjs.org", - "strategy": "public", - "browser": false, + "example": "webcmd --profile mercury check-login -f json", + "domain": "app.mercury.com", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "status", + "loggedIn", + "url", + "hasSubmitExpense", + "hasReimbursements", + "title" + ], + "type": "js", + "modulePath": "plugins/mercury/check-login.js", + "sourceFile": "plugins/mercury/check-login.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "mercury", + "name": "reimbursement-draft", + "description": "Create a Mercury reimbursement draft from a local receipt, correct OCR fields, and stop at Review", + "access": "write", + "example": "webcmd --profile mercury reimbursement-draft --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", + "domain": "app.mercury.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "name", + "name": "receipt", "type": "str", "required": true, - "positional": true, - "help": "npm package name (e.g. \"react\", \"@vercel/og\")" + "help": "Local receipt/proof file path", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp", + "application/pdf" + ], + "maxBytes": 26214400 + } }, { - "name": "period", + "name": "amount", "type": "str", - "default": "last-week", + "required": true, + "help": "Original-currency amount, e.g. 140.00" + }, + { + "name": "currency", + "type": "str", + "default": "CNY", "required": false, - "help": "last-day / last-week / last-month / last-year, or YYYY-MM-DD:YYYY-MM-DD" + "help": "Original currency code" + }, + { + "name": "date", + "type": "str", + "required": true, + "help": "Expense date as YYYY-MM-DD" + }, + { + "name": "merchant", + "type": "str", + "required": true, + "help": "Merchant shown on the reimbursement" + }, + { + "name": "category", + "type": "str", + "default": "Marketing & Advertising", + "required": false, + "help": "Mercury expense category" + }, + { + "name": "notes", + "type": "str", + "required": true, + "help": "Business purpose / reimbursement notes" + }, + { + "name": "ocr-wait-seconds", + "type": "str", + "default": "8", + "required": false, + "help": "Seconds to wait after receipt upload before correcting OCR-overwritten fields" + }, + { + "name": "close-after-review", + "type": "boolean", + "default": false, + "required": false, + "help": "Close the Review dialog after verification; final Submit is still never clicked" } ], "columns": [ - "rank", - "package", - "day", - "downloads" + "status", + "url", + "receipt", + "uploaded", + "fieldsTouched", + "reviewReady", + "submitBlocked", + "warnings" ], "type": "js", - "modulePath": "plugins/npm/downloads.js", - "sourceFile": "plugins/npm/downloads.js" + "modulePath": "plugins/mercury/reimbursement-draft.js", + "sourceFile": "plugins/mercury/reimbursement-draft.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "npm", - "name": "package", - "description": "Single npm package metadata (latest version, license, homepage, repository). Use `npm downloads` for stats.", + "site": "mercury", + "name": "reimbursement-plan", + "description": "Validate Mercury reimbursement inputs and print the draft plan without opening a browser", "access": "read", - "domain": "registry.npmjs.org", - "strategy": "public", + "example": "webcmd mercury reimbursement-plan --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "receipt", + "type": "str", + "required": true, + "help": "Local receipt/proof file path" + }, + { + "name": "amount", + "type": "str", + "required": true, + "help": "Original-currency amount, e.g. 140.00" + }, + { + "name": "currency", + "type": "str", + "default": "CNY", + "required": false, + "help": "Original currency code" + }, + { + "name": "date", + "type": "str", + "required": true, + "help": "Expense date as YYYY-MM-DD" + }, + { + "name": "merchant", + "type": "str", + "required": true, + "help": "Merchant shown on the reimbursement" + }, + { + "name": "category", + "type": "str", + "default": "Marketing & Advertising", + "required": false, + "help": "Mercury expense category" + }, + { + "name": "notes", + "type": "str", + "required": true, + "help": "Business purpose / reimbursement notes" + }, + { + "name": "ocr-wait-seconds", + "type": "str", + "default": "8", + "required": false, + "help": "Seconds the draft command waits after receipt upload before correcting OCR fields" + }, + { + "name": "close-after-review", + "type": "boolean", + "default": false, + "required": false, + "help": "For draft command: close the Review dialog after verification" + } + ], + "columns": [ + "status", + "receipt", + "amount", + "currency", + "date", + "merchant", + "category", + "notes", + "safety" + ], + "type": "js", + "modulePath": "plugins/mercury/reimbursement-plan.js", + "sourceFile": "plugins/mercury/reimbursement-plan.js" + }, + { + "site": "npm", + "name": "downloads", + "description": "Daily download counts for an npm package over a window", + "access": "read", + "domain": "api.npmjs.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "npm package name (e.g. \"react\", \"@vercel/og\")" + }, + { + "name": "period", + "type": "str", + "default": "last-week", + "required": false, + "help": "last-day / last-week / last-month / last-year, or YYYY-MM-DD:YYYY-MM-DD" + } + ], + "columns": [ + "rank", + "package", + "day", + "downloads" + ], + "type": "js", + "modulePath": "plugins/npm/downloads.js", + "sourceFile": "plugins/npm/downloads.js" + }, + { + "site": "npm", + "name": "package", + "description": "Single npm package metadata (latest version, license, homepage, repository). Use `npm downloads` for stats.", + "access": "read", + "domain": "registry.npmjs.org", + "strategy": "public", "browser": false, "args": [ { @@ -13160,6 +14601,173 @@ "modulePath": "plugins/packagist/search.js", "sourceFile": "plugins/packagist/search.js" }, + { + "site": "paperreview", + "name": "feedback", + "description": "Submit feedback for a paperreview.ai review token", + "access": "write", + "domain": "paperreview.ai", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "token", + "type": "str", + "required": true, + "positional": true, + "help": "Review token returned by paperreview.ai" + }, + { + "name": "helpfulness", + "type": "int", + "required": true, + "help": "Helpfulness score from 1 to 5" + }, + { + "name": "critical-error", + "type": "str", + "required": true, + "help": "Whether the review contains a critical error", + "choices": [ + "yes", + "no" + ] + }, + { + "name": "actionable-suggestions", + "type": "str", + "required": true, + "help": "Whether the review contains actionable suggestions", + "choices": [ + "yes", + "no" + ] + }, + { + "name": "additional-comments", + "type": "str", + "required": false, + "help": "Optional free-text feedback" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds for the overall command (default: 30)" + } + ], + "columns": [ + "status", + "token", + "helpfulness", + "critical_error", + "actionable_suggestions", + "message" + ], + "type": "js", + "modulePath": "plugins/paperreview/feedback.js", + "sourceFile": "plugins/paperreview/feedback.js" + }, + { + "site": "paperreview", + "name": "review", + "description": "Fetch a paperreview.ai review by token", + "access": "read", + "domain": "paperreview.ai", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "token", + "type": "str", + "required": true, + "positional": true, + "help": "Review token returned by paperreview.ai" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds for the overall command (default: 30)" + } + ], + "columns": [ + "status", + "title", + "venue", + "numerical_score", + "has_feedback", + "review_url" + ], + "type": "js", + "modulePath": "plugins/paperreview/review.js", + "sourceFile": "plugins/paperreview/review.js" + }, + { + "site": "paperreview", + "name": "submit", + "description": "Submit a PDF to paperreview.ai for review", + "access": "write", + "domain": "paperreview.ai", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "pdf", + "type": "str", + "required": true, + "positional": true, + "help": "Path to the paper PDF" + }, + { + "name": "email", + "type": "str", + "required": true, + "help": "Email address for the submission" + }, + { + "name": "venue", + "type": "str", + "required": false, + "help": "Optional target venue such as ICLR or NeurIPS" + }, + { + "name": "dry-run", + "type": "bool", + "default": false, + "required": false, + "help": "Validate the input and stop before remote submission" + }, + { + "name": "prepare-only", + "type": "bool", + "default": false, + "required": false, + "help": "Request an upload slot but stop before uploading the PDF" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds for the overall command (default: 120)" + } + ], + "columns": [ + "status", + "file", + "email", + "venue", + "token", + "review_url", + "message" + ], + "type": "js", + "modulePath": "plugins/paperreview/submit.js", + "sourceFile": "plugins/paperreview/submit.js" + }, { "site": "pixiv", "name": "detail", @@ -15282,51 +16890,298 @@ "navigateBefore": false }, { - "site": "stackoverflow", - "name": "bounties", - "description": "Active bounties on Stack Overflow", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", + "site": "spotify", + "name": "auth", + "description": "Authenticate with Spotify (OAuth — run once)", + "access": "write", + "strategy": "local", "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max number of results" - } - ], + "args": [], "columns": [ - "rank", - "id", - "bounty", - "title", - "score", - "answers", - "views", - "is_answered", - "tags", - "author", - "creation_date", - "url" + "status" ], "type": "js", - "modulePath": "plugins/stackoverflow/bounties.js", - "sourceFile": "plugins/stackoverflow/bounties.js" + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" }, { - "site": "stackoverflow", - "name": "hot", - "description": "Hot Stack Overflow questions", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", + "site": "spotify", + "name": "next", + "description": "Skip to next track", + "access": "write", + "strategy": "local", "browser": false, - "args": [ - { - "name": "limit", + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" + }, + { + "site": "spotify", + "name": "pause", + "description": "Pause playback", + "access": "write", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" + }, + { + "site": "spotify", + "name": "play", + "description": "Resume playback or search and play a track/artist", + "access": "write", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "default": "", + "required": false, + "positional": true, + "help": "Track or artist to play (optional)" + } + ], + "columns": [ + "track", + "artist", + "status" + ], + "type": "js", + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" + }, + { + "site": "spotify", + "name": "prev", + "description": "Skip to previous track", + "access": "write", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" + }, + { + "site": "spotify", + "name": "queue", + "description": "Add a track to the playback queue", + "access": "write", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Track to add to queue" + } + ], + "columns": [ + "track", + "artist", + "status" + ], + "type": "js", + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" + }, + { + "site": "spotify", + "name": "repeat", + "description": "Set repeat mode (off / track / context)", + "access": "write", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "mode", + "type": "str", + "default": "context", + "required": false, + "positional": true, + "help": "off / track / context", + "choices": [ + "off", + "track", + "context" + ] + } + ], + "columns": [ + "repeat" + ], + "type": "js", + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" + }, + { + "site": "spotify", + "name": "search", + "description": "Search for tracks", + "access": "read", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results (default: 10)" + } + ], + "columns": [ + "track", + "artist", + "album", + "uri" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" + }, + { + "site": "spotify", + "name": "shuffle", + "description": "Toggle shuffle on/off", + "access": "write", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "state", + "type": "str", + "default": "on", + "required": false, + "positional": true, + "help": "on or off", + "choices": [ + "on", + "off" + ] + } + ], + "columns": [ + "shuffle" + ], + "type": "js", + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" + }, + { + "site": "spotify", + "name": "status", + "description": "Show current playback status", + "access": "read", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "track", + "artist", + "album", + "status", + "progress" + ], + "type": "js", + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" + }, + { + "site": "spotify", + "name": "volume", + "description": "Set playback volume (0-100)", + "access": "write", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "level", + "type": "int", + "default": 50, + "required": true, + "positional": true, + "help": "Volume 0–100" + } + ], + "columns": [ + "volume" + ], + "type": "js", + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" + }, + { + "site": "stackoverflow", + "name": "bounties", + "description": "Active bounties on Stack Overflow", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max number of results" + } + ], + "columns": [ + "rank", + "id", + "bounty", + "title", + "score", + "answers", + "views", + "is_answered", + "tags", + "author", + "creation_date", + "url" + ], + "type": "js", + "modulePath": "plugins/stackoverflow/bounties.js", + "sourceFile": "plugins/stackoverflow/bounties.js" + }, + { + "site": "stackoverflow", + "name": "hot", + "description": "Hot Stack Overflow questions", + "access": "read", + "domain": "stackoverflow.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", "type": "int", "default": 10, "required": false, @@ -18124,6 +19979,609 @@ "sourceFile": "plugins/ycombinator/company.js", "navigateBefore": false }, + { + "site": "yollomi", + "name": "background", + "description": "Generate AI background for a product/object image (5 credits)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image URL (upload via \"webcmd yollomi upload\" first)" + }, + { + "name": "prompt", + "type": "str", + "default": "", + "required": false, + "help": "Background description (optional)" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "plugins/yollomi/background.js", + "sourceFile": "plugins/yollomi/background.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "edit", + "description": "Edit images with AI text prompts (Qwen image edit)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Input image URL (upload via \"webcmd yollomi upload\" first)" + }, + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Editing instruction (e.g. \"Make it look vintage\")" + }, + { + "name": "model", + "type": "str", + "default": "qwen-image-edit", + "required": false, + "help": "Edit model", + "choices": [ + "qwen-image-edit", + "qwen-image-edit-plus" + ] + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "credits", + "url" + ], + "type": "js", + "modulePath": "plugins/yollomi/edit.js", + "sourceFile": "plugins/yollomi/edit.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "face-swap", + "description": "Swap faces between two photos (3 credits)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "source", + "type": "str", + "required": true, + "help": "Source face image URL" + }, + { + "name": "target", + "type": "str", + "required": true, + "help": "Target photo URL" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "plugins/yollomi/face-swap.js", + "sourceFile": "plugins/yollomi/face-swap.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "generate", + "description": "Generate images with AI (text-to-image or image-to-image)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Text prompt describing the image" + }, + { + "name": "model", + "type": "str", + "default": "z-image-turbo", + "required": false, + "help": "Model ID (z-image-turbo, flux-schnell, nano-banana, flux-2-pro, ...)" + }, + { + "name": "ratio", + "type": "str", + "default": "1:1", + "required": false, + "help": "Aspect ratio", + "choices": [ + "1:1", + "16:9", + "9:16", + "4:3", + "3:4" + ] + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Input image URL for image-to-image (upload via \"webcmd yollomi upload\" first)" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URLs, skip download" + } + ], + "columns": [ + "index", + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "plugins/yollomi/generate.js", + "sourceFile": "plugins/yollomi/generate.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "models", + "description": "List available Yollomi AI models (image, video, tools)", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "type", + "type": "str", + "default": "all", + "required": false, + "help": "Filter by model type", + "choices": [ + "all", + "image", + "video", + "tool" + ] + } + ], + "columns": [ + "type", + "model", + "credits", + "description" + ], + "type": "js", + "modulePath": "plugins/yollomi/models.js", + "sourceFile": "plugins/yollomi/models.js" + }, + { + "site": "yollomi", + "name": "object-remover", + "description": "Remove unwanted objects from images (3 credits)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image URL" + }, + { + "name": "mask", + "type": "str", + "required": true, + "positional": true, + "help": "Mask image URL (white = area to remove)" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "plugins/yollomi/object-remover.js", + "sourceFile": "plugins/yollomi/object-remover.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "remove-bg", + "description": "Remove image background with AI (free)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image URL to remove background from" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "plugins/yollomi/remove-bg.js", + "sourceFile": "plugins/yollomi/remove-bg.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "restore", + "description": "Restore old or damaged photos with AI (4 credits)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image URL to restore" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "plugins/yollomi/restore.js", + "sourceFile": "plugins/yollomi/restore.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "try-on", + "description": "Virtual try-on — see how clothes look on a person (3 credits)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "person", + "type": "str", + "required": true, + "help": "Person photo URL (upload via \"webcmd yollomi upload\" first)" + }, + { + "name": "cloth", + "type": "str", + "required": true, + "help": "Clothing image URL" + }, + { + "name": "cloth-type", + "type": "str", + "default": "upper", + "required": false, + "help": "Clothing type", + "choices": [ + "upper", + "lower", + "overall" + ] + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "plugins/yollomi/try-on.js", + "sourceFile": "plugins/yollomi/try-on.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "upload", + "description": "Upload an image or video to Yollomi (returns URL for other commands)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "file", + "type": "str", + "required": true, + "positional": true, + "help": "Local file path to upload" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "plugins/yollomi/upload.js", + "sourceFile": "plugins/yollomi/upload.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "upscale", + "description": "Upscale image resolution with AI (1 credit)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image URL to upscale" + }, + { + "name": "scale", + "type": "str", + "default": "2", + "required": false, + "help": "Upscale factor (2 or 4)", + "choices": [ + "2", + "4" + ] + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" + } + ], + "columns": [ + "status", + "file", + "size", + "scale", + "url" + ], + "type": "js", + "modulePath": "plugins/yollomi/upscale.js", + "sourceFile": "plugins/yollomi/upscale.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "video", + "description": "Generate videos with AI (text-to-video or image-to-video)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Text prompt describing the video" + }, + { + "name": "model", + "type": "str", + "default": "kling-2-1", + "required": false, + "help": "Model (kling-2-1, openai-sora-2, google-veo-3-1, wan-2-5-t2v, ...)" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Input image URL for image-to-video" + }, + { + "name": "ratio", + "type": "str", + "default": "16:9", + "required": false, + "help": "Aspect ratio", + "choices": [ + "1:1", + "16:9", + "9:16", + "4:3", + "3:4" + ] + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL, skip download" + } + ], + "columns": [ + "status", + "file", + "size", + "credits", + "url" + ], + "type": "js", + "modulePath": "plugins/yollomi/video.js", + "sourceFile": "plugins/yollomi/video.js", + "navigateBefore": "https://yollomi.com" + }, { "site": "zepto", "name": "add-to-cart", diff --git a/plugins/bigbasket/README.md b/plugins/bigbasket/README.md new file mode 100644 index 00000000..1e2035de --- /dev/null +++ b/plugins/bigbasket/README.md @@ -0,0 +1,21 @@ +# webcmd-plugin-bigbasket + +Webcmd commands for bigbasket. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/bigbasket +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd bigbasket add-to-cart` | Add a BigBasket product to cart | +| `webcmd bigbasket cart` | Read BigBasket cart line items | +| `webcmd bigbasket category` | Read BigBasket category product cards | +| `webcmd bigbasket checkout` | Open BigBasket checkout review without placing an order | +| `webcmd bigbasket location` | Show the selected BigBasket delivery location | +| `webcmd bigbasket product` | Read BigBasket product details | +| `webcmd bigbasket search` | Search BigBasket products | diff --git a/clis/bigbasket/add-to-cart.js b/plugins/bigbasket/add-to-cart.js similarity index 100% rename from clis/bigbasket/add-to-cart.js rename to plugins/bigbasket/add-to-cart.js diff --git a/clis/bigbasket/cart.js b/plugins/bigbasket/cart.js similarity index 100% rename from clis/bigbasket/cart.js rename to plugins/bigbasket/cart.js diff --git a/clis/bigbasket/category.js b/plugins/bigbasket/category.js similarity index 100% rename from clis/bigbasket/category.js rename to plugins/bigbasket/category.js diff --git a/clis/bigbasket/checkout.js b/plugins/bigbasket/checkout.js similarity index 100% rename from clis/bigbasket/checkout.js rename to plugins/bigbasket/checkout.js diff --git a/clis/bigbasket/location.js b/plugins/bigbasket/location.js similarity index 100% rename from clis/bigbasket/location.js rename to plugins/bigbasket/location.js diff --git a/plugins/bigbasket/package.json b/plugins/bigbasket/package.json new file mode 100644 index 00000000..48a655cd --- /dev/null +++ b/plugins/bigbasket/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-bigbasket", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for bigbasket", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/bigbasket/product.js b/plugins/bigbasket/product.js similarity index 100% rename from clis/bigbasket/product.js rename to plugins/bigbasket/product.js diff --git a/clis/bigbasket/search.js b/plugins/bigbasket/search.js similarity index 100% rename from clis/bigbasket/search.js rename to plugins/bigbasket/search.js diff --git a/clis/bigbasket/bigbasket.test.js b/plugins/bigbasket/test/bigbasket.test.js similarity index 97% rename from clis/bigbasket/bigbasket.test.js rename to plugins/bigbasket/test/bigbasket.test.js index 113b5d37..a7a1d874 100644 --- a/clis/bigbasket/bigbasket.test.js +++ b/plugins/bigbasket/test/bigbasket.test.js @@ -2,15 +2,15 @@ import { describe, expect, it } from 'vitest'; import { JSDOM } from 'jsdom'; import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './search.js'; -import './category.js'; -import './product.js'; -import './add-to-cart.js'; -import './cart.js'; -import './checkout.js'; -import './location.js'; -import { CART_EVALUATE } from './cart.js'; -import { CHECKOUT_REVIEW_EVALUATE } from './checkout.js'; +import '../search.js'; +import '../category.js'; +import '../product.js'; +import '../add-to-cart.js'; +import '../cart.js'; +import '../checkout.js'; +import '../location.js'; +import { CART_EVALUATE } from '../cart.js'; +import { CHECKOUT_REVIEW_EVALUATE } from '../checkout.js'; import { buildSearchUrl, normalizeLocationState, @@ -20,7 +20,7 @@ import { productCardsEvaluate, resolveCategoryUrl, resolveProductInput, -} from './utils.js'; +} from '../utils.js'; describe('bigbasket helpers', () => { it('builds search and category URLs', () => { diff --git a/clis/bigbasket/utils.js b/plugins/bigbasket/utils.js similarity index 100% rename from clis/bigbasket/utils.js rename to plugins/bigbasket/utils.js diff --git a/plugins/bigbasket/webcmd-plugin.json b/plugins/bigbasket/webcmd-plugin.json new file mode 100644 index 00000000..3ce7c498 --- /dev/null +++ b/plugins/bigbasket/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "bigbasket", + "version": "0.1.0", + "description": "Webcmd commands for bigbasket", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/chatgpt-app/README.md b/plugins/chatgpt-app/README.md new file mode 100644 index 00000000..73fe84d2 --- /dev/null +++ b/plugins/chatgpt-app/README.md @@ -0,0 +1,20 @@ +# webcmd-plugin-chatgpt-app + +Webcmd commands for chatgpt-app. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/chatgpt-app +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd chatgpt-app ask` | Send a prompt and wait for the AI response (send + wait + read) | +| `webcmd chatgpt-app model` | Switch ChatGPT Desktop model/mode (auto, instant, thinking, 5.2-instant, 5.2-thinking) | +| `webcmd chatgpt-app new` | Open a new chat in ChatGPT Desktop App | +| `webcmd chatgpt-app read` | Read the last visible message from the focused ChatGPT Desktop window | +| `webcmd chatgpt-app send` | Send a message to the active ChatGPT Desktop App window | +| `webcmd chatgpt-app status` | Check if ChatGPT Desktop App is running natively on macOS | diff --git a/clis/chatgpt-app/ask.js b/plugins/chatgpt-app/ask.js similarity index 100% rename from clis/chatgpt-app/ask.js rename to plugins/chatgpt-app/ask.js diff --git a/clis/chatgpt-app/ax.js b/plugins/chatgpt-app/ax.js similarity index 100% rename from clis/chatgpt-app/ax.js rename to plugins/chatgpt-app/ax.js diff --git a/clis/chatgpt-app/model.js b/plugins/chatgpt-app/model.js similarity index 100% rename from clis/chatgpt-app/model.js rename to plugins/chatgpt-app/model.js diff --git a/clis/chatgpt-app/new.js b/plugins/chatgpt-app/new.js similarity index 100% rename from clis/chatgpt-app/new.js rename to plugins/chatgpt-app/new.js diff --git a/plugins/chatgpt-app/package.json b/plugins/chatgpt-app/package.json new file mode 100644 index 00000000..bbaa0d5f --- /dev/null +++ b/plugins/chatgpt-app/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-chatgpt-app", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for chatgpt-app", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/chatgpt-app/read.js b/plugins/chatgpt-app/read.js similarity index 100% rename from clis/chatgpt-app/read.js rename to plugins/chatgpt-app/read.js diff --git a/clis/chatgpt-app/send.js b/plugins/chatgpt-app/send.js similarity index 100% rename from clis/chatgpt-app/send.js rename to plugins/chatgpt-app/send.js diff --git a/clis/chatgpt-app/status.js b/plugins/chatgpt-app/status.js similarity index 100% rename from clis/chatgpt-app/status.js rename to plugins/chatgpt-app/status.js diff --git a/clis/chatgpt-app/ax.test.js b/plugins/chatgpt-app/test/ax.test.js similarity index 99% rename from clis/chatgpt-app/ax.test.js rename to plugins/chatgpt-app/test/ax.test.js index a7f6f060..cc138243 100644 --- a/clis/chatgpt-app/ax.test.js +++ b/plugins/chatgpt-app/test/ax.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { __test__ } from './ax.js'; +import { __test__ } from '../ax.js'; describe('chatgpt-app AX send script', () => { it('prefers the focused composer before falling back to the last editable input', () => { diff --git a/clis/chatgpt-app/commands.test.js b/plugins/chatgpt-app/test/commands.test.js similarity index 92% rename from clis/chatgpt-app/commands.test.js rename to plugins/chatgpt-app/test/commands.test.js index 987f4fa3..d95dd581 100644 --- a/clis/chatgpt-app/commands.test.js +++ b/plugins/chatgpt-app/test/commands.test.js @@ -1,11 +1,11 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './ask.js'; -import './new.js'; -import './send.js'; -import './read.js'; -import './status.js'; -import './model.js'; +import '../ask.js'; +import '../new.js'; +import '../send.js'; +import '../read.js'; +import '../status.js'; +import '../model.js'; describe('chatgpt-app desktop command registration', () => { it('registers the baseline desktop chat commands with localhost scope', () => { diff --git a/plugins/chatgpt-app/webcmd-plugin.json b/plugins/chatgpt-app/webcmd-plugin.json new file mode 100644 index 00000000..36ea71bc --- /dev/null +++ b/plugins/chatgpt-app/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "chatgpt-app", + "version": "0.1.0", + "description": "Webcmd commands for chatgpt-app", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/confluence/README.md b/plugins/confluence/README.md new file mode 100644 index 00000000..5a389552 --- /dev/null +++ b/plugins/confluence/README.md @@ -0,0 +1,18 @@ +# webcmd-plugin-confluence + +Webcmd commands for confluence. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/confluence +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd confluence create` | Create a Confluence page from Markdown or storage XHTML | +| `webcmd confluence page` | Confluence page by id with storage and Markdown body | +| `webcmd confluence search` | Search Confluence content with CQL | +| `webcmd confluence update` | Update a Confluence page body from Markdown or storage XHTML | diff --git a/clis/_atlassian/shared.js b/plugins/confluence/atlassian.js similarity index 54% rename from clis/_atlassian/shared.js rename to plugins/confluence/atlassian.js index b9a7ccee..cd873844 100644 --- a/clis/_atlassian/shared.js +++ b/plugins/confluence/atlassian.js @@ -48,36 +48,27 @@ function parseDeployment(raw, baseUrl) { return host === 'atlassian.net' || host.endsWith('.atlassian.net') ? 'cloud' : 'datacenter'; } -function appendPath(baseUrl, suffix) { - const base = new URL(baseUrl); - const path = base.pathname.replace(/\/+$/, ''); - base.pathname = `${path}${suffix}`; - return base.toString().replace(/\/+$/, ''); -} - function normalizeConfluenceBaseUrl(baseUrl, deployment) { if (deployment !== 'cloud') return baseUrl; const parsed = new URL(baseUrl); - const normalized = parsed.pathname.replace(/\/+$/, ''); - if (normalized === '/wiki' || normalized.endsWith('/wiki')) return baseUrl; - return appendPath(baseUrl, '/wiki'); -} - -function basicAuth(user, token) { - return `Basic ${Buffer.from(`${user}:${token}`, 'utf8').toString('base64')}`; + const path = parsed.pathname.replace(/\/+$/, ''); + if (path === '/wiki' || path.endsWith('/wiki')) return baseUrl; + parsed.pathname = `${path}/wiki`; + return parsed.toString().replace(/\/+$/, ''); } -function resolveAuthHeaders(deployment, productLabel) { +function resolveAuthHeaders(deployment) { const bearer = firstEnv(['ATLASSIAN_BEARER_TOKEN', 'ATLASSIAN_OAUTH_TOKEN']); if (bearer) return { Authorization: `Bearer ${bearer}` }; - const pat = firstEnv(['ATLASSIAN_PAT', `${productLabel.toUpperCase()}_PAT`]); + const pat = firstEnv(['ATLASSIAN_PAT', 'CONFLUENCE_PAT']); if (deployment === 'datacenter' && pat) return { Authorization: `Bearer ${pat}` }; - const prefix = productLabel.toUpperCase(); - const email = firstEnv(['ATLASSIAN_EMAIL', 'ATLASSIAN_USERNAME', `${prefix}_EMAIL`, `${prefix}_USERNAME`]); - const token = firstEnv(['ATLASSIAN_API_TOKEN', 'ATLASSIAN_PASSWORD', `${prefix}_API_TOKEN`, `${prefix}_PASSWORD`]); - if (email && token) return { Authorization: basicAuth(email, token) }; + const email = firstEnv(['ATLASSIAN_EMAIL', 'ATLASSIAN_USERNAME', 'CONFLUENCE_EMAIL', 'CONFLUENCE_USERNAME']); + const token = firstEnv(['ATLASSIAN_API_TOKEN', 'ATLASSIAN_PASSWORD', 'CONFLUENCE_API_TOKEN', 'CONFLUENCE_PASSWORD']); + if (email && token) { + return { Authorization: `Basic ${Buffer.from(`${email}:${token}`, 'utf8').toString('base64')}` }; + } if (deployment === 'cloud') { throw new ConfigError( @@ -91,17 +82,6 @@ function resolveAuthHeaders(deployment, productLabel) { ); } -export function getJiraConfig() { - const baseUrl = normalizeBaseUrl(firstEnv(['ATLASSIAN_JIRA_BASE_URL', 'JIRA_BASE_URL']), 'ATLASSIAN_JIRA_BASE_URL'); - const deployment = parseDeployment(process.env.ATLASSIAN_DEPLOYMENT, baseUrl); - return { - product: 'jira', - baseUrl, - deployment, - authHeaders: resolveAuthHeaders(deployment, 'jira'), - }; -} - export function getConfluenceConfig() { const initialBaseUrl = normalizeBaseUrl( firstEnv(['ATLASSIAN_CONFLUENCE_BASE_URL', 'CONFLUENCE_BASE_URL']), @@ -112,16 +92,10 @@ export function getConfluenceConfig() { product: 'confluence', baseUrl: normalizeConfluenceBaseUrl(initialBaseUrl, deployment), deployment, - authHeaders: resolveAuthHeaders(deployment, 'confluence'), + authHeaders: resolveAuthHeaders(deployment), }; } -function joinUrl(baseUrl, apiPath) { - if (/^https?:\/\//i.test(apiPath)) return apiPath; - const path = apiPath.startsWith('/') ? apiPath : `/${apiPath}`; - return `${baseUrl}${path}`; -} - function summarizeApiError(parsed, fallback) { if (parsed && typeof parsed === 'object') { const messages = []; @@ -130,9 +104,7 @@ function summarizeApiError(parsed, fallback) { if (typeof parsed.error === 'string') messages.push(parsed.error); if (typeof parsed.reason === 'string') messages.push(parsed.reason); if (parsed.errors && typeof parsed.errors === 'object') { - for (const [key, value] of Object.entries(parsed.errors)) { - messages.push(`${key}: ${String(value)}`); - } + for (const [key, value] of Object.entries(parsed.errors)) messages.push(`${key}: ${String(value)}`); } if (messages.length) return messages.join(' \u00b7 '); } @@ -140,13 +112,13 @@ function summarizeApiError(parsed, fallback) { return fallback; } -async function parseResponseBody(resp, label) { +async function parseResponseBody(response, label) { let text; try { - text = await resp.text(); - } catch (err) { + text = await response.text(); + } catch (error) { throw new CommandExecutionError( - `${label} response body could not be read: ${err?.message ?? err}`, + `${label} response body could not be read: ${error?.message ?? error}`, 'Check whether the Atlassian instance, proxy, or network interrupted the response.', ); } @@ -173,46 +145,46 @@ export async function atlassianRequest(config, apiPath, options = {}) { body = typeof options.body === 'string' ? options.body : JSON.stringify(options.body); } - let resp; - const url = joinUrl(config.baseUrl, apiPath); + const url = /^https?:\/\//i.test(apiPath) + ? apiPath + : `${config.baseUrl}${apiPath.startsWith('/') ? apiPath : `/${apiPath}`}`; + let response; try { - resp = await fetch(url, { method, headers, body }); - } catch (err) { + response = await fetch(url, { method, headers, body }); + } catch (error) { throw new CommandExecutionError( - `${label} request failed: ${err?.message ?? err}`, + `${label} request failed: ${error?.message ?? error}`, 'Check the Atlassian base URL, VPN/network access, and proxy settings.', ); } - const parsed = await parseResponseBody(resp, label); - if (resp.status === 401) { + const parsed = await parseResponseBody(response, label); + if (response.status === 401) { throw new AuthRequiredError( config.baseUrl, `${label} returned HTTP 401`, 'Check Atlassian credentials and whether this instance accepts the configured auth method.', ); } - if (resp.status === 403) { + if (response.status === 403) { throw new AuthRequiredError( config.baseUrl, `${label} returned HTTP 403: ${summarizeApiError(parsed, 'forbidden')}`, - 'The authenticated user lacks permission for this Jira issue, Confluence page, or space.', + 'The authenticated user lacks permission for this Confluence page or space.', ); } - if (resp.status === 404) { - throw new EmptyResultError(label, `Atlassian returned 404 for ${url}.`); - } - if (resp.status === 409) { + if (response.status === 404) throw new EmptyResultError(label, `Atlassian returned 404 for ${url}.`); + if (response.status === 409) { throw new CommandExecutionError( `${label} returned HTTP 409: ${summarizeApiError(parsed, 'version conflict')}`, 'Reload the current Confluence page version and retry the update.', ); } - if (resp.status === 429) { + if (response.status === 429) { throw new CommandExecutionError(`${label} returned HTTP 429 (rate limited)`, 'Wait and retry with a smaller limit.'); } - if (!resp.ok) { - throw new CommandExecutionError(`${label} returned HTTP ${resp.status}: ${summarizeApiError(parsed, resp.statusText)}`); + if (!response.ok) { + throw new CommandExecutionError(`${label} returned HTTP ${response.status}: ${summarizeApiError(parsed, response.statusText)}`); } if (typeof parsed === 'string') { throw new CommandExecutionError( @@ -224,23 +196,23 @@ export async function atlassianRequest(config, apiPath, options = {}) { } export function queryString(params) { - const qs = new URLSearchParams(); + const query = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { if (value === undefined || value === null || value === '') continue; if (Array.isArray(value)) { - for (const item of value) qs.append(key, String(item)); + for (const item of value) query.append(key, String(item)); } else { - qs.set(key, String(value)); + query.set(key, String(value)); } } - const s = qs.toString(); - return s ? `?${s}` : ''; + const value = query.toString(); + return value ? `?${value}` : ''; } export function requireString(value, label) { - const s = String(value ?? '').trim(); - if (!s) throw new ArgumentError(`${label} is required`); - return s; + const string = String(value ?? '').trim(); + if (!string) throw new ArgumentError(`${label} is required`); + return string; } export function requirePayloadObject(value, label) { @@ -261,9 +233,9 @@ export function requirePayloadString(value, field, label) { if (typeof value !== 'string' && typeof value !== 'number') { throw new CommandExecutionError(`${label} did not include a stable ${field}.`); } - const s = String(value).trim(); - if (!s) throw new CommandExecutionError(`${label} did not include a stable ${field}.`); - return s; + const string = String(value).trim(); + if (!string) throw new CommandExecutionError(`${label} did not include a stable ${field}.`); + return string; } export function requireNonEmptyRows(rows, label, hint) { @@ -273,14 +245,10 @@ export function requireNonEmptyRows(rows, label, hint) { export function parseLimit(value, defaultValue = 20, maxValue = 100, label = 'limit') { const raw = value ?? defaultValue; - const n = typeof raw === 'number' ? raw : Number(raw); - if (!Number.isInteger(n) || n <= 0) { - throw new ArgumentError(`${label} must be a positive integer`); - } - if (n > maxValue) { - throw new ArgumentError(`${label} must be <= ${maxValue}`); - } - return n; + const parsed = typeof raw === 'number' ? raw : Number(raw); + if (!Number.isInteger(parsed) || parsed <= 0) throw new ArgumentError(`${label} must be a positive integer`); + if (parsed > maxValue) throw new ArgumentError(`${label} must be <= ${maxValue}`); + return parsed; } export function requireExecute(args, commandName) { @@ -297,9 +265,7 @@ export async function readUtf8File(filePath) { } catch { throw new ArgumentError(`File not found: ${path}`); } - if (!fileStat.isFile()) { - throw new ArgumentError(`File must be a readable text file: ${path}`); - } + if (!fileStat.isFile()) throw new ArgumentError(`File must be a readable text file: ${path}`); let raw; try { raw = await readFile(path); @@ -313,7 +279,11 @@ export async function readUtf8File(filePath) { } } -export function htmlEscape(value) { +export function htmlToMarkdown(html) { + return coreHtmlToMarkdown(String(html ?? '')); +} + +function htmlEscape(value) { return String(value ?? '') .replace(/&/g, '&') .replace(/ content.map((child) => renderAdfNode(child, depth)).filter(Boolean).join(sep); - switch (node.type) { - case 'doc': - return content.map((child) => renderAdfNode(child, depth)).filter(Boolean).join('\n\n').trim(); - case 'paragraph': - return renderChildren(''); - case 'text': - return applyAdfMarks(String(node.text ?? ''), Array.isArray(node.marks) ? node.marks : []); - case 'hardBreak': - return '\n'; - case 'heading': - return `${'#'.repeat(Math.max(1, Math.min(6, Number(node.attrs?.level ?? 2))))} ${renderChildren('')}`; - case 'bulletList': - return content.map((child) => renderAdfListItem(child, depth, '-')).join('\n'); - case 'orderedList': - return content.map((child, i) => renderAdfListItem(child, depth, `${i + 1}.`)).join('\n'); - case 'listItem': - return renderChildren('\n'); - case 'codeBlock': - return `\`\`\`\n${renderChildren('')}\n\`\`\``; - case 'blockquote': - return renderChildren('\n').split('\n').map((line) => `> ${line}`).join('\n'); - case 'rule': - return '---'; - case 'table': - return renderAdfTable(content); - case 'tableRow': - return content.map((cell) => escapeMarkdownTableCell(renderAdfNode(cell, depth))).join(' | '); - case 'tableHeader': - case 'tableCell': - return renderChildren(' ').replace(/\s+/g, ' ').trim(); - case 'mention': - return node.attrs?.text ? String(node.attrs.text) : ''; - case 'emoji': - return String(node.attrs?.shortName ?? node.attrs?.text ?? ''); - case 'inlineCard': - return node.attrs?.url ? String(node.attrs.url) : ''; - default: - return renderChildren(''); - } -} - -function renderAdfListItem(node, depth, marker) { - const indent = ' '.repeat(depth); - const body = renderAdfNode(node, depth + 1).trim(); - const lines = body.split('\n'); - const [first, ...rest] = lines; - return `${indent}${marker} ${first ?? ''}${rest.length ? `\n${rest.map((line) => `${indent} ${line}`).join('\n')}` : ''}`; -} - -function escapeMarkdownTableCell(value) { - return String(value ?? '').replace(/\|/g, '\\|').replace(/\n+/g, '
    ').trim(); -} - -function renderAdfTable(rows) { - const matrix = rows - .map((row) => { - const cells = Array.isArray(row?.content) ? row.content : []; - return cells.map((cell) => escapeMarkdownTableCell(renderAdfNode(cell))); - }) - .filter((row) => row.length > 0); - if (!matrix.length) return ''; - const colCount = Math.max(...matrix.map((row) => row.length)); - const normalize = (row) => Array.from({ length: colCount }, (_value, index) => row[index] ?? '').join(' | '); - return [ - normalize(matrix[0]), - Array.from({ length: colCount }, () => '---').join(' | '), - ...matrix.slice(1).map(normalize), - ].join('\n'); -} - -export function adfToMarkdown(value) { - if (!value) return ''; - if (typeof value === 'string') return value.trim(); - return renderAdfNode(value).trim(); -} - function renderInlineMarkdown(value) { - const src = String(value ?? ''); - const linkRe = /\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g; - let out = ''; + const source = String(value ?? ''); + const linkPattern = /\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g; + let output = ''; let last = 0; - for (const match of src.matchAll(linkRe)) { - out += htmlEscape(src.slice(last, match.index)); - out += `${htmlEscape(match[1])}`; + for (const match of source.matchAll(linkPattern)) { + output += htmlEscape(source.slice(last, match.index)); + output += `${htmlEscape(match[1])}`; last = match.index + match[0].length; } - out += htmlEscape(src.slice(last)); - return out + output += htmlEscape(source.slice(last)); + return output .replace(/\*\*([^*]+)\*\*/g, '$1') .replace(/`([^`]+)`/g, '$1'); } @@ -443,10 +316,8 @@ function parseTableRow(line) { } function renderMarkdownTable(lines, start) { - const rows = []; - let index = start; - rows.push(parseTableRow(lines[index])); - index += 2; + const rows = [parseTableRow(lines[start])]; + let index = start + 2; while (index < lines.length && lines[index].includes('|') && lines[index].trim()) { rows.push(parseTableRow(lines[index])); index += 1; @@ -460,32 +331,28 @@ function renderMarkdownTable(lines, start) { export function markdownToConfluenceStorage(markdown) { const lines = String(markdown ?? '').replace(/\r\n/g, '\n').split('\n'); - const out = []; - let i = 0; + const output = []; + const listStack = []; + let index = 0; let inCode = false; let codeLines = []; - const listStack = []; const closeOneList = () => { const current = listStack.pop(); if (!current) return; - if (current.liOpen) out.push(''); - out.push(``); + if (current.liOpen) output.push(''); + output.push(``); }; - const closeListsTo = (indent) => { while (listStack.length && listStack[listStack.length - 1].indent > indent) closeOneList(); }; - const closeAllLists = () => { while (listStack.length) closeOneList(); }; - const openList = (tag, indent) => { - out.push(`<${tag}>`); + output.push(`<${tag}>`); listStack.push({ tag, indent, liOpen: false }); }; - const renderListItem = (tag, indent, text) => { closeListsTo(indent); let current = listStack[listStack.length - 1]; @@ -497,81 +364,64 @@ export function markdownToConfluenceStorage(markdown) { openList(tag, indent); current = listStack[listStack.length - 1]; } - if (current.indent === indent && current.liOpen) { - out.push(''); - current.liOpen = false; - } - out.push(`
  • ${renderInlineMarkdown(text)}`); + if (current.indent === indent && current.liOpen) output.push('
  • '); + output.push(`
  • ${renderInlineMarkdown(text)}`); current.liOpen = true; }; - while (i < lines.length) { - const line = lines[i]; - const fence = line.match(/^```/); - if (fence) { + while (index < lines.length) { + const line = lines[index]; + if (/^```/.test(line)) { if (inCode) { - out.push(``); + output.push(``); codeLines = []; - inCode = false; } else { closeAllLists(); - inCode = true; } - i += 1; + inCode = !inCode; + index += 1; continue; } if (inCode) { codeLines.push(line); - i += 1; + index += 1; continue; } if (!line.trim()) { closeAllLists(); - i += 1; + index += 1; continue; } - if (isMarkdownTable(lines, i)) { + if (isMarkdownTable(lines, index)) { closeAllLists(); - const table = renderMarkdownTable(lines, i); - out.push(table.html); - i = table.next; + const table = renderMarkdownTable(lines, index); + output.push(table.html); + index = table.next; continue; } const heading = line.match(/^(#{1,6})\s+(.+)$/); if (heading) { closeAllLists(); - out.push(`${renderInlineMarkdown(heading[2])}`); - i += 1; + output.push(`${renderInlineMarkdown(heading[2])}`); + index += 1; continue; } const unordered = line.match(/^(\s*)[-*]\s+(.+)$/); const ordered = line.match(/^(\s*)\d+\.\s+(.+)$/); if (unordered || ordered) { const match = unordered || ordered; - const indent = match[1].replace(/\t/g, ' ').length; - renderListItem(unordered ? 'ul' : 'ol', indent, match[2]); - i += 1; + renderListItem(unordered ? 'ul' : 'ol', match[1].replace(/\t/g, ' ').length, match[2]); + index += 1; continue; } closeAllLists(); - out.push(`

    ${renderInlineMarkdown(line)}

    `); - i += 1; + output.push(`

    ${renderInlineMarkdown(line)}

    `); + index += 1; } closeAllLists(); if (inCode) { - out.push(``); + output.push(``); } - return out.join('\n'); + return output.join('\n'); } - -export const __test__ = { - adfToMarkdown, - atlassianRequest, - getConfluenceConfig, - getJiraConfig, - htmlToMarkdown, - markdownToConfluenceStorage, - parseLimit, - queryString, -}; diff --git a/clis/confluence/create.js b/plugins/confluence/create.js similarity index 95% rename from clis/confluence/create.js rename to plugins/confluence/create.js index 5be44ea4..da7e8d55 100644 --- a/clis/confluence/create.js +++ b/plugins/confluence/create.js @@ -1,7 +1,7 @@ import { cli, Strategy } from '@agentrhq/webcmd/registry'; -import { requireExecute, requirePayloadObject, requireString } from '../_atlassian/shared.js'; +import { requireExecute, requirePayloadObject, requireString } from './atlassian.js'; import { confluenceConfig, createPagePayload, normalizeConfluencePage, readPageBodyFile } from './shared.js'; -import { atlassianRequest } from '../_atlassian/shared.js'; +import { atlassianRequest } from './atlassian.js'; cli({ site: 'confluence', diff --git a/plugins/confluence/package.json b/plugins/confluence/package.json new file mode 100644 index 00000000..6effe708 --- /dev/null +++ b/plugins/confluence/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-confluence", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for confluence", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/confluence/page.js b/plugins/confluence/page.js similarity index 93% rename from clis/confluence/page.js rename to plugins/confluence/page.js index 91d25429..f836c4c1 100644 --- a/clis/confluence/page.js +++ b/plugins/confluence/page.js @@ -1,5 +1,5 @@ import { cli, Strategy } from '@agentrhq/webcmd/registry'; -import { requireString } from '../_atlassian/shared.js'; +import { requireString } from './atlassian.js'; import { confluenceConfig, getPage, normalizeConfluencePage } from './shared.js'; cli({ diff --git a/clis/confluence/search.js b/plugins/confluence/search.js similarity index 96% rename from clis/confluence/search.js rename to plugins/confluence/search.js index 4b1d03f3..d97809ea 100644 --- a/clis/confluence/search.js +++ b/plugins/confluence/search.js @@ -1,5 +1,5 @@ import { cli, Strategy } from '@agentrhq/webcmd/registry'; -import { atlassianRequest, parseLimit, queryString, requireNonEmptyRows, requireString } from '../_atlassian/shared.js'; +import { atlassianRequest, parseLimit, queryString, requireNonEmptyRows, requireString } from './atlassian.js'; import { confluenceConfig, confluenceResults, normalizeSearchResult, withSpaceCql } from './shared.js'; cli({ diff --git a/clis/confluence/shared.js b/plugins/confluence/shared.js similarity index 99% rename from clis/confluence/shared.js rename to plugins/confluence/shared.js index c41ac36c..d3fe15f0 100644 --- a/clis/confluence/shared.js +++ b/plugins/confluence/shared.js @@ -9,7 +9,7 @@ import { requirePayloadString, readUtf8File, requireString, -} from '../_atlassian/shared.js'; +} from './atlassian.js'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; export function confluenceConfig() { diff --git a/plugins/confluence/test/atlassian.test.js b/plugins/confluence/test/atlassian.test.js new file mode 100644 index 00000000..642ceb65 --- /dev/null +++ b/plugins/confluence/test/atlassian.test.js @@ -0,0 +1,101 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { CommandExecutionError } from '@agentrhq/webcmd/errors'; +import { + atlassianRequest, + getConfluenceConfig, + htmlToMarkdown, + markdownToConfluenceStorage, +} from '../atlassian.js'; + +const ENV_KEYS = [ + 'ATLASSIAN_BEARER_TOKEN', + 'ATLASSIAN_CONFLUENCE_BASE_URL', + 'ATLASSIAN_DEPLOYMENT', + 'ATLASSIAN_EMAIL', + 'ATLASSIAN_API_TOKEN', + 'ATLASSIAN_OAUTH_TOKEN', + 'ATLASSIAN_PAT', + 'ATLASSIAN_PASSWORD', + 'ATLASSIAN_USERNAME', + 'CONFLUENCE_API_TOKEN', + 'CONFLUENCE_BASE_URL', + 'CONFLUENCE_EMAIL', + 'CONFLUENCE_PASSWORD', + 'CONFLUENCE_PAT', + 'CONFLUENCE_USERNAME', +]; + +function clearEnv() { + for (const key of ENV_KEYS) delete process.env[key]; +} + +afterEach(() => { + clearEnv(); + vi.unstubAllGlobals(); +}); + +describe('confluence atlassian helpers', () => { + it('builds Confluence Cloud and Data Center authentication', () => { + process.env.ATLASSIAN_CONFLUENCE_BASE_URL = 'https://team.atlassian.net'; + process.env.ATLASSIAN_EMAIL = 'bot@example.com'; + process.env.ATLASSIAN_API_TOKEN = 'secret'; + expect(getConfluenceConfig()).toMatchObject({ + baseUrl: 'https://team.atlassian.net/wiki', + deployment: 'cloud', + authHeaders: { Authorization: `Basic ${Buffer.from('bot@example.com:secret').toString('base64')}` }, + }); + + clearEnv(); + process.env.ATLASSIAN_CONFLUENCE_BASE_URL = 'https://confluence.example.com/confluence'; + process.env.ATLASSIAN_DEPLOYMENT = 'datacenter'; + process.env.ATLASSIAN_PAT = 'pat-123'; + expect(getConfluenceConfig()).toMatchObject({ + baseUrl: 'https://confluence.example.com/confluence', + deployment: 'datacenter', + authHeaders: { Authorization: 'Bearer pat-123' }, + }); + }); + + it('converts Confluence HTML and Markdown storage formats', () => { + expect(htmlToMarkdown('

    Fixed
    Ready

    ')).toContain('**Fixed**'); + const storage = markdownToConfluenceStorage([ + '# RCA', + '', + '- Parent', + ' - Child', + '', + '| Service | Status |', + '| --- | --- |', + '| payments | fixed |', + ].join('\n')); + expect(storage.replace(/\s*\n\s*/g, '')).toContain('
    • Parent
      • Child
    '); + expect(storage).toContain('

    RCA

    '); + expect(storage).toContain('
  • '); + }); + + it('sends JSON requests and preserves typed failures', async () => { + const config = { + product: 'confluence', + baseUrl: 'https://confluence.example.com', + deployment: 'datacenter', + authHeaders: { Authorization: 'Bearer token' }, + }; + const fetchMock = vi.fn(async () => new Response(JSON.stringify({ ok: true }), { status: 200 })); + vi.stubGlobal('fetch', fetchMock); + await expect(atlassianRequest(config, '/rest/api/content', { label: 'confluence content' })).resolves.toEqual({ ok: true }); + expect(fetchMock.mock.calls[0][0]).toBe('https://confluence.example.com/rest/api/content'); + expect(fetchMock.mock.calls[0][1].headers.Authorization).toBe('Bearer token'); + + vi.stubGlobal('fetch', vi.fn(async () => new Response(JSON.stringify({ message: 'bad token' }), { status: 401 }))); + await expect(atlassianRequest(config, '/rest/api/content', { label: 'confluence content' })) + .rejects.toMatchObject({ code: 'AUTH_REQUIRED' }); + + vi.stubGlobal('fetch', vi.fn(async () => new Response(JSON.stringify({ message: 'slow down' }), { status: 429 }))); + await expect(atlassianRequest(config, '/rest/api/content', { label: 'confluence content' })) + .rejects.toMatchObject({ code: 'COMMAND_EXEC' }); + + vi.stubGlobal('fetch', vi.fn(async () => new Response('login', { status: 200 }))); + await expect(atlassianRequest(config, '/rest/api/content', { label: 'confluence content' })) + .rejects.toBeInstanceOf(CommandExecutionError); + }); +}); diff --git a/clis/confluence/commands.test.js b/plugins/confluence/test/commands.test.js similarity index 98% rename from clis/confluence/commands.test.js rename to plugins/confluence/test/commands.test.js index dbf4d47d..9158ff6b 100644 --- a/clis/confluence/commands.test.js +++ b/plugins/confluence/test/commands.test.js @@ -4,10 +4,10 @@ import { tmpdir } from 'node:os'; import { describe, expect, it, afterEach, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './page.js'; -import './search.js'; -import './create.js'; -import './update.js'; +import '../page.js'; +import '../search.js'; +import '../create.js'; +import '../update.js'; const ENV_KEYS = [ 'ATLASSIAN_CONFLUENCE_BASE_URL', diff --git a/clis/confluence/update.js b/plugins/confluence/update.js similarity index 97% rename from clis/confluence/update.js rename to plugins/confluence/update.js index 536b01a1..bb371f66 100644 --- a/clis/confluence/update.js +++ b/plugins/confluence/update.js @@ -1,5 +1,5 @@ import { cli, Strategy } from '@agentrhq/webcmd/registry'; -import { atlassianRequest, requireExecute, requirePayloadObject, requireString } from '../_atlassian/shared.js'; +import { atlassianRequest, requireExecute, requirePayloadObject, requireString } from './atlassian.js'; import { confluenceConfig, getPage, normalizeConfluencePage, readPageBodyFile, updatePagePayload } from './shared.js'; cli({ diff --git a/plugins/confluence/webcmd-plugin.json b/plugins/confluence/webcmd-plugin.json new file mode 100644 index 00000000..33c22b31 --- /dev/null +++ b/plugins/confluence/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "confluence", + "version": "0.1.0", + "description": "Webcmd commands for confluence", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/discord-app/README.md b/plugins/discord-app/README.md new file mode 100644 index 00000000..fb35ca1f --- /dev/null +++ b/plugins/discord-app/README.md @@ -0,0 +1,25 @@ +# webcmd-plugin-discord-app + +Webcmd commands for discord-app. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/discord-app +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd discord-app channels` | List channels in the current Discord server | +| `webcmd discord-app delete` | Delete a message by its ID in the active Discord channel | +| `webcmd discord-app goto` | Open a Discord channel by id/name/url without sending messages | +| `webcmd discord-app members` | List online members in the current Discord channel | +| `webcmd discord-app read` | Read recent messages from the active or targeted Discord channel | +| `webcmd discord-app search` | Search messages in the current Discord server/channel (Cmd+F) | +| `webcmd discord-app send` | Send a message in the active Discord channel | +| `webcmd discord-app servers` | List all Discord servers (guilds) in the sidebar | +| `webcmd discord-app status` | Check active CDP connection to Discord Desktop | +| `webcmd discord-app thread-read` | Read recent messages from a Discord thread/post by id or URL | +| `webcmd discord-app threads` | List visible Discord forum/thread posts in the active or targeted channel | diff --git a/clis/discord-app/channels.js b/plugins/discord-app/channels.js similarity index 100% rename from clis/discord-app/channels.js rename to plugins/discord-app/channels.js diff --git a/clis/discord-app/delete.js b/plugins/discord-app/delete.js similarity index 100% rename from clis/discord-app/delete.js rename to plugins/discord-app/delete.js diff --git a/clis/discord-app/goto.js b/plugins/discord-app/goto.js similarity index 100% rename from clis/discord-app/goto.js rename to plugins/discord-app/goto.js diff --git a/clis/discord-app/members.js b/plugins/discord-app/members.js similarity index 100% rename from clis/discord-app/members.js rename to plugins/discord-app/members.js diff --git a/plugins/discord-app/package.json b/plugins/discord-app/package.json new file mode 100644 index 00000000..e315de0c --- /dev/null +++ b/plugins/discord-app/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-discord-app", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for discord-app", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/discord-app/read.js b/plugins/discord-app/read.js similarity index 100% rename from clis/discord-app/read.js rename to plugins/discord-app/read.js diff --git a/clis/discord-app/search.js b/plugins/discord-app/search.js similarity index 100% rename from clis/discord-app/search.js rename to plugins/discord-app/search.js diff --git a/clis/discord-app/send.js b/plugins/discord-app/send.js similarity index 100% rename from clis/discord-app/send.js rename to plugins/discord-app/send.js diff --git a/clis/discord-app/servers.js b/plugins/discord-app/servers.js similarity index 100% rename from clis/discord-app/servers.js rename to plugins/discord-app/servers.js diff --git a/clis/discord-app/status.js b/plugins/discord-app/status.js similarity index 100% rename from clis/discord-app/status.js rename to plugins/discord-app/status.js diff --git a/clis/discord-app/commands.test.js b/plugins/discord-app/test/commands.test.js similarity index 98% rename from clis/discord-app/commands.test.js rename to plugins/discord-app/test/commands.test.js index 1440e3bd..e81672d8 100644 --- a/clis/discord-app/commands.test.js +++ b/plugins/discord-app/test/commands.test.js @@ -2,13 +2,13 @@ import { describe, expect, it, vi } from 'vitest'; import { JSDOM } from 'jsdom'; import { CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './channels.js'; -import './goto.js'; -import './read.js'; -import './search.js'; -import './servers.js'; -import './thread-read.js'; -import './threads.js'; +import '../channels.js'; +import '../goto.js'; +import '../read.js'; +import '../search.js'; +import '../servers.js'; +import '../thread-read.js'; +import '../threads.js'; import { buildDiscordChannelUrl, buildListChannelsScript, @@ -18,7 +18,7 @@ import { listDiscordThreads, parseDiscordChannelUrl, resolveDiscordChannelTarget, -} from './utils.js'; +} from '../utils.js'; function runDomScript(html, script, url = 'https://discord.com/channels/111/222') { const dom = new JSDOM(html, { url, runScripts: 'outside-only' }); diff --git a/clis/discord-app/thread-read.js b/plugins/discord-app/thread-read.js similarity index 100% rename from clis/discord-app/thread-read.js rename to plugins/discord-app/thread-read.js diff --git a/clis/discord-app/threads.js b/plugins/discord-app/threads.js similarity index 100% rename from clis/discord-app/threads.js rename to plugins/discord-app/threads.js diff --git a/clis/discord-app/utils.js b/plugins/discord-app/utils.js similarity index 100% rename from clis/discord-app/utils.js rename to plugins/discord-app/utils.js diff --git a/plugins/discord-app/webcmd-plugin.json b/plugins/discord-app/webcmd-plugin.json new file mode 100644 index 00000000..6ca6fbb3 --- /dev/null +++ b/plugins/discord-app/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "discord-app", + "version": "0.1.0", + "description": "Webcmd commands for discord-app", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/geogebra/README.md b/plugins/geogebra/README.md new file mode 100644 index 00000000..73f81a8f --- /dev/null +++ b/plugins/geogebra/README.md @@ -0,0 +1,23 @@ +# webcmd-plugin-geogebra + +Webcmd commands for geogebra. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/geogebra +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd geogebra add-circle` | Create a circle by center+radius or center+point | +| `webcmd geogebra add-line` | Create a line through two points or a segment between two points | +| `webcmd geogebra add-point` | Create a point with given label and coordinates | +| `webcmd geogebra add-polygon` | Create a polygon from a list of point labels | +| `webcmd geogebra eval` | Execute one or more GeoGebra command strings (semicolon-separated) | +| `webcmd geogebra hexagon` | Draw a regular hexagon centered at the origin | +| `webcmd geogebra info` | Get detailed properties of a GeoGebra object | +| `webcmd geogebra list` | List all geometric objects on the GeoGebra canvas | +| `webcmd geogebra triangle` | Draw an equilateral triangle from a horizontal base segment | diff --git a/clis/geogebra/add-circle.js b/plugins/geogebra/add-circle.js similarity index 100% rename from clis/geogebra/add-circle.js rename to plugins/geogebra/add-circle.js diff --git a/clis/geogebra/add-line.js b/plugins/geogebra/add-line.js similarity index 100% rename from clis/geogebra/add-line.js rename to plugins/geogebra/add-line.js diff --git a/clis/geogebra/add-point.js b/plugins/geogebra/add-point.js similarity index 100% rename from clis/geogebra/add-point.js rename to plugins/geogebra/add-point.js diff --git a/clis/geogebra/add-polygon.js b/plugins/geogebra/add-polygon.js similarity index 100% rename from clis/geogebra/add-polygon.js rename to plugins/geogebra/add-polygon.js diff --git a/clis/geogebra/eval.js b/plugins/geogebra/eval.js similarity index 100% rename from clis/geogebra/eval.js rename to plugins/geogebra/eval.js diff --git a/clis/geogebra/hexagon.js b/plugins/geogebra/hexagon.js similarity index 100% rename from clis/geogebra/hexagon.js rename to plugins/geogebra/hexagon.js diff --git a/clis/geogebra/info.js b/plugins/geogebra/info.js similarity index 100% rename from clis/geogebra/info.js rename to plugins/geogebra/info.js diff --git a/clis/geogebra/list.js b/plugins/geogebra/list.js similarity index 100% rename from clis/geogebra/list.js rename to plugins/geogebra/list.js diff --git a/plugins/geogebra/package.json b/plugins/geogebra/package.json new file mode 100644 index 00000000..88de598e --- /dev/null +++ b/plugins/geogebra/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-geogebra", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for geogebra", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/geogebra/geogebra.test.js b/plugins/geogebra/test/geogebra.test.js similarity index 96% rename from clis/geogebra/geogebra.test.js rename to plugins/geogebra/test/geogebra.test.js index a5130666..92b58491 100644 --- a/clis/geogebra/geogebra.test.js +++ b/plugins/geogebra/test/geogebra.test.js @@ -1,16 +1,16 @@ import { describe, expect, it, vi } from 'vitest'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { ensureApplet, ggbEval, ggbGetProperty, ggbListObjects, ggbWaitForObjectCount } from './utils.js'; -import './add-circle.js'; -import './add-line.js'; -import './add-point.js'; -import './add-polygon.js'; -import './eval.js'; -import './hexagon.js'; -import './info.js'; -import './list.js'; -import './triangle.js'; +import { ensureApplet, ggbEval, ggbGetProperty, ggbListObjects, ggbWaitForObjectCount } from '../utils.js'; +import '../add-circle.js'; +import '../add-line.js'; +import '../add-point.js'; +import '../add-polygon.js'; +import '../eval.js'; +import '../hexagon.js'; +import '../info.js'; +import '../list.js'; +import '../triangle.js'; function createPageMock(url = 'https://www.geogebra.org/geometry') { return { diff --git a/clis/geogebra/triangle.js b/plugins/geogebra/triangle.js similarity index 100% rename from clis/geogebra/triangle.js rename to plugins/geogebra/triangle.js diff --git a/clis/geogebra/utils.js b/plugins/geogebra/utils.js similarity index 100% rename from clis/geogebra/utils.js rename to plugins/geogebra/utils.js diff --git a/plugins/geogebra/webcmd-plugin.json b/plugins/geogebra/webcmd-plugin.json new file mode 100644 index 00000000..8d2a3466 --- /dev/null +++ b/plugins/geogebra/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "geogebra", + "version": "0.1.0", + "description": "Webcmd commands for geogebra", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/mercury/README.md b/plugins/mercury/README.md new file mode 100644 index 00000000..b396481a --- /dev/null +++ b/plugins/mercury/README.md @@ -0,0 +1,17 @@ +# webcmd-plugin-mercury + +Webcmd commands for mercury. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/mercury +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd mercury check-login` | Open Mercury reimbursements and report whether the active browser profile is logged in | +| `webcmd mercury reimbursement-draft` | Create a Mercury reimbursement draft from a local receipt, correct OCR fields, and stop at Review | +| `webcmd mercury reimbursement-plan` | Validate Mercury reimbursement inputs and print the draft plan without opening a browser | diff --git a/clis/mercury/check-login.js b/plugins/mercury/check-login.js similarity index 100% rename from clis/mercury/check-login.js rename to plugins/mercury/check-login.js diff --git a/plugins/mercury/package.json b/plugins/mercury/package.json new file mode 100644 index 00000000..2de5b782 --- /dev/null +++ b/plugins/mercury/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-mercury", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for mercury", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/mercury/reimbursement-draft.js b/plugins/mercury/reimbursement-draft.js similarity index 100% rename from clis/mercury/reimbursement-draft.js rename to plugins/mercury/reimbursement-draft.js diff --git a/clis/mercury/reimbursement-plan.js b/plugins/mercury/reimbursement-plan.js similarity index 100% rename from clis/mercury/reimbursement-plan.js rename to plugins/mercury/reimbursement-plan.js diff --git a/clis/mercury/mercury.test.js b/plugins/mercury/test/mercury.test.js similarity index 98% rename from clis/mercury/mercury.test.js rename to plugins/mercury/test/mercury.test.js index 5486b7dc..01a94372 100644 --- a/clis/mercury/mercury.test.js +++ b/plugins/mercury/test/mercury.test.js @@ -4,10 +4,10 @@ import path from 'node:path'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { normalizeReimbursementInput } from './utils.js'; -import './check-login.js'; -import './reimbursement-draft.js'; -import './reimbursement-plan.js'; +import { normalizeReimbursementInput } from '../utils.js'; +import '../check-login.js'; +import '../reimbursement-draft.js'; +import '../reimbursement-plan.js'; let tmpDir; let receiptPath; diff --git a/clis/mercury/utils.js b/plugins/mercury/utils.js similarity index 100% rename from clis/mercury/utils.js rename to plugins/mercury/utils.js diff --git a/plugins/mercury/webcmd-plugin.json b/plugins/mercury/webcmd-plugin.json new file mode 100644 index 00000000..900dd7b9 --- /dev/null +++ b/plugins/mercury/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "mercury", + "version": "0.1.0", + "description": "Webcmd commands for mercury", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/paperreview/README.md b/plugins/paperreview/README.md new file mode 100644 index 00000000..868805ce --- /dev/null +++ b/plugins/paperreview/README.md @@ -0,0 +1,17 @@ +# webcmd-plugin-paperreview + +Webcmd commands for paperreview. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/paperreview +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd paperreview feedback` | Submit feedback for a paperreview.ai review token | +| `webcmd paperreview review` | Fetch a paperreview.ai review by token | +| `webcmd paperreview submit` | Submit a PDF to paperreview.ai for review | diff --git a/clis/paperreview/feedback.js b/plugins/paperreview/feedback.js similarity index 100% rename from clis/paperreview/feedback.js rename to plugins/paperreview/feedback.js diff --git a/plugins/paperreview/package.json b/plugins/paperreview/package.json new file mode 100644 index 00000000..5fe2869b --- /dev/null +++ b/plugins/paperreview/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-paperreview", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for paperreview", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/paperreview/review.js b/plugins/paperreview/review.js similarity index 100% rename from clis/paperreview/review.js rename to plugins/paperreview/review.js diff --git a/clis/paperreview/submit.js b/plugins/paperreview/submit.js similarity index 100% rename from clis/paperreview/submit.js rename to plugins/paperreview/submit.js diff --git a/clis/paperreview/commands.test.js b/plugins/paperreview/test/commands.test.js similarity index 98% rename from clis/paperreview/commands.test.js rename to plugins/paperreview/test/commands.test.js index d020c976..4e50ad68 100644 --- a/clis/paperreview/commands.test.js +++ b/plugins/paperreview/test/commands.test.js @@ -6,8 +6,8 @@ const { mockReadPdfFile, mockRequestJson, mockUploadPresignedPdf, mockValidateHe mockValidateHelpfulness: vi.fn(), mockParseYesNo: vi.fn(), })); -vi.mock('./utils.js', async () => { - const actual = await vi.importActual('./utils.js'); +vi.mock('../utils.js', async () => { + const actual = await vi.importActual('../utils.js'); return { ...actual, readPdfFile: mockReadPdfFile, @@ -18,9 +18,9 @@ vi.mock('./utils.js', async () => { }; }); import { getRegistry } from '@agentrhq/webcmd/registry'; -import './submit.js'; -import './review.js'; -import './feedback.js'; +import '../submit.js'; +import '../review.js'; +import '../feedback.js'; describe('paperreview submit command', () => { beforeEach(() => { mockReadPdfFile.mockReset(); diff --git a/clis/paperreview/utils.test.js b/plugins/paperreview/test/utils.test.js similarity index 97% rename from clis/paperreview/utils.test.js rename to plugins/paperreview/test/utils.test.js index a4c999e1..4e228929 100644 --- a/clis/paperreview/utils.test.js +++ b/plugins/paperreview/test/utils.test.js @@ -3,7 +3,7 @@ import * as os from 'node:os'; import * as path from 'node:path'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { CliError } from '@agentrhq/webcmd/errors'; -import { MAX_PDF_BYTES, buildReviewUrl, parseYesNo, readPdfFile, requestJson, validateHelpfulness, } from './utils.js'; +import { MAX_PDF_BYTES, buildReviewUrl, parseYesNo, readPdfFile, requestJson, validateHelpfulness, } from '../utils.js'; describe('paperreview utils', () => { beforeEach(() => { vi.restoreAllMocks(); diff --git a/clis/paperreview/utils.js b/plugins/paperreview/utils.js similarity index 100% rename from clis/paperreview/utils.js rename to plugins/paperreview/utils.js diff --git a/plugins/paperreview/webcmd-plugin.json b/plugins/paperreview/webcmd-plugin.json new file mode 100644 index 00000000..a3a6d2f3 --- /dev/null +++ b/plugins/paperreview/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "paperreview", + "version": "0.1.0", + "description": "Webcmd commands for paperreview", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/spotify/README.md b/plugins/spotify/README.md new file mode 100644 index 00000000..5b1512bb --- /dev/null +++ b/plugins/spotify/README.md @@ -0,0 +1,25 @@ +# webcmd-plugin-spotify + +Webcmd commands for spotify. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/spotify +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd spotify auth` | Authenticate with Spotify (OAuth — run once) | +| `webcmd spotify next` | Skip to next track | +| `webcmd spotify pause` | Pause playback | +| `webcmd spotify play` | Resume playback or search and play a track/artist | +| `webcmd spotify prev` | Skip to previous track | +| `webcmd spotify queue` | Add a track to the playback queue | +| `webcmd spotify repeat` | Set repeat mode (off / track / context) | +| `webcmd spotify search` | Search for tracks | +| `webcmd spotify shuffle` | Toggle shuffle on/off | +| `webcmd spotify status` | Show current playback status | +| `webcmd spotify volume` | Set playback volume (0-100) | diff --git a/plugins/spotify/package.json b/plugins/spotify/package.json new file mode 100644 index 00000000..b74ab084 --- /dev/null +++ b/plugins/spotify/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-spotify", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for spotify", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/spotify/spotify.js b/plugins/spotify/spotify.js similarity index 100% rename from clis/spotify/spotify.js rename to plugins/spotify/spotify.js diff --git a/clis/spotify/utils.test.js b/plugins/spotify/test/utils.test.js similarity index 97% rename from clis/spotify/utils.test.js rename to plugins/spotify/test/utils.test.js index 5f99b973..58b76b97 100644 --- a/clis/spotify/utils.test.js +++ b/plugins/spotify/test/utils.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { assertSpotifyCredentialsConfigured, getFirstSpotifyTrack, hasConfiguredSpotifyCredentials, mapSpotifyTrackResults, parseDotEnv, resolveSpotifyCredentials, } from './utils.js'; +import { assertSpotifyCredentialsConfigured, getFirstSpotifyTrack, hasConfiguredSpotifyCredentials, mapSpotifyTrackResults, parseDotEnv, resolveSpotifyCredentials, } from '../utils.js'; describe('spotify utils', () => { it('parses dotenv-style credential files', () => { const env = parseDotEnv(` diff --git a/clis/spotify/utils.js b/plugins/spotify/utils.js similarity index 100% rename from clis/spotify/utils.js rename to plugins/spotify/utils.js diff --git a/plugins/spotify/webcmd-plugin.json b/plugins/spotify/webcmd-plugin.json new file mode 100644 index 00000000..a91d4433 --- /dev/null +++ b/plugins/spotify/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "spotify", + "version": "0.1.0", + "description": "Webcmd commands for spotify", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/yollomi/README.md b/plugins/yollomi/README.md new file mode 100644 index 00000000..971b6c08 --- /dev/null +++ b/plugins/yollomi/README.md @@ -0,0 +1,26 @@ +# webcmd-plugin-yollomi + +Webcmd commands for yollomi. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/yollomi +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd yollomi background` | Generate AI background for a product/object image (5 credits) | +| `webcmd yollomi edit` | Edit images with AI text prompts (Qwen image edit) | +| `webcmd yollomi face-swap` | Swap faces between two photos (3 credits) | +| `webcmd yollomi generate` | Generate images with AI (text-to-image or image-to-image) | +| `webcmd yollomi models` | List available Yollomi AI models (image, video, tools) | +| `webcmd yollomi object-remover` | Remove unwanted objects from images (3 credits) | +| `webcmd yollomi remove-bg` | Remove image background with AI (free) | +| `webcmd yollomi restore` | Restore old or damaged photos with AI (4 credits) | +| `webcmd yollomi try-on` | Virtual try-on — see how clothes look on a person (3 credits) | +| `webcmd yollomi upload` | Upload an image or video to Yollomi (returns URL for other commands) | +| `webcmd yollomi upscale` | Upscale image resolution with AI (1 credit) | +| `webcmd yollomi video` | Generate videos with AI (text-to-video or image-to-video) | diff --git a/clis/yollomi/background.js b/plugins/yollomi/background.js similarity index 100% rename from clis/yollomi/background.js rename to plugins/yollomi/background.js diff --git a/clis/yollomi/edit.js b/plugins/yollomi/edit.js similarity index 100% rename from clis/yollomi/edit.js rename to plugins/yollomi/edit.js diff --git a/clis/yollomi/face-swap.js b/plugins/yollomi/face-swap.js similarity index 100% rename from clis/yollomi/face-swap.js rename to plugins/yollomi/face-swap.js diff --git a/clis/yollomi/generate.js b/plugins/yollomi/generate.js similarity index 100% rename from clis/yollomi/generate.js rename to plugins/yollomi/generate.js diff --git a/clis/yollomi/models.js b/plugins/yollomi/models.js similarity index 100% rename from clis/yollomi/models.js rename to plugins/yollomi/models.js diff --git a/clis/yollomi/object-remover.js b/plugins/yollomi/object-remover.js similarity index 100% rename from clis/yollomi/object-remover.js rename to plugins/yollomi/object-remover.js diff --git a/plugins/yollomi/package.json b/plugins/yollomi/package.json new file mode 100644 index 00000000..02b781a6 --- /dev/null +++ b/plugins/yollomi/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-yollomi", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for yollomi", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/yollomi/remove-bg.js b/plugins/yollomi/remove-bg.js similarity index 100% rename from clis/yollomi/remove-bg.js rename to plugins/yollomi/remove-bg.js diff --git a/clis/yollomi/restore.js b/plugins/yollomi/restore.js similarity index 100% rename from clis/yollomi/restore.js rename to plugins/yollomi/restore.js diff --git a/clis/yollomi/try-on.js b/plugins/yollomi/try-on.js similarity index 100% rename from clis/yollomi/try-on.js rename to plugins/yollomi/try-on.js diff --git a/clis/yollomi/upload.js b/plugins/yollomi/upload.js similarity index 100% rename from clis/yollomi/upload.js rename to plugins/yollomi/upload.js diff --git a/clis/yollomi/upscale.js b/plugins/yollomi/upscale.js similarity index 100% rename from clis/yollomi/upscale.js rename to plugins/yollomi/upscale.js diff --git a/clis/yollomi/utils.js b/plugins/yollomi/utils.js similarity index 100% rename from clis/yollomi/utils.js rename to plugins/yollomi/utils.js diff --git a/clis/yollomi/video.js b/plugins/yollomi/video.js similarity index 100% rename from clis/yollomi/video.js rename to plugins/yollomi/video.js diff --git a/plugins/yollomi/webcmd-plugin.json b/plugins/yollomi/webcmd-plugin.json new file mode 100644 index 00000000..27b0c60f --- /dev/null +++ b/plugins/yollomi/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "yollomi", + "version": "0.1.0", + "description": "Webcmd commands for yollomi", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/scripts/silent-column-drop-baseline.json b/scripts/silent-column-drop-baseline.json index 0c9e981b..45cce13e 100644 --- a/scripts/silent-column-drop-baseline.json +++ b/scripts/silent-column-drop-baseline.json @@ -309,7 +309,7 @@ }, { "command": "paperreview/review", - "file": "clis/paperreview/review.js", + "file": "plugins/paperreview/review.js", "missing": [ "message", "token" @@ -425,7 +425,7 @@ }, { "command": "yollomi/upload", - "file": "clis/yollomi/upload.js", + "file": "plugins/yollomi/upload.js", "missing": [ "data" ] diff --git a/scripts/typed-error-lint-baseline.json b/scripts/typed-error-lint-baseline.json index e8f2ab3f..0a59e29f 100644 --- a/scripts/typed-error-lint-baseline.json +++ b/scripts/typed-error-lint-baseline.json @@ -218,7 +218,7 @@ { "rule": "silent-clamp", "command": "spotify/auth", - "file": "clis/spotify/spotify.js", + "file": "plugins/spotify/spotify.js", "line": 278, "text": "const limit = Math.min(50, Math.max(1, Math.round(kwargs.limit)));", "occurrence": 0 @@ -226,7 +226,7 @@ { "rule": "silent-clamp", "command": "spotify/next", - "file": "clis/spotify/spotify.js", + "file": "plugins/spotify/spotify.js", "line": 278, "text": "const limit = Math.min(50, Math.max(1, Math.round(kwargs.limit)));", "occurrence": 0 @@ -234,7 +234,7 @@ { "rule": "silent-clamp", "command": "spotify/pause", - "file": "clis/spotify/spotify.js", + "file": "plugins/spotify/spotify.js", "line": 278, "text": "const limit = Math.min(50, Math.max(1, Math.round(kwargs.limit)));", "occurrence": 0 @@ -242,7 +242,7 @@ { "rule": "silent-clamp", "command": "spotify/play", - "file": "clis/spotify/spotify.js", + "file": "plugins/spotify/spotify.js", "line": 278, "text": "const limit = Math.min(50, Math.max(1, Math.round(kwargs.limit)));", "occurrence": 0 @@ -250,7 +250,7 @@ { "rule": "silent-clamp", "command": "spotify/prev", - "file": "clis/spotify/spotify.js", + "file": "plugins/spotify/spotify.js", "line": 278, "text": "const limit = Math.min(50, Math.max(1, Math.round(kwargs.limit)));", "occurrence": 0 @@ -258,7 +258,7 @@ { "rule": "silent-clamp", "command": "spotify/queue", - "file": "clis/spotify/spotify.js", + "file": "plugins/spotify/spotify.js", "line": 278, "text": "const limit = Math.min(50, Math.max(1, Math.round(kwargs.limit)));", "occurrence": 0 @@ -266,7 +266,7 @@ { "rule": "silent-clamp", "command": "spotify/repeat", - "file": "clis/spotify/spotify.js", + "file": "plugins/spotify/spotify.js", "line": 278, "text": "const limit = Math.min(50, Math.max(1, Math.round(kwargs.limit)));", "occurrence": 0 @@ -274,7 +274,7 @@ { "rule": "silent-clamp", "command": "spotify/search", - "file": "clis/spotify/spotify.js", + "file": "plugins/spotify/spotify.js", "line": 278, "text": "const limit = Math.min(50, Math.max(1, Math.round(kwargs.limit)));", "occurrence": 0 @@ -282,7 +282,7 @@ { "rule": "silent-clamp", "command": "spotify/shuffle", - "file": "clis/spotify/spotify.js", + "file": "plugins/spotify/spotify.js", "line": 278, "text": "const limit = Math.min(50, Math.max(1, Math.round(kwargs.limit)));", "occurrence": 0 @@ -290,7 +290,7 @@ { "rule": "silent-clamp", "command": "spotify/status", - "file": "clis/spotify/spotify.js", + "file": "plugins/spotify/spotify.js", "line": 278, "text": "const limit = Math.min(50, Math.max(1, Math.round(kwargs.limit)));", "occurrence": 0 @@ -298,7 +298,7 @@ { "rule": "silent-clamp", "command": "spotify/volume", - "file": "clis/spotify/spotify.js", + "file": "plugins/spotify/spotify.js", "line": 278, "text": "const limit = Math.min(50, Math.max(1, Math.round(kwargs.limit)));", "occurrence": 0 @@ -578,7 +578,7 @@ { "rule": "silent-sentinel", "command": "yollomi/edit", - "file": "clis/yollomi/edit.js", + "file": "plugins/yollomi/edit.js", "line": 54, "text": "return [{ status: 'download-failed', file: '-', size: '-', credits: credits ?? '-', url }];", "occurrence": 0 @@ -586,7 +586,7 @@ { "rule": "silent-sentinel", "command": "yollomi/edit", - "file": "clis/yollomi/edit.js", + "file": "plugins/yollomi/edit.js", "line": 45, "text": "return [{ status: 'edited', file: '-', size: '-', credits: credits ?? '-', url }];", "occurrence": 0 @@ -594,7 +594,7 @@ { "rule": "silent-sentinel", "command": "yollomi/edit", - "file": "clis/yollomi/edit.js", + "file": "plugins/yollomi/edit.js", "line": 51, "text": "return [{ status: 'saved', file: path.relative('.', fp), size: fmtBytes(size), credits: credits ?? '-', url }];", "occurrence": 0 @@ -602,7 +602,7 @@ { "rule": "silent-sentinel", "command": "yollomi/video", - "file": "clis/yollomi/video.js", + "file": "plugins/yollomi/video.js", "line": 54, "text": "return [{ status: 'download-failed', file: '-', size: '-', credits: credits ?? '-', url: videoUrl }];", "occurrence": 0 @@ -610,7 +610,7 @@ { "rule": "silent-sentinel", "command": "yollomi/video", - "file": "clis/yollomi/video.js", + "file": "plugins/yollomi/video.js", "line": 44, "text": "return [{ status: 'generated', file: '-', size: '-', credits: credits ?? '-', url: videoUrl }];", "occurrence": 0 @@ -618,7 +618,7 @@ { "rule": "silent-sentinel", "command": "yollomi/video", - "file": "clis/yollomi/video.js", + "file": "plugins/yollomi/video.js", "line": 51, "text": "return [{ status: 'saved', file: path.relative('.', fp), size: fmtBytes(size), credits: credits ?? '-', url: videoUrl }];", "occurrence": 0 diff --git a/webcmd-plugin.json b/webcmd-plugin.json index 964de6af..761a1005 100644 --- a/webcmd-plugin.json +++ b/webcmd-plugin.json @@ -84,6 +84,16 @@ "handle": "agentrhq" } }, + "bigbasket": { + "path": "plugins/bigbasket", + "version": "0.1.0", + "description": "Webcmd commands for bigbasket", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "binance": { "path": "plugins/binance", "version": "0.1.0", @@ -164,6 +174,16 @@ "handle": "agentrhq" } }, + "chatgpt-app": { + "path": "plugins/chatgpt-app", + "version": "0.1.0", + "description": "Webcmd commands for chatgpt-app", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "chatwise": { "path": "plugins/chatwise", "version": "0.1.0", @@ -234,6 +254,16 @@ "handle": "agentrhq" } }, + "confluence": { + "path": "plugins/confluence", + "version": "0.1.0", + "description": "Webcmd commands for confluence", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "coupang": { "path": "plugins/coupang", "version": "0.1.0", @@ -304,6 +334,16 @@ "handle": "agentrhq" } }, + "discord-app": { + "path": "plugins/discord-app", + "version": "0.1.0", + "description": "Webcmd commands for discord-app", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "district": { "path": "plugins/district", "version": "0.1.0", @@ -364,6 +404,16 @@ "handle": "agentrhq" } }, + "geogebra": { + "path": "plugins/geogebra", + "version": "0.1.0", + "description": "Webcmd commands for geogebra", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "github": { "path": "plugins/github", "version": "0.1.0", @@ -624,6 +674,16 @@ "handle": "agentrhq" } }, + "mercury": { + "path": "plugins/mercury", + "version": "0.1.0", + "description": "Webcmd commands for mercury", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "npm": { "path": "plugins/npm", "version": "0.1.0", @@ -714,6 +774,16 @@ "handle": "agentrhq" } }, + "paperreview": { + "path": "plugins/paperreview", + "version": "0.1.0", + "description": "Webcmd commands for paperreview", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "pixiv": { "path": "plugins/pixiv", "version": "0.1.0", @@ -824,6 +894,16 @@ "handle": "rishabhraj36" } }, + "spotify": { + "path": "plugins/spotify", + "version": "0.1.0", + "description": "Webcmd commands for spotify", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "stackoverflow": { "path": "plugins/stackoverflow", "version": "0.1.0", @@ -1004,6 +1084,16 @@ "handle": "agentrhq" } }, + "yollomi": { + "path": "plugins/yollomi", + "version": "0.1.0", + "description": "Webcmd commands for yollomi", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "zepto": { "path": "plugins/zepto", "version": "0.1.0", From 42885ca71d20d0928c39b1a82d2bcf77ad97989c Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 17:55:32 +0530 Subject: [PATCH 19/39] refactor: complete independent adapter plugin migration --- cli-manifest.json | 9347 +---- clis/_shared/common.js | 32 - clis/_shared/desktop-commands.js | 112 - clis/_shared/search-adapter.js | 70 - clis/_shared/site-auth.js | 119 - clis/test-utils.js | 61 - plugin-command-manifest.json | 34142 ++++++++++------ plugins/antigravity/README.md | 45 + {clis => plugins}/antigravity/_actions.js | 0 {clis => plugins}/antigravity/audit-extras.js | 0 {clis => plugins}/antigravity/delete.js | 0 {clis => plugins}/antigravity/dump.js | 0 {clis => plugins}/antigravity/extract-code.js | 0 {clis => plugins}/antigravity/history.js | 0 {clis => plugins}/antigravity/mark-read.js | 0 {clis => plugins}/antigravity/model.js | 0 {clis => plugins}/antigravity/new.js | 0 plugins/antigravity/package.json | 9 + {clis => plugins}/antigravity/read.js | 0 {clis => plugins}/antigravity/rename.js | 0 {clis => plugins}/antigravity/send.js | 0 {clis => plugins}/antigravity/serve.js | 0 {clis => plugins}/antigravity/status.js | 0 {clis => plugins}/antigravity/storage.js | 0 .../antigravity/test}/antigravity.test.js | 16 +- {clis => plugins}/antigravity/watch.js | 0 plugins/antigravity/webcmd-plugin.json | 10 + plugins/facebook/README.md | 28 + .../__fixtures__/notifications-page.html | 0 {clis => plugins}/facebook/add-friend.js | 0 {clis => plugins}/facebook/auth.js | 2 +- {clis => plugins}/facebook/events.js | 0 {clis => plugins}/facebook/feed.js | 0 {clis => plugins}/facebook/friends.js | 0 {clis => plugins}/facebook/groups.js | 0 {clis => plugins}/facebook/join-group.js | 0 .../facebook/marketplace-inbox.js | 0 .../facebook/marketplace-listings.js | 0 {clis => plugins}/facebook/memories.js | 0 {clis => plugins}/facebook/notifications.js | 0 plugins/facebook/package.json | 9 + {clis => plugins}/facebook/profile.js | 0 {clis => plugins}/facebook/search.js | 0 .../facebook/test}/feed.test.js | 2 +- .../facebook/test}/marketplace.test.js | 4 +- .../facebook/test}/notifications.test.js | 6 +- .../facebook/test}/search.test.js | 2 +- plugins/facebook/webcmd-plugin.json | 10 + plugins/grok/README.md | 29 + {clis => plugins}/grok/ask.js | 0 {clis => plugins}/grok/auth.js | 2 +- {clis => plugins}/grok/delete.js | 0 {clis => plugins}/grok/detail.js | 0 {clis => plugins}/grok/export-all.js | 0 {clis => plugins}/grok/export-utils.js | 0 {clis => plugins}/grok/export.js | 0 {clis => plugins}/grok/history.js | 0 {clis => plugins}/grok/image.js | 0 {clis => plugins}/grok/image.test.ts | 0 {clis => plugins}/grok/new.js | 0 plugins/grok/package.json | 9 + {clis => plugins}/grok/pin.js | 0 {clis => plugins}/grok/read.js | 0 {clis => plugins}/grok/send.js | 0 {clis => plugins}/grok/status.js | 0 {clis/grok => plugins/grok/test}/ask.test.js | 2 +- .../grok => plugins/grok/test}/export.test.js | 6 +- .../grok => plugins/grok/test}/utils.test.js | 2 +- {clis => plugins}/grok/utils.js | 0 plugins/grok/webcmd-plugin.json | 10 + plugins/instagram/README.md | 37 + .../instagram/_shared/private-publish.js | 0 .../instagram/_shared/protocol-capture.js | 0 .../instagram/_shared/runtime-info.js | 0 {clis => plugins}/instagram/auth.js | 2 +- .../instagram/collection-create.js | 0 .../instagram/collection-delete.js | 0 {clis => plugins}/instagram/comment.js | 0 {clis => plugins}/instagram/download.js | 0 {clis => plugins}/instagram/explore.js | 0 {clis => plugins}/instagram/follow.js | 0 {clis => plugins}/instagram/followers.js | 0 {clis => plugins}/instagram/following.js | 0 {clis => plugins}/instagram/like.js | 0 {clis => plugins}/instagram/note.js | 0 plugins/instagram/package.json | 9 + {clis => plugins}/instagram/post.js | 0 {clis => plugins}/instagram/profile.js | 0 {clis => plugins}/instagram/reel.js | 0 {clis => plugins}/instagram/save.js | 0 {clis => plugins}/instagram/saved.js | 0 {clis => plugins}/instagram/search.js | 0 {clis => plugins}/instagram/story.js | 0 .../instagram/test}/download.test.js | 2 +- .../instagram/test}/explore.test.js | 2 +- .../instagram/test}/instagram.test.js | 2 +- .../instagram/test}/note.test.js | 4 +- plugins/instagram/test/page-mock.js | 12 + .../instagram/test}/post.test.js | 6 +- .../instagram/test}/private-publish.test.js | 2 +- .../instagram/test}/protocol-capture.test.js | 2 +- .../instagram/test}/reel.test.js | 2 +- .../instagram/test}/story.test.js | 6 +- .../instagram/test}/user.test.js | 2 +- {clis => plugins}/instagram/unfollow.js | 0 {clis => plugins}/instagram/unlike.js | 0 {clis => plugins}/instagram/unsave.js | 0 {clis => plugins}/instagram/user.js | 0 plugins/instagram/webcmd-plugin.json | 10 + plugins/linkedin/auth.js | 2 +- plugins/linkedin/site-auth.js | 119 - plugins/notebooklm/README.md | 34 + {clis => plugins}/notebooklm/add-source.js | 0 {clis => plugins}/notebooklm/auth.js | 2 +- {clis => plugins}/notebooklm/create.js | 0 {clis => plugins}/notebooklm/current.js | 0 .../notebooklm/generate-audio.js | 0 .../notebooklm/generate-slides.js | 0 {clis => plugins}/notebooklm/get.js | 0 {clis => plugins}/notebooklm/history.js | 0 {clis => plugins}/notebooklm/list.js | 0 {clis => plugins}/notebooklm/note-list.js | 0 {clis => plugins}/notebooklm/notes-get.js | 0 {clis => plugins}/notebooklm/open.js | 0 plugins/notebooklm/package.json | 9 + {clis => plugins}/notebooklm/rpc.js | 0 {clis => plugins}/notebooklm/shared.js | 0 .../notebooklm/source-fulltext.js | 0 {clis => plugins}/notebooklm/source-get.js | 0 {clis => plugins}/notebooklm/source-guide.js | 0 {clis => plugins}/notebooklm/source-list.js | 0 {clis => plugins}/notebooklm/status.js | 0 {clis => plugins}/notebooklm/summary.js | 0 .../notebooklm/test}/add-source.test.js | 2 +- .../notebooklm/test}/compat.test.js | 6 +- .../notebooklm/test}/create.test.js | 2 +- .../notebooklm/test}/generate-audio.test.js | 2 +- .../notebooklm/test}/generate-slides.test.js | 2 +- .../notebooklm/test}/history.test.js | 6 +- .../notebooklm/test}/note-list.test.js | 6 +- .../notebooklm/test}/notes-get.test.js | 6 +- .../notebooklm/test}/open.test.js | 6 +- .../notebooklm/test}/rpc.test.js | 2 +- .../notebooklm/test}/source-fulltext.test.js | 6 +- .../notebooklm/test}/source-get.test.js | 6 +- .../notebooklm/test}/source-guide.test.js | 6 +- .../notebooklm/test}/summary.test.js | 6 +- .../notebooklm/test}/utils.test.js | 2 +- .../notebooklm/test}/write-note.test.js | 2 +- {clis => plugins}/notebooklm/utils.js | 0 plugins/notebooklm/webcmd-plugin.json | 10 + {clis => plugins}/notebooklm/write-note.js | 0 plugins/qoder/README.md | 33 + {clis => plugins}/qoder/_utils.js | 0 {clis => plugins}/qoder/composer.js | 0 {clis => plugins}/qoder/history.js | 0 plugins/qoder/package.json | 9 + {clis => plugins}/qoder/quest.js | 0 {clis => plugins}/qoder/read.js | 0 {clis => plugins}/qoder/status.js | 0 .../qoder/test}/qoder.test.js | 14 +- {clis => plugins}/qoder/ui.js | 0 plugins/qoder/webcmd-plugin.json | 10 + plugins/reddit/README.md | 35 + {clis => plugins}/reddit/auth.js | 3 +- {clis => plugins}/reddit/comment.js | 0 {clis => plugins}/reddit/frontpage.js | 0 {clis => plugins}/reddit/home.js | 0 {clis => plugins}/reddit/hot.js | 0 plugins/reddit/package.json | 9 + {clis => plugins}/reddit/popular.js | 0 {clis => plugins}/reddit/read.js | 0 {clis => plugins}/reddit/reply.js | 0 {clis => plugins}/reddit/save.js | 0 {clis => plugins}/reddit/saved.js | 0 {clis => plugins}/reddit/search.js | 0 {clis => plugins}/reddit/subreddit-info.js | 0 {clis => plugins}/reddit/subreddit.js | 0 {clis => plugins}/reddit/subscribe.js | 0 {clis => plugins}/reddit/subscribed.js | 0 .../reddit/test}/extract-media.test.js | 0 .../reddit/test}/frontpage.test.js | 2 +- .../reddit/test}/home.test.js | 4 +- .../reddit/test}/hot.test.js | 2 +- .../reddit/test}/popular.test.js | 2 +- .../reddit/test}/read.test.js | 4 +- .../reddit/test}/reply.test.js | 4 +- .../reddit/test}/search.test.js | 2 +- .../reddit/test}/subreddit-info.test.js | 4 +- .../reddit/test}/subreddit.test.js | 2 +- .../reddit/test}/subscribed.test.js | 4 +- .../reddit/test}/whoami.test.js | 2 +- {clis => plugins}/reddit/upvote.js | 0 {clis => plugins}/reddit/upvoted.js | 0 {clis => plugins}/reddit/user-comments.js | 0 {clis => plugins}/reddit/user-posts.js | 0 {clis => plugins}/reddit/user.js | 0 plugins/reddit/webcmd-plugin.json | 10 + {clis => plugins}/reddit/whoami.js | 0 plugins/slock/README.md | 58 + .../slock/attachment-download.js | 0 {clis => plugins}/slock/attachment-upload.js | 0 {clis => plugins}/slock/attachment-url.js | 0 {clis => plugins}/slock/auth-verify.js | 0 {clis => plugins}/slock/bookmark-add.js | 0 {clis => plugins}/slock/bookmark-list.js | 0 {clis => plugins}/slock/bookmark-remove.js | 0 {clis => plugins}/slock/channel-action.js | 0 {clis => plugins}/slock/channel-archive.js | 0 {clis => plugins}/slock/channel-create.js | 0 {clis => plugins}/slock/channel-files.js | 0 {clis => plugins}/slock/channel-info.js | 0 {clis => plugins}/slock/channel-join.js | 0 {clis => plugins}/slock/channel-leave.js | 0 {clis => plugins}/slock/channel-list.js | 0 {clis => plugins}/slock/channel-mark.js | 0 {clis => plugins}/slock/channel-members.js | 0 {clis => plugins}/slock/channel-unarchive.js | 0 {clis => plugins}/slock/dm-list.js | 0 {clis => plugins}/slock/errors.js | 0 {clis => plugins}/slock/in-page.js | 2 +- {clis => plugins}/slock/inbox-done.js | 0 {clis => plugins}/slock/inbox-read-all.js | 0 {clis => plugins}/slock/inbox.js | 0 {clis => plugins}/slock/login.js | 0 {clis => plugins}/slock/message-read.js | 0 {clis => plugins}/slock/message-search.js | 0 {clis => plugins}/slock/message-send.js | 0 plugins/slock/package.json | 9 + {clis => plugins}/slock/reaction-add.js | 0 {clis => plugins}/slock/reaction-remove.js | 0 {clis => plugins}/slock/resolve.js | 0 {clis => plugins}/slock/server-list.js | 0 {clis => plugins}/slock/server-use.js | 0 {clis => plugins}/slock/shared.js | 0 {clis => plugins}/slock/task-claim.js | 0 {clis => plugins}/slock/task-convert.js | 0 {clis => plugins}/slock/task-create.js | 0 {clis => plugins}/slock/task-delete.js | 0 {clis => plugins}/slock/task-get.js | 0 {clis => plugins}/slock/task-list-server.js | 0 {clis => plugins}/slock/task-list.js | 0 {clis => plugins}/slock/task-status.js | 0 {clis => plugins}/slock/task-unclaim.js | 0 .../slock/test}/api-base-canary.test.js | 8 +- .../slock/test}/attachment-download.test.js | 2 +- .../slock/test}/attachment-upload.test.js | 2 +- .../slock/test}/attachment-url.test.js | 2 +- .../slock/test}/bookmark-add.test.js | 2 +- .../slock/test}/bookmark-list.test.js | 2 +- .../slock/test}/bookmark-remove.test.js | 2 +- .../slock/test}/channel-action.test.js | 8 +- .../slock/test}/channel-create.test.js | 2 +- .../slock/test}/channel-files.test.js | 2 +- .../slock/test}/channel-info.test.js | 2 +- .../slock/test}/channel-list.test.js | 2 +- .../slock/test}/channel-mark.test.js | 2 +- .../slock/test}/channel-members.test.js | 2 +- .../slock/test}/cross-command.test.js | 2 +- .../slock/test}/dm-list.test.js | 2 +- .../slock/test}/error-detail-canary.test.js | 4 +- .../slock/test}/errors.test.js | 2 +- .../slock/test}/in-page.test.js | 4 +- .../slock/test}/inbox-done.test.js | 2 +- .../slock/test}/inbox-read-all.test.js | 2 +- .../slock/test}/inbox.test.js | 2 +- .../slock/test}/message-read.test.js | 2 +- .../slock/test}/message-search.test.js | 2 +- .../slock/test}/message-send.test.js | 2 +- .../slock/test}/reaction-add.test.js | 2 +- .../slock/test}/reaction-remove.test.js | 2 +- .../slock/test}/resolve.test.js | 10 +- .../slock/test}/server-list.test.js | 2 +- .../test}/server-override-canary.test.js | 6 +- .../slock/test}/server-use.test.js | 2 +- .../slock/test}/short-id-canary.test.js | 6 +- .../slock/test}/site-session-canary.test.js | 6 +- .../slock/test}/task-claim.test.js | 2 +- .../slock/test}/task-convert.test.js | 2 +- .../slock/test}/task-create.test.js | 2 +- .../slock/test}/task-delete.test.js | 2 +- .../slock/test}/task-get.test.js | 2 +- .../slock/test}/task-list-server.test.js | 2 +- .../slock/test}/task-list.test.js | 2 +- .../slock/test}/task-status.test.js | 2 +- .../slock/test}/task-unclaim.test.js | 2 +- .../slock/test}/thread-follow.test.js | 2 +- .../slock/test}/thread-list.test.js | 2 +- .../slock/test}/thread-state.test.js | 6 +- .../slock/test}/unread-summary.test.js | 2 +- .../slock/test}/whoami.test.js | 2 +- {clis => plugins}/slock/thread-done.js | 0 {clis => plugins}/slock/thread-follow.js | 0 {clis => plugins}/slock/thread-list.js | 0 {clis => plugins}/slock/thread-state.js | 0 {clis => plugins}/slock/thread-undone.js | 0 {clis => plugins}/slock/thread-unfollow.js | 0 {clis => plugins}/slock/unread-summary.js | 0 plugins/slock/webcmd-plugin.json | 10 + {clis => plugins}/slock/whoami.js | 2 +- plugins/tiktok/README.md | 32 + {clis => plugins}/tiktok/auth.js | 2 +- {clis => plugins}/tiktok/comment.js | 0 {clis => plugins}/tiktok/creator-videos.js | 0 {clis => plugins}/tiktok/explore.js | 0 {clis => plugins}/tiktok/follow.js | 0 {clis => plugins}/tiktok/following.js | 0 {clis => plugins}/tiktok/friends.js | 0 {clis => plugins}/tiktok/like.js | 0 {clis => plugins}/tiktok/live.js | 0 {clis => plugins}/tiktok/notifications.js | 0 plugins/tiktok/package.json | 9 + {clis => plugins}/tiktok/profile.js | 0 {clis => plugins}/tiktok/save.js | 0 {clis => plugins}/tiktok/search.js | 0 .../tiktok/test}/creator-videos.test.js | 2 +- .../tiktok/test}/refactor.test.js | 14 +- .../tiktok/test}/write-refactor.test.js | 8 +- {clis => plugins}/tiktok/unfollow.js | 0 {clis => plugins}/tiktok/unlike.js | 0 {clis => plugins}/tiktok/unsave.js | 0 {clis => plugins}/tiktok/user.js | 0 {clis => plugins}/tiktok/utils.js | 0 plugins/tiktok/webcmd-plugin.json | 10 + plugins/trip/README.md | 26 + {clis => plugins}/trip/attraction.js | 0 {clis => plugins}/trip/car.js | 0 {clis => plugins}/trip/deals.js | 0 {clis => plugins}/trip/flight-round.js | 0 {clis => plugins}/trip/flight.js | 0 {clis => plugins}/trip/hotel-search.js | 0 {clis => plugins}/trip/hotel.js | 0 {clis => plugins}/trip/package.js | 0 plugins/trip/package.json | 9 + {clis => plugins}/trip/search.js | 0 {clis/trip => plugins/trip/test}/trip.test.js | 26 +- {clis => plugins}/trip/tour.js | 0 {clis => plugins}/trip/train.js | 0 {clis => plugins}/trip/transfer.js | 0 {clis => plugins}/trip/utils.js | 0 plugins/trip/webcmd-plugin.json | 10 + plugins/twitter/README.md | 58 + {clis => plugins}/twitter/accept.js | 0 {clis => plugins}/twitter/article.js | 0 {clis => plugins}/twitter/auth.js | 2 +- {clis => plugins}/twitter/block.js | 0 {clis => plugins}/twitter/bookmark-folder.js | 0 {clis => plugins}/twitter/bookmark-folders.js | 0 {clis => plugins}/twitter/bookmark.js | 0 {clis => plugins}/twitter/bookmarks.js | 0 {clis => plugins}/twitter/delete.js | 0 {clis => plugins}/twitter/device-follow.js | 0 {clis => plugins}/twitter/download.js | 0 {clis => plugins}/twitter/follow-batch.js | 0 {clis => plugins}/twitter/follow.js | 0 {clis => plugins}/twitter/followers.js | 0 {clis => plugins}/twitter/following.js | 0 {clis => plugins}/twitter/hide-reply.js | 0 {clis => plugins}/twitter/like.js | 0 {clis => plugins}/twitter/likes.js | 0 {clis => plugins}/twitter/list-add-batch.js | 0 {clis => plugins}/twitter/list-add-core.js | 2 +- {clis => plugins}/twitter/list-add.js | 0 {clis => plugins}/twitter/list-batch-utils.js | 0 {clis => plugins}/twitter/list-create.js | 0 {clis => plugins}/twitter/list-delete.js | 2 +- .../twitter/list-remove-batch.js | 0 {clis => plugins}/twitter/list-remove-core.js | 2 +- {clis => plugins}/twitter/list-remove.js | 0 {clis => plugins}/twitter/list-tweets.js | 0 plugins/twitter/lists-parser.js | 54 + {clis => plugins}/twitter/lists.js | 58 +- {clis => plugins}/twitter/notifications.js | 0 plugins/twitter/package.json | 9 + {clis => plugins}/twitter/post.js | 0 {clis => plugins}/twitter/profile.js | 0 {clis => plugins}/twitter/quote.js | 0 {clis => plugins}/twitter/reply-dm.js | 0 {clis => plugins}/twitter/reply.js | 0 {clis => plugins}/twitter/retweet.js | 0 {clis => plugins}/twitter/search.js | 0 {clis => plugins}/twitter/shared.js | 0 .../twitter/test}/article-evaluate.test.js | 4 +- .../twitter/test}/article.test.js | 2 +- .../twitter/test}/bookmark-folder.test.js | 2 +- .../twitter/test}/bookmark-folders.test.js | 2 +- .../twitter/test}/bookmark.test.js | 4 +- .../twitter/test}/bookmarks.test.js | 2 +- .../twitter/test}/delete.test.js | 2 +- .../twitter/test}/device-follow.test.js | 4 +- .../twitter/test}/download.test.js | 2 +- .../twitter/test}/follow-batch.test.js | 4 +- .../twitter/test}/followers.test.js | 2 +- .../twitter/test}/following.test.js | 2 +- .../twitter/test}/hide-reply.test.js | 4 +- .../twitter/test}/like.test.js | 4 +- .../twitter/test}/likes.test.js | 2 +- .../twitter/test}/list-add.test.js | 2 +- .../twitter/test}/list-batch.test.js | 6 +- .../twitter/test}/list-create.test.js | 4 +- .../twitter/test}/list-delete.test.js | 2 +- .../twitter/test}/list-remove.test.js | 2 +- .../twitter/test}/list-tweets.test.js | 2 +- .../twitter/test}/lists.test.js | 2 +- plugins/twitter/test/page-mock.js | 12 + .../twitter/test}/post.test.js | 2 +- .../twitter/test}/profile.test.js | 2 +- .../twitter/test}/quote.test.js | 6 +- .../twitter/test}/reply.test.js | 6 +- .../twitter/test}/retweet.test.js | 4 +- .../twitter/test}/search.test.js | 2 +- .../twitter/test}/shared.test.js | 2 +- .../twitter/test}/thread.test.js | 2 +- .../twitter/test}/timeline.test.js | 2 +- .../twitter/test}/trending.test.js | 2 +- .../twitter/test}/tweets.test.js | 2 +- .../twitter/test}/unbookmark.test.js | 4 +- .../twitter/test}/unlike.test.js | 4 +- .../twitter/test}/unretweet.test.js | 4 +- .../twitter/test}/utils.test.js | 2 +- {clis => plugins}/twitter/thread.js | 0 {clis => plugins}/twitter/timeline.js | 0 {clis => plugins}/twitter/trending.js | 0 {clis => plugins}/twitter/tweets.js | 0 {clis => plugins}/twitter/unblock.js | 0 {clis => plugins}/twitter/unbookmark.js | 0 {clis => plugins}/twitter/unfollow.js | 0 {clis => plugins}/twitter/unlike.js | 0 {clis => plugins}/twitter/unretweet.js | 0 {clis => plugins}/twitter/utils.js | 0 plugins/twitter/webcmd-plugin.json | 10 + plugins/youtube/README.md | 30 + {clis => plugins}/youtube/auth.js | 2 +- {clis => plugins}/youtube/channel.js | 0 {clis => plugins}/youtube/comments.js | 0 {clis => plugins}/youtube/feed.js | 0 {clis => plugins}/youtube/history.js | 0 {clis => plugins}/youtube/like.js | 0 plugins/youtube/package.json | 9 + {clis => plugins}/youtube/playlist.js | 0 {clis => plugins}/youtube/search.js | 0 {clis => plugins}/youtube/subscribe.js | 0 {clis => plugins}/youtube/subscriptions.js | 0 .../youtube/test}/channel.test.js | 2 +- .../youtube/test}/feed.test.js | 2 +- .../youtube/test}/transcript-group.test.js | 2 +- .../youtube/test}/transcript.test.js | 4 +- .../youtube/test}/utils.test.js | 2 +- .../youtube/test}/video.test.js | 6 +- {clis => plugins}/youtube/transcript-group.js | 0 {clis => plugins}/youtube/transcript.js | 0 {clis => plugins}/youtube/unlike.js | 0 {clis => plugins}/youtube/unsubscribe.js | 0 {clis => plugins}/youtube/utils.js | 0 {clis => plugins}/youtube/video.js | 0 {clis => plugins}/youtube/watch-later.js | 0 plugins/youtube/webcmd-plugin.json | 10 + scripts/silent-column-drop-baseline.json | 26 +- scripts/typed-error-lint-baseline.json | 56 +- src/build-manifest.test.ts | 4 +- src/cli.test.ts | 19 +- src/cli.ts | 13 +- src/package-exports.test.ts | 5 +- src/plugin-runtime.test.ts | 13 + src/plugin-runtime.ts | 49 +- webcmd-plugin.json | 120 + 466 files changed, 22992 insertions(+), 22647 deletions(-) delete mode 100644 clis/_shared/common.js delete mode 100644 clis/_shared/desktop-commands.js delete mode 100644 clis/_shared/search-adapter.js delete mode 100644 clis/_shared/site-auth.js delete mode 100644 clis/test-utils.js create mode 100644 plugins/antigravity/README.md rename {clis => plugins}/antigravity/_actions.js (100%) rename {clis => plugins}/antigravity/audit-extras.js (100%) rename {clis => plugins}/antigravity/delete.js (100%) rename {clis => plugins}/antigravity/dump.js (100%) rename {clis => plugins}/antigravity/extract-code.js (100%) rename {clis => plugins}/antigravity/history.js (100%) rename {clis => plugins}/antigravity/mark-read.js (100%) rename {clis => plugins}/antigravity/model.js (100%) rename {clis => plugins}/antigravity/new.js (100%) create mode 100644 plugins/antigravity/package.json rename {clis => plugins}/antigravity/read.js (100%) rename {clis => plugins}/antigravity/rename.js (100%) rename {clis => plugins}/antigravity/send.js (100%) rename {clis => plugins}/antigravity/serve.js (100%) rename {clis => plugins}/antigravity/status.js (100%) rename {clis => plugins}/antigravity/storage.js (100%) rename {clis/antigravity => plugins/antigravity/test}/antigravity.test.js (96%) rename {clis => plugins}/antigravity/watch.js (100%) create mode 100644 plugins/antigravity/webcmd-plugin.json create mode 100644 plugins/facebook/README.md rename {clis => plugins}/facebook/__fixtures__/notifications-page.html (100%) rename {clis => plugins}/facebook/add-friend.js (100%) rename {clis => plugins}/facebook/auth.js (95%) rename {clis => plugins}/facebook/events.js (100%) rename {clis => plugins}/facebook/feed.js (100%) rename {clis => plugins}/facebook/friends.js (100%) rename {clis => plugins}/facebook/groups.js (100%) rename {clis => plugins}/facebook/join-group.js (100%) rename {clis => plugins}/facebook/marketplace-inbox.js (100%) rename {clis => plugins}/facebook/marketplace-listings.js (100%) rename {clis => plugins}/facebook/memories.js (100%) rename {clis => plugins}/facebook/notifications.js (100%) create mode 100644 plugins/facebook/package.json rename {clis => plugins}/facebook/profile.js (100%) rename {clis => plugins}/facebook/search.js (100%) rename {clis/facebook => plugins/facebook/test}/feed.test.js (99%) rename {clis/facebook => plugins/facebook/test}/marketplace.test.js (98%) rename {clis/facebook => plugins/facebook/test}/notifications.test.js (99%) rename {clis/facebook => plugins/facebook/test}/search.test.js (99%) create mode 100644 plugins/facebook/webcmd-plugin.json create mode 100644 plugins/grok/README.md rename {clis => plugins}/grok/ask.js (100%) rename {clis => plugins}/grok/auth.js (96%) rename {clis => plugins}/grok/delete.js (100%) rename {clis => plugins}/grok/detail.js (100%) rename {clis => plugins}/grok/export-all.js (100%) rename {clis => plugins}/grok/export-utils.js (100%) rename {clis => plugins}/grok/export.js (100%) rename {clis => plugins}/grok/history.js (100%) rename {clis => plugins}/grok/image.js (100%) rename {clis => plugins}/grok/image.test.ts (100%) rename {clis => plugins}/grok/new.js (100%) create mode 100644 plugins/grok/package.json rename {clis => plugins}/grok/pin.js (100%) rename {clis => plugins}/grok/read.js (100%) rename {clis => plugins}/grok/send.js (100%) rename {clis => plugins}/grok/status.js (100%) rename {clis/grok => plugins/grok/test}/ask.test.js (97%) rename {clis/grok => plugins/grok/test}/export.test.js (98%) rename {clis/grok => plugins/grok/test}/utils.test.js (99%) rename {clis => plugins}/grok/utils.js (100%) create mode 100644 plugins/grok/webcmd-plugin.json create mode 100644 plugins/instagram/README.md rename {clis => plugins}/instagram/_shared/private-publish.js (100%) rename {clis => plugins}/instagram/_shared/protocol-capture.js (100%) rename {clis => plugins}/instagram/_shared/runtime-info.js (100%) rename {clis => plugins}/instagram/auth.js (96%) rename {clis => plugins}/instagram/collection-create.js (100%) rename {clis => plugins}/instagram/collection-delete.js (100%) rename {clis => plugins}/instagram/comment.js (100%) rename {clis => plugins}/instagram/download.js (100%) rename {clis => plugins}/instagram/explore.js (100%) rename {clis => plugins}/instagram/follow.js (100%) rename {clis => plugins}/instagram/followers.js (100%) rename {clis => plugins}/instagram/following.js (100%) rename {clis => plugins}/instagram/like.js (100%) rename {clis => plugins}/instagram/note.js (100%) create mode 100644 plugins/instagram/package.json rename {clis => plugins}/instagram/post.js (100%) rename {clis => plugins}/instagram/profile.js (100%) rename {clis => plugins}/instagram/reel.js (100%) rename {clis => plugins}/instagram/save.js (100%) rename {clis => plugins}/instagram/saved.js (100%) rename {clis => plugins}/instagram/search.js (100%) rename {clis => plugins}/instagram/story.js (100%) rename {clis/instagram => plugins/instagram/test}/download.test.js (99%) rename {clis/instagram => plugins/instagram/test}/explore.test.js (98%) rename {clis/instagram => plugins/instagram/test}/instagram.test.js (99%) rename {clis/instagram => plugins/instagram/test}/note.test.js (97%) create mode 100644 plugins/instagram/test/page-mock.js rename {clis/instagram => plugins/instagram/test}/post.test.js (99%) rename {clis/instagram/_shared => plugins/instagram/test}/private-publish.test.js (99%) rename {clis/instagram/_shared => plugins/instagram/test}/protocol-capture.test.js (98%) rename {clis/instagram => plugins/instagram/test}/reel.test.js (99%) rename {clis/instagram => plugins/instagram/test}/story.test.js (97%) rename {clis/instagram => plugins/instagram/test}/user.test.js (99%) rename {clis => plugins}/instagram/unfollow.js (100%) rename {clis => plugins}/instagram/unlike.js (100%) rename {clis => plugins}/instagram/unsave.js (100%) rename {clis => plugins}/instagram/user.js (100%) create mode 100644 plugins/instagram/webcmd-plugin.json delete mode 100644 plugins/linkedin/site-auth.js create mode 100644 plugins/notebooklm/README.md rename {clis => plugins}/notebooklm/add-source.js (100%) rename {clis => plugins}/notebooklm/auth.js (96%) rename {clis => plugins}/notebooklm/create.js (100%) rename {clis => plugins}/notebooklm/current.js (100%) rename {clis => plugins}/notebooklm/generate-audio.js (100%) rename {clis => plugins}/notebooklm/generate-slides.js (100%) rename {clis => plugins}/notebooklm/get.js (100%) rename {clis => plugins}/notebooklm/history.js (100%) rename {clis => plugins}/notebooklm/list.js (100%) rename {clis => plugins}/notebooklm/note-list.js (100%) rename {clis => plugins}/notebooklm/notes-get.js (100%) rename {clis => plugins}/notebooklm/open.js (100%) create mode 100644 plugins/notebooklm/package.json rename {clis => plugins}/notebooklm/rpc.js (100%) rename {clis => plugins}/notebooklm/shared.js (100%) rename {clis => plugins}/notebooklm/source-fulltext.js (100%) rename {clis => plugins}/notebooklm/source-get.js (100%) rename {clis => plugins}/notebooklm/source-guide.js (100%) rename {clis => plugins}/notebooklm/source-list.js (100%) rename {clis => plugins}/notebooklm/status.js (100%) rename {clis => plugins}/notebooklm/summary.js (100%) rename {clis/notebooklm => plugins/notebooklm/test}/add-source.test.js (99%) rename {clis/notebooklm => plugins/notebooklm/test}/compat.test.js (91%) rename {clis/notebooklm => plugins/notebooklm/test}/create.test.js (98%) rename {clis/notebooklm => plugins/notebooklm/test}/generate-audio.test.js (98%) rename {clis/notebooklm => plugins/notebooklm/test}/generate-slides.test.js (98%) rename {clis/notebooklm => plugins/notebooklm/test}/history.test.js (94%) rename {clis/notebooklm => plugins/notebooklm/test}/note-list.test.js (94%) rename {clis/notebooklm => plugins/notebooklm/test}/notes-get.test.js (96%) rename {clis/notebooklm => plugins/notebooklm/test}/open.test.js (96%) rename {clis/notebooklm => plugins/notebooklm/test}/rpc.test.js (99%) rename {clis/notebooklm => plugins/notebooklm/test}/source-fulltext.test.js (97%) rename {clis/notebooklm => plugins/notebooklm/test}/source-get.test.js (96%) rename {clis/notebooklm => plugins/notebooklm/test}/source-guide.test.js (97%) rename {clis/notebooklm => plugins/notebooklm/test}/summary.test.js (96%) rename {clis/notebooklm => plugins/notebooklm/test}/utils.test.js (99%) rename {clis/notebooklm => plugins/notebooklm/test}/write-note.test.js (98%) rename {clis => plugins}/notebooklm/utils.js (100%) create mode 100644 plugins/notebooklm/webcmd-plugin.json rename {clis => plugins}/notebooklm/write-note.js (100%) create mode 100644 plugins/qoder/README.md rename {clis => plugins}/qoder/_utils.js (100%) rename {clis => plugins}/qoder/composer.js (100%) rename {clis => plugins}/qoder/history.js (100%) create mode 100644 plugins/qoder/package.json rename {clis => plugins}/qoder/quest.js (100%) rename {clis => plugins}/qoder/read.js (100%) rename {clis => plugins}/qoder/status.js (100%) rename {clis/qoder => plugins/qoder/test}/qoder.test.js (96%) rename {clis => plugins}/qoder/ui.js (100%) create mode 100644 plugins/qoder/webcmd-plugin.json create mode 100644 plugins/reddit/README.md rename {clis => plugins}/reddit/auth.js (95%) rename {clis => plugins}/reddit/comment.js (100%) rename {clis => plugins}/reddit/frontpage.js (100%) rename {clis => plugins}/reddit/home.js (100%) rename {clis => plugins}/reddit/hot.js (100%) create mode 100644 plugins/reddit/package.json rename {clis => plugins}/reddit/popular.js (100%) rename {clis => plugins}/reddit/read.js (100%) rename {clis => plugins}/reddit/reply.js (100%) rename {clis => plugins}/reddit/save.js (100%) rename {clis => plugins}/reddit/saved.js (100%) rename {clis => plugins}/reddit/search.js (100%) rename {clis => plugins}/reddit/subreddit-info.js (100%) rename {clis => plugins}/reddit/subreddit.js (100%) rename {clis => plugins}/reddit/subscribe.js (100%) rename {clis => plugins}/reddit/subscribed.js (100%) rename {clis/reddit => plugins/reddit/test}/extract-media.test.js (100%) rename {clis/reddit => plugins/reddit/test}/frontpage.test.js (98%) rename {clis/reddit => plugins/reddit/test}/home.test.js (98%) rename {clis/reddit => plugins/reddit/test}/hot.test.js (98%) rename {clis/reddit => plugins/reddit/test}/popular.test.js (98%) rename {clis/reddit => plugins/reddit/test}/read.test.js (99%) rename {clis/reddit => plugins/reddit/test}/reply.test.js (99%) rename {clis/reddit => plugins/reddit/test}/search.test.js (97%) rename {clis/reddit => plugins/reddit/test}/subreddit-info.test.js (98%) rename {clis/reddit => plugins/reddit/test}/subreddit.test.js (97%) rename {clis/reddit => plugins/reddit/test}/subscribed.test.js (99%) rename {clis/reddit => plugins/reddit/test}/whoami.test.js (99%) rename {clis => plugins}/reddit/upvote.js (100%) rename {clis => plugins}/reddit/upvoted.js (100%) rename {clis => plugins}/reddit/user-comments.js (100%) rename {clis => plugins}/reddit/user-posts.js (100%) rename {clis => plugins}/reddit/user.js (100%) create mode 100644 plugins/reddit/webcmd-plugin.json rename {clis => plugins}/reddit/whoami.js (100%) create mode 100644 plugins/slock/README.md rename {clis => plugins}/slock/attachment-download.js (100%) rename {clis => plugins}/slock/attachment-upload.js (100%) rename {clis => plugins}/slock/attachment-url.js (100%) rename {clis => plugins}/slock/auth-verify.js (100%) rename {clis => plugins}/slock/bookmark-add.js (100%) rename {clis => plugins}/slock/bookmark-list.js (100%) rename {clis => plugins}/slock/bookmark-remove.js (100%) rename {clis => plugins}/slock/channel-action.js (100%) rename {clis => plugins}/slock/channel-archive.js (100%) rename {clis => plugins}/slock/channel-create.js (100%) rename {clis => plugins}/slock/channel-files.js (100%) rename {clis => plugins}/slock/channel-info.js (100%) rename {clis => plugins}/slock/channel-join.js (100%) rename {clis => plugins}/slock/channel-leave.js (100%) rename {clis => plugins}/slock/channel-list.js (100%) rename {clis => plugins}/slock/channel-mark.js (100%) rename {clis => plugins}/slock/channel-members.js (100%) rename {clis => plugins}/slock/channel-unarchive.js (100%) rename {clis => plugins}/slock/dm-list.js (100%) rename {clis => plugins}/slock/errors.js (100%) rename {clis => plugins}/slock/in-page.js (99%) rename {clis => plugins}/slock/inbox-done.js (100%) rename {clis => plugins}/slock/inbox-read-all.js (100%) rename {clis => plugins}/slock/inbox.js (100%) rename {clis => plugins}/slock/login.js (100%) rename {clis => plugins}/slock/message-read.js (100%) rename {clis => plugins}/slock/message-search.js (100%) rename {clis => plugins}/slock/message-send.js (100%) create mode 100644 plugins/slock/package.json rename {clis => plugins}/slock/reaction-add.js (100%) rename {clis => plugins}/slock/reaction-remove.js (100%) rename {clis => plugins}/slock/resolve.js (100%) rename {clis => plugins}/slock/server-list.js (100%) rename {clis => plugins}/slock/server-use.js (100%) rename {clis => plugins}/slock/shared.js (100%) rename {clis => plugins}/slock/task-claim.js (100%) rename {clis => plugins}/slock/task-convert.js (100%) rename {clis => plugins}/slock/task-create.js (100%) rename {clis => plugins}/slock/task-delete.js (100%) rename {clis => plugins}/slock/task-get.js (100%) rename {clis => plugins}/slock/task-list-server.js (100%) rename {clis => plugins}/slock/task-list.js (100%) rename {clis => plugins}/slock/task-status.js (100%) rename {clis => plugins}/slock/task-unclaim.js (100%) rename {clis/slock => plugins/slock/test}/api-base-canary.test.js (88%) rename {clis/slock => plugins/slock/test}/attachment-download.test.js (99%) rename {clis/slock => plugins/slock/test}/attachment-upload.test.js (99%) rename {clis/slock => plugins/slock/test}/attachment-url.test.js (98%) rename {clis/slock => plugins/slock/test}/bookmark-add.test.js (98%) rename {clis/slock => plugins/slock/test}/bookmark-list.test.js (98%) rename {clis/slock => plugins/slock/test}/bookmark-remove.test.js (96%) rename {clis/slock => plugins/slock/test}/channel-action.test.js (96%) rename {clis/slock => plugins/slock/test}/channel-create.test.js (98%) rename {clis/slock => plugins/slock/test}/channel-files.test.js (98%) rename {clis/slock => plugins/slock/test}/channel-info.test.js (97%) rename {clis/slock => plugins/slock/test}/channel-list.test.js (97%) rename {clis/slock => plugins/slock/test}/channel-mark.test.js (98%) rename {clis/slock => plugins/slock/test}/channel-members.test.js (98%) rename {clis/slock => plugins/slock/test}/cross-command.test.js (96%) rename {clis/slock => plugins/slock/test}/dm-list.test.js (97%) rename {clis/slock => plugins/slock/test}/error-detail-canary.test.js (97%) rename {clis/slock => plugins/slock/test}/errors.test.js (95%) rename {clis/slock => plugins/slock/test}/in-page.test.js (99%) rename {clis/slock => plugins/slock/test}/inbox-done.test.js (97%) rename {clis/slock => plugins/slock/test}/inbox-read-all.test.js (96%) rename {clis/slock => plugins/slock/test}/inbox.test.js (99%) rename {clis/slock => plugins/slock/test}/message-read.test.js (99%) rename {clis/slock => plugins/slock/test}/message-search.test.js (98%) rename {clis/slock => plugins/slock/test}/message-send.test.js (99%) rename {clis/slock => plugins/slock/test}/reaction-add.test.js (98%) rename {clis/slock => plugins/slock/test}/reaction-remove.test.js (97%) rename {clis/slock => plugins/slock/test}/resolve.test.js (95%) rename {clis/slock => plugins/slock/test}/server-list.test.js (96%) rename {clis/slock => plugins/slock/test}/server-override-canary.test.js (92%) rename {clis/slock => plugins/slock/test}/server-use.test.js (98%) rename {clis/slock => plugins/slock/test}/short-id-canary.test.js (93%) rename {clis/slock => plugins/slock/test}/site-session-canary.test.js (90%) rename {clis/slock => plugins/slock/test}/task-claim.test.js (99%) rename {clis/slock => plugins/slock/test}/task-convert.test.js (99%) rename {clis/slock => plugins/slock/test}/task-create.test.js (99%) rename {clis/slock => plugins/slock/test}/task-delete.test.js (98%) rename {clis/slock => plugins/slock/test}/task-get.test.js (98%) rename {clis/slock => plugins/slock/test}/task-list-server.test.js (98%) rename {clis/slock => plugins/slock/test}/task-list.test.js (99%) rename {clis/slock => plugins/slock/test}/task-status.test.js (99%) rename {clis/slock => plugins/slock/test}/task-unclaim.test.js (98%) rename {clis/slock => plugins/slock/test}/thread-follow.test.js (98%) rename {clis/slock => plugins/slock/test}/thread-list.test.js (97%) rename {clis/slock => plugins/slock/test}/thread-state.test.js (94%) rename {clis/slock => plugins/slock/test}/unread-summary.test.js (97%) rename {clis/slock => plugins/slock/test}/whoami.test.js (97%) rename {clis => plugins}/slock/thread-done.js (100%) rename {clis => plugins}/slock/thread-follow.js (100%) rename {clis => plugins}/slock/thread-list.js (100%) rename {clis => plugins}/slock/thread-state.js (100%) rename {clis => plugins}/slock/thread-undone.js (100%) rename {clis => plugins}/slock/thread-unfollow.js (100%) rename {clis => plugins}/slock/unread-summary.js (100%) create mode 100644 plugins/slock/webcmd-plugin.json rename {clis => plugins}/slock/whoami.js (83%) create mode 100644 plugins/tiktok/README.md rename {clis => plugins}/tiktok/auth.js (96%) rename {clis => plugins}/tiktok/comment.js (100%) rename {clis => plugins}/tiktok/creator-videos.js (100%) rename {clis => plugins}/tiktok/explore.js (100%) rename {clis => plugins}/tiktok/follow.js (100%) rename {clis => plugins}/tiktok/following.js (100%) rename {clis => plugins}/tiktok/friends.js (100%) rename {clis => plugins}/tiktok/like.js (100%) rename {clis => plugins}/tiktok/live.js (100%) rename {clis => plugins}/tiktok/notifications.js (100%) create mode 100644 plugins/tiktok/package.json rename {clis => plugins}/tiktok/profile.js (100%) rename {clis => plugins}/tiktok/save.js (100%) rename {clis => plugins}/tiktok/search.js (100%) rename {clis/tiktok => plugins/tiktok/test}/creator-videos.test.js (98%) rename {clis/tiktok => plugins/tiktok/test}/refactor.test.js (98%) rename {clis/tiktok => plugins/tiktok/test}/write-refactor.test.js (98%) rename {clis => plugins}/tiktok/unfollow.js (100%) rename {clis => plugins}/tiktok/unlike.js (100%) rename {clis => plugins}/tiktok/unsave.js (100%) rename {clis => plugins}/tiktok/user.js (100%) rename {clis => plugins}/tiktok/utils.js (100%) create mode 100644 plugins/tiktok/webcmd-plugin.json create mode 100644 plugins/trip/README.md rename {clis => plugins}/trip/attraction.js (100%) rename {clis => plugins}/trip/car.js (100%) rename {clis => plugins}/trip/deals.js (100%) rename {clis => plugins}/trip/flight-round.js (100%) rename {clis => plugins}/trip/flight.js (100%) rename {clis => plugins}/trip/hotel-search.js (100%) rename {clis => plugins}/trip/hotel.js (100%) rename {clis => plugins}/trip/package.js (100%) create mode 100644 plugins/trip/package.json rename {clis => plugins}/trip/search.js (100%) rename {clis/trip => plugins/trip/test}/trip.test.js (99%) rename {clis => plugins}/trip/tour.js (100%) rename {clis => plugins}/trip/train.js (100%) rename {clis => plugins}/trip/transfer.js (100%) rename {clis => plugins}/trip/utils.js (100%) create mode 100644 plugins/trip/webcmd-plugin.json create mode 100644 plugins/twitter/README.md rename {clis => plugins}/twitter/accept.js (100%) rename {clis => plugins}/twitter/article.js (100%) rename {clis => plugins}/twitter/auth.js (94%) rename {clis => plugins}/twitter/block.js (100%) rename {clis => plugins}/twitter/bookmark-folder.js (100%) rename {clis => plugins}/twitter/bookmark-folders.js (100%) rename {clis => plugins}/twitter/bookmark.js (100%) rename {clis => plugins}/twitter/bookmarks.js (100%) rename {clis => plugins}/twitter/delete.js (100%) rename {clis => plugins}/twitter/device-follow.js (100%) rename {clis => plugins}/twitter/download.js (100%) rename {clis => plugins}/twitter/follow-batch.js (100%) rename {clis => plugins}/twitter/follow.js (100%) rename {clis => plugins}/twitter/followers.js (100%) rename {clis => plugins}/twitter/following.js (100%) rename {clis => plugins}/twitter/hide-reply.js (100%) rename {clis => plugins}/twitter/like.js (100%) rename {clis => plugins}/twitter/likes.js (100%) rename {clis => plugins}/twitter/list-add-batch.js (100%) rename {clis => plugins}/twitter/list-add-core.js (99%) rename {clis => plugins}/twitter/list-add.js (100%) rename {clis => plugins}/twitter/list-batch-utils.js (100%) rename {clis => plugins}/twitter/list-create.js (100%) rename {clis => plugins}/twitter/list-delete.js (99%) rename {clis => plugins}/twitter/list-remove-batch.js (100%) rename {clis => plugins}/twitter/list-remove-core.js (99%) rename {clis => plugins}/twitter/list-remove.js (100%) rename {clis => plugins}/twitter/list-tweets.js (100%) create mode 100644 plugins/twitter/lists-parser.js rename {clis => plugins}/twitter/lists.js (73%) rename {clis => plugins}/twitter/notifications.js (100%) create mode 100644 plugins/twitter/package.json rename {clis => plugins}/twitter/post.js (100%) rename {clis => plugins}/twitter/profile.js (100%) rename {clis => plugins}/twitter/quote.js (100%) rename {clis => plugins}/twitter/reply-dm.js (100%) rename {clis => plugins}/twitter/reply.js (100%) rename {clis => plugins}/twitter/retweet.js (100%) rename {clis => plugins}/twitter/search.js (100%) rename {clis => plugins}/twitter/shared.js (100%) rename {clis/twitter => plugins/twitter/test}/article-evaluate.test.js (93%) rename {clis/twitter => plugins/twitter/test}/article.test.js (99%) rename {clis/twitter => plugins/twitter/test}/bookmark-folder.test.js (99%) rename {clis/twitter => plugins/twitter/test}/bookmark-folders.test.js (99%) rename {clis/twitter => plugins/twitter/test}/bookmark.test.js (97%) rename {clis/twitter => plugins/twitter/test}/bookmarks.test.js (99%) rename {clis/twitter => plugins/twitter/test}/delete.test.js (99%) rename {clis/twitter => plugins/twitter/test}/device-follow.test.js (98%) rename {clis/twitter => plugins/twitter/test}/download.test.js (99%) rename {clis/twitter => plugins/twitter/test}/follow-batch.test.js (98%) rename {clis/twitter => plugins/twitter/test}/followers.test.js (98%) rename {clis/twitter => plugins/twitter/test}/following.test.js (99%) rename {clis/twitter => plugins/twitter/test}/hide-reply.test.js (97%) rename {clis/twitter => plugins/twitter/test}/like.test.js (98%) rename {clis/twitter => plugins/twitter/test}/likes.test.js (99%) rename {clis/twitter => plugins/twitter/test}/list-add.test.js (98%) rename {clis/twitter => plugins/twitter/test}/list-batch.test.js (97%) rename {clis/twitter => plugins/twitter/test}/list-create.test.js (98%) rename {clis/twitter => plugins/twitter/test}/list-delete.test.js (98%) rename {clis/twitter => plugins/twitter/test}/list-remove.test.js (99%) rename {clis/twitter => plugins/twitter/test}/list-tweets.test.js (99%) rename {clis/twitter => plugins/twitter/test}/lists.test.js (99%) create mode 100644 plugins/twitter/test/page-mock.js rename {clis/twitter => plugins/twitter/test}/post.test.js (99%) rename {clis/twitter => plugins/twitter/test}/profile.test.js (99%) rename {clis/twitter => plugins/twitter/test}/quote.test.js (98%) rename {clis/twitter => plugins/twitter/test}/reply.test.js (98%) rename {clis/twitter => plugins/twitter/test}/retweet.test.js (98%) rename {clis/twitter => plugins/twitter/test}/search.test.js (99%) rename {clis/twitter => plugins/twitter/test}/shared.test.js (99%) rename {clis/twitter => plugins/twitter/test}/thread.test.js (98%) rename {clis/twitter => plugins/twitter/test}/timeline.test.js (99%) rename {clis/twitter => plugins/twitter/test}/trending.test.js (96%) rename {clis/twitter => plugins/twitter/test}/tweets.test.js (99%) rename {clis/twitter => plugins/twitter/test}/unbookmark.test.js (97%) rename {clis/twitter => plugins/twitter/test}/unlike.test.js (98%) rename {clis/twitter => plugins/twitter/test}/unretweet.test.js (98%) rename {clis/twitter => plugins/twitter/test}/utils.test.js (99%) rename {clis => plugins}/twitter/thread.js (100%) rename {clis => plugins}/twitter/timeline.js (100%) rename {clis => plugins}/twitter/trending.js (100%) rename {clis => plugins}/twitter/tweets.js (100%) rename {clis => plugins}/twitter/unblock.js (100%) rename {clis => plugins}/twitter/unbookmark.js (100%) rename {clis => plugins}/twitter/unfollow.js (100%) rename {clis => plugins}/twitter/unlike.js (100%) rename {clis => plugins}/twitter/unretweet.js (100%) rename {clis => plugins}/twitter/utils.js (100%) create mode 100644 plugins/twitter/webcmd-plugin.json create mode 100644 plugins/youtube/README.md rename {clis => plugins}/youtube/auth.js (96%) rename {clis => plugins}/youtube/channel.js (100%) rename {clis => plugins}/youtube/comments.js (100%) rename {clis => plugins}/youtube/feed.js (100%) rename {clis => plugins}/youtube/history.js (100%) rename {clis => plugins}/youtube/like.js (100%) create mode 100644 plugins/youtube/package.json rename {clis => plugins}/youtube/playlist.js (100%) rename {clis => plugins}/youtube/search.js (100%) rename {clis => plugins}/youtube/subscribe.js (100%) rename {clis => plugins}/youtube/subscriptions.js (100%) rename {clis/youtube => plugins/youtube/test}/channel.test.js (99%) rename {clis/youtube => plugins/youtube/test}/feed.test.js (99%) rename {clis/youtube => plugins/youtube/test}/transcript-group.test.js (99%) rename {clis/youtube => plugins/youtube/test}/transcript.test.js (99%) rename {clis/youtube => plugins/youtube/test}/utils.test.js (98%) rename {clis/youtube => plugins/youtube/test}/video.test.js (96%) rename {clis => plugins}/youtube/transcript-group.js (100%) rename {clis => plugins}/youtube/transcript.js (100%) rename {clis => plugins}/youtube/unlike.js (100%) rename {clis => plugins}/youtube/unsubscribe.js (100%) rename {clis => plugins}/youtube/utils.js (100%) rename {clis => plugins}/youtube/video.js (100%) rename {clis => plugins}/youtube/watch-later.js (100%) create mode 100644 plugins/youtube/webcmd-plugin.json diff --git a/cli-manifest.json b/cli-manifest.json index febbc261..fe51488c 100644 --- a/cli-manifest.json +++ b/cli-manifest.json @@ -1,9346 +1 @@ -[ - { - "site": "antigravity", - "name": "add-context", - "description": "Click the Add context button in the composer (opens file/URL picker for context attachment).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "cookies", - "description": "List cookies on the Antigravity renderer (JS-visible via document.cookie).", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "copy-code", - "description": "Return the text of a code block in the current conversation. Default: last code block; pass --index N (1-based from top) to pick a specific one.", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "index", - "type": "int", - "required": false, - "help": "1-based index of code block (default: last)" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "copy-message", - "description": "Return the text of the last assistant message (best-effort: walks up from the last visible Copy button).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "click-button", - "type": "boolean", - "default": false, - "required": false, - "help": "Also click the in-UI Copy button" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "delete", - "description": "Delete an Antigravity conversation by ID. Antigravity asks for confirmation; we click through it. Require --yes to actually delete.", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" - }, - { - "name": "yes", - "type": "boolean", - "default": false, - "required": false, - "help": "Actually delete (default: dry-run preview)" - } - ], - "columns": [ - "status", - "id" - ], - "type": "js", - "modulePath": "antigravity/delete.js", - "sourceFile": "antigravity/delete.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "display-options", - "description": "Open the Display Options menu and list its items.", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Item" - ], - "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "dump", - "description": "Dump the DOM to help AI understand the UI", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "htmlFile", - "snapFile" - ], - "type": "js", - "modulePath": "antigravity/dump.js", - "sourceFile": "antigravity/dump.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "extract-code", - "description": "Extract multi-line code blocks from the current Antigravity conversation", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "code" - ], - "type": "js", - "modulePath": "antigravity/extract-code.js", - "sourceFile": "antigravity/extract-code.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "history", - "description": "List visible Antigravity conversations from the sidebar", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max conversations to return" - } - ], - "columns": [ - "Index", - "Id", - "Title" - ], - "type": "js", - "modulePath": "antigravity/history.js", - "sourceFile": "antigravity/history.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "idb-list", - "description": "List IndexedDB databases on the Antigravity renderer.", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "mark-read", - "description": "Mark an unread Antigravity conversation as read. Fails if the row is already read or the postcondition cannot be verified.", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" - } - ], - "columns": [ - "status", - "id", - "clicked" - ], - "type": "js", - "modulePath": "antigravity/mark-read.js", - "sourceFile": "antigravity/mark-read.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "model", - "description": "Read or switch the active model in Antigravity. Without arguments, reports the current model. With (substring, case-insensitive), switches.", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": false, - "positional": true, - "help": "Substring (case-insensitive) of target model name. Omit to read current." - }, - { - "name": "list", - "type": "boolean", - "default": false, - "required": false, - "help": "List models in the picker (does not switch)" - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "antigravity/model.js", - "sourceFile": "antigravity/model.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "nav", - "description": "Click Go Back or Go Forward (Antigravity in-app history).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "direction", - "type": "str", - "required": true, - "positional": true, - "help": "back or forward" - } - ], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "new", - "description": "Start a new conversation / clear context in Antigravity", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "antigravity/new.js", - "sourceFile": "antigravity/new.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "react", - "description": "Click \"Good response\" or \"Bad response\" on the LAST assistant message.", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "kind", - "type": "str", - "required": true, - "positional": true, - "help": "good or bad" - } - ], - "columns": [ - "Status", - "Reaction" - ], - "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "read", - "description": "Read the latest chat messages from Antigravity AI", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "last", - "type": "str", - "required": false, - "help": "Number of recent messages to read (not fully implemented due to generic structure, currently returns full history text or latest chunk)" - } - ], - "columns": [ - "role", - "content" - ], - "type": "js", - "modulePath": "antigravity/read.js", - "sourceFile": "antigravity/read.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "recent-paths", - "description": "Show Antigravity's recently-opened folders/files (history.recentlyOpenedPathsList).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max rows to return" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js" - }, - { - "site": "antigravity", - "name": "rename", - "description": "Rename an Antigravity conversation by ID (NOT YET IMPLEMENTED — see source comment).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" - }, - { - "name": "title", - "type": "string", - "required": true, - "positional": true, - "help": "New title" - } - ], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "antigravity/rename.js", - "sourceFile": "antigravity/rename.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "revert", - "description": "Click the revert button (per-message revert for agent changes). Requires --yes (this modifies your workspace).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "yes", - "type": "boolean", - "default": false, - "required": false, - "help": "Actually revert (default: dry-run)" - } - ], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "send", - "description": "Send a message to Antigravity AI via the internal Lexical editor", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "message", - "type": "str", - "required": true, - "positional": true, - "help": "The message text to send" - } - ], - "columns": [ - "Status", - "Message" - ], - "type": "js", - "modulePath": "antigravity/send.js", - "sourceFile": "antigravity/send.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "settings", - "description": "Click the Antigravity settings button (matched by data-testid=\"settings-button\").", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "settings-read", - "description": "Read Antigravity's user settings.json (theme, proxy, agCockpit, tfa.system.autoAccept, etc.).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js" - }, - { - "site": "antigravity", - "name": "sidebar-toggle", - "description": "Click Toggle Sidebar (collapses/expands the Antigravity sidebar).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "state-get", - "description": "Read one value from Antigravity's state.vscdb. Pass --workspace for per-workspace.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Storage key name" - }, - { - "name": "workspace", - "type": "str", - "required": false, - "help": "Workspace id (from workspaces-list) to query per-workspace DB" - }, - { - "name": "max-bytes", - "type": "int", - "default": 8000, - "required": false, - "help": "Truncate value to this many chars" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js" - }, - { - "site": "antigravity", - "name": "state-keys", - "description": "List keys in Antigravity's globalStorage state.vscdb (VSCode-style). Pass --workspace to query a per-workspace DB. Works while Antigravity is closed.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "filter", - "type": "str", - "required": false, - "help": "Case-insensitive substring filter over keys" - }, - { - "name": "workspace", - "type": "str", - "required": false, - "help": "Workspace id (from workspaces-list) to query per-workspace DB" - }, - { - "name": "limit", - "type": "int", - "default": 200, - "required": false, - "help": "Max rows to return" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js" - }, - { - "site": "antigravity", - "name": "status", - "description": "Check Antigravity CDP connection and get current page state", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "status", - "url", - "title" - ], - "type": "js", - "modulePath": "antigravity/status.js", - "sourceFile": "antigravity/status.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "storage-get", - "description": "Read a single localStorage / sessionStorage value on the Antigravity renderer.", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Storage key name" - }, - { - "name": "storage", - "type": "str", - "default": "local", - "required": false, - "help": "\"local\" or \"session\"" - }, - { - "name": "max-bytes", - "type": "int", - "default": 4000, - "required": false, - "help": "Truncate value to this many chars" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "storage-keys", - "description": "List localStorage / sessionStorage keys on the Antigravity renderer (CDP).", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "storage", - "type": "str", - "default": "local", - "required": false, - "help": "\"local\" or \"session\"" - }, - { - "name": "filter", - "type": "str", - "required": false, - "help": "Case-insensitive substring filter" - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Max rows to return" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "toggle-aux", - "description": "Toggle the Auxiliary Pane (Antigravity's secondary panel for code/preview).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "antigravity/audit-extras.js", - "sourceFile": "antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "watch", - "description": "Stream new chat messages from Antigravity in real-time", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "timeout", - "type": "int", - "default": 86400, - "required": false, - "help": "Max seconds to keep watching (default: 86400 — 24h)" - } - ], - "columns": [], - "type": "js", - "modulePath": "antigravity/watch.js", - "sourceFile": "antigravity/watch.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "workspaces-list", - "description": "List Antigravity workspaceStorage entries (each represents a previously-opened folder).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max rows to return" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "antigravity/storage.js", - "sourceFile": "antigravity/storage.js" - }, - { - "site": "facebook", - "name": "add-friend", - "description": "Send a friend request on Facebook", - "access": "write", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Facebook username or profile URL" - } - ], - "columns": [ - "status", - "username" - ], - "type": "js", - "modulePath": "facebook/add-friend.js", - "sourceFile": "facebook/add-friend.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "events", - "description": "Browse Facebook event categories", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Number of categories" - } - ], - "columns": [ - "index", - "name" - ], - "type": "js", - "modulePath": "facebook/events.js", - "sourceFile": "facebook/events.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "feed", - "description": "Get your Facebook news feed", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of posts" - } - ], - "columns": [ - "index", - "author", - "content", - "likes", - "comments", - "shares" - ], - "type": "js", - "modulePath": "facebook/feed.js", - "sourceFile": "facebook/feed.js", - "navigateBefore": false - }, - { - "site": "facebook", - "name": "friends", - "description": "Get Facebook friend suggestions", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of friend suggestions" - } - ], - "columns": [ - "index", - "name", - "mutual" - ], - "type": "js", - "modulePath": "facebook/friends.js", - "sourceFile": "facebook/friends.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "groups", - "description": "List your Facebook groups", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of groups" - } - ], - "columns": [ - "index", - "name", - "last_post", - "url" - ], - "type": "js", - "modulePath": "facebook/groups.js", - "sourceFile": "facebook/groups.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "join-group", - "description": "Join a Facebook group", - "access": "write", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "group", - "type": "str", - "required": true, - "positional": true, - "help": "Group ID or URL path (e.g. '1876150192925481' or group name)" - } - ], - "columns": [ - "status", - "group" - ], - "type": "js", - "modulePath": "facebook/join-group.js", - "sourceFile": "facebook/join-group.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "login", - "description": "Open facebook login", - "access": "write", - "domain": "facebook.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "vanity", - "profile_url", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "facebook/auth.js", - "sourceFile": "facebook/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "facebook", - "name": "marketplace-inbox", - "description": "List recent Facebook Marketplace buyer/seller conversations", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of conversations to return" - } - ], - "columns": [ - "index", - "buyer", - "listing", - "snippet", - "time", - "unread" - ], - "type": "js", - "modulePath": "facebook/marketplace-inbox.js", - "sourceFile": "facebook/marketplace-inbox.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "marketplace-listings", - "description": "List your Facebook Marketplace seller listings", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of listings to return" - } - ], - "columns": [ - "index", - "title", - "price", - "status", - "listed", - "clicks", - "actions" - ], - "type": "js", - "modulePath": "facebook/marketplace-listings.js", - "sourceFile": "facebook/marketplace-listings.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "memories", - "description": "Get your Facebook memories (On This Day)", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of memories" - } - ], - "columns": [ - "index", - "source", - "content", - "time" - ], - "type": "js", - "modulePath": "facebook/memories.js", - "sourceFile": "facebook/memories.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "notifications", - "description": "Get recent Facebook notifications (includes unread / time / url / notif_id / notif_type columns)", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Number of notifications (1-100)" - } - ], - "columns": [ - "index", - "unread", - "text", - "time", - "url", - "notif_id", - "notif_type" - ], - "type": "js", - "modulePath": "facebook/notifications.js", - "sourceFile": "facebook/notifications.js", - "navigateBefore": false - }, - { - "site": "facebook", - "name": "profile", - "description": "Get Facebook user/page profile info", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Facebook username or page name" - } - ], - "columns": [ - "name", - "username", - "friends", - "followers", - "url" - ], - "type": "js", - "modulePath": "facebook/profile.js", - "sourceFile": "facebook/profile.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "search", - "description": "Search Facebook for people, pages, or posts", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "index", - "title", - "text", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "facebook/search.js", - "sourceFile": "facebook/search.js", - "navigateBefore": false - }, - { - "site": "facebook", - "name": "whoami", - "description": "Show the current logged-in facebook account", - "access": "read", - "domain": "facebook.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "vanity", - "profile_url" - ], - "type": "js", - "modulePath": "facebook/auth.js", - "sourceFile": "facebook/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "ask", - "description": "Send a message to Grok and get response", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "string", - "required": true, - "positional": true, - "help": "Prompt to send to Grok" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait for response (default: 120)" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending (default: false)" - } - ], - "columns": [ - "response" - ], - "type": "js", - "modulePath": "grok/ask.js", - "sourceFile": "grok/ask.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "delete", - "description": "Delete a Grok conversation by ID. Grok takes effect immediately with no confirmation dialog — require --yes to actually delete.", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" - }, - { - "name": "yes", - "type": "boolean", - "default": false, - "required": false, - "help": "Actually delete (default is a dry-run preview)" - } - ], - "columns": [ - "status", - "id" - ], - "type": "js", - "modulePath": "grok/delete.js", - "sourceFile": "grok/delete.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "detail", - "description": "Open a Grok conversation by ID and read its messages", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Session ID (UUID) or full https://grok.com/c/ URL" - }, - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" - } - ], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "grok/detail.js", - "sourceFile": "grok/detail.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "export", - "description": "Export all visible Grok conversation history metadata", - "access": "read", - "example": "webcmd grok export -f yaml", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 0, - "required": false, - "help": "Max conversations to export; 0 means all loaded history" - }, - { - "name": "maxScrolls", - "type": "int", - "default": 80, - "required": false, - "help": "Max history-list scroll rounds when limit is 0 (max 500)" - } - ], - "columns": [ - "index", - "id", - "title", - "date", - "url" - ], - "type": "js", - "modulePath": "grok/export.js", - "sourceFile": "grok/export.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "export-all", - "description": "Export Grok conversation history and each conversation transcript", - "access": "read", - "example": "webcmd grok export-all --limit 5 -f json", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 0, - "required": false, - "help": "Max conversations to export; 0 means all loaded history" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Skip this many conversations before exporting" - }, - { - "name": "manifestPath", - "type": "string", - "default": "", - "required": false, - "help": "Optional grok/export JSON manifest path; skips history dialog and visits listed /c pages directly" - }, - { - "name": "maxScrolls", - "type": "int", - "default": 80, - "required": false, - "help": "Max history-list scroll rounds when limit is 0 (max 500)" - }, - { - "name": "pageScrolls", - "type": "int", - "default": 30, - "required": false, - "help": "Max per-conversation scroll-to-bottom rounds (max 200)" - }, - { - "name": "pageTimeoutMs", - "type": "int", - "default": 30000, - "required": false, - "help": "Max wait for each conversation page to show messages" - }, - { - "name": "delayMinMs", - "type": "int", - "default": 0, - "required": false, - "help": "Minimum polite delay after a conversation page loads" - }, - { - "name": "delayMaxMs", - "type": "int", - "default": 5000, - "required": false, - "help": "Maximum polite delay after a conversation page loads" - } - ], - "columns": [ - "index", - "id", - "title", - "date", - "url", - "status", - "messageCount", - "error", - "messagesJson" - ], - "type": "js", - "modulePath": "grok/export-all.js", - "sourceFile": "grok/export-all.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "history", - "description": "List recent Grok conversations from the sidebar (requires login)", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max conversations to show (default 20, max 100)" - } - ], - "columns": [ - "Index", - "Title", - "Url" - ], - "type": "js", - "modulePath": "grok/history.js", - "sourceFile": "grok/history.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "image", - "description": "Generate images on grok.com and return image URLs", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "string", - "required": true, - "positional": true, - "help": "Image generation prompt" - }, - { - "name": "timeout", - "type": "int", - "default": 240, - "required": false, - "help": "Max seconds to wait for the image (default: 240)" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending (default: false)" - }, - { - "name": "count", - "type": "int", - "default": 1, - "required": false, - "help": "Minimum images to wait for before returning (default: 1)" - }, - { - "name": "out", - "type": "string", - "default": "", - "required": false, - "help": "Directory to save downloaded images (uses browser session to bypass auth)" - } - ], - "columns": [ - "url", - "width", - "height", - "path" - ], - "type": "js", - "modulePath": "grok/image.js", - "sourceFile": "grok/image.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "login", - "description": "Open grok login", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "grok/auth.js", - "sourceFile": "grok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "new", - "description": "Start a new conversation in Grok", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "grok/new.js", - "sourceFile": "grok/new.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "pin", - "description": "Pin a Grok conversation by ID", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" - } - ], - "columns": [ - "status", - "id" - ], - "type": "js", - "modulePath": "grok/pin.js", - "sourceFile": "grok/pin.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "read", - "description": "Read messages in the current Grok conversation", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" - } - ], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "grok/read.js", - "sourceFile": "grok/read.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "send", - "description": "Fire-and-forget: send a prompt to Grok without waiting for the reply", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send to Grok" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - } - ], - "columns": [ - "Status", - "Prompt" - ], - "type": "js", - "modulePath": "grok/send.js", - "sourceFile": "grok/send.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "status", - "description": "Check Grok page availability, login state, current session and model", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status", - "Login", - "Model", - "SessionId", - "Url" - ], - "type": "js", - "modulePath": "grok/status.js", - "sourceFile": "grok/status.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "unpin", - "description": "Unpin a Grok conversation by ID", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" - } - ], - "columns": [ - "status", - "id" - ], - "type": "js", - "modulePath": "grok/pin.js", - "sourceFile": "grok/pin.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "whoami", - "description": "Show the current logged-in grok account", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "grok/auth.js", - "sourceFile": "grok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "instagram", - "name": "collection-create", - "description": "Create a new Instagram saved-posts collection (folder)", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Name of the collection to create" - } - ], - "columns": [ - "status", - "collectionId", - "collectionName", - "mediaCount" - ], - "type": "js", - "modulePath": "instagram/collection-create.js", - "sourceFile": "instagram/collection-create.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "collection-delete", - "description": "Delete an Instagram saved-posts collection (folder) by name or id", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "target", - "type": "str", - "required": true, - "positional": true, - "help": "Collection name (case-insensitive) or numeric collection_id" - } - ], - "columns": [ - "status", - "collectionId", - "collectionName" - ], - "type": "js", - "modulePath": "instagram/collection-delete.js", - "sourceFile": "instagram/collection-delete.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "comment", - "description": "Comment on an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Comment text" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "text" - ], - "type": "js", - "modulePath": "instagram/comment.js", - "sourceFile": "instagram/comment.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "download", - "description": "Download images and videos from Instagram posts and reels", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram post / reel / tv URL" - }, - { - "name": "path", - "type": "str", - "default": "~/Downloads/Instagram", - "required": false, - "help": "Download directory" - } - ], - "type": "js", - "modulePath": "instagram/download.js", - "sourceFile": "instagram/download.js", - "navigateBefore": false - }, - { - "site": "instagram", - "name": "explore", - "description": "Instagram explore/discover trending posts", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts" - } - ], - "columns": [ - "rank", - "user", - "caption", - "likes", - "comments", - "type" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "instagram/explore.js", - "sourceFile": "instagram/explore.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "follow", - "description": "Follow an Instagram user", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username to follow" - } - ], - "columns": [ - "status", - "username" - ], - "type": "js", - "modulePath": "instagram/follow.js", - "sourceFile": "instagram/follow.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "followers", - "description": "List followers of an Instagram user", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of followers" - } - ], - "columns": [ - "rank", - "username", - "name", - "verified", - "private" - ], - "type": "js", - "modulePath": "instagram/followers.js", - "sourceFile": "instagram/followers.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "following", - "description": "List accounts an Instagram user is following", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of accounts" - } - ], - "columns": [ - "rank", - "username", - "name", - "verified", - "private" - ], - "type": "js", - "modulePath": "instagram/following.js", - "sourceFile": "instagram/following.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "like", - "description": "Like an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "post" - ], - "type": "js", - "modulePath": "instagram/like.js", - "sourceFile": "instagram/like.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "login", - "description": "Open instagram login", - "access": "write", - "domain": "instagram.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "username", - "full_name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "instagram/auth.js", - "sourceFile": "instagram/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "instagram", - "name": "note", - "description": "Publish a text Instagram note", - "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "content", - "type": "str", - "required": true, - "positional": true, - "help": "Note text (max 60 characters)" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds for the overall command (default: 120)" - } - ], - "columns": [ - "status", - "detail", - "noteId" - ], - "type": "js", - "modulePath": "instagram/note.js", - "sourceFile": "instagram/note.js", - "navigateBefore": true - }, - { - "site": "instagram", - "name": "post", - "description": "Post an Instagram feed image or mixed-media carousel", - "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "media", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Comma-separated media paths (images/videos, up to 10)", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "image/jpeg", - "image/png", - "image/webp", - "video/mp4" - ], - "maxBytes": 262144000 - } - }, - { - "name": "content", - "type": "str", - "required": false, - "positional": true, - "help": "Caption text" - }, - { - "name": "timeout", - "type": "int", - "default": 300, - "required": false, - "help": "Max seconds for the overall command (default: 300)" - } - ], - "columns": [ - "status", - "detail", - "url" - ], - "type": "js", - "modulePath": "instagram/post.js", - "sourceFile": "instagram/post.js", - "navigateBefore": true - }, - { - "site": "instagram", - "name": "profile", - "description": "Get Instagram user profile info", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" - } - ], - "columns": [ - "username", - "name", - "followers", - "following", - "posts", - "verified", - "bio" - ], - "type": "js", - "modulePath": "instagram/profile.js", - "sourceFile": "instagram/profile.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "reel", - "description": "Post an Instagram reel video", - "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "video", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Path to a single .mp4 video file", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "video/mp4" - ], - "maxBytes": 262144000 - } - }, - { - "name": "content", - "type": "str", - "required": false, - "positional": true, - "help": "Caption text" - }, - { - "name": "timeout", - "type": "int", - "default": 600, - "required": false, - "help": "Max seconds for the overall command (default: 600)" - } - ], - "columns": [ - "status", - "detail", - "url" - ], - "type": "js", - "modulePath": "instagram/reel.js", - "sourceFile": "instagram/reel.js", - "navigateBefore": true - }, - { - "site": "instagram", - "name": "save", - "description": "Save (bookmark) an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "post" - ], - "type": "js", - "modulePath": "instagram/save.js", - "sourceFile": "instagram/save.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "saved", - "description": "Get your saved Instagram posts (optionally from a specific collection)", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of saved posts" - }, - { - "name": "collection", - "type": "str", - "required": false, - "help": "Collection name (case-insensitive). Omit for the default \"All posts\" feed." - } - ], - "columns": [ - "index", - "user", - "caption", - "likes", - "comments", - "type" - ], - "type": "js", - "modulePath": "instagram/saved.js", - "sourceFile": "instagram/saved.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "search", - "description": "Search Instagram users", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "username", - "name", - "verified", - "private", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "instagram/search.js", - "sourceFile": "instagram/search.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "story", - "description": "Post a single Instagram story image or video", - "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "media", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Path to a single story image or video file" - }, - { - "name": "timeout", - "type": "int", - "default": 300, - "required": false, - "help": "Max seconds for the overall command (default: 300)" - } - ], - "columns": [ - "status", - "detail", - "url" - ], - "type": "js", - "modulePath": "instagram/story.js", - "sourceFile": "instagram/story.js", - "navigateBefore": true - }, - { - "site": "instagram", - "name": "unfollow", - "description": "Unfollow an Instagram user", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username to unfollow" - } - ], - "columns": [ - "status", - "username" - ], - "type": "js", - "modulePath": "instagram/unfollow.js", - "sourceFile": "instagram/unfollow.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "unlike", - "description": "Unlike an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "post" - ], - "type": "js", - "modulePath": "instagram/unlike.js", - "sourceFile": "instagram/unlike.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "unsave", - "description": "Unsave (remove bookmark) an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "post" - ], - "type": "js", - "modulePath": "instagram/unsave.js", - "sourceFile": "instagram/unsave.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "user", - "description": "Get recent posts from an Instagram user", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" - }, - { - "name": "limit", - "type": "int", - "default": 12, - "required": false, - "help": "Number of posts" - } - ], - "columns": [ - "index", - "caption", - "likes", - "comments", - "type", - "date" - ], - "type": "js", - "modulePath": "instagram/user.js", - "sourceFile": "instagram/user.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "whoami", - "description": "Show the current logged-in instagram account", - "access": "read", - "domain": "instagram.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "username", - "full_name" - ], - "type": "js", - "modulePath": "instagram/auth.js", - "sourceFile": "instagram/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "notebooklm", - "name": "add-source", - "description": "Add a URL, text, or local file source to an existing NotebookLM notebook", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Source URL to add (http/https). Pass exactly one of --url, --content, --file." - }, - { - "name": "content", - "type": "str", - "required": false, - "help": "Raw text content to add as a Text source (max 10 MB)." - }, - { - "name": "file", - "type": "str", - "required": false, - "help": "Local file path to upload as a source (max 52428800 bytes; pdf / txt / md / html / docx / etc.). Uses Google Drive's 3-step resumable upload protocol." - }, - { - "name": "title", - "type": "str", - "required": false, - "help": "Title for the text source (default \"Text Source\"). Ignored for --url and --file." - }, - { - "name": "mime-type", - "type": "str", - "required": false, - "help": "Override the auto-detected MIME type when --file is given." - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually add the remote source to the NotebookLM notebook" - } - ], - "columns": [ - "notebook_id", - "source_id", - "kind", - "identifier", - "notebook_url" - ], - "type": "js", - "modulePath": "notebooklm/add-source.js", - "sourceFile": "notebooklm/add-source.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "create", - "description": "Create a new NotebookLM notebook with the given title", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "title", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook title (1-200 chars)" - }, - { - "name": "emoji", - "type": "str", - "required": false, - "help": "Notebook emoji icon (default 📒)" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually create the remote NotebookLM notebook" - } - ], - "columns": [ - "id", - "title", - "emoji", - "url" - ], - "type": "js", - "modulePath": "notebooklm/create.js", - "sourceFile": "notebooklm/create.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "current", - "description": "Show metadata for the currently opened NotebookLM notebook tab", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", - "title", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/current.js", - "sourceFile": "notebooklm/current.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "generate-audio", - "description": "Trigger an Audio Overview (Deep Dive podcast) generation for a NotebookLM notebook, using all of its sources", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually trigger remote NotebookLM audio generation" - } - ], - "columns": [ - "notebook_id", - "audio_id", - "source_count", - "status", - "notebook_url" - ], - "type": "js", - "modulePath": "notebooklm/generate-audio.js", - "sourceFile": "notebooklm/generate-audio.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "generate-slides", - "description": "Trigger a Slide Deck (AI presentation) generation for a NotebookLM notebook, using all of its sources", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "length", - "type": "str", - "required": false, - "help": "Slide deck length: 1=Short, 3=Default (default 3)" - }, - { - "name": "language", - "type": "str", - "required": false, - "help": "Language code (default en)" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually trigger remote NotebookLM slide deck generation" - } - ], - "columns": [ - "notebook_id", - "slides_id", - "source_count", - "status", - "notebook_url" - ], - "type": "js", - "modulePath": "notebooklm/generate-slides.js", - "sourceFile": "notebooklm/generate-slides.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "get", - "aliases": [ - "metadata" - ], - "description": "Get rich metadata for the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", - "title", - "emoji", - "source_count", - "created_at", - "updated_at", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/get.js", - "sourceFile": "notebooklm/get.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "history", - "description": "List NotebookLM conversation history threads in the current notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "thread_id", - "item_count", - "preview", - "source", - "notebook_id", - "url" - ], - "type": "js", - "modulePath": "notebooklm/history.js", - "sourceFile": "notebooklm/history.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "list", - "description": "List NotebookLM notebooks via in-page batchexecute RPC in the current logged-in session", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "id", - "is_owner", - "created_at", - "source", - "url" - ], - "type": "js", - "modulePath": "notebooklm/list.js", - "sourceFile": "notebooklm/list.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "login", - "description": "Open notebooklm login", - "access": "write", - "domain": "google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "name", - "authuser", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "notebooklm/auth.js", - "sourceFile": "notebooklm/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "notebooklm", - "name": "note-list", - "aliases": [ - "notes-list" - ], - "description": "List saved notes from the Studio panel of the current NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "created_at", - "source", - "url" - ], - "type": "js", - "modulePath": "notebooklm/note-list.js", - "sourceFile": "notebooklm/note-list.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "notes-get", - "description": "Get one note from the current NotebookLM notebook by title from the visible note editor", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "note", - "type": "str", - "required": true, - "positional": true, - "help": "Note title or id from the current notebook" - } - ], - "columns": [ - "title", - "content", - "source", - "url" - ], - "type": "js", - "modulePath": "notebooklm/notes-get.js", - "sourceFile": "notebooklm/notes-get.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "open", - "aliases": [ - "select" - ], - "description": "Open one NotebookLM notebook in the adapter session by id or URL", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from list output, or a full NotebookLM notebook URL" - } - ], - "columns": [ - "id", - "title", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/open.js", - "sourceFile": "notebooklm/open.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-fulltext", - "description": "Get the extracted fulltext for one source in the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "source", - "type": "str", - "required": true, - "positional": true, - "help": "Source id or title from the current notebook" - } - ], - "columns": [ - "title", - "kind", - "char_count", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/source-fulltext.js", - "sourceFile": "notebooklm/source-fulltext.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-get", - "description": "Get one source from the currently opened NotebookLM notebook by id or title", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "source", - "type": "str", - "required": true, - "positional": true, - "help": "Source id or title from the current notebook" - } - ], - "columns": [ - "title", - "id", - "type", - "size", - "created_at", - "updated_at", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/source-get.js", - "sourceFile": "notebooklm/source-get.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-guide", - "description": "Get the guide summary and keywords for one source in the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "source", - "type": "str", - "required": true, - "positional": true, - "help": "Source id or title from the current notebook" - } - ], - "columns": [ - "source_id", - "notebook_id", - "title", - "type", - "summary", - "keywords", - "source" - ], - "type": "js", - "modulePath": "notebooklm/source-guide.js", - "sourceFile": "notebooklm/source-guide.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-list", - "description": "List sources for the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "id", - "type", - "size", - "created_at", - "updated_at", - "url", - "source" - ], - "type": "js", - "modulePath": "notebooklm/source-list.js", - "sourceFile": "notebooklm/source-list.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "status", - "description": "Check NotebookLM page availability and login state in the current Chrome session", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "login", - "page", - "url", - "title", - "notebooks" - ], - "type": "js", - "modulePath": "notebooklm/status.js", - "sourceFile": "notebooklm/status.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "summary", - "description": "Get the summary block from the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "summary", - "source", - "url" - ], - "type": "js", - "modulePath": "notebooklm/summary.js", - "sourceFile": "notebooklm/summary.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "whoami", - "description": "Show the current logged-in notebooklm account", - "access": "read", - "domain": "google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name", - "authuser" - ], - "type": "js", - "modulePath": "notebooklm/auth.js", - "sourceFile": "notebooklm/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "notebooklm", - "name": "write-note", - "description": "Create a Studio note in an existing NotebookLM notebook with the given title and Markdown content", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "title", - "type": "str", - "required": true, - "help": "Note title (1-200 chars)" - }, - { - "name": "content", - "type": "str", - "required": true, - "help": "Note body as Markdown" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually create the remote NotebookLM note" - } - ], - "columns": [ - "notebook_id", - "note_id", - "title", - "notebook_url" - ], - "type": "js", - "modulePath": "notebooklm/write-note.js", - "sourceFile": "notebooklm/write-note.js", - "navigateBefore": false - }, - { - "site": "qoder", - "name": "account", - "description": "Click the account button (username) in the Qoder sidebar and return the visible account dropdown items.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": false, - "help": "Username text shown in the sidebar (default: tries common short labels)" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "add-workspace", - "description": "Click \"Add Workspace\" — opens the folder picker. Note: this opens a system file-picker dialog that Qoder controls; the actual folder selection must be done in the UI by the user.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "ask", - "description": "Send a prompt to Qoder and wait up to --timeout seconds for the reply (best-effort: polls for the chat turn count to grow + stabilize).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt text" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait" - } - ], - "columns": [ - "Role", - "Text", - "WaitedSeconds" - ], - "type": "js", - "modulePath": "qoder/quest.js", - "sourceFile": "qoder/quest.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "credits", - "description": "Click \"Credits Usage\" and return the credits-usage display text.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "history", - "description": "List Quests visible in the Qoder sidebar. Returns title + visible metadata.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Title" - ], - "type": "js", - "modulePath": "qoder/history.js", - "sourceFile": "qoder/history.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "knowledge", - "description": "Open the Knowledge view (Qoder's personal/team knowledge base).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "marketplace", - "description": "Open the Qoder Marketplace.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "more-actions", - "description": "Click the \"More Actions\" button and list its menu items.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Item" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "new", - "description": "Start a new Qoder Quest (conversation). Clicks the \"New Quest\" button in the sidebar (or its ⌘N variant).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/quest.js", - "sourceFile": "qoder/quest.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "open-editor", - "description": "Click \"Open Editor\" — opens the current draft in a full editor pane.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/composer.js", - "sourceFile": "qoder/composer.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "open-panel", - "description": "Open / close the Qoder bottom panel (Output / Terminal / Debug Console). ⌥⌘B equivalent.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "prompt-enhance", - "description": "Click \"Prompt Enhance\" — Qoder rewrites the current composer draft for better LLM consumption.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/composer.js", - "sourceFile": "qoder/composer.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "read", - "description": "Read messages in the current Qoder Quest. Returns role + text for each visible turn.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Role", - "Text" - ], - "type": "js", - "modulePath": "qoder/read.js", - "sourceFile": "qoder/read.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "search", - "description": "Open Qoder Search palette (⌘P), type a query, return matched options.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search text" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Item" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "send", - "description": "Type text into the Qoder composer and click \"Send message\" (fire-and-forget).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Text to send" - } - ], - "columns": [ - "Status", - "Length" - ], - "type": "js", - "modulePath": "qoder/quest.js", - "sourceFile": "qoder/quest.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "settings", - "description": "Click the Settings button in the Qoder sidebar.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "sidebar-toggle", - "description": "Collapse / Expand the Qoder Quest List sidebar (⌘B).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "status", - "description": "Check Qoder CDP connection and report the current renderer URL + title.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" - ], - "type": "js", - "modulePath": "qoder/status.js", - "sourceFile": "qoder/status.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "view-all", - "description": "Click \"View all\" to show all Quests.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "qoder/ui.js", - "sourceFile": "qoder/ui.js", - "navigateBefore": true - }, - { - "site": "reddit", - "name": "comment", - "description": "Post a comment on a Reddit post", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" - }, - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "Comment text" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "reddit/comment.js", - "sourceFile": "reddit/comment.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "frontpage", - "description": "Reddit Frontpage / r/all", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "author", - "upvotes", - "comments", - "url", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/frontpage.js", - "sourceFile": "reddit/frontpage.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "home", - "description": "Reddit personalized home feed (Best, requires login)", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Number of posts (1–100)" - } - ], - "columns": [ - "rank", - "title", - "subreddit", - "score", - "comments", - "postId", - "author", - "url", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/home.js", - "sourceFile": "reddit/home.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "hot", - "description": "Reddit hot posts", - "access": "read", - "domain": "www.reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "subreddit", - "type": "str", - "default": "", - "required": false, - "help": "Subreddit name (e.g. programming). Empty for frontpage" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts" - } - ], - "columns": [ - "rank", - "title", - "subreddit", - "score", - "comments", - "postId", - "author", - "url", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/hot.js", - "sourceFile": "reddit/hot.js", - "navigateBefore": "https://www.reddit.com" - }, - { - "site": "reddit", - "name": "login", - "description": "Open reddit login", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "username", - "id", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "reddit/auth.js", - "sourceFile": "reddit/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "reddit", - "name": "popular", - "description": "Reddit Popular posts (/r/popular)", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "" - } - ], - "columns": [ - "rank", - "id", - "title", - "subreddit", - "score", - "comments", - "author", - "url", - "created_utc", - "selftext", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/popular.js", - "sourceFile": "reddit/popular.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "read", - "description": "Read a Reddit post and its comments", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "str", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or full URL" - }, - { - "name": "sort", - "type": "str", - "default": "best", - "required": false, - "help": "Comment sort: best, top, new, controversial, old, qa" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Number of top-level comments" - }, - { - "name": "depth", - "type": "int", - "default": 2, - "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" - }, - { - "name": "replies", - "type": "int", - "default": 5, - "required": false, - "help": "Max replies shown per comment at each level (sorted by score)" - }, - { - "name": "max-length", - "type": "int", - "default": 2000, - "required": false, - "help": "Max characters per comment body (min 100)" - }, - { - "name": "expand-more", - "type": "bool", - "default": false, - "required": false, - "help": "Follow Reddit \"more comments\" stubs by calling /api/morechildren.json" - }, - { - "name": "expand-rounds", - "type": "int", - "default": 2, - "required": false, - "help": "Max expansion passes when --expand-more is on (1–5; each round can fan out new \"more\" stubs)" - } - ], - "columns": [ - "type", - "author", - "score", - "text", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/read.js", - "sourceFile": "reddit/read.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "reply", - "description": "Reply to a Reddit comment", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "comment-id", - "type": "string", - "required": true, - "positional": true, - "help": "Comment ID (e.g. okf3s7u) or fullname (t1_xxx)" - }, - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "Reply text" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "reddit/reply.js", - "sourceFile": "reddit/reply.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "save", - "description": "Save or unsave a Reddit post", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" - }, - { - "name": "undo", - "type": "boolean", - "default": false, - "required": false, - "help": "Unsave instead of save" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "reddit/save.js", - "sourceFile": "reddit/save.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "saved", - "description": "Browse your saved Reddit posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "score", - "comments", - "url" - ], - "type": "js", - "modulePath": "reddit/saved.js", - "sourceFile": "reddit/saved.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "search", - "description": "Search Reddit Posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit search query" - }, - { - "name": "subreddit", - "type": "string", - "default": "", - "required": false, - "help": "Search within a specific subreddit" - }, - { - "name": "sort", - "type": "string", - "default": "relevance", - "required": false, - "help": "Sort order: relevance, hot, top, new, comments" - }, - { - "name": "time", - "type": "string", - "default": "all", - "required": false, - "help": "Time filter: hour, day, week, month, year, all" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "id", - "title", - "subreddit", - "author", - "score", - "comments", - "url", - "created_utc", - "selftext", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "reddit/search.js", - "sourceFile": "reddit/search.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "subreddit", - "description": "Get posts from a specific Subreddit", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "name", - "type": "string", - "required": true, - "positional": true, - "help": "Subreddit name (no `r/` prefix; e.g. `python`)" - }, - { - "name": "sort", - "type": "string", - "default": "hot", - "required": false, - "help": "Sorting method: hot, new, top, rising, controversial" - }, - { - "name": "time", - "type": "string", - "default": "all", - "required": false, - "help": "Time filter for top/controversial: hour, day, week, month, year, all" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "id", - "title", - "subreddit", - "author", - "upvotes", - "comments", - "url", - "created_utc", - "selftext", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "reddit/subreddit.js", - "sourceFile": "reddit/subreddit.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "subreddit-info", - "description": "Show metadata for a Reddit subreddit (subscribers, description, created date, NSFW)", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "name", - "type": "string", - "required": true, - "positional": true, - "help": "Subreddit name (no `r/` prefix needed)" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "reddit/subreddit-info.js", - "sourceFile": "reddit/subreddit-info.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "subscribe", - "description": "Subscribe or unsubscribe to a subreddit", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "subreddit", - "type": "string", - "required": true, - "positional": true, - "help": "Subreddit name (e.g. python)" - }, - { - "name": "undo", - "type": "boolean", - "default": false, - "required": false, - "help": "Unsubscribe instead of subscribe" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "reddit/subscribe.js", - "sourceFile": "reddit/subscribe.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "subscribed", - "description": "List subreddits you are subscribed to", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Max subreddits to return (1-1000, auto-paginates)" - } - ], - "columns": [ - "id", - "subreddit", - "title", - "subscribers", - "description", - "url" - ], - "type": "js", - "modulePath": "reddit/subscribed.js", - "sourceFile": "reddit/subscribed.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "upvote", - "description": "Upvote or downvote a Reddit post", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" - }, - { - "name": "direction", - "type": "string", - "default": "up", - "required": false, - "help": "Vote direction: up, down, none" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "reddit/upvote.js", - "sourceFile": "reddit/upvote.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "upvoted", - "description": "Browse your upvoted Reddit posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "score", - "comments", - "url" - ], - "type": "js", - "modulePath": "reddit/upvoted.js", - "sourceFile": "reddit/upvoted.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "user", - "description": "View a Reddit user profile", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit username (no `u/` prefix needed)" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "reddit/user.js", - "sourceFile": "reddit/user.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "user-comments", - "description": "View a Reddit user's comment history", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit username (no `u/` prefix needed)" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "subreddit", - "score", - "body", - "url" - ], - "type": "js", - "modulePath": "reddit/user-comments.js", - "sourceFile": "reddit/user-comments.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "user-posts", - "description": "View a Reddit user's submitted posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit username (no `u/` prefix needed)" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "score", - "comments", - "url" - ], - "type": "js", - "modulePath": "reddit/user-posts.js", - "sourceFile": "reddit/user-posts.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "whoami", - "description": "Show the currently logged-in Reddit user", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "reddit/whoami.js", - "sourceFile": "reddit/whoami.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "slock", - "name": "attachment-download", - "description": "Download an attachment to a local file. Resolves a signed CDN URL in the page, then fetches bytes node-side (no CORS).", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "attachmentId", - "type": "str", - "required": true, - "positional": true, - "help": "Attachment UUID" - }, - { - "name": "out", - "type": "str", - "required": false, - "help": "Local path to write to. Defaults to ./.bin" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server slug" - } - ], - "columns": [ - "attachmentId", - "out", - "sizeBytes" - ], - "type": "js", - "modulePath": "slock/attachment-download.js", - "sourceFile": "slock/attachment-download.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "attachment-upload", - "description": "Upload a local file to Slock attachments. Prints the attachmentId for use with `message-send --attach`.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "file", - "type": "str", - "required": true, - "positional": true, - "help": "Local file path to upload (single file; max 50 MB)" - }, - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name — server requires the attachment be scoped to a channel" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server slug" - } - ], - "columns": [ - "attachmentId", - "filename", - "mimeType", - "sizeBytes" - ], - "type": "js", - "modulePath": "slock/attachment-upload.js", - "sourceFile": "slock/attachment-upload.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "attachment-url", - "description": "Get a short-lived signed CDN URL for an attachment (does not download bytes).", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "attachmentId", - "type": "str", - "required": true, - "positional": true, - "help": "Attachment UUID" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server slug" - } - ], - "columns": [ - "attachmentId", - "url", - "expiresAt" - ], - "type": "js", - "modulePath": "slock/attachment-url.js", - "sourceFile": "slock/attachment-url.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "bookmark-add", - "description": "Bookmark a message (POST /channels/saved). Requires full messageId UUID.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full messageId UUID (short ids rejected)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "messageId", - "saved" - ], - "type": "js", - "modulePath": "slock/bookmark-add.js", - "sourceFile": "slock/bookmark-add.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "bookmark-list", - "description": "List bookmarks (saved messages) in the active server", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max results" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Offset" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "messageId", - "content", - "savedAt" - ], - "type": "js", - "modulePath": "slock/bookmark-list.js", - "sourceFile": "slock/bookmark-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "bookmark-remove", - "description": "Remove a bookmark (DELETE /channels/saved/:messageId). 404 is treated as already-removed.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full messageId UUID" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "messageId", - "removed", - "note" - ], - "type": "js", - "modulePath": "slock/bookmark-remove.js", - "sourceFile": "slock/bookmark-remove.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-archive", - "description": "Archive a channel — admin only (POST /channels/:id/archive)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "id", - "archivedAt", - "result" - ], - "type": "js", - "modulePath": "slock/channel-archive.js", - "sourceFile": "slock/channel-archive.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-create", - "description": "Create a channel — admin only (POST /channels/). Public unless --private.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Channel name" - }, - { - "name": "description", - "type": "str", - "required": false, - "help": "Channel description / topic (≤500 chars)" - }, - { - "name": "private", - "type": "bool", - "default": false, - "required": false, - "help": "Create a private channel instead of public" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "name", - "type", - "result" - ], - "type": "js", - "modulePath": "slock/channel-create.js", - "sourceFile": "slock/channel-create.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-files", - "description": "List files shared in a channel (GET /channels/:id/files)", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max files" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "filename", - "mimeType", - "sizeBytes", - "messageId", - "createdAt" - ], - "type": "js", - "modulePath": "slock/channel-files.js", - "sourceFile": "slock/channel-files.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-info", - "description": "Show one channel's details (GET /channels/:id)", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "name", - "type", - "topic", - "joined", - "archivedAt" - ], - "type": "js", - "modulePath": "slock/channel-info.js", - "sourceFile": "slock/channel-info.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-join", - "description": "Join a public channel (POST /channels/:id/join)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "id", - "archivedAt", - "result" - ], - "type": "js", - "modulePath": "slock/channel-join.js", - "sourceFile": "slock/channel-join.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-leave", - "description": "Leave a channel (POST /channels/:id/leave)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "id", - "archivedAt", - "result" - ], - "type": "js", - "modulePath": "slock/channel-leave.js", - "sourceFile": "slock/channel-leave.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-list", - "description": "List channels in the active slock server", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id) for this call" - } - ], - "columns": [ - "id", - "name", - "topic" - ], - "type": "js", - "modulePath": "slock/channel-list.js", - "sourceFile": "slock/channel-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-mark", - "description": "Mark a channel read (default), read up to --seq, or --unread.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "seq", - "type": "int", - "required": false, - "help": "Mark read up to this seq (omit for read-all)" - }, - { - "name": "unread", - "type": "bool", - "default": false, - "required": false, - "help": "Mark the channel unread instead of read" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "action", - "result" - ], - "type": "js", - "modulePath": "slock/channel-mark.js", - "sourceFile": "slock/channel-mark.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-members", - "description": "List members of a channel", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id)" - } - ], - "columns": [ - "userId", - "name", - "kind", - "role" - ], - "type": "js", - "modulePath": "slock/channel-members.js", - "sourceFile": "slock/channel-members.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-unarchive", - "description": "Unarchive a channel — admin only (POST /channels/:id/unarchive). #name lookups exclude archived channels; pass the channelId UUID for archived ones.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "id", - "archivedAt", - "result" - ], - "type": "js", - "modulePath": "slock/channel-unarchive.js", - "sourceFile": "slock/channel-unarchive.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "dm-list", - "description": "List DM channels in the active server (GET /channels/dm)", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id)" - } - ], - "columns": [ - "channelId", - "peerName", - "peerId", - "createdAt" - ], - "type": "js", - "modulePath": "slock/dm-list.js", - "sourceFile": "slock/dm-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "inbox", - "description": "List unified inbox items (channels, DMs, followed threads) that need attention.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "filter", - "type": "str", - "default": "all", - "required": false, - "help": "all | unread | mentions" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max items (server caps at 100)" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Pagination offset" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "kind", - "id", - "name", - "unreadCount", - "hasMention", - "lastActivityAt", - "preview" - ], - "type": "js", - "modulePath": "slock/inbox.js", - "sourceFile": "slock/inbox.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "inbox-done", - "description": "Mark one chat as done / clear it from the inbox (POST /channels/inbox/done)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "result" - ], - "type": "js", - "modulePath": "slock/inbox-done.js", - "sourceFile": "slock/inbox-done.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "inbox-read-all", - "description": "Mark the entire inbox as read (POST /channels/inbox/read-all)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "result", - "markedCount" - ], - "type": "js", - "modulePath": "slock/inbox-read-all.js", - "sourceFile": "slock/inbox-read-all.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "login", - "description": "Open slock login", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "id", - "name", - "email", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "slock/whoami.js", - "sourceFile": "slock/whoami.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "message-read", - "description": "Read messages in a channel or thread. Thread form: \"#channel:msgIdOrShort\". Use --after seq|UUID for cursor.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID, \"#name\", or \"#channel:msgIdOrShort\"" - }, - { - "name": "after", - "type": "str", - "required": false, - "help": "Cursor: seq number or messageId UUID (exclusive)" - }, - { - "name": "before", - "type": "str", - "required": false, - "help": "seq to page before" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max messages" - }, - { - "name": "no-threads", - "type": "bool", - "default": false, - "required": false, - "help": "Skip /threads enrichment" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "seq", - "createdAt", - "senderName", - "content", - "threadChannelId", - "replyCount", - "unreadCount", - "lastReplyAt" - ], - "type": "js", - "modulePath": "slock/message-read.js", - "sourceFile": "slock/message-read.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "message-search", - "description": "Search messages", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Restrict to a channel (UUID or #name)" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max results" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "channelId", - "createdAt", - "senderName", - "content" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "slock/message-search.js", - "sourceFile": "slock/message-search.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "message-send", - "description": "Send a message to a channel, DM, or thread (content sent verbatim)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "target", - "type": "str", - "required": true, - "positional": true, - "help": "\"#channel\", \"#channel:msgIdOrShort\", \"dm:@name\", \"dm:\", or channel UUID" - }, - { - "name": "content", - "type": "str", - "required": true, - "positional": true, - "help": "Message body (sent verbatim, no marker)" - }, - { - "name": "dry-run", - "type": "bool", - "default": false, - "required": false, - "help": "Print the planned payload without sending" - }, - { - "name": "as-task", - "type": "bool", - "default": false, - "required": false, - "help": "Create the message as a task (asTask)" - }, - { - "name": "attach", - "type": "str", - "required": false, - "help": "Comma-separated attachmentId UUIDs (upload separately first)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id)" - } - ], - "columns": [ - "target", - "channelId", - "content", - "result", - "messageId" - ], - "type": "js", - "modulePath": "slock/message-send.js", - "sourceFile": "slock/message-send.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "reaction-add", - "description": "Add an emoji reaction to a message (POST /messages/:id/reactions). Idempotent server-side.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full messageId UUID (short ids rejected)" - }, - { - "name": "emoji", - "type": "str", - "required": true, - "positional": true, - "help": "A single unicode emoji, e.g. 👍" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "messageId", - "emoji", - "result" - ], - "type": "js", - "modulePath": "slock/reaction-add.js", - "sourceFile": "slock/reaction-add.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "reaction-remove", - "description": "Remove your emoji reaction from a message (DELETE /messages/:id/reactions).", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full messageId UUID (short ids rejected)" - }, - { - "name": "emoji", - "type": "str", - "required": true, - "positional": true, - "help": "The unicode emoji to remove, e.g. 👍" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "messageId", - "emoji", - "result" - ], - "type": "js", - "modulePath": "slock/reaction-remove.js", - "sourceFile": "slock/reaction-remove.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "server-list", - "description": "List slock servers you belong to; marks active per localStorage slug", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", - "slug", - "name", - "active" - ], - "type": "js", - "modulePath": "slock/server-list.js", - "sourceFile": "slock/server-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "server-use", - "description": "Set the active slock server (writes localStorage.slock_last_server_slug)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "server slug, \"#slug\", or UUID id" - } - ], - "columns": [ - "id", - "slug", - "name", - "written" - ], - "type": "js", - "modulePath": "slock/server-use.js", - "sourceFile": "slock/server-use.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-claim", - "description": "Claim a chat task (PATCH /tasks/:id/claim). Requires full task UUID (= message id).", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "taskId", - "type": "str", - "required": true, - "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "taskId", - "taskStatus", - "assigneeId", - "taskNumber" - ], - "type": "js", - "modulePath": "slock/task-claim.js", - "sourceFile": "slock/task-claim.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-convert", - "description": "Convert a message into a chat task (POST /tasks/convert-message). Accepts a message UUID or \"#channel:shortId\".", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full message UUID, or \"#channel:shortId\" (short id expanded via /messages/context)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "channelId" - ], - "type": "js", - "modulePath": "slock/task-convert.js", - "sourceFile": "slock/task-convert.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-create", - "description": "Create a task in a channel (single title; batch 1-50 is server-supported but client surface is single — see backlog R4).", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "title", - "type": "str", - "required": true, - "positional": true, - "help": "Task title (single; batch TODO via R4)" - }, - { - "name": "desc", - "type": "str", - "required": false, - "help": "Optional description body for the task" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "channelId" - ], - "type": "js", - "modulePath": "slock/task-create.js", - "sourceFile": "slock/task-create.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-delete", - "description": "Delete a chat task (DELETE /tasks/:taskId). Requires --confirm — destructive, irreversible.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "taskId", - "type": "str", - "required": true, - "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" - }, - { - "name": "confirm", - "type": "bool", - "default": false, - "required": false, - "help": "Required acknowledgement: deletion is irreversible" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "taskId", - "deleted" - ], - "type": "js", - "modulePath": "slock/task-delete.js", - "sourceFile": "slock/task-delete.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-get", - "description": "Fetch a task by channel + taskNumber (GET /tasks/channel/:channelId/number/:taskNumber).", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "number", - "type": "str", - "required": true, - "positional": true, - "help": "taskNumber (per-channel integer, as shown in \"task #N\")" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "assigneeId" - ], - "type": "js", - "modulePath": "slock/task-get.js", - "sourceFile": "slock/task-get.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-list", - "description": "List tasks (chat tasks = messages with task fields) attached to a channel. Optional --status filter.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "status", - "type": "str", - "required": false, - "help": "Filter by status: todo|in_progress|in_review|done|closed" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "assigneeId" - ], - "type": "js", - "modulePath": "slock/task-list.js", - "sourceFile": "slock/task-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-list-server", - "description": "List tasks across all channels in the active server (GET /tasks/server). Optional --status filter.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "status", - "type": "str", - "required": false, - "help": "Filter by status: todo|in_progress|in_review|done|closed" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "channelId", - "assigneeId" - ], - "type": "js", - "modulePath": "slock/task-list-server.js", - "sourceFile": "slock/task-list-server.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-status", - "description": "Set a task's status (PATCH /tasks/:taskId/status, body {status}). One of todo|in_progress|in_review|done|closed.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "taskId", - "type": "str", - "required": true, - "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" - }, - { - "name": "status", - "type": "str", - "required": true, - "positional": true, - "help": "One of: todo|in_progress|in_review|done|closed" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "taskId", - "taskStatus", - "assigneeId", - "taskNumber" - ], - "type": "js", - "modulePath": "slock/task-status.js", - "sourceFile": "slock/task-status.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-unclaim", - "description": "Release ownership of a chat task (PATCH /tasks/:id/unclaim).", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "taskId", - "type": "str", - "required": true, - "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "taskId", - "taskStatus", - "assigneeId", - "taskNumber" - ], - "type": "js", - "modulePath": "slock/task-unclaim.js", - "sourceFile": "slock/task-unclaim.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "thread-done", - "description": "Mark a thread as done / hide it from the active list (POST /channels/threads/done)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "threadChannelId", - "type": "str", - "required": true, - "positional": true, - "help": "Thread channel UUID (from thread-list / message-read)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "threadChannelId", - "result" - ], - "type": "js", - "modulePath": "slock/thread-done.js", - "sourceFile": "slock/thread-done.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "thread-follow", - "description": "Follow the thread on a parent message (POST /channels/threads/follow)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "parentMessageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full parent messageId UUID (short ids rejected)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "parentMessageId", - "threadChannelId", - "result" - ], - "type": "js", - "modulePath": "slock/thread-follow.js", - "sourceFile": "slock/thread-follow.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "thread-list", - "description": "List followed threads in the active server (GET /channels/threads/followed)", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "threadChannelId", - "parentMessageId", - "parentChannelName", - "unreadCount", - "replyCount", - "lastReplyAt" - ], - "type": "js", - "modulePath": "slock/thread-list.js", - "sourceFile": "slock/thread-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "thread-undone", - "description": "Restore a done thread to the active list (POST /channels/threads/undone)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "threadChannelId", - "type": "str", - "required": true, - "positional": true, - "help": "Thread channel UUID (from thread-list / message-read)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "threadChannelId", - "result" - ], - "type": "js", - "modulePath": "slock/thread-undone.js", - "sourceFile": "slock/thread-undone.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "thread-unfollow", - "description": "Stop following a thread (POST /channels/threads/unfollow)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "threadChannelId", - "type": "str", - "required": true, - "positional": true, - "help": "Thread channel UUID (from thread-list / message-read)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "threadChannelId", - "result" - ], - "type": "js", - "modulePath": "slock/thread-unfollow.js", - "sourceFile": "slock/thread-unfollow.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "unread-summary", - "description": "Global unread counts across every server you belong to.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "serverId", - "slug", - "name", - "unreadCount" - ], - "type": "js", - "modulePath": "slock/unread-summary.js", - "sourceFile": "slock/unread-summary.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "whoami", - "description": "Show the current logged-in slock account", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "id", - "name", - "email" - ], - "type": "js", - "modulePath": "slock/whoami.js", - "sourceFile": "slock/whoami.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "tiktok", - "name": "comment", - "description": "Post a comment on a TikTok video", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok video URL (https://www.tiktok.com/@user/video/)" - }, - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Comment text (≤150 chars)" - } - ], - "columns": [ - "url", - "text", - "result" - ], - "type": "js", - "modulePath": "tiktok/comment.js", - "sourceFile": "tiktok/comment.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "creator-videos", - "description": "TikTok Studio creator content list (views/likes/comments/saves/shares)", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of creator videos to return (max 250)" - }, - { - "name": "cursor", - "type": "string", - "default": "0", - "required": false, - "help": "Non-negative TikTok Studio pagination cursor" - } - ], - "columns": [ - "video_id", - "title", - "date", - "views", - "likes", - "comments", - "saves", - "shares", - "url" - ], - "type": "js", - "modulePath": "tiktok/creator-videos.js", - "sourceFile": "tiktok/creator-videos.js", - "navigateBefore": "https://www.tiktok.com/tiktokstudio/content" - }, - { - "site": "tiktok", - "name": "explore", - "description": "Get trending TikTok videos from the recommend feed via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of videos to return (max 120)" - } - ], - "columns": [ - "index", - "id", - "author", - "url", - "cover", - "title", - "desc", - "plays", - "likes", - "comments", - "shares", - "createTime" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "tiktok/explore.js", - "sourceFile": "tiktok/explore.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "follow", - "description": "Follow a TikTok user by username", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok username (without @)" - } - ], - "columns": [ - "username", - "url", - "result" - ], - "type": "js", - "modulePath": "tiktok/follow.js", - "sourceFile": "tiktok/follow.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "following", - "description": "List accounts the logged-in user follows on TikTok via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of accounts (max 200)" - } - ], - "columns": [ - "index", - "username", - "name", - "secUid", - "verified", - "followers", - "following", - "url" - ], - "type": "js", - "modulePath": "tiktok/following.js", - "sourceFile": "tiktok/following.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "friends", - "description": "Get TikTok friend / who-to-follow suggestions via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of suggestions (max 100)" - } - ], - "columns": [ - "index", - "username", - "name", - "secUid", - "verified", - "followers", - "following", - "url" - ], - "type": "js", - "modulePath": "tiktok/friends.js", - "sourceFile": "tiktok/friends.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "like", - "description": "Like a TikTok video", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok video URL" - } - ], - "columns": [ - "status", - "likes", - "url" - ], - "type": "js", - "modulePath": "tiktok/like.js", - "sourceFile": "tiktok/like.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "live", - "description": "Browse TikTok live streams via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of streams (max 60)" - } - ], - "columns": [ - "index", - "streamer", - "name", - "title", - "viewers", - "likes", - "secUid", - "url" - ], - "type": "js", - "modulePath": "tiktok/live.js", - "sourceFile": "tiktok/live.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "login", - "description": "Open tiktok login", - "access": "write", - "domain": "tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "sec_uid", - "username", - "nickname", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "tiktok/auth.js", - "sourceFile": "tiktok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "tiktok", - "name": "notifications", - "description": "Read TikTok inbox notifications (likes, comments, mentions, followers) via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Number of notifications (max 100)" - }, - { - "name": "type", - "type": "str", - "default": "all", - "required": false, - "help": "Notification type", - "choices": [ - "all", - "likes", - "comments", - "mentions", - "followers" - ] - } - ], - "columns": [ - "index", - "id", - "from", - "text", - "createTime" - ], - "type": "js", - "modulePath": "tiktok/notifications.js", - "sourceFile": "tiktok/notifications.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "profile", - "description": "Get TikTok user profile info", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok username (without @)" - } - ], - "columns": [ - "username", - "name", - "followers", - "following", - "likes", - "videos", - "verified", - "bio" - ], - "type": "js", - "modulePath": "tiktok/profile.js", - "sourceFile": "tiktok/profile.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "save", - "description": "Add a TikTok video to Favorites", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok video URL" - } - ], - "columns": [ - "status", - "url" - ], - "type": "js", - "modulePath": "tiktok/save.js", - "sourceFile": "tiktok/save.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "search", - "description": "Search TikTok videos", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "desc", - "author", - "url", - "plays", - "likes", - "comments", - "shares" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "tiktok/search.js", - "sourceFile": "tiktok/search.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "unfollow", - "description": "Unfollow a TikTok user by username", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok username (without @)" - } - ], - "columns": [ - "username", - "url", - "result" - ], - "type": "js", - "modulePath": "tiktok/unfollow.js", - "sourceFile": "tiktok/unfollow.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "unlike", - "description": "Unlike a TikTok video", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok video URL" - } - ], - "columns": [ - "status", - "likes", - "url" - ], - "type": "js", - "modulePath": "tiktok/unlike.js", - "sourceFile": "tiktok/unlike.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "unsave", - "description": "Remove a TikTok video from Favorites", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok video URL" - } - ], - "columns": [ - "status", - "url" - ], - "type": "js", - "modulePath": "tiktok/unsave.js", - "sourceFile": "tiktok/unsave.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "user", - "description": "Get recent videos from a TikTok user via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok username (without @)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of videos to return (max 120)" - } - ], - "columns": [ - "index", - "id", - "source", - "author", - "url", - "cover", - "title", - "desc", - "plays", - "likes", - "comments", - "shares", - "createTime" - ], - "type": "js", - "modulePath": "tiktok/user.js", - "sourceFile": "tiktok/user.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "whoami", - "description": "Show the current logged-in tiktok account", - "access": "read", - "domain": "tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "sec_uid", - "username", - "nickname" - ], - "type": "js", - "modulePath": "tiktok/auth.js", - "sourceFile": "tiktok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "trip", - "name": "attraction", - "description": "Search Trip.com attractions and experiences by destination keyword", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Destination or attraction keyword (e.g. Tokyo / Paris / Louvre)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results (1-50)" - } - ], - "columns": [ - "rank", - "name", - "rating", - "reviews", - "booked", - "price", - "currency", - "url" - ], - "type": "js", - "modulePath": "trip/attraction.js", - "sourceFile": "trip/attraction.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "car", - "description": "List Trip.com car-rental vehicles for a city (category, model, seats, daily price)", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "city", - "type": "str", - "required": true, - "positional": true, - "help": "Numeric Trip.com carhire city id (discover via the carhire search box)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of vehicles (1-50)" - } - ], - "columns": [ - "rank", - "category", - "vehicle", - "seats", - "price", - "currency", - "url" - ], - "type": "js", - "modulePath": "trip/car.js", - "sourceFile": "trip/car.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "deals", - "description": "List Trip.com live promotions from the Top Deals hub: campaign title, offer, discount, and link", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of deals (1-50)" - } - ], - "columns": [ - "rank", - "title", - "offer", - "discount", - "url" - ], - "type": "js", - "modulePath": "trip/deals.js", - "sourceFile": "trip/deals.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "flight", - "description": "Search Trip.com one-way flights by IATA route + departure date", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "from", - "type": "str", - "required": true, - "positional": true, - "help": "Departure IATA code (e.g. LON / LHR)" - }, - { - "name": "to", - "type": "str", - "required": true, - "positional": true, - "help": "Arrival IATA code (e.g. NYC / JFK)" - }, - { - "name": "date", - "type": "str", - "required": true, - "help": "Departure date (YYYY-MM-DD)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of flights (1-50)" - } - ], - "columns": [ - "rank", - "airline", - "departureTime", - "departureAirport", - "arrivalTime", - "arrivalAirport", - "duration", - "stops", - "price", - "currency", - "url" - ], - "type": "js", - "modulePath": "trip/flight.js", - "sourceFile": "trip/flight.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "flight-round", - "description": "Search Trip.com round-trip flights by IATA route + depart/return dates", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "from", - "type": "str", - "required": true, - "positional": true, - "help": "Departure IATA code (e.g. LON / LHR)" - }, - { - "name": "to", - "type": "str", - "required": true, - "positional": true, - "help": "Arrival IATA code (e.g. NYC / JFK)" - }, - { - "name": "depart", - "type": "str", - "required": true, - "help": "Outbound date (YYYY-MM-DD)" - }, - { - "name": "return", - "type": "str", - "required": true, - "help": "Return date (YYYY-MM-DD)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of flights (1-50)" - } - ], - "columns": [ - "rank", - "airline", - "departureTime", - "departureAirport", - "arrivalTime", - "arrivalAirport", - "duration", - "stops", - "price", - "currency", - "url" - ], - "type": "js", - "modulePath": "trip/flight-round.js", - "sourceFile": "trip/flight-round.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "hotel", - "description": "Show a Trip.com hotel detail by id (rating breakdown, amenities, check-in/out policy)", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Numeric Trip.com hotel id (discover via the hotels list; e.g. 715233)" - } - ], - "columns": [ - "hotelId", - "name", - "enName", - "star", - "score", - "scoreLabel", - "reviewCount", - "ratingBreakdown", - "facilities", - "checkInOut", - "cityName", - "address", - "lat", - "lon", - "url" - ], - "type": "js", - "modulePath": "trip/hotel.js", - "sourceFile": "trip/hotel.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "hotel-search", - "description": "List Trip.com hotels for a city id + check-in/out date range", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "city", - "type": "str", - "required": true, - "positional": true, - "help": "Numeric Trip.com city id (discover via the hotels search box; e.g. 338 for London)" - }, - { - "name": "checkin", - "type": "str", - "required": true, - "help": "Check-in date (YYYY-MM-DD)" - }, - { - "name": "checkout", - "type": "str", - "required": true, - "help": "Check-out date (YYYY-MM-DD)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of hotels (1-50)" - } - ], - "columns": [ - "rank", - "name", - "score", - "reviewLabel", - "reviews", - "location", - "room", - "price", - "currency", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "trip/hotel-search.js", - "sourceFile": "trip/hotel-search.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "package", - "description": "Search Trip.com flight+hotel packages by route + dates; lists the package flight options priced at the bundle rate", - "access": "read", - "domain": "trip.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "from", - "type": "str", - "required": true, - "positional": true, - "help": "Origin city keyword (e.g. Seoul / London / Bangkok)" - }, - { - "name": "to", - "type": "str", - "required": true, - "positional": true, - "help": "Destination city keyword (e.g. Tokyo / Paris / Singapore)" - }, - { - "name": "depart", - "type": "str", - "required": true, - "help": "Outbound date (YYYY-MM-DD)" - }, - { - "name": "return", - "type": "str", - "required": true, - "help": "Return date (YYYY-MM-DD)" - }, - { - "name": "adults", - "type": "int", - "default": 2, - "required": false, - "help": "Number of adults (1-9, default 2)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of packages (1-50)" - } - ], - "columns": [ - "rank", - "airline", - "flightNo", - "from", - "to", - "departure", - "arrival", - "stops", - "price", - "currency" - ], - "type": "js", - "modulePath": "trip/package.js", - "sourceFile": "trip/package.js" - }, - { - "site": "trip", - "name": "search", - "description": "Suggest Trip.com destinations (cities, airports) for a keyword; resolves the ids the other commands take", - "access": "read", - "domain": "trip.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Destination keyword (e.g. Tokyo / Bali / London)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of suggestions (1-50)" - } - ], - "columns": [ - "rank", - "name", - "type", - "cityId", - "airportCode", - "province", - "country" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "trip/search.js", - "sourceFile": "trip/search.js" - }, - { - "site": "trip", - "name": "tour", - "description": "Search Trip.com tour packages by destination keyword (private or group tours)", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Destination or tour keyword (e.g. Tokyo / Kyoto / Bali)" - }, - { - "name": "type", - "type": "str", - "default": "private", - "required": false, - "help": "Tour line: private or group (default private)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of tours (1-50)" - } - ], - "columns": [ - "rank", - "name", - "type", - "rating", - "reviews", - "price", - "currency", - "url" - ], - "type": "js", - "modulePath": "trip/tour.js", - "sourceFile": "trip/tour.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "train", - "description": "Show a Trip.com train route timetable (departure/arrival times, duration, changes)", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "from", - "type": "str", - "required": true, - "positional": true, - "help": "Departure city (e.g. London / Paris / Shanghai)" - }, - { - "name": "to", - "type": "str", - "required": true, - "positional": true, - "help": "Arrival city (e.g. Manchester / Lyon / Beijing)" - }, - { - "name": "country", - "type": "str", - "required": true, - "help": "Route country slug (e.g. uk / france / italy / spain / germany / china)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of journeys (1-50)" - } - ], - "columns": [ - "rank", - "departureTime", - "fromStation", - "arrivalTime", - "toStation", - "duration", - "changes", - "url" - ], - "type": "js", - "modulePath": "trip/train.js", - "sourceFile": "trip/train.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "transfer", - "description": "List Trip.com airport-transfer vehicles for a city + airport (type, seats, from-price)", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "city", - "type": "str", - "required": true, - "positional": true, - "help": "Airport city (e.g. Bangkok / Beijing / Da Nang)" - }, - { - "name": "airport", - "type": "str", - "required": true, - "positional": true, - "help": "3-letter airport IATA code (e.g. DMK / PKX / DAD)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of vehicles (1-50)" - } - ], - "columns": [ - "rank", - "type", - "passengers", - "luggage", - "price", - "currency", - "url" - ], - "type": "js", - "modulePath": "trip/transfer.js", - "sourceFile": "trip/transfer.js", - "navigateBefore": false - }, - { - "site": "twitter", - "name": "accept", - "description": "Auto-accept DM requests containing specific keywords", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "query", - "type": "string", - "required": true, - "positional": true, - "help": "Keywords to match (comma-separated for OR, e.g. \"invoice,urgent\")" - }, - { - "name": "max", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of requests to accept (default: 20)" - }, - { - "name": "timeout", - "type": "int", - "default": 600, - "required": false, - "help": "Max seconds for the overall command (default: 600 — batch op)" - } - ], - "columns": [ - "index", - "status", - "user", - "message" - ], - "type": "js", - "modulePath": "twitter/accept.js", - "sourceFile": "twitter/accept.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "article", - "description": "Fetch a Twitter Article (long-form content) and export as Markdown", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "tweet-id", - "type": "string", - "required": true, - "positional": true, - "help": "Tweet ID or URL containing the article" - } - ], - "columns": [ - "title", - "author", - "content", - "url" - ], - "type": "js", - "modulePath": "twitter/article.js", - "sourceFile": "twitter/article.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "block", - "description": "Block a Twitter user", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Twitter screen name (without @)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/block.js", - "sourceFile": "twitter/block.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "bookmark", - "description": "Bookmark a tweet", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "Tweet URL to bookmark" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/bookmark.js", - "sourceFile": "twitter/bookmark.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "bookmark-folder", - "description": "Read the tweets inside a single Twitter/X bookmark folder. Get the folder id from `webcmd twitter bookmark-folders`.", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "folder-id", - "type": "string", - "required": true, - "positional": true, - "help": "Folder id from `webcmd twitter bookmark-folders`." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of bookmarks to return (default 20)." - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the folder by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (saved-time) ordering." - } - ], - "columns": [ - "id", - "author", - "text", - "likes", - "retweets", - "bookmarks", - "created_at", - "url", - "has_media", - "media_urls", - "media_posters" - ], - "type": "js", - "modulePath": "twitter/bookmark-folder.js", - "sourceFile": "twitter/bookmark-folder.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "bookmark-folders", - "description": "List your Twitter/X bookmark folders (the user-created collections under Bookmarks). Returns folder id, name, item count, and created_at.", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", - "name", - "items", - "created_at" - ], - "type": "js", - "modulePath": "twitter/bookmark-folders.js", - "sourceFile": "twitter/bookmark-folders.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "bookmarks", - "description": "Fetch your Twitter/X bookmarks (the logged-in user's saved tweets, newest first)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of bookmarks to return (default 20)." - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the bookmarks by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (saved-time) ordering." - } - ], - "columns": [ - "id", - "author", - "text", - "likes", - "retweets", - "bookmarks", - "created_at", - "url", - "has_media", - "media_urls", - "media_posters" - ], - "type": "js", - "modulePath": "twitter/bookmarks.js", - "sourceFile": "twitter/bookmarks.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "delete", - "description": "Delete a specific tweet by URL", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to delete" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/delete.js", - "sourceFile": "twitter/delete.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "device-follow", - "description": "Read the /i/timeline device-follow notification stream (tweets aggregated under a bell-icon \"new posts from @userA and N others\" notification)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of tweets to return (1-200, default 20)" - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank by weighted engagement and return the top N. Default 0 keeps upstream ordering." - } - ], - "columns": [ - "id", - "author", - "text", - "likes", - "retweets", - "replies", - "views", - "created_at", - "url" - ], - "type": "js", - "modulePath": "twitter/device-follow.js", - "sourceFile": "twitter/device-follow.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "download", - "description": "Download Twitter/X media (images and videos). Provide either to fetch every media item from their profile via the GraphQL UserMedia endpoint with cursor pagination, or --tweet-url to download a single tweet.", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": false, - "positional": true, - "help": "Twitter username (with or without @) to scan their profile media. Either or --tweet-url is required." - }, - { - "name": "tweet-url", - "type": "str", - "required": false, - "help": "Single tweet URL to download. Use this OR , not both required at once." - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum number of media items to download when scanning a profile (default 10). Ignored when --tweet-url is used." - }, - { - "name": "output", - "type": "str", - "default": "./twitter-downloads", - "required": false, - "help": "Output directory (default ./twitter-downloads). A per-source subdir is created inside.", - "file": { - "direction": "output", - "pathKind": "directory", - "multiple": false - } - } - ], - "columns": [ - "index", - "tweet_id", - "url", - "type", - "status", - "size" - ], - "type": "js", - "modulePath": "twitter/download.js", - "sourceFile": "twitter/download.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "follow", - "description": "Follow a Twitter user", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Twitter screen name (without @)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/follow.js", - "sourceFile": "twitter/follow.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "follow-batch", - "description": "Follow multiple Twitter/X users from a comma-separated username list", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "usernames", - "type": "string", - "required": true, - "positional": true, - "help": "Comma-separated Twitter/X screen names, with or without @" - }, - { - "name": "delay-ms", - "type": "int", - "default": 3000, - "required": false, - "help": "Delay between follow attempts in milliseconds" - } - ], - "columns": [ - "username", - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/follow-batch.js", - "sourceFile": "twitter/follow-batch.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "followers", - "description": "Get accounts following a Twitter/X user (defaults to the logged-in user when no user is given)", - "access": "read", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "user", - "type": "string", - "required": false, - "positional": true, - "help": "Twitter/X handle (with or without @). Omit to fetch followers of the currently logged-in account." - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Maximum number of follower rows to return (default 50). Must be a positive integer." - } - ], - "columns": [ - "screen_name", - "name", - "bio" - ], - "type": "js", - "modulePath": "twitter/followers.js", - "sourceFile": "twitter/followers.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "following", - "description": "Get accounts a Twitter/X user is following (defaults to the logged-in user when no user is given)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "user", - "type": "string", - "required": false, - "positional": true, - "help": "Twitter/X handle (with or without @). Omit to fetch the accounts the currently logged-in user follows." - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Maximum number of following rows to return (default 50). Must be a positive integer." - } - ], - "columns": [ - "screen_name", - "name", - "bio", - "followers" - ], - "type": "js", - "modulePath": "twitter/following.js", - "sourceFile": "twitter/following.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "hide-reply", - "description": "Hide a reply on your tweet (useful for hiding bot/spam replies)", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the reply tweet to hide" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/hide-reply.js", - "sourceFile": "twitter/hide-reply.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "like", - "description": "Like a specific tweet", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to like" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/like.js", - "sourceFile": "twitter/like.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "likes", - "description": "Fetch liked tweets of a Twitter user (defaults to the logged-in user when no username is given)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": false, - "positional": true, - "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of liked tweets to return (default 20)." - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the liked tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (recency) ordering." - } - ], - "columns": [ - "id", - "author", - "name", - "text", - "likes", - "retweets", - "created_at", - "url", - "has_media", - "media_urls", - "media_posters" - ], - "type": "js", - "modulePath": "twitter/likes.js", - "sourceFile": "twitter/likes.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "list-add", - "description": "Add a user to a Twitter/X list you own (no-op if already a member)", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" - }, - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Twitter/X handle to add (with or without @)" - } - ], - "columns": [ - "listId", - "username", - "userId", - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/list-add.js", - "sourceFile": "twitter/list-add.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "list-add-batch", - "description": "Add multiple users to a Twitter/X list you own from a comma-separated username list", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" - }, - { - "name": "usernames", - "type": "string", - "required": true, - "positional": true, - "help": "Comma-separated Twitter/X handles to add (with or without @)" - }, - { - "name": "interval", - "type": "int", - "default": 5, - "required": false, - "help": "Seconds to wait between account additions (default: 5)" - }, - { - "name": "timeout", - "type": "int", - "default": 600, - "required": false, - "help": "Max seconds for the overall batch command (default: 600)" - } - ], - "columns": [ - "listId", - "username", - "userId", - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/list-add-batch.js", - "sourceFile": "twitter/list-add-batch.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "list-create", - "description": "Create a new Twitter/X list (returns the new list id)", - "access": "write", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "name", - "type": "string", - "required": true, - "positional": true, - "help": "List name (max 25 chars)" - }, - { - "name": "description", - "type": "string", - "default": "", - "required": false, - "help": "Optional list description (max 100 chars)" - }, - { - "name": "mode", - "type": "string", - "default": "public", - "required": false, - "help": "public | private" - } - ], - "columns": [ - "id", - "name", - "description", - "mode", - "status" - ], - "type": "js", - "modulePath": "twitter/list-create.js", - "sourceFile": "twitter/list-create.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "list-delete", - "description": "Delete a Twitter/X list you own after explicit confirmation", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" - }, - { - "name": "confirm", - "type": "boolean", - "default": false, - "required": false, - "help": "Required. Set --confirm true to delete the list." - }, - { - "name": "timeout", - "type": "int", - "default": 300, - "required": false, - "help": "Max seconds for the overall delete command (default: 300)" - } - ], - "columns": [ - "listId", - "name", - "members", - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/list-delete.js", - "sourceFile": "twitter/list-delete.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "list-remove", - "description": "Remove a user from a Twitter/X list you own (toggles via UI; no-op if not currently a member)", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" - }, - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Twitter/X handle to remove (with or without @)" - } - ], - "columns": [ - "listId", - "username", - "userId", - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/list-remove.js", - "sourceFile": "twitter/list-remove.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "list-remove-batch", - "description": "Remove multiple users from a Twitter/X list you own from a comma-separated username list", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" - }, - { - "name": "usernames", - "type": "string", - "required": true, - "positional": true, - "help": "Comma-separated Twitter/X handles to remove (with or without @)" - }, - { - "name": "interval", - "type": "int", - "default": 5, - "required": false, - "help": "Seconds to wait between account removals (default: 5)" - }, - { - "name": "timeout", - "type": "int", - "default": 600, - "required": false, - "help": "Max seconds for the overall batch command (default: 600)" - } - ], - "columns": [ - "listId", - "username", - "userId", - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/list-remove-batch.js", - "sourceFile": "twitter/list-remove-batch.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "list-tweets", - "description": "Fetch tweets from a Twitter/X list timeline", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of a Twitter/X list (e.g. from `webcmd twitter lists`)" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "" - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the list timeline by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the list's native (recency) ordering." - } - ], - "columns": [ - "id", - "author", - "bio", - "text", - "likes", - "retweets", - "replies", - "created_at", - "url", - "has_media", - "media_urls", - "media_posters", - "card", - "quoted_tweet" - ], - "type": "js", - "modulePath": "twitter/list-tweets.js", - "sourceFile": "twitter/list-tweets.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "lists", - "description": "Get Twitter/X lists for the logged-in user (owned + subscribed)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Maximum number of lists to return (default 50)." - } - ], - "columns": [ - "id", - "name", - "members", - "followers", - "mode" - ], - "type": "js", - "modulePath": "twitter/lists.js", - "sourceFile": "twitter/lists.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "login", - "description": "Open twitter login", - "access": "write", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "username", - "url", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "twitter/auth.js", - "sourceFile": "twitter/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "twitter", - "name": "notifications", - "description": "Get your Twitter/X notifications (the logged-in user's likes/replies/follows feed, newest first)", - "access": "read", - "domain": "x.com", - "strategy": "intercept", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of notifications to return (default 20)." - } - ], - "columns": [ - "id", - "action", - "author", - "text", - "url" - ], - "type": "js", - "modulePath": "twitter/notifications.js", - "sourceFile": "twitter/notifications.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "post", - "description": "Post a new tweet/thread", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "The text content of the tweet" - }, - { - "name": "images", - "type": "string", - "required": false, - "help": "Image paths, comma-separated, max 4 (jpg/png/gif/webp)", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - } - ], - "columns": [ - "status", - "message", - "text", - "id", - "url" - ], - "type": "js", - "modulePath": "twitter/post.js", - "sourceFile": "twitter/post.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "profile", - "description": "Fetch a Twitter user profile — bio, stats, etc. (defaults to the logged-in user when no username is given)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": false, - "positional": true, - "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." - } - ], - "columns": [ - "screen_name", - "name", - "bio", - "location", - "url", - "followers", - "following", - "tweets", - "likes", - "verified", - "created_at" - ], - "type": "js", - "modulePath": "twitter/profile.js", - "sourceFile": "twitter/profile.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "quote", - "description": "Quote-tweet a specific tweet with your own text, optionally with a local or remote image", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to quote" - }, - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "The text content of your quote" - }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Optional local image path to attach to the quote tweet", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - }, - { - "name": "image-url", - "type": "str", - "required": false, - "help": "Optional remote image URL to download and attach to the quote tweet" - } - ], - "columns": [ - "status", - "message", - "text" - ], - "type": "js", - "modulePath": "twitter/quote.js", - "sourceFile": "twitter/quote.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "reply", - "description": "Reply to a specific tweet, optionally with a local or remote image", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to reply to" - }, - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "The text content of your reply" - }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Optional local image path to attach to the reply", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - }, - { - "name": "image-url", - "type": "str", - "required": false, - "help": "Optional remote image URL to download and attach to the reply" - } - ], - "columns": [ - "status", - "message", - "text", - "url" - ], - "type": "js", - "modulePath": "twitter/reply.js", - "sourceFile": "twitter/reply.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "reply-dm", - "description": "Send a message to recent DM conversations", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "Message text to send (e.g. \"my messaging handle wxkabi\")" - }, - { - "name": "max", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of conversations to reply to (default: 20)" - }, - { - "name": "skip-replied", - "type": "boolean", - "default": true, - "required": false, - "help": "Skip conversations where you already sent the same text (default: true)" - }, - { - "name": "timeout", - "type": "int", - "default": 600, - "required": false, - "help": "Max seconds for the overall command (default: 600 — batch op)" - } - ], - "columns": [ - "index", - "status", - "user", - "message" - ], - "type": "js", - "modulePath": "twitter/reply-dm.js", - "sourceFile": "twitter/reply-dm.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "retweet", - "description": "Retweet a specific tweet", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to retweet" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/retweet.js", - "sourceFile": "twitter/retweet.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "search", - "description": "Search Twitter/X for tweets, with optional --from / --has / --exclude / --product filters mapped to X's search operators", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "string", - "required": true, - "positional": true, - "help": "Search query. Raw X operators (e.g. \"exact phrase\", #tag, OR, lang:en, since:YYYY-MM-DD, from:, since:) are passed through unchanged." - }, - { - "name": "filter", - "type": "string", - "default": "top", - "required": false, - "help": "Legacy alias for --product. Kept for backwards compatibility; if --product is set it wins.", - "choices": [ - "top", - "live" - ] - }, - { - "name": "product", - "type": "string", - "required": false, - "help": "Which X search tab to read: top (default), live (Latest), photos, videos. Maps to the f= URL param.", - "choices": [ - "top", - "live", - "photos", - "videos" - ] - }, - { - "name": "from", - "type": "string", - "required": false, - "help": "Restrict to tweets authored by . Leading @ is stripped. Equivalent to appending `from:` to the query." - }, - { - "name": "has", - "type": "string", - "required": false, - "help": "Restrict to tweets that have media|images|videos|links|replies. Maps to X's `filter:` operator.", - "choices": [ - "media", - "images", - "videos", - "links", - "replies" - ] - }, - { - "name": "exclude", - "type": "string", - "required": false, - "help": "Exclude tweets matching : replies|retweets|media|links. Maps to X's `-filter:` operator (retweets → -filter:nativeretweets).", - "choices": [ - "replies", - "retweets", - "media", - "links" - ] - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Maximum number of tweets to return (default 15). Result count after server-side filtering." - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the results by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X's native ordering." - } - ], - "columns": [ - "id", - "author", - "bio", - "text", - "created_at", - "likes", - "views", - "url", - "has_media", - "media_urls", - "media_posters", - "card", - "quoted_tweet" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "twitter/search.js", - "sourceFile": "twitter/search.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "thread", - "description": "Get a tweet thread (original + all replies)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "tweet-id", - "type": "string", - "required": true, - "positional": true, - "help": "Tweet numeric ID (e.g. 1234567890) or full status URL" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "" - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the thread by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the conversation's structural ordering." - } - ], - "columns": [ - "id", - "author", - "bio", - "text", - "likes", - "retweets", - "url", - "has_media", - "media_urls", - "media_posters", - "card", - "quoted_tweet" - ], - "type": "js", - "modulePath": "twitter/thread.js", - "sourceFile": "twitter/thread.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "timeline", - "description": "Fetch the logged-in user's home timeline (for-you algorithmic feed by default; pass --type following for the chronological feed of accounts you follow)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "type", - "type": "str", - "default": "for-you", - "required": false, - "help": "Which home-timeline feed to read. Default for-you (algorithmic). Use following for the chronological feed of accounts you follow.", - "choices": [ - "for-you", - "following" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of tweets to return (default 20)." - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the timeline by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X's native ordering." - } - ], - "columns": [ - "id", - "author", - "bio", - "text", - "likes", - "retweets", - "replies", - "quotes", - "bookmarks", - "views", - "created_at", - "url", - "has_media", - "media_urls", - "media_posters", - "card", - "quoted_tweet" - ], - "type": "js", - "modulePath": "twitter/timeline.js", - "sourceFile": "twitter/timeline.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "trending", - "description": "Twitter/X trending topics", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of trends to show" - } - ], - "columns": [ - "rank", - "topic", - "category" - ], - "type": "js", - "modulePath": "twitter/trending.js", - "sourceFile": "twitter/trending.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "tweets", - "description": "Fetch a Twitter user's most recent tweets (chronological, excludes pinned; defaults to the logged-in user when no username is given)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": false, - "positional": true, - "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max tweets to return (1-10000; fetched across cursor pages)" - }, - { - "name": "page-delay", - "type": "int", - "default": 2, - "required": false, - "help": "Seconds to wait between paginated timeline requests to reduce rate-limit risk. Use 0 to disable." - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the chronological ordering." - } - ], - "columns": [ - "id", - "author", - "created_at", - "is_retweet", - "text", - "likes", - "retweets", - "replies", - "views", - "url", - "has_media", - "media_urls", - "media_posters", - "quoted_tweet" - ], - "type": "js", - "modulePath": "twitter/tweets.js", - "sourceFile": "twitter/tweets.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "unblock", - "description": "Unblock a Twitter user", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Twitter screen name (without @)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/unblock.js", - "sourceFile": "twitter/unblock.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "unbookmark", - "description": "Remove a tweet from bookmarks", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "Tweet URL to unbookmark" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/unbookmark.js", - "sourceFile": "twitter/unbookmark.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "unfollow", - "description": "Unfollow a Twitter user", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Twitter screen name (without @)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/unfollow.js", - "sourceFile": "twitter/unfollow.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "unlike", - "description": "Remove a like from a specific tweet", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to unlike" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/unlike.js", - "sourceFile": "twitter/unlike.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "unretweet", - "description": "Undo a retweet on a specific tweet", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to unretweet" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "twitter/unretweet.js", - "sourceFile": "twitter/unretweet.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "whoami", - "description": "Show the current logged-in twitter account", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "username", - "url" - ], - "type": "js", - "modulePath": "twitter/auth.js", - "sourceFile": "twitter/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "youtube", - "name": "channel", - "description": "Get YouTube channel info and recent videos", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Channel ID (UCxxxx) or handle (@name)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max recent videos (max 30)" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "youtube/channel.js", - "sourceFile": "youtube/channel.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "comments", - "description": "Get YouTube video comments", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "YouTube video URL or video ID" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max comments (max 100)" - } - ], - "columns": [ - "rank", - "author", - "text", - "likes", - "replies", - "time" - ], - "type": "js", - "modulePath": "youtube/comments.js", - "sourceFile": "youtube/comments.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "feed", - "description": "Get YouTube homepage recommended videos", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max videos to return (default 20, max 100)" - } - ], - "columns": [ - "rank", - "title", - "channel", - "video_id", - "views", - "duration", - "published", - "url" - ], - "type": "js", - "modulePath": "youtube/feed.js", - "sourceFile": "youtube/feed.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "history", - "description": "Get YouTube watch history", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max videos to return (default 30, max 200)" - } - ], - "columns": [ - "rank", - "title", - "channel", - "views", - "duration", - "url" - ], - "type": "js", - "modulePath": "youtube/history.js", - "sourceFile": "youtube/history.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "like", - "description": "Like a YouTube video", - "access": "write", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "YouTube video URL or video ID" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "youtube/like.js", - "sourceFile": "youtube/like.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "login", - "description": "Open youtube login", - "access": "write", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "youtube/auth.js", - "sourceFile": "youtube/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "youtube", - "name": "playlist", - "description": "Get YouTube playlist info and video list", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Playlist URL or playlist ID (PLxxxxxx)" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max videos to return (default 50, max 200)" - } - ], - "columns": [ - "rank", - "title", - "channel", - "duration", - "views", - "published", - "url" - ], - "type": "js", - "modulePath": "youtube/playlist.js", - "sourceFile": "youtube/playlist.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "search", - "description": "Search YouTube videos", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (max 50)" - }, - { - "name": "type", - "type": "str", - "default": "", - "required": false, - "help": "Filter type: shorts, video, channel, playlist" - }, - { - "name": "upload", - "type": "str", - "default": "", - "required": false, - "help": "Upload date: hour, today, week, month, year" - }, - { - "name": "sort", - "type": "str", - "default": "", - "required": false, - "help": "Sort by: relevance, date, views, rating" - } - ], - "columns": [ - "rank", - "title", - "channel", - "views", - "duration", - "published", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "youtube/search.js", - "sourceFile": "youtube/search.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "subscribe", - "description": "Subscribe to a YouTube channel", - "access": "write", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "Channel ID (UCxxxx) or handle (@name)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "youtube/subscribe.js", - "sourceFile": "youtube/subscribe.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "subscriptions", - "description": "List subscribed YouTube channels", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max channels to return (default 50)" - } - ], - "columns": [ - "rank", - "name", - "handle", - "subscribers", - "url" - ], - "type": "js", - "modulePath": "youtube/subscriptions.js", - "sourceFile": "youtube/subscriptions.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "transcript", - "description": "Get YouTube video transcript/subtitles", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "YouTube video URL or video ID" - }, - { - "name": "lang", - "type": "str", - "required": false, - "help": "Language code (e.g. en, zh-Hans). Omit to auto-select" - }, - { - "name": "mode", - "type": "str", - "default": "grouped", - "required": false, - "help": "Output mode: grouped (readable paragraphs) or raw (every segment)" - } - ], - "type": "js", - "modulePath": "youtube/transcript.js", - "sourceFile": "youtube/transcript.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "unlike", - "description": "Remove like from a YouTube video", - "access": "write", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "YouTube video URL or video ID" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "youtube/unlike.js", - "sourceFile": "youtube/unlike.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "unsubscribe", - "description": "Unsubscribe from a YouTube channel", - "access": "write", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "Channel ID (UCxxxx) or handle (@name)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "youtube/unsubscribe.js", - "sourceFile": "youtube/unsubscribe.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "video", - "description": "Get YouTube video metadata (title, views, description, etc.)", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "YouTube video URL or video ID" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "youtube/video.js", - "sourceFile": "youtube/video.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "watch-later", - "description": "Get your YouTube Watch Later queue", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max videos to return (default 50, max 200)" - } - ], - "columns": [ - "rank", - "title", - "channel", - "duration", - "views", - "published", - "url" - ], - "type": "js", - "modulePath": "youtube/watch-later.js", - "sourceFile": "youtube/watch-later.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "whoami", - "description": "Show the current logged-in youtube account", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name" - ], - "type": "js", - "modulePath": "youtube/auth.js", - "sourceFile": "youtube/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - } -] +[] diff --git a/clis/_shared/common.js b/clis/_shared/common.js deleted file mode 100644 index ba22007c..00000000 --- a/clis/_shared/common.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Shared utilities for CLI adapters. - */ -import { ArgumentError } from '@agentrhq/webcmd/errors'; -/** - * Clamp a numeric value to [min, max]. - * Matches the signature of lodash.clamp and Rust's clamp. - */ -export function clamp(value, min, max) { - return Math.max(min, Math.min(value, max)); -} -export function clampInt(raw, fallback, min, max) { - const parsed = Number(raw); - if (!Number.isFinite(parsed)) { - return fallback; - } - return clamp(Math.floor(parsed), min, max); -} -export function normalizeNumericId(value, label, example) { - const normalized = String(value ?? '').trim(); - if (!/^\d+$/.test(normalized)) { - throw new ArgumentError(`${label} must be a numeric ID`, `Pass a numeric ${label}, for example: ${example}`); - } - return normalized; -} -export function requireNonEmptyQuery(value, label = 'query') { - const normalized = String(value ?? '').trim(); - if (!normalized) { - throw new ArgumentError(`${label} cannot be empty`); - } - return normalized; -} diff --git a/clis/_shared/desktop-commands.js b/clis/_shared/desktop-commands.js deleted file mode 100644 index 79092061..00000000 --- a/clis/_shared/desktop-commands.js +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Shared command factories for Electron/desktop app adapters. - * Eliminates duplicate screenshot/status/new/dump implementations - * across cursor, codex, chatwise, etc. - */ -import * as fs from 'node:fs'; -import { cli, Strategy } from '@agentrhq/webcmd/registry'; -/** - * Factory: capture DOM HTML + accessibility snapshot. - */ -export function makeScreenshotCommand(site, displayName, extra = {}) { - const label = displayName ?? site; - return cli({ - ...extra, - site, - name: 'screenshot', - access: 'read', - description: `Capture a snapshot of the current ${label} window (DOM + Accessibility tree)`, - domain: 'localhost', - strategy: Strategy.UI, - browser: true, - args: [ - { name: 'output', required: false, help: `Output file path (default: /tmp/${site}-snapshot.txt)` }, - ], - columns: ['Status', 'File'], - func: async (page, kwargs) => { - const outputPath = kwargs.output || `/tmp/${site}-snapshot.txt`; - const snap = await page.snapshot({ compact: true }); - const html = await page.evaluate('document.documentElement.outerHTML'); - const htmlPath = outputPath.replace(/\.\w+$/, '') + '-dom.html'; - const snapPath = outputPath.replace(/\.\w+$/, '') + '-a11y.txt'; - fs.writeFileSync(htmlPath, html); - fs.writeFileSync(snapPath, typeof snap === 'string' ? snap : JSON.stringify(snap, null, 2)); - return [ - { Status: 'Success', File: htmlPath }, - { Status: 'Success', File: snapPath }, - ]; - }, - }); -} -/** - * Factory: check CDP connection status. - */ -export function makeStatusCommand(site, displayName, extra = {}) { - const label = displayName ?? site; - return cli({ - ...extra, - site, - name: 'status', - access: 'read', - description: `Check active CDP connection to ${label}`, - domain: 'localhost', - strategy: Strategy.UI, - browser: true, - columns: ['Status', 'Url', 'Title'], - func: async (page) => { - const url = await page.evaluate('window.location.href'); - const title = await page.evaluate('document.title'); - return [{ Status: 'Connected', Url: url, Title: title }]; - }, - }); -} -/** - * Factory: start a new session via Cmd/Ctrl+N. - */ -export function makeNewCommand(site, displayName, extra = {}) { - const label = displayName ?? site; - return cli({ - ...extra, - site, - name: 'new', - access: 'write', - description: `Start a new ${label} session`, - domain: 'localhost', - strategy: Strategy.UI, - browser: true, - columns: ['Status'], - func: async (page) => { - const isMac = process.platform === 'darwin'; - await page.pressKey(isMac ? 'Meta+N' : 'Control+N'); - await page.wait(1); - return [{ Status: 'Success' }]; - }, - }); -} -/** - * Factory: dump DOM + snapshot for reverse-engineering. - */ -export function makeDumpCommand(site) { - return cli({ - site, - name: 'dump', - access: 'read', - description: `Dump the DOM and Accessibility tree of ${site} for reverse-engineering`, - domain: 'localhost', - strategy: Strategy.UI, - browser: true, - columns: ['action', 'files'], - func: async (page) => { - const dom = await page.evaluate('document.body.innerHTML'); - fs.writeFileSync(`/tmp/${site}-dom.html`, dom); - const snap = await page.snapshot({ interactive: false }); - fs.writeFileSync(`/tmp/${site}-snapshot.json`, JSON.stringify(snap, null, 2)); - return [ - { - action: 'Dom extraction finished', - files: `/tmp/${site}-dom.html, /tmp/${site}-snapshot.json`, - }, - ]; - }, - }); -} diff --git a/clis/_shared/search-adapter.js b/clis/_shared/search-adapter.js deleted file mode 100644 index e5f737e5..00000000 --- a/clis/_shared/search-adapter.js +++ /dev/null @@ -1,70 +0,0 @@ -import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; - -export function requireSearchQuery(value, label = 'keyword') { - const query = String(value ?? '').trim(); - if (!query) { - throw new ArgumentError(`${label} cannot be empty`); - } - return query; -} - -export function requireBoundedInteger(value, defaultValue, min, max, label) { - const raw = value ?? defaultValue; - const parsed = typeof raw === 'number' ? raw : Number(raw); - if (!Number.isInteger(parsed)) { - throw new ArgumentError(`${label} must be an integer between ${min} and ${max}, got ${JSON.stringify(value)}`); - } - if (parsed < min || parsed > max) { - throw new ArgumentError(`${label} must be between ${min} and ${max}, got ${parsed}`); - } - return parsed; -} - -export function requireNonNegativeInteger(value, defaultValue, label) { - const raw = value ?? defaultValue; - const parsed = typeof raw === 'number' ? raw : Number(raw); - if (!Number.isInteger(parsed) || parsed < 0) { - throw new ArgumentError(`${label} must be a non-negative integer, got ${JSON.stringify(value)}`); - } - return parsed; -} - -export function unwrapBrowserResult(value) { - if (value && typeof value === 'object' && !Array.isArray(value) && 'session' in value && 'data' in value) { - return value.data; - } - return value; -} - -export function requireRows(value, label) { - const rows = unwrapBrowserResult(value); - if (!Array.isArray(rows)) { - throw new CommandExecutionError(`${label} returned an unexpected payload shape; expected an array of result rows.`); - } - return rows; -} - -export function toHttpsUrl(value, baseUrl) { - const raw = String(value ?? '').trim(); - if (!raw) return ''; - try { - const url = new URL(raw, baseUrl); - if (url.protocol !== 'http:' && url.protocol !== 'https:') return ''; - return url.href; - } catch { - return ''; - } -} - -export function emptySearchResults(site, query) { - return new EmptyResultError(`${site} search`, `No ${site} results matched "${query}".`); -} - -export async function runBrowserStep(label, fn) { - try { - return await fn(); - } catch (error) { - if (error?.code || error?.name === 'ArgumentError') throw error; - throw new CommandExecutionError(`${label} failed: ${error?.message ?? error}`); - } -} diff --git a/clis/_shared/site-auth.js b/clis/_shared/site-auth.js deleted file mode 100644 index 8c3281b2..00000000 --- a/clis/_shared/site-auth.js +++ /dev/null @@ -1,119 +0,0 @@ -import { AuthRequiredError } from '@agentrhq/webcmd/errors'; -import { cli, Strategy } from '@agentrhq/webcmd/registry'; - -const LOGIN_ACTION = 'Complete sign-in in the opened Webcmd browser, then tell the agent when you are done.'; - -function normalizeIdentity(config, identity) { - const row = identity && typeof identity === 'object' && !Array.isArray(identity) - ? identity - : {}; - return { ...blankIdentity(config), ...row, logged_in: true, site: config.site }; -} - -function isAuthRequired(error) { - return error instanceof AuthRequiredError; -} - -async function tryProbe(config, page) { - return normalizeIdentity(config, await config.verify(page, { phase: 'identity' })); -} - -function identityColumns(config) { - return config.columns ?? ['id', 'username', 'name']; -} - -function blankIdentity(config) { - return Object.fromEntries(identityColumns(config).map((column) => [column, ''])); -} - -function commandColumns(config) { - return ['logged_in', 'site', ...identityColumns(config)]; -} - -function loginColumns(config) { - return ['status', ...commandColumns(config), 'action', 'verify_command']; -} - -function normalizeQuickCheck(result) { - if (typeof result === 'boolean') return { logged_in: result }; - if (result && typeof result === 'object' && !Array.isArray(result)) { - return { logged_in: !!result.logged_in, ...result }; - } - return { logged_in: false }; -} - -function normalizeRefreshResult(result) { - if (result && typeof result === 'object' && !Array.isArray(result)) return result; - return { touched: true }; -} - -export function registerSiteAuthCommands(config) { - if (!config?.site || !config?.domain || !config?.loginUrl || typeof config.verify !== 'function') { - throw new Error('registerSiteAuthCommands requires site, domain, loginUrl, and verify(page)'); - } - // Sites whose login is a modal/flow rather than a page can pass - // openLogin(page) to bring the login UI up; default is a plain navigation. - const openLogin = typeof config.openLogin === 'function' - ? config.openLogin - : async (page) => { await page.goto(config.loginUrl); }; - - cli({ - site: config.site, - name: 'whoami', - access: 'read', - description: config.whoamiDescription ?? `Show the current logged-in ${config.site} account`, - domain: config.domain, - strategy: Strategy.COOKIE, - browser: true, - navigateBefore: false, - siteSession: 'persistent', - aliases: config.whoamiAliases ?? [], - args: [], - columns: commandColumns(config), - authStatus: { - ...(typeof config.quickCheck === 'function' - ? { quickCheck: async (page) => normalizeQuickCheck(await config.quickCheck(page)) } - : {}), - ...(typeof config.refresh === 'function' - ? { refresh: async (page, kwargs) => normalizeRefreshResult(await config.refresh(page, kwargs)) } - : {}), - }, - func: async (page) => [await tryProbe(config, page)], - }); - - cli({ - site: config.site, - name: 'login', - access: 'write', - description: config.loginDescription ?? `Open ${config.site} login`, - domain: config.domain, - strategy: Strategy.COOKIE, - browser: true, - navigateBefore: false, - siteSession: 'persistent', - args: [], - columns: loginColumns(config), - func: async (page) => { - try { - return [{ - status: 'already_logged_in', - ...await tryProbe(config, page), - action: '', - verify_command: '', - }]; - } catch (error) { - if (!isAuthRequired(error)) throw error; - } - - await openLogin(page); - return [{ - status: 'action_required', - logged_in: false, - site: config.site, - ...blankIdentity(config), - action: LOGIN_ACTION, - verify_command: `webcmd ${config.site} whoami`, - }]; - }, - }); -} diff --git a/clis/test-utils.js b/clis/test-utils.js deleted file mode 100644 index 91bfc429..00000000 --- a/clis/test-utils.js +++ /dev/null @@ -1,61 +0,0 @@ -import { vi } from 'vitest'; - -/** - * Create a page mock with all standard browser automation methods. - * - * @param {any[]} evaluateResults - Sequential results for page.evaluate() calls - * @param {Record} [overrides] - Override or add mock methods - * @returns A mock page object compatible with Webcmd's browser page interface - */ -export function createPageMock(evaluateResults = [], overrides = {}) { - const evaluate = vi.fn(); - for (const result of evaluateResults) { - evaluate.mockResolvedValueOnce(result); - } - return { - // Navigation - goto: vi.fn().mockResolvedValue(undefined), - tabs: vi.fn().mockResolvedValue([]), - selectTab: vi.fn().mockResolvedValue(undefined), - closeTab: vi.fn().mockResolvedValue(undefined), - newTab: vi.fn().mockResolvedValue(undefined), - - // Content extraction - evaluate, - snapshot: vi.fn().mockResolvedValue(undefined), - screenshot: vi.fn().mockResolvedValue(''), - - // User interaction - click: vi.fn().mockResolvedValue(undefined), - typeText: vi.fn().mockResolvedValue(undefined), - pressKey: vi.fn().mockResolvedValue(undefined), - scrollTo: vi.fn().mockResolvedValue(undefined), - scroll: vi.fn().mockResolvedValue(undefined), - autoScroll: vi.fn().mockResolvedValue(undefined), - setFileInput: vi.fn().mockResolvedValue(undefined), - - // Form handling - getFormState: vi.fn().mockResolvedValue({ forms: [], orphanFields: [] }), - - // Monitoring - networkRequests: vi.fn().mockResolvedValue([]), - consoleMessages: vi.fn().mockResolvedValue([]), - - // Request interception - installInterceptor: vi.fn().mockResolvedValue(undefined), - getInterceptedRequests: vi.fn().mockResolvedValue([]), - waitForCapture: vi.fn().mockResolvedValue(undefined), - - // Network capture - startNetworkCapture: vi.fn().mockResolvedValue(undefined), - readNetworkCapture: vi.fn().mockResolvedValue([]), - - // Auth - getCookies: vi.fn().mockResolvedValue([]), - - // Wait - wait: vi.fn().mockResolvedValue(undefined), - - ...overrides, - }; -} diff --git a/plugin-command-manifest.json b/plugin-command-manifest.json index 7bd8deb0..9101229e 100644 --- a/plugin-command-manifest.json +++ b/plugin-command-manifest.json @@ -569,2088 +569,2250 @@ "siteSession": "persistent" }, { - "site": "apple-podcasts", - "name": "episodes", - "description": "List recent episodes of an Apple Podcast (use ID from search)", + "site": "antigravity", + "name": "add-context", + "description": "Click the Add context button in the composer (opens file/URL picker for context attachment).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/antigravity/audit-extras.js", + "sourceFile": "plugins/antigravity/audit-extras.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "cookies", + "description": "List cookies on the Antigravity renderer (JS-visible via document.cookie).", "access": "read", - "strategy": "public", - "browser": false, + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" + ], + "type": "js", + "modulePath": "plugins/antigravity/storage.js", + "sourceFile": "plugins/antigravity/storage.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "copy-code", + "description": "Return the text of a code block in the current conversation. Default: last code block; pass --index N (1-based from top) to pick a specific one.", + "access": "read", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Podcast ID (collectionId from search output)" - }, - { - "name": "limit", + "name": "index", "type": "int", - "default": 15, "required": false, - "help": "Max episodes to show" + "help": "1-based index of code block (default: last)" } ], "columns": [ - "title", - "duration", - "date" + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/apple-podcasts/episodes.js", - "sourceFile": "plugins/apple-podcasts/episodes.js" + "modulePath": "plugins/antigravity/audit-extras.js", + "sourceFile": "plugins/antigravity/audit-extras.js", + "navigateBefore": true }, { - "site": "apple-podcasts", - "name": "search", - "description": "Search Apple Podcasts", - "access": "read", - "strategy": "public", - "browser": false, + "site": "antigravity", + "name": "copy-message", + "description": "Return the text of the last assistant message (best-effort: walks up from the last visible Copy button).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", - "type": "str", + "name": "click-button", + "type": "boolean", + "default": false, + "required": false, + "help": "Also click the in-UI Copy button" + } + ], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "plugins/antigravity/audit-extras.js", + "sourceFile": "plugins/antigravity/audit-extras.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "delete", + "description": "Delete an Antigravity conversation by ID. Antigravity asks for confirmation; we click through it. Require --yes to actually delete.", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "id", + "type": "string", "required": true, "positional": true, - "help": "Search keyword" + "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "yes", + "type": "boolean", + "default": false, "required": false, - "help": "Max results" + "help": "Actually delete (default: dry-run preview)" } ], "columns": [ - "id", - "title", - "author", - "episodes", - "genre", - "url" + "status", + "id" ], - "tags": [ - "search" + "type": "js", + "modulePath": "plugins/antigravity/delete.js", + "sourceFile": "plugins/antigravity/delete.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "display-options", + "description": "Open the Display Options menu and list its items.", + "access": "read", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Item" ], "type": "js", - "modulePath": "plugins/apple-podcasts/search.js", - "sourceFile": "plugins/apple-podcasts/search.js" + "modulePath": "plugins/antigravity/audit-extras.js", + "sourceFile": "plugins/antigravity/audit-extras.js", + "navigateBefore": true }, { - "site": "apple-podcasts", - "name": "top", - "description": "Top podcasts chart on Apple Podcasts", + "site": "antigravity", + "name": "dump", + "description": "Dump the DOM to help AI understand the UI", "access": "read", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "htmlFile", + "snapFile" + ], + "type": "js", + "modulePath": "plugins/antigravity/dump.js", + "sourceFile": "plugins/antigravity/dump.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "extract-code", + "description": "Extract multi-line code blocks from the current Antigravity conversation", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "code" + ], + "type": "js", + "modulePath": "plugins/antigravity/extract-code.js", + "sourceFile": "plugins/antigravity/extract-code.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "history", + "description": "List visible Antigravity conversations from the sidebar", + "access": "read", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 20, - "required": false, - "help": "Number of podcasts (max 100)" - }, - { - "name": "country", - "type": "str", - "default": "us", + "default": 50, "required": false, - "help": "Country code (e.g. us, cn, gb, jp)" + "help": "Max conversations to return" } ], "columns": [ - "rank", - "title", - "author", - "id" + "Index", + "Id", + "Title" ], "type": "js", - "modulePath": "plugins/apple-podcasts/top.js", - "sourceFile": "plugins/apple-podcasts/top.js" + "modulePath": "plugins/antigravity/history.js", + "sourceFile": "plugins/antigravity/history.js", + "navigateBefore": true }, { - "site": "archive", - "name": "item", - "description": "Fetch metadata for a single Internet Archive item by identifier.", + "site": "antigravity", + "name": "idb-list", + "description": "List IndexedDB databases on the Antigravity renderer.", "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" + ], + "type": "js", + "modulePath": "plugins/antigravity/storage.js", + "sourceFile": "plugins/antigravity/storage.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "mark-read", + "description": "Mark an unread Antigravity conversation as read. Fails if the row is already read or the postcondition cannot be verified.", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, "args": [ { - "name": "identifier", - "type": "str", + "name": "id", + "type": "string", "required": true, "positional": true, - "help": "Archive item identifier (e.g. \"open-syllabus\", \"FinalFantasy2_356\")." + "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" } ], "columns": [ - "identifier", - "title", - "creator", - "date", - "mediatype", - "collection", - "description", - "file_count", - "url" + "status", + "id", + "clicked" ], "type": "js", - "modulePath": "plugins/archive/item.js", - "sourceFile": "plugins/archive/item.js" + "modulePath": "plugins/antigravity/mark-read.js", + "sourceFile": "plugins/antigravity/mark-read.js", + "navigateBefore": true }, { - "site": "archive", - "name": "search", - "description": "Search Internet Archive items across books, movies, audio, software, and web.", - "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, + "site": "antigravity", + "name": "model", + "description": "Read or switch the active model in Antigravity. Without arguments, reports the current model. With (substring, case-insensitive), switches.", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", + "name": "name", "type": "str", - "required": true, - "positional": true, - "help": "Full-text query (matches title, description, creator, subject)." - }, - { - "name": "mediatype", - "type": "string", "required": false, - "help": "Restrict to mediatype: texts, movies, audio, software, image, web, data, collection" + "positional": true, + "help": "Substring (case-insensitive) of target model name. Omit to read current." }, { - "name": "sort", - "type": "string", - "default": "downloads", + "name": "list", + "type": "boolean", + "default": false, "required": false, - "help": "Sort key: downloads, date, addeddate, week, title" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max items (max 100; one API page)." + "help": "List models in the picker (does not switch)" } ], "columns": [ - "rank", - "identifier", - "title", - "creator", - "date", - "mediatype", - "downloads", - "url" - ], - "tags": [ - "search" + "Status", + "Model" ], "type": "js", - "modulePath": "plugins/archive/search.js", - "sourceFile": "plugins/archive/search.js" + "modulePath": "plugins/antigravity/model.js", + "sourceFile": "plugins/antigravity/model.js", + "navigateBefore": true }, { - "site": "archive", - "name": "snapshots", - "description": "List Wayback Machine snapshots over time for a URL via the CDX API.", - "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, + "site": "antigravity", + "name": "nav", + "description": "Click Go Back or Go Forward (Antigravity in-app history).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, "args": [ { - "name": "url", + "name": "direction", "type": "str", "required": true, "positional": true, - "help": "URL to look up (with or without scheme)." - }, - { - "name": "from", - "type": "string", - "required": false, - "help": "Earliest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" - }, - { - "name": "to", - "type": "string", - "required": false, - "help": "Latest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max snapshots to return (max 1000)." + "help": "back or forward" } ], "columns": [ - "timestamp", - "snapshot_url", - "status", - "mimetype", - "original_url" + "Status" ], "type": "js", - "modulePath": "plugins/archive/snapshots.js", - "sourceFile": "plugins/archive/snapshots.js" + "modulePath": "plugins/antigravity/audit-extras.js", + "sourceFile": "plugins/antigravity/audit-extras.js", + "navigateBefore": true }, { - "site": "archive", - "name": "wayback", - "description": "Look up the closest Wayback Machine snapshot for a URL.", + "site": "antigravity", + "name": "new", + "description": "Start a new conversation / clear context in Antigravity", "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "plugins/antigravity/new.js", + "sourceFile": "plugins/antigravity/new.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "react", + "description": "Click \"Good response\" or \"Bad response\" on the LAST assistant message.", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, "args": [ { - "name": "url", + "name": "kind", "type": "str", "required": true, "positional": true, - "help": "URL to look up (with or without scheme)." - }, + "help": "good or bad" + } + ], + "columns": [ + "Status", + "Reaction" + ], + "type": "js", + "modulePath": "plugins/antigravity/audit-extras.js", + "sourceFile": "plugins/antigravity/audit-extras.js", + "navigateBefore": true + }, + { + "site": "antigravity", + "name": "read", + "description": "Read the latest chat messages from Antigravity AI", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ { - "name": "timestamp", - "type": "string", + "name": "last", + "type": "str", "required": false, - "help": "Target timestamp (YYYY[MM[DD[hh[mm[ss]]]]] or ISO date). Defaults to most recent snapshot." + "help": "Number of recent messages to read (not fully implemented due to generic structure, currently returns full history text or latest chunk)" } ], "columns": [ - "original_url", - "requested_timestamp", - "snapshot_timestamp", - "snapshot_url", - "status" + "role", + "content" ], "type": "js", - "modulePath": "plugins/archive/wayback.js", - "sourceFile": "plugins/archive/wayback.js" + "modulePath": "plugins/antigravity/read.js", + "sourceFile": "plugins/antigravity/read.js", + "navigateBefore": true }, { - "site": "arxiv", - "name": "author", - "description": "List arXiv papers by a given author (newest first)", + "site": "antigravity", + "name": "recent-paths", + "description": "Show Antigravity's recently-opened folders/files (history.recentlyOpenedPathsList).", "access": "read", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ - { - "name": "author", - "type": "str", - "required": true, - "positional": true, - "help": "Author name (e.g. \"Yoshua Bengio\" or \"Y Bengio\")" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max papers to return (max 50)" + "help": "Max rows to return" } ], "columns": [ - "id", - "title", - "authors", - "published", - "primary_category", - "url" + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/arxiv/author.js", - "sourceFile": "plugins/arxiv/author.js" + "modulePath": "plugins/antigravity/storage.js", + "sourceFile": "plugins/antigravity/storage.js" }, { - "site": "arxiv", - "name": "paper", - "description": "Get arXiv paper details by ID", - "access": "read", - "strategy": "public", - "browser": false, + "site": "antigravity", + "name": "rename", + "description": "Rename an Antigravity conversation by ID (NOT YET IMPLEMENTED — see source comment).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, "args": [ { "name": "id", - "type": "str", + "type": "string", "required": true, "positional": true, - "help": "arXiv paper ID (e.g. 1706.03762)" + "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" + }, + { + "name": "title", + "type": "string", + "required": true, + "positional": true, + "help": "New title" } ], "columns": [ - "id", - "title", - "authors", - "published", - "updated", - "primary_category", - "categories", - "abstract", - "comment", - "pdf", - "url" + "status" ], "type": "js", - "modulePath": "plugins/arxiv/paper.js", - "sourceFile": "plugins/arxiv/paper.js" + "modulePath": "plugins/antigravity/rename.js", + "sourceFile": "plugins/antigravity/rename.js", + "navigateBefore": true }, { - "site": "arxiv", - "name": "recent", - "description": "List recent arXiv submissions in a category", - "access": "read", - "strategy": "public", - "browser": false, + "site": "antigravity", + "name": "revert", + "description": "Click the revert button (per-message revert for agent changes). Requires --yes (this modifies your workspace).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", + "browser": true, "args": [ { - "name": "category", - "type": "str", - "required": true, - "positional": true, - "help": "arXiv category (e.g. cs.CL, cs.LG, math.PR, q-bio.NC)" - }, - { - "name": "limit", - "type": "int", - "default": 10, + "name": "yes", + "type": "boolean", + "default": false, "required": false, - "help": "Max results (max 50)" + "help": "Actually revert (default: dry-run)" } ], "columns": [ - "id", - "title", - "authors", - "published", - "primary_category", - "url" + "Status" ], "type": "js", - "modulePath": "plugins/arxiv/recent.js", - "sourceFile": "plugins/arxiv/recent.js" + "modulePath": "plugins/antigravity/audit-extras.js", + "sourceFile": "plugins/antigravity/audit-extras.js", + "navigateBefore": true }, { - "site": "arxiv", - "name": "search", - "description": "Search arXiv papers", - "access": "read", - "strategy": "public", - "browser": false, + "site": "antigravity", + "name": "send", + "description": "Send a message to Antigravity AI via the internal Lexical editor", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", + "name": "message", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"attention is all you need\")" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max results (max 25)" + "help": "The message text to send" } ], "columns": [ - "id", - "title", - "authors", - "published", - "primary_category", - "url" - ], - "tags": [ - "search" + "Status", + "Message" ], "type": "js", - "modulePath": "plugins/arxiv/search.js", - "sourceFile": "plugins/arxiv/search.js" + "modulePath": "plugins/antigravity/send.js", + "sourceFile": "plugins/antigravity/send.js", + "navigateBefore": true }, { - "site": "band", - "name": "bands", - "description": "List all Bands you belong to", - "access": "read", - "domain": "www.band.us", - "strategy": "cookie", + "site": "antigravity", + "name": "settings", + "description": "Click the Antigravity settings button (matched by data-testid=\"settings-button\").", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", "browser": true, "args": [], "columns": [ - "band_no", - "name", - "members" + "Status" ], "type": "js", - "modulePath": "plugins/band/bands.js", - "sourceFile": "plugins/band/bands.js", - "navigateBefore": "https://www.band.us" + "modulePath": "plugins/antigravity/audit-extras.js", + "sourceFile": "plugins/antigravity/audit-extras.js", + "navigateBefore": true }, { - "site": "band", - "name": "login", - "description": "Open band login", - "access": "write", - "domain": "band.us", - "strategy": "cookie", - "browser": true, + "site": "antigravity", + "name": "settings-read", + "description": "Read Antigravity's user settings.json (theme, proxy, agCockpit, tfa.system.autoAccept, etc.).", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, "args": [], "columns": [ - "status", - "logged_in", - "site", - "user_id", - "action", - "verify_command" + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/band/auth.js", - "sourceFile": "plugins/band/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/antigravity/storage.js", + "sourceFile": "plugins/antigravity/storage.js" }, { - "site": "band", - "name": "mentions", - "description": "Show Band notifications where you are @mentioned", - "access": "read", - "domain": "www.band.us", - "strategy": "intercept", + "site": "antigravity", + "name": "sidebar-toggle", + "description": "Click Toggle Sidebar (collapses/expands the Antigravity sidebar).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "filter", - "type": "str", - "default": "mentioned", - "required": false, - "help": "Filter: mentioned (default) | all | post | comment", - "choices": [ - "mentioned", - "all", - "post", - "comment" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results" - }, - { - "name": "unread", - "type": "bool", - "default": false, - "required": false, - "help": "Show only unread notifications" - } - ], + "args": [], "columns": [ - "time", - "band", - "type", - "from", - "text", - "url" + "Status" ], "type": "js", - "modulePath": "plugins/band/mentions.js", - "sourceFile": "plugins/band/mentions.js", + "modulePath": "plugins/antigravity/audit-extras.js", + "sourceFile": "plugins/antigravity/audit-extras.js", "navigateBefore": true }, { - "site": "band", - "name": "post", - "description": "Export full content of a post including comments", + "site": "antigravity", + "name": "state-get", + "description": "Read one value from Antigravity's state.vscdb. Pass --workspace for per-workspace.", "access": "read", - "domain": "www.band.us", - "strategy": "cookie", - "browser": true, + "domain": "localhost", + "strategy": "local", + "browser": false, "args": [ { - "name": "band_no", - "type": "int", - "required": true, - "positional": true, - "help": "Band number" - }, - { - "name": "post_no", - "type": "int", + "name": "key", + "type": "str", "required": true, "positional": true, - "help": "Post number" + "help": "Storage key name" }, { - "name": "output", + "name": "workspace", "type": "str", - "default": "", "required": false, - "help": "Directory to save attached photos" + "help": "Workspace id (from workspaces-list) to query per-workspace DB" }, { - "name": "comments", - "type": "bool", - "default": true, + "name": "max-bytes", + "type": "int", + "default": 8000, "required": false, - "help": "Include comments (default: true)" + "help": "Truncate value to this many chars" } ], "columns": [ - "type", - "author", - "date", - "text" + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/band/post.js", - "sourceFile": "plugins/band/post.js", - "navigateBefore": false + "modulePath": "plugins/antigravity/storage.js", + "sourceFile": "plugins/antigravity/storage.js" }, { - "site": "band", - "name": "posts", - "description": "List posts from a Band", + "site": "antigravity", + "name": "state-keys", + "description": "List keys in Antigravity's globalStorage state.vscdb (VSCode-style). Pass --workspace to query a per-workspace DB. Works while Antigravity is closed.", "access": "read", - "domain": "www.band.us", - "strategy": "cookie", - "browser": true, + "domain": "localhost", + "strategy": "local", + "browser": false, "args": [ { - "name": "band_no", - "type": "int", - "required": true, - "positional": true, - "help": "Band number (get it from: band bands)" + "name": "filter", + "type": "str", + "required": false, + "help": "Case-insensitive substring filter over keys" + }, + { + "name": "workspace", + "type": "str", + "required": false, + "help": "Workspace id (from workspaces-list) to query per-workspace DB" }, { "name": "limit", "type": "int", - "default": 20, + "default": 200, "required": false, - "help": "Max results" + "help": "Max rows to return" } ], "columns": [ - "date", - "author", - "content", - "comments", - "url" + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/band/posts.js", - "sourceFile": "plugins/band/posts.js", - "navigateBefore": false + "modulePath": "plugins/antigravity/storage.js", + "sourceFile": "plugins/antigravity/storage.js" }, { - "site": "band", - "name": "whoami", - "description": "Show the current logged-in band account", + "site": "antigravity", + "name": "status", + "description": "Check Antigravity CDP connection and get current page state", "access": "read", - "domain": "band.us", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "user_id" + "status", + "url", + "title" ], "type": "js", - "modulePath": "plugins/band/auth.js", - "sourceFile": "plugins/band/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/antigravity/status.js", + "sourceFile": "plugins/antigravity/status.js", + "navigateBefore": true }, { - "site": "barchart", - "name": "flow", - "description": "Barchart unusual options activity / options flow", + "site": "antigravity", + "name": "storage-get", + "description": "Read a single localStorage / sessionStorage value on the Antigravity renderer.", "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", + "domain": "127.0.0.1", + "strategy": "ui", "browser": true, "args": [ { - "name": "type", + "name": "key", "type": "str", - "default": "all", + "required": true, + "positional": true, + "help": "Storage key name" + }, + { + "name": "storage", + "type": "str", + "default": "local", "required": false, - "help": "Filter: all, call, or put", - "choices": [ - "all", - "call", - "put" - ] + "help": "\"local\" or \"session\"" }, { - "name": "limit", + "name": "max-bytes", "type": "int", - "default": 20, + "default": 4000, "required": false, - "help": "Number of results" + "help": "Truncate value to this many chars" } ], "columns": [ - "symbol", - "type", - "strike", - "expiration", - "last", - "volume", - "openInterest", - "volOiRatio", - "iv" + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/barchart/flow.js", - "sourceFile": "plugins/barchart/flow.js", - "navigateBefore": "https://www.barchart.com" + "modulePath": "plugins/antigravity/storage.js", + "sourceFile": "plugins/antigravity/storage.js", + "navigateBefore": true }, { - "site": "barchart", - "name": "greeks", - "description": "Barchart options greeks overview (IV, delta, gamma, theta, vega)", + "site": "antigravity", + "name": "storage-keys", + "description": "List localStorage / sessionStorage keys on the Antigravity renderer (CDP).", "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", + "domain": "127.0.0.1", + "strategy": "ui", "browser": true, "args": [ { - "name": "symbol", + "name": "storage", "type": "str", - "required": true, - "positional": true, - "help": "Stock ticker (e.g. AAPL)" + "default": "local", + "required": false, + "help": "\"local\" or \"session\"" }, { - "name": "expiration", + "name": "filter", "type": "str", "required": false, - "help": "Expiration date (YYYY-MM-DD). Defaults to the nearest available expiration." + "help": "Case-insensitive substring filter" }, { "name": "limit", "type": "int", - "default": 10, + "default": 100, "required": false, - "help": "Number of near-the-money strikes per type (1-100)" + "help": "Max rows to return" } ], "columns": [ - "type", - "strike", - "last", - "iv", - "delta", - "gamma", - "theta", - "vega", - "rho", - "volume", - "openInterest", - "expiration" + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/barchart/greeks.js", - "sourceFile": "plugins/barchart/greeks.js", - "navigateBefore": "https://www.barchart.com" + "modulePath": "plugins/antigravity/storage.js", + "sourceFile": "plugins/antigravity/storage.js", + "navigateBefore": true }, { - "site": "barchart", - "name": "options", - "description": "Barchart options chain with greeks, IV, volume, and open interest", - "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", + "site": "antigravity", + "name": "toggle-aux", + "description": "Toggle the Auxiliary Pane (Antigravity's secondary panel for code/preview).", + "access": "write", + "domain": "127.0.0.1", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Stock ticker (e.g. AAPL)" - }, - { - "name": "type", - "type": "str", - "default": "Call", - "required": false, - "help": "Option type: Call or Put", - "choices": [ - "Call", - "Put" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max number of strikes to return" - } - ], + "args": [], "columns": [ - "strike", - "bid", - "ask", - "last", - "change", - "volume", - "openInterest", - "iv", - "delta", - "gamma", - "theta", - "vega", - "expiration" + "Status" ], "type": "js", - "modulePath": "plugins/barchart/options.js", - "sourceFile": "plugins/barchart/options.js", - "navigateBefore": "https://www.barchart.com" + "modulePath": "plugins/antigravity/audit-extras.js", + "sourceFile": "plugins/antigravity/audit-extras.js", + "navigateBefore": true }, { - "site": "barchart", - "name": "quote", - "description": "Barchart stock quote with price, volume, and key metrics", + "site": "antigravity", + "name": "watch", + "description": "Stream new chat messages from Antigravity in real-time", "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" + "name": "timeout", + "type": "int", + "default": 86400, + "required": false, + "help": "Max seconds to keep watching (default: 86400 — 24h)" } ], - "columns": [ - "symbol", - "name", - "price", - "change", - "changePct", - "open", - "high", - "low", - "prevClose", - "volume", - "avgVolume", - "marketCap", - "peRatio", - "eps" - ], + "columns": [], "type": "js", - "modulePath": "plugins/barchart/quote.js", - "sourceFile": "plugins/barchart/quote.js", - "navigateBefore": "https://www.barchart.com" + "modulePath": "plugins/antigravity/watch.js", + "sourceFile": "plugins/antigravity/watch.js", + "navigateBefore": true }, { - "site": "bbc", - "name": "news", - "description": "BBC News headlines (RSS)", + "site": "antigravity", + "name": "workspaces-list", + "description": "List Antigravity workspaceStorage entries (each represents a previously-opened folder).", "access": "read", - "domain": "www.bbc.com", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 50, "required": false, - "help": "Number of headlines (max 50)" + "help": "Max rows to return" } ], "columns": [ - "rank", - "title", - "description", - "url" + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version", + "Kind", + "Path", + "Workspace Id", + "Folder", + "Modified", + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/bbc/news.js", - "sourceFile": "plugins/bbc/news.js" + "modulePath": "plugins/antigravity/storage.js", + "sourceFile": "plugins/antigravity/storage.js" }, { - "site": "bbc", - "name": "topic", - "description": "BBC News headlines for a specific section (RSS feed)", + "site": "apple-podcasts", + "name": "episodes", + "description": "List recent episodes of an Apple Podcast (use ID from search)", "access": "read", - "domain": "www.bbc.com", "strategy": "public", "browser": false, "args": [ { - "name": "topic", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Section name (world / business / politics / health / education / science_and_environment / technology / entertainment_and_arts)" + "help": "Podcast ID (collectionId from search output)" }, { "name": "limit", "type": "int", - "default": 20, + "default": 15, "required": false, - "help": "Max headlines (1-50)" + "help": "Max episodes to show" } ], "columns": [ - "rank", "title", - "description", - "pubDate", - "url" + "duration", + "date" ], "type": "js", - "modulePath": "plugins/bbc/topic.js", - "sourceFile": "plugins/bbc/topic.js" + "modulePath": "plugins/apple-podcasts/episodes.js", + "sourceFile": "plugins/apple-podcasts/episodes.js" }, { - "site": "bigbasket", - "name": "add-to-cart", - "description": "Add a BigBasket product to cart", - "access": "write", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, + "site": "apple-podcasts", + "name": "search", + "description": "Search Apple Podcasts", + "access": "read", + "strategy": "public", + "browser": false, "args": [ { - "name": "product", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Product ID or URL" + "help": "Search keyword" }, { - "name": "quantity", + "name": "limit", "type": "int", - "default": 1, + "default": 10, "required": false, - "help": "Quantity to add (max 20)" + "help": "Max results" } ], "columns": [ - "ok", - "product_id", - "quantity", - "url", - "message" + "id", + "title", + "author", + "episodes", + "genre", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/bigbasket/add-to-cart.js", - "sourceFile": "plugins/bigbasket/add-to-cart.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "cart", - "description": "Read BigBasket cart line items", - "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "product_id", - "title", - "quantity", - "price", - "line_total", - "availability", - "url" - ], - "type": "js", - "modulePath": "plugins/bigbasket/cart.js", - "sourceFile": "plugins/bigbasket/cart.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "plugins/apple-podcasts/search.js", + "sourceFile": "plugins/apple-podcasts/search.js" }, { - "site": "bigbasket", - "name": "category", - "description": "Read BigBasket category product cards", + "site": "apple-podcasts", + "name": "top", + "description": "Top podcasts chart on Apple Podcasts", "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, + "strategy": "public", + "browser": false, "args": [ - { - "name": "category", - "type": "str", - "required": true, - "positional": true, - "help": "Category URL or slug" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Maximum products to return (max 50)" + "help": "Number of podcasts (max 100)" + }, + { + "name": "country", + "type": "str", + "default": "us", + "required": false, + "help": "Country code (e.g. us, cn, gb, jp)" } ], "columns": [ "rank", - "product_id", "title", - "brand", - "pack_size", - "price", - "mrp", - "discount", - "availability", - "url" - ], - "type": "js", - "modulePath": "plugins/bigbasket/category.js", - "sourceFile": "plugins/bigbasket/category.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "checkout", - "description": "Open BigBasket checkout review without placing an order", - "access": "write", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "ok", - "stage", - "cart_total", - "address_ready", - "delivery_ready", - "payment_ready", - "next_action", - "url" - ], - "type": "js", - "modulePath": "plugins/bigbasket/checkout.js", - "sourceFile": "plugins/bigbasket/checkout.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "location", - "description": "Show the selected BigBasket delivery location", - "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "selected", - "label", - "area", - "city", - "pincode", - "source" + "author", + "id" ], "type": "js", - "modulePath": "plugins/bigbasket/location.js", - "sourceFile": "plugins/bigbasket/location.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "plugins/apple-podcasts/top.js", + "sourceFile": "plugins/apple-podcasts/top.js" }, { - "site": "bigbasket", - "name": "product", - "description": "Read BigBasket product details", + "site": "archive", + "name": "item", + "description": "Fetch metadata for a single Internet Archive item by identifier.", "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, + "domain": "archive.org", + "strategy": "public", + "browser": false, "args": [ { - "name": "product", + "name": "identifier", "type": "str", "required": true, "positional": true, - "help": "Product ID or URL" + "help": "Archive item identifier (e.g. \"open-syllabus\", \"FinalFantasy2_356\")." } ], "columns": [ - "product_id", + "identifier", "title", - "brand", - "pack_size", - "price", - "mrp", - "discount", - "availability", - "delivery", - "image_url", + "creator", + "date", + "mediatype", + "collection", + "description", + "file_count", "url" ], "type": "js", - "modulePath": "plugins/bigbasket/product.js", - "sourceFile": "plugins/bigbasket/product.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "plugins/archive/item.js", + "sourceFile": "plugins/archive/item.js" }, { - "site": "bigbasket", + "site": "archive", "name": "search", - "description": "Search BigBasket products", + "description": "Search Internet Archive items across books, movies, audio, software, and web.", "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, + "domain": "archive.org", + "strategy": "public", + "browser": false, "args": [ { "name": "query", "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "Full-text query (matches title, description, creator, subject)." + }, + { + "name": "mediatype", + "type": "string", + "required": false, + "help": "Restrict to mediatype: texts, movies, audio, software, image, web, data, collection" + }, + { + "name": "sort", + "type": "string", + "default": "downloads", + "required": false, + "help": "Sort key: downloads, date, addeddate, week, title" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Maximum products to return (max 50)" + "help": "Max items (max 100; one API page)." } ], "columns": [ "rank", - "product_id", + "identifier", "title", - "brand", - "pack_size", - "price", - "mrp", - "discount", - "availability", + "creator", + "date", + "mediatype", + "downloads", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/bigbasket/search.js", - "sourceFile": "plugins/bigbasket/search.js", - "navigateBefore": "https://www.bigbasket.com" + "modulePath": "plugins/archive/search.js", + "sourceFile": "plugins/archive/search.js" }, { - "site": "binance", - "name": "asks", - "description": "Order book ask prices for a trading pair", + "site": "archive", + "name": "snapshots", + "description": "List Wayback Machine snapshots over time for a URL via the CDX API.", "access": "read", - "domain": "data-api.binance.vision", + "domain": "archive.org", "strategy": "public", "browser": false, "args": [ { - "name": "symbol", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + "help": "URL to look up (with or without scheme)." + }, + { + "name": "from", + "type": "string", + "required": false, + "help": "Earliest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" + }, + { + "name": "to", + "type": "string", + "required": false, + "help": "Latest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of price levels (5, 10, 20, 50, 100)" + "help": "Max snapshots to return (max 1000)." } ], "columns": [ - "rank", - "ask_price", - "ask_qty" + "timestamp", + "snapshot_url", + "status", + "mimetype", + "original_url" ], "type": "js", - "modulePath": "plugins/binance/asks.js", - "sourceFile": "plugins/binance/asks.js" + "modulePath": "plugins/archive/snapshots.js", + "sourceFile": "plugins/archive/snapshots.js" }, { - "site": "binance", - "name": "depth", - "description": "Order book bid and ask prices for a trading pair", + "site": "archive", + "name": "wayback", + "description": "Look up the closest Wayback Machine snapshot for a URL.", "access": "read", - "domain": "data-api.binance.vision", + "domain": "archive.org", "strategy": "public", "browser": false, "args": [ { - "name": "symbol", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + "help": "URL to look up (with or without scheme)." }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "timestamp", + "type": "string", "required": false, - "help": "Number of price levels (5, 10, 20, 50, 100)" + "help": "Target timestamp (YYYY[MM[DD[hh[mm[ss]]]]] or ISO date). Defaults to most recent snapshot." } ], "columns": [ - "rank", - "bid_price", - "bid_qty", - "ask_price", - "ask_qty" + "original_url", + "requested_timestamp", + "snapshot_timestamp", + "snapshot_url", + "status" ], "type": "js", - "modulePath": "plugins/binance/depth.js", - "sourceFile": "plugins/binance/depth.js" + "modulePath": "plugins/archive/wayback.js", + "sourceFile": "plugins/archive/wayback.js" }, { - "site": "binance", - "name": "gainers", - "description": "Top gaining trading pairs by 24h price change", + "site": "arxiv", + "name": "author", + "description": "List arXiv papers by a given author (newest first)", "access": "read", - "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ + { + "name": "author", + "type": "str", + "required": true, + "positional": true, + "help": "Author name (e.g. \"Yoshua Bengio\" or \"Y Bengio\")" + }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of trading pairs" + "help": "Max papers to return (max 50)" } ], "columns": [ - "rank", - "symbol", - "price", - "change_24h", - "volume" + "id", + "title", + "authors", + "published", + "primary_category", + "url" ], "type": "js", - "modulePath": "plugins/binance/gainers.js", - "sourceFile": "plugins/binance/gainers.js" + "modulePath": "plugins/arxiv/author.js", + "sourceFile": "plugins/arxiv/author.js" }, { - "site": "binance", - "name": "klines", - "description": "Candlestick/kline data for a trading pair", + "site": "arxiv", + "name": "paper", + "description": "Get arXiv paper details by ID", "access": "read", - "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { - "name": "symbol", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" - }, - { - "name": "interval", - "type": "str", - "default": "1d", - "required": false, - "help": "Kline interval (1m, 5m, 15m, 1h, 4h, 1d, 1w, 1M)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of klines (max 1000)" + "help": "arXiv paper ID (e.g. 1706.03762)" } ], "columns": [ - "open", - "high", - "low", - "close", - "volume" + "id", + "title", + "authors", + "published", + "updated", + "primary_category", + "categories", + "abstract", + "comment", + "pdf", + "url" ], "type": "js", - "modulePath": "plugins/binance/klines.js", - "sourceFile": "plugins/binance/klines.js" + "modulePath": "plugins/arxiv/paper.js", + "sourceFile": "plugins/arxiv/paper.js" }, { - "site": "binance", - "name": "losers", - "description": "Top losing trading pairs by 24h price change", + "site": "arxiv", + "name": "recent", + "description": "List recent arXiv submissions in a category", "access": "read", - "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ + { + "name": "category", + "type": "str", + "required": true, + "positional": true, + "help": "arXiv category (e.g. cs.CL, cs.LG, math.PR, q-bio.NC)" + }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Number of trading pairs" + "help": "Max results (max 50)" } ], "columns": [ - "rank", - "symbol", - "price", - "change_24h", - "volume" + "id", + "title", + "authors", + "published", + "primary_category", + "url" ], "type": "js", - "modulePath": "plugins/binance/losers.js", - "sourceFile": "plugins/binance/losers.js" + "modulePath": "plugins/arxiv/recent.js", + "sourceFile": "plugins/arxiv/recent.js" }, { - "site": "binance", - "name": "pairs", - "description": "List active trading pairs on Binance", + "site": "arxiv", + "name": "search", + "description": "Search arXiv papers", "access": "read", - "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"attention is all you need\")" + }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Number of trading pairs" + "help": "Max results (max 25)" } ], "columns": [ - "symbol", - "base", - "quote", - "status" + "id", + "title", + "authors", + "published", + "primary_category", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/binance/pairs.js", - "sourceFile": "plugins/binance/pairs.js" + "modulePath": "plugins/arxiv/search.js", + "sourceFile": "plugins/arxiv/search.js" }, { - "site": "binance", - "name": "price", - "description": "Quick price check for a trading pair", + "site": "band", + "name": "bands", + "description": "List all Bands you belong to", "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" - } + "domain": "www.band.us", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "band_no", + "name", + "members" ], + "type": "js", + "modulePath": "plugins/band/bands.js", + "sourceFile": "plugins/band/bands.js", + "navigateBefore": "https://www.band.us" + }, + { + "site": "band", + "name": "login", + "description": "Open band login", + "access": "write", + "domain": "band.us", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "symbol", - "price", - "change", - "change_pct", - "high", - "low", - "volume", - "quote_volume", - "trades" + "status", + "logged_in", + "site", + "user_id", + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/binance/price.js", - "sourceFile": "plugins/binance/price.js" + "modulePath": "plugins/band/auth.js", + "sourceFile": "plugins/band/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "binance", - "name": "prices", - "description": "Latest prices for all trading pairs", + "site": "band", + "name": "mentions", + "description": "Show Band notifications where you are @mentioned", "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, + "domain": "www.band.us", + "strategy": "intercept", + "browser": true, "args": [ + { + "name": "filter", + "type": "str", + "default": "mentioned", + "required": false, + "help": "Filter: mentioned (default) | all | post | comment", + "choices": [ + "mentioned", + "all", + "post", + "comment" + ] + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of prices" + "help": "Max results" + }, + { + "name": "unread", + "type": "bool", + "default": false, + "required": false, + "help": "Show only unread notifications" } ], "columns": [ - "rank", - "symbol", - "price" - ], - "type": "js", - "modulePath": "plugins/binance/prices.js", - "sourceFile": "plugins/binance/prices.js" - }, + "time", + "band", + "type", + "from", + "text", + "url" + ], + "type": "js", + "modulePath": "plugins/band/mentions.js", + "sourceFile": "plugins/band/mentions.js", + "navigateBefore": true + }, { - "site": "binance", - "name": "ticker", - "description": "24h ticker statistics for top trading pairs by volume", + "site": "band", + "name": "post", + "description": "Export full content of a post including comments", "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, + "domain": "www.band.us", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", + "name": "band_no", "type": "int", - "default": 20, + "required": true, + "positional": true, + "help": "Band number" + }, + { + "name": "post_no", + "type": "int", + "required": true, + "positional": true, + "help": "Post number" + }, + { + "name": "output", + "type": "str", + "default": "", "required": false, - "help": "Number of tickers" + "help": "Directory to save attached photos" + }, + { + "name": "comments", + "type": "bool", + "default": true, + "required": false, + "help": "Include comments (default: true)" } ], "columns": [ - "symbol", - "price", - "change_pct", - "high", - "low", - "volume", - "quote_vol", - "trades" + "type", + "author", + "date", + "text" ], "type": "js", - "modulePath": "plugins/binance/ticker.js", - "sourceFile": "plugins/binance/ticker.js" + "modulePath": "plugins/band/post.js", + "sourceFile": "plugins/band/post.js", + "navigateBefore": false }, { - "site": "binance", - "name": "top", - "description": "Top trading pairs by 24h volume on Binance", + "site": "band", + "name": "posts", + "description": "List posts from a Band", "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, + "domain": "www.band.us", + "strategy": "cookie", + "browser": true, "args": [ + { + "name": "band_no", + "type": "int", + "required": true, + "positional": true, + "help": "Band number (get it from: band bands)" + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of trading pairs" + "help": "Max results" } ], "columns": [ - "rank", - "symbol", - "price", - "change_24h", - "high", - "low", - "volume" + "date", + "author", + "content", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/binance/top.js", - "sourceFile": "plugins/binance/top.js" + "modulePath": "plugins/band/posts.js", + "sourceFile": "plugins/band/posts.js", + "navigateBefore": false }, { - "site": "binance", - "name": "trades", - "description": "Recent trades for a trading pair", + "site": "band", + "name": "whoami", + "description": "Show the current logged-in band account", "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, + "domain": "band.us", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id" + ], + "type": "js", + "modulePath": "plugins/band/auth.js", + "sourceFile": "plugins/band/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "barchart", + "name": "flow", + "description": "Barchart unusual options activity / options flow", + "access": "read", + "domain": "www.barchart.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "symbol", + "name": "type", "type": "str", - "required": true, - "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + "default": "all", + "required": false, + "help": "Filter: all, call, or put", + "choices": [ + "all", + "call", + "put" + ] }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of trades (max 1000)" + "help": "Number of results" } ], "columns": [ - "id", - "price", - "qty", - "quote_qty", - "buyer_maker" + "symbol", + "type", + "strike", + "expiration", + "last", + "volume", + "openInterest", + "volOiRatio", + "iv" ], "type": "js", - "modulePath": "plugins/binance/trades.js", - "sourceFile": "plugins/binance/trades.js" + "modulePath": "plugins/barchart/flow.js", + "sourceFile": "plugins/barchart/flow.js", + "navigateBefore": "https://www.barchart.com" }, { - "site": "blinkit", - "name": "add-to-cart", - "description": "Add a Blinkit product to cart", - "access": "write", - "domain": "blinkit.com", + "site": "barchart", + "name": "greeks", + "description": "Barchart options greeks overview (IV, delta, gamma, theta, vega)", + "access": "read", + "domain": "www.barchart.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "productId", + "name": "symbol", "type": "str", "required": true, "positional": true, - "help": "Blinkit product id" - }, - { - "name": "quantity", - "type": "int", - "default": 1, - "required": false, - "help": "Quantity to add (default 1, max 12)" + "help": "Stock ticker (e.g. AAPL)" }, { - "name": "lat", + "name": "expiration", "type": "str", "required": false, - "help": "Delivery latitude (defaults to current Blinkit browser location)" + "help": "Expiration date (YYYY-MM-DD). Defaults to the nearest available expiration." }, { - "name": "lon", - "type": "str", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Delivery longitude (defaults to current Blinkit browser location)" + "help": "Number of near-the-money strikes per type (1-100)" } ], "columns": [ - "status", - "productId", - "quantity", - "itemCount", - "itemsTotal", - "payable", - "message" + "type", + "strike", + "last", + "iv", + "delta", + "gamma", + "theta", + "vega", + "rho", + "volume", + "openInterest", + "expiration" ], "type": "js", - "modulePath": "plugins/blinkit/add-to-cart.js", - "sourceFile": "plugins/blinkit/add-to-cart.js", - "navigateBefore": false + "modulePath": "plugins/barchart/greeks.js", + "sourceFile": "plugins/barchart/greeks.js", + "navigateBefore": "https://www.barchart.com" }, { - "site": "blinkit", - "name": "cart", - "description": "Show the current Blinkit cart", + "site": "barchart", + "name": "options", + "description": "Barchart options chain with greeks, IV, volume, and open interest", "access": "read", - "domain": "blinkit.com", + "domain": "www.barchart.com", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "status", - "productId", - "name", - "variant", - "price", - "quantity", - "total", - "itemCount", - "payable", - "cartState" - ], - "type": "js", - "modulePath": "plugins/blinkit/cart.js", - "sourceFile": "plugins/blinkit/cart.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "checkout", - "description": "Review Blinkit checkout totals and blockers without placing an order", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [], + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Stock ticker (e.g. AAPL)" + }, + { + "name": "type", + "type": "str", + "default": "Call", + "required": false, + "help": "Option type: Call or Put", + "choices": [ + "Call", + "Put" + ] + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max number of strikes to return" + } + ], "columns": [ - "status", - "itemCount", - "itemsTotal", - "deliveryCharge", - "handlingCharge", - "payable", - "cartState", - "checkoutBlocked", - "validations" + "strike", + "bid", + "ask", + "last", + "change", + "volume", + "openInterest", + "iv", + "delta", + "gamma", + "theta", + "vega", + "expiration" ], "type": "js", - "modulePath": "plugins/blinkit/checkout.js", - "sourceFile": "plugins/blinkit/checkout.js", - "navigateBefore": false + "modulePath": "plugins/barchart/options.js", + "sourceFile": "plugins/barchart/options.js", + "navigateBefore": "https://www.barchart.com" }, { - "site": "blinkit", - "name": "location", - "description": "Show the selected Blinkit delivery location", + "site": "barchart", + "name": "quote", + "description": "Barchart stock quote with price, volume, and key metrics", "access": "read", - "domain": "blinkit.com", + "domain": "www.barchart.com", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "selected", - "label", - "area", - "city", - "pincode", - "hasCoordinates", - "source" + "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" + } ], - "type": "js", - "modulePath": "plugins/blinkit/location.js", - "sourceFile": "plugins/blinkit/location.js", - "navigateBefore": "https://blinkit.com" - }, - { - "site": "blinkit", - "name": "login", - "description": "Open blinkit login", - "access": "write", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [], "columns": [ - "status", - "logged_in", - "site", - "phone", - "user_id", - "action", - "verify_command" + "symbol", + "name", + "price", + "change", + "changePct", + "open", + "high", + "low", + "prevClose", + "volume", + "avgVolume", + "marketCap", + "peRatio", + "eps" ], "type": "js", - "modulePath": "plugins/blinkit/auth.js", - "sourceFile": "plugins/blinkit/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/barchart/quote.js", + "sourceFile": "plugins/barchart/quote.js", + "navigateBefore": "https://www.barchart.com" }, { - "site": "blinkit", - "name": "place-order", - "description": "Submit the visible Blinkit final order/payment action. Requires --confirm.", - "access": "write", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, + "site": "bbc", + "name": "news", + "description": "BBC News headlines (RSS)", + "access": "read", + "domain": "www.bbc.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "confirm", - "type": "bool", - "default": false, + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Required acknowledgement that this may place/pay for a real order" + "help": "Number of headlines (max 50)" } ], "columns": [ - "status", - "confirmed", - "itemCount", - "payable", - "orderId", - "url", - "message" + "rank", + "title", + "description", + "url" ], "type": "js", - "modulePath": "plugins/blinkit/place-order.js", - "sourceFile": "plugins/blinkit/place-order.js", - "navigateBefore": false + "modulePath": "plugins/bbc/news.js", + "sourceFile": "plugins/bbc/news.js" }, { - "site": "blinkit", - "name": "product", - "description": "Read Blinkit product details for a delivery location", + "site": "bbc", + "name": "topic", + "description": "BBC News headlines for a specific section (RSS feed)", "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, + "domain": "www.bbc.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "productId", + "name": "topic", "type": "str", "required": true, "positional": true, - "help": "Blinkit product id" - }, - { - "name": "lat", - "type": "str", - "required": false, - "help": "Delivery latitude (defaults to current Blinkit browser location)" + "help": "Section name (world / business / politics / health / education / science_and_environment / technology / entertainment_and_arts)" }, { - "name": "lon", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Delivery longitude (defaults to current Blinkit browser location)" + "help": "Max headlines (1-50)" } ], "columns": [ - "productId", - "name", - "brand", - "variant", - "price", - "mrp", - "currency", - "inventory", - "available", - "imageUrl", + "rank", + "title", + "description", + "pubDate", "url" ], "type": "js", - "modulePath": "plugins/blinkit/product.js", - "sourceFile": "plugins/blinkit/product.js", - "navigateBefore": false + "modulePath": "plugins/bbc/topic.js", + "sourceFile": "plugins/bbc/topic.js" }, { - "site": "blinkit", - "name": "search", - "description": "Search Blinkit products for a delivery location", - "access": "read", - "domain": "blinkit.com", + "site": "bigbasket", + "name": "add-to-cart", + "description": "Add a BigBasket product to cart", + "access": "write", + "domain": "www.bigbasket.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", + "name": "product", "type": "str", "required": true, "positional": true, - "help": "Search keyword" + "help": "Product ID or URL" }, { - "name": "limit", + "name": "quantity", "type": "int", - "default": 20, - "required": false, - "help": "Max results (max 48)" - }, - { - "name": "lat", - "type": "str", - "required": false, - "help": "Delivery latitude (defaults to current Blinkit browser location)" - }, - { - "name": "lon", - "type": "str", + "default": 1, "required": false, - "help": "Delivery longitude (defaults to current Blinkit browser location)" + "help": "Quantity to add (max 20)" } ], "columns": [ - "rank", - "productId", - "name", - "brand", - "variant", - "price", - "mrp", - "currency", - "inventory", - "available", - "imageUrl", - "url" - ], - "tags": [ - "search" + "ok", + "product_id", + "quantity", + "url", + "message" ], "type": "js", - "modulePath": "plugins/blinkit/search.js", - "sourceFile": "plugins/blinkit/search.js", - "navigateBefore": false + "modulePath": "plugins/bigbasket/add-to-cart.js", + "sourceFile": "plugins/bigbasket/add-to-cart.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "blinkit", - "name": "whoami", - "description": "Show the current logged-in blinkit account", + "site": "bigbasket", + "name": "cart", + "description": "Read BigBasket cart line items", "access": "read", - "domain": "blinkit.com", + "domain": "www.bigbasket.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "phone", - "user_id" + "product_id", + "title", + "quantity", + "price", + "line_total", + "availability", + "url" ], "type": "js", - "modulePath": "plugins/blinkit/auth.js", - "sourceFile": "plugins/blinkit/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/bigbasket/cart.js", + "sourceFile": "plugins/bigbasket/cart.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "bloomberg", - "name": "businessweek", - "description": "Bloomberg Businessweek top stories", + "site": "bigbasket", + "name": "category", + "description": "Read BigBasket category product cards", "access": "read", - "domain": "www.bloomberg.com", - "strategy": "public", + "domain": "www.bigbasket.com", + "strategy": "cookie", "browser": true, "args": [ + { + "name": "category", + "type": "str", + "required": true, + "positional": true, + "help": "Category URL or slug" + }, { "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Number of stories to return (max 20)" + "help": "Maximum products to return (max 50)" } ], "columns": [ + "rank", + "product_id", "title", - "summary", - "link", - "mediaLinks" + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", + "url" ], "type": "js", - "modulePath": "plugins/bloomberg/businessweek.js", - "sourceFile": "plugins/bloomberg/businessweek.js" + "modulePath": "plugins/bigbasket/category.js", + "sourceFile": "plugins/bigbasket/category.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "bloomberg", - "name": "crypto", - "description": "Bloomberg Crypto top stories (RSS)", + "site": "bigbasket", + "name": "checkout", + "description": "Open BigBasket checkout review without placing an order", + "access": "write", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "ok", + "stage", + "cart_total", + "address_ready", + "delivery_ready", + "payment_ready", + "next_action", + "url" + ], + "type": "js", + "modulePath": "plugins/bigbasket/checkout.js", + "sourceFile": "plugins/bigbasket/checkout.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "location", + "description": "Show the selected BigBasket delivery location", "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "selected", + "label", + "area", + "city", + "pincode", + "source" + ], + "type": "js", + "modulePath": "plugins/bigbasket/location.js", + "sourceFile": "plugins/bigbasket/location.js", + "navigateBefore": "https://www.bigbasket.com" + }, + { + "site": "bigbasket", + "name": "product", + "description": "Read BigBasket product details", + "access": "read", + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" + "name": "product", + "type": "str", + "required": true, + "positional": true, + "help": "Product ID or URL" } ], "columns": [ + "product_id", "title", - "summary", - "link", - "mediaLinks" + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", + "delivery", + "image_url", + "url" ], "type": "js", - "modulePath": "plugins/bloomberg/crypto.js", - "sourceFile": "plugins/bloomberg/crypto.js" + "modulePath": "plugins/bigbasket/product.js", + "sourceFile": "plugins/bigbasket/product.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "bloomberg", - "name": "economics", - "description": "Bloomberg Economics top stories (RSS)", + "site": "bigbasket", + "name": "search", + "description": "Search BigBasket products", "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, + "domain": "www.bigbasket.com", + "strategy": "cookie", + "browser": true, "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, { "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Maximum products to return (max 50)" } ], "columns": [ + "rank", + "product_id", "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "plugins/bloomberg/economics.js", - "sourceFile": "plugins/bloomberg/economics.js" - }, - { - "site": "bloomberg", - "name": "feeds", - "description": "List the Bloomberg RSS feed aliases used by the adapter", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "name", + "brand", + "pack_size", + "price", + "mrp", + "discount", + "availability", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/bloomberg/feeds.js", - "sourceFile": "plugins/bloomberg/feeds.js" + "modulePath": "plugins/bigbasket/search.js", + "sourceFile": "plugins/bigbasket/search.js", + "navigateBefore": "https://www.bigbasket.com" }, { - "site": "bloomberg", - "name": "green", - "description": "Bloomberg Green (climate & energy) top stories (RSS)", + "site": "binance", + "name": "asks", + "description": "Order book ask prices for a trading pair", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, { "name": "limit", "type": "int", - "default": 1, + "default": 10, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of price levels (5, 10, 20, 50, 100)" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "rank", + "ask_price", + "ask_qty" ], "type": "js", - "modulePath": "plugins/bloomberg/green.js", - "sourceFile": "plugins/bloomberg/green.js" + "modulePath": "plugins/binance/asks.js", + "sourceFile": "plugins/binance/asks.js" }, { - "site": "bloomberg", - "name": "industries", - "description": "Bloomberg Industries top stories (RSS)", + "site": "binance", + "name": "depth", + "description": "Order book bid and ask prices for a trading pair", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, { "name": "limit", "type": "int", - "default": 1, + "default": 10, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of price levels (5, 10, 20, 50, 100)" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "rank", + "bid_price", + "bid_qty", + "ask_price", + "ask_qty" ], "type": "js", - "modulePath": "plugins/bloomberg/industries.js", - "sourceFile": "plugins/bloomberg/industries.js" + "modulePath": "plugins/binance/depth.js", + "sourceFile": "plugins/binance/depth.js" }, { - "site": "bloomberg", - "name": "main", - "description": "Bloomberg homepage top stories (RSS)", + "site": "binance", + "name": "gainers", + "description": "Top gaining trading pairs by 24h price change", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 1, + "default": 10, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of trading pairs" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "rank", + "symbol", + "price", + "change_24h", + "volume" ], "type": "js", - "modulePath": "plugins/bloomberg/main.js", - "sourceFile": "plugins/bloomberg/main.js" + "modulePath": "plugins/binance/gainers.js", + "sourceFile": "plugins/binance/gainers.js" }, { - "site": "bloomberg", - "name": "markets", - "description": "Bloomberg Markets top stories (RSS)", + "site": "binance", + "name": "klines", + "description": "Candlestick/kline data for a trading pair", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ + { + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + }, + { + "name": "interval", + "type": "str", + "default": "1d", + "required": false, + "help": "Kline interval (1m, 5m, 15m, 1h, 4h, 1d, 1w, 1M)" + }, { "name": "limit", "type": "int", - "default": 1, + "default": 10, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of klines (max 1000)" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "open", + "high", + "low", + "close", + "volume" ], "type": "js", - "modulePath": "plugins/bloomberg/markets.js", - "sourceFile": "plugins/bloomberg/markets.js" + "modulePath": "plugins/binance/klines.js", + "sourceFile": "plugins/binance/klines.js" }, { - "site": "bloomberg", - "name": "news", - "description": "Read a Bloomberg story/article page and return title, full content, and media links", + "site": "binance", + "name": "losers", + "description": "Top losing trading pairs by 24h price change", "access": "read", - "domain": "www.bloomberg.com", - "strategy": "cookie", - "browser": true, + "domain": "data-api.binance.vision", + "strategy": "public", + "browser": false, "args": [ { - "name": "link", - "type": "str", - "required": true, - "positional": true, - "help": "Bloomberg story/article URL or relative Bloomberg path" + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of trading pairs" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks", - "content" + "rank", + "symbol", + "price", + "change_24h", + "volume" ], "type": "js", - "modulePath": "plugins/bloomberg/news.js", - "sourceFile": "plugins/bloomberg/news.js", - "navigateBefore": "https://www.bloomberg.com" + "modulePath": "plugins/binance/losers.js", + "sourceFile": "plugins/binance/losers.js" }, { - "site": "bloomberg", - "name": "opinions", - "description": "Bloomberg Opinion top stories (RSS)", + "site": "binance", + "name": "pairs", + "description": "List active trading pairs on Binance", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of trading pairs" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "symbol", + "base", + "quote", + "status" ], "type": "js", - "modulePath": "plugins/bloomberg/opinions.js", - "sourceFile": "plugins/bloomberg/opinions.js" + "modulePath": "plugins/binance/pairs.js", + "sourceFile": "plugins/binance/pairs.js" }, { - "site": "bloomberg", - "name": "politics", - "description": "Bloomberg Politics top stories (RSS)", + "site": "binance", + "name": "price", + "description": "Quick price check for a trading pair", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" + "name": "symbol", + "type": "str", + "required": true, + "positional": true, + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "symbol", + "price", + "change", + "change_pct", + "high", + "low", + "volume", + "quote_volume", + "trades" ], "type": "js", - "modulePath": "plugins/bloomberg/politics.js", - "sourceFile": "plugins/bloomberg/politics.js" + "modulePath": "plugins/binance/price.js", + "sourceFile": "plugins/binance/price.js" }, { - "site": "bloomberg", - "name": "pursuits", - "description": "Bloomberg Pursuits (lifestyle) top stories (RSS)", + "site": "binance", + "name": "prices", + "description": "Latest prices for all trading pairs", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of prices" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "rank", + "symbol", + "price" ], "type": "js", - "modulePath": "plugins/bloomberg/pursuits.js", - "sourceFile": "plugins/bloomberg/pursuits.js" + "modulePath": "plugins/binance/prices.js", + "sourceFile": "plugins/binance/prices.js" }, { - "site": "bloomberg", - "name": "tech", - "description": "Bloomberg Tech top stories (RSS)", + "site": "binance", + "name": "ticker", + "description": "24h ticker statistics for top trading pairs by volume", "access": "read", - "domain": "feeds.bloomberg.com", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Number of feed items to return (max 20)" + "help": "Number of tickers" } ], "columns": [ - "title", - "summary", - "link", - "mediaLinks" + "symbol", + "price", + "change_pct", + "high", + "low", + "volume", + "quote_vol", + "trades" ], "type": "js", - "modulePath": "plugins/bloomberg/tech.js", - "sourceFile": "plugins/bloomberg/tech.js" + "modulePath": "plugins/binance/ticker.js", + "sourceFile": "plugins/binance/ticker.js" }, { - "site": "bluesky", - "name": "feeds", - "description": "Popular Bluesky feed generators", + "site": "binance", + "name": "top", + "description": "Top trading pairs by 24h volume on Binance", "access": "read", - "domain": "public.api.bsky.app", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ @@ -2659,1336 +2821,1253 @@ "type": "int", "default": 20, "required": false, - "help": "Number of feeds" + "help": "Number of trading pairs" } ], "columns": [ "rank", - "name", - "likes", - "creator", - "description" + "symbol", + "price", + "change_24h", + "high", + "low", + "volume" ], "type": "js", - "modulePath": "plugins/bluesky/feeds.js", - "sourceFile": "plugins/bluesky/feeds.js" + "modulePath": "plugins/binance/top.js", + "sourceFile": "plugins/binance/top.js" }, { - "site": "bluesky", - "name": "followers", - "description": "List followers of a Bluesky user", + "site": "binance", + "name": "trades", + "description": "Recent trades for a trading pair", "access": "read", - "domain": "public.api.bsky.app", + "domain": "data-api.binance.vision", "strategy": "public", "browser": false, "args": [ { - "name": "handle", + "name": "symbol", "type": "str", "required": true, "positional": true, - "help": "Bluesky handle" + "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of followers" + "help": "Number of trades (max 1000)" } ], "columns": [ - "rank", - "handle", - "name", - "description" + "id", + "price", + "qty", + "quote_qty", + "buyer_maker" ], "type": "js", - "modulePath": "plugins/bluesky/followers.js", - "sourceFile": "plugins/bluesky/followers.js" + "modulePath": "plugins/binance/trades.js", + "sourceFile": "plugins/binance/trades.js" }, { - "site": "bluesky", - "name": "following", - "description": "List accounts a Bluesky user is following", - "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, + "site": "blinkit", + "name": "add-to-cart", + "description": "Add a Blinkit product to cart", + "access": "write", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "handle", + "name": "productId", "type": "str", "required": true, "positional": true, - "help": "Bluesky handle" + "help": "Blinkit product id" }, { - "name": "limit", + "name": "quantity", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of accounts" + "help": "Quantity to add (default 1, max 12)" + }, + { + "name": "lat", + "type": "str", + "required": false, + "help": "Delivery latitude (defaults to current Blinkit browser location)" + }, + { + "name": "lon", + "type": "str", + "required": false, + "help": "Delivery longitude (defaults to current Blinkit browser location)" } ], "columns": [ - "rank", - "handle", + "status", + "productId", + "quantity", + "itemCount", + "itemsTotal", + "payable", + "message" + ], + "type": "js", + "modulePath": "plugins/blinkit/add-to-cart.js", + "sourceFile": "plugins/blinkit/add-to-cart.js", + "navigateBefore": false + }, + { + "site": "blinkit", + "name": "cart", + "description": "Show the current Blinkit cart", + "access": "read", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "productId", "name", - "description" + "variant", + "price", + "quantity", + "total", + "itemCount", + "payable", + "cartState" ], "type": "js", - "modulePath": "plugins/bluesky/following.js", - "sourceFile": "plugins/bluesky/following.js" + "modulePath": "plugins/blinkit/cart.js", + "sourceFile": "plugins/blinkit/cart.js", + "navigateBefore": false }, { - "site": "bluesky", - "name": "profile", - "description": "Get Bluesky user profile info", + "site": "blinkit", + "name": "checkout", + "description": "Review Blinkit checkout totals and blockers without placing an order", "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "itemCount", + "itemsTotal", + "deliveryCharge", + "handlingCharge", + "payable", + "cartState", + "checkoutBlocked", + "validations" + ], + "type": "js", + "modulePath": "plugins/blinkit/checkout.js", + "sourceFile": "plugins/blinkit/checkout.js", + "navigateBefore": false + }, + { + "site": "blinkit", + "name": "location", + "description": "Show the selected Blinkit delivery location", + "access": "read", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "selected", + "label", + "area", + "city", + "pincode", + "hasCoordinates", + "source" + ], + "type": "js", + "modulePath": "plugins/blinkit/location.js", + "sourceFile": "plugins/blinkit/location.js", + "navigateBefore": "https://blinkit.com" + }, + { + "site": "blinkit", + "name": "login", + "description": "Open blinkit login", + "access": "write", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "phone", + "user_id", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/blinkit/auth.js", + "sourceFile": "plugins/blinkit/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "blinkit", + "name": "place-order", + "description": "Submit the visible Blinkit final order/payment action. Requires --confirm.", + "access": "write", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "handle", - "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle (e.g. bsky.app, jay.bsky.team)" + "name": "confirm", + "type": "bool", + "default": false, + "required": false, + "help": "Required acknowledgement that this may place/pay for a real order" } ], "columns": [ - "handle", - "name", - "followers", - "following", - "posts", - "description" + "status", + "confirmed", + "itemCount", + "payable", + "orderId", + "url", + "message" ], "type": "js", - "modulePath": "plugins/bluesky/profile.js", - "sourceFile": "plugins/bluesky/profile.js" + "modulePath": "plugins/blinkit/place-order.js", + "sourceFile": "plugins/blinkit/place-order.js", + "navigateBefore": false }, { - "site": "bluesky", - "name": "search", - "description": "Search Bluesky users", + "site": "blinkit", + "name": "product", + "description": "Read Blinkit product details for a delivery location", "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "productId", "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "Blinkit product id" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "lat", + "type": "str", "required": false, - "help": "Number of results" + "help": "Delivery latitude (defaults to current Blinkit browser location)" + }, + { + "name": "lon", + "type": "str", + "required": false, + "help": "Delivery longitude (defaults to current Blinkit browser location)" } ], "columns": [ - "rank", - "handle", + "productId", "name", - "followers", - "description" - ], - "tags": [ - "search" + "brand", + "variant", + "price", + "mrp", + "currency", + "inventory", + "available", + "imageUrl", + "url" ], "type": "js", - "modulePath": "plugins/bluesky/search.js", - "sourceFile": "plugins/bluesky/search.js" + "modulePath": "plugins/blinkit/product.js", + "sourceFile": "plugins/blinkit/product.js", + "navigateBefore": false }, { - "site": "bluesky", - "name": "starter-packs", - "description": "Get starter packs created by a Bluesky user", + "site": "blinkit", + "name": "search", + "description": "Search Blinkit products for a delivery location", "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "handle", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Bluesky handle" + "help": "Search keyword" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of starter packs" + "help": "Max results (max 48)" + }, + { + "name": "lat", + "type": "str", + "required": false, + "help": "Delivery latitude (defaults to current Blinkit browser location)" + }, + { + "name": "lon", + "type": "str", + "required": false, + "help": "Delivery longitude (defaults to current Blinkit browser location)" } ], "columns": [ "rank", + "productId", "name", - "description", - "members", - "joins" + "brand", + "variant", + "price", + "mrp", + "currency", + "inventory", + "available", + "imageUrl", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/bluesky/starter-packs.js", - "sourceFile": "plugins/bluesky/starter-packs.js" + "modulePath": "plugins/blinkit/search.js", + "sourceFile": "plugins/blinkit/search.js", + "navigateBefore": false }, { - "site": "bluesky", - "name": "thread", - "description": "Get a Bluesky post thread with replies", + "site": "blinkit", + "name": "whoami", + "description": "Show the current logged-in blinkit account", "access": "read", - "domain": "public.api.bsky.app", + "domain": "blinkit.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "phone", + "user_id" + ], + "type": "js", + "modulePath": "plugins/blinkit/auth.js", + "sourceFile": "plugins/blinkit/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "bloomberg", + "name": "businessweek", + "description": "Bloomberg Businessweek top stories", + "access": "read", + "domain": "www.bloomberg.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ - { - "name": "uri", - "type": "str", - "required": true, - "positional": true, - "help": "Post AT URI (at://did:.../app.bsky.feed.post/...) or bsky.app URL" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of replies" + "help": "Number of stories to return (max 20)" } ], "columns": [ - "author", - "text", - "likes", - "reposts", - "replies_count" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/thread.js", - "sourceFile": "plugins/bluesky/thread.js" + "modulePath": "plugins/bloomberg/businessweek.js", + "sourceFile": "plugins/bloomberg/businessweek.js" }, { - "site": "bluesky", - "name": "trending", - "description": "Trending topics on Bluesky", + "site": "bloomberg", + "name": "crypto", + "description": "Bloomberg Crypto top stories (RSS)", "access": "read", - "domain": "public.api.bsky.app", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of topics" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "topic", - "link" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/trending.js", - "sourceFile": "plugins/bluesky/trending.js" + "modulePath": "plugins/bloomberg/crypto.js", + "sourceFile": "plugins/bloomberg/crypto.js" }, { - "site": "bluesky", - "name": "user", - "description": "Get recent posts from a Bluesky user", + "site": "bloomberg", + "name": "economics", + "description": "Bloomberg Economics top stories (RSS)", "access": "read", - "domain": "public.api.bsky.app", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ - { - "name": "handle", - "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle (e.g. bsky.app)" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Number of posts" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "uri", - "text", - "likes", - "reposts", - "replies" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bluesky/user.js", - "sourceFile": "plugins/bluesky/user.js" + "modulePath": "plugins/bloomberg/economics.js", + "sourceFile": "plugins/bloomberg/economics.js" }, { - "site": "bmwblog", - "name": "article", - "description": "Read a BMWBLOG article by URL or slug", + "site": "bloomberg", + "name": "feeds", + "description": "List the Bloomberg RSS feed aliases used by the adapter", "access": "read", - "domain": "www.bmwblog.com", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, - "args": [ - { - "name": "url-or-slug", - "type": "str", - "required": true, - "positional": true, - "help": "BMWBLOG article URL or slug" - } - ], + "args": [], "columns": [ - "title", - "date", - "author", - "sections", - "excerpt", - "url", - "content" + "name", + "url" ], "type": "js", - "modulePath": "plugins/bmwblog/article.js", - "sourceFile": "plugins/bmwblog/article.js" + "modulePath": "plugins/bloomberg/feeds.js", + "sourceFile": "plugins/bloomberg/feeds.js" }, { - "site": "bmwblog", - "name": "latest", - "description": "List the latest BMWBLOG articles", + "site": "bloomberg", + "name": "green", + "description": "Bloomberg Green (climate & energy) top stories (RSS)", "access": "read", - "domain": "www.bmwblog.com", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Number of articles (1-50)" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", "title", - "date", - "author", - "section", - "excerpt", - "url" + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bmwblog/latest.js", - "sourceFile": "plugins/bmwblog/latest.js" + "modulePath": "plugins/bloomberg/green.js", + "sourceFile": "plugins/bloomberg/green.js" }, { - "site": "bmwblog", - "name": "search", - "description": "Search BMWBLOG articles", + "site": "bloomberg", + "name": "industries", + "description": "Bloomberg Industries top stories (RSS)", "access": "read", - "domain": "www.bmwblog.com", + "domain": "feeds.bloomberg.com", "strategy": "public", "browser": false, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 1, "required": false, - "help": "Number of results (1-50)" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", "title", - "date", - "author", - "section", - "excerpt", - "url" - ], - "tags": [ - "search" + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/bmwblog/search.js", - "sourceFile": "plugins/bmwblog/search.js" + "modulePath": "plugins/bloomberg/industries.js", + "sourceFile": "plugins/bloomberg/industries.js" }, { - "site": "booking", - "name": "search", - "description": "Search Booking.com hotels by destination and dates (server-rendered card scrape).", + "site": "bloomberg", + "name": "main", + "description": "Bloomberg homepage top stories (RSS)", "access": "read", - "example": "webcmd booking search Tokyo --checkin 2026-06-15 --checkout 2026-06-17 -f yaml", - "domain": "www.booking.com", + "domain": "feeds.bloomberg.com", "strategy": "public", - "browser": true, + "browser": false, "args": [ - { - "name": "destination", - "type": "str", - "required": true, - "positional": true, - "help": "Destination keyword (city, district, or hotel name)" - }, - { - "name": "checkin", - "type": "str", - "required": true, - "help": "Check-in date YYYY-MM-DD" - }, - { - "name": "checkout", - "type": "str", - "required": true, - "help": "Check-out date YYYY-MM-DD" - }, - { - "name": "adults", - "type": "int", - "default": 2, - "required": false, - "help": "Number of adults (1-30)" - }, - { - "name": "rooms", - "type": "int", - "default": 1, - "required": false, - "help": "Number of rooms (1-30)" - }, - { - "name": "children", - "type": "int", - "default": 0, - "required": false, - "help": "Number of children (0-10)" - }, - { - "name": "currency", - "type": "str", - "required": false, - "help": "Force result currency (e.g. USD, JPY, CNY)" - }, - { - "name": "lang", - "type": "str", - "required": false, - "help": "Force result language (e.g. en-us, zh-cn, ja)" - }, { "name": "limit", "type": "int", - "default": 25, - "required": false, - "help": "Max rows to return (1-100; Booking pages 25 per request)" - }, - { - "name": "offset", - "type": "int", - "default": 0, + "default": 1, "required": false, - "help": "Result offset for pagination (multiple of 25)" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", - "name", - "country", - "slug", - "star_rating", - "review_score", - "review_count", - "price_amount", - "price_currency", - "distance", - "recommended_room", - "url" - ], - "tags": [ - "search" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/booking/search.js", - "sourceFile": "plugins/booking/search.js" + "modulePath": "plugins/bloomberg/main.js", + "sourceFile": "plugins/bloomberg/main.js" }, { - "site": "brave", - "name": "search", - "description": "Search Brave Search", + "site": "bloomberg", + "name": "markets", + "description": "Bloomberg Markets top stories (RSS)", "access": "read", - "domain": "search.brave.com", + "domain": "feeds.bloomberg.com", "strategy": "public", - "browser": true, + "browser": false, "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, { "name": "limit", "type": "int", - "default": 10, - "required": false, - "help": "Number of results per page (max 18)" - }, - { - "name": "offset", - "type": "int", - "default": 0, + "default": 1, "required": false, - "help": "Page offset (0, 1, 2...). Brave returns ~18 results per page" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "rank", "title", - "url", - "snippet" - ], - "tags": [ - "search" + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/brave/search.js", - "sourceFile": "plugins/brave/search.js" + "modulePath": "plugins/bloomberg/markets.js", + "sourceFile": "plugins/bloomberg/markets.js" }, { - "site": "chatgpt", - "name": "ask", - "description": "Send a prompt to ChatGPT web and wait for the response", - "access": "write", - "domain": "chatgpt.com", + "site": "bloomberg", + "name": "news", + "description": "Read a Bloomberg story/article page and return title, full content, and media links", + "access": "read", + "domain": "www.bloomberg.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", + "name": "link", "type": "str", "required": true, "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait for response" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Continue an existing ChatGPT conversation ID or /c/ URL" - }, - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" - }, - { - "name": "wait", - "type": "boolean", - "default": true, - "required": false, - "help": "Wait for the assistant response after sending" - }, - { - "name": "deep-research", - "type": "boolean", - "default": false, - "required": false, - "help": "Enable ChatGPT Deep Research (Deep Research)" - }, - { - "name": "web-search", - "type": "boolean", - "default": false, - "required": false, - "help": "Enable ChatGPT Web Search (Web Search)" + "help": "Bloomberg story/article URL or relative Bloomberg path" } ], "columns": [ - "conversationId", - "conversationUrl", - "tool", - "response" + "title", + "summary", + "link", + "mediaLinks", + "content" ], "type": "js", - "modulePath": "plugins/chatgpt/ask.js", - "sourceFile": "plugins/chatgpt/ask.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/bloomberg/news.js", + "sourceFile": "plugins/bloomberg/news.js", + "navigateBefore": "https://www.bloomberg.com" }, { - "site": "chatgpt", - "name": "deep-research-result", - "description": "Read a ChatGPT Deep Research report or progress from the conversation payload", + "site": "bloomberg", + "name": "opinions", + "description": "Bloomberg Opinion top stories (RSS)", "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Conversation ID or full /c/ URL" - }, - { - "name": "wait", - "type": "boolean", - "default": false, - "required": false, - "help": "Wait until Deep Research completes or becomes extractable" - }, - { - "name": "timeout", + "name": "limit", "type": "int", - "default": 120, + "default": 1, "required": false, - "help": "Max seconds to wait when --wait is true" - }, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "plugins/bloomberg/opinions.js", + "sourceFile": "plugins/bloomberg/opinions.js" + }, + { + "site": "bloomberg", + "name": "politics", + "description": "Bloomberg Politics top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ { - "name": "stable", + "name": "limit", "type": "int", - "default": 6, + "default": 1, "required": false, - "help": "Seconds the report text must remain unchanged when --wait is true" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "conversationId", - "status", - "report", - "sources", - "progress", - "asyncTaskConversationId", - "widgetSessionId", - "asyncStatus", - "venusMessageType", - "venusStatus", - "waitingForUserUntil", - "planTitle", - "planId", - "url", - "method", - "diagnostics" - ], - "tags": [ - "search" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/chatgpt/deep-research-result.js", - "sourceFile": "plugins/chatgpt/deep-research-result.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/bloomberg/politics.js", + "sourceFile": "plugins/bloomberg/politics.js" }, { - "site": "chatgpt", - "name": "detail", - "description": "Open a ChatGPT web conversation by ID and read its messages", + "site": "bloomberg", + "name": "pursuits", + "description": "Bloomberg Pursuits (lifestyle) top stories (RSS)", "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Conversation ID or full /c/ URL" - }, - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" - }, - { - "name": "wait", - "type": "boolean", - "default": false, - "required": false, - "help": "Wait until the conversation stops generating and stabilizes" - }, - { - "name": "timeout", + "name": "limit", "type": "int", - "default": 120, + "default": 1, "required": false, - "help": "Max seconds to wait when --wait is true" - }, + "help": "Number of feed items to return (max 20)" + } + ], + "columns": [ + "title", + "summary", + "link", + "mediaLinks" + ], + "type": "js", + "modulePath": "plugins/bloomberg/pursuits.js", + "sourceFile": "plugins/bloomberg/pursuits.js" + }, + { + "site": "bloomberg", + "name": "tech", + "description": "Bloomberg Tech top stories (RSS)", + "access": "read", + "domain": "feeds.bloomberg.com", + "strategy": "public", + "browser": false, + "args": [ { - "name": "stable", + "name": "limit", "type": "int", - "default": 6, + "default": 1, "required": false, - "help": "Seconds the final messages must remain unchanged when --wait is true" + "help": "Number of feed items to return (max 20)" } ], "columns": [ - "Index", - "Role", - "Text", - "Generating", - "StableSeconds" + "title", + "summary", + "link", + "mediaLinks" ], "type": "js", - "modulePath": "plugins/chatgpt/detail.js", - "sourceFile": "plugins/chatgpt/detail.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/bloomberg/tech.js", + "sourceFile": "plugins/bloomberg/tech.js" }, { - "site": "chatgpt", - "name": "history", - "description": "List visible ChatGPT web conversation history from the sidebar", + "site": "bluesky", + "name": "feeds", + "description": "Popular Bluesky feed generators", "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, "args": [ { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max conversations to show" + "help": "Number of feeds" } ], "columns": [ - "Index", - "Id", - "Title", - "Url" + "rank", + "name", + "likes", + "creator", + "description" ], "type": "js", - "modulePath": "plugins/chatgpt/history.js", - "sourceFile": "plugins/chatgpt/history.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/bluesky/feeds.js", + "sourceFile": "plugins/bluesky/feeds.js" }, { - "site": "chatgpt", - "name": "image", - "description": "Generate images with ChatGPT web and save them locally", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "site": "bluesky", + "name": "followers", + "description": "List followers of a Bluesky user", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, "args": [ { - "name": "prompt", + "name": "handle", "type": "str", "required": true, "positional": true, - "help": "Image prompt to send to ChatGPT" + "help": "Bluesky handle" }, { - "name": "image", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Local image path to attach before prompting; comma-separated paths are supported", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - }, + "help": "Number of followers" + } + ], + "columns": [ + "rank", + "handle", + "name", + "description" + ], + "type": "js", + "modulePath": "plugins/bluesky/followers.js", + "sourceFile": "plugins/bluesky/followers.js" + }, + { + "site": "bluesky", + "name": "following", + "description": "List accounts a Bluesky user is following", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ { - "name": "project", + "name": "handle", "type": "str", - "required": false, - "valueRequired": true, - "help": "Start image generation inside a ChatGPT project ID or /g/g-p- URL" + "required": true, + "positional": true, + "help": "Bluesky handle" }, { - "name": "op", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Output directory (default: ~/Pictures/chatgpt)", - "file": { - "direction": "output", - "pathKind": "directory", - "multiple": false, - "defaultPath": "~/Pictures/chatgpt" - } - }, - { - "name": "sd", - "type": "boolean", - "default": false, - "required": false, - "help": "Skip download shorthand; only show ChatGPT link" - }, - { - "name": "timeout", - "type": "int", - "default": 240, - "required": false, - "help": "Max seconds for the overall command (default: 240)" + "help": "Number of accounts" } ], "columns": [ - "status", - "file", - "link" + "rank", + "handle", + "name", + "description" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/chatgpt/image.js", - "sourceFile": "plugins/chatgpt/image.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/bluesky/following.js", + "sourceFile": "plugins/bluesky/following.js" }, { - "site": "chatgpt", - "name": "login", - "description": "Open chatgpt login", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [], + "site": "bluesky", + "name": "profile", + "description": "Get Bluesky user profile info", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle (e.g. bsky.app, jay.bsky.team)" + } + ], "columns": [ - "status", - "logged_in", - "site", - "user_id", + "handle", "name", - "action", - "verify_command" + "followers", + "following", + "posts", + "description" ], "type": "js", - "modulePath": "plugins/chatgpt/auth.js", - "sourceFile": "plugins/chatgpt/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/bluesky/profile.js", + "sourceFile": "plugins/bluesky/profile.js" }, { - "site": "chatgpt", - "name": "model", - "description": "Switch ChatGPT web model or intelligence level (GPT-5.6 Pro, fast, balanced, advanced, very-high, pro)", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "site": "bluesky", + "name": "search", + "description": "Search Bluesky users", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, "args": [ { - "name": "model", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "ChatGPT model or intelligence level to switch to", - "choices": [ - "fast", - "speed", - "instant", - "balanced", - "balance", - "medium", - "advanced", - "high", - "thinking", - "very-high", - "ultra", - "xhigh", - "x-high", - "extra-high", - "very high", - "gpt-5.6-pro", - "gpt-5-6-pro", - "gpt-5.6-sol-pro", - "gpt-5-6-sol-pro", - "gpt-5.6", - "gpt-5-6", - "5.6-pro", - "5.6", - "pro", - "professional" - ] + "help": "Search query" }, { - "name": "project", - "type": "str", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "valueRequired": true, - "help": "Open a ChatGPT project ID or /g/g-p- URL before switching intelligence level" + "help": "Number of results" } ], "columns": [ - "Status", - "Model" + "rank", + "handle", + "name", + "followers", + "description" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/chatgpt/model.js", - "sourceFile": "plugins/chatgpt/model.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/bluesky/search.js", + "sourceFile": "plugins/bluesky/search.js" }, { - "site": "chatgpt", - "name": "new", - "description": "Start a new ChatGPT web conversation", + "site": "bluesky", + "name": "starter-packs", + "description": "Get starter packs created by a Bluesky user", "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, "args": [ { - "name": "project", + "name": "handle", "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle" + }, + { + "name": "limit", + "type": "int", + "default": 10, "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + "help": "Number of starter packs" } ], "columns": [ - "Status" + "rank", + "name", + "description", + "members", + "joins" ], "type": "js", - "modulePath": "plugins/chatgpt/new.js", - "sourceFile": "plugins/chatgpt/new.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/bluesky/starter-packs.js", + "sourceFile": "plugins/bluesky/starter-packs.js" }, { - "site": "chatgpt", - "name": "project-file-add", - "description": "Upload files to a ChatGPT project as project knowledge (not just conversation attachments)", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "site": "bluesky", + "name": "thread", + "description": "Get a Bluesky post thread with replies", + "access": "read", + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, "args": [ { - "name": "file", + "name": "uri", "type": "str", "required": true, "positional": true, - "help": "Local file path(s) to upload; comma-separated paths are supported", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "application/pdf", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } + "help": "Post AT URI (at://did:.../app.bsky.feed.post/...) or bsky.app URL" }, { - "name": "id", - "type": "str", - "required": true, - "help": "Project ID or /g/g-p- URL" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of replies" } ], "columns": [ - "Status", - "File" + "author", + "text", + "likes", + "reposts", + "replies_count" ], "type": "js", - "modulePath": "plugins/chatgpt/project-file-add.js", - "sourceFile": "plugins/chatgpt/project-file-add.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/bluesky/thread.js", + "sourceFile": "plugins/bluesky/thread.js" }, { - "site": "chatgpt", - "name": "project-list", - "description": "List visible ChatGPT projects from the sidebar", + "site": "bluesky", + "name": "trending", + "description": "Trending topics on Bluesky", "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, "args": [ { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max projects to show" + "help": "Number of topics" } ], "columns": [ - "Index", - "Id", - "Title", - "Url" + "rank", + "topic", + "link" ], "type": "js", - "modulePath": "plugins/chatgpt/project-list.js", - "sourceFile": "plugins/chatgpt/project-list.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/bluesky/trending.js", + "sourceFile": "plugins/bluesky/trending.js" }, { - "site": "chatgpt", - "name": "read", - "description": "Read messages in the current ChatGPT web conversation", + "site": "bluesky", + "name": "user", + "description": "Get recent posts from a Bluesky user", "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "domain": "public.api.bsky.app", + "strategy": "public", + "browser": false, "args": [ { - "name": "markdown", - "type": "boolean", - "default": false, + "name": "handle", + "type": "str", + "required": true, + "positional": true, + "help": "Bluesky handle (e.g. bsky.app)" + }, + { + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Emit assistant replies as markdown" + "help": "Number of posts" } ], "columns": [ - "Index", - "Role", - "Text" + "rank", + "uri", + "text", + "likes", + "reposts", + "replies" ], "type": "js", - "modulePath": "plugins/chatgpt/read.js", - "sourceFile": "plugins/chatgpt/read.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/bluesky/user.js", + "sourceFile": "plugins/bluesky/user.js" }, { - "site": "chatgpt", - "name": "send", - "description": "Send a prompt to ChatGPT web without waiting for the response", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, + "site": "bmwblog", + "name": "article", + "description": "Read a BMWBLOG article by URL or slug", + "access": "read", + "domain": "www.bmwblog.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "prompt", + "name": "url-or-slug", "type": "str", "required": true, "positional": true, - "help": "Prompt to send" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Continue an existing ChatGPT conversation ID or /c/ URL" - }, - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + "help": "BMWBLOG article URL or slug" } ], "columns": [ - "Status", - "InjectedText" - ], - "type": "js", - "modulePath": "plugins/chatgpt/send.js", - "sourceFile": "plugins/chatgpt/send.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "status", - "description": "Check ChatGPT web page availability and login state", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status", - "Login", - "Url" + "title", + "date", + "author", + "sections", + "excerpt", + "url", + "content" ], "type": "js", - "modulePath": "plugins/chatgpt/status.js", - "sourceFile": "plugins/chatgpt/status.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/bmwblog/article.js", + "sourceFile": "plugins/bmwblog/article.js" }, { - "site": "chatgpt", - "name": "whoami", - "description": "Show the current logged-in chatgpt account", + "site": "bmwblog", + "name": "latest", + "description": "List the latest BMWBLOG articles", "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "plugins/chatgpt/auth.js", - "sourceFile": "plugins/chatgpt/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt-app", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", - "access": "write", - "domain": "localhost", + "domain": "www.bmwblog.com", "strategy": "public", "browser": false, "args": [ { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "model", - "type": "str", - "required": false, - "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] - }, - { - "name": "timeout", + "name": "limit", "type": "int", - "default": 30, - "required": false, - "help": "Max seconds to wait for response (default: 30)" - }, - { - "name": "image", - "type": "str", + "default": 10, "required": false, - "help": "Path to local image to attach (optional)" + "help": "Number of articles (1-50)" } ], "columns": [ - "Role", - "Text" + "rank", + "title", + "date", + "author", + "section", + "excerpt", + "url" ], "type": "js", - "modulePath": "plugins/chatgpt-app/ask.js", - "sourceFile": "plugins/chatgpt-app/ask.js" + "modulePath": "plugins/bmwblog/latest.js", + "sourceFile": "plugins/bmwblog/latest.js" }, { - "site": "chatgpt-app", - "name": "model", - "description": "Switch ChatGPT Desktop model/mode (auto, instant, thinking, 5.2-instant, 5.2-thinking)", + "site": "bmwblog", + "name": "search", + "description": "Search BMWBLOG articles", "access": "read", - "domain": "localhost", + "domain": "www.bmwblog.com", "strategy": "public", "browser": false, "args": [ { - "name": "model", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Model to switch to", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results (1-50)" } ], "columns": [ - "Status", - "Model" + "rank", + "title", + "date", + "author", + "section", + "excerpt", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/chatgpt-app/model.js", - "sourceFile": "plugins/chatgpt-app/model.js" + "modulePath": "plugins/bmwblog/search.js", + "sourceFile": "plugins/bmwblog/search.js" }, { - "site": "chatgpt-app", - "name": "new", - "description": "Open a new chat in ChatGPT Desktop App", - "access": "write", - "domain": "localhost", + "site": "booking", + "name": "search", + "description": "Search Booking.com hotels by destination and dates (server-rendered card scrape).", + "access": "read", + "example": "webcmd booking search Tokyo --checkin 2026-06-15 --checkout 2026-06-17 -f yaml", + "domain": "www.booking.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "temp", - "type": "boolean", - "default": false, + "name": "destination", + "type": "str", + "required": true, + "positional": true, + "help": "Destination keyword (city, district, or hotel name)" + }, + { + "name": "checkin", + "type": "str", + "required": true, + "help": "Check-in date YYYY-MM-DD" + }, + { + "name": "checkout", + "type": "str", + "required": true, + "help": "Check-out date YYYY-MM-DD" + }, + { + "name": "adults", + "type": "int", + "default": 2, "required": false, - "help": "Open a temporary chat with privacy protection" + "help": "Number of adults (1-30)" + }, + { + "name": "rooms", + "type": "int", + "default": 1, + "required": false, + "help": "Number of rooms (1-30)" + }, + { + "name": "children", + "type": "int", + "default": 0, + "required": false, + "help": "Number of children (0-10)" + }, + { + "name": "currency", + "type": "str", + "required": false, + "help": "Force result currency (e.g. USD, JPY, CNY)" + }, + { + "name": "lang", + "type": "str", + "required": false, + "help": "Force result language (e.g. en-us, zh-cn, ja)" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max rows to return (1-100; Booking pages 25 per request)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Result offset for pagination (multiple of 25)" } ], "columns": [ - "Status" + "rank", + "name", + "country", + "slug", + "star_rating", + "review_score", + "review_count", + "price_amount", + "price_currency", + "distance", + "recommended_room", + "url" ], - "type": "js", - "modulePath": "plugins/chatgpt-app/new.js", - "sourceFile": "plugins/chatgpt-app/new.js" - }, - { - "site": "chatgpt-app", - "name": "read", - "description": "Read the last visible message from the focused ChatGPT Desktop window", - "access": "read", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "Role", - "Text" + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/chatgpt-app/read.js", - "sourceFile": "plugins/chatgpt-app/read.js" + "modulePath": "plugins/booking/search.js", + "sourceFile": "plugins/booking/search.js" }, { - "site": "chatgpt-app", - "name": "send", - "description": "Send a message to the active ChatGPT Desktop App window", - "access": "write", - "domain": "localhost", + "site": "brave", + "name": "search", + "description": "Search Brave Search", + "access": "read", + "domain": "search.brave.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "text", + "name": "keyword", "type": "str", "required": true, "positional": true, - "help": "Message to send" + "help": "Search query" }, { - "name": "model", - "type": "str", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] + "help": "Number of results per page (max 18)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Page offset (0, 1, 2...). Brave returns ~18 results per page" } ], "columns": [ - "Status" + "rank", + "title", + "url", + "snippet" ], - "type": "js", - "modulePath": "plugins/chatgpt-app/send.js", - "sourceFile": "plugins/chatgpt-app/send.js" - }, - { - "site": "chatgpt-app", - "name": "status", - "description": "Check if ChatGPT Desktop App is running natively on macOS", - "access": "read", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "Status" + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/chatgpt-app/status.js", - "sourceFile": "plugins/chatgpt-app/status.js" + "modulePath": "plugins/brave/search.js", + "sourceFile": "plugins/brave/search.js" }, { - "site": "chatwise", + "site": "chatgpt", "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", + "description": "Send a prompt to ChatGPT web and wait for the response", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "chatgpt.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "text", + "name": "prompt", "type": "str", "required": true, "positional": true, @@ -3997,1357 +4076,9451 @@ { "name": "timeout", "type": "int", - "default": 30, + "default": 120, "required": false, - "help": "Max seconds to wait (default: 30)" + "help": "Max seconds to wait for response" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Continue an existing ChatGPT conversation ID or /c/ URL" + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + }, + { + "name": "wait", + "type": "boolean", + "default": true, + "required": false, + "help": "Wait for the assistant response after sending" + }, + { + "name": "deep-research", + "type": "boolean", + "default": false, + "required": false, + "help": "Enable ChatGPT Deep Research (Deep Research)" + }, + { + "name": "web-search", + "type": "boolean", + "default": false, + "required": false, + "help": "Enable ChatGPT Web Search (Web Search)" } ], "columns": [ - "Role", - "Text" + "conversationId", + "conversationUrl", + "tool", + "response" ], "type": "js", - "modulePath": "plugins/chatwise/ask.js", - "sourceFile": "plugins/chatwise/ask.js", - "navigateBefore": true + "modulePath": "plugins/chatgpt/ask.js", + "sourceFile": "plugins/chatgpt/ask.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "chatwise", - "name": "export", - "description": "Export the current ChatWise conversation to a Markdown file", + "site": "chatgpt", + "name": "deep-research-result", + "description": "Read a ChatGPT Deep Research report or progress from the conversation payload", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "chatgpt.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "output", + "name": "id", "type": "str", + "required": true, + "positional": true, + "help": "Conversation ID or full /c/ URL" + }, + { + "name": "wait", + "type": "boolean", + "default": false, "required": false, - "help": "Output file (default: /tmp/chatwise-export.md)" - } - ], + "help": "Wait until Deep Research completes or becomes extractable" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait when --wait is true" + }, + { + "name": "stable", + "type": "int", + "default": 6, + "required": false, + "help": "Seconds the report text must remain unchanged when --wait is true" + } + ], + "columns": [ + "conversationId", + "status", + "report", + "sources", + "progress", + "asyncTaskConversationId", + "widgetSessionId", + "asyncStatus", + "venusMessageType", + "venusStatus", + "waitingForUserUntil", + "planTitle", + "planId", + "url", + "method", + "diagnostics" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/chatgpt/deep-research-result.js", + "sourceFile": "plugins/chatgpt/deep-research-result.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "detail", + "description": "Open a ChatGPT web conversation by ID and read its messages", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Conversation ID or full /c/ URL" + }, + { + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" + }, + { + "name": "wait", + "type": "boolean", + "default": false, + "required": false, + "help": "Wait until the conversation stops generating and stabilizes" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait when --wait is true" + }, + { + "name": "stable", + "type": "int", + "default": 6, + "required": false, + "help": "Seconds the final messages must remain unchanged when --wait is true" + } + ], + "columns": [ + "Index", + "Role", + "Text", + "Generating", + "StableSeconds" + ], + "type": "js", + "modulePath": "plugins/chatgpt/detail.js", + "sourceFile": "plugins/chatgpt/detail.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "history", + "description": "List visible ChatGPT web conversation history from the sidebar", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max conversations to show" + } + ], + "columns": [ + "Index", + "Id", + "Title", + "Url" + ], + "type": "js", + "modulePath": "plugins/chatgpt/history.js", + "sourceFile": "plugins/chatgpt/history.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "image", + "description": "Generate images with ChatGPT web and save them locally", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Image prompt to send to ChatGPT" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Local image path to attach before prompting; comma-separated paths are supported", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start image generation inside a ChatGPT project ID or /g/g-p- URL" + }, + { + "name": "op", + "type": "str", + "required": false, + "help": "Output directory (default: ~/Pictures/chatgpt)", + "file": { + "direction": "output", + "pathKind": "directory", + "multiple": false, + "defaultPath": "~/Pictures/chatgpt" + } + }, + { + "name": "sd", + "type": "boolean", + "default": false, + "required": false, + "help": "Skip download shorthand; only show ChatGPT link" + }, + { + "name": "timeout", + "type": "int", + "default": 240, + "required": false, + "help": "Max seconds for the overall command (default: 240)" + } + ], + "columns": [ + "status", + "file", + "link" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "plugins/chatgpt/image.js", + "sourceFile": "plugins/chatgpt/image.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "login", + "description": "Open chatgpt login", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/chatgpt/auth.js", + "sourceFile": "plugins/chatgpt/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "model", + "description": "Switch ChatGPT web model or intelligence level (GPT-5.6 Pro, fast, balanced, advanced, very-high, pro)", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "model", + "type": "str", + "required": true, + "positional": true, + "help": "ChatGPT model or intelligence level to switch to", + "choices": [ + "fast", + "speed", + "instant", + "balanced", + "balance", + "medium", + "advanced", + "high", + "thinking", + "very-high", + "ultra", + "xhigh", + "x-high", + "extra-high", + "very high", + "gpt-5.6-pro", + "gpt-5-6-pro", + "gpt-5.6-sol-pro", + "gpt-5-6-sol-pro", + "gpt-5.6", + "gpt-5-6", + "5.6-pro", + "5.6", + "pro", + "professional" + ] + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Open a ChatGPT project ID or /g/g-p- URL before switching intelligence level" + } + ], + "columns": [ + "Status", + "Model" + ], + "type": "js", + "modulePath": "plugins/chatgpt/model.js", + "sourceFile": "plugins/chatgpt/model.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "new", + "description": "Start a new ChatGPT web conversation", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + } + ], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/chatgpt/new.js", + "sourceFile": "plugins/chatgpt/new.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "project-file-add", + "description": "Upload files to a ChatGPT project as project knowledge (not just conversation attachments)", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "file", + "type": "str", + "required": true, + "positional": true, + "help": "Local file path(s) to upload; comma-separated paths are supported", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "application/pdf", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + }, + { + "name": "id", + "type": "str", + "required": true, + "help": "Project ID or /g/g-p- URL" + } + ], + "columns": [ + "Status", + "File" + ], + "type": "js", + "modulePath": "plugins/chatgpt/project-file-add.js", + "sourceFile": "plugins/chatgpt/project-file-add.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "project-list", + "description": "List visible ChatGPT projects from the sidebar", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max projects to show" + } + ], + "columns": [ + "Index", + "Id", + "Title", + "Url" + ], + "type": "js", + "modulePath": "plugins/chatgpt/project-list.js", + "sourceFile": "plugins/chatgpt/project-list.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "read", + "description": "Read messages in the current ChatGPT web conversation", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" + } + ], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/chatgpt/read.js", + "sourceFile": "plugins/chatgpt/read.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "send", + "description": "Send a prompt to ChatGPT web without waiting for the response", + "access": "write", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Continue an existing ChatGPT conversation ID or /c/ URL" + }, + { + "name": "project", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" + } + ], + "columns": [ + "Status", + "InjectedText" + ], + "type": "js", + "modulePath": "plugins/chatgpt/send.js", + "sourceFile": "plugins/chatgpt/send.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "status", + "description": "Check ChatGPT web page availability and login state", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status", + "Login", + "Url" + ], + "type": "js", + "modulePath": "plugins/chatgpt/status.js", + "sourceFile": "plugins/chatgpt/status.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt", + "name": "whoami", + "description": "Show the current logged-in chatgpt account", + "access": "read", + "domain": "chatgpt.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" + ], + "type": "js", + "modulePath": "plugins/chatgpt/auth.js", + "sourceFile": "plugins/chatgpt/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "chatgpt-app", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "model", + "type": "str", + "required": false, + "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds to wait for response (default: 30)" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Path to local image to attach (optional)" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/chatgpt-app/ask.js", + "sourceFile": "plugins/chatgpt-app/ask.js" + }, + { + "site": "chatgpt-app", + "name": "model", + "description": "Switch ChatGPT Desktop model/mode (auto, instant, thinking, 5.2-instant, 5.2-thinking)", + "access": "read", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "model", + "type": "str", + "required": true, + "positional": true, + "help": "Model to switch to", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] + } + ], + "columns": [ + "Status", + "Model" + ], + "type": "js", + "modulePath": "plugins/chatgpt-app/model.js", + "sourceFile": "plugins/chatgpt-app/model.js" + }, + { + "site": "chatgpt-app", + "name": "new", + "description": "Open a new chat in ChatGPT Desktop App", + "access": "write", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "temp", + "type": "boolean", + "default": false, + "required": false, + "help": "Open a temporary chat with privacy protection" + } + ], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/chatgpt-app/new.js", + "sourceFile": "plugins/chatgpt-app/new.js" + }, + { + "site": "chatgpt-app", + "name": "read", + "description": "Read the last visible message from the focused ChatGPT Desktop window", + "access": "read", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/chatgpt-app/read.js", + "sourceFile": "plugins/chatgpt-app/read.js" + }, + { + "site": "chatgpt-app", + "name": "send", + "description": "Send a message to the active ChatGPT Desktop App window", + "access": "write", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Message to send" + }, + { + "name": "model", + "type": "str", + "required": false, + "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", + "choices": [ + "auto", + "instant", + "thinking", + "5.2-instant", + "5.2-thinking" + ] + } + ], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/chatgpt-app/send.js", + "sourceFile": "plugins/chatgpt-app/send.js" + }, + { + "site": "chatgpt-app", + "name": "status", + "description": "Check if ChatGPT Desktop App is running natively on macOS", + "access": "read", + "domain": "localhost", + "strategy": "public", + "browser": false, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/chatgpt-app/status.js", + "sourceFile": "plugins/chatgpt-app/status.js" + }, + { + "site": "chatwise", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds to wait (default: 30)" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/chatwise/ask.js", + "sourceFile": "plugins/chatwise/ask.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "export", + "description": "Export the current ChatWise conversation to a Markdown file", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file (default: /tmp/chatwise-export.md)" + } + ], + "columns": [ + "Status", + "File", + "Messages" + ], + "type": "js", + "modulePath": "plugins/chatwise/export.js", + "sourceFile": "plugins/chatwise/export.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "history", + "description": "List conversation history in ChatWise sidebar", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Title" + ], + "type": "js", + "modulePath": "plugins/chatwise/history.js", + "sourceFile": "plugins/chatwise/history.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "model", + "description": "Get or switch the active AI model in ChatWise", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "model-name", + "type": "str", + "required": false, + "positional": true, + "help": "Model to switch to (e.g. gpt-4, claude-3)" + } + ], + "columns": [ + "Status", + "Model" + ], + "type": "js", + "modulePath": "plugins/chatwise/model.js", + "sourceFile": "plugins/chatwise/model.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "new", + "description": "Start a new ChatWise conversation session", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/chatwise/new.js", + "sourceFile": "plugins/chatwise/new.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "read", + "description": "Read the current ChatWise conversation history", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Content" + ], + "type": "js", + "modulePath": "plugins/chatwise/read.js", + "sourceFile": "plugins/chatwise/read.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "screenshot", + "description": "Capture a snapshot of the current ChatWise window (DOM + Accessibility tree)", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file path (default: /tmp/chatwise-snapshot.txt)" + } + ], + "columns": [ + "Status", + "File" + ], + "type": "js", + "modulePath": "plugins/chatwise/screenshot.js", + "sourceFile": "plugins/chatwise/screenshot.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "send", + "description": "Send a message to the active ChatWise conversation", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Message to send" + } + ], + "columns": [ + "Status", + "InjectedText" + ], + "type": "js", + "modulePath": "plugins/chatwise/send.js", + "sourceFile": "plugins/chatwise/send.js", + "navigateBefore": true + }, + { + "site": "chatwise", + "name": "status", + "description": "Check active CDP connection to ChatWise Desktop", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "plugins/chatwise/status.js", + "sourceFile": "plugins/chatwise/status.js", + "navigateBefore": true + }, + { + "site": "chess", + "name": "analyze", + "description": "Open a Chess.com game in the browser analysis board", + "access": "read", + "domain": "www.chess.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "game-url", + "type": "string", + "required": true, + "positional": true, + "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" + } + ], + "columns": [ + "kind", + "game_id", + "analysis_url" + ], + "type": "js", + "modulePath": "plugins/chess/analyze.js", + "sourceFile": "plugins/chess/analyze.js", + "navigateBefore": false + }, + { + "site": "chess", + "name": "game", + "description": "Chess.com single-game detail (white, black, result, ECO, time control) by full game URL", + "access": "read", + "domain": "www.chess.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "game-url", + "type": "string", + "required": true, + "positional": true, + "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" + } + ], + "columns": [ + "kind", + "game_id", + "date", + "white", + "white_rating", + "black", + "black_rating", + "result", + "winner_color", + "termination", + "eco", + "time_control", + "rated", + "ply_count", + "url" + ], + "type": "js", + "modulePath": "plugins/chess/game.js", + "sourceFile": "plugins/chess/game.js" + }, + { + "site": "chess", + "name": "games", + "description": "Chess.com recent games for a player, newest first", + "access": "read", + "domain": "api.chess.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Chess.com username" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of recent games (1-100)" + } + ], + "columns": [ + "date", + "time_class", + "rated", + "my_color", + "my_rating", + "my_result", + "opponent", + "opponent_rating", + "accuracy_white", + "accuracy_black", + "eco", + "opening_name", + "url" + ], + "type": "js", + "modulePath": "plugins/chess/games.js", + "sourceFile": "plugins/chess/games.js" + }, + { + "site": "chess", + "name": "stats", + "description": "Chess.com player ratings + win/loss record across game kinds", + "access": "read", + "domain": "api.chess.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Chess.com username (case-insensitive)" + } + ], + "columns": [ + "kind", + "rating_current", + "rating_best", + "wins", + "losses", + "draws" + ], + "type": "js", + "modulePath": "plugins/chess/stats.js", + "sourceFile": "plugins/chess/stats.js" + }, + { + "site": "cincinnati", + "name": "export-postgraduate-courses", + "description": "Export University of Cincinnati graduate and professional programs from official public sources.", + "access": "read", + "example": "webcmd cincinnati export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.grad.uc.edu", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/cincinnati/export-postgraduate-courses.js", + "sourceFile": "plugins/cincinnati/export-postgraduate-courses.js" + }, + { + "site": "claude", + "name": "ask", + "description": "Send a prompt to Claude and get the response", + "access": "write", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait for response" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" + }, + { + "name": "model", + "type": "str", + "default": "sonnet", + "required": false, + "help": "Model to use: sonnet, opus, or haiku", + "choices": [ + "sonnet", + "opus", + "haiku" + ] + }, + { + "name": "think", + "type": "boolean", + "default": false, + "required": false, + "help": "Enable Adaptive thinking" + }, + { + "name": "file", + "type": "str", + "required": false, + "help": "Attach a file (image, PDF, text) with the prompt", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "application/pdf", + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + } + ], + "columns": [ + "response" + ], + "type": "js", + "modulePath": "plugins/claude/ask.js", + "sourceFile": "plugins/claude/ask.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "detail", + "description": "Open a Claude conversation by ID and read its messages", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Conversation ID (UUID from /chat/)" + } + ], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/claude/detail.js", + "sourceFile": "plugins/claude/detail.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "history", + "description": "List conversation history from Claude /recents", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max conversations to show" + } + ], + "columns": [ + "Index", + "Id", + "Title", + "Url" + ], + "type": "js", + "modulePath": "plugins/claude/history.js", + "sourceFile": "plugins/claude/history.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "login", + "description": "Open claude login", + "access": "write", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "org_name", + "org_uuid", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/claude/auth.js", + "sourceFile": "plugins/claude/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "new", + "description": "Start a new conversation in Claude", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/claude/new.js", + "sourceFile": "plugins/claude/new.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "read", + "description": "Read the current Claude conversation", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/claude/read.js", + "sourceFile": "plugins/claude/read.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "send", + "description": "Send a prompt to Claude without waiting for the response", + "access": "write", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" + } + ], + "columns": [ + "Status", + "SubmittedBy", + "InjectedText" + ], + "type": "js", + "modulePath": "plugins/claude/send.js", + "sourceFile": "plugins/claude/send.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "status", + "description": "Check Claude page availability and login state", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status", + "Login", + "Url" + ], + "type": "js", + "modulePath": "plugins/claude/status.js", + "sourceFile": "plugins/claude/status.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "claude", + "name": "whoami", + "description": "Show the current logged-in claude account", + "access": "read", + "domain": "claude.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "org_name", + "org_uuid" + ], + "type": "js", + "modulePath": "plugins/claude/auth.js", + "sourceFile": "plugins/claude/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "codex", + "name": "archive", + "description": "Archive (Codex's term for delete) the selected conversation via the Chat actions header menu. No confirmation in UI — pass --yes to actually archive.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "yes", + "type": "boolean", + "default": false, + "required": false, + "help": "Actually archive (default: dry-run preview)" + }, + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" + } + ], + "columns": [ + "status", + "thread_id", + "project", + "conversation" + ], + "type": "js", + "modulePath": "plugins/codex/archive.js", + "sourceFile": "plugins/codex/archive.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "ask", + "description": "Send a prompt to the current or selected Codex conversation and wait for the AI response", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 60, + "required": false, + "help": "Max seconds to wait for response (default: 60)" + }, + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" + } + ], + "columns": [ + "Role", + "Project", + "Conversation", + "Text" + ], + "type": "js", + "modulePath": "plugins/codex/ask.js", + "sourceFile": "plugins/codex/ask.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "dump", + "description": "Dump the DOM and Accessibility tree of codex for reverse-engineering", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "action", + "files" + ], + "type": "js", + "modulePath": "plugins/codex/dump.js", + "sourceFile": "plugins/codex/dump.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "export", + "description": "Export the current Codex conversation to a Markdown file", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file (default: /tmp/codex-export.md)" + } + ], + "columns": [ + "Status", + "File", + "Messages" + ], + "type": "js", + "modulePath": "plugins/codex/export.js", + "sourceFile": "plugins/codex/export.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "extract-diff", + "description": "Extract visual code review diff patches from Codex", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "File", + "Diff" + ], + "type": "js", + "modulePath": "plugins/codex/extract-diff.js", + "sourceFile": "plugins/codex/extract-diff.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "history", + "description": "List visible Codex conversation threads grouped by project", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "project", + "type": "str", + "required": false, + "help": "Filter by project label or path" + }, + { + "name": "limit", + "type": "str", + "required": false, + "help": "Max conversations per project" + } + ], + "columns": [ + "Project", + "Index", + "Title", + "Updated", + "Active" + ], + "type": "js", + "modulePath": "plugins/codex/history.js", + "sourceFile": "plugins/codex/history.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "model", + "description": "Read, list, or switch the active model / reasoning level in Codex Desktop. The composer toolbar button toggles a menu that mixes model variants (GPT-5.5, Speed) with reasoning levels (Low/Medium/High/Extra High).", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": false, + "positional": true, + "help": "Substring (case-insensitive) of a model / reasoning level to switch to. Omit to read current." + }, + { + "name": "list", + "type": "boolean", + "default": false, + "required": false, + "help": "List all menu options (does not switch)" + } + ], + "columns": [ + "Status", + "Model" + ], + "type": "js", + "modulePath": "plugins/codex/model.js", + "sourceFile": "plugins/codex/model.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "new", + "description": "Start a new Codex conversation session", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/codex/new.js", + "sourceFile": "plugins/codex/new.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "pin", + "description": "Pin the selected Codex conversation via the Chat actions header menu.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" + } + ], + "columns": [ + "status", + "thread_id", + "project", + "conversation" + ], + "type": "js", + "modulePath": "plugins/codex/pin.js", + "sourceFile": "plugins/codex/pin.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "projects", + "description": "List Codex projects and visible conversations from the sidebar", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "project", + "type": "str", + "required": false, + "help": "Filter by project label or path" + }, + { + "name": "limit", + "type": "str", + "required": false, + "help": "Max conversations per project" + } + ], + "columns": [ + "Project", + "Index", + "Title", + "Updated", + "Active" + ], + "type": "js", + "modulePath": "plugins/codex/projects.js", + "sourceFile": "plugins/codex/projects.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "read", + "description": "Read the contents of the current or selected Codex conversation thread", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" + } + ], + "columns": [ + "Project", + "Conversation", + "Content" + ], + "type": "js", + "modulePath": "plugins/codex/read.js", + "sourceFile": "plugins/codex/read.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "rename", + "description": "Rename the selected Codex conversation. Opens the Chat actions menu → \"Rename chat\", then types the new title.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "title", + "type": "str", + "required": true, + "positional": true, + "help": "New title (single line, no newlines)" + }, + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" + } + ], + "columns": [ + "status", + "title", + "thread_id", + "project" + ], + "type": "js", + "modulePath": "plugins/codex/rename.js", + "sourceFile": "plugins/codex/rename.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "screenshot", + "description": "Capture a snapshot of the current Codex window (DOM + Accessibility tree)", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file path (default: /tmp/codex-snapshot.txt)" + } + ], + "columns": [ + "Status", + "File" + ], + "type": "js", + "modulePath": "plugins/codex/screenshot.js", + "sourceFile": "plugins/codex/screenshot.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "send", + "description": "Send text/commands to the current or selected Codex AI composer", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Text, command (e.g. /review), or skill (e.g. $imagegen)" + }, + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" + } + ], + "columns": [ + "Status", + "Project", + "Conversation", + "InjectedText" + ], + "type": "js", + "modulePath": "plugins/codex/send.js", + "sourceFile": "plugins/codex/send.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "status", + "description": "Check active CDP connection to OpenAI Codex App", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "plugins/codex/status.js", + "sourceFile": "plugins/codex/status.js", + "navigateBefore": true + }, + { + "site": "codex", + "name": "unpin", + "description": "Unpin the selected Codex conversation via the Chat actions header menu.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "project", + "type": "str", + "required": false, + "help": "Project label or path to select before running the command" + }, + { + "name": "conversation", + "type": "str", + "required": false, + "help": "Conversation title to select within --project" + }, + { + "name": "index", + "type": "str", + "required": false, + "help": "1-based conversation index within --project" + }, + { + "name": "thread-id", + "type": "str", + "required": false, + "help": "Exact Codex thread id to select" + } + ], + "columns": [ + "status", + "thread_id", + "project", + "conversation" + ], + "type": "js", + "modulePath": "plugins/codex/pin.js", + "sourceFile": "plugins/codex/pin.js", + "navigateBefore": true + }, + { + "site": "coingecko", + "name": "categories", + "description": "Crypto categories ranked by aggregated market cap", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "sort", + "type": "str", + "default": "market_cap_desc", + "required": false, + "help": "Sort order (market_cap_desc / market_cap_asc / name_desc / name_asc / market_cap_change_24h_desc / market_cap_change_24h_asc)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of categories (1-100; CoinGecko returns ~120 max)" + } + ], + "columns": [ + "rank", + "id", + "name", + "marketCap", + "volume24h", + "marketCapChange24hPct", + "top3Coins" + ], + "type": "js", + "modulePath": "plugins/coingecko/categories.js", + "sourceFile": "plugins/coingecko/categories.js" + }, + { + "site": "coingecko", + "name": "coin", + "description": "Fetch a single cryptocurrency's market data by CoinGecko id (e.g. bitcoin, ethereum).", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "CoinGecko coin id (lowercase, e.g. bitcoin / ethereum / solana)." + }, + { + "name": "currency", + "type": "string", + "default": "usd", + "required": false, + "help": "Quote currency (usd, cny, eur, jpy, ...)." + } + ], + "columns": [ + "id", + "symbol", + "name", + "rank", + "price", + "marketCap", + "volume24h", + "change24hPct", + "change7dPct", + "change30dPct", + "ath", + "athDate", + "atl", + "atlDate", + "circulatingSupply", + "totalSupply", + "maxSupply", + "genesisDate", + "homepage" + ], + "type": "js", + "modulePath": "plugins/coingecko/coin.js", + "sourceFile": "plugins/coingecko/coin.js" + }, + { + "site": "coingecko", + "name": "derivatives", + "description": "Top crypto derivative (perpetual / futures) markets by 24h volume", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max rows to return (1-500; CoinGecko returns one large page)." + }, + { + "name": "symbol", + "type": "string", + "required": false, + "help": "Optional symbol substring filter (e.g. \"BTC\", \"ETHUSDT\")." + } + ], + "columns": [ + "rank", + "market", + "symbol", + "indexId", + "contractType", + "price", + "change24hPct", + "fundingRate", + "openInterestUsd", + "volume24hUsd", + "expired" + ], + "type": "js", + "modulePath": "plugins/coingecko/derivatives.js", + "sourceFile": "plugins/coingecko/derivatives.js" + }, + { + "site": "coingecko", + "name": "exchanges", + "description": "Top crypto exchanges by 24h BTC trading volume", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of exchanges (1-250, CoinGecko per_page upper bound)" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number (1-based)" + } + ], + "columns": [ + "rank", + "id", + "name", + "trustScore", + "volume24hBtc", + "country", + "yearEstablished", + "url" + ], + "type": "js", + "modulePath": "plugins/coingecko/exchanges.js", + "sourceFile": "plugins/coingecko/exchanges.js" + }, + { + "site": "coingecko", + "name": "global", + "description": "Aggregate crypto market stats: total market cap, volume, dominance", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "currency", + "type": "string", + "default": "usd", + "required": false, + "help": "Quote currency for total market cap / volume (usd, cny, eur, jpy, ...)" + } + ], + "columns": [ + "currency", + "totalMarketCap", + "totalVolume24h", + "marketCapChange24hPct", + "btcDominancePct", + "ethDominancePct", + "activeCryptocurrencies", + "markets", + "ongoingIcos", + "updatedAt" + ], + "type": "js", + "modulePath": "plugins/coingecko/global.js", + "sourceFile": "plugins/coingecko/global.js" + }, + { + "site": "coingecko", + "name": "top", + "description": "Cryptocurrency quotes by market cap (default USD)", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "currency", + "type": "string", + "default": "usd", + "required": false, + "help": "quote currency (usd / cny / eur / jpy ...)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number to return (default 10, maximum 250)" + } + ], + "columns": [ + "rank", + "symbol", + "name", + "price", + "change24hPct", + "marketCap", + "volume24h", + "high24h", + "low24h" + ], + "type": "js", + "modulePath": "plugins/coingecko/top.js", + "sourceFile": "plugins/coingecko/top.js" + }, + { + "site": "coingecko", + "name": "trending", + "description": "Top trending cryptocurrencies on CoinGecko in the last 24h (search-volume based).", + "access": "read", + "domain": "api.coingecko.com", + "strategy": "public", + "browser": false, + "args": [], + "columns": [ + "rank", + "id", + "symbol", + "name", + "marketCapRank", + "priceBtc", + "thumb" + ], + "type": "js", + "modulePath": "plugins/coingecko/trending.js", + "sourceFile": "plugins/coingecko/trending.js" + }, + { + "site": "concordia", + "name": "export-postgraduate-courses", + "description": "Export Concordia University Montreal postgraduate programs using official public sources.", + "access": "read", + "example": "webcmd concordia export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.concordia.ca", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/concordia/export-postgraduate-courses.js", + "sourceFile": "plugins/concordia/export-postgraduate-courses.js" + }, + { + "site": "confluence", + "name": "create", + "description": "Create a Confluence page from Markdown or storage XHTML", + "access": "write", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "space", + "type": "string", + "required": true, + "help": "Cloud space id, or Data Center space key" + }, + { + "name": "title", + "type": "string", + "required": true, + "help": "Page title" + }, + { + "name": "file", + "type": "string", + "required": true, + "help": "Markdown file path" + }, + { + "name": "parent", + "type": "string", + "required": false, + "help": "Optional parent page id" + }, + { + "name": "representation", + "type": "string", + "default": "markdown", + "required": false, + "help": "Input file format", + "choices": [ + "markdown", + "storage" + ] + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually create the remote page" + } + ], + "columns": [ + "status", + "id", + "title", + "spaceId", + "spaceKey", + "version", + "url" + ], + "type": "js", + "modulePath": "plugins/confluence/create.js", + "sourceFile": "plugins/confluence/create.js" + }, + { + "site": "confluence", + "name": "page", + "description": "Confluence page by id with storage and Markdown body", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Confluence page id" + } + ], + "columns": [ + "id", + "title", + "status", + "spaceId", + "spaceKey", + "version", + "url" + ], + "type": "js", + "modulePath": "plugins/confluence/page.js", + "sourceFile": "plugins/confluence/page.js" + }, + { + "site": "confluence", + "name": "search", + "description": "Search Confluence content with CQL", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "cql", + "type": "str", + "required": true, + "positional": true, + "help": "CQL query, e.g. \"type = page and title ~ \\\"RCA\\\"\"" + }, + { + "name": "space", + "type": "string", + "required": false, + "help": "Limit search to a Confluence space key" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results to return (1-100)" + } + ], + "columns": [ + "id", + "title", + "type", + "spaceKey", + "status", + "lastModified", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/confluence/search.js", + "sourceFile": "plugins/confluence/search.js" + }, + { + "site": "confluence", + "name": "update", + "description": "Update a Confluence page body from Markdown or storage XHTML", + "access": "write", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Confluence page id" + }, + { + "name": "file", + "type": "string", + "required": true, + "help": "Markdown file path" + }, + { + "name": "title", + "type": "string", + "required": false, + "help": "Optional replacement title; defaults to current title" + }, + { + "name": "version-message", + "type": "string", + "required": false, + "help": "Confluence version message" + }, + { + "name": "representation", + "type": "string", + "default": "markdown", + "required": false, + "help": "Input file format", + "choices": [ + "markdown", + "storage" + ] + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually update the remote page" + } + ], + "columns": [ + "status", + "id", + "title", + "spaceId", + "spaceKey", + "version", + "url" + ], + "type": "js", + "modulePath": "plugins/confluence/update.js", + "sourceFile": "plugins/confluence/update.js" + }, + { + "site": "coupang", + "name": "add-to-cart", + "description": "Add a Coupang product to cart using logged-in browser session", + "access": "write", + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product-id", + "type": "str", + "required": false, + "positional": true, + "help": "Coupang product ID" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Canonical product URL" + } + ], + "columns": [ + "ok", + "product_id", + "url", + "message" + ], + "type": "js", + "modulePath": "plugins/coupang/add-to-cart.js", + "sourceFile": "plugins/coupang/add-to-cart.js", + "navigateBefore": "https://www.coupang.com" + }, + { + "site": "coupang", + "name": "login", + "description": "Open coupang login", + "access": "write", + "domain": "coupang.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/coupang/auth.js", + "sourceFile": "plugins/coupang/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "coupang", + "name": "product", + "description": "Read full product detail (price, rating, seller, delivery) for a Coupang product", + "access": "read", + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "product-id", + "type": "str", + "required": false, + "positional": true, + "help": "Coupang product ID (digits only)" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Canonical Coupang product URL (alternative to --product-id)" + } + ], + "columns": [ + "product_id", + "title", + "price", + "original_price", + "discount_rate", + "rating", + "review_count", + "seller", + "brand", + "rocket", + "delivery_promise", + "image_url", + "url" + ], + "type": "js", + "modulePath": "plugins/coupang/product.js", + "sourceFile": "plugins/coupang/product.js", + "navigateBefore": "https://www.coupang.com" + }, + { + "site": "coupang", + "name": "search", + "description": "Search Coupang products with logged-in browser session", + "access": "read", + "domain": "www.coupang.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Search result page number" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (max 50)" + }, + { + "name": "filter", + "type": "str", + "required": false, + "help": "Optional search filter (currently supports: rocket)" + } + ], + "columns": [ + "rank", + "product_id", + "title", + "price", + "unit_price", + "rating", + "review_count", + "rocket", + "delivery_type", + "delivery_promise", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/coupang/search.js", + "sourceFile": "plugins/coupang/search.js", + "navigateBefore": "https://www.coupang.com" + }, + { + "site": "coupang", + "name": "whoami", + "description": "Show the current logged-in coupang account", + "access": "read", + "domain": "coupang.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name" + ], + "type": "js", + "modulePath": "plugins/coupang/auth.js", + "sourceFile": "plugins/coupang/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "crates", + "name": "crate", + "description": "Single crates.io crate metadata (latest version, downloads, license, repo)", + "access": "read", + "domain": "crates.io", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "crates.io crate name (e.g. \"serde\", \"tokio\")" + } + ], + "columns": [ + "name", + "latestVersion", + "description", + "downloads", + "recentDownloads", + "versions", + "license", + "homepage", + "documentation", + "repository", + "keywords", + "categories", + "created", + "updated", + "url" + ], + "type": "js", + "modulePath": "plugins/crates/crate.js", + "sourceFile": "plugins/crates/crate.js" + }, + { + "site": "crates", + "name": "search", + "description": "Search the public crates.io registry by keyword", + "access": "read", + "domain": "crates.io", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"serde\", \"async runtime\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + } + ], + "columns": [ + "rank", + "name", + "latestVersion", + "description", + "downloads", + "recentDownloads", + "repository", + "updated", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/crates/search.js", + "sourceFile": "plugins/crates/search.js" + }, + { + "site": "cursor", + "name": "ask", + "description": "Send a prompt and wait for the AI response (send + wait + read)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Max seconds to wait for response (default: 30)" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/cursor/ask.js", + "sourceFile": "plugins/cursor/ask.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "composer", + "description": "Send a prompt directly into Cursor Composer (Cmd+I shortcut)", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Text to send into Composer" + } + ], + "columns": [ + "Status", + "InjectedText" + ], + "type": "js", + "modulePath": "plugins/cursor/composer.js", + "sourceFile": "plugins/cursor/composer.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "dump", + "description": "Dump the DOM and Accessibility tree of cursor for reverse-engineering", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "action", + "files" + ], + "type": "js", + "modulePath": "plugins/cursor/dump.js", + "sourceFile": "plugins/cursor/dump.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "export", + "description": "Export the current cursor conversation to a Markdown file", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file (default: /tmp/cursor-export.md)" + } + ], + "columns": [ + "Status", + "File", + "Messages" + ], + "type": "js", + "modulePath": "plugins/cursor/export.js", + "sourceFile": "plugins/cursor/export.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "extract-code", + "description": "Extract multi-line code blocks from the current Cursor conversation", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Code" + ], + "type": "js", + "modulePath": "plugins/cursor/extract-code.js", + "sourceFile": "plugins/cursor/extract-code.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "history", + "description": "List recent chat sessions from the Cursor sidebar", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Title" + ], + "type": "js", + "modulePath": "plugins/cursor/history.js", + "sourceFile": "plugins/cursor/history.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "model", + "description": "Get or switch the currently active AI model in Cursor", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "model-name", + "type": "str", + "required": false, + "positional": true, + "help": "The ID of the model to switch to (e.g. claude-3.5-sonnet)" + } + ], + "columns": [ + "Status", + "Model" + ], + "type": "js", + "modulePath": "plugins/cursor/model.js", + "sourceFile": "plugins/cursor/model.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "new", + "description": "Start a new Cursor chat or Composer session", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/cursor/new.js", + "sourceFile": "plugins/cursor/new.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "read", + "description": "Read the current Cursor chat/composer conversation history", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/cursor/read.js", + "sourceFile": "plugins/cursor/read.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "screenshot", + "description": "Capture a snapshot of the current cursor window (DOM + Accessibility tree)", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "output", + "type": "str", + "required": false, + "help": "Output file path (default: /tmp/cursor-snapshot.txt)" + } + ], + "columns": [ + "Status", + "File" + ], + "type": "js", + "modulePath": "plugins/cursor/screenshot.js", + "sourceFile": "plugins/cursor/screenshot.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "send", + "description": "Send a prompt directly into Cursor Composer/Chat", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Text to send into Cursor" + } + ], + "columns": [ + "Status", + "InjectedText" + ], + "type": "js", + "modulePath": "plugins/cursor/send.js", + "sourceFile": "plugins/cursor/send.js", + "navigateBefore": true + }, + { + "site": "cursor", + "name": "status", + "description": "Check active CDP connection to Cursor AI Editor", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "plugins/cursor/status.js", + "sourceFile": "plugins/cursor/status.js", + "navigateBefore": true + }, + { + "site": "dblp", + "name": "author", + "description": "List dblp publications by a given author (newest first; resolves to top PID match)", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "author", + "type": "str", + "required": false, + "positional": true, + "help": "Author name (e.g. \"Yoshua Bengio\"). Optional when --pid is given." + }, + { + "name": "pid", + "type": "str", + "required": false, + "help": "Canonical dblp PID (e.g. \"56/953\"). Bypasses author search." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max publications (1-200)" + } + ], + "columns": [ + "rank", + "key", + "title", + "authors", + "venue", + "year", + "type", + "doi", + "pid", + "url" + ], + "type": "js", + "modulePath": "plugins/dblp/author.js", + "sourceFile": "plugins/dblp/author.js" + }, + { + "site": "dblp", + "name": "paper", + "aliases": [ + "detail", + "view" + ], + "description": "Fetch a dblp record by canonical key (e.g. conf/nips/VaswaniSPUJGKP17)", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "key", + "type": "str", + "required": true, + "positional": true, + "help": "dblp record key (round-tripped from the `key` column of `dblp search`)" + } + ], + "columns": [ + "key", + "type", + "title", + "authors", + "venue", + "year", + "pages", + "doi", + "open_access_url", + "dblp_url" + ], + "type": "js", + "modulePath": "plugins/dblp/paper.js", + "sourceFile": "plugins/dblp/paper.js" + }, + { + "site": "dblp", + "name": "search", + "description": "Search dblp computer-science bibliography by free-text query", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (title / author / venue, e.g. \"attention is all you need\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100, single dblp page)" + } + ], + "columns": [ + "rank", + "key", + "title", + "authors", + "venue", + "year", + "type", + "doi", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/dblp/search.js", + "sourceFile": "plugins/dblp/search.js" + }, + { + "site": "dblp", + "name": "venue", + "description": "Search dblp venue registry (conferences / journals) by name or acronym", + "access": "read", + "domain": "dblp.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Venue name or acronym (e.g. \"ICLR\", \"neural networks\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max venues (1-100, single dblp page)" + } + ], + "columns": [ + "rank", + "acronym", + "venue", + "type", + "url" + ], + "type": "js", + "modulePath": "plugins/dblp/venue.js", + "sourceFile": "plugins/dblp/venue.js" + }, + { + "site": "defillama", + "name": "protocol", + "description": "Single DefiLlama protocol details (current TVL, mcap, chains, twitter, github, description)", + "access": "read", + "domain": "defillama.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "slug", + "type": "string", + "required": true, + "positional": true, + "help": "DefiLlama protocol slug (e.g. \"aave\", \"lido\")" + } + ], + "columns": [ + "slug", + "name", + "category", + "isParent", + "tvl", + "tvlAt", + "mcap", + "chains", + "twitter", + "github", + "audits", + "listedAt", + "description", + "website", + "url" + ], + "type": "js", + "modulePath": "plugins/defillama/protocol.js", + "sourceFile": "plugins/defillama/protocol.js" + }, + { + "site": "defillama", + "name": "protocols", + "description": "Top DeFi protocols on DefiLlama by current TVL (slug, name, category, TVL, mcap, change_1d/7d, chains)", + "access": "read", + "domain": "defillama.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Number of rows to return (1-500)" + } + ], + "columns": [ + "rank", + "slug", + "name", + "category", + "tvl", + "mcap", + "change_1d", + "change_7d", + "chains", + "listedAt", + "url" + ], + "type": "js", + "modulePath": "plugins/defillama/protocols.js", + "sourceFile": "plugins/defillama/protocols.js" + }, + { + "site": "devto", + "name": "latest", + "description": "Newest dev.to articles (firehose, all tags)", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Articles per page (1-100)" + }, + { + "name": "page", + "type": "int", + "default": 1, + "required": false, + "help": "Page number (1-based)" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "tags", + "reactions", + "comments", + "published", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/latest.js", + "sourceFile": "plugins/devto/latest.js" + }, + { + "site": "devto", + "name": "read", + "description": "Read a DEV.to article body by id", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "DEV.to article id (numeric, e.g. 3605688)" + }, + { + "name": "max-length", + "type": "int", + "default": 20000, + "required": false, + "help": "Max characters of body to return (min 100)" + } + ], + "columns": [ + "id", + "title", + "author", + "reactions", + "reading_time", + "tags", + "published_at", + "body", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/read.js", + "sourceFile": "plugins/devto/read.js" + }, + { + "site": "devto", + "name": "tag", + "description": "Latest DEV.to articles for a specific tag", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "tag", + "type": "str", + "required": true, + "positional": true, + "help": "Tag name (e.g. javascript, python, webdev)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of articles" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/tag.js", + "sourceFile": "plugins/devto/tag.js" + }, + { + "site": "devto", + "name": "top", + "description": "Top DEV.to articles of the day", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of articles" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/top.js", + "sourceFile": "plugins/devto/top.js" + }, + { + "site": "devto", + "name": "user", + "description": "Recent DEV.to articles from a specific user", + "access": "read", + "domain": "dev.to", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "DEV.to username (e.g. ben, thepracticaldev)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of articles" + } + ], + "columns": [ + "rank", + "id", + "title", + "reactions", + "comments", + "reading_time", + "published_at", + "tags", + "url" + ], + "type": "js", + "modulePath": "plugins/devto/user.js", + "sourceFile": "plugins/devto/user.js" + }, + { + "site": "dictionary", + "name": "examples", + "description": "Read real-world example sentences utilizing the word", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to get example sentences for" + } + ], + "columns": [ + "word", + "example" + ], + "type": "js", + "modulePath": "plugins/dictionary/examples.js", + "sourceFile": "plugins/dictionary/examples.js" + }, + { + "site": "dictionary", + "name": "search", + "description": "Search the Free Dictionary API for definitions, parts of speech, and pronunciations.", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to define (e.g., serendipity)" + } + ], + "columns": [ + "word", + "phonetic", + "type", + "definition" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/dictionary/search.js", + "sourceFile": "plugins/dictionary/search.js" + }, + { + "site": "dictionary", + "name": "synonyms", + "description": "Find synonyms for a specific word", + "access": "read", + "domain": "api.dictionaryapi.dev", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "word", + "type": "string", + "required": true, + "positional": true, + "help": "Word to find synonyms for (e.g., serendipity)" + } + ], + "columns": [ + "word", + "synonyms" + ], + "type": "js", + "modulePath": "plugins/dictionary/synonyms.js", + "sourceFile": "plugins/dictionary/synonyms.js" + }, + { + "site": "discord-app", + "name": "channels", + "description": "List channels in the current Discord server", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Channel", + "Type", + "guild_id", + "channel_id", + "url" + ], + "type": "js", + "modulePath": "plugins/discord-app/channels.js", + "sourceFile": "plugins/discord-app/channels.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "delete", + "description": "Delete a message by its ID in the active Discord channel", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "message_id", + "type": "string", + "required": true, + "positional": true, + "help": "The ID of the message to delete (visible via Developer Mode or the read command)" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "plugins/discord-app/delete.js", + "sourceFile": "plugins/discord-app/delete.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "goto", + "description": "Open a Discord channel by id/name/url without sending messages", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "guild", + "type": "str", + "required": false, + "help": "Guild/server id or visible name" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Channel id or visible name" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Discord channel URL" + }, + { + "name": "timeout", + "type": "str", + "default": "8", + "required": false, + "help": "Seconds to wait for Discord to show the route (default: 8)" + } + ], + "columns": [ + "Status", + "guild_id", + "channel_id", + "url" + ], + "type": "js", + "modulePath": "plugins/discord-app/goto.js", + "sourceFile": "plugins/discord-app/goto.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "members", + "description": "List online members in the current Discord channel", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Name", + "Status" + ], + "type": "js", + "modulePath": "plugins/discord-app/members.js", + "sourceFile": "plugins/discord-app/members.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "read", + "description": "Read recent messages from the active or targeted Discord channel", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "count", + "type": "str", + "default": "20", + "required": false, + "help": "Number of messages to read (default: 20)" + }, + { + "name": "guild", + "type": "str", + "required": false, + "help": "Guild/server id or visible name for targeted reads" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Channel id or visible name for targeted reads" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Discord channel URL to open before reading" + } + ], + "columns": [ + "Author", + "Time", + "Message", + "channel_id", + "message_id" + ], + "type": "js", + "modulePath": "plugins/discord-app/read.js", + "sourceFile": "plugins/discord-app/read.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "search", + "description": "Search messages in the current Discord server/channel (Cmd+F)", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + } + ], + "columns": [ + "Index", + "Author", + "Message" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/discord-app/search.js", + "sourceFile": "plugins/discord-app/search.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "send", + "description": "Send a message in the active Discord channel", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Message to send" + } + ], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/discord-app/send.js", + "sourceFile": "plugins/discord-app/send.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "servers", + "description": "List all Discord servers (guilds) in the sidebar", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Server", + "guild_id", + "url" + ], + "type": "js", + "modulePath": "plugins/discord-app/servers.js", + "sourceFile": "plugins/discord-app/servers.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "status", + "description": "Check active CDP connection to Discord Desktop", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "plugins/discord-app/status.js", + "sourceFile": "plugins/discord-app/status.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "thread-read", + "description": "Read recent messages from a Discord thread/post by id or URL", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "thread", + "type": "str", + "required": false, + "help": "Thread/post id, or a full Discord thread/post URL" + }, + { + "name": "count", + "type": "str", + "default": "20", + "required": false, + "help": "Number of messages to read (default: 20)" + }, + { + "name": "guild", + "type": "str", + "required": false, + "help": "Parent guild/server id or visible name" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Parent forum/channel id or visible name" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Discord thread/post URL" + } + ], + "columns": [ + "Author", + "Time", + "Message", + "channel_id", + "message_id" + ], + "type": "js", + "modulePath": "plugins/discord-app/thread-read.js", + "sourceFile": "plugins/discord-app/thread-read.js", + "navigateBefore": true + }, + { + "site": "discord-app", + "name": "threads", + "description": "List visible Discord forum/thread posts in the active or targeted channel", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "limit", + "type": "str", + "default": "30", + "required": false, + "help": "Maximum thread/post cards to return (default: 30)" + }, + { + "name": "guild", + "type": "str", + "required": false, + "help": "Guild/server id or visible name for targeted thread listing" + }, + { + "name": "channel", + "type": "str", + "required": false, + "help": "Forum/channel id or visible name for targeted thread listing" + }, + { + "name": "url", + "type": "str", + "required": false, + "help": "Discord forum/channel URL to open before listing threads" + } + ], + "columns": [ + "Index", + "Thread", + "Author", + "Updated", + "Preview", + "guild_id", + "channel_id", + "thread_id", + "url" + ], + "type": "js", + "modulePath": "plugins/discord-app/threads.js", + "sourceFile": "plugins/discord-app/threads.js", + "navigateBefore": true + }, + { + "site": "district", + "name": "checkout", + "description": "Select District movie seats and open the UPI QR payment scanner", + "access": "write", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "show", + "type": "str", + "required": true, + "positional": true, + "help": "District seat-layout URL or showId from district showtimes" + }, + { + "name": "seats", + "type": "str", + "required": true, + "help": "Comma-separated seat labels to select, e.g. I22,I21" + }, + { + "name": "format-id", + "type": "str", + "required": false, + "help": "District formatId from showtimes; required when show is a showId" + }, + { + "name": "content-id", + "type": "str", + "required": false, + "help": "District content id; required when show is a showId" + }, + { + "name": "timeout", + "type": "int", + "default": 45, + "required": false, + "help": "Maximum seconds to wait for selection, review page, and payment handoff" + }, + { + "name": "payment", + "type": "str", + "default": "upi-qr", + "required": false, + "help": "Payment handoff target: upi-qr opens the scanner; review stops on order review" + } + ], + "columns": [ + "status", + "movie", + "cinema", + "date", + "time", + "seats", + "ticketCount", + "orderAmount", + "bookingCharge", + "total", + "paymentMethod", + "paymentState", + "upiQrVisible", + "paymentAmount", + "paymentUrl", + "showId" + ], + "type": "js", + "modulePath": "plugins/district/checkout.js", + "sourceFile": "plugins/district/checkout.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true + }, + { + "site": "district", + "name": "listings", + "aliases": [ + "ls" + ], + "description": "List public District by Zomato movies, events, and nearby going-out cards", + "access": "read", + "domain": "www.district.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "input", + "type": "str", + "default": "home", + "required": false, + "positional": true, + "help": "home, movies, events, a district.in URL, or a District path" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum rows to return (1-100)" + } + ], + "columns": [ + "rank", + "title", + "category", + "date", + "venue", + "price", + "url" + ], + "type": "js", + "modulePath": "plugins/district/listings.js", + "sourceFile": "plugins/district/listings.js" + }, + { + "site": "district", + "name": "locations", + "aliases": [ + "location-search" + ], + "description": "Search District-supported cities, areas, malls, and places for booking filters", + "access": "read", + "domain": "www.district.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "City, area, mall, or locality, for example \"bangalore\" or \"indiranagar\"" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum location rows to return (1-50)" + } + ], + "columns": [ + "rank", + "name", + "kind", + "city", + "state", + "cityKey", + "cityId", + "placeId", + "lat", + "lng", + "distanceKm", + "source" + ], + "type": "js", + "modulePath": "plugins/district/locations.js", + "sourceFile": "plugins/district/locations.js" + }, + { + "site": "district", + "name": "login", + "description": "Open district login", + "access": "write", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "phone_number", + "email", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/district/auth.js", + "sourceFile": "plugins/district/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "district", + "name": "search", + "aliases": [ + "s" + ], + "description": "Search District by Zomato across movies, events, dining, stores, activities, and play", + "access": "read", + "domain": "www.district.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query, for example \"hamlet\" or \"arijit\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum rows to return (1-100)" + }, + { + "name": "tab", + "type": "str", + "default": "all", + "required": false, + "help": "Search tab: all, dining, events, movies, stores, activities, or play" + } + ], + "columns": [ + "rank", + "title", + "category", + "date", + "venue", + "price", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/district/search.js", + "sourceFile": "plugins/district/search.js" + }, + { + "site": "district", + "name": "seats", + "description": "List available seats for a District movie showtime", + "access": "read", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "show", + "type": "str", + "required": true, + "positional": true, + "help": "District seat-layout URL or showId from district showtimes" + }, + { + "name": "format-id", + "type": "str", + "required": false, + "help": "District formatId from showtimes; required when show is a showId" + }, + { + "name": "content-id", + "type": "str", + "required": false, + "help": "District content id; required when show is a showId" + }, + { + "name": "class", + "type": "str", + "required": false, + "help": "Optional seat class filter, e.g. premium, premium xl, or recliner" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Number of seats to choose (1-10); without count, seats are listed normally" + }, + { + "name": "together", + "type": "str", + "required": false, + "help": "Require selected seats to be adjacent when count is provided" + }, + { + "name": "max-price", + "type": "float", + "required": false, + "help": "Maximum price per seat" + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Maximum seats to return (1-300)" + }, + { + "name": "timeout", + "type": "int", + "default": 30, + "required": false, + "help": "Maximum seconds to wait for the seat map to render" + } + ], + "columns": [ + "rank", + "seat", + "row", + "number", + "column", + "seatClass", + "price", + "status", + "flags", + "showId", + "formatId", + "url" + ], + "type": "js", + "modulePath": "plugins/district/seats.js", + "sourceFile": "plugins/district/seats.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "district", + "name": "set-location", + "aliases": [ + "setlocation" + ], + "description": "Set the District browser session location for movie booking filters", + "access": "write", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "location", + "type": "str", + "required": true, + "positional": true, + "help": "City, area, mall, or locality, for example \"Bangalore\" or \"Indiranagar\"" + }, + { + "name": "rank", + "type": "int", + "default": 1, + "required": false, + "help": "Pick the Nth District location result (1-20), default: 1" + }, + { + "name": "timeout", + "type": "int", + "default": 45, + "required": false, + "help": "Maximum seconds to wait for the picker and location change" + } + ], + "columns": [ + "status", + "name", + "city", + "state", + "cityKey", + "cityId", + "placeId", + "subzoneId", + "lat", + "lng", + "availableTabs", + "source" + ], + "type": "js", + "modulePath": "plugins/district/set-location.js", + "sourceFile": "plugins/district/set-location.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "district", + "name": "showtimes", + "aliases": [ + "shows" + ], + "description": "List District movie showtimes with location, time, cinema, language, price, and format filters", + "access": "read", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "movie", + "type": "str", + "required": true, + "positional": true, + "help": "Movie name or District movie URL" + }, + { + "name": "date", + "type": "str", + "required": false, + "help": "Show date in YYYY-MM-DD format; defaults to District selected date" + }, + { + "name": "city", + "type": "str", + "required": false, + "help": "District city name/key, for example Bangalore or Bengaluru" + }, + { + "name": "near", + "type": "str", + "required": false, + "help": "Area, mall, or locality to search near, for example Indiranagar" + }, + { + "name": "city-key", + "type": "str", + "required": false, + "help": "Legacy District city key override, for example bengaluru" + }, + { + "name": "after", + "type": "str", + "required": false, + "help": "Only shows at or after HH:MM, 24-hour time" + }, + { + "name": "before", + "type": "str", + "required": false, + "help": "Only shows at or before HH:MM, 24-hour time" + }, + { + "name": "cinema", + "type": "str", + "required": false, + "help": "Filter cinema/theatre name, for example PVR, INOX, Orion" + }, + { + "name": "language", + "type": "str", + "required": false, + "help": "Filter movie language, for example English, Hindi, Kannada" + }, + { + "name": "max-price", + "type": "float", + "required": false, + "help": "Only shows with at least one ticket class at or below this price" + }, + { + "name": "quality", + "type": "str", + "required": false, + "help": "Generic format/quality filter, for example 2D, 3D, IMAX, IMAX 3D, 4DX" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Maximum showtime rows to return (1-200)" + } + ], + "columns": [ + "rank", + "movie", + "language", + "date", + "time", + "cinema", + "format", + "priceRange", + "available", + "showId", + "formatId", + "url" + ], + "type": "js", + "modulePath": "plugins/district/showtimes.js", + "sourceFile": "plugins/district/showtimes.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "district", + "name": "whoami", + "description": "Show the current logged-in district account", + "access": "read", + "domain": "www.district.in", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name", + "phone_number", + "email" + ], + "type": "js", + "modulePath": "plugins/district/auth.js", + "sourceFile": "plugins/district/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "dockerhub", + "name": "image", + "description": "Fetch a Docker Hub repository's public metadata (stars, pulls, last updated, status)", + "access": "read", + "domain": "hub.docker.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image name (e.g. \"nginx\", \"library/nginx\", \"bitnami/redis\")" + } + ], + "columns": [ + "image", + "official", + "stars", + "pulls", + "description", + "lastUpdated", + "lastModified", + "registered", + "status", + "url" + ], + "type": "js", + "modulePath": "plugins/dockerhub/image.js", + "sourceFile": "plugins/dockerhub/image.js" + }, + { + "site": "dockerhub", + "name": "search", + "description": "Search Docker Hub repositories by keyword", + "access": "read", + "domain": "hub.docker.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"nginx\", \"bitnami redis\")" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max repositories (1-100, single Docker Hub page)" + } + ], + "columns": [ + "rank", + "image", + "official", + "stars", + "pulls", + "description", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/dockerhub/search.js", + "sourceFile": "plugins/dockerhub/search.js" + }, + { + "site": "duckduckgo", + "name": "search", + "description": "Search DuckDuckGo", + "access": "read", + "domain": "html.duckduckgo.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results per page (1-10). For multi-page, use --offset" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Result offset for pagination (0, 10, 20...). Uses XHR POST internally" + }, + { + "name": "region", + "type": "str", + "required": false, + "help": "Region code (e.g. jp-jp, us-en, cn-zh). Default: all regions" + }, + { + "name": "time", + "type": "str", + "required": false, + "help": "Time range: d (day), w (week), m (month), y (year)" + } + ], + "columns": [ + "rank", + "title", + "url", + "snippet", + "displayUrl", + "icon", + "resultType" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/duckduckgo/search.js", + "sourceFile": "plugins/duckduckgo/search.js" + }, + { + "site": "duckduckgo", + "name": "suggest", + "description": "DuckDuckGo search suggestions", + "access": "read", + "domain": "duckduckgo.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query prefix" + }, + { + "name": "limit", + "type": "int", + "default": 8, + "required": false, + "help": "Max number of suggestions" + } + ], + "columns": [ + "phrase" + ], + "type": "js", + "modulePath": "plugins/duckduckgo/suggest.js", + "sourceFile": "plugins/duckduckgo/suggest.js" + }, + { + "site": "endoflife", + "name": "product", + "description": "Release cycles + EOL / LTS / support dates for one product on endoflife.date", + "access": "read", + "domain": "endoflife.date", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "product", + "type": "string", + "required": true, + "positional": true, + "help": "endoflife.date product slug (e.g. \"nodejs\", \"python\", \"ubuntu\")" + } + ], + "columns": [ + "product", + "cycle", + "releaseDate", + "latest", + "latestReleaseDate", + "lts", + "support", + "eol", + "extendedSupport", + "eolStatus", + "url" + ], + "type": "js", + "modulePath": "plugins/endoflife/product.js", + "sourceFile": "plugins/endoflife/product.js" + }, + { + "site": "facebook", + "name": "add-friend", + "description": "Send a friend request on Facebook", + "access": "write", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Facebook username or profile URL" + } + ], + "columns": [ + "status", + "username" + ], + "type": "js", + "modulePath": "plugins/facebook/add-friend.js", + "sourceFile": "plugins/facebook/add-friend.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "events", + "description": "Browse Facebook event categories", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Number of categories" + } + ], + "columns": [ + "index", + "name" + ], + "type": "js", + "modulePath": "plugins/facebook/events.js", + "sourceFile": "plugins/facebook/events.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "feed", + "description": "Get your Facebook news feed", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of posts" + } + ], + "columns": [ + "index", + "author", + "content", + "likes", + "comments", + "shares" + ], + "type": "js", + "modulePath": "plugins/facebook/feed.js", + "sourceFile": "plugins/facebook/feed.js", + "navigateBefore": false + }, + { + "site": "facebook", + "name": "friends", + "description": "Get Facebook friend suggestions", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of friend suggestions" + } + ], + "columns": [ + "index", + "name", + "mutual" + ], + "type": "js", + "modulePath": "plugins/facebook/friends.js", + "sourceFile": "plugins/facebook/friends.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "groups", + "description": "List your Facebook groups", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of groups" + } + ], + "columns": [ + "index", + "name", + "last_post", + "url" + ], + "type": "js", + "modulePath": "plugins/facebook/groups.js", + "sourceFile": "plugins/facebook/groups.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "join-group", + "description": "Join a Facebook group", + "access": "write", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "group", + "type": "str", + "required": true, + "positional": true, + "help": "Group ID or URL path (e.g. '1876150192925481' or group name)" + } + ], + "columns": [ + "status", + "group" + ], + "type": "js", + "modulePath": "plugins/facebook/join-group.js", + "sourceFile": "plugins/facebook/join-group.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "login", + "description": "Open facebook login", + "access": "write", + "domain": "facebook.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "vanity", + "profile_url", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/facebook/auth.js", + "sourceFile": "plugins/facebook/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "facebook", + "name": "marketplace-inbox", + "description": "List recent Facebook Marketplace buyer/seller conversations", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of conversations to return" + } + ], + "columns": [ + "index", + "buyer", + "listing", + "snippet", + "time", + "unread" + ], + "type": "js", + "modulePath": "plugins/facebook/marketplace-inbox.js", + "sourceFile": "plugins/facebook/marketplace-inbox.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "marketplace-listings", + "description": "List your Facebook Marketplace seller listings", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of listings to return" + } + ], + "columns": [ + "index", + "title", + "price", + "status", + "listed", + "clicks", + "actions" + ], + "type": "js", + "modulePath": "plugins/facebook/marketplace-listings.js", + "sourceFile": "plugins/facebook/marketplace-listings.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "memories", + "description": "Get your Facebook memories (On This Day)", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of memories" + } + ], + "columns": [ + "index", + "source", + "content", + "time" + ], + "type": "js", + "modulePath": "plugins/facebook/memories.js", + "sourceFile": "plugins/facebook/memories.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "notifications", + "description": "Get recent Facebook notifications (includes unread / time / url / notif_id / notif_type columns)", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Number of notifications (1-100)" + } + ], + "columns": [ + "index", + "unread", + "text", + "time", + "url", + "notif_id", + "notif_type" + ], + "type": "js", + "modulePath": "plugins/facebook/notifications.js", + "sourceFile": "plugins/facebook/notifications.js", + "navigateBefore": false + }, + { + "site": "facebook", + "name": "profile", + "description": "Get Facebook user/page profile info", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Facebook username or page name" + } + ], + "columns": [ + "name", + "username", + "friends", + "followers", + "url" + ], + "type": "js", + "modulePath": "plugins/facebook/profile.js", + "sourceFile": "plugins/facebook/profile.js", + "navigateBefore": "https://www.facebook.com" + }, + { + "site": "facebook", + "name": "search", + "description": "Search Facebook for people, pages, or posts", + "access": "read", + "domain": "www.facebook.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "index", + "title", + "text", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/facebook/search.js", + "sourceFile": "plugins/facebook/search.js", + "navigateBefore": false + }, + { + "site": "facebook", + "name": "whoami", + "description": "Show the current logged-in facebook account", + "access": "read", + "domain": "facebook.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "vanity", + "profile_url" + ], + "type": "js", + "modulePath": "plugins/facebook/auth.js", + "sourceFile": "plugins/facebook/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "flathub", + "name": "app", + "description": "Full Flathub appstream metadata for an app id (license, categories, latest release)", + "access": "read", + "domain": "flathub.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "appId", + "type": "str", + "required": true, + "positional": true, + "help": "AppStream id (e.g. \"org.mozilla.firefox\", \"org.gnome.Calculator\")" + } + ], + "columns": [ + "appId", + "name", + "summary", + "developer", + "license", + "isFreeLicense", + "isEol", + "categories", + "keywords", + "latestVersion", + "latestReleaseDate", + "homepage", + "bugtracker", + "donation", + "url" + ], + "type": "js", + "modulePath": "plugins/flathub/app.js", + "sourceFile": "plugins/flathub/app.js" + }, + { + "site": "flathub", + "name": "search", + "description": "Search Flathub apps by keyword", + "access": "read", + "domain": "flathub.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max apps (1-100)" + } + ], + "columns": [ + "rank", + "appId", + "name", + "summary", + "developer", + "license", + "isFreeLicense", + "mainCategories", + "installsLastMonth", + "updatedAt", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/flathub/search.js", + "sourceFile": "plugins/flathub/search.js" + }, + { + "site": "gemini", + "name": "ask", + "description": "Send a prompt to Gemini and return only the assistant response", + "access": "write", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "model", + "type": "string", + "required": false, + "help": "Gemini model to use (e.g. \"2.5-flash\"). Use \"webcmd gemini models\" to list available values." + }, + { + "name": "timeout", + "type": "int", + "default": 60, + "required": false, + "help": "Max seconds to wait (default: 60)" + }, + { + "name": "new", + "type": "str", + "default": "false", + "required": false, + "help": "Start a new chat first (true/false, default: false)" + }, + { + "name": "thinking", + "type": "str", + "default": null, + "required": false, + "help": "Thinking level: standard or extended (omitted = leave unchanged)" + } + ], + "columns": [ + "response" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "plugins/gemini/ask.js", + "sourceFile": "plugins/gemini/ask.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "deep-research", + "description": "Start a Gemini Deep Research run and confirm it", + "access": "write", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send" + }, + { + "name": "timeout", + "type": "int", + "default": 180, + "required": false, + "help": "Max seconds for the overall command (default: 180; confirm-wait clamps internally to 6-20s)" + }, + { + "name": "tool", + "type": "str", + "required": false, + "help": "Override tool label (default: Deep Research)" + }, + { + "name": "confirm", + "type": "str", + "required": false, + "help": "Override confirm button label (default: Start research)" + } + ], + "columns": [ + "status", + "url" + ], + "tags": [ + "search" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "plugins/gemini/deep-research.js", + "sourceFile": "plugins/gemini/deep-research.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "deep-research-result", + "description": "Export Deep Research report URL from a Gemini conversation", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": false, + "positional": true, + "help": "Conversation title or URL (optional; defaults to latest conversation)" + }, + { + "name": "match", + "type": "str", + "default": "contains", + "required": false, + "help": "Match mode", + "choices": [ + "contains", + "exact" + ] + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait for Docs export (default: 120)" + } + ], + "columns": [ + "response" + ], + "tags": [ + "search" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "plugins/gemini/deep-research-result.js", + "sourceFile": "plugins/gemini/deep-research-result.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "detail", + "description": "Open a Gemini web conversation by id, URL, or sidebar title and read its turns", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Conversation id, /app/ URL, or sidebar title" + } + ], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/gemini/detail.js", + "sourceFile": "plugins/gemini/detail.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "history", + "description": "List visible Gemini web conversation history from the sidebar", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max conversations to show" + } + ], + "columns": [ + "Index", + "Id", + "Title", + "Url" + ], + "type": "js", + "modulePath": "plugins/gemini/history.js", + "sourceFile": "plugins/gemini/history.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "image", + "description": "Generate images with Gemini web and save them locally", + "access": "write", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Image prompt to send to Gemini" + }, + { + "name": "rt", + "type": "str", + "default": "1:1", + "required": false, + "help": "Ratio shorthand for aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3)" + }, + { + "name": "st", + "type": "str", + "default": "", + "required": false, + "help": "Style shorthand, e.g. anime, icon, watercolor" + }, + { + "name": "op", + "type": "str", + "default": "~/tmp/gemini-images", + "required": false, + "help": "Output directory shorthand" + }, + { + "name": "sd", + "type": "boolean", + "default": false, + "required": false, + "help": "Skip download shorthand; only show Gemini page link" + }, + { + "name": "timeout", + "type": "int", + "default": 240, + "required": false, + "help": "Max seconds for the overall command (default: 240)" + } + ], + "columns": [ + "status", + "file", + "link" + ], + "defaultFormat": "plain", + "type": "js", + "modulePath": "plugins/gemini/image.js", + "sourceFile": "plugins/gemini/image.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "login", + "description": "Open gemini login", + "access": "write", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/gemini/auth.js", + "sourceFile": "plugins/gemini/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "models", + "description": "List available Gemini models from the web UI", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "model", + "thinkingValues" + ], + "type": "js", + "modulePath": "plugins/gemini/models.js", + "sourceFile": "plugins/gemini/models.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "new", + "description": "Start a new conversation in Gemini web chat", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status", + "Action" + ], + "type": "js", + "modulePath": "plugins/gemini/new.js", + "sourceFile": "plugins/gemini/new.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "read", + "description": "Read the turns visible in the current Gemini web conversation", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Index", + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/gemini/read.js", + "sourceFile": "plugins/gemini/read.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "status", + "description": "Check Gemini web page availability and login state", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status", + "Login", + "Url" + ], + "type": "js", + "modulePath": "plugins/gemini/status.js", + "sourceFile": "plugins/gemini/status.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "gemini", + "name": "whoami", + "description": "Show the current logged-in gemini account", + "access": "read", + "domain": "gemini.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name" + ], + "type": "js", + "modulePath": "plugins/gemini/auth.js", + "sourceFile": "plugins/gemini/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "geogebra", + "name": "add-circle", + "description": "Create a circle by center+radius or center+point", + "access": "write", + "example": "webcmd geogebra add-circle --center A --radius 3", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "center", + "type": "str", + "required": true, + "help": "Center point label (e.g. A)" + }, + { + "name": "radius", + "type": "str", + "required": false, + "help": "Radius value (number) or a point label on the circle" + }, + { + "name": "point", + "type": "str", + "required": false, + "help": "Alternative: a point label on the circle (use instead of --radius for Circle(center,point))" + } + ], + "columns": [ + "label", + "center", + "radius" + ], + "type": "js", + "modulePath": "plugins/geogebra/add-circle.js", + "sourceFile": "plugins/geogebra/add-circle.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "add-line", + "description": "Create a line through two points or a segment between two points", + "access": "write", + "example": "webcmd geogebra add-line --points A,B --type segment", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "points", + "type": "str", + "required": true, + "help": "Two point labels separated by comma (e.g. \"A,B\")" + }, + { + "name": "type", + "type": "str", + "default": "line", + "required": false, + "help": "Type: line, segment, or ray (default: line)", + "choices": [ + "line", + "segment", + "ray" + ] + } + ], + "columns": [ + "label", + "type", + "points" + ], + "type": "js", + "modulePath": "plugins/geogebra/add-line.js", + "sourceFile": "plugins/geogebra/add-line.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "add-point", + "description": "Create a point with given label and coordinates", + "access": "write", + "example": "webcmd geogebra add-point --name A --coords 1,2", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "help": "Point label (e.g. A, B, P1)" + }, + { + "name": "coords", + "type": "str", + "required": true, + "help": "Coordinates as x,y (e.g. \"1,2\")" + } + ], + "columns": [ + "name", + "x", + "y" + ], + "type": "js", + "modulePath": "plugins/geogebra/add-point.js", + "sourceFile": "plugins/geogebra/add-point.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "add-polygon", + "description": "Create a polygon from a list of point labels", + "access": "write", + "example": "webcmd geogebra add-polygon --points A,B,C", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "points", + "type": "str", + "required": true, + "help": "Comma-separated point labels (e.g. \"A,B,C\" or \"A,B,C,D\")" + } + ], + "columns": [ + "label", + "vertices" + ], + "type": "js", + "modulePath": "plugins/geogebra/add-polygon.js", + "sourceFile": "plugins/geogebra/add-polygon.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "eval", + "description": "Execute one or more GeoGebra command strings (semicolon-separated)", + "access": "write", + "example": "webcmd geogebra eval \"A=(0,0);B=(4,0);c=Circle(A,B);d=Circle(B,A);C=Intersect(c,d,1);Polygon(A,B,C)\"", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "command", + "type": "str", + "required": true, + "positional": true, + "help": "GeoGebra command string (use ; to chain multiple commands)" + } + ], + "columns": [ + "command", + "result" + ], + "type": "js", + "modulePath": "plugins/geogebra/eval.js", + "sourceFile": "plugins/geogebra/eval.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "hexagon", + "description": "Draw a regular hexagon centered at the origin", + "access": "write", + "example": "webcmd geogebra hexagon --size 3", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "size", + "type": "str", + "default": "2", + "required": false, + "help": "Radius of the hexagon (default: 2)" + } + ], + "columns": [ + "step", + "result" + ], + "type": "js", + "modulePath": "plugins/geogebra/hexagon.js", + "sourceFile": "plugins/geogebra/hexagon.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "info", + "description": "Get detailed properties of a GeoGebra object", + "access": "read", + "example": "webcmd geogebra info --name A", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "help": "Object label (e.g. A, c1, poly1)" + } + ], + "columns": [ + "property", + "value" + ], + "type": "js", + "modulePath": "plugins/geogebra/info.js", + "sourceFile": "plugins/geogebra/info.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "list", + "description": "List all geometric objects on the GeoGebra canvas", + "access": "read", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "type", + "type": "str", + "required": false, + "help": "Filter by object type (e.g. \"point\", \"line\", \"circle\")" + } + ], + "columns": [ + "name", + "type", + "value", + "visible" + ], + "type": "js", + "modulePath": "plugins/geogebra/list.js", + "sourceFile": "plugins/geogebra/list.js", + "navigateBefore": false + }, + { + "site": "geogebra", + "name": "triangle", + "description": "Draw an equilateral triangle from a horizontal base segment", + "access": "write", + "example": "webcmd geogebra triangle --size 4", + "domain": "www.geogebra.org", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "size", + "type": "str", + "default": "2", + "required": false, + "help": "Side length of the triangle (default: 2)" + } + ], + "columns": [ + "step", + "result" + ], + "type": "js", + "modulePath": "plugins/geogebra/triangle.js", + "sourceFile": "plugins/geogebra/triangle.js", + "navigateBefore": false + }, + { + "site": "github", + "name": "login", + "description": "Open github login", + "access": "write", + "domain": "github.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "id", + "username", + "name", + "url", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/github/auth.js", + "sourceFile": "plugins/github/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "github", + "name": "whoami", + "description": "Show the current logged-in github account", + "access": "read", + "domain": "github.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "id", + "username", + "name", + "url" + ], + "type": "js", + "modulePath": "plugins/github/auth.js", + "sourceFile": "plugins/github/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "github-trending", + "name": "repos", + "description": "GitHub Trending repositories (public, no login). Filter by --language and --since.", + "access": "read", + "domain": "github.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "since", + "type": "string", + "default": "daily", + "required": false, + "help": "Time range: daily / weekly / monthly" + }, + { + "name": "language", + "type": "string", + "default": "", + "required": false, + "help": "Filter by programming language slug, e.g. python, rust, \"c++\"" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Number of repositories to return (max 25)" + } + ], + "columns": [ + "rank", + "repo", + "description", + "language", + "stars", + "forks", + "starsSince", + "url" + ], + "type": "js", + "modulePath": "plugins/github-trending/repos.js", + "sourceFile": "plugins/github-trending/repos.js" + }, + { + "site": "goettingen", + "name": "export-postgraduate-courses", + "description": "Export University of Göttingen postgraduate programmes from the official A-Z API.", + "access": "read", + "example": "webcmd goettingen export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.uni-goettingen.de", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programmes after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/goettingen/export-postgraduate-courses.js", + "sourceFile": "plugins/goettingen/export-postgraduate-courses.js" + }, + { + "site": "google", + "name": "images", + "description": "Search Google Images for photos and image results", + "access": "read", + "domain": "google.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Image search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of image results (1-100)" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language short code (e.g. en, zh)" + }, + { + "name": "resolve", + "type": "bool", + "default": true, + "required": false, + "help": "Click image previews to resolve original imgurl values" + } + ], + "columns": [ + "rank", + "title", + "imageUrl", + "thumbnailUrl", + "sourceUrl", + "source", + "width", + "height" + ], + "type": "js", + "modulePath": "plugins/google/images.js", + "sourceFile": "plugins/google/images.js", + "navigateBefore": false + }, + { + "site": "google", + "name": "news", + "description": "Get Google News headlines", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "keyword", + "type": "str", + "required": false, + "positional": true, + "help": "Search query (omit for top stories)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language short code (e.g. en, zh)" + }, + { + "name": "region", + "type": "str", + "default": "US", + "required": false, + "help": "Region code (e.g. US, CN)" + } + ], + "columns": [ + "title", + "source", + "date", + "url" + ], + "type": "js", + "modulePath": "plugins/google/news.js", + "sourceFile": "plugins/google/news.js" + }, + { + "site": "google", + "name": "search", + "description": "Search Google", + "access": "read", + "domain": "google.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results (1-100)" + }, + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language short code (e.g. en, zh)" + } + ], + "columns": [ + "type", + "title", + "url", + "snippet" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/google/search.js", + "sourceFile": "plugins/google/search.js" + }, + { + "site": "google", + "name": "suggest", + "description": "Get Google search suggestions", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "keyword", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "lang", + "type": "str", + "default": "zh-CN", + "required": false, + "help": "Language code" + } + ], + "columns": [ + "suggestion" + ], + "type": "js", + "modulePath": "plugins/google/suggest.js", + "sourceFile": "plugins/google/suggest.js" + }, + { + "site": "google", + "name": "trends", + "description": "Get Google Trends daily trending searches", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "region", + "type": "str", + "default": "US", + "required": false, + "help": "Region code (e.g. US, CN, JP)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "title", + "traffic", + "date" + ], + "type": "js", + "modulePath": "plugins/google/trends.js", + "sourceFile": "plugins/google/trends.js" + }, + { + "site": "google-scholar", + "name": "cite", + "description": "Get citation for a Google Scholar paper", + "access": "read", + "domain": "scholar.google.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Paper title to search for" + }, + { + "name": "style", + "type": "str", + "default": "bibtex", + "required": false, + "help": "Citation format", + "choices": [ + "bibtex", + "endnote", + "refman", + "refworks" + ] + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Which search result to cite (1-based)" + } + ], + "columns": [ + "title", + "format", + "citation" + ], + "type": "js", + "modulePath": "plugins/google-scholar/cite.js", + "sourceFile": "plugins/google-scholar/cite.js" + }, + { + "site": "google-scholar", + "name": "profile", + "description": "View a Google Scholar author profile", + "access": "read", + "domain": "scholar.google.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "author", + "type": "str", + "required": true, + "positional": true, + "help": "Author name or Scholar user ID (e.g. JicYPdAAAAAJ)" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max papers to show (max 20)" + } + ], + "columns": [ + "rank", + "title", + "cited", + "year" + ], + "type": "js", + "modulePath": "plugins/google-scholar/profile.js", + "sourceFile": "plugins/google-scholar/profile.js" + }, + { + "site": "google-scholar", + "name": "search", + "description": "Google Scholar scholar search", + "access": "read", + "domain": "scholar.google.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results to return (max 20)" + } + ], + "columns": [ + "rank", + "title", + "authors", + "source", + "year", + "cited", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/google-scholar/search.js", + "sourceFile": "plugins/google-scholar/search.js" + }, + { + "site": "goproxy", + "name": "module", + "description": "Latest version + VCS origin metadata for a Go module on proxy.golang.org", + "access": "read", + "domain": "proxy.golang.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "module", + "type": "string", + "required": true, + "positional": true, + "help": "Go module path (e.g. \"github.com/gin-gonic/gin\", \"golang.org/x/net\")" + } + ], + "columns": [ + "module", + "version", + "publishedAt", + "vcs", + "repository", + "commit", + "ref", + "pkgGoDevUrl", + "url" + ], + "type": "js", + "modulePath": "plugins/goproxy/module.js", + "sourceFile": "plugins/goproxy/module.js" + }, + { + "site": "goproxy", + "name": "versions", + "description": "Published version tags for a Go module (newest first), optionally with publish times", + "access": "read", + "domain": "proxy.golang.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "module", + "type": "string", + "required": true, + "positional": true, + "help": "Go module path (e.g. \"github.com/gin-gonic/gin\")" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max rows to return (1-200)" + }, + { + "name": "with-time", + "type": "boolean", + "default": false, + "required": false, + "help": "Fetch each version's publish time (one extra request per row)" + } + ], + "columns": [ + "rank", + "module", + "version", + "publishedAt", + "url" + ], + "type": "js", + "modulePath": "plugins/goproxy/versions.js", + "sourceFile": "plugins/goproxy/versions.js" + }, + { + "site": "grok", + "name": "ask", + "description": "Send a message to Grok and get response", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "string", + "required": true, + "positional": true, + "help": "Prompt to send to Grok" + }, + { + "name": "timeout", + "type": "int", + "default": 120, + "required": false, + "help": "Max seconds to wait for response (default: 120)" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending (default: false)" + } + ], + "columns": [ + "response" + ], + "type": "js", + "modulePath": "plugins/grok/ask.js", + "sourceFile": "plugins/grok/ask.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "delete", + "description": "Delete a Grok conversation by ID. Grok takes effect immediately with no confirmation dialog — require --yes to actually delete.", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID or grok.com/c/ URL" + }, + { + "name": "yes", + "type": "boolean", + "default": false, + "required": false, + "help": "Actually delete (default is a dry-run preview)" + } + ], + "columns": [ + "status", + "id" + ], + "type": "js", + "modulePath": "plugins/grok/delete.js", + "sourceFile": "plugins/grok/delete.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "detail", + "description": "Open a Grok conversation by ID and read its messages", + "access": "read", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Session ID (UUID) or full https://grok.com/c/ URL" + }, + { + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/grok/detail.js", + "sourceFile": "plugins/grok/detail.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "export", + "description": "Export all visible Grok conversation history metadata", + "access": "read", + "example": "webcmd grok export -f yaml", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 0, + "required": false, + "help": "Max conversations to export; 0 means all loaded history" + }, + { + "name": "maxScrolls", + "type": "int", + "default": 80, + "required": false, + "help": "Max history-list scroll rounds when limit is 0 (max 500)" + } + ], + "columns": [ + "index", + "id", + "title", + "date", + "url" + ], + "type": "js", + "modulePath": "plugins/grok/export.js", + "sourceFile": "plugins/grok/export.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "export-all", + "description": "Export Grok conversation history and each conversation transcript", + "access": "read", + "example": "webcmd grok export-all --limit 5 -f json", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 0, + "required": false, + "help": "Max conversations to export; 0 means all loaded history" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Skip this many conversations before exporting" + }, + { + "name": "manifestPath", + "type": "string", + "default": "", + "required": false, + "help": "Optional grok/export JSON manifest path; skips history dialog and visits listed /c pages directly" + }, + { + "name": "maxScrolls", + "type": "int", + "default": 80, + "required": false, + "help": "Max history-list scroll rounds when limit is 0 (max 500)" + }, + { + "name": "pageScrolls", + "type": "int", + "default": 30, + "required": false, + "help": "Max per-conversation scroll-to-bottom rounds (max 200)" + }, + { + "name": "pageTimeoutMs", + "type": "int", + "default": 30000, + "required": false, + "help": "Max wait for each conversation page to show messages" + }, + { + "name": "delayMinMs", + "type": "int", + "default": 0, + "required": false, + "help": "Minimum polite delay after a conversation page loads" + }, + { + "name": "delayMaxMs", + "type": "int", + "default": 5000, + "required": false, + "help": "Maximum polite delay after a conversation page loads" + } + ], + "columns": [ + "index", + "id", + "title", + "date", + "url", + "status", + "messageCount", + "error", + "messagesJson" + ], + "type": "js", + "modulePath": "plugins/grok/export-all.js", + "sourceFile": "plugins/grok/export-all.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "history", + "description": "List recent Grok conversations from the sidebar (requires login)", + "access": "read", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max conversations to show (default 20, max 100)" + } + ], + "columns": [ + "Index", + "Title", + "Url" + ], + "type": "js", + "modulePath": "plugins/grok/history.js", + "sourceFile": "plugins/grok/history.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "image", + "description": "Generate images on grok.com and return image URLs", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "string", + "required": true, + "positional": true, + "help": "Image generation prompt" + }, + { + "name": "timeout", + "type": "int", + "default": 240, + "required": false, + "help": "Max seconds to wait for the image (default: 240)" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending (default: false)" + }, + { + "name": "count", + "type": "int", + "default": 1, + "required": false, + "help": "Minimum images to wait for before returning (default: 1)" + }, + { + "name": "out", + "type": "string", + "default": "", + "required": false, + "help": "Directory to save downloaded images (uses browser session to bypass auth)" + } + ], + "columns": [ + "url", + "width", + "height", + "path" + ], + "type": "js", + "modulePath": "plugins/grok/image.js", + "sourceFile": "plugins/grok/image.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "login", + "description": "Open grok login", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/grok/auth.js", + "sourceFile": "plugins/grok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "new", + "description": "Start a new conversation in Grok", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/grok/new.js", + "sourceFile": "plugins/grok/new.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "pin", + "description": "Pin a Grok conversation by ID", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID or grok.com/c/ URL" + } + ], + "columns": [ + "status", + "id" + ], + "type": "js", + "modulePath": "plugins/grok/pin.js", + "sourceFile": "plugins/grok/pin.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "read", + "description": "Read messages in the current Grok conversation", + "access": "read", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "markdown", + "type": "boolean", + "default": false, + "required": false, + "help": "Emit assistant replies as markdown" + } + ], + "columns": [ + "Role", + "Text" + ], + "type": "js", + "modulePath": "plugins/grok/read.js", + "sourceFile": "plugins/grok/read.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "send", + "description": "Fire-and-forget: send a prompt to Grok without waiting for the reply", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Prompt to send to Grok" + }, + { + "name": "new", + "type": "boolean", + "default": false, + "required": false, + "help": "Start a new chat before sending" + } + ], + "columns": [ + "Status", + "Prompt" + ], + "type": "js", + "modulePath": "plugins/grok/send.js", + "sourceFile": "plugins/grok/send.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "status", + "description": "Check Grok page availability, login state, current session and model", + "access": "read", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Status", + "Login", + "Model", + "SessionId", + "Url" + ], + "type": "js", + "modulePath": "plugins/grok/status.js", + "sourceFile": "plugins/grok/status.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "unpin", + "description": "Unpin a Grok conversation by ID", + "access": "write", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "Conversation UUID or grok.com/c/ URL" + } + ], + "columns": [ + "status", + "id" + ], + "type": "js", + "modulePath": "plugins/grok/pin.js", + "sourceFile": "plugins/grok/pin.js", + "navigateBefore": "https://grok.com", + "siteSession": "persistent" + }, + { + "site": "grok", + "name": "whoami", + "description": "Show the current logged-in grok account", + "access": "read", + "domain": "grok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" + ], + "type": "js", + "modulePath": "plugins/grok/auth.js", + "sourceFile": "plugins/grok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "hackernews", + "name": "ask", + "description": "Hacker News Ask HN posts", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/hackernews/ask.js", + "sourceFile": "plugins/hackernews/ask.js" + }, + { + "site": "hackernews", + "name": "best", + "description": "Hacker News best stories", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/hackernews/best.js", + "sourceFile": "plugins/hackernews/best.js" + }, + { + "site": "hackernews", + "name": "jobs", + "description": "Hacker News job postings", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of job postings" + } + ], + "columns": [ + "rank", + "id", + "title", + "author", + "url" + ], + "type": "js", + "modulePath": "plugins/hackernews/jobs.js", + "sourceFile": "plugins/hackernews/jobs.js" + }, + { + "site": "hackernews", + "name": "new", + "description": "Hacker News newest stories", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/hackernews/new.js", + "sourceFile": "plugins/hackernews/new.js" + }, + { + "site": "hackernews", + "name": "read", + "description": "Read a Hacker News story and its comment tree", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "HN item ID (e.g. 39847301)" + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Max top-level comments" + }, + { + "name": "depth", + "type": "int", + "default": 2, + "required": false, + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + }, + { + "name": "replies", + "type": "int", + "default": 5, + "required": false, + "help": "Max replies shown per comment at each level" + }, + { + "name": "max-length", + "type": "int", + "default": 2000, + "required": false, + "help": "Max characters per comment body (min 100)" + } + ], + "columns": [ + "type", + "author", + "score", + "text" + ], + "type": "js", + "modulePath": "plugins/hackernews/read.js", + "sourceFile": "plugins/hackernews/read.js" + }, + { + "site": "hackernews", + "name": "search", + "description": "Search Hacker News stories", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance or date", + "choices": [ + "relevance", + "date" + ] + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/hackernews/search.js", + "sourceFile": "plugins/hackernews/search.js" + }, + { + "site": "hackernews", + "name": "show", + "description": "Hacker News Show HN posts", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/hackernews/show.js", + "sourceFile": "plugins/hackernews/show.js" + }, + { + "site": "hackernews", + "name": "top", + "description": "Hacker News top stories", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], + "columns": [ + "rank", + "id", + "title", + "score", + "author", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/hackernews/top.js", + "sourceFile": "plugins/hackernews/top.js" + }, + { + "site": "hackernews", + "name": "user", + "description": "Hacker News user profile", + "access": "read", + "domain": "news.ycombinator.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "HN username" + } + ], + "columns": [ + "username", + "karma", + "created", + "about" + ], + "type": "js", + "modulePath": "plugins/hackernews/user.js", + "sourceFile": "plugins/hackernews/user.js" + }, + { + "site": "heidelberg", + "name": "export-postgraduate-courses", + "description": "Export Heidelberg University non-bachelor/postgraduate programs using the official study finder.", + "access": "read", + "example": "webcmd heidelberg export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.uni-heidelberg.de", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/heidelberg/export-postgraduate-courses.js", + "sourceFile": "plugins/heidelberg/export-postgraduate-courses.js" + }, + { + "site": "hf", + "name": "datasets", + "description": "Top Hugging Face datasets (downloads / likes / trending / freshness).", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "sort", + "type": "string", + "default": "downloads", + "required": false, + "help": "Sort key: downloads, likes, trending, created_at, last_modified" + }, + { + "name": "search", + "type": "string", + "required": false, + "help": "Optional name/owner substring filter." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max datasets (max 100; one API page)." + } + ], + "columns": [ + "rank", + "id", + "author", + "downloads", + "likes", + "tags", + "lastModified", + "url" + ], + "type": "js", + "modulePath": "plugins/hf/datasets.js", + "sourceFile": "plugins/hf/datasets.js" + }, + { + "site": "hf", + "name": "login", + "description": "Open hf login", + "access": "write", + "domain": "huggingface.co", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "username", + "fullname", + "type", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/hf/auth.js", + "sourceFile": "plugins/hf/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "hf", + "name": "models", + "description": "Top Hugging Face models (downloads / likes / trending / freshness).", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "sort", + "type": "string", + "default": "downloads", + "required": false, + "help": "Sort key: downloads, likes, trending, created_at, last_modified" + }, + { + "name": "search", + "type": "string", + "required": false, + "help": "Optional name/owner substring filter (e.g. \"llama\", \"mistralai/\")" + }, + { + "name": "pipeline", + "type": "string", + "required": false, + "help": "Filter by pipeline tag (e.g. text-generation, image-classification)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max models (max 100; one API page)." + } + ], + "columns": [ + "rank", + "id", + "author", + "pipelineTag", + "downloads", + "likes", + "tags", + "lastModified", + "url" + ], + "type": "js", + "modulePath": "plugins/hf/models.js", + "sourceFile": "plugins/hf/models.js" + }, + { + "site": "hf", + "name": "paper", + "description": "Hugging Face paper detail by arXiv id (full title / summary / authors / AI keywords)", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "arXiv id (e.g. \"1706.03762\") — same value HF uses to mirror the paper" + } + ], + "columns": [ + "id", + "title", + "authors", + "publishedAt", + "upvotes", + "aiKeywords", + "summary", + "aiSummary", + "url" + ], + "type": "js", + "modulePath": "plugins/hf/paper.js", + "sourceFile": "plugins/hf/paper.js" + }, + { + "site": "hf", + "name": "spaces", + "description": "Top Hugging Face Spaces (likes / created_at / last_modified).", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "sort", + "type": "string", + "default": "likes", + "required": false, + "help": "Sort key: likes, created_at, last_modified" + }, + { + "name": "search", + "type": "string", + "required": false, + "help": "Optional name/owner substring filter (e.g. \"stability\", \"openai/\")" + }, + { + "name": "sdk", + "type": "string", + "required": false, + "help": "Filter by Space SDK: gradio / streamlit / docker / static" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max spaces (max 100; one API page)." + } + ], + "columns": [ + "rank", + "id", + "author", + "sdk", + "likes", + "tags", + "lastModified", + "url" + ], + "type": "js", + "modulePath": "plugins/hf/spaces.js", + "sourceFile": "plugins/hf/spaces.js" + }, + { + "site": "hf", + "name": "top", + "description": "Top upvoted Hugging Face papers", + "access": "read", + "domain": "huggingface.co", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of papers" + }, + { + "name": "all", + "type": "bool", + "default": false, + "required": false, + "help": "Return all papers (ignore limit)" + }, + { + "name": "date", + "type": "str", + "required": false, + "help": "Date (YYYY-MM-DD), defaults to most recent" + }, + { + "name": "period", + "type": "str", + "default": "daily", + "required": false, + "help": "Time period: daily, weekly, or monthly", + "choices": [ + "daily", + "weekly", + "monthly" + ] + } + ], + "columns": [ + "rank", + "id", + "title", + "upvotes", + "authors" + ], + "type": "js", + "modulePath": "plugins/hf/top.js", + "sourceFile": "plugins/hf/top.js" + }, + { + "site": "hf", + "name": "whoami", + "description": "Show the current logged-in hf account", + "access": "read", + "domain": "huggingface.co", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "username", + "fullname", + "type" + ], + "type": "js", + "modulePath": "plugins/hf/auth.js", + "sourceFile": "plugins/hf/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "hft", + "name": "export-postgraduate-courses", + "description": "Export HFT Stuttgart postgraduate programs using the official public programme pages.", + "access": "read", + "example": "webcmd hft export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.hft-stuttgart.de", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/hft/export-postgraduate-courses.js", + "sourceFile": "plugins/hft/export-postgraduate-courses.js" + }, + { + "site": "homebrew", + "name": "cask", + "description": "Fetch a Homebrew cask's metadata (version, homepage, deprecation, download URL)", + "access": "read", + "domain": "formulae.brew.sh", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "token", + "type": "str", + "required": true, + "positional": true, + "help": "Cask token (e.g. \"firefox\", \"visual-studio-code\", \"google-chrome\")" + } + ], + "columns": [ + "cask", + "tap", + "name", + "version", + "description", + "homepage", + "deprecated", + "disabled", + "download", + "url" + ], + "type": "js", + "modulePath": "plugins/homebrew/cask.js", + "sourceFile": "plugins/homebrew/cask.js" + }, + { + "site": "homebrew", + "name": "formula", + "description": "Fetch a Homebrew formula's metadata (version, license, deps, deprecation, source)", + "access": "read", + "domain": "formulae.brew.sh", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Formula name (e.g. \"wget\", \"gcc@13\", \"imagemagick\")" + } + ], + "columns": [ + "formula", + "tap", + "version", + "license", + "description", + "homepage", + "dependencies", + "deprecated", + "disabled", + "source", + "url" + ], + "type": "js", + "modulePath": "plugins/homebrew/formula.js", + "sourceFile": "plugins/homebrew/formula.js" + }, + { + "site": "homebrew", + "name": "popular", + "description": "List most-installed Homebrew formulae or casks (Homebrew's analytics ranking)", + "access": "read", + "domain": "formulae.brew.sh", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "type", + "type": "str", + "default": "formula", + "required": false, + "help": "Package type (formula / cask)" + }, + { + "name": "window", + "type": "str", + "default": "30d", + "required": false, + "help": "Time window (30d / 90d / 365d)" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max rows (1-500)" + } + ], + "columns": [ + "rank", + "token", + "type", + "installs", + "percent", + "window", + "url" + ], + "type": "js", + "modulePath": "plugins/homebrew/popular.js", + "sourceFile": "plugins/homebrew/popular.js" + }, + { + "site": "iit", + "name": "export-postgraduate-courses", + "description": "Export Illinois Tech postgraduate programs using official public sources.", + "access": "read", + "example": "webcmd iit export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "www.iit.edu", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "degree-level", + "type": "string", + "default": "all", + "required": false, + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" + } + ], + "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" + ], + "type": "js", + "modulePath": "plugins/iit/export-postgraduate-courses.js", + "sourceFile": "plugins/iit/export-postgraduate-courses.js" + }, + { + "site": "imdb", + "name": "person", + "description": "Get actor or director info", + "access": "read", + "domain": "www.imdb.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "IMDb person ID (nm0634240) or URL" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max filmography entries" + } + ], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "plugins/imdb/person.js", + "sourceFile": "plugins/imdb/person.js" + }, + { + "site": "imdb", + "name": "reviews", + "description": "Get user reviews for a movie or TV show", + "access": "read", + "domain": "www.imdb.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "IMDb title ID (tt1375666) or URL" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of reviews" + } + ], + "columns": [ + "rank", + "title", + "rating", + "author", + "date", + "text" + ], + "type": "js", + "modulePath": "plugins/imdb/reviews.js", + "sourceFile": "plugins/imdb/reviews.js" + }, + { + "site": "imdb", + "name": "search", + "description": "Search IMDb for movies, TV shows, and people", + "access": "read", + "domain": "www.imdb.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "id", + "title", + "year", + "type", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/imdb/search.js", + "sourceFile": "plugins/imdb/search.js" + }, + { + "site": "imdb", + "name": "title", + "description": "Get movie or TV show details", + "access": "read", + "domain": "www.imdb.com", + "strategy": "public", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "IMDb title ID (tt1375666) or URL" + } + ], "columns": [ - "Status", - "File", - "Messages" + "field", + "value" ], "type": "js", - "modulePath": "plugins/chatwise/export.js", - "sourceFile": "plugins/chatwise/export.js", - "navigateBefore": true + "modulePath": "plugins/imdb/title.js", + "sourceFile": "plugins/imdb/title.js" }, { - "site": "chatwise", - "name": "history", - "description": "List conversation history in ChatWise sidebar", + "site": "imdb", + "name": "top", + "description": "IMDb Top 250 Movies", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.imdb.com", + "strategy": "public", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results" + } + ], "columns": [ - "Index", - "Title" + "rank", + "title", + "rating", + "votes", + "genre", + "url" ], "type": "js", - "modulePath": "plugins/chatwise/history.js", - "sourceFile": "plugins/chatwise/history.js", - "navigateBefore": true + "modulePath": "plugins/imdb/top.js", + "sourceFile": "plugins/imdb/top.js" }, { - "site": "chatwise", - "name": "model", - "description": "Get or switch the active AI model in ChatWise", + "site": "imdb", + "name": "trending", + "description": "IMDb Most Popular Movies", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.imdb.com", + "strategy": "public", "browser": true, "args": [ { - "name": "model-name", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "positional": true, - "help": "Model to switch to (e.g. gpt-4, claude-3)" + "help": "Number of results" } ], "columns": [ - "Status", - "Model" + "rank", + "title", + "rating", + "genre", + "url" ], "type": "js", - "modulePath": "plugins/chatwise/model.js", - "sourceFile": "plugins/chatwise/model.js", - "navigateBefore": true + "modulePath": "plugins/imdb/trending.js", + "sourceFile": "plugins/imdb/trending.js" }, { - "site": "chatwise", - "name": "new", - "description": "Start a new ChatWise conversation session", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "site": "indeed", + "name": "job", + "aliases": [ + "detail", + "view" + ], + "description": "Read the full Indeed job posting by jk (job key)", + "access": "read", + "domain": "www.indeed.com", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Job key (16-char hex from `indeed search`, e.g. \"dccc07ac5a6a3683\")" + } + ], "columns": [ - "Status" + "id", + "title", + "company", + "location", + "salary", + "job_type", + "description", + "url" ], "type": "js", - "modulePath": "plugins/chatwise/new.js", - "sourceFile": "plugins/chatwise/new.js", - "navigateBefore": true + "modulePath": "plugins/indeed/job.js", + "sourceFile": "plugins/indeed/job.js", + "navigateBefore": false }, { - "site": "chatwise", - "name": "read", - "description": "Read the current ChatWise conversation history", + "site": "indeed", + "name": "search", + "description": "Indeed keyword job search (rendered DOM via browser session, US site)", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.indeed.com", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Job keyword (title / skill / company)" + }, + { + "name": "location", + "type": "string", + "default": "", + "required": false, + "help": "Location filter (e.g. \"remote\", \"New York, NY\", \"San Francisco\")" + }, + { + "name": "fromage", + "type": "string", + "default": "", + "required": false, + "help": "Recency filter, days back: 1 / 3 / 7 / 14" + }, + { + "name": "sort", + "type": "string", + "default": "relevance", + "required": false, + "help": "Sort order: relevance | date" + }, + { + "name": "start", + "type": "int", + "default": 0, + "required": false, + "help": "Pagination offset (multiple of 10, 0-based)" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "Max rows to return (1-25, capped at one page)" + } + ], "columns": [ - "Content" + "rank", + "id", + "title", + "company", + "location", + "salary", + "tags", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/chatwise/read.js", - "sourceFile": "plugins/chatwise/read.js", - "navigateBefore": true + "modulePath": "plugins/indeed/search.js", + "sourceFile": "plugins/indeed/search.js", + "navigateBefore": false }, { - "site": "chatwise", - "name": "screenshot", - "description": "Capture a snapshot of the current ChatWise window (DOM + Accessibility tree)", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "instagram", + "name": "collection-create", + "description": "Create a new Instagram saved-posts collection (folder)", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "output", + "name": "name", "type": "str", - "required": false, - "help": "Output file path (default: /tmp/chatwise-snapshot.txt)" + "required": true, + "positional": true, + "help": "Name of the collection to create" } ], "columns": [ - "Status", - "File" + "status", + "collectionId", + "collectionName", + "mediaCount" ], "type": "js", - "modulePath": "plugins/chatwise/screenshot.js", - "sourceFile": "plugins/chatwise/screenshot.js", - "navigateBefore": true + "modulePath": "plugins/instagram/collection-create.js", + "sourceFile": "plugins/instagram/collection-create.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "chatwise", - "name": "send", - "description": "Send a message to the active ChatWise conversation", + "site": "instagram", + "name": "collection-delete", + "description": "Delete an Instagram saved-posts collection (folder) by name or id", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "target", + "type": "str", + "required": true, + "positional": true, + "help": "Collection name (case-insensitive) or numeric collection_id" + } + ], + "columns": [ + "status", + "collectionId", + "collectionName" + ], + "type": "js", + "modulePath": "plugins/instagram/collection-delete.js", + "sourceFile": "plugins/instagram/collection-delete.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "comment", + "description": "Comment on an Instagram post", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Username of the post author" + }, { "name": "text", "type": "str", "required": true, "positional": true, - "help": "Message to send" + "help": "Comment text" + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Post index (1 = most recent)" } ], "columns": [ - "Status", - "InjectedText" + "status", + "user", + "text" ], "type": "js", - "modulePath": "plugins/chatwise/send.js", - "sourceFile": "plugins/chatwise/send.js", - "navigateBefore": true + "modulePath": "plugins/instagram/comment.js", + "sourceFile": "plugins/instagram/comment.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "chatwise", - "name": "status", - "description": "Check active CDP connection to ChatWise Desktop", + "site": "instagram", + "name": "download", + "description": "Download images and videos from Instagram posts and reels", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "Instagram post / reel / tv URL" + }, + { + "name": "path", + "type": "str", + "default": "~/Downloads/Instagram", + "required": false, + "help": "Download directory" + } ], "type": "js", - "modulePath": "plugins/chatwise/status.js", - "sourceFile": "plugins/chatwise/status.js", - "navigateBefore": true + "modulePath": "plugins/instagram/download.js", + "sourceFile": "plugins/instagram/download.js", + "navigateBefore": false }, { - "site": "chess", - "name": "analyze", - "description": "Open a Chess.com game in the browser analysis board", + "site": "instagram", + "name": "explore", + "description": "Instagram explore/discover trending posts", "access": "read", - "domain": "www.chess.com", - "strategy": "ui", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "game-url", - "type": "string", - "required": true, - "positional": true, - "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts" } ], "columns": [ - "kind", - "game_id", - "analysis_url" + "rank", + "user", + "caption", + "likes", + "comments", + "type" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/chess/analyze.js", - "sourceFile": "plugins/chess/analyze.js", - "navigateBefore": false + "modulePath": "plugins/instagram/explore.js", + "sourceFile": "plugins/instagram/explore.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "chess", - "name": "game", - "description": "Chess.com single-game detail (white, black, result, ECO, time control) by full game URL", - "access": "read", - "domain": "www.chess.com", - "strategy": "public", - "browser": false, + "site": "instagram", + "name": "follow", + "description": "Follow an Instagram user", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "game-url", - "type": "string", + "name": "username", + "type": "str", "required": true, "positional": true, - "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" + "help": "Instagram username to follow" } ], "columns": [ - "kind", - "game_id", - "date", - "white", - "white_rating", - "black", - "black_rating", - "result", - "winner_color", - "termination", - "eco", - "time_control", - "rated", - "ply_count", - "url" + "status", + "username" ], "type": "js", - "modulePath": "plugins/chess/game.js", - "sourceFile": "plugins/chess/game.js" + "modulePath": "plugins/instagram/follow.js", + "sourceFile": "plugins/instagram/follow.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "chess", - "name": "games", - "description": "Chess.com recent games for a player, newest first", + "site": "instagram", + "name": "followers", + "description": "List followers of an Instagram user", "access": "read", - "domain": "api.chess.com", - "strategy": "public", - "browser": false, + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "username", - "type": "string", + "type": "str", "required": true, "positional": true, - "help": "Chess.com username" + "help": "Instagram username" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of recent games (1-100)" + "help": "Number of followers" } ], "columns": [ - "date", - "time_class", - "rated", - "my_color", - "my_rating", - "my_result", - "opponent", - "opponent_rating", - "accuracy_white", - "accuracy_black", - "eco", - "opening_name", - "url" + "rank", + "username", + "name", + "verified", + "private" ], "type": "js", - "modulePath": "plugins/chess/games.js", - "sourceFile": "plugins/chess/games.js" + "modulePath": "plugins/instagram/followers.js", + "sourceFile": "plugins/instagram/followers.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "chess", - "name": "stats", - "description": "Chess.com player ratings + win/loss record across game kinds", + "site": "instagram", + "name": "following", + "description": "List accounts an Instagram user is following", "access": "read", - "domain": "api.chess.com", - "strategy": "public", - "browser": false, + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "username", - "type": "string", + "type": "str", "required": true, "positional": true, - "help": "Chess.com username (case-insensitive)" + "help": "Instagram username" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of accounts" } ], "columns": [ - "kind", - "rating_current", - "rating_best", - "wins", - "losses", - "draws" + "rank", + "username", + "name", + "verified", + "private" ], "type": "js", - "modulePath": "plugins/chess/stats.js", - "sourceFile": "plugins/chess/stats.js" + "modulePath": "plugins/instagram/following.js", + "sourceFile": "plugins/instagram/following.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "cincinnati", - "name": "export-postgraduate-courses", - "description": "Export University of Cincinnati graduate and professional programs from official public sources.", - "access": "read", - "example": "webcmd cincinnati export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.grad.uc.edu", - "strategy": "public", - "browser": false, + "site": "instagram", + "name": "like", + "description": "Like an Instagram post", + "access": "write", + "domain": "www.instagram.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Username of the post author" }, { - "name": "count", + "name": "index", "type": "int", + "default": 1, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Post index (1 = most recent)" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "status", + "user", + "post" ], "type": "js", - "modulePath": "plugins/cincinnati/export-postgraduate-courses.js", - "sourceFile": "plugins/cincinnati/export-postgraduate-courses.js" + "modulePath": "plugins/instagram/like.js", + "sourceFile": "plugins/instagram/like.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "claude", - "name": "ask", - "description": "Send a prompt to Claude and get the response", + "site": "instagram", + "name": "login", + "description": "Open instagram login", "access": "write", - "domain": "claude.ai", + "domain": "instagram.com", "strategy": "cookie", "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "username", + "full_name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/instagram/auth.js", + "sourceFile": "plugins/instagram/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "instagram", + "name": "note", + "description": "Publish a text Instagram note", + "access": "write", + "domain": "www.instagram.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "prompt", + "name": "content", "type": "str", "required": true, "positional": true, - "help": "Prompt to send" + "help": "Note text (max 60 characters)" }, { "name": "timeout", "type": "int", "default": 120, "required": false, - "help": "Max seconds to wait for response" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - }, - { - "name": "model", - "type": "str", - "default": "sonnet", - "required": false, - "help": "Model to use: sonnet, opus, or haiku", - "choices": [ - "sonnet", - "opus", - "haiku" - ] - }, - { - "name": "think", - "type": "boolean", - "default": false, - "required": false, - "help": "Enable Adaptive thinking" - }, + "help": "Max seconds for the overall command (default: 120)" + } + ], + "columns": [ + "status", + "detail", + "noteId" + ], + "type": "js", + "modulePath": "plugins/instagram/note.js", + "sourceFile": "plugins/instagram/note.js", + "navigateBefore": true + }, + { + "site": "instagram", + "name": "post", + "description": "Post an Instagram feed image or mixed-media carousel", + "access": "write", + "domain": "www.instagram.com", + "strategy": "ui", + "browser": true, + "args": [ { - "name": "file", + "name": "media", "type": "str", "required": false, - "help": "Attach a file (image, PDF, text) with the prompt", + "valueRequired": true, + "help": "Comma-separated media paths (images/videos, up to 10)", "file": { "direction": "input", "pathKind": "file", - "multiple": false, + "multiple": true, + "separator": ",", "contentTypes": [ - "application/pdf", - "text/plain", - "text/markdown", - "text/csv", - "application/json", "image/jpeg", "image/png", - "image/gif", - "image/webp" + "image/webp", + "video/mp4" ], - "maxBytes": 26214400 + "maxBytes": 262144000 } + }, + { + "name": "content", + "type": "str", + "required": false, + "positional": true, + "help": "Caption text" + }, + { + "name": "timeout", + "type": "int", + "default": 300, + "required": false, + "help": "Max seconds for the overall command (default: 300)" } ], "columns": [ - "response" + "status", + "detail", + "url" ], "type": "js", - "modulePath": "plugins/claude/ask.js", - "sourceFile": "plugins/claude/ask.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/instagram/post.js", + "sourceFile": "plugins/instagram/post.js", + "navigateBefore": true }, { - "site": "claude", - "name": "detail", - "description": "Open a Claude conversation by ID and read its messages", + "site": "instagram", + "name": "profile", + "description": "Get Instagram user profile info", "access": "read", - "domain": "claude.ai", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "id", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Conversation ID (UUID from /chat/)" + "help": "Instagram username" } ], "columns": [ - "Index", - "Role", - "Text" + "username", + "name", + "followers", + "following", + "posts", + "verified", + "bio" ], "type": "js", - "modulePath": "plugins/claude/detail.js", - "sourceFile": "plugins/claude/detail.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/instagram/profile.js", + "sourceFile": "plugins/instagram/profile.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "claude", - "name": "history", - "description": "List conversation history from Claude /recents", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", + "site": "instagram", + "name": "reel", + "description": "Post an Instagram reel video", + "access": "write", + "domain": "www.instagram.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "limit", + "name": "video", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Path to a single .mp4 video file", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "video/mp4" + ], + "maxBytes": 262144000 + } + }, + { + "name": "content", + "type": "str", + "required": false, + "positional": true, + "help": "Caption text" + }, + { + "name": "timeout", "type": "int", - "default": 20, + "default": 600, "required": false, - "help": "Max conversations to show" + "help": "Max seconds for the overall command (default: 600)" } ], "columns": [ - "Index", - "Id", - "Title", - "Url" + "status", + "detail", + "url" ], "type": "js", - "modulePath": "plugins/claude/history.js", - "sourceFile": "plugins/claude/history.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/instagram/reel.js", + "sourceFile": "plugins/instagram/reel.js", + "navigateBefore": true }, { - "site": "claude", - "name": "login", - "description": "Open claude login", + "site": "instagram", + "name": "save", + "description": "Save (bookmark) an Instagram post", "access": "write", - "domain": "claude.ai", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Username of the post author" + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Post index (1 = most recent)" + } + ], "columns": [ "status", - "logged_in", - "site", - "user_id", - "org_name", - "org_uuid", - "action", - "verify_command" + "user", + "post" ], "type": "js", - "modulePath": "plugins/claude/auth.js", - "sourceFile": "plugins/claude/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/instagram/save.js", + "sourceFile": "plugins/instagram/save.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "claude", - "name": "new", - "description": "Start a new conversation in Claude", + "site": "instagram", + "name": "saved", + "description": "Get your saved Instagram posts (optionally from a specific collection)", "access": "read", - "domain": "claude.ai", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of saved posts" + }, + { + "name": "collection", + "type": "str", + "required": false, + "help": "Collection name (case-insensitive). Omit for the default \"All posts\" feed." + } + ], "columns": [ - "Status" + "index", + "user", + "caption", + "likes", + "comments", + "type" ], "type": "js", - "modulePath": "plugins/claude/new.js", - "sourceFile": "plugins/claude/new.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/instagram/saved.js", + "sourceFile": "plugins/instagram/saved.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "claude", - "name": "read", - "description": "Read the current Claude conversation", + "site": "instagram", + "name": "search", + "description": "Search Instagram users", "access": "read", - "domain": "claude.ai", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], "columns": [ - "Index", - "Role", - "Text" + "rank", + "username", + "name", + "verified", + "private", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/instagram/search.js", + "sourceFile": "plugins/instagram/search.js", + "navigateBefore": "https://www.instagram.com" + }, + { + "site": "instagram", + "name": "story", + "description": "Post a single Instagram story image or video", + "access": "write", + "domain": "www.instagram.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "media", + "type": "str", + "required": false, + "valueRequired": true, + "help": "Path to a single story image or video file" + }, + { + "name": "timeout", + "type": "int", + "default": 300, + "required": false, + "help": "Max seconds for the overall command (default: 300)" + } + ], + "columns": [ + "status", + "detail", + "url" ], "type": "js", - "modulePath": "plugins/claude/read.js", - "sourceFile": "plugins/claude/read.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/instagram/story.js", + "sourceFile": "plugins/instagram/story.js", + "navigateBefore": true }, { - "site": "claude", - "name": "send", - "description": "Send a prompt to Claude without waiting for the response", + "site": "instagram", + "name": "unfollow", + "description": "Unfollow an Instagram user", "access": "write", - "domain": "claude.ai", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Prompt to send" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" + "help": "Instagram username to unfollow" } ], "columns": [ - "Status", - "SubmittedBy", - "InjectedText" + "status", + "username" ], "type": "js", - "modulePath": "plugins/claude/send.js", - "sourceFile": "plugins/claude/send.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/instagram/unfollow.js", + "sourceFile": "plugins/instagram/unfollow.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "claude", - "name": "status", - "description": "Check Claude page availability and login state", - "access": "read", - "domain": "claude.ai", + "site": "instagram", + "name": "unlike", + "description": "Unlike an Instagram post", + "access": "write", + "domain": "www.instagram.com", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "Status", - "Login", - "Url" + "args": [ + { + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "Username of the post author" + }, + { + "name": "index", + "type": "int", + "default": 1, + "required": false, + "help": "Post index (1 = most recent)" + } ], - "type": "js", - "modulePath": "plugins/claude/status.js", - "sourceFile": "plugins/claude/status.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "whoami", - "description": "Show the current logged-in claude account", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], "columns": [ - "logged_in", - "site", - "user_id", - "org_name", - "org_uuid" + "status", + "user", + "post" ], "type": "js", - "modulePath": "plugins/claude/auth.js", - "sourceFile": "plugins/claude/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/instagram/unlike.js", + "sourceFile": "plugins/instagram/unlike.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "codex", - "name": "archive", - "description": "Archive (Codex's term for delete) the selected conversation via the Chat actions header menu. No confirmation in UI — pass --yes to actually archive.", + "site": "instagram", + "name": "unsave", + "description": "Unsave (remove bookmark) an Instagram post", "access": "write", - "domain": "localhost", - "strategy": "ui", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "yes", - "type": "boolean", - "default": false, - "required": false, - "help": "Actually archive (default: dry-run preview)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", + "name": "username", "type": "str", - "required": false, - "help": "Conversation title to select within --project" + "required": true, + "positional": true, + "help": "Username of the post author" }, { "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", + "type": "int", + "default": 1, "required": false, - "help": "Exact Codex thread id to select" + "help": "Post index (1 = most recent)" } ], "columns": [ "status", - "thread_id", - "project", - "conversation" + "user", + "post" ], "type": "js", - "modulePath": "plugins/codex/archive.js", - "sourceFile": "plugins/codex/archive.js", - "navigateBefore": true + "modulePath": "plugins/instagram/unsave.js", + "sourceFile": "plugins/instagram/unsave.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "codex", - "name": "ask", - "description": "Send a prompt to the current or selected Codex conversation and wait for the AI response", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "site": "instagram", + "name": "user", + "description": "Get recent posts from an Instagram user", + "access": "read", + "domain": "www.instagram.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "text", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Prompt to send" + "help": "Instagram username" }, { - "name": "timeout", + "name": "limit", "type": "int", - "default": 60, - "required": false, - "help": "Max seconds to wait for response (default: 60)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", + "default": 12, "required": false, - "help": "Exact Codex thread id to select" + "help": "Number of posts" } ], "columns": [ - "Role", - "Project", - "Conversation", - "Text" + "index", + "caption", + "likes", + "comments", + "type", + "date" ], "type": "js", - "modulePath": "plugins/codex/ask.js", - "sourceFile": "plugins/codex/ask.js", - "navigateBefore": true + "modulePath": "plugins/instagram/user.js", + "sourceFile": "plugins/instagram/user.js", + "navigateBefore": "https://www.instagram.com" }, { - "site": "codex", - "name": "dump", - "description": "Dump the DOM and Accessibility tree of codex for reverse-engineering", + "site": "instagram", + "name": "whoami", + "description": "Show the current logged-in instagram account", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "instagram.com", + "strategy": "cookie", "browser": true, "args": [], "columns": [ - "action", - "files" + "logged_in", + "site", + "user_id", + "username", + "full_name" ], "type": "js", - "modulePath": "plugins/codex/dump.js", - "sourceFile": "plugins/codex/dump.js", - "navigateBefore": true + "modulePath": "plugins/instagram/auth.js", + "sourceFile": "plugins/instagram/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "codex", - "name": "export", - "description": "Export the current Codex conversation to a Markdown file", + "site": "jhu", + "name": "export-postgraduate-courses", + "description": "Export Johns Hopkins University postgraduate programs using the official Academic Catalogue.", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "example": "webcmd jhu export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "e-catalogue.jhu.edu", + "strategy": "public", + "browser": false, "args": [ { - "name": "output", - "type": "str", + "name": "degree-level", + "type": "string", + "default": "all", "required": false, - "help": "Output file (default: /tmp/codex-export.md)" + "help": "all, masters, certificate, diploma, professional, or doctorate" + }, + { + "name": "count", + "type": "int", + "required": false, + "help": "Positive maximum number of programs after filtering and deduplication" } ], "columns": [ + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", "Status", - "File", - "Messages" - ], - "type": "js", - "modulePath": "plugins/codex/export.js", - "sourceFile": "plugins/codex/export.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "extract-diff", - "description": "Extract visual code review diff patches from Codex", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "File", - "Diff" + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/codex/extract-diff.js", - "sourceFile": "plugins/codex/extract-diff.js", - "navigateBefore": true + "modulePath": "plugins/jhu/export-postgraduate-courses.js", + "sourceFile": "plugins/jhu/export-postgraduate-courses.js" }, { - "site": "codex", - "name": "history", - "description": "List visible Codex conversation threads grouped by project", + "site": "jira", + "name": "attachments", + "description": "Jira issue attachment metadata", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "project", - "type": "str", - "required": false, - "help": "Filter by project label or path" - }, - { - "name": "limit", + "name": "key", "type": "str", - "required": false, - "help": "Max conversations per project" + "required": true, + "positional": true, + "help": "Jira issue key, e.g. PROJ-123" } ], "columns": [ - "Project", - "Index", - "Title", - "Updated", - "Active" + "id", + "filename", + "mimeType", + "size", + "url" ], "type": "js", - "modulePath": "plugins/codex/history.js", - "sourceFile": "plugins/codex/history.js", - "navigateBefore": true + "modulePath": "plugins/jira/attachments.js", + "sourceFile": "plugins/jira/attachments.js" }, { - "site": "codex", - "name": "model", - "description": "Read, list, or switch the active model / reasoning level in Codex Desktop. The composer toolbar button toggles a menu that mixes model variants (GPT-5.5, Speed) with reasoning levels (Low/Medium/High/Extra High).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "site": "jira", + "name": "comments", + "description": "Jira issue comments as Markdown", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "name", + "name": "key", "type": "str", - "required": false, + "required": true, "positional": true, - "help": "Substring (case-insensitive) of a model / reasoning level to switch to. Omit to read current." + "help": "Jira issue key, e.g. PROJ-123" }, { - "name": "list", - "type": "boolean", - "default": false, + "name": "limit", + "type": "int", + "default": 50, "required": false, - "help": "List all menu options (does not switch)" + "help": "Max comments to return (1-100)" } ], "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "plugins/codex/model.js", - "sourceFile": "plugins/codex/model.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "new", - "description": "Start a new Codex conversation session", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" + "id", + "author", + "created", + "updated", + "markdown" ], "type": "js", - "modulePath": "plugins/codex/new.js", - "sourceFile": "plugins/codex/new.js", - "navigateBefore": true + "modulePath": "plugins/jira/comments.js", + "sourceFile": "plugins/jira/comments.js" }, { - "site": "codex", - "name": "pin", - "description": "Pin the selected Codex conversation via the Chat actions header menu.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "site": "jira", + "name": "issue", + "description": "Jira issue detail normalized for agents (description, comments, attachments, links)", + "access": "read", + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", + "name": "key", "type": "str", - "required": false, - "help": "1-based conversation index within --project" + "required": true, + "positional": true, + "help": "Jira issue key, e.g. PROJ-123" }, { - "name": "thread-id", - "type": "str", + "name": "comments-limit", + "type": "int", + "default": 100, "required": false, - "help": "Exact Codex thread id to select" + "help": "Max comments to include (1-100)" } ], "columns": [ + "key", + "summary", + "issueType", "status", - "thread_id", - "project", - "conversation" + "priority", + "assignee", + "updated", + "url" ], "type": "js", - "modulePath": "plugins/codex/pin.js", - "sourceFile": "plugins/codex/pin.js", - "navigateBefore": true + "modulePath": "plugins/jira/issue.js", + "sourceFile": "plugins/jira/issue.js" }, { - "site": "codex", - "name": "projects", - "description": "List Codex projects and visible conversations from the sidebar", + "site": "jira", + "name": "links", + "description": "Jira issue links", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "project", - "type": "str", - "required": false, - "help": "Filter by project label or path" - }, - { - "name": "limit", + "name": "key", "type": "str", - "required": false, - "help": "Max conversations per project" + "required": true, + "positional": true, + "help": "Jira issue key, e.g. PROJ-123" } ], "columns": [ - "Project", - "Index", - "Title", - "Updated", - "Active" + "key", + "type", + "direction" ], "type": "js", - "modulePath": "plugins/codex/projects.js", - "sourceFile": "plugins/codex/projects.js", - "navigateBefore": true + "modulePath": "plugins/jira/links.js", + "sourceFile": "plugins/jira/links.js" }, { - "site": "codex", - "name": "read", - "description": "Read the contents of the current or selected Codex conversation thread", + "site": "jira", + "name": "search", + "description": "Search Jira issues with JQL", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "domain": "atlassian.net", + "strategy": "public", + "browser": false, "args": [ { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", + "name": "jql", "type": "str", - "required": false, - "help": "1-based conversation index within --project" + "required": true, + "positional": true, + "help": "JQL query, e.g. \"project = PROJ order by updated desc\"" }, { - "name": "thread-id", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Exact Codex thread id to select" + "help": "Max issues to return (1-100)" } ], "columns": [ - "Project", - "Conversation", - "Content" + "key", + "summary", + "issueType", + "status", + "priority", + "assignee", + "updated", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/codex/read.js", - "sourceFile": "plugins/codex/read.js", - "navigateBefore": true + "modulePath": "plugins/jira/search.js", + "sourceFile": "plugins/jira/search.js" }, { - "site": "codex", - "name": "rename", - "description": "Rename the selected Codex conversation. Opens the Chat actions menu → \"Rename chat\", then types the new title.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "site": "lesswrong", + "name": "comments", + "description": "Top comments on a post", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "title", - "type": "str", + "name": "url-or-id", + "type": "string", "required": true, "positional": true, - "help": "New title (single line, no newlines)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" + "help": "Post URL or LessWrong post ID" }, { - "name": "thread-id", - "type": "str", + "name": "limit", + "type": "int", + "default": 5, "required": false, - "help": "Exact Codex thread id to select" + "help": "Number of comments" } ], "columns": [ - "status", - "title", - "thread_id", - "project" + "rank", + "score", + "author", + "text" ], "type": "js", - "modulePath": "plugins/codex/rename.js", - "sourceFile": "plugins/codex/rename.js", - "navigateBefore": true + "modulePath": "plugins/lesswrong/comments.js", + "sourceFile": "plugins/lesswrong/comments.js" }, { - "site": "codex", - "name": "screenshot", - "description": "Capture a snapshot of the current Codex window (DOM + Accessibility tree)", + "site": "lesswrong", + "name": "curated", + "description": "Curated editor's picks", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "output", - "type": "str", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Output file path (default: /tmp/codex-snapshot.txt)" + "help": "Number of results" } ], "columns": [ - "Status", - "File" + "rank", + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/codex/screenshot.js", - "sourceFile": "plugins/codex/screenshot.js", - "navigateBefore": true + "modulePath": "plugins/lesswrong/curated.js", + "sourceFile": "plugins/lesswrong/curated.js" }, { - "site": "codex", - "name": "send", - "description": "Send text/commands to the current or selected Codex AI composer", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "site": "lesswrong", + "name": "frontpage", + "description": "Algorithmic frontpage", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Text, command (e.g. /review), or skill (e.g. $imagegen)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Exact Codex thread id to select" + "help": "Number of results" } ], "columns": [ - "Status", - "Project", - "Conversation", - "InjectedText" + "rank", + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/codex/send.js", - "sourceFile": "plugins/codex/send.js", - "navigateBefore": true + "modulePath": "plugins/lesswrong/frontpage.js", + "sourceFile": "plugins/lesswrong/frontpage.js" }, { - "site": "codex", - "name": "status", - "description": "Check active CDP connection to OpenAI Codex App", + "site": "lesswrong", + "name": "new", + "description": "Latest posts", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of results" + } + ], "columns": [ - "Status", - "Url", - "Title" + "rank", + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/codex/status.js", - "sourceFile": "plugins/codex/status.js", - "navigateBefore": true + "modulePath": "plugins/lesswrong/new.js", + "sourceFile": "plugins/lesswrong/new.js" }, { - "site": "codex", - "name": "unpin", - "description": "Unpin the selected Codex conversation via the Chat actions header menu.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "site": "lesswrong", + "name": "read", + "description": "Read full post by URL or ID", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, + "name": "url-or-id", + "type": "string", + "required": true, + "positional": true, + "help": "Post URL or LessWrong post ID" + } + ], + "columns": [ + "title", + "author", + "karma", + "comments", + "tags", + "content", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/read.js", + "sourceFile": "plugins/lesswrong/read.js" + }, + { + "site": "lesswrong", + "name": "sequences", + "description": "List post collections", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ { - "name": "thread-id", - "type": "str", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Exact Codex thread id to select" + "help": "Number of results" } ], "columns": [ - "status", - "thread_id", - "project", - "conversation" + "rank", + "title", + "author" ], "type": "js", - "modulePath": "plugins/codex/pin.js", - "sourceFile": "plugins/codex/pin.js", - "navigateBefore": true + "modulePath": "plugins/lesswrong/sequences.js", + "sourceFile": "plugins/lesswrong/sequences.js" }, { - "site": "coingecko", - "name": "categories", - "description": "Crypto categories ranked by aggregated market cap", + "site": "lesswrong", + "name": "shortform", + "description": "Quick takes / shortform posts", "access": "read", - "domain": "api.coingecko.com", + "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ - { - "name": "sort", - "type": "str", - "default": "market_cap_desc", - "required": false, - "help": "Sort order (market_cap_desc / market_cap_asc / name_desc / name_asc / market_cap_change_24h_desc / market_cap_change_24h_asc)" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Number of categories (1-100; CoinGecko returns ~120 max)" + "help": "Number of results" } ], "columns": [ "rank", - "id", - "name", - "marketCap", - "volume24h", - "marketCapChange24hPct", - "top3Coins" + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/coingecko/categories.js", - "sourceFile": "plugins/coingecko/categories.js" + "modulePath": "plugins/lesswrong/shortform.js", + "sourceFile": "plugins/lesswrong/shortform.js" }, { - "site": "coingecko", - "name": "coin", - "description": "Fetch a single cryptocurrency's market data by CoinGecko id (e.g. bitcoin, ethereum).", + "site": "lesswrong", + "name": "tag", + "description": "Posts by tag", "access": "read", - "domain": "api.coingecko.com", + "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ { - "name": "id", + "name": "tag", "type": "string", "required": true, "positional": true, - "help": "CoinGecko coin id (lowercase, e.g. bitcoin / ethereum / solana)." + "help": "Tag slug or name" }, { - "name": "currency", - "type": "string", - "default": "usd", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Quote currency (usd, cny, eur, jpy, ...)." + "help": "Number of results" } ], "columns": [ - "id", - "symbol", - "name", "rank", - "price", - "marketCap", - "volume24h", - "change24hPct", - "change7dPct", - "change30dPct", - "ath", - "athDate", - "atl", - "atlDate", - "circulatingSupply", - "totalSupply", - "maxSupply", - "genesisDate", - "homepage" + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/coingecko/coin.js", - "sourceFile": "plugins/coingecko/coin.js" + "modulePath": "plugins/lesswrong/tag.js", + "sourceFile": "plugins/lesswrong/tag.js" }, { - "site": "coingecko", - "name": "derivatives", - "description": "Top crypto derivative (perpetual / futures) markets by 24h volume", + "site": "lesswrong", + "name": "tags", + "description": "List popular tags", "access": "read", - "domain": "api.coingecko.com", + "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ @@ -5356,2004 +13529,2209 @@ "type": "int", "default": 20, "required": false, - "help": "Max rows to return (1-500; CoinGecko returns one large page)." - }, - { - "name": "symbol", - "type": "string", - "required": false, - "help": "Optional symbol substring filter (e.g. \"BTC\", \"ETHUSDT\")." + "help": "Number of results" } ], "columns": [ "rank", - "market", - "symbol", - "indexId", - "contractType", - "price", - "change24hPct", - "fundingRate", - "openInterestUsd", - "volume24hUsd", - "expired" + "name", + "posts" ], "type": "js", - "modulePath": "plugins/coingecko/derivatives.js", - "sourceFile": "plugins/coingecko/derivatives.js" + "modulePath": "plugins/lesswrong/tags.js", + "sourceFile": "plugins/lesswrong/tags.js" }, { - "site": "coingecko", - "name": "exchanges", - "description": "Top crypto exchanges by 24h BTC trading volume", + "site": "lesswrong", + "name": "top", + "description": "Top all-time", "access": "read", - "domain": "api.coingecko.com", + "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ { "name": "limit", "type": "int", - "default": 20, - "required": false, - "help": "Number of exchanges (1-250, CoinGecko per_page upper bound)" - }, - { - "name": "page", - "type": "int", - "default": 1, + "default": 10, "required": false, - "help": "Page number (1-based)" + "help": "Number of results" } ], "columns": [ "rank", - "id", - "name", - "trustScore", - "volume24hBtc", - "country", - "yearEstablished", + "title", + "author", + "karma", + "comments", "url" ], "type": "js", - "modulePath": "plugins/coingecko/exchanges.js", - "sourceFile": "plugins/coingecko/exchanges.js" + "modulePath": "plugins/lesswrong/top.js", + "sourceFile": "plugins/lesswrong/top.js" }, { - "site": "coingecko", - "name": "global", - "description": "Aggregate crypto market stats: total market cap, volume, dominance", + "site": "lesswrong", + "name": "top-month", + "description": "Top this month", "access": "read", - "domain": "api.coingecko.com", + "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ { - "name": "currency", - "type": "string", - "default": "usd", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Quote currency for total market cap / volume (usd, cny, eur, jpy, ...)" + "help": "Number of results" } ], "columns": [ - "currency", - "totalMarketCap", - "totalVolume24h", - "marketCapChange24hPct", - "btcDominancePct", - "ethDominancePct", - "activeCryptocurrencies", - "markets", - "ongoingIcos", - "updatedAt" + "rank", + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/coingecko/global.js", - "sourceFile": "plugins/coingecko/global.js" + "modulePath": "plugins/lesswrong/top-month.js", + "sourceFile": "plugins/lesswrong/top-month.js" }, { - "site": "coingecko", - "name": "top", - "description": "Cryptocurrency quotes by market cap (default USD)", + "site": "lesswrong", + "name": "top-week", + "description": "Top this week", "access": "read", - "domain": "api.coingecko.com", + "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ { - "name": "currency", - "type": "string", - "default": "usd", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "quote currency (usd / cny / eur / jpy ...)" - }, + "help": "Number of results" + } + ], + "columns": [ + "rank", + "title", + "author", + "karma", + "comments", + "url" + ], + "type": "js", + "modulePath": "plugins/lesswrong/top-week.js", + "sourceFile": "plugins/lesswrong/top-week.js" + }, + { + "site": "lesswrong", + "name": "top-year", + "description": "Top this year", + "access": "read", + "domain": "www.lesswrong.com", + "strategy": "public", + "browser": false, + "args": [ { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Number to return (default 10, maximum 250)" + "help": "Number of results" } ], "columns": [ "rank", - "symbol", - "name", - "price", - "change24hPct", - "marketCap", - "volume24h", - "high24h", - "low24h" + "title", + "author", + "karma", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/coingecko/top.js", - "sourceFile": "plugins/coingecko/top.js" + "modulePath": "plugins/lesswrong/top-year.js", + "sourceFile": "plugins/lesswrong/top-year.js" }, { - "site": "coingecko", - "name": "trending", - "description": "Top trending cryptocurrencies on CoinGecko in the last 24h (search-volume based).", + "site": "lesswrong", + "name": "user", + "description": "User profile", "access": "read", - "domain": "api.coingecko.com", + "domain": "www.lesswrong.com", "strategy": "public", "browser": false, - "args": [], + "args": [ + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "LessWrong username or slug" + } + ], "columns": [ - "rank", - "id", - "symbol", - "name", - "marketCapRank", - "priceBtc", - "thumb" + "field", + "value" ], "type": "js", - "modulePath": "plugins/coingecko/trending.js", - "sourceFile": "plugins/coingecko/trending.js" + "modulePath": "plugins/lesswrong/user.js", + "sourceFile": "plugins/lesswrong/user.js" }, { - "site": "concordia", - "name": "export-postgraduate-courses", - "description": "Export Concordia University Montreal postgraduate programs using official public sources.", + "site": "lesswrong", + "name": "user-posts", + "description": "List a user's posts", "access": "read", - "example": "webcmd concordia export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.concordia.ca", + "domain": "www.lesswrong.com", "strategy": "public", "browser": false, "args": [ { - "name": "degree-level", + "name": "username", "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "required": true, + "positional": true, + "help": "LessWrong username or slug" }, { - "name": "count", + "name": "limit", "type": "int", + "default": 10, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Number of results" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "rank", + "title", + "karma", + "comments", + "date", + "url" ], "type": "js", - "modulePath": "plugins/concordia/export-postgraduate-courses.js", - "sourceFile": "plugins/concordia/export-postgraduate-courses.js" + "modulePath": "plugins/lesswrong/user-posts.js", + "sourceFile": "plugins/lesswrong/user-posts.js" }, { - "site": "confluence", - "name": "create", - "description": "Create a Confluence page from Markdown or storage XHTML", - "access": "write", - "domain": "atlassian.net", + "site": "lichess", + "name": "top", + "description": "Top-N Lichess leaderboard for a perf type (bullet/blitz/rapid/classical/...)", + "access": "read", + "domain": "lichess.org", "strategy": "public", "browser": false, "args": [ { - "name": "space", - "type": "string", - "required": true, - "help": "Cloud space id, or Data Center space key" - }, - { - "name": "title", - "type": "string", - "required": true, - "help": "Page title" - }, - { - "name": "file", - "type": "string", + "name": "perf", + "type": "str", "required": true, - "help": "Markdown file path" - }, - { - "name": "parent", - "type": "string", - "required": false, - "help": "Optional parent page id" - }, - { - "name": "representation", - "type": "string", - "default": "markdown", - "required": false, - "help": "Input file format", - "choices": [ - "markdown", - "storage" - ] + "positional": true, + "help": "Perf type (bullet, blitz, rapid, classical, ultraBullet, chess960, ...)" }, { - "name": "execute", - "type": "boolean", + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Actually create the remote page" + "help": "Top-N rows (1-200)" } ], "columns": [ - "status", + "rank", + "username", "id", "title", - "spaceId", - "spaceKey", - "version", + "rating", + "progress", + "patron", "url" ], "type": "js", - "modulePath": "plugins/confluence/create.js", - "sourceFile": "plugins/confluence/create.js" + "modulePath": "plugins/lichess/top.js", + "sourceFile": "plugins/lichess/top.js" }, { - "site": "confluence", - "name": "page", - "description": "Confluence page by id with storage and Markdown body", + "site": "lichess", + "name": "user", + "description": "Fetch a Lichess player profile by username (rating, perfs, counts)", "access": "read", - "domain": "atlassian.net", + "domain": "lichess.org", "strategy": "public", "browser": false, "args": [ { - "name": "id", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Confluence page id" + "help": "Lichess username (case-insensitive)" } ], "columns": [ + "username", "id", "title", - "status", - "spaceId", - "spaceKey", - "version", + "patron", + "online", + "tosViolation", + "createdAt", + "seenAt", + "gamesAll", + "gamesWin", + "gamesLoss", + "gamesDraw", + "topPerfName", + "topPerfRating", + "topPerfGames", + "fideRating", + "country", + "bio", "url" ], "type": "js", - "modulePath": "plugins/confluence/page.js", - "sourceFile": "plugins/confluence/page.js" + "modulePath": "plugins/lichess/user.js", + "sourceFile": "plugins/lichess/user.js" }, { - "site": "confluence", - "name": "search", - "description": "Search Confluence content with CQL", + "site": "linkedin", + "name": "company", + "description": "Read a LinkedIn company page: industry, size, HQ, founded, website, followers, and about text", "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "cql", - "type": "str", - "required": true, - "positional": true, - "help": "CQL query, e.g. \"type = page and title ~ \\\"RCA\\\"\"" - }, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "space", + "name": "company", "type": "string", - "required": false, - "help": "Limit search to a Confluence space key" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results to return (1-100)" + "required": true, + "positional": true, + "help": "Company universal name, /company/ path, or full URL" } ], "columns": [ - "id", - "title", - "type", - "spaceKey", - "status", - "lastModified", + "name", + "industry", + "size", + "headquarters", + "founded", + "website", + "specialties", + "followers", + "about", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/confluence/search.js", - "sourceFile": "plugins/confluence/search.js" + "modulePath": "plugins/linkedin/company.js", + "sourceFile": "plugins/linkedin/company.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "confluence", - "name": "update", - "description": "Update a Confluence page body from Markdown or storage XHTML", + "site": "linkedin", + "name": "connect", + "description": "Fail-closed LinkedIn connection request sender that verifies the exact profile before optionally sending a note", "access": "write", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "id", - "type": "str", + "name": "profile-url", + "type": "string", "required": true, "positional": true, - "help": "Confluence page id" + "help": "Exact LinkedIn profile URL to open and verify" }, { - "name": "file", + "name": "expected-name", "type": "string", "required": true, - "help": "Markdown file path" - }, - { - "name": "title", - "type": "string", - "required": false, - "help": "Optional replacement title; defaults to current title" - }, - { - "name": "version-message", - "type": "string", - "required": false, - "help": "Confluence version message" + "help": "Expected visible profile name" }, { - "name": "representation", + "name": "note", "type": "string", - "default": "markdown", + "default": "", "required": false, - "help": "Input file format", - "choices": [ - "markdown", - "storage" - ] + "help": "Optional connection note, max 300 chars" }, { - "name": "execute", - "type": "boolean", + "name": "send", + "type": "bool", + "default": false, "required": false, - "help": "Actually update the remote page" + "help": "Actually click Send. Default is dry-run verification only." } ], "columns": [ "status", - "id", - "title", - "spaceId", - "spaceKey", - "version", - "url" + "recipient", + "reason", + "profile_url", + "note_chars", + "connectable", + "delivery_verified", + "matched_invitation_name", + "matched_invitation_url", + "actualValue", + "blockReason", + "expectedValue", + "observedUrl", + "safety" ], "type": "js", - "modulePath": "plugins/confluence/update.js", - "sourceFile": "plugins/confluence/update.js" + "modulePath": "plugins/linkedin/connect.js", + "sourceFile": "plugins/linkedin/connect.js", + "navigateBefore": true }, { - "site": "coupang", - "name": "add-to-cart", - "description": "Add a Coupang product to cart using logged-in browser session", - "access": "write", - "domain": "www.coupang.com", + "site": "linkedin", + "name": "connections", + "description": "List your LinkedIn first-degree connections with names, headlines, and profile URLs", + "access": "read", + "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "product-id", - "type": "str", - "required": false, - "positional": true, - "help": "Coupang product ID" - }, - { - "name": "url", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Canonical product URL" + "help": "Number of connections to return (max 500)" } ], "columns": [ - "ok", - "product_id", - "url", - "message" - ], - "type": "js", - "modulePath": "plugins/coupang/add-to-cart.js", - "sourceFile": "plugins/coupang/add-to-cart.js", - "navigateBefore": "https://www.coupang.com" - }, - { - "site": "coupang", - "name": "login", - "description": "Open coupang login", - "access": "write", - "domain": "coupang.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", + "rank", "name", - "action", - "verify_command" + "occupation", + "public_id", + "connected_at", + "url" ], "type": "js", - "modulePath": "plugins/coupang/auth.js", - "sourceFile": "plugins/coupang/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/linkedin/connections.js", + "sourceFile": "plugins/linkedin/connections.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "coupang", - "name": "product", - "description": "Read full product detail (price, rating, seller, delivery) for a Coupang product", + "site": "linkedin", + "name": "inbox", + "description": "List LinkedIn messaging inbox conversations and unread messages", "access": "read", - "domain": "www.coupang.com", + "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "product-id", - "type": "str", + "name": "limit", + "type": "int", + "default": 40, "required": false, - "positional": true, - "help": "Coupang product ID (digits only)" + "help": "Maximum conversations to return (1-100)" }, { - "name": "url", - "type": "str", + "name": "unread-only", + "type": "bool", + "default": false, "required": false, - "help": "Canonical Coupang product URL (alternative to --product-id)" + "help": "Return only conversations with unread messages" } ], "columns": [ - "product_id", - "title", - "price", - "original_price", - "discount_rate", - "rating", - "review_count", - "seller", - "brand", - "rocket", - "delivery_promise", - "image_url", - "url" + "rank", + "thread_url", + "thread_id", + "person_name", + "last_message_preview", + "unread", + "counterparty_type", + "category", + "timestamp" ], "type": "js", - "modulePath": "plugins/coupang/product.js", - "sourceFile": "plugins/coupang/product.js", - "navigateBefore": "https://www.coupang.com" - }, - { - "site": "coupang", - "name": "search", - "description": "Search Coupang products with logged-in browser session", - "access": "read", - "domain": "www.coupang.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Search result page number" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (max 50)" - }, + "modulePath": "plugins/linkedin/inbox.js", + "sourceFile": "plugins/linkedin/inbox.js", + "navigateBefore": "https://www.linkedin.com" + }, + { + "site": "linkedin", + "name": "job-detail", + "description": "Read one LinkedIn job page with description, apply URL, workplace type, applicants, and company metadata", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "filter", - "type": "str", - "required": false, - "help": "Optional search filter (currently supports: rocket)" + "name": "job-url", + "type": "string", + "required": true, + "positional": true, + "help": "Exact LinkedIn job URL, e.g. https://www.linkedin.com/jobs/view/123/" } ], "columns": [ - "rank", - "product_id", "title", - "price", - "unit_price", - "rating", - "review_count", - "rocket", - "delivery_type", - "delivery_promise", - "url" - ], - "tags": [ - "search" + "company", + "location", + "workplace_type", + "job_type", + "applicants", + "listed", + "apply_url", + "company_url", + "url", + "description" ], "type": "js", - "modulePath": "plugins/coupang/search.js", - "sourceFile": "plugins/coupang/search.js", - "navigateBefore": "https://www.coupang.com" + "modulePath": "plugins/linkedin/job-detail.js", + "sourceFile": "plugins/linkedin/job-detail.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "coupang", - "name": "whoami", - "description": "Show the current logged-in coupang account", + "site": "linkedin", + "name": "jobs-preferences", + "description": "Read visible LinkedIn Jobs preferences and alert settings without changing them", "access": "read", - "domain": "coupang.com", + "domain": "www.linkedin.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "name" + "open_to_work", + "job_titles", + "locations", + "job_alerts", + "preferences_url", + "alerts_url", + "raw_preferences" ], "type": "js", - "modulePath": "plugins/coupang/auth.js", - "sourceFile": "plugins/coupang/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/linkedin/jobs-preferences.js", + "sourceFile": "plugins/linkedin/jobs-preferences.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "crates", - "name": "crate", - "description": "Single crates.io crate metadata (latest version, downloads, license, repo)", - "access": "read", - "domain": "crates.io", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "crates.io crate name (e.g. \"serde\", \"tokio\")" - } - ], + "site": "linkedin", + "name": "login", + "description": "Open linkedin login", + "access": "write", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ + "status", + "logged_in", + "site", + "public_id", + "plain_id", "name", - "latestVersion", - "description", - "downloads", - "recentDownloads", - "versions", - "license", - "homepage", - "documentation", - "repository", - "keywords", - "categories", - "created", - "updated", - "url" + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/crates/crate.js", - "sourceFile": "plugins/crates/crate.js" + "modulePath": "plugins/linkedin/auth.js", + "sourceFile": "plugins/linkedin/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "crates", - "name": "search", - "description": "Search the public crates.io registry by keyword", + "site": "linkedin", + "name": "people-search", + "description": "Search standard LinkedIn (not Sales Navigator) for people by keyword. Each invocation consumes against LinkedIn's monthly Commercial Use Limit on people search; throttle accordingly.", "access": "read", - "domain": "crates.io", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", - "type": "str", + "name": "keywords", + "type": "string", "required": true, "positional": true, - "help": "Search keyword (e.g. \"serde\", \"async runtime\")" + "help": "People search keywords, e.g. \"site reliability engineer berlin\"" }, { "name": "limit", "type": "int", - "default": 20, + "default": 5, "required": false, - "help": "Max results (1-100)" + "help": "Maximum people to return (1-10); each query counts toward LinkedIn's monthly CUL" } ], "columns": [ "rank", "name", - "latestVersion", - "description", - "downloads", - "recentDownloads", - "repository", - "updated", - "url" + "headline", + "location", + "profile_url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/crates/search.js", - "sourceFile": "plugins/crates/search.js" + "modulePath": "plugins/linkedin/people-search.js", + "sourceFile": "plugins/linkedin/people-search.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "cursor", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "site": "linkedin", + "name": "post-analytics", + "description": "Summarize raw visible LinkedIn post counters without custom scoring or classification", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." }, { - "name": "timeout", + "name": "limit", "type": "int", "default": 30, "required": false, - "help": "Max seconds to wait for response (default: 30)" + "help": "Maximum posts to summarize (1-100)" } ], "columns": [ - "Role", - "Text" + "posts_analyzed", + "total_reactions", + "total_comments", + "total_reposts", + "total_impressions", + "posts_with_media", + "posts_with_urls", + "latest_posted_at", + "latest_reactions", + "latest_comments", + "latest_reposts", + "latest_impressions", + "latest_url" ], "type": "js", - "modulePath": "plugins/cursor/ask.js", - "sourceFile": "plugins/cursor/ask.js", - "navigateBefore": true + "modulePath": "plugins/linkedin/post-analytics.js", + "sourceFile": "plugins/linkedin/post-analytics.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "cursor", - "name": "composer", - "description": "Send a prompt directly into Cursor Composer (Cmd+I shortcut)", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "site": "linkedin", + "name": "post-comments", + "description": "List unique commenters and reply authors from one exact LinkedIn post URL", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "text", - "type": "str", + "name": "post-url", + "type": "string", "required": true, "positional": true, - "help": "Text to send into Composer" + "help": "Exact LinkedIn post URL" + }, + { + "name": "limit", + "type": "int", + "required": false, + "help": "Maximum unique commenters to return; omit to fetch all" } ], "columns": [ - "Status", - "InjectedText" - ], - "type": "js", - "modulePath": "plugins/cursor/composer.js", - "sourceFile": "plugins/cursor/composer.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "dump", - "description": "Dump the DOM and Accessibility tree of cursor for reverse-engineering", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "action", - "files" + "rank", + "name", + "headline", + "profile_url", + "comment_count", + "sample_comment", + "commented_at", + "source_post" ], "type": "js", - "modulePath": "plugins/cursor/dump.js", - "sourceFile": "plugins/cursor/dump.js", - "navigateBefore": true + "modulePath": "plugins/linkedin/post-comments.js", + "sourceFile": "plugins/linkedin/post-comments.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "cursor", - "name": "export", - "description": "Export the current cursor conversation to a Markdown file", + "site": "linkedin", + "name": "posts", + "description": "Export visible posts from a LinkedIn profile activity page with engagement metrics", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.linkedin.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "output", - "type": "str", + "name": "profile-url", + "type": "string", "required": false, - "help": "Output file (default: /tmp/cursor-export.md)" + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum posts to return (1-100)" } ], "columns": [ - "Status", - "File", - "Messages" + "rank", + "author", + "posted_at", + "body", + "reactions", + "comments", + "reposts", + "impressions", + "media", + "media_urls", + "url", + "raw_text" ], "type": "js", - "modulePath": "plugins/cursor/export.js", - "sourceFile": "plugins/cursor/export.js", - "navigateBefore": true + "modulePath": "plugins/linkedin/posts.js", + "sourceFile": "plugins/linkedin/posts.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "cursor", - "name": "extract-code", - "description": "Extract multi-line code blocks from the current Cursor conversation", + "site": "linkedin", + "name": "profile-analytics", + "description": "Read visible LinkedIn profile dashboard metrics such as profile views, post impressions, and search appearances", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.linkedin.com", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], "columns": [ - "Code" + "profile_url", + "profile_views", + "post_impressions", + "search_appearances", + "followers", + "connections", + "raw_analytics" ], "type": "js", - "modulePath": "plugins/cursor/extract-code.js", - "sourceFile": "plugins/cursor/extract-code.js", - "navigateBefore": true + "modulePath": "plugins/linkedin/profile-analytics.js", + "sourceFile": "plugins/linkedin/profile-analytics.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "cursor", - "name": "history", - "description": "List recent chat sessions from the Cursor sidebar", + "site": "linkedin", + "name": "profile-experience", + "description": "Read visible LinkedIn profile experience entries with titles, dates, locations, skills, media, and URLs", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.linkedin.com", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], "columns": [ - "Index", - "Title" + "rank", + "total_count", + "title", + "employment_type", + "company", + "date_range", + "start_date", + "end_date", + "location", + "location_type", + "description", + "skills", + "media", + "urls", + "skill_url", + "media_url", + "profile_url", + "raw_text" ], "type": "js", - "modulePath": "plugins/cursor/history.js", - "sourceFile": "plugins/cursor/history.js", - "navigateBefore": true + "modulePath": "plugins/linkedin/profile-experience.js", + "sourceFile": "plugins/linkedin/profile-experience.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "cursor", - "name": "model", - "description": "Get or switch the currently active AI model in Cursor", + "site": "linkedin", + "name": "profile-projects", + "description": "Read visible LinkedIn profile projects with descriptions, dates, skills, media, and URLs", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "www.linkedin.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "model-name", - "type": "str", + "name": "profile-url", + "type": "string", "required": false, - "positional": true, - "help": "The ID of the model to switch to (e.g. claude-3.5-sonnet)" + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." } ], "columns": [ - "Status", - "Model" + "rank", + "title", + "date_range", + "associated_with", + "description", + "skills", + "media", + "urls", + "profile_url", + "raw_text" ], "type": "js", - "modulePath": "plugins/cursor/model.js", - "sourceFile": "plugins/cursor/model.js", - "navigateBefore": true + "modulePath": "plugins/linkedin/profile-projects.js", + "sourceFile": "plugins/linkedin/profile-projects.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "cursor", - "name": "new", - "description": "Start a new Cursor chat or Composer session", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "site": "linkedin", + "name": "profile-read", + "description": "Read visible LinkedIn profile sections: headline, About, experience, education, services, and featured sections", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "profile-url", + "type": "string", + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + } + ], "columns": [ - "Status" + "profile_url", + "name", + "headline", + "location", + "about", + "about_character_count", + "about_skills", + "experience", + "education", + "services", + "featured" ], "type": "js", - "modulePath": "plugins/cursor/new.js", - "sourceFile": "plugins/cursor/new.js", - "navigateBefore": true + "modulePath": "plugins/linkedin/profile-read.js", + "sourceFile": "plugins/linkedin/profile-read.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "cursor", - "name": "read", - "description": "Read the current Cursor chat/composer conversation history", - "access": "read", - "domain": "localhost", + "site": "linkedin", + "name": "safe-send", + "description": "Fail-closed LinkedIn message sender that verifies exact thread, recipient, and latest message before filling/sending", + "access": "write", + "domain": "www.linkedin.com", "strategy": "ui", "browser": true, - "args": [], + "args": [ + { + "name": "thread-url", + "type": "str", + "required": true, + "help": "Exact LinkedIn messaging thread URL to open and verify" + }, + { + "name": "expected-name", + "type": "str", + "required": true, + "help": "Expected visible recipient name in the active thread header" + }, + { + "name": "message", + "type": "str", + "required": true, + "help": "Message body to send or dry-run" + }, + { + "name": "expected-last-text", + "type": "str", + "required": false, + "help": "Substring expected in the currently visible latest conversation context" + }, + { + "name": "expected-last-hash", + "type": "str", + "required": false, + "help": "SHA-256 hash of expected latest visible message text" + }, + { + "name": "send", + "type": "bool", + "default": false, + "required": false, + "help": "Actually click Send. Default is dry-run verification only." + }, + { + "name": "screenshot", + "type": "bool", + "default": false, + "required": false, + "help": "Capture a screenshot during verification" + } + ], "columns": [ - "Role", - "Text" + "status", + "recipient", + "reason", + "thread_url", + "message_chars", + "screenshot" ], "type": "js", - "modulePath": "plugins/cursor/read.js", - "sourceFile": "plugins/cursor/read.js", + "modulePath": "plugins/linkedin/safe-send.js", + "sourceFile": "plugins/linkedin/safe-send.js", "navigateBefore": true }, { - "site": "cursor", - "name": "screenshot", - "description": "Capture a snapshot of the current cursor window (DOM + Accessibility tree)", + "site": "linkedin", + "name": "salesnav-inbox", + "description": "List LinkedIn Sales Navigator message conversations with API pagination", "access": "read", - "domain": "localhost", + "domain": "www.linkedin.com", "strategy": "ui", "browser": true, "args": [ { - "name": "output", - "type": "str", + "name": "limit", + "type": "number", + "default": 40, "required": false, - "help": "Output file path (default: /tmp/cursor-snapshot.txt)" + "help": "Maximum conversations to return (1-500)" + }, + { + "name": "max-pages", + "type": "number", + "default": 30, + "required": false, + "help": "Maximum Sales Navigator API pages to fetch" + }, + { + "name": "unread-only", + "type": "bool", + "default": false, + "required": false, + "help": "Return only unread conversations" } ], "columns": [ - "Status", - "File" + "rank", + "thread_id", + "thread_url", + "person_name", + "last_message_snippet", + "last_activity_time", + "unread", + "unread_count", + "total_message_count", + "archived", + "participants", + "next_page_starts_at" ], "type": "js", - "modulePath": "plugins/cursor/screenshot.js", - "sourceFile": "plugins/cursor/screenshot.js", + "modulePath": "plugins/linkedin/salesnav-inbox.js", + "sourceFile": "plugins/linkedin/salesnav-inbox.js", "navigateBefore": true }, { - "site": "cursor", - "name": "send", - "description": "Send a prompt directly into Cursor Composer/Chat", + "site": "linkedin", + "name": "salesnav-message", + "description": "Send or dry-run a LinkedIn Sales Navigator InMail to a lead using the Sales Navigator messaging API", "access": "write", - "domain": "localhost", + "domain": "www.linkedin.com", "strategy": "ui", "browser": true, "args": [ { - "name": "text", - "type": "str", + "name": "recipient", + "type": "string", "required": true, "positional": true, - "help": "Text to send into Cursor" + "help": "Sales Navigator lead URL, LinkedIn /in/ URL from salesnav-search, or urn:li:fs_salesProfile:(...)" + }, + { + "name": "subject", + "type": "string", + "required": true, + "help": "InMail subject" + }, + { + "name": "body", + "type": "string", + "required": true, + "help": "InMail body" + }, + { + "name": "send", + "type": "bool", + "default": false, + "required": false, + "help": "Actually send the InMail. Default is dry-run validation only." + }, + { + "name": "copy-to-crm", + "type": "bool", + "default": false, + "required": false, + "help": "Set Sales Navigator copyToCrm on the message request" } ], "columns": [ - "Status", - "InjectedText" + "status", + "recipient", + "title", + "company", + "credits_remaining", + "credits_before", + "credits_after", + "sent_in_salesnav", + "message_chars", + "subject_chars", + "recipient_urn", + "degree", + "inmail_restriction", + "open_link" ], "type": "js", - "modulePath": "plugins/cursor/send.js", - "sourceFile": "plugins/cursor/send.js", + "modulePath": "plugins/linkedin/salesnav-message.js", + "sourceFile": "plugins/linkedin/salesnav-message.js", "navigateBefore": true }, { - "site": "cursor", - "name": "status", - "description": "Check active CDP connection to Cursor AI Editor", + "site": "linkedin", + "name": "salesnav-search", + "description": "Search LinkedIn Sales Navigator for people leads by keyword", "access": "read", - "domain": "localhost", + "domain": "www.linkedin.com", "strategy": "ui", "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" - ], - "type": "js", - "modulePath": "plugins/cursor/status.js", - "sourceFile": "plugins/cursor/status.js", - "navigateBefore": true - }, - { - "site": "dblp", - "name": "author", - "description": "List dblp publications by a given author (newest first; resolves to top PID match)", - "access": "read", - "domain": "dblp.org", - "strategy": "public", - "browser": false, "args": [ { - "name": "author", - "type": "str", - "required": false, + "name": "keywords", + "type": "string", + "required": true, "positional": true, - "help": "Author name (e.g. \"Yoshua Bengio\"). Optional when --pid is given." - }, - { - "name": "pid", - "type": "str", - "required": false, - "help": "Canonical dblp PID (e.g. \"56/953\"). Bypasses author search." + "help": "People search keywords, e.g. \"quality manager food manufacturing\"" }, { "name": "limit", - "type": "int", - "default": 20, + "type": "number", + "default": 25, "required": false, - "help": "Max publications (1-200)" + "help": "Maximum leads to return (1-500, fetched 25 per request)" } ], "columns": [ "rank", - "key", + "name", "title", - "authors", - "venue", - "year", - "type", - "doi", - "pid", - "url" + "company", + "location", + "degree", + "profile_url", + "lead_url", + "recipient_urn" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/dblp/author.js", - "sourceFile": "plugins/dblp/author.js" + "modulePath": "plugins/linkedin/salesnav-search.js", + "sourceFile": "plugins/linkedin/salesnav-search.js", + "navigateBefore": true }, { - "site": "dblp", - "name": "paper", - "aliases": [ - "detail", - "view" - ], - "description": "Fetch a dblp record by canonical key (e.g. conf/nips/VaswaniSPUJGKP17)", + "site": "linkedin", + "name": "salesnav-thread", + "description": "Return full Sales Navigator message history for a thread id, Sales Navigator inbox URL, lead URL, recipient urn, or exact recipient name", "access": "read", - "domain": "dblp.org", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "key", - "type": "str", + "name": "thread-or-recipient", + "type": "string", "required": true, "positional": true, - "help": "dblp record key (round-tripped from the `key` column of `dblp search`)" + "help": "Sales Navigator inbox URL/thread id, Sales Navigator lead URL, recipient urn, or exact participant name" + }, + { + "name": "limit", + "type": "number", + "default": 200, + "required": false, + "help": "Maximum messages to return (1-500)" + }, + { + "name": "max-pages", + "type": "number", + "default": 30, + "required": false, + "help": "Maximum inbox pages to scan when resolving a recipient" } ], "columns": [ - "key", + "index", + "thread_id", + "thread_url", + "sender", + "text", + "timestamp", + "subject", + "message_id", + "sender_urn", + "delivered_at", "type", - "title", - "authors", - "venue", - "year", - "pages", - "doi", - "open_access_url", - "dblp_url" + "total_message_count" ], - "type": "js", - "modulePath": "plugins/dblp/paper.js", - "sourceFile": "plugins/dblp/paper.js" + "type": "js", + "modulePath": "plugins/linkedin/salesnav-thread.js", + "sourceFile": "plugins/linkedin/salesnav-thread.js", + "navigateBefore": true }, { - "site": "dblp", + "site": "linkedin", "name": "search", - "description": "Search dblp computer-science bibliography by free-text query", + "description": "Search LinkedIn jobs", "access": "read", - "domain": "dblp.org", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "query", - "type": "str", + "type": "string", "required": true, "positional": true, - "help": "Search keyword (title / author / venue, e.g. \"attention is all you need\")" + "help": "Job search keywords" + }, + { + "name": "location", + "type": "string", + "required": false, + "help": "Location text such as San Francisco Bay Area" }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Max results (1-100, single dblp page)" + "help": "Number of jobs to return (max 100)" + }, + { + "name": "start", + "type": "int", + "default": 0, + "required": false, + "help": "Result offset for pagination" + }, + { + "name": "details", + "type": "bool", + "default": false, + "required": false, + "help": "Include full job description and apply URL (slower)" + }, + { + "name": "company", + "type": "string", + "required": false, + "help": "Comma-separated company names or LinkedIn company IDs" + }, + { + "name": "experience-level", + "type": "string", + "required": false, + "help": "Comma-separated: internship, entry, associate, mid-senior, director, executive" + }, + { + "name": "job-type", + "type": "string", + "required": false, + "help": "Comma-separated: full-time, part-time, contract, temporary, volunteer, internship, other" + }, + { + "name": "date-posted", + "type": "string", + "required": false, + "help": "One of: any, month, week, 24h" + }, + { + "name": "remote", + "type": "string", + "required": false, + "help": "Comma-separated: on-site, hybrid, remote" } ], "columns": [ "rank", - "key", "title", - "authors", - "venue", - "year", - "type", - "doi", + "company", + "location", + "listed", + "salary", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/dblp/search.js", - "sourceFile": "plugins/dblp/search.js" + "modulePath": "plugins/linkedin/search.js", + "sourceFile": "plugins/linkedin/search.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "dblp", - "name": "venue", - "description": "Search dblp venue registry (conferences / journals) by name or acronym", + "site": "linkedin", + "name": "sent-invitations", + "description": "List pending LinkedIn sent invitations for CRM reconciliation", "access": "read", - "domain": "dblp.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Venue name or acronym (e.g. \"ICLR\", \"neural networks\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max venues (1-100, single dblp page)" - } - ], + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ "rank", - "acronym", - "venue", - "type", - "url" + "name", + "profile_url", + "invited_date_text" ], "type": "js", - "modulePath": "plugins/dblp/venue.js", - "sourceFile": "plugins/dblp/venue.js" + "modulePath": "plugins/linkedin/sent-invitations.js", + "sourceFile": "plugins/linkedin/sent-invitations.js", + "navigateBefore": true }, { - "site": "defillama", - "name": "protocol", - "description": "Single DefiLlama protocol details (current TVL, mcap, chains, twitter, github, description)", + "site": "linkedin", + "name": "services-read", + "description": "Read LinkedIn Services page details including services, overview, availability, pricing, and media titles/descriptions", "access": "read", - "domain": "defillama.com", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "slug", + "name": "profile-url", "type": "string", - "required": true, - "positional": true, - "help": "DefiLlama protocol slug (e.g. \"aave\", \"lido\")" + "required": false, + "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + }, + { + "name": "services-url", + "type": "string", + "required": false, + "help": "LinkedIn /services/page// URL. If omitted, it is discovered from the profile." } ], "columns": [ - "slug", - "name", - "category", - "isParent", - "tvl", - "tvlAt", - "mcap", - "chains", - "twitter", - "github", - "audits", - "listedAt", - "description", - "website", - "url" + "service_url", + "page_title", + "overview", + "availability", + "work_locations", + "pricing", + "services_provided", + "services_count", + "media", + "media_count", + "messages", + "reviews_visibility" ], "type": "js", - "modulePath": "plugins/defillama/protocol.js", - "sourceFile": "plugins/defillama/protocol.js" + "modulePath": "plugins/linkedin/services-read.js", + "sourceFile": "plugins/linkedin/services-read.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "defillama", - "name": "protocols", - "description": "Top DeFi protocols on DefiLlama by current TVL (slug, name, category, TVL, mcap, change_1d/7d, chains)", + "site": "linkedin", + "name": "thread-snapshot", + "description": "Load a LinkedIn messaging thread, scroll for available history, and return a full context snapshot", "access": "read", - "domain": "defillama.com", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", - "type": "int", + "name": "thread-url", + "type": "str", + "required": true, + "help": "Exact LinkedIn messaging thread URL to open and snapshot" + }, + { + "name": "max-scrolls", + "type": "number", "default": 30, "required": false, - "help": "Number of rows to return (1-500)" + "help": "Maximum upward scroll attempts to load older messages" + }, + { + "name": "json", + "type": "bool", + "default": false, + "required": false, + "help": "Return only JSON snapshot string in the snapshot_json field" } ], "columns": [ - "rank", - "slug", - "name", - "category", - "tvl", - "mcap", - "change_1d", - "change_7d", - "chains", - "listedAt", - "url" + "thread_url", + "recipient", + "message_count", + "latest_text", + "snapshot_json" ], "type": "js", - "modulePath": "plugins/defillama/protocols.js", - "sourceFile": "plugins/defillama/protocols.js" + "modulePath": "plugins/linkedin/thread-snapshot.js", + "sourceFile": "plugins/linkedin/thread-snapshot.js", + "navigateBefore": true }, { - "site": "devto", - "name": "latest", - "description": "Newest dev.to articles (firehose, all tags)", + "site": "linkedin", + "name": "timeline", + "description": "Read LinkedIn home timeline posts", "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 20, - "required": false, - "help": "Articles per page (1-100)" - }, - { - "name": "page", - "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Page number (1-based)" + "help": "Number of posts to return (max 100)" } ], "columns": [ "rank", - "id", - "title", "author", - "tags", + "author_url", + "headline", + "text", + "posted_at", "reactions", "comments", - "published", "url" ], "type": "js", - "modulePath": "plugins/devto/latest.js", - "sourceFile": "plugins/devto/latest.js" + "modulePath": "plugins/linkedin/timeline.js", + "sourceFile": "plugins/linkedin/timeline.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "devto", - "name": "read", - "description": "Read a DEV.to article body by id", + "site": "linkedin", + "name": "whoami", + "description": "Show the current logged-in linkedin account", "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "public_id", + "plain_id", + "name" + ], + "type": "js", + "modulePath": "plugins/linkedin/auth.js", + "sourceFile": "plugins/linkedin/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "linkedin-learning", + "name": "course", + "description": "Get LinkedIn Learning course detail by slug or course URL", + "access": "read", + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", - "type": "str", + "name": "slug", + "type": "string", "required": true, "positional": true, - "help": "DEV.to article id (numeric, e.g. 3605688)" - }, - { - "name": "max-length", - "type": "int", - "default": 20000, - "required": false, - "help": "Max characters of body to return (min 100)" + "help": "Course slug (e.g. agentic-ai-build-your-first-agentic-ai-system) or full /learning/ URL" } ], "columns": [ - "id", "title", - "author", - "reactions", - "reading_time", - "tags", - "published_at", - "body", + "slug", + "description", + "difficulty", + "duration_sec", + "videos_count", + "rating", + "rating_count", + "released", "url" ], "type": "js", - "modulePath": "plugins/devto/read.js", - "sourceFile": "plugins/devto/read.js" + "modulePath": "plugins/linkedin-learning/course.js", + "sourceFile": "plugins/linkedin-learning/course.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "devto", - "name": "tag", - "description": "Latest DEV.to articles for a specific tag", + "site": "linkedin-learning", + "name": "login", + "description": "Open linkedin-learning login", + "access": "write", + "domain": "linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "public_id", + "plain_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/linkedin-learning/auth.js", + "sourceFile": "plugins/linkedin-learning/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "linkedin-learning", + "name": "search", + "description": "Search LinkedIn Learning courses, videos, and learning paths by keyword", "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "tag", - "type": "str", + "name": "keywords", + "type": "string", "required": true, "positional": true, - "help": "Tag name (e.g. javascript, python, webdev)" + "help": "Search keywords, e.g. \"AI agent\"" }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Number of articles" + "help": "Maximum results to return (1-50)" } ], "columns": [ "rank", - "id", + "type", "title", - "author", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", + "instructor", + "difficulty", + "duration_sec", + "rating", + "rating_count", + "viewers", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/devto/tag.js", - "sourceFile": "plugins/devto/tag.js" + "modulePath": "plugins/linkedin-learning/search.js", + "sourceFile": "plugins/linkedin-learning/search.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "devto", - "name": "top", - "description": "Top DEV.to articles of the day", + "site": "linkedin-learning", + "name": "trending", + "description": "Browse LinkedIn Learning recommended courses across personalized carousels", "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, + "domain": "www.linkedin.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Number of articles" + "help": "Maximum results to return (1-50)" } ], "columns": [ "rank", - "id", + "group", + "type", "title", - "author", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", + "difficulty", + "viewers", "url" ], "type": "js", - "modulePath": "plugins/devto/top.js", - "sourceFile": "plugins/devto/top.js" + "modulePath": "plugins/linkedin-learning/trending.js", + "sourceFile": "plugins/linkedin-learning/trending.js", + "navigateBefore": "https://www.linkedin.com" }, { - "site": "devto", - "name": "user", - "description": "Recent DEV.to articles from a specific user", + "site": "linkedin-learning", + "name": "whoami", + "description": "Show the current logged-in linkedin-learning account", "access": "read", - "domain": "dev.to", + "domain": "linkedin.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "public_id", + "plain_id", + "name" + ], + "type": "js", + "modulePath": "plugins/linkedin-learning/auth.js", + "sourceFile": "plugins/linkedin-learning/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "lobsters", + "name": "active", + "description": "Lobste.rs most active discussions", + "access": "read", + "domain": "lobste.rs", "strategy": "public", "browser": false, "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "DEV.to username (e.g. ben, thepracticaldev)" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of articles" + "help": "Number of stories" } ], "columns": [ "rank", "id", "title", - "reactions", + "score", + "author", "comments", - "reading_time", - "published_at", - "tags", - "url" - ], - "type": "js", - "modulePath": "plugins/devto/user.js", - "sourceFile": "plugins/devto/user.js" - }, - { - "site": "dictionary", - "name": "examples", - "description": "Read real-world example sentences utilizing the word", - "access": "read", - "domain": "api.dictionaryapi.dev", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "word", - "type": "string", - "required": true, - "positional": true, - "help": "Word to get example sentences for" - } - ], - "columns": [ - "word", - "example" + "created_at", + "tags", + "url" ], "type": "js", - "modulePath": "plugins/dictionary/examples.js", - "sourceFile": "plugins/dictionary/examples.js" + "modulePath": "plugins/lobsters/active.js", + "sourceFile": "plugins/lobsters/active.js" }, { - "site": "dictionary", - "name": "search", - "description": "Search the Free Dictionary API for definitions, parts of speech, and pronunciations.", + "site": "lobsters", + "name": "domain", + "description": "Lobste.rs stories submitted from a specific domain", "access": "read", - "domain": "api.dictionaryapi.dev", + "domain": "lobste.rs", "strategy": "public", "browser": false, "args": [ { - "name": "word", - "type": "string", + "name": "domain", + "type": "str", "required": true, "positional": true, - "help": "Word to define (e.g., serendipity)" + "help": "Source domain (e.g. github.com, arxiv.org, blog.cloudflare.com)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories (1-25 — single page)" } ], "columns": [ - "word", - "phonetic", - "type", - "definition" - ], - "tags": [ - "search" + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "submission_url", + "comments_url" ], "type": "js", - "modulePath": "plugins/dictionary/search.js", - "sourceFile": "plugins/dictionary/search.js" + "modulePath": "plugins/lobsters/domain.js", + "sourceFile": "plugins/lobsters/domain.js" }, { - "site": "dictionary", - "name": "synonyms", - "description": "Find synonyms for a specific word", + "site": "lobsters", + "name": "hot", + "description": "Lobste.rs hottest stories", "access": "read", - "domain": "api.dictionaryapi.dev", + "domain": "lobste.rs", "strategy": "public", "browser": false, "args": [ { - "name": "word", - "type": "string", - "required": true, - "positional": true, - "help": "Word to find synonyms for (e.g., serendipity)" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" } ], "columns": [ - "word", - "synonyms" - ], - "type": "js", - "modulePath": "plugins/dictionary/synonyms.js", - "sourceFile": "plugins/dictionary/synonyms.js" - }, - { - "site": "discord-app", - "name": "channels", - "description": "List channels in the current Discord server", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Channel", - "Type", - "guild_id", - "channel_id", + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", "url" ], "type": "js", - "modulePath": "plugins/discord-app/channels.js", - "sourceFile": "plugins/discord-app/channels.js", - "navigateBefore": true + "modulePath": "plugins/lobsters/hot.js", + "sourceFile": "plugins/lobsters/hot.js" }, { - "site": "discord-app", - "name": "delete", - "description": "Delete a message by its ID in the active Discord channel", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "site": "lobsters", + "name": "newest", + "description": "Lobste.rs newest stories", + "access": "read", + "domain": "lobste.rs", + "strategy": "public", + "browser": false, "args": [ { - "name": "message_id", - "type": "string", - "required": true, - "positional": true, - "help": "The ID of the message to delete (visible via Developer Mode or the read command)" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" } ], "columns": [ - "status", - "message" + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" ], "type": "js", - "modulePath": "plugins/discord-app/delete.js", - "sourceFile": "plugins/discord-app/delete.js", - "navigateBefore": true + "modulePath": "plugins/lobsters/newest.js", + "sourceFile": "plugins/lobsters/newest.js" }, { - "site": "discord-app", - "name": "goto", - "description": "Open a Discord channel by id/name/url without sending messages", + "site": "lobsters", + "name": "read", + "description": "Read a Lobste.rs story and its comment tree", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, + "domain": "lobste.rs", + "strategy": "public", + "browser": false, "args": [ { - "name": "guild", + "name": "id", "type": "str", + "required": true, + "positional": true, + "help": "Lobste.rs short_id (e.g. 6cmh6h)" + }, + { + "name": "limit", + "type": "int", + "default": 25, "required": false, - "help": "Guild/server id or visible name" + "help": "Max top-level comments" }, { - "name": "channel", - "type": "str", + "name": "depth", + "type": "int", + "default": 2, "required": false, - "help": "Channel id or visible name" + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" }, { - "name": "url", - "type": "str", + "name": "replies", + "type": "int", + "default": 5, "required": false, - "help": "Discord channel URL" + "help": "Max replies shown per comment at each level" }, { - "name": "timeout", - "type": "str", - "default": "8", + "name": "max-length", + "type": "int", + "default": 2000, "required": false, - "help": "Seconds to wait for Discord to show the route (default: 8)" + "help": "Max characters per comment body (min 100)" } ], "columns": [ - "Status", - "guild_id", - "channel_id", - "url" + "type", + "author", + "score", + "text" ], "type": "js", - "modulePath": "plugins/discord-app/goto.js", - "sourceFile": "plugins/discord-app/goto.js", - "navigateBefore": true + "modulePath": "plugins/lobsters/read.js", + "sourceFile": "plugins/lobsters/read.js" }, { - "site": "discord-app", - "name": "members", - "description": "List online members in the current Discord channel", + "site": "lobsters", + "name": "tag", + "description": "Lobste.rs stories by tag", "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], + "domain": "lobste.rs", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "tag", + "type": "str", + "required": true, + "positional": true, + "help": "Tag name (e.g. programming, rust, security, ai)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of stories" + } + ], "columns": [ - "Index", - "Name", - "Status" + "rank", + "id", + "title", + "score", + "author", + "comments", + "created_at", + "tags", + "url" ], "type": "js", - "modulePath": "plugins/discord-app/members.js", - "sourceFile": "plugins/discord-app/members.js", - "navigateBefore": true + "modulePath": "plugins/lobsters/tag.js", + "sourceFile": "plugins/lobsters/tag.js" }, { - "site": "discord-app", - "name": "read", - "description": "Read recent messages from the active or targeted Discord channel", - "access": "read", - "domain": "localhost", + "site": "luma", + "name": "create-event", + "description": "Create a free single-session Luma event", + "access": "write", + "domain": "luma.com", "strategy": "ui", "browser": true, "args": [ { - "name": "count", + "name": "name", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "start", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "end", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "timezone", + "type": "str", + "required": true, + "help": "" + }, + { + "name": "calendar", "type": "str", - "default": "20", "required": false, - "help": "Number of messages to read (default: 20)" + "help": "" }, { - "name": "guild", + "name": "description", "type": "str", "required": false, - "help": "Guild/server id or visible name for targeted reads" + "help": "" }, { - "name": "channel", + "name": "location", "type": "str", "required": false, - "help": "Channel id or visible name for targeted reads" + "help": "" }, { - "name": "url", + "name": "virtual-url", "type": "str", "required": false, - "help": "Discord channel URL to open before reading" - } - ], - "columns": [ - "Author", - "Time", - "Message", - "channel_id", - "message_id" - ], - "type": "js", - "modulePath": "plugins/discord-app/read.js", - "sourceFile": "plugins/discord-app/read.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "search", - "description": "Search messages in the current Discord server/channel (Cmd+F)", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ + "help": "" + }, { - "name": "query", + "name": "visibility", "type": "str", - "required": true, - "positional": true, - "help": "Search query" + "default": "public", + "required": false, + "help": "", + "choices": [ + "public", + "private", + "members-only" + ] + }, + { + "name": "capacity", + "type": "int", + "required": false, + "help": "" + }, + { + "name": "require-approval", + "type": "boolean", + "default": false, + "required": false, + "help": "" + }, + { + "name": "confirm", + "type": "boolean", + "default": false, + "required": false, + "help": "" } ], "columns": [ - "Index", - "Author", - "Message" - ], - "tags": [ - "search" + "eventId", + "name", + "startsAt", + "endsAt", + "timezone", + "visibility", + "requireApproval", + "capacity", + "eventUrl", + "manageUrl" ], "type": "js", - "modulePath": "plugins/discord-app/search.js", - "sourceFile": "plugins/discord-app/search.js", - "navigateBefore": true + "modulePath": "plugins/luma/create-event.js", + "sourceFile": "plugins/luma/create-event.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true }, { - "site": "discord-app", - "name": "send", - "description": "Send a message in the active Discord channel", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "site": "luma", + "name": "events", + "description": "List upcoming or past Luma events managed by the logged-in account", + "access": "read", + "example": "webcmd luma events --period future --limit 25 -f json", + "domain": "luma.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "text", + "name": "period", "type": "str", - "required": true, - "positional": true, - "help": "Message to send" + "default": "future", + "required": false, + "help": "List future or past events", + "choices": [ + "future", + "past" + ] + }, + { + "name": "limit", + "type": "int", + "default": 25, + "required": false, + "help": "Maximum number of events to request" } ], "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/discord-app/send.js", - "sourceFile": "plugins/discord-app/send.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "servers", - "description": "List all Discord servers (guilds) in the sidebar", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Server", - "guild_id", - "url" - ], - "type": "js", - "modulePath": "plugins/discord-app/servers.js", - "sourceFile": "plugins/discord-app/servers.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "status", - "description": "Check active CDP connection to Discord Desktop", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" + "eventId", + "name", + "startsAt", + "endsAt", + "timezone", + "guestCount", + "requireApproval", + "managerLevel", + "location", + "manageUrl", + "eventUrl" ], "type": "js", - "modulePath": "plugins/discord-app/status.js", - "sourceFile": "plugins/discord-app/status.js", - "navigateBefore": true + "modulePath": "plugins/luma/events.js", + "sourceFile": "plugins/luma/events.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "thread-read", - "description": "Read recent messages from a Discord thread/post by id or URL", + "site": "luma", + "name": "guests", + "description": "List guests and all custom registration answers for a managed Luma event", "access": "read", - "domain": "localhost", - "strategy": "ui", + "example": "webcmd luma guests evt-abc --status pending_approval --limit 100 -f json", + "domain": "luma.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "thread", - "type": "str", - "required": false, - "help": "Thread/post id, or a full Discord thread/post URL" - }, - { - "name": "count", + "name": "eventId", "type": "str", - "default": "20", - "required": false, - "help": "Number of messages to read (default: 20)" + "required": true, + "positional": true, + "help": "Luma event ID returned by webcmd luma events" }, { - "name": "guild", + "name": "status", "type": "str", + "default": "all", "required": false, - "help": "Parent guild/server id or visible name" + "help": "Filter by guest approval status", + "choices": [ + "all", + "approved", + "pending_approval", + "declined", + "waitlist", + "invited" + ] }, { - "name": "channel", - "type": "str", + "name": "limit", + "type": "int", + "default": 100, "required": false, - "help": "Parent forum/channel id or visible name" + "help": "Maximum matching guests to return" }, { - "name": "url", + "name": "query", "type": "str", + "default": "", "required": false, - "help": "Discord thread/post URL" + "help": "Search text passed to Luma guest search" } ], "columns": [ - "Author", - "Time", - "Message", - "channel_id", - "message_id" + "eventId", + "guestId", + "userId", + "name", + "email", + "phone", + "status", + "registeredAt", + "profiles", + "answers" ], "type": "js", - "modulePath": "plugins/discord-app/thread-read.js", - "sourceFile": "plugins/discord-app/thread-read.js", - "navigateBefore": true + "modulePath": "plugins/luma/guests.js", + "sourceFile": "plugins/luma/guests.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "discord-app", - "name": "threads", - "description": "List visible Discord forum/thread posts in the active or targeted channel", - "access": "read", - "domain": "localhost", + "site": "luma", + "name": "login", + "description": "Open Luma sign in", + "access": "write", + "domain": "luma.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "email", + "url", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/luma/auth.js", + "sourceFile": "plugins/luma/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "luma", + "name": "set-registration-questions", + "description": "Append or replace custom registration questions on a managed Luma event", + "access": "write", + "domain": "luma.com", "strategy": "ui", "browser": true, "args": [ { - "name": "limit", + "name": "eventId", "type": "str", - "default": "30", - "required": false, - "help": "Maximum thread/post cards to return (default: 30)" + "required": true, + "positional": true, + "help": "" }, { - "name": "guild", + "name": "questions-file", "type": "str", - "required": false, - "help": "Guild/server id or visible name for targeted thread listing" + "required": true, + "help": "" }, { - "name": "channel", + "name": "mode", "type": "str", - "required": false, - "help": "Forum/channel id or visible name for targeted thread listing" + "required": true, + "help": "", + "choices": [ + "append", + "replace" + ] }, { - "name": "url", - "type": "str", + "name": "confirm", + "type": "boolean", + "default": false, "required": false, - "help": "Discord forum/channel URL to open before listing threads" + "help": "" } ], "columns": [ - "Index", - "Thread", - "Author", - "Updated", - "Preview", - "guild_id", - "channel_id", - "thread_id", - "url" + "eventId", + "mode", + "previousCount", + "questionCount", + "questions", + "registrationUrl" ], "type": "js", - "modulePath": "plugins/discord-app/threads.js", - "sourceFile": "plugins/discord-app/threads.js", - "navigateBefore": true + "modulePath": "plugins/luma/set-registration-questions.js", + "sourceFile": "plugins/luma/set-registration-questions.js", + "navigateBefore": false, + "siteSession": "persistent", + "freshPage": true }, { - "site": "district", - "name": "checkout", - "description": "Select District movie seats and open the UPI QR payment scanner", + "site": "luma", + "name": "update-guest-status", + "description": "Approve or decline a pending Luma guest after explicit confirmation", "access": "write", - "domain": "www.district.in", + "example": "webcmd luma update-guest-status evt-abc gst-abc --status approved --confirm true -f json", + "domain": "luma.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "show", + "name": "eventId", "type": "str", "required": true, "positional": true, - "help": "District seat-layout URL or showId from district showtimes" + "help": "Luma event ID returned by webcmd luma events" }, { - "name": "seats", + "name": "guestId", "type": "str", "required": true, - "help": "Comma-separated seat labels to select, e.g. I22,I21" - }, - { - "name": "format-id", - "type": "str", - "required": false, - "help": "District formatId from showtimes; required when show is a showId" + "positional": true, + "help": "Luma guest ID returned by webcmd luma guests" }, { - "name": "content-id", + "name": "status", "type": "str", - "required": false, - "help": "District content id; required when show is a showId" + "required": true, + "help": "New guest status", + "choices": [ + "approved", + "declined" + ] }, { - "name": "timeout", - "type": "int", - "default": 45, + "name": "suppress-email", + "type": "boolean", + "default": false, "required": false, - "help": "Maximum seconds to wait for selection, review page, and payment handoff" + "help": "Set true to prevent Luma from emailing the guest" }, { - "name": "payment", - "type": "str", - "default": "upi-qr", + "name": "confirm", + "type": "boolean", + "default": false, "required": false, - "help": "Payment handoff target: upi-qr opens the scanner; review stops on order review" + "help": "Required. Set --confirm true to change the real guest status" } ], "columns": [ + "eventId", + "guestId", + "name", + "email", + "previousStatus", "status", - "movie", - "cinema", - "date", - "time", - "seats", - "ticketCount", - "orderAmount", - "bookingCharge", - "total", - "paymentMethod", - "paymentState", - "upiQrVisible", - "paymentAmount", - "paymentUrl", - "showId" + "emailSuppressed" ], "type": "js", - "modulePath": "plugins/district/checkout.js", - "sourceFile": "plugins/district/checkout.js", + "modulePath": "plugins/luma/update-guest-status.js", + "sourceFile": "plugins/luma/update-guest-status.js", "navigateBefore": false, "siteSession": "persistent", "freshPage": true }, { - "site": "district", - "name": "listings", - "aliases": [ - "ls" + "site": "luma", + "name": "whoami", + "description": "Show the current logged-in Luma account", + "access": "read", + "domain": "luma.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name", + "email", + "url" ], - "description": "List public District by Zomato movies, events, and nearby going-out cards", + "type": "js", + "modulePath": "plugins/luma/auth.js", + "sourceFile": "plugins/luma/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "connectors", + "description": "List available Manus connectors (integrations).", "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, + "domain": "manus.im", + "strategy": "cookie", + "browser": true, "args": [ - { - "name": "input", - "type": "str", - "default": "home", - "required": false, - "positional": true, - "help": "home, movies, events, a district.in URL, or a District path" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 50, "required": false, - "help": "Maximum rows to return (1-100)" + "help": "Max connectors to return" } ], "columns": [ - "rank", - "title", - "category", - "date", - "venue", - "price", - "url" + "UID", + "Name", + "Brief" + ], + "type": "js", + "modulePath": "plugins/manus/connectors.js", + "sourceFile": "plugins/manus/connectors.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "credits", + "description": "Show Manus credit balance and refresh details.", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/district/listings.js", - "sourceFile": "plugins/district/listings.js" + "modulePath": "plugins/manus/credits.js", + "sourceFile": "plugins/manus/credits.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "district", - "name": "locations", - "aliases": [ - "location-search" - ], - "description": "Search District-supported cities, areas, malls, and places for booking filters", + "site": "manus", + "name": "list", + "description": "List Manus sessions (tasks).", "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, + "domain": "manus.im", + "strategy": "cookie", + "browser": true, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "City, area, mall, or locality, for example \"bangalore\" or \"indiranagar\"" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Maximum location rows to return (1-50)" + "help": "Max sessions to return" + }, + { + "name": "archived", + "type": "bool", + "default": false, + "required": false, + "help": "Include archived sessions" } ], "columns": [ - "rank", - "name", - "kind", - "city", - "state", - "cityKey", - "cityId", - "placeId", - "lat", - "lng", - "distanceKm", - "source" + "id", + "Title", + "Status", + "Last Message", + "Last Updated", + "Credits" ], "type": "js", - "modulePath": "plugins/district/locations.js", - "sourceFile": "plugins/district/locations.js" + "modulePath": "plugins/manus/list.js", + "sourceFile": "plugins/manus/list.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "district", + "site": "manus", "name": "login", - "description": "Open district login", + "description": "Open manus login", "access": "write", - "domain": "www.district.in", + "domain": "manus.im", "strategy": "cookie", "browser": true, "args": [], @@ -7363,418 +15741,273 @@ "site", "user_id", "name", - "phone_number", - "email", "action", "verify_command" ], "type": "js", - "modulePath": "plugins/district/auth.js", - "sourceFile": "plugins/district/auth.js", + "modulePath": "plugins/manus/auth.js", + "sourceFile": "plugins/manus/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "district", - "name": "search", - "aliases": [ - "s" - ], - "description": "Search District by Zomato across movies, events, dining, stores, activities, and play", + "site": "manus", + "name": "read", + "description": "Show details for a specific Manus session.", "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, + "domain": "manus.im", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "uid", "type": "str", "required": true, "positional": true, - "help": "Search query, for example \"hamlet\" or \"arijit\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum rows to return (1-100)" - }, - { - "name": "tab", - "type": "str", - "default": "all", - "required": false, - "help": "Search tab: all, dining, events, movies, stores, activities, or play" + "help": "Session UID" } ], "columns": [ - "rank", - "title", - "category", - "date", - "venue", - "price", - "url" - ], - "tags": [ - "search" + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/district/search.js", - "sourceFile": "plugins/district/search.js" + "modulePath": "plugins/manus/read.js", + "sourceFile": "plugins/manus/read.js", + "navigateBefore": true, + "siteSession": "persistent" }, { - "site": "district", - "name": "seats", - "description": "List available seats for a District movie showtime", + "site": "manus", + "name": "skills", + "description": "List Manus skills (user-added and system).", "access": "read", - "domain": "www.district.in", + "domain": "manus.im", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "show", - "type": "str", - "required": true, - "positional": true, - "help": "District seat-layout URL or showId from district showtimes" - }, - { - "name": "format-id", - "type": "str", - "required": false, - "help": "District formatId from showtimes; required when show is a showId" - }, - { - "name": "content-id", - "type": "str", - "required": false, - "help": "District content id; required when show is a showId" - }, - { - "name": "class", - "type": "str", - "required": false, - "help": "Optional seat class filter, e.g. premium, premium xl, or recliner" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Number of seats to choose (1-10); without count, seats are listed normally" - }, - { - "name": "together", - "type": "str", - "required": false, - "help": "Require selected seats to be adjacent when count is provided" - }, - { - "name": "max-price", - "type": "float", - "required": false, - "help": "Maximum price per seat" - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Maximum seats to return (1-300)" - }, - { - "name": "timeout", - "type": "int", - "default": 30, - "required": false, - "help": "Maximum seconds to wait for the seat map to render" - } + "args": [], + "columns": [ + "ID", + "Name", + "Description", + "Source" ], + "type": "js", + "modulePath": "plugins/manus/skills.js", + "sourceFile": "plugins/manus/skills.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "status", + "description": "Show current Manus user profile and credit summary.", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], "columns": [ - "rank", - "seat", - "row", - "number", - "column", - "seatClass", - "price", - "status", - "flags", - "showId", - "formatId", - "url" + "Field", + "Value" + ], + "type": "js", + "modulePath": "plugins/manus/status.js", + "sourceFile": "plugins/manus/status.js", + "navigateBefore": true, + "siteSession": "persistent" + }, + { + "site": "manus", + "name": "whoami", + "description": "Show the current logged-in manus account", + "access": "read", + "domain": "manus.im", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" ], "type": "js", - "modulePath": "plugins/district/seats.js", - "sourceFile": "plugins/district/seats.js", + "modulePath": "plugins/manus/auth.js", + "sourceFile": "plugins/manus/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "district", - "name": "set-location", - "aliases": [ - "setlocation" - ], - "description": "Set the District browser session location for movie booking filters", - "access": "write", - "domain": "www.district.in", - "strategy": "cookie", - "browser": true, + "site": "maven", + "name": "artifact", + "description": "Fetch a Maven Central artifact's version history (groupId:artifactId[:version])", + "access": "read", + "domain": "search.maven.org", + "strategy": "public", + "browser": false, "args": [ { - "name": "location", + "name": "coordinate", "type": "str", "required": true, "positional": true, - "help": "City, area, mall, or locality, for example \"Bangalore\" or \"Indiranagar\"" - }, - { - "name": "rank", - "type": "int", - "default": 1, - "required": false, - "help": "Pick the Nth District location result (1-20), default: 1" + "help": "Maven coord \"groupId:artifactId\" or \"groupId:artifactId:version\"" }, { - "name": "timeout", + "name": "limit", "type": "int", - "default": 45, + "default": 20, "required": false, - "help": "Maximum seconds to wait for the picker and location change" + "help": "Max versions (1-200, ignored when version is pinned)" } ], "columns": [ - "status", - "name", - "city", - "state", - "cityKey", - "cityId", - "placeId", - "subzoneId", - "lat", - "lng", - "availableTabs", - "source" + "groupId", + "artifactId", + "version", + "packaging", + "publishedAt", + "tags", + "url" ], "type": "js", - "modulePath": "plugins/district/set-location.js", - "sourceFile": "plugins/district/set-location.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/maven/artifact.js", + "sourceFile": "plugins/maven/artifact.js" }, { - "site": "district", - "name": "showtimes", - "aliases": [ - "shows" - ], - "description": "List District movie showtimes with location, time, cinema, language, price, and format filters", + "site": "maven", + "name": "search", + "description": "Search Maven Central by keyword (artifact name, groupId, tag)", "access": "read", - "domain": "www.district.in", - "strategy": "cookie", - "browser": true, + "domain": "search.maven.org", + "strategy": "public", + "browser": false, "args": [ { - "name": "movie", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Movie name or District movie URL" - }, - { - "name": "date", - "type": "str", - "required": false, - "help": "Show date in YYYY-MM-DD format; defaults to District selected date" - }, - { - "name": "city", - "type": "str", - "required": false, - "help": "District city name/key, for example Bangalore or Bengaluru" - }, - { - "name": "near", - "type": "str", - "required": false, - "help": "Area, mall, or locality to search near, for example Indiranagar" - }, - { - "name": "city-key", - "type": "str", - "required": false, - "help": "Legacy District city key override, for example bengaluru" - }, - { - "name": "after", - "type": "str", - "required": false, - "help": "Only shows at or after HH:MM, 24-hour time" - }, - { - "name": "before", - "type": "str", - "required": false, - "help": "Only shows at or before HH:MM, 24-hour time" - }, - { - "name": "cinema", - "type": "str", - "required": false, - "help": "Filter cinema/theatre name, for example PVR, INOX, Orion" - }, - { - "name": "language", - "type": "str", - "required": false, - "help": "Filter movie language, for example English, Hindi, Kannada" - }, - { - "name": "max-price", - "type": "float", - "required": false, - "help": "Only shows with at least one ticket class at or below this price" - }, - { - "name": "quality", - "type": "str", - "required": false, - "help": "Generic format/quality filter, for example 2D, 3D, IMAX, IMAX 3D, 4DX" + "help": "Search keyword (e.g. \"jackson\", \"guava\", \"ai.koog\")" }, { "name": "limit", "type": "int", - "default": 50, + "default": 30, "required": false, - "help": "Maximum showtime rows to return (1-200)" + "help": "Max artifacts (1-200)" } ], "columns": [ "rank", - "movie", - "language", - "date", - "time", - "cinema", - "format", - "priceRange", - "available", - "showId", - "formatId", + "coordinate", + "groupId", + "artifactId", + "latestVersion", + "packaging", + "versions", + "lastPublished", + "repository", "url" ], - "type": "js", - "modulePath": "plugins/district/showtimes.js", - "sourceFile": "plugins/district/showtimes.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "district", - "name": "whoami", - "description": "Show the current logged-in district account", - "access": "read", - "domain": "www.district.in", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name", - "phone_number", - "email" + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/district/auth.js", - "sourceFile": "plugins/district/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/maven/search.js", + "sourceFile": "plugins/maven/search.js" }, { - "site": "dockerhub", - "name": "image", - "description": "Fetch a Docker Hub repository's public metadata (stars, pulls, last updated, status)", + "site": "mdn", + "name": "search", + "description": "Search MDN Web Docs by keyword", "access": "read", - "domain": "hub.docker.com", + "domain": "developer.mozilla.org", "strategy": "public", "browser": false, "args": [ { - "name": "image", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Image name (e.g. \"nginx\", \"library/nginx\", \"bitnami/redis\")" + "help": "Search keyword (e.g. \"fetch\", \"flexbox\", \"Array.prototype.map\")" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results (1-50)" + }, + { + "name": "locale", + "type": "str", + "default": "en-US", + "required": false, + "help": "Doc locale (en-US default; de / es / fr / ja / ko / pt-BR / ru / zh-CN / zh-TW)" } ], "columns": [ - "image", - "official", - "stars", - "pulls", - "description", - "lastUpdated", - "lastModified", - "registered", - "status", + "rank", + "title", + "slug", + "locale", + "summary", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/dockerhub/image.js", - "sourceFile": "plugins/dockerhub/image.js" + "modulePath": "plugins/mdn/search.js", + "sourceFile": "plugins/mdn/search.js" }, { - "site": "dockerhub", - "name": "search", - "description": "Search Docker Hub repositories by keyword", - "access": "read", - "domain": "hub.docker.com", - "strategy": "public", - "browser": false, + "site": "medium", + "name": "feed", + "description": "Medium popular posts Feed", + "access": "read", + "domain": "medium.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "topic", "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"nginx\", \"bitnami redis\")" + "default": "", + "required": false, + "help": "Topic (for example technology, programming, ai)" }, { "name": "limit", "type": "int", - "default": 25, + "default": 20, "required": false, - "help": "Max repositories (1-100, single Docker Hub page)" + "help": "Number of posts to return" } ], "columns": [ "rank", - "image", - "official", - "stars", - "pulls", - "description", - "url" - ], - "tags": [ - "search" + "title", + "author", + "date", + "readTime", + "claps" ], "type": "js", - "modulePath": "plugins/dockerhub/search.js", - "sourceFile": "plugins/dockerhub/search.js" + "modulePath": "plugins/medium/feed.js", + "sourceFile": "plugins/medium/feed.js", + "navigateBefore": "https://medium.com" }, { - "site": "duckduckgo", + "site": "medium", "name": "search", - "description": "Search DuckDuckGo", + "description": "Search Medium posts", "access": "read", - "domain": "html.duckduckgo.com", - "strategy": "public", + "domain": "medium.com", + "strategy": "cookie", "browser": true, "args": [ { @@ -7782,3680 +16015,4057 @@ "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "Search keyword" }, { "name": "limit", "type": "int", - "default": 10, - "required": false, - "help": "Number of results per page (1-10). For multi-page, use --offset" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Result offset for pagination (0, 10, 20...). Uses XHR POST internally" - }, - { - "name": "region", - "type": "str", - "required": false, - "help": "Region code (e.g. jp-jp, us-en, cn-zh). Default: all regions" - }, - { - "name": "time", - "type": "str", + "default": 20, "required": false, - "help": "Time range: d (day), w (week), m (month), y (year)" + "help": "Number of posts to return" } ], "columns": [ "rank", "title", - "url", - "snippet", - "displayUrl", - "icon", - "resultType" + "author", + "date", + "readTime", + "claps", + "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/duckduckgo/search.js", - "sourceFile": "plugins/duckduckgo/search.js" + "modulePath": "plugins/medium/search.js", + "sourceFile": "plugins/medium/search.js", + "navigateBefore": "https://medium.com" }, { - "site": "duckduckgo", - "name": "suggest", - "description": "DuckDuckGo search suggestions", + "site": "medium", + "name": "tag", + "description": "Latest Medium articles tagged with a given keyword (RSS feed)", "access": "read", - "domain": "duckduckgo.com", + "domain": "medium.com", "strategy": "public", "browser": false, "args": [ { - "name": "keyword", + "name": "tag", "type": "str", "required": true, "positional": true, - "help": "Search query prefix" + "help": "Lowercase tag slug (e.g. \"programming\", \"machine-learning\")" }, { "name": "limit", "type": "int", - "default": 8, + "default": 20, "required": false, - "help": "Max number of suggestions" + "help": "Max articles (1-25 — single RSS page)" } ], "columns": [ - "phrase" + "rank", + "title", + "author", + "description", + "categories", + "published", + "url" ], "type": "js", - "modulePath": "plugins/duckduckgo/suggest.js", - "sourceFile": "plugins/duckduckgo/suggest.js" + "modulePath": "plugins/medium/tag.js", + "sourceFile": "plugins/medium/tag.js" }, { - "site": "endoflife", - "name": "product", - "description": "Release cycles + EOL / LTS / support dates for one product on endoflife.date", + "site": "medium", + "name": "user", + "description": "Get Medium user posts", "access": "read", - "domain": "endoflife.date", - "strategy": "public", - "browser": false, + "domain": "medium.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "product", - "type": "string", + "name": "username", + "type": "str", "required": true, "positional": true, - "help": "endoflife.date product slug (e.g. \"nodejs\", \"python\", \"ubuntu\")" + "help": "Medium username(for example @username or username)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" } ], "columns": [ - "product", - "cycle", - "releaseDate", - "latest", - "latestReleaseDate", - "lts", - "support", - "eol", - "extendedSupport", - "eolStatus", + "rank", + "title", + "date", + "readTime", + "claps", "url" ], "type": "js", - "modulePath": "plugins/endoflife/product.js", - "sourceFile": "plugins/endoflife/product.js" + "modulePath": "plugins/medium/user.js", + "sourceFile": "plugins/medium/user.js", + "navigateBefore": "https://medium.com" }, { - "site": "flathub", - "name": "app", - "description": "Full Flathub appstream metadata for an app id (license, categories, latest release)", + "site": "mercury", + "name": "check-login", + "description": "Open Mercury reimbursements and report whether the active browser profile is logged in", "access": "read", - "domain": "flathub.org", - "strategy": "public", - "browser": false, + "example": "webcmd --profile mercury check-login -f json", + "domain": "app.mercury.com", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "status", + "loggedIn", + "url", + "hasSubmitExpense", + "hasReimbursements", + "title" + ], + "type": "js", + "modulePath": "plugins/mercury/check-login.js", + "sourceFile": "plugins/mercury/check-login.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "mercury", + "name": "reimbursement-draft", + "description": "Create a Mercury reimbursement draft from a local receipt, correct OCR fields, and stop at Review", + "access": "write", + "example": "webcmd --profile mercury reimbursement-draft --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", + "domain": "app.mercury.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "appId", + "name": "receipt", "type": "str", "required": true, - "positional": true, - "help": "AppStream id (e.g. \"org.mozilla.firefox\", \"org.gnome.Calculator\")" + "help": "Local receipt/proof file path", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp", + "application/pdf" + ], + "maxBytes": 26214400 + } + }, + { + "name": "amount", + "type": "str", + "required": true, + "help": "Original-currency amount, e.g. 140.00" + }, + { + "name": "currency", + "type": "str", + "default": "CNY", + "required": false, + "help": "Original currency code" + }, + { + "name": "date", + "type": "str", + "required": true, + "help": "Expense date as YYYY-MM-DD" + }, + { + "name": "merchant", + "type": "str", + "required": true, + "help": "Merchant shown on the reimbursement" + }, + { + "name": "category", + "type": "str", + "default": "Marketing & Advertising", + "required": false, + "help": "Mercury expense category" + }, + { + "name": "notes", + "type": "str", + "required": true, + "help": "Business purpose / reimbursement notes" + }, + { + "name": "ocr-wait-seconds", + "type": "str", + "default": "8", + "required": false, + "help": "Seconds to wait after receipt upload before correcting OCR-overwritten fields" + }, + { + "name": "close-after-review", + "type": "boolean", + "default": false, + "required": false, + "help": "Close the Review dialog after verification; final Submit is still never clicked" } ], "columns": [ - "appId", - "name", - "summary", - "developer", - "license", - "isFreeLicense", - "isEol", - "categories", - "keywords", - "latestVersion", - "latestReleaseDate", - "homepage", - "bugtracker", - "donation", - "url" + "status", + "url", + "receipt", + "uploaded", + "fieldsTouched", + "reviewReady", + "submitBlocked", + "warnings" ], "type": "js", - "modulePath": "plugins/flathub/app.js", - "sourceFile": "plugins/flathub/app.js" + "modulePath": "plugins/mercury/reimbursement-draft.js", + "sourceFile": "plugins/mercury/reimbursement-draft.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "flathub", - "name": "search", - "description": "Search Flathub apps by keyword", + "site": "mercury", + "name": "reimbursement-plan", + "description": "Validate Mercury reimbursement inputs and print the draft plan without opening a browser", "access": "read", - "domain": "flathub.org", - "strategy": "public", + "example": "webcmd mercury reimbursement-plan --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", + "strategy": "local", "browser": false, "args": [ { - "name": "query", + "name": "receipt", "type": "str", "required": true, - "positional": true, - "help": "Search keyword" + "help": "Local receipt/proof file path" }, { - "name": "limit", - "type": "int", - "default": 25, + "name": "amount", + "type": "str", + "required": true, + "help": "Original-currency amount, e.g. 140.00" + }, + { + "name": "currency", + "type": "str", + "default": "CNY", "required": false, - "help": "Max apps (1-100)" - } - ], - "columns": [ - "rank", - "appId", - "name", - "summary", - "developer", - "license", - "isFreeLicense", - "mainCategories", - "installsLastMonth", - "updatedAt", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/flathub/search.js", - "sourceFile": "plugins/flathub/search.js" - }, - { - "site": "gemini", - "name": "ask", - "description": "Send a prompt to Gemini and return only the assistant response", - "access": "write", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [ + "help": "Original currency code" + }, { - "name": "prompt", + "name": "date", "type": "str", "required": true, - "positional": true, - "help": "Prompt to send" + "help": "Expense date as YYYY-MM-DD" }, { - "name": "model", - "type": "string", - "required": false, - "help": "Gemini model to use (e.g. \"2.5-flash\"). Use \"webcmd gemini models\" to list available values." + "name": "merchant", + "type": "str", + "required": true, + "help": "Merchant shown on the reimbursement" }, { - "name": "timeout", - "type": "int", - "default": 60, + "name": "category", + "type": "str", + "default": "Marketing & Advertising", "required": false, - "help": "Max seconds to wait (default: 60)" + "help": "Mercury expense category" }, { - "name": "new", + "name": "notes", "type": "str", - "default": "false", - "required": false, - "help": "Start a new chat first (true/false, default: false)" + "required": true, + "help": "Business purpose / reimbursement notes" }, { - "name": "thinking", + "name": "ocr-wait-seconds", "type": "str", - "default": null, + "default": "8", "required": false, - "help": "Thinking level: standard or extended (omitted = leave unchanged)" + "help": "Seconds the draft command waits after receipt upload before correcting OCR fields" + }, + { + "name": "close-after-review", + "type": "boolean", + "default": false, + "required": false, + "help": "For draft command: close the Review dialog after verification" } ], "columns": [ - "response" + "status", + "receipt", + "amount", + "currency", + "date", + "merchant", + "category", + "notes", + "safety" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/gemini/ask.js", - "sourceFile": "plugins/gemini/ask.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/mercury/reimbursement-plan.js", + "sourceFile": "plugins/mercury/reimbursement-plan.js" }, { - "site": "gemini", - "name": "deep-research", - "description": "Start a Gemini Deep Research run and confirm it", + "site": "notebooklm", + "name": "add-source", + "description": "Add a URL, text, or local file source to an existing NotebookLM notebook", "access": "write", - "domain": "gemini.google.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", + "name": "notebook", "type": "str", "required": true, "positional": true, - "help": "Prompt to send" + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "timeout", - "type": "int", - "default": 180, + "name": "url", + "type": "str", "required": false, - "help": "Max seconds for the overall command (default: 180; confirm-wait clamps internally to 6-20s)" + "help": "Source URL to add (http/https). Pass exactly one of --url, --content, --file." }, { - "name": "tool", + "name": "content", "type": "str", "required": false, - "help": "Override tool label (default: Deep Research)" + "help": "Raw text content to add as a Text source (max 10 MB)." }, { - "name": "confirm", + "name": "file", "type": "str", "required": false, - "help": "Override confirm button label (default: Start research)" + "help": "Local file path to upload as a source (max 52428800 bytes; pdf / txt / md / html / docx / etc.). Uses Google Drive's 3-step resumable upload protocol." + }, + { + "name": "title", + "type": "str", + "required": false, + "help": "Title for the text source (default \"Text Source\"). Ignored for --url and --file." + }, + { + "name": "mime-type", + "type": "str", + "required": false, + "help": "Override the auto-detected MIME type when --file is given." + }, + { + "name": "execute", + "type": "boolean", + "required": false, + "help": "Actually add the remote source to the NotebookLM notebook" } ], "columns": [ - "status", - "url" - ], - "tags": [ - "search" + "notebook_id", + "source_id", + "kind", + "identifier", + "notebook_url" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/gemini/deep-research.js", - "sourceFile": "plugins/gemini/deep-research.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/notebooklm/add-source.js", + "sourceFile": "plugins/notebooklm/add-source.js", + "navigateBefore": false }, { - "site": "gemini", - "name": "deep-research-result", - "description": "Export Deep Research report URL from a Gemini conversation", - "access": "read", - "domain": "gemini.google.com", + "site": "notebooklm", + "name": "create", + "description": "Create a new NotebookLM notebook with the given title", + "access": "write", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", + "name": "title", "type": "str", - "required": false, + "required": true, "positional": true, - "help": "Conversation title or URL (optional; defaults to latest conversation)" + "help": "Notebook title (1-200 chars)" }, { - "name": "match", + "name": "emoji", "type": "str", - "default": "contains", "required": false, - "help": "Match mode", - "choices": [ - "contains", - "exact" - ] + "help": "Notebook emoji icon (default 📒)" }, { - "name": "timeout", - "type": "int", - "default": 120, + "name": "execute", + "type": "boolean", "required": false, - "help": "Max seconds to wait for Docs export (default: 120)" + "help": "Actually create the remote NotebookLM notebook" } ], "columns": [ - "response" - ], - "tags": [ - "search" + "id", + "title", + "emoji", + "url" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/gemini/deep-research-result.js", - "sourceFile": "plugins/gemini/deep-research-result.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/notebooklm/create.js", + "sourceFile": "plugins/notebooklm/create.js", + "navigateBefore": false }, { - "site": "gemini", - "name": "detail", - "description": "Open a Gemini web conversation by id, URL, or sidebar title and read its turns", + "site": "notebooklm", + "name": "current", + "description": "Show metadata for the currently opened NotebookLM notebook tab", "access": "read", - "domain": "gemini.google.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Conversation id, /app/ URL, or sidebar title" - } - ], - "columns": [ - "Index", - "Role", - "Text" + "args": [], + "columns": [ + "id", + "title", + "url", + "source" ], "type": "js", - "modulePath": "plugins/gemini/detail.js", - "sourceFile": "plugins/gemini/detail.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/notebooklm/current.js", + "sourceFile": "plugins/notebooklm/current.js", + "navigateBefore": false }, { - "site": "gemini", - "name": "history", - "description": "List visible Gemini web conversation history from the sidebar", - "access": "read", - "domain": "gemini.google.com", + "site": "notebooklm", + "name": "generate-audio", + "description": "Trigger an Audio Overview (Deep Dive podcast) generation for a NotebookLM notebook, using all of its sources", + "access": "write", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "notebook", + "type": "str", + "required": true, + "positional": true, + "help": "Notebook id from `notebooklm list` or full notebook URL" + }, + { + "name": "execute", + "type": "boolean", "required": false, - "help": "Max conversations to show" + "help": "Actually trigger remote NotebookLM audio generation" } ], "columns": [ - "Index", - "Id", - "Title", - "Url" + "notebook_id", + "audio_id", + "source_count", + "status", + "notebook_url" ], "type": "js", - "modulePath": "plugins/gemini/history.js", - "sourceFile": "plugins/gemini/history.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/notebooklm/generate-audio.js", + "sourceFile": "plugins/notebooklm/generate-audio.js", + "navigateBefore": false }, { - "site": "gemini", - "name": "image", - "description": "Generate images with Gemini web and save them locally", + "site": "notebooklm", + "name": "generate-slides", + "description": "Trigger a Slide Deck (AI presentation) generation for a NotebookLM notebook, using all of its sources", "access": "write", - "domain": "gemini.google.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", + "name": "notebook", "type": "str", "required": true, "positional": true, - "help": "Image prompt to send to Gemini" - }, - { - "name": "rt", - "type": "str", - "default": "1:1", - "required": false, - "help": "Ratio shorthand for aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3)" + "help": "Notebook id from `notebooklm list` or full notebook URL" }, { - "name": "st", + "name": "length", "type": "str", - "default": "", "required": false, - "help": "Style shorthand, e.g. anime, icon, watercolor" + "help": "Slide deck length: 1=Short, 3=Default (default 3)" }, { - "name": "op", + "name": "language", "type": "str", - "default": "~/tmp/gemini-images", "required": false, - "help": "Output directory shorthand" + "help": "Language code (default en)" }, { - "name": "sd", + "name": "execute", "type": "boolean", - "default": false, - "required": false, - "help": "Skip download shorthand; only show Gemini page link" - }, - { - "name": "timeout", - "type": "int", - "default": 240, "required": false, - "help": "Max seconds for the overall command (default: 240)" + "help": "Actually trigger remote NotebookLM slide deck generation" } ], "columns": [ + "notebook_id", + "slides_id", + "source_count", "status", - "file", - "link" + "notebook_url" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/gemini/image.js", - "sourceFile": "plugins/gemini/image.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/notebooklm/generate-slides.js", + "sourceFile": "plugins/notebooklm/generate-slides.js", + "navigateBefore": false }, { - "site": "gemini", - "name": "login", - "description": "Open gemini login", - "access": "write", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "name", - "action", - "verify_command" + "site": "notebooklm", + "name": "get", + "aliases": [ + "metadata" ], - "type": "js", - "modulePath": "plugins/gemini/auth.js", - "sourceFile": "plugins/gemini/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "models", - "description": "List available Gemini models from the web UI", + "description": "Get rich metadata for the currently opened NotebookLM notebook", "access": "read", - "domain": "gemini.google.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "model", - "thinkingValues" + "id", + "title", + "emoji", + "source_count", + "created_at", + "updated_at", + "url", + "source" ], "type": "js", - "modulePath": "plugins/gemini/models.js", - "sourceFile": "plugins/gemini/models.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/notebooklm/get.js", + "sourceFile": "plugins/notebooklm/get.js", + "navigateBefore": false }, { - "site": "gemini", - "name": "new", - "description": "Start a new conversation in Gemini web chat", + "site": "notebooklm", + "name": "history", + "description": "List NotebookLM conversation history threads in the current notebook", "access": "read", - "domain": "gemini.google.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Status", - "Action" + "thread_id", + "item_count", + "preview", + "source", + "notebook_id", + "url" ], "type": "js", - "modulePath": "plugins/gemini/new.js", - "sourceFile": "plugins/gemini/new.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/notebooklm/history.js", + "sourceFile": "plugins/notebooklm/history.js", + "navigateBefore": false }, { - "site": "gemini", - "name": "read", - "description": "Read the turns visible in the current Gemini web conversation", + "site": "notebooklm", + "name": "list", + "description": "List NotebookLM notebooks via in-page batchexecute RPC in the current logged-in session", "access": "read", - "domain": "gemini.google.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Index", - "Role", - "Text" + "title", + "id", + "is_owner", + "created_at", + "source", + "url" ], "type": "js", - "modulePath": "plugins/gemini/read.js", - "sourceFile": "plugins/gemini/read.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/notebooklm/list.js", + "sourceFile": "plugins/notebooklm/list.js", + "navigateBefore": false }, { - "site": "gemini", - "name": "status", - "description": "Check Gemini web page availability and login state", - "access": "read", - "domain": "gemini.google.com", + "site": "notebooklm", + "name": "login", + "description": "Open notebooklm login", + "access": "write", + "domain": "google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Status", - "Login", - "Url" + "status", + "logged_in", + "site", + "name", + "authuser", + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/gemini/status.js", - "sourceFile": "plugins/gemini/status.js", + "modulePath": "plugins/notebooklm/auth.js", + "sourceFile": "plugins/notebooklm/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "gemini", - "name": "whoami", - "description": "Show the current logged-in gemini account", + "site": "notebooklm", + "name": "note-list", + "aliases": [ + "notes-list" + ], + "description": "List saved notes from the Studio panel of the current NotebookLM notebook", "access": "read", - "domain": "gemini.google.com", + "domain": "notebooklm.google.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "logged_in", - "site", - "name" + "title", + "created_at", + "source", + "url" ], "type": "js", - "modulePath": "plugins/gemini/auth.js", - "sourceFile": "plugins/gemini/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/notebooklm/note-list.js", + "sourceFile": "plugins/notebooklm/note-list.js", + "navigateBefore": false }, { - "site": "geogebra", - "name": "add-circle", - "description": "Create a circle by center+radius or center+point", - "access": "write", - "example": "webcmd geogebra add-circle --center A --radius 3", - "domain": "www.geogebra.org", - "strategy": "public", + "site": "notebooklm", + "name": "notes-get", + "description": "Get one note from the current NotebookLM notebook by title from the visible note editor", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "center", + "name": "note", "type": "str", "required": true, - "help": "Center point label (e.g. A)" - }, - { - "name": "radius", - "type": "str", - "required": false, - "help": "Radius value (number) or a point label on the circle" - }, - { - "name": "point", - "type": "str", - "required": false, - "help": "Alternative: a point label on the circle (use instead of --radius for Circle(center,point))" + "positional": true, + "help": "Note title or id from the current notebook" } ], "columns": [ - "label", - "center", - "radius" + "title", + "content", + "source", + "url" ], "type": "js", - "modulePath": "plugins/geogebra/add-circle.js", - "sourceFile": "plugins/geogebra/add-circle.js", + "modulePath": "plugins/notebooklm/notes-get.js", + "sourceFile": "plugins/notebooklm/notes-get.js", "navigateBefore": false }, { - "site": "geogebra", - "name": "add-line", - "description": "Create a line through two points or a segment between two points", - "access": "write", - "example": "webcmd geogebra add-line --points A,B --type segment", - "domain": "www.geogebra.org", - "strategy": "public", + "site": "notebooklm", + "name": "open", + "aliases": [ + "select" + ], + "description": "Open one NotebookLM notebook in the adapter session by id or URL", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "points", + "name": "notebook", "type": "str", "required": true, - "help": "Two point labels separated by comma (e.g. \"A,B\")" - }, - { - "name": "type", - "type": "str", - "default": "line", - "required": false, - "help": "Type: line, segment, or ray (default: line)", - "choices": [ - "line", - "segment", - "ray" - ] + "positional": true, + "help": "Notebook id from list output, or a full NotebookLM notebook URL" } ], "columns": [ - "label", - "type", - "points" + "id", + "title", + "url", + "source" ], "type": "js", - "modulePath": "plugins/geogebra/add-line.js", - "sourceFile": "plugins/geogebra/add-line.js", + "modulePath": "plugins/notebooklm/open.js", + "sourceFile": "plugins/notebooklm/open.js", "navigateBefore": false }, { - "site": "geogebra", - "name": "add-point", - "description": "Create a point with given label and coordinates", - "access": "write", - "example": "webcmd geogebra add-point --name A --coords 1,2", - "domain": "www.geogebra.org", - "strategy": "public", + "site": "notebooklm", + "name": "source-fulltext", + "description": "Get the extracted fulltext for one source in the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "name", - "type": "str", - "required": true, - "help": "Point label (e.g. A, B, P1)" - }, - { - "name": "coords", + "name": "source", "type": "str", "required": true, - "help": "Coordinates as x,y (e.g. \"1,2\")" + "positional": true, + "help": "Source id or title from the current notebook" } ], "columns": [ - "name", - "x", - "y" + "title", + "kind", + "char_count", + "url", + "source" ], "type": "js", - "modulePath": "plugins/geogebra/add-point.js", - "sourceFile": "plugins/geogebra/add-point.js", + "modulePath": "plugins/notebooklm/source-fulltext.js", + "sourceFile": "plugins/notebooklm/source-fulltext.js", "navigateBefore": false }, { - "site": "geogebra", - "name": "add-polygon", - "description": "Create a polygon from a list of point labels", - "access": "write", - "example": "webcmd geogebra add-polygon --points A,B,C", - "domain": "www.geogebra.org", - "strategy": "public", + "site": "notebooklm", + "name": "source-get", + "description": "Get one source from the currently opened NotebookLM notebook by id or title", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "points", + "name": "source", "type": "str", "required": true, - "help": "Comma-separated point labels (e.g. \"A,B,C\" or \"A,B,C,D\")" + "positional": true, + "help": "Source id or title from the current notebook" } ], "columns": [ - "label", - "vertices" + "title", + "id", + "type", + "size", + "created_at", + "updated_at", + "url", + "source" ], "type": "js", - "modulePath": "plugins/geogebra/add-polygon.js", - "sourceFile": "plugins/geogebra/add-polygon.js", + "modulePath": "plugins/notebooklm/source-get.js", + "sourceFile": "plugins/notebooklm/source-get.js", "navigateBefore": false }, { - "site": "geogebra", - "name": "eval", - "description": "Execute one or more GeoGebra command strings (semicolon-separated)", - "access": "write", - "example": "webcmd geogebra eval \"A=(0,0);B=(4,0);c=Circle(A,B);d=Circle(B,A);C=Intersect(c,d,1);Polygon(A,B,C)\"", - "domain": "www.geogebra.org", - "strategy": "public", + "site": "notebooklm", + "name": "source-guide", + "description": "Get the guide summary and keywords for one source in the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "command", + "name": "source", "type": "str", "required": true, "positional": true, - "help": "GeoGebra command string (use ; to chain multiple commands)" + "help": "Source id or title from the current notebook" } ], "columns": [ - "command", - "result" + "source_id", + "notebook_id", + "title", + "type", + "summary", + "keywords", + "source" ], "type": "js", - "modulePath": "plugins/geogebra/eval.js", - "sourceFile": "plugins/geogebra/eval.js", + "modulePath": "plugins/notebooklm/source-guide.js", + "sourceFile": "plugins/notebooklm/source-guide.js", "navigateBefore": false }, { - "site": "geogebra", - "name": "hexagon", - "description": "Draw a regular hexagon centered at the origin", + "site": "notebooklm", + "name": "source-list", + "description": "List sources for the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "title", + "id", + "type", + "size", + "created_at", + "updated_at", + "url", + "source" + ], + "type": "js", + "modulePath": "plugins/notebooklm/source-list.js", + "sourceFile": "plugins/notebooklm/source-list.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "status", + "description": "Check NotebookLM page availability and login state in the current Chrome session", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "login", + "page", + "url", + "title", + "notebooks" + ], + "type": "js", + "modulePath": "plugins/notebooklm/status.js", + "sourceFile": "plugins/notebooklm/status.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "summary", + "description": "Get the summary block from the currently opened NotebookLM notebook", + "access": "read", + "domain": "notebooklm.google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "title", + "summary", + "source", + "url" + ], + "type": "js", + "modulePath": "plugins/notebooklm/summary.js", + "sourceFile": "plugins/notebooklm/summary.js", + "navigateBefore": false + }, + { + "site": "notebooklm", + "name": "whoami", + "description": "Show the current logged-in notebooklm account", + "access": "read", + "domain": "google.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name", + "authuser" + ], + "type": "js", + "modulePath": "plugins/notebooklm/auth.js", + "sourceFile": "plugins/notebooklm/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "notebooklm", + "name": "write-note", + "description": "Create a Studio note in an existing NotebookLM notebook with the given title and Markdown content", "access": "write", - "example": "webcmd geogebra hexagon --size 3", - "domain": "www.geogebra.org", - "strategy": "public", + "domain": "notebooklm.google.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "size", + "name": "notebook", "type": "str", - "default": "2", + "required": true, + "positional": true, + "help": "Notebook id from `notebooklm list` or full notebook URL" + }, + { + "name": "title", + "type": "str", + "required": true, + "help": "Note title (1-200 chars)" + }, + { + "name": "content", + "type": "str", + "required": true, + "help": "Note body as Markdown" + }, + { + "name": "execute", + "type": "boolean", "required": false, - "help": "Radius of the hexagon (default: 2)" + "help": "Actually create the remote NotebookLM note" } ], "columns": [ - "step", - "result" + "notebook_id", + "note_id", + "title", + "notebook_url" ], "type": "js", - "modulePath": "plugins/geogebra/hexagon.js", - "sourceFile": "plugins/geogebra/hexagon.js", + "modulePath": "plugins/notebooklm/write-note.js", + "sourceFile": "plugins/notebooklm/write-note.js", "navigateBefore": false }, { - "site": "geogebra", - "name": "info", - "description": "Get detailed properties of a GeoGebra object", + "site": "npm", + "name": "downloads", + "description": "Daily download counts for an npm package over a window", "access": "read", - "example": "webcmd geogebra info --name A", - "domain": "www.geogebra.org", + "domain": "api.npmjs.org", "strategy": "public", - "browser": true, + "browser": false, "args": [ { "name": "name", "type": "str", "required": true, - "help": "Object label (e.g. A, c1, poly1)" + "positional": true, + "help": "npm package name (e.g. \"react\", \"@vercel/og\")" + }, + { + "name": "period", + "type": "str", + "default": "last-week", + "required": false, + "help": "last-day / last-week / last-month / last-year, or YYYY-MM-DD:YYYY-MM-DD" } ], "columns": [ - "property", - "value" + "rank", + "package", + "day", + "downloads" ], "type": "js", - "modulePath": "plugins/geogebra/info.js", - "sourceFile": "plugins/geogebra/info.js", - "navigateBefore": false + "modulePath": "plugins/npm/downloads.js", + "sourceFile": "plugins/npm/downloads.js" }, { - "site": "geogebra", - "name": "list", - "description": "List all geometric objects on the GeoGebra canvas", + "site": "npm", + "name": "package", + "description": "Single npm package metadata (latest version, license, homepage, repository). Use `npm downloads` for stats.", "access": "read", - "domain": "www.geogebra.org", + "domain": "registry.npmjs.org", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "type", + "name": "name", "type": "str", - "required": false, - "help": "Filter by object type (e.g. \"point\", \"line\", \"circle\")" + "required": true, + "positional": true, + "help": "npm package name (e.g. \"react\", \"@vercel/og\")" } ], "columns": [ "name", - "type", - "value", - "visible" + "latestVersion", + "description", + "license", + "homepage", + "repository", + "bugs", + "maintainers", + "keywords", + "created", + "modified", + "url" ], "type": "js", - "modulePath": "plugins/geogebra/list.js", - "sourceFile": "plugins/geogebra/list.js", - "navigateBefore": false + "modulePath": "plugins/npm/package.js", + "sourceFile": "plugins/npm/package.js" }, { - "site": "geogebra", - "name": "triangle", - "description": "Draw an equilateral triangle from a horizontal base segment", - "access": "write", - "example": "webcmd geogebra triangle --size 4", - "domain": "www.geogebra.org", + "site": "npm", + "name": "search", + "description": "Search the public npm registry by keyword", + "access": "read", + "domain": "registry.npmjs.org", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "size", + "name": "query", "type": "str", - "default": "2", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"react\", \"graphql client\")" + }, + { + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Side length of the triangle (default: 2)" + "help": "Max results (1-250)" } ], "columns": [ - "step", - "result" - ], - "type": "js", - "modulePath": "plugins/geogebra/triangle.js", - "sourceFile": "plugins/geogebra/triangle.js", - "navigateBefore": false - }, - { - "site": "github", - "name": "login", - "description": "Open github login", - "access": "write", - "domain": "github.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "id", - "username", + "rank", "name", - "url", - "action", - "verify_command" + "version", + "description", + "weeklyDownloads", + "dependents", + "license", + "publisher", + "updated", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/github/auth.js", - "sourceFile": "plugins/github/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/npm/search.js", + "sourceFile": "plugins/npm/search.js" }, { - "site": "github", - "name": "whoami", - "description": "Show the current logged-in github account", + "site": "nuget", + "name": "package", + "description": "Full NuGet package version history (catalogEntry per release)", "access": "read", - "domain": "github.com", - "strategy": "cookie", - "browser": true, - "args": [], + "domain": "api.nuget.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "NuGet package id (e.g. \"Newtonsoft.Json\", case-insensitive)" + } + ], "columns": [ - "logged_in", - "site", + "rank", "id", - "username", - "name", + "version", + "title", + "authors", + "tags", + "language", + "licenseExpression", + "projectUrl", + "published", + "listed", "url" ], "type": "js", - "modulePath": "plugins/github/auth.js", - "sourceFile": "plugins/github/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/nuget/package.js", + "sourceFile": "plugins/nuget/package.js" }, { - "site": "github-trending", - "name": "repos", - "description": "GitHub Trending repositories (public, no login). Filter by --language and --since.", + "site": "nuget", + "name": "search", + "description": "Search NuGet packages by keyword", "access": "read", - "domain": "github.com", + "domain": "api.nuget.org", "strategy": "public", "browser": false, "args": [ { - "name": "since", - "type": "string", - "default": "daily", - "required": false, - "help": "Time range: daily / weekly / monthly" + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword" }, { - "name": "language", - "type": "string", - "default": "", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Filter by programming language slug, e.g. python, rust, \"c++\"" + "help": "Max packages (1-1000)" }, { - "name": "limit", - "type": "int", - "default": 25, + "name": "prerelease", + "type": "boolean", + "default": false, "required": false, - "help": "Number of repositories to return (max 25)" + "help": "Include prerelease versions" } ], "columns": [ "rank", - "repo", + "id", + "version", + "title", "description", - "language", - "stars", - "forks", - "starsSince", + "authors", + "tags", + "totalDownloads", + "verified", + "projectUrl", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/github-trending/repos.js", - "sourceFile": "plugins/github-trending/repos.js" + "modulePath": "plugins/nuget/search.js", + "sourceFile": "plugins/nuget/search.js" }, { - "site": "goettingen", - "name": "export-postgraduate-courses", - "description": "Export University of Göttingen postgraduate programmes from the official A-Z API.", + "site": "nvd", + "name": "cve", + "description": "NIST NVD CVE detail (description, CVSS, CWE, KEV flag)", "access": "read", - "example": "webcmd goettingen export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.uni-goettingen.de", + "domain": "services.nvd.nist.gov", "strategy": "public", "browser": false, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "CVE identifier (e.g. \"CVE-2021-44228\")" + } + ], + "columns": [ + "id", + "published", + "lastModified", + "vulnStatus", + "baseScore", + "severity", + "attackVector", + "cwe", + "kevAdded", + "description", + "url" + ], + "type": "js", + "modulePath": "plugins/nvd/cve.js", + "sourceFile": "plugins/nvd/cve.js" + }, + { + "site": "oeis", + "name": "search", + "description": "Search OEIS sequences by keyword or numeric pattern", + "access": "read", + "domain": "oeis.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword or comma-separated terms (e.g. \"fibonacci\", \"1,1,2,3,5,8\")" }, { - "name": "count", + "name": "limit", "type": "int", + "default": 10, "required": false, - "help": "Positive maximum number of programmes after filtering and deduplication" + "help": "Max sequences (1-100)" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "rank", + "id", + "name", + "keywords", + "preview", + "author", + "created", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/goettingen/export-postgraduate-courses.js", - "sourceFile": "plugins/goettingen/export-postgraduate-courses.js" + "modulePath": "plugins/oeis/search.js", + "sourceFile": "plugins/oeis/search.js" }, { - "site": "google", - "name": "images", - "description": "Search Google Images for photos and image results", + "site": "oeis", + "name": "sequence", + "description": "Full OEIS sequence detail by A-number (terms, name, keywords, formula counts)", "access": "read", - "domain": "google.com", + "domain": "oeis.org", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "keyword", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Image search query" + "help": "OEIS sequence id (e.g. \"A000045\" for Fibonacci)" + } + ], + "columns": [ + "id", + "name", + "keywords", + "preview", + "termCount", + "offset", + "author", + "created", + "revision", + "commentCount", + "formulaCount", + "referenceCount", + "xrefCount", + "linkCount", + "url" + ], + "type": "js", + "modulePath": "plugins/oeis/sequence.js", + "sourceFile": "plugins/oeis/sequence.js" + }, + { + "site": "openalex", + "name": "search", + "description": "Search OpenAlex Works (papers, books, preprints) by keyword", + "access": "read", + "domain": "api.openalex.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search text (e.g. \"transformers\", \"open access scholarly\")" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of image results (1-100)" - }, - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language short code (e.g. en, zh)" - }, - { - "name": "resolve", - "type": "bool", - "default": true, - "required": false, - "help": "Click image previews to resolve original imgurl values" + "help": "Max works (1-200, single OpenAlex page)" } ], "columns": [ "rank", + "id", "title", - "imageUrl", - "thumbnailUrl", - "sourceUrl", - "source", - "width", - "height" + "year", + "citations", + "firstAuthor", + "venue", + "openAccess", + "type", + "doi", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/google/images.js", - "sourceFile": "plugins/google/images.js", - "navigateBefore": false + "modulePath": "plugins/openalex/search.js", + "sourceFile": "plugins/openalex/search.js" }, { - "site": "google", - "name": "news", - "description": "Get Google News headlines", + "site": "openalex", + "name": "work", + "description": "Fetch a single OpenAlex Work (paper / preprint / book) — metadata + abstract", "access": "read", + "domain": "api.openalex.org", "strategy": "public", "browser": false, "args": [ { - "name": "keyword", + "name": "id", "type": "str", - "required": false, + "required": true, "positional": true, - "help": "Search query (omit for top stories)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - }, - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language short code (e.g. en, zh)" - }, - { - "name": "region", - "type": "str", - "default": "US", - "required": false, - "help": "Region code (e.g. US, CN)" + "help": "OpenAlex Work id (\"W2741809807\"), DOI (\"10.7717/peerj.4375\"), or full URL" } ], "columns": [ + "id", "title", - "source", + "type", + "year", "date", + "language", + "authors", + "venue", + "citations", + "openAccess", + "openAccessUrl", + "referencedCount", + "doi", + "abstract", "url" ], "type": "js", - "modulePath": "plugins/google/news.js", - "sourceFile": "plugins/google/news.js" + "modulePath": "plugins/openalex/work.js", + "sourceFile": "plugins/openalex/work.js" }, { - "site": "google", - "name": "search", - "description": "Search Google", + "site": "openfda", + "name": "drug-label", + "description": "Search FDA-approved drug labels (brand or generic name)", "access": "read", - "domain": "google.com", + "domain": "fda.gov", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "keyword", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "Brand or generic drug name (e.g. \"aspirin\", \"lisinopril\")" }, { "name": "limit", "type": "int", - "default": 10, - "required": false, - "help": "Number of results (1-100)" - }, - { - "name": "lang", - "type": "str", - "default": "en", + "default": 5, "required": false, - "help": "Language short code (e.g. en, zh)" + "help": "Max rows (1-25, default 5; openFDA caps anonymous tier at 25/page)" } ], "columns": [ - "type", - "title", - "url", - "snippet" - ], - "tags": [ - "search" + "rank", + "id", + "brandName", + "genericName", + "manufacturer", + "productType", + "route", + "productNdc", + "pharmClass", + "purpose", + "indications", + "warnings", + "dosage", + "effectiveTime" ], "type": "js", - "modulePath": "plugins/google/search.js", - "sourceFile": "plugins/google/search.js" + "modulePath": "plugins/openfda/drug-label.js", + "sourceFile": "plugins/openfda/drug-label.js" }, { - "site": "google", - "name": "suggest", - "description": "Get Google search suggestions", + "site": "openfda", + "name": "food-recall", + "description": "FDA food recall and enforcement actions (most recent first)", "access": "read", + "domain": "fda.gov", "strategy": "public", "browser": false, "args": [ { - "name": "keyword", + "name": "query", "type": "str", - "required": true, - "positional": true, - "help": "Search query" + "required": false, + "help": "Free-text Lucene query (e.g. \"salmonella\", \"listeria\"); default: all recent recalls" }, { - "name": "lang", + "name": "status", "type": "str", - "default": "zh-CN", "required": false, - "help": "Language code" + "help": "Filter by status: \"Ongoing\", \"Completed\", \"Terminated\"" + }, + { + "name": "classification", + "type": "str", + "required": false, + "help": "Filter by class: \"Class I\" (most serious), \"Class II\", \"Class III\"" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max rows (1-100, default 10; openFDA caps anonymous tier at 100/page)" } ], "columns": [ - "suggestion" + "rank", + "recallNumber", + "status", + "classification", + "voluntary", + "recallingFirm", + "city", + "state", + "country", + "productDescription", + "reasonForRecall", + "productQuantity", + "distributionPattern", + "reportDate", + "recallInitiationDate", + "terminationDate" ], - "type": "js", - "modulePath": "plugins/google/suggest.js", - "sourceFile": "plugins/google/suggest.js" + "type": "js", + "modulePath": "plugins/openfda/food-recall.js", + "sourceFile": "plugins/openfda/food-recall.js" }, { - "site": "google", - "name": "trends", - "description": "Get Google Trends daily trending searches", + "site": "openreview", + "name": "author", + "description": "List OpenReview submissions by an author profile id (newest first)", "access": "read", + "domain": "openreview.net", "strategy": "public", "browser": false, "args": [ { - "name": "region", + "name": "profile", "type": "str", - "default": "US", - "required": false, - "help": "Region code (e.g. US, CN, JP)" + "required": true, + "positional": true, + "help": "OpenReview profile id (e.g. \"~Yoshua_Bengio1\"). Find it on the author profile URL on openreview.net." }, { "name": "limit", "type": "int", - "default": 20, + "default": 50, "required": false, - "help": "Number of results" + "help": "Max submissions (1-1000)" } ], "columns": [ + "rank", + "id", "title", - "traffic", - "date" + "authors", + "venue", + "pdate", + "url" ], "type": "js", - "modulePath": "plugins/google/trends.js", - "sourceFile": "plugins/google/trends.js" + "modulePath": "plugins/openreview/author.js", + "sourceFile": "plugins/openreview/author.js" }, { - "site": "google-scholar", - "name": "cite", - "description": "Get citation for a Google Scholar paper", + "site": "openreview", + "name": "paper", + "description": "Show full metadata for a single OpenReview paper", "access": "read", - "domain": "scholar.google.com", + "domain": "openreview.net", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "query", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Paper title to search for" - }, - { - "name": "style", - "type": "str", - "default": "bibtex", - "required": false, - "help": "Citation format", - "choices": [ - "bibtex", - "endnote", - "refman", - "refworks" - ] - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Which search result to cite (1-based)" + "help": "OpenReview note id (e.g. \"5sRnsubyAK\")" } ], "columns": [ + "id", "title", - "format", - "citation" + "authors", + "keywords", + "venue", + "venueid", + "primary_area", + "abstract", + "pdate", + "pdf", + "url" ], "type": "js", - "modulePath": "plugins/google-scholar/cite.js", - "sourceFile": "plugins/google-scholar/cite.js" + "modulePath": "plugins/openreview/paper.js", + "sourceFile": "plugins/openreview/paper.js" }, { - "site": "google-scholar", - "name": "profile", - "description": "View a Google Scholar author profile", + "site": "openreview", + "name": "reviews", + "description": "Show full review thread (paper + reviews + decisions) for an OpenReview forum", "access": "read", - "domain": "scholar.google.com", + "domain": "openreview.net", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "author", + "name": "forum", "type": "str", "required": true, "positional": true, - "help": "Author name or Scholar user ID (e.g. JicYPdAAAAAJ)" + "help": "OpenReview forum id (same as paper id)" }, { - "name": "limit", + "name": "max-length", "type": "int", - "default": 10, + "default": 4000, "required": false, - "help": "Max papers to show (max 20)" + "help": "Per-row text truncation (min 200)" } ], "columns": [ - "rank", - "title", - "cited", - "year" + "type", + "author", + "rating", + "confidence", + "text" ], "type": "js", - "modulePath": "plugins/google-scholar/profile.js", - "sourceFile": "plugins/google-scholar/profile.js" + "modulePath": "plugins/openreview/reviews.js", + "sourceFile": "plugins/openreview/reviews.js" }, { - "site": "google-scholar", + "site": "openreview", "name": "search", - "description": "Google Scholar scholar search", + "description": "Search OpenReview papers by free-text query", "access": "read", - "domain": "scholar.google.com", + "domain": "openreview.net", "strategy": "public", - "browser": true, + "browser": false, "args": [ { "name": "query", "type": "str", "required": true, "positional": true, - "help": "Search keyword" + "help": "Search keyword (e.g. \"diffusion model\")" }, { "name": "limit", "type": "int", - "default": 10, + "default": 25, "required": false, - "help": "Number of results to return (max 20)" + "help": "Max results (max 50)" } ], "columns": [ "rank", + "id", "title", "authors", - "source", - "year", - "cited", + "venue", + "pdate", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/google-scholar/search.js", - "sourceFile": "plugins/google-scholar/search.js" - }, - { - "site": "goproxy", - "name": "module", - "description": "Latest version + VCS origin metadata for a Go module on proxy.golang.org", - "access": "read", - "domain": "proxy.golang.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "module", - "type": "string", - "required": true, - "positional": true, - "help": "Go module path (e.g. \"github.com/gin-gonic/gin\", \"golang.org/x/net\")" - } - ], - "columns": [ - "module", - "version", - "publishedAt", - "vcs", - "repository", - "commit", - "ref", - "pkgGoDevUrl", - "url" - ], - "type": "js", - "modulePath": "plugins/goproxy/module.js", - "sourceFile": "plugins/goproxy/module.js" + "modulePath": "plugins/openreview/search.js", + "sourceFile": "plugins/openreview/search.js" }, { - "site": "goproxy", - "name": "versions", - "description": "Published version tags for a Go module (newest first), optionally with publish times", + "site": "openreview", + "name": "venue", + "description": "List papers at an OpenReview venue (e.g. \"ICLR 2024 oral\" or full invitation id)", "access": "read", - "domain": "proxy.golang.org", + "domain": "openreview.net", "strategy": "public", "browser": false, "args": [ { - "name": "module", - "type": "string", + "name": "venue", + "type": "str", "required": true, "positional": true, - "help": "Go module path (e.g. \"github.com/gin-gonic/gin\")" + "help": "Venue name (\"ICLR 2024 oral\") or invitation (\"ICLR.cc/2025/Conference/-/Submission\")" }, { "name": "limit", "type": "int", - "default": 30, + "default": 25, "required": false, - "help": "Max rows to return (1-200)" + "help": "Max results (max 200)" }, { - "name": "with-time", - "type": "boolean", - "default": false, + "name": "offset", + "type": "int", + "default": 0, "required": false, - "help": "Fetch each version's publish time (one extra request per row)" + "help": "Pagination offset" } ], "columns": [ "rank", - "module", - "version", - "publishedAt", + "id", + "title", + "authors", + "keywords", + "primary_area", + "pdate", + "pdf", "url" ], "type": "js", - "modulePath": "plugins/goproxy/versions.js", - "sourceFile": "plugins/goproxy/versions.js" + "modulePath": "plugins/openreview/venue.js", + "sourceFile": "plugins/openreview/venue.js" }, { - "site": "hackernews", - "name": "ask", - "description": "Hacker News Ask HN posts", + "site": "osv", + "name": "query", + "description": "OSV.dev vulnerabilities affecting a package (optionally pinned to a version)", "access": "read", - "domain": "news.ycombinator.com", + "domain": "osv.dev", "strategy": "public", "browser": false, "args": [ + { + "name": "package", + "type": "string", + "required": true, + "positional": true, + "help": "Package name (e.g. \"lodash\", \"django\")" + }, + { + "name": "ecosystem", + "type": "string", + "required": true, + "help": "OSV ecosystem (npm / PyPI / Go / Maven / NuGet / RubyGems / crates.io / Packagist / ...)" + }, + { + "name": "version", + "type": "string", + "required": false, + "help": "Pin to a specific version (e.g. \"4.17.20\"); omit for all known vulns" + }, { "name": "limit", "type": "int", - "default": 20, + "default": 30, "required": false, - "help": "Number of stories" + "help": "Max rows to return (1-200)" } ], "columns": [ "rank", "id", - "title", - "score", - "author", - "comments", + "summary", + "severity", + "aliases", + "published", + "modified", + "affectedPackages", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/hackernews/ask.js", - "sourceFile": "plugins/hackernews/ask.js" + "modulePath": "plugins/osv/query.js", + "sourceFile": "plugins/osv/query.js" }, { - "site": "hackernews", - "name": "best", - "description": "Hacker News best stories", + "site": "osv", + "name": "vulnerability", + "description": "Single OSV.dev vulnerability detail (severity, affected packages, CVE/GHSA aliases)", "access": "read", - "domain": "news.ycombinator.com", + "domain": "osv.dev", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" + "name": "id", + "type": "string", + "required": true, + "positional": true, + "help": "OSV vulnerability id (e.g. \"GHSA-29mw-wpgm-hmr9\", \"CVE-2020-28500\")" } ], "columns": [ - "rank", "id", - "title", - "score", - "author", - "comments", + "summary", + "severity", + "aliases", + "published", + "modified", + "affectedPackages", + "cwes", + "referenceCount", "url" ], "type": "js", - "modulePath": "plugins/hackernews/best.js", - "sourceFile": "plugins/hackernews/best.js" + "modulePath": "plugins/osv/vulnerability.js", + "sourceFile": "plugins/osv/vulnerability.js" }, { - "site": "hackernews", - "name": "jobs", - "description": "Hacker News job postings", + "site": "packagist", + "name": "package", + "description": "Fetch a Packagist package's metadata (version, downloads, license, repo, GitHub stars)", "access": "read", - "domain": "news.ycombinator.com", + "domain": "packagist.org", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of job postings" + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Composer package \"/\" (e.g. \"symfony/console\", \"monolog/monolog\")" } ], "columns": [ - "rank", - "id", - "title", - "author", + "package", + "version", + "releasedAt", + "license", + "description", + "repository", + "githubStars", + "favers", + "downloads", + "monthlyDownloads", + "dailyDownloads", "url" ], "type": "js", - "modulePath": "plugins/hackernews/jobs.js", - "sourceFile": "plugins/hackernews/jobs.js" + "modulePath": "plugins/packagist/package.js", + "sourceFile": "plugins/packagist/package.js" }, { - "site": "hackernews", - "name": "new", - "description": "Hacker News newest stories", + "site": "packagist", + "name": "search", + "description": "Search Packagist (PHP / Composer) packages by keyword", "access": "read", - "domain": "news.ycombinator.com", + "domain": "packagist.org", "strategy": "public", "browser": false, "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (e.g. \"symfony\", \"laravel http\")" + }, { "name": "limit", "type": "int", - "default": 20, + "default": 30, "required": false, - "help": "Number of stories" + "help": "Max packages (1-100, single Packagist page)" } ], "columns": [ "rank", - "id", - "title", - "score", - "author", - "comments", + "package", + "description", + "downloads", + "favers", + "repository", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/hackernews/new.js", - "sourceFile": "plugins/hackernews/new.js" + "modulePath": "plugins/packagist/search.js", + "sourceFile": "plugins/packagist/search.js" }, { - "site": "hackernews", - "name": "read", - "description": "Read a Hacker News story and its comment tree", - "access": "read", - "domain": "news.ycombinator.com", + "site": "paperreview", + "name": "feedback", + "description": "Submit feedback for a paperreview.ai review token", + "access": "write", + "domain": "paperreview.ai", "strategy": "public", "browser": false, "args": [ { - "name": "id", + "name": "token", "type": "str", "required": true, "positional": true, - "help": "HN item ID (e.g. 39847301)" + "help": "Review token returned by paperreview.ai" }, { - "name": "limit", + "name": "helpfulness", "type": "int", - "default": 25, - "required": false, - "help": "Max top-level comments" + "required": true, + "help": "Helpfulness score from 1 to 5" }, { - "name": "depth", - "type": "int", - "default": 2, - "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + "name": "critical-error", + "type": "str", + "required": true, + "help": "Whether the review contains a critical error", + "choices": [ + "yes", + "no" + ] }, { - "name": "replies", - "type": "int", - "default": 5, + "name": "actionable-suggestions", + "type": "str", + "required": true, + "help": "Whether the review contains actionable suggestions", + "choices": [ + "yes", + "no" + ] + }, + { + "name": "additional-comments", + "type": "str", "required": false, - "help": "Max replies shown per comment at each level" + "help": "Optional free-text feedback" }, { - "name": "max-length", + "name": "timeout", "type": "int", - "default": 2000, + "default": 30, "required": false, - "help": "Max characters per comment body (min 100)" + "help": "Max seconds for the overall command (default: 30)" } ], "columns": [ - "type", - "author", - "score", - "text" + "status", + "token", + "helpfulness", + "critical_error", + "actionable_suggestions", + "message" ], "type": "js", - "modulePath": "plugins/hackernews/read.js", - "sourceFile": "plugins/hackernews/read.js" + "modulePath": "plugins/paperreview/feedback.js", + "sourceFile": "plugins/paperreview/feedback.js" }, { - "site": "hackernews", - "name": "search", - "description": "Search Hacker News stories", + "site": "paperreview", + "name": "review", + "description": "Fetch a paperreview.ai review by token", "access": "read", - "domain": "news.ycombinator.com", + "domain": "paperreview.ai", "strategy": "public", "browser": false, "args": [ { - "name": "query", + "name": "token", "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "Review token returned by paperreview.ai" }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - }, - { - "name": "sort", - "type": "str", - "default": "relevance", + "default": 30, "required": false, - "help": "Sort by relevance or date", - "choices": [ - "relevance", - "date" - ] + "help": "Max seconds for the overall command (default: 30)" } ], "columns": [ - "rank", - "id", + "status", "title", - "score", - "author", - "comments", - "url" - ], - "tags": [ - "search" + "venue", + "numerical_score", + "has_feedback", + "review_url" ], "type": "js", - "modulePath": "plugins/hackernews/search.js", - "sourceFile": "plugins/hackernews/search.js" + "modulePath": "plugins/paperreview/review.js", + "sourceFile": "plugins/paperreview/review.js" }, { - "site": "hackernews", - "name": "show", - "description": "Hacker News Show HN posts", - "access": "read", - "domain": "news.ycombinator.com", + "site": "paperreview", + "name": "submit", + "description": "Submit a PDF to paperreview.ai for review", + "access": "write", + "domain": "paperreview.ai", "strategy": "public", "browser": false, "args": [ { - "name": "limit", + "name": "pdf", + "type": "str", + "required": true, + "positional": true, + "help": "Path to the paper PDF" + }, + { + "name": "email", + "type": "str", + "required": true, + "help": "Email address for the submission" + }, + { + "name": "venue", + "type": "str", + "required": false, + "help": "Optional target venue such as ICLR or NeurIPS" + }, + { + "name": "dry-run", + "type": "bool", + "default": false, + "required": false, + "help": "Validate the input and stop before remote submission" + }, + { + "name": "prepare-only", + "type": "bool", + "default": false, + "required": false, + "help": "Request an upload slot but stop before uploading the PDF" + }, + { + "name": "timeout", "type": "int", - "default": 20, + "default": 120, "required": false, - "help": "Number of stories" + "help": "Max seconds for the overall command (default: 120)" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" + "status", + "file", + "email", + "venue", + "token", + "review_url", + "message" ], "type": "js", - "modulePath": "plugins/hackernews/show.js", - "sourceFile": "plugins/hackernews/show.js" + "modulePath": "plugins/paperreview/submit.js", + "sourceFile": "plugins/paperreview/submit.js" }, { - "site": "hackernews", - "name": "top", - "description": "Hacker News top stories", + "site": "pixiv", + "name": "detail", + "description": "View illustration details (tags, stats, URLs)", "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Illustration ID" } ], "columns": [ - "rank", - "id", + "illust_id", "title", - "score", "author", - "comments", + "type", + "pages", + "bookmarks", + "likes", + "views", + "tags", + "created", "url" ], "type": "js", - "modulePath": "plugins/hackernews/top.js", - "sourceFile": "plugins/hackernews/top.js" + "modulePath": "plugins/pixiv/detail.js", + "sourceFile": "plugins/pixiv/detail.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "hackernews", - "name": "user", - "description": "Hacker News user profile", + "site": "pixiv", + "name": "download", + "description": "Download illustration images from Pixiv", "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "username", + "name": "illust-id", "type": "str", "required": true, "positional": true, - "help": "HN username" + "help": "Illustration ID" + }, + { + "name": "output", + "type": "str", + "default": "./pixiv-downloads", + "required": false, + "help": "Output directory" } ], "columns": [ - "username", - "karma", - "created", - "about" + "index", + "type", + "status", + "size" ], "type": "js", - "modulePath": "plugins/hackernews/user.js", - "sourceFile": "plugins/hackernews/user.js" + "modulePath": "plugins/pixiv/download.js", + "sourceFile": "plugins/pixiv/download.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "heidelberg", - "name": "export-postgraduate-courses", - "description": "Export Heidelberg University non-bachelor/postgraduate programs using the official study finder.", + "site": "pixiv", + "name": "illusts", + "description": "List a Pixiv artist's illustrations", "access": "read", - "example": "webcmd heidelberg export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.uni-heidelberg.de", - "strategy": "public", - "browser": false, + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "name": "user-id", + "type": "str", + "required": true, + "positional": true, + "help": "Pixiv user ID" }, { - "name": "count", + "name": "limit", "type": "int", + "default": 20, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Number of results" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "rank", + "title", + "illust_id", + "pages", + "bookmarks", + "tags", + "created", + "url" ], "type": "js", - "modulePath": "plugins/heidelberg/export-postgraduate-courses.js", - "sourceFile": "plugins/heidelberg/export-postgraduate-courses.js" + "modulePath": "plugins/pixiv/illusts.js", + "sourceFile": "plugins/pixiv/illusts.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "hf", - "name": "datasets", - "description": "Top Hugging Face datasets (downloads / likes / trending / freshness).", + "site": "pixiv", + "name": "login", + "description": "Open pixiv login", + "access": "write", + "domain": "pixiv.net", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/pixiv/auth.js", + "sourceFile": "plugins/pixiv/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "pixiv", + "name": "ranking", + "description": "Pixiv illustration rankings (daily/weekly/monthly)", "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "sort", - "type": "string", - "default": "downloads", + "name": "mode", + "type": "str", + "default": "daily", "required": false, - "help": "Sort key: downloads, likes, trending, created_at, last_modified" + "help": "Ranking mode", + "choices": [ + "daily", + "weekly", + "monthly", + "rookie", + "original", + "male", + "female", + "daily_r18", + "weekly_r18" + ] }, { - "name": "search", - "type": "string", + "name": "page", + "type": "int", + "default": 1, "required": false, - "help": "Optional name/owner substring filter." + "help": "Page number" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max datasets (max 100; one API page)." + "help": "Number of results" } ], "columns": [ "rank", - "id", + "title", "author", - "downloads", - "likes", - "tags", - "lastModified", + "user_id", + "illust_id", + "pages", + "bookmarks", "url" ], "type": "js", - "modulePath": "plugins/hf/datasets.js", - "sourceFile": "plugins/hf/datasets.js" + "modulePath": "plugins/pixiv/ranking.js", + "sourceFile": "plugins/pixiv/ranking.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "hf", - "name": "login", - "description": "Open hf login", - "access": "write", - "domain": "huggingface.co", + "site": "pixiv", + "name": "search", + "description": "Search Pixiv illustrations by keyword", + "access": "read", + "domain": "www.pixiv.net", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "username", - "fullname", - "type", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/hf/auth.js", - "sourceFile": "plugins/hf/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "hf", - "name": "models", - "description": "Top Hugging Face models (downloads / likes / trending / freshness).", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, "args": [ { - "name": "sort", - "type": "string", - "default": "downloads", + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword or tag" + }, + { + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Sort key: downloads, likes, trending, created_at, last_modified" + "help": "Number of results" }, { - "name": "search", - "type": "string", + "name": "order", + "type": "str", + "default": "date_d", "required": false, - "help": "Optional name/owner substring filter (e.g. \"llama\", \"mistralai/\")" + "help": "Sort order", + "choices": [ + "date_d", + "date", + "popular_d", + "popular_male_d", + "popular_female_d" + ] }, { - "name": "pipeline", - "type": "string", + "name": "mode", + "type": "str", + "default": "all", "required": false, - "help": "Filter by pipeline tag (e.g. text-generation, image-classification)" + "help": "Search mode", + "choices": [ + "all", + "safe", + "r18" + ] }, { - "name": "limit", + "name": "page", "type": "int", - "default": 20, + "default": 1, "required": false, - "help": "Max models (max 100; one API page)." + "help": "Page number" } ], "columns": [ "rank", - "id", + "title", "author", - "pipelineTag", - "downloads", - "likes", + "user_id", + "illust_id", + "pages", + "bookmarks", "tags", - "lastModified", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/hf/models.js", - "sourceFile": "plugins/hf/models.js" + "modulePath": "plugins/pixiv/search.js", + "sourceFile": "plugins/pixiv/search.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "hf", - "name": "paper", - "description": "Hugging Face paper detail by arXiv id (full title / summary / authors / AI keywords)", + "site": "pixiv", + "name": "user", + "description": "View Pixiv artist profile", "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, + "domain": "www.pixiv.net", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "uid", "type": "str", "required": true, "positional": true, - "help": "arXiv id (e.g. \"1706.03762\") — same value HF uses to mirror the paper" + "help": "Pixiv user ID" } ], "columns": [ - "id", - "title", - "authors", - "publishedAt", - "upvotes", - "aiKeywords", - "summary", - "aiSummary", + "user_id", + "name", + "premium", + "following", + "illusts", + "manga", + "novels", + "comment", "url" ], "type": "js", - "modulePath": "plugins/hf/paper.js", - "sourceFile": "plugins/hf/paper.js" + "modulePath": "plugins/pixiv/user.js", + "sourceFile": "plugins/pixiv/user.js", + "navigateBefore": "https://www.pixiv.net" }, { - "site": "hf", - "name": "spaces", - "description": "Top Hugging Face Spaces (likes / created_at / last_modified).", + "site": "pixiv", + "name": "whoami", + "description": "Show the current logged-in pixiv account", "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, + "domain": "pixiv.net", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "user_id", + "name" + ], + "type": "js", + "modulePath": "plugins/pixiv/auth.js", + "sourceFile": "plugins/pixiv/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "practo", + "name": "appointment", + "description": "Show logged-in Practo Drive appointment details", + "access": "read", + "domain": "drive.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "sort", - "type": "string", - "default": "likes", - "required": false, - "help": "Sort key: likes, created_at, last_modified" + "name": "appointment_id", + "type": "str", + "required": true, + "positional": true, + "help": "Appointment id from `practo appointments`" + } + ], + "columns": [ + "appointment_id", + "status", + "summary" + ], + "type": "js", + "modulePath": "plugins/practo/appointment.js", + "sourceFile": "plugins/practo/appointment.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "practo", + "name": "appointments", + "description": "List logged-in Practo Drive appointments", + "access": "read", + "domain": "drive.practo.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "appointment_id", + "doctor", + "practice", + "time", + "status" + ], + "type": "js", + "modulePath": "plugins/practo/appointments.js", + "sourceFile": "plugins/practo/appointments.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "practo", + "name": "book-confirm", + "description": "Confirm a Practo clinic visit booking after explicit confirmation", + "access": "write", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "practice_doctor_id", + "type": "str", + "required": true, + "positional": true, + "help": "Practo practice_doctor_id" }, { - "name": "search", - "type": "string", - "required": false, - "help": "Optional name/owner substring filter (e.g. \"stability\", \"openai/\")" + "name": "time", + "type": "str", + "required": true, + "help": "Slot time YYYY-MM-DD HH:mm:ss" }, { - "name": "sdk", - "type": "string", + "name": "profile-url", + "type": "str", "required": false, - "help": "Filter by Space SDK: gradio / streamlit / docker / static" + "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "confirm", + "type": "boolean", + "default": false, "required": false, - "help": "Max spaces (max 100; one API page)." + "help": "Required. Set --confirm true to create the appointment." } ], "columns": [ - "rank", - "id", - "author", - "sdk", - "likes", - "tags", - "lastModified", + "status", + "practice_doctor_id", + "time", "url" ], "type": "js", - "modulePath": "plugins/hf/spaces.js", - "sourceFile": "plugins/hf/spaces.js" + "modulePath": "plugins/practo/book-confirm.js", + "sourceFile": "plugins/practo/book-confirm.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "hf", - "name": "top", - "description": "Top upvoted Hugging Face papers", + "site": "practo", + "name": "book-preview", + "description": "Preview Practo booking details for a selected slot without confirming", "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of papers" - }, - { - "name": "all", - "type": "bool", - "default": false, - "required": false, - "help": "Return all papers (ignore limit)" + "name": "practice_doctor_id", + "type": "str", + "required": true, + "positional": true, + "help": "Practo practice_doctor_id" }, { - "name": "date", + "name": "time", "type": "str", - "required": false, - "help": "Date (YYYY-MM-DD), defaults to most recent" + "required": true, + "help": "Slot time YYYY-MM-DD HH:mm:ss" }, { - "name": "period", + "name": "profile-url", "type": "str", - "default": "daily", "required": false, - "help": "Time period: daily, weekly, or monthly", - "choices": [ - "daily", - "weekly", - "monthly" - ] + "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" } ], "columns": [ - "rank", - "id", - "title", - "upvotes", - "authors" + "practice_doctor_id", + "time", + "amount", + "prepaid", + "payment_mode", + "requires_payment", + "confirm_button", + "booking_url" ], "type": "js", - "modulePath": "plugins/hf/top.js", - "sourceFile": "plugins/hf/top.js" + "modulePath": "plugins/practo/book-preview.js", + "sourceFile": "plugins/practo/book-preview.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "hf", - "name": "whoami", - "description": "Show the current logged-in hf account", + "site": "practo", + "name": "booking-link", + "description": "Build a Practo booking URL for a selected slot without confirming it", "access": "read", - "domain": "huggingface.co", + "domain": "www.practo.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "practice_doctor_id", + "type": "str", + "required": true, + "positional": true, + "help": "Practo practice_doctor_id" + }, + { + "name": "time", + "type": "str", + "required": true, + "help": "Slot time YYYY-MM-DD HH:mm:ss" + }, + { + "name": "profile-url", + "type": "str", + "required": false, + "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" + } + ], "columns": [ - "logged_in", - "site", - "username", - "fullname", - "type" + "practice_doctor_id", + "time", + "booking_url" ], "type": "js", - "modulePath": "plugins/hf/auth.js", - "sourceFile": "plugins/hf/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/practo/booking-link.js", + "sourceFile": "plugins/practo/booking-link.js", + "navigateBefore": false }, { - "site": "hft", - "name": "export-postgraduate-courses", - "description": "Export HFT Stuttgart postgraduate programs using the official public programme pages.", - "access": "read", - "example": "webcmd hft export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.hft-stuttgart.de", - "strategy": "public", - "browser": false, + "site": "practo", + "name": "cancel", + "description": "Cancel a logged-in Practo Drive appointment after explicit confirmation", + "access": "write", + "domain": "drive.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "name": "appointment_id", + "type": "str", + "required": true, + "positional": true, + "help": "Appointment id from `practo appointments`" }, { - "name": "count", - "type": "int", + "name": "confirm", + "type": "boolean", + "default": false, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Required. Set --confirm true to cancel the appointment." } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "status", + "appointment_id" ], "type": "js", - "modulePath": "plugins/hft/export-postgraduate-courses.js", - "sourceFile": "plugins/hft/export-postgraduate-courses.js" + "modulePath": "plugins/practo/cancel.js", + "sourceFile": "plugins/practo/cancel.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "homebrew", - "name": "cask", - "description": "Fetch a Homebrew cask's metadata (version, homepage, deprecation, download URL)", + "site": "practo", + "name": "contact", + "description": "Get Practo virtual contact number for a practice_doctor_id", "access": "read", - "domain": "formulae.brew.sh", - "strategy": "public", - "browser": false, + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "token", + "name": "practice_doctor_id", "type": "str", "required": true, "positional": true, - "help": "Cask token (e.g. \"firefox\", \"visual-studio-code\", \"google-chrome\")" + "help": "Practo practice_doctor_id from search results" } ], "columns": [ - "cask", - "tap", + "practice_doctor_id", + "phone", + "raw" + ], + "type": "js", + "modulePath": "plugins/practo/contact.js", + "sourceFile": "plugins/practo/contact.js", + "navigateBefore": false + }, + { + "site": "practo", + "name": "login", + "description": "Open practo login", + "access": "write", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", "name", - "version", - "description", - "homepage", - "deprecated", - "disabled", - "download", - "url" + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/homebrew/cask.js", - "sourceFile": "plugins/homebrew/cask.js" + "modulePath": "plugins/practo/login.js", + "sourceFile": "plugins/practo/login.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "homebrew", - "name": "formula", - "description": "Fetch a Homebrew formula's metadata (version, license, deps, deprecation, source)", + "site": "practo", + "name": "profile", + "description": "Read public details from a Practo doctor profile URL", "access": "read", - "domain": "formulae.brew.sh", - "strategy": "public", - "browser": false, + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "name", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Formula name (e.g. \"wget\", \"gcc@13\", \"imagemagick\")" + "help": "Practo doctor profile URL" } ], "columns": [ - "formula", - "tap", - "version", - "license", - "description", - "homepage", - "dependencies", - "deprecated", - "disabled", - "source", - "url" + "name", + "specialty", + "experience", + "fee", + "profile_url" ], "type": "js", - "modulePath": "plugins/homebrew/formula.js", - "sourceFile": "plugins/homebrew/formula.js" + "modulePath": "plugins/practo/profile.js", + "sourceFile": "plugins/practo/profile.js", + "navigateBefore": false }, { - "site": "homebrew", - "name": "popular", - "description": "List most-installed Homebrew formulae or casks (Homebrew's analytics ranking)", + "site": "practo", + "name": "search", + "description": "Search Practo doctors by specialty, city, and optional locality", "access": "read", - "domain": "formulae.brew.sh", - "strategy": "public", - "browser": false, + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "type", + "name": "specialty", "type": "str", - "default": "formula", + "required": true, + "positional": true, + "help": "Doctor specialty, e.g. orthopedist or dermatologist" + }, + { + "name": "city", + "type": "str", + "default": "bangalore", "required": false, - "help": "Package type (formula / cask)" + "help": "City, e.g. bangalore" }, { - "name": "window", + "name": "locality", "type": "str", - "default": "30d", "required": false, - "help": "Time window (30d / 90d / 365d)" + "help": "Optional locality, e.g. indiranagar" }, { "name": "limit", "type": "int", - "default": 30, + "default": 10, "required": false, - "help": "Max rows (1-500)" + "help": "Max doctors to return (1-25)" } ], "columns": [ "rank", - "token", - "type", - "installs", - "percent", - "window", - "url" + "practice_doctor_id", + "doctor_id", + "practice_id", + "name", + "specialty", + "experience_years", + "locality", + "clinic", + "fee", + "next_available", + "profile_url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/homebrew/popular.js", - "sourceFile": "plugins/homebrew/popular.js" + "modulePath": "plugins/practo/search.js", + "sourceFile": "plugins/practo/search.js", + "navigateBefore": false }, { - "site": "iit", - "name": "export-postgraduate-courses", - "description": "Export Illinois Tech postgraduate programs using official public sources.", + "site": "practo", + "name": "slots", + "description": "List available Practo appointment slots for a practice_doctor_id", "access": "read", - "example": "webcmd iit export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.iit.edu", - "strategy": "public", - "browser": false, + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "name": "practice_doctor_id", + "type": "str", + "required": true, + "positional": true, + "help": "Practo practice_doctor_id from search results" }, { - "name": "count", + "name": "limit", "type": "int", + "default": 20, "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Max slots to return (1-25)" } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "practice_doctor_id", + "time", + "available", + "amount", + "prepaid", + "appointment_token" ], "type": "js", - "modulePath": "plugins/iit/export-postgraduate-courses.js", - "sourceFile": "plugins/iit/export-postgraduate-courses.js" + "modulePath": "plugins/practo/slots.js", + "sourceFile": "plugins/practo/slots.js", + "navigateBefore": false }, { - "site": "imdb", - "name": "person", - "description": "Get actor or director info", + "site": "practo", + "name": "whoami", + "aliases": [ + "auth-status" + ], + "description": "Show the current logged-in practo account", + "access": "read", + "domain": "www.practo.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name" + ], + "type": "js", + "modulePath": "plugins/practo/login.js", + "sourceFile": "plugins/practo/login.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "producthunt", + "name": "browse", + "description": "Best products in a Product Hunt category", "access": "read", - "domain": "www.imdb.com", - "strategy": "public", + "domain": "www.producthunt.com", + "strategy": "intercept", "browser": true, "args": [ { - "name": "id", - "type": "str", + "name": "category", + "type": "string", "required": true, "positional": true, - "help": "IMDb person ID (nm0634240) or URL" + "help": "Category slug, e.g. vibe-coding, ai-agents, developer-tools" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Max filmography entries" + "help": "Number of results (max 50)" } ], "columns": [ - "field", - "value" + "rank", + "name", + "tagline", + "reviews", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/imdb/person.js", - "sourceFile": "plugins/imdb/person.js" + "modulePath": "plugins/producthunt/browse.js", + "sourceFile": "plugins/producthunt/browse.js", + "navigateBefore": true }, { - "site": "imdb", - "name": "reviews", - "description": "Get user reviews for a movie or TV show", + "site": "producthunt", + "name": "hot", + "description": "Today's top Product Hunt launches with vote counts", "access": "read", - "domain": "www.imdb.com", - "strategy": "public", + "domain": "www.producthunt.com", + "strategy": "intercept", "browser": true, "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "IMDb title ID (tt1375666) or URL" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of reviews" + "help": "Number of results (max 50)" } ], "columns": [ "rank", - "title", - "rating", - "author", - "date", - "text" + "name", + "votes", + "url" ], "type": "js", - "modulePath": "plugins/imdb/reviews.js", - "sourceFile": "plugins/imdb/reviews.js" + "modulePath": "plugins/producthunt/hot.js", + "sourceFile": "plugins/producthunt/hot.js", + "navigateBefore": true }, { - "site": "imdb", - "name": "search", - "description": "Search IMDb for movies, TV shows, and people", + "site": "producthunt", + "name": "posts", + "description": "Latest Product Hunt launches (optional category filter)", "access": "read", - "domain": "www.imdb.com", + "domain": "www.producthunt.com", "strategy": "public", - "browser": true, + "browser": false, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of results" + "help": "Number of results (max 50)" + }, + { + "name": "category", + "type": "string", + "default": "", + "required": false, + "help": "Category filter: ai-agents, ai-coding-agents, ai-code-editors, ai-chatbots, ai-workflow-automation, vibe-coding, developer-tools, productivity, design-creative, marketing-sales, no-code-platforms, llms, finance, social-community, engineering-development" } ], "columns": [ "rank", - "id", - "title", - "year", - "type", + "name", + "tagline", + "author", + "date", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/imdb/search.js", - "sourceFile": "plugins/imdb/search.js" + "modulePath": "plugins/producthunt/posts.js", + "sourceFile": "plugins/producthunt/posts.js" }, { - "site": "imdb", - "name": "title", - "description": "Get movie or TV show details", + "site": "producthunt", + "name": "today", + "description": "Today's Product Hunt launches (most recent day in feed)", "access": "read", - "domain": "www.imdb.com", + "domain": "www.producthunt.com", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "IMDb title ID (tt1375666) or URL" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results" } ], "columns": [ - "field", - "value" + "rank", + "name", + "tagline", + "author", + "url" ], "type": "js", - "modulePath": "plugins/imdb/title.js", - "sourceFile": "plugins/imdb/title.js" + "modulePath": "plugins/producthunt/today.js", + "sourceFile": "plugins/producthunt/today.js" }, { - "site": "imdb", - "name": "top", - "description": "IMDb Top 250 Movies", + "site": "pubmed", + "name": "article", + "aliases": [ + "paper", + "read" + ], + "description": "Get detailed information for a PubMed article by PMID", "access": "read", - "domain": "www.imdb.com", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", - "browser": true, + "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "pmid", + "type": "str", + "required": true, + "positional": true, + "help": "PubMed ID, e.g. 37780221" + }, + { + "name": "full-abstract", + "type": "boolean", + "default": false, "required": false, - "help": "Number of results" + "help": "Do not truncate the abstract in table output" } ], "columns": [ - "rank", + "pmid", "title", - "rating", - "votes", - "genre", + "authors", + "journal", + "year", + "date", + "article_type", + "language", + "doi", + "pmc", + "affiliations", + "grants", + "mesh_terms", + "keywords", + "abstract", "url" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/imdb/top.js", - "sourceFile": "plugins/imdb/top.js" + "modulePath": "plugins/pubmed/article.js", + "sourceFile": "plugins/pubmed/article.js" }, { - "site": "imdb", - "name": "trending", - "description": "IMDb Most Popular Movies", + "site": "pubmed", + "name": "author", + "description": "Search PubMed articles by author name and optional affiliation", "access": "read", - "domain": "www.imdb.com", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", - "browser": true, + "browser": false, "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Author name, e.g. \"Smith J\"" + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of results" + "help": "Max results (1-100)" + }, + { + "name": "affiliation", + "type": "str", + "required": false, + "help": "Filter by author affiliation" + }, + { + "name": "position", + "type": "str", + "default": "any", + "required": false, + "help": "Author position: any, first, or last", + "choices": [ + "any", + "first", + "last" + ] + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "sort", + "type": "str", + "default": "date", + "required": false, + "help": "Sort by date or relevance", + "choices": [ + "date", + "relevance" + ] } ], "columns": [ "rank", - "title", - "rating", - "genre", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", "url" ], "type": "js", - "modulePath": "plugins/imdb/trending.js", - "sourceFile": "plugins/imdb/trending.js" + "modulePath": "plugins/pubmed/author.js", + "sourceFile": "plugins/pubmed/author.js" }, { - "site": "indeed", - "name": "job", - "aliases": [ - "detail", - "view" - ], - "description": "Read the full Indeed job posting by jk (job key)", + "site": "pubmed", + "name": "citations", + "description": "Get PubMed citation relationships for an article", "access": "read", - "domain": "www.indeed.com", - "strategy": "cookie", - "browser": true, + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, "args": [ { - "name": "id", + "name": "pmid", "type": "str", "required": true, "positional": true, - "help": "Job key (16-char hex from `indeed search`, e.g. \"dccc07ac5a6a3683\")" + "help": "PubMed ID, e.g. 37780221" + }, + { + "name": "direction", + "type": "str", + "default": "citedby", + "required": false, + "help": "citedby or references", + "choices": [ + "citedby", + "references" + ] + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" } ], "columns": [ - "id", + "rank", + "pmid", "title", - "company", - "location", - "salary", - "job_type", - "description", + "authors", + "journal", + "year", + "article_type", + "doi", "url" ], "type": "js", - "modulePath": "plugins/indeed/job.js", - "sourceFile": "plugins/indeed/job.js", - "navigateBefore": false + "modulePath": "plugins/pubmed/citations.js", + "sourceFile": "plugins/pubmed/citations.js" }, { - "site": "indeed", - "name": "search", - "description": "Indeed keyword job search (rendered DOM via browser session, US site)", + "site": "pubmed", + "name": "clinical-trial", + "description": "Search PubMed clinical trials with a trial-study preset", "access": "read", - "domain": "www.indeed.com", - "strategy": "cookie", - "browser": true, + "domain": "pubmed.ncbi.nlm.nih.gov", + "strategy": "public", + "browser": false, "args": [ { "name": "query", "type": "str", "required": true, "positional": true, - "help": "Job keyword (title / skill / company)" + "help": "Clinical topic query, e.g. \"breast cancer\"" }, { - "name": "location", - "type": "string", - "default": "", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Location filter (e.g. \"remote\", \"New York, NY\", \"San Francisco\")" + "help": "Max results (1-100)" }, { - "name": "fromage", - "type": "string", - "default": "", + "name": "year-from", + "type": "int", "required": false, - "help": "Recency filter, days back: 1 / 3 / 7 / 14" + "help": "Filter publication year from" }, { - "name": "sort", - "type": "string", - "default": "relevance", + "name": "year-to", + "type": "int", "required": false, - "help": "Sort order: relevance | date" + "help": "Filter publication year to" }, { - "name": "start", - "type": "int", - "default": 0, + "name": "free-full-text", + "type": "boolean", + "default": false, "required": false, - "help": "Pagination offset (multiple of 10, 0-based)" + "help": "Only include free full text articles" }, { - "name": "limit", - "type": "int", - "default": 15, + "name": "sort", + "type": "str", + "default": "date", "required": false, - "help": "Max rows to return (1-25, capped at one page)" + "help": "Sort by date or relevance", + "choices": [ + "date", + "relevance" + ] } ], "columns": [ "rank", - "id", + "pmid", "title", - "company", - "location", - "salary", - "tags", + "authors", + "journal", + "year", + "article_type", + "doi", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/indeed/search.js", - "sourceFile": "plugins/indeed/search.js", - "navigateBefore": false + "modulePath": "plugins/pubmed/clinical-trial.js", + "sourceFile": "plugins/pubmed/clinical-trial.js" }, { - "site": "jhu", - "name": "export-postgraduate-courses", - "description": "Export Johns Hopkins University postgraduate programs using the official Academic Catalogue.", + "site": "pubmed", + "name": "journal", + "description": "Search PubMed articles by journal name", "access": "read", - "example": "webcmd jhu export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "e-catalogue.jhu.edu", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "degree-level", - "type": "string", - "default": "all", + "name": "journal", + "type": "str", + "required": true, + "positional": true, + "help": "Journal name, e.g. \"Nature\" or \"The Lancet\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" + "help": "Max results (1-100)" }, { - "name": "count", + "name": "year-from", "type": "int", "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" - } - ], - "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" - ], - "type": "js", - "modulePath": "plugins/jhu/export-postgraduate-courses.js", - "sourceFile": "plugins/jhu/export-postgraduate-courses.js" - }, - { - "site": "jira", - "name": "attachments", - "description": "Jira issue attachment metadata", - "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ + "help": "Filter publication year from" + }, { - "name": "key", + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "sort", "type": "str", - "required": true, - "positional": true, - "help": "Jira issue key, e.g. PROJ-123" + "default": "relevance", + "required": false, + "help": "Sort by relevance or date", + "choices": [ + "relevance", + "date" + ] } ], "columns": [ - "id", - "filename", - "mimeType", - "size", + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", "url" ], "type": "js", - "modulePath": "plugins/jira/attachments.js", - "sourceFile": "plugins/jira/attachments.js" + "modulePath": "plugins/pubmed/journal.js", + "sourceFile": "plugins/pubmed/journal.js" }, { - "site": "jira", - "name": "comments", - "description": "Jira issue comments as Markdown", + "site": "pubmed", + "name": "mesh", + "description": "Search PubMed articles by MeSH term", "access": "read", - "domain": "atlassian.net", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "key", + "name": "term", "type": "str", "required": true, "positional": true, - "help": "Jira issue key, e.g. PROJ-123" + "help": "MeSH term, e.g. \"Neoplasms\" or \"Machine Learning\"" }, { "name": "limit", "type": "int", - "default": 50, + "default": 20, "required": false, - "help": "Max comments to return (1-100)" + "help": "Max results (1-100)" + }, + { + "name": "major", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include articles where this is a major MeSH topic" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance or date", + "choices": [ + "relevance", + "date" + ] } ], "columns": [ - "id", - "author", - "created", - "updated", - "markdown" + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" ], "type": "js", - "modulePath": "plugins/jira/comments.js", - "sourceFile": "plugins/jira/comments.js" + "modulePath": "plugins/pubmed/mesh.js", + "sourceFile": "plugins/pubmed/mesh.js" }, { - "site": "jira", - "name": "issue", - "description": "Jira issue detail normalized for agents (description, comments, attachments, links)", + "site": "pubmed", + "name": "related", + "description": "Find articles related to a PubMed article", "access": "read", - "domain": "atlassian.net", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "key", + "name": "pmid", "type": "str", "required": true, "positional": true, - "help": "Jira issue key, e.g. PROJ-123" + "help": "PubMed ID, e.g. 37780221" }, { - "name": "comments-limit", + "name": "limit", "type": "int", - "default": 100, + "default": 20, "required": false, - "help": "Max comments to include (1-100)" + "help": "Max results (1-100)" + }, + { + "name": "score", + "type": "boolean", + "default": false, + "required": false, + "help": "Show similarity scores when available" } ], "columns": [ - "key", - "summary", - "issueType", - "status", - "priority", - "assignee", - "updated", + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "score", + "doi", "url" ], "type": "js", - "modulePath": "plugins/jira/issue.js", - "sourceFile": "plugins/jira/issue.js" + "modulePath": "plugins/pubmed/related.js", + "sourceFile": "plugins/pubmed/related.js" }, { - "site": "jira", - "name": "links", - "description": "Jira issue links", + "site": "pubmed", + "name": "review", + "description": "Search PubMed review articles with a review preset", "access": "read", - "domain": "atlassian.net", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "key", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Jira issue key, e.g. PROJ-123" + "help": "Review topic query, e.g. \"immunotherapy\"" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (1-100)" + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "has-abstract", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include articles with abstracts" + }, + { + "name": "sort", + "type": "str", + "default": "date", + "required": false, + "help": "Sort by date or relevance", + "choices": [ + "date", + "relevance" + ] } ], "columns": [ - "key", - "type", - "direction" + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", + "url" ], "type": "js", - "modulePath": "plugins/jira/links.js", - "sourceFile": "plugins/jira/links.js" + "modulePath": "plugins/pubmed/review.js", + "sourceFile": "plugins/pubmed/review.js" }, { - "site": "jira", + "site": "pubmed", "name": "search", - "description": "Search Jira issues with JQL", + "description": "Search PubMed articles with advanced filters", "access": "read", - "domain": "atlassian.net", + "domain": "pubmed.ncbi.nlm.nih.gov", "strategy": "public", "browser": false, "args": [ { - "name": "jql", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "JQL query, e.g. \"project = PROJ order by updated desc\"" + "help": "Search query, e.g. \"machine learning cancer\"" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max issues to return (1-100)" + "help": "Max results (1-100)" + }, + { + "name": "author", + "type": "str", + "required": false, + "help": "Filter by author name" + }, + { + "name": "journal", + "type": "str", + "required": false, + "help": "Filter by journal name" + }, + { + "name": "year-from", + "type": "int", + "required": false, + "help": "Filter publication year from" + }, + { + "name": "year-to", + "type": "int", + "required": false, + "help": "Filter publication year to" + }, + { + "name": "article-type", + "type": "str", + "required": false, + "help": "Filter by publication type, e.g. Review or Clinical Trial" + }, + { + "name": "has-abstract", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include articles with abstracts" + }, + { + "name": "free-full-text", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include free full text articles" + }, + { + "name": "humans-only", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include human studies" + }, + { + "name": "english-only", + "type": "boolean", + "default": false, + "required": false, + "help": "Only include English articles" + }, + { + "name": "sort", + "type": "str", + "default": "relevance", + "required": false, + "help": "Sort by relevance, date, author, or journal", + "choices": [ + "relevance", + "date", + "author", + "journal" + ] } ], "columns": [ - "key", - "summary", - "issueType", - "status", - "priority", - "assignee", - "updated", + "rank", + "pmid", + "title", + "authors", + "journal", + "year", + "article_type", + "doi", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/jira/search.js", - "sourceFile": "plugins/jira/search.js" + "modulePath": "plugins/pubmed/search.js", + "sourceFile": "plugins/pubmed/search.js" }, { - "site": "lesswrong", - "name": "comments", - "description": "Top comments on a post", + "site": "pypi", + "name": "downloads", + "description": "PyPI download stats for a package (recent totals or full daily history)", "access": "read", - "domain": "www.lesswrong.com", + "domain": "pypistats.org", "strategy": "public", "browser": false, "args": [ { - "name": "url-or-id", - "type": "string", + "name": "name", + "type": "str", "required": true, "positional": true, - "help": "Post URL or LessWrong post ID" + "help": "PyPI package name (e.g. \"requests\", \"pandas\")" }, { - "name": "limit", - "type": "int", - "default": 5, + "name": "period", + "type": "str", + "default": "recent", "required": false, - "help": "Number of comments" + "help": "recent (default — 1 row, last day/week/month) or overall (1 row per day)" } ], "columns": [ "rank", - "score", - "author", - "text" + "package", + "period", + "date", + "downloads" ], "type": "js", - "modulePath": "plugins/lesswrong/comments.js", - "sourceFile": "plugins/lesswrong/comments.js" + "modulePath": "plugins/pypi/downloads.js", + "sourceFile": "plugins/pypi/downloads.js" }, { - "site": "lesswrong", - "name": "curated", - "description": "Curated editor's picks", + "site": "pypi", + "name": "package", + "description": "Single PyPI package metadata (latest version, license, homepage, classifiers)", "access": "read", - "domain": "www.lesswrong.com", + "domain": "pypi.org", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "PyPI package name (e.g. \"requests\", \"pandas\")" } ], "columns": [ - "rank", - "title", + "name", + "latestVersion", + "summary", "author", - "karma", - "comments", + "license", + "homepage", + "repository", + "requiresPython", + "keywords", + "releases", + "firstReleased", + "lastReleased", "url" ], "type": "js", - "modulePath": "plugins/lesswrong/curated.js", - "sourceFile": "plugins/lesswrong/curated.js" + "modulePath": "plugins/pypi/package.js", + "sourceFile": "plugins/pypi/package.js" }, { - "site": "lesswrong", - "name": "frontpage", - "description": "Algorithmic frontpage", + "site": "pypi", + "name": "releases", + "description": "List recent public PyPI package releases", "access": "read", - "domain": "www.lesswrong.com", + "domain": "pypi.org", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/lesswrong/frontpage.js", - "sourceFile": "plugins/lesswrong/frontpage.js" - }, - { - "site": "lesswrong", - "name": "new", - "description": "Latest posts", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ + "name": "name", + "type": "string", + "required": true, + "positional": true, + "help": "Python package name, for example django" + }, { "name": "limit", "type": "int", "default": 10, "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/lesswrong/new.js", - "sourceFile": "plugins/lesswrong/new.js" - }, - { - "site": "lesswrong", - "name": "read", - "description": "Read full post by URL or ID", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "url-or-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post URL or LessWrong post ID" + "help": "Maximum releases to return (1-50)" } ], "columns": [ - "title", - "author", - "karma", - "comments", - "tags", - "content", + "version", + "uploadedAt", + "fileCount", + "pythonVersions", + "yanked", "url" ], "type": "js", - "modulePath": "plugins/lesswrong/read.js", - "sourceFile": "plugins/lesswrong/read.js" + "modulePath": "plugins/pypi/releases.js", + "sourceFile": "plugins/pypi/releases.js" }, { - "site": "lesswrong", - "name": "sequences", - "description": "List post collections", + "site": "qoder", + "name": "account", + "description": "Click the account button (username) in the Qoder sidebar and return the visible account dropdown items.", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 10, + "name": "username", + "type": "str", "required": false, - "help": "Number of results" + "help": "Username text shown in the sidebar (default: tries common short labels)" } ], "columns": [ - "rank", - "title", - "author" + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/lesswrong/sequences.js", - "sourceFile": "plugins/lesswrong/sequences.js" + "modulePath": "plugins/qoder/ui.js", + "sourceFile": "plugins/qoder/ui.js", + "navigateBefore": true }, { - "site": "lesswrong", - "name": "shortform", - "description": "Quick takes / shortform posts", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], + "site": "qoder", + "name": "add-workspace", + "description": "Click \"Add Workspace\" — opens the folder picker. Note: this opens a system file-picker dialog that Qoder controls; the actual folder selection must be done in the UI by the user.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" + "Status" ], "type": "js", - "modulePath": "plugins/lesswrong/shortform.js", - "sourceFile": "plugins/lesswrong/shortform.js" + "modulePath": "plugins/qoder/ui.js", + "sourceFile": "plugins/qoder/ui.js", + "navigateBefore": true }, { - "site": "lesswrong", - "name": "tag", - "description": "Posts by tag", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "site": "qoder", + "name": "ask", + "description": "Send a prompt to Qoder and wait up to --timeout seconds for the reply (best-effort: polls for the chat turn count to grow + stabilize).", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "tag", - "type": "string", + "name": "text", + "type": "str", "required": true, "positional": true, - "help": "Tag slug or name" + "help": "Prompt text" }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 10, + "default": 120, "required": false, - "help": "Number of results" + "help": "Max seconds to wait" } ], "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" + "Role", + "Text", + "WaitedSeconds" ], "type": "js", - "modulePath": "plugins/lesswrong/tag.js", - "sourceFile": "plugins/lesswrong/tag.js" + "modulePath": "plugins/qoder/quest.js", + "sourceFile": "plugins/qoder/quest.js", + "navigateBefore": true }, { - "site": "lesswrong", - "name": "tags", - "description": "List popular tags", + "site": "qoder", + "name": "credits", + "description": "Click \"Credits Usage\" and return the credits-usage display text.", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "rank", - "name", - "posts" + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/lesswrong/tags.js", - "sourceFile": "plugins/lesswrong/tags.js" + "modulePath": "plugins/qoder/ui.js", + "sourceFile": "plugins/qoder/ui.js", + "navigateBefore": true }, { - "site": "lesswrong", - "name": "top", - "description": "Top all-time", + "site": "qoder", + "name": "history", + "description": "List Quests visible in the Qoder sidebar. Returns title + visible metadata.", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 50, "required": false, - "help": "Number of results" + "help": "" } ], "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" + "Index", + "Title" ], "type": "js", - "modulePath": "plugins/lesswrong/top.js", - "sourceFile": "plugins/lesswrong/top.js" + "modulePath": "plugins/qoder/history.js", + "sourceFile": "plugins/qoder/history.js", + "navigateBefore": true }, { - "site": "lesswrong", - "name": "top-month", - "description": "Top this month", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } + "site": "qoder", + "name": "knowledge", + "description": "Open the Knowledge view (Qoder's personal/team knowledge base).", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" ], + "type": "js", + "modulePath": "plugins/qoder/ui.js", + "sourceFile": "plugins/qoder/ui.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "marketplace", + "description": "Open the Qoder Marketplace.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" + "Status" ], "type": "js", - "modulePath": "plugins/lesswrong/top-month.js", - "sourceFile": "plugins/lesswrong/top-month.js" + "modulePath": "plugins/qoder/ui.js", + "sourceFile": "plugins/qoder/ui.js", + "navigateBefore": true }, { - "site": "lesswrong", - "name": "top-week", - "description": "Top this week", + "site": "qoder", + "name": "more-actions", + "description": "Click the \"More Actions\" button and list its menu items.", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" + "Index", + "Item" ], "type": "js", - "modulePath": "plugins/lesswrong/top-week.js", - "sourceFile": "plugins/lesswrong/top-week.js" + "modulePath": "plugins/qoder/ui.js", + "sourceFile": "plugins/qoder/ui.js", + "navigateBefore": true }, { - "site": "lesswrong", - "name": "top-year", - "description": "Top this year", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } + "site": "qoder", + "name": "new", + "description": "Start a new Qoder Quest (conversation). Clicks the \"New Quest\" button in the sidebar (or its ⌘N variant).", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" ], + "type": "js", + "modulePath": "plugins/qoder/quest.js", + "sourceFile": "plugins/qoder/quest.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "open-editor", + "description": "Click \"Open Editor\" — opens the current draft in a full editor pane.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" + "Status" ], "type": "js", - "modulePath": "plugins/lesswrong/top-year.js", - "sourceFile": "plugins/lesswrong/top-year.js" + "modulePath": "plugins/qoder/composer.js", + "sourceFile": "plugins/qoder/composer.js", + "navigateBefore": true }, { - "site": "lesswrong", - "name": "user", - "description": "User profile", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "LessWrong username or slug" - } + "site": "qoder", + "name": "open-panel", + "description": "Open / close the Qoder bottom panel (Output / Terminal / Debug Console). ⌥⌘B equivalent.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status" ], + "type": "js", + "modulePath": "plugins/qoder/ui.js", + "sourceFile": "plugins/qoder/ui.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "prompt-enhance", + "description": "Click \"Prompt Enhance\" — Qoder rewrites the current composer draft for better LLM consumption.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "field", - "value" + "Status" ], "type": "js", - "modulePath": "plugins/lesswrong/user.js", - "sourceFile": "plugins/lesswrong/user.js" + "modulePath": "plugins/qoder/composer.js", + "sourceFile": "plugins/qoder/composer.js", + "navigateBefore": true }, { - "site": "lesswrong", - "name": "user-posts", - "description": "List a user's posts", + "site": "qoder", + "name": "read", + "description": "Read messages in the current Qoder Quest. Returns role + text for each visible turn.", "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "LessWrong username or slug" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 30, "required": false, - "help": "Number of results" + "help": "" } ], "columns": [ - "rank", - "title", - "karma", - "comments", - "date", - "url" + "Index", + "Role", + "Text" ], "type": "js", - "modulePath": "plugins/lesswrong/user-posts.js", - "sourceFile": "plugins/lesswrong/user-posts.js" + "modulePath": "plugins/qoder/read.js", + "sourceFile": "plugins/qoder/read.js", + "navigateBefore": true }, { - "site": "lichess", - "name": "top", - "description": "Top-N Lichess leaderboard for a perf type (bullet/blitz/rapid/classical/...)", - "access": "read", - "domain": "lichess.org", - "strategy": "public", - "browser": false, + "site": "qoder", + "name": "search", + "description": "Open Qoder Search palette (⌘P), type a query, return matched options.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "perf", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Perf type (bullet, blitz, rapid, classical, ultraBullet, chess960, ...)" + "help": "Search text" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Top-N rows (1-200)" + "help": "" } ], "columns": [ - "rank", - "username", - "id", - "title", - "rating", - "progress", - "patron", - "url" + "Index", + "Item" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/lichess/top.js", - "sourceFile": "plugins/lichess/top.js" + "modulePath": "plugins/qoder/ui.js", + "sourceFile": "plugins/qoder/ui.js", + "navigateBefore": true }, { - "site": "lichess", - "name": "user", - "description": "Fetch a Lichess player profile by username (rating, perfs, counts)", - "access": "read", - "domain": "lichess.org", - "strategy": "public", - "browser": false, + "site": "qoder", + "name": "send", + "description": "Type text into the Qoder composer and click \"Send message\" (fire-and-forget).", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "username", + "name": "text", "type": "str", "required": true, "positional": true, - "help": "Lichess username (case-insensitive)" + "help": "Text to send" } ], "columns": [ - "username", - "id", - "title", - "patron", - "online", - "tosViolation", - "createdAt", - "seenAt", - "gamesAll", - "gamesWin", - "gamesLoss", - "gamesDraw", - "topPerfName", - "topPerfRating", - "topPerfGames", - "fideRating", - "country", - "bio", - "url" + "Status", + "Length" ], "type": "js", - "modulePath": "plugins/lichess/user.js", - "sourceFile": "plugins/lichess/user.js" + "modulePath": "plugins/qoder/quest.js", + "sourceFile": "plugins/qoder/quest.js", + "navigateBefore": true }, { - "site": "linkedin", - "name": "company", - "description": "Read a LinkedIn company page: industry, size, HQ, founded, website, followers, and about text", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", + "site": "qoder", + "name": "settings", + "description": "Click the Settings button in the Qoder sidebar.", + "access": "write", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [ - { - "name": "company", - "type": "string", - "required": true, - "positional": true, - "help": "Company universal name, /company/ path, or full URL" - } + "args": [], + "columns": [ + "Status" ], + "type": "js", + "modulePath": "plugins/qoder/ui.js", + "sourceFile": "plugins/qoder/ui.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "sidebar-toggle", + "description": "Collapse / Expand the Qoder Quest List sidebar (⌘B).", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], "columns": [ - "name", - "industry", - "size", - "headquarters", - "founded", - "website", - "specialties", - "followers", - "about", - "url" + "Status" ], "type": "js", - "modulePath": "plugins/linkedin/company.js", - "sourceFile": "plugins/linkedin/company.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/qoder/ui.js", + "sourceFile": "plugins/qoder/ui.js", + "navigateBefore": true }, { - "site": "linkedin", - "name": "connect", - "description": "Fail-closed LinkedIn connection request sender that verifies the exact profile before optionally sending a note", + "site": "qoder", + "name": "status", + "description": "Check Qoder CDP connection and report the current renderer URL + title.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Status", + "Url", + "Title" + ], + "type": "js", + "modulePath": "plugins/qoder/status.js", + "sourceFile": "plugins/qoder/status.js", + "navigateBefore": true + }, + { + "site": "qoder", + "name": "view-all", + "description": "Click \"View all\" to show all Quests.", "access": "write", - "domain": "www.linkedin.com", + "domain": "localhost", "strategy": "ui", "browser": true, + "args": [], + "columns": [ + "Status" + ], + "type": "js", + "modulePath": "plugins/qoder/ui.js", + "sourceFile": "plugins/qoder/ui.js", + "navigateBefore": true + }, + { + "site": "reddit", + "name": "comment", + "description": "Post a comment on a Reddit post", + "access": "write", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "profile-url", + "name": "post-id", "type": "string", "required": true, "positional": true, - "help": "Exact LinkedIn profile URL to open and verify" + "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" }, { - "name": "expected-name", + "name": "text", "type": "string", "required": true, - "help": "Expected visible profile name" - }, - { - "name": "note", - "type": "string", - "default": "", - "required": false, - "help": "Optional connection note, max 300 chars" - }, - { - "name": "send", - "type": "bool", - "default": false, - "required": false, - "help": "Actually click Send. Default is dry-run verification only." + "positional": true, + "help": "Comment text" } ], "columns": [ "status", - "recipient", - "reason", - "profile_url", - "note_chars", - "connectable", - "delivery_verified", - "matched_invitation_name", - "matched_invitation_url", - "actualValue", - "blockReason", - "expectedValue", - "observedUrl", - "safety" + "message" ], "type": "js", - "modulePath": "plugins/linkedin/connect.js", - "sourceFile": "plugins/linkedin/connect.js", - "navigateBefore": true + "modulePath": "plugins/reddit/comment.js", + "sourceFile": "plugins/reddit/comment.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "connections", - "description": "List your LinkedIn first-degree connections with names, headlines, and profile URLs", + "site": "reddit", + "name": "frontpage", + "description": "Reddit Frontpage / r/all", "access": "read", - "domain": "www.linkedin.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 20, + "default": 15, "required": false, - "help": "Number of connections to return (max 500)" + "help": "" } ], "columns": [ - "rank", - "name", - "occupation", - "public_id", - "connected_at", - "url" + "title", + "subreddit", + "author", + "upvotes", + "comments", + "url", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "plugins/linkedin/connections.js", - "sourceFile": "plugins/linkedin/connections.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/reddit/frontpage.js", + "sourceFile": "plugins/reddit/frontpage.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "inbox", - "description": "List LinkedIn messaging inbox conversations and unread messages", + "site": "reddit", + "name": "home", + "description": "Reddit personalized home feed (Best, requires login)", "access": "read", - "domain": "www.linkedin.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 40, - "required": false, - "help": "Maximum conversations to return (1-100)" - }, - { - "name": "unread-only", - "type": "bool", - "default": false, + "default": 25, "required": false, - "help": "Return only conversations with unread messages" + "help": "Number of posts (1–100)" } ], "columns": [ "rank", - "thread_url", - "thread_id", - "person_name", - "last_message_preview", - "unread", - "counterparty_type", - "category", - "timestamp" - ], - "type": "js", - "modulePath": "plugins/linkedin/inbox.js", - "sourceFile": "plugins/linkedin/inbox.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "job-detail", - "description": "Read one LinkedIn job page with description, apply URL, workplace type, applicants, and company metadata", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "job-url", - "type": "string", - "required": true, - "positional": true, - "help": "Exact LinkedIn job URL, e.g. https://www.linkedin.com/jobs/view/123/" - } - ], - "columns": [ "title", - "company", - "location", - "workplace_type", - "job_type", - "applicants", - "listed", - "apply_url", - "company_url", + "subreddit", + "score", + "comments", + "postId", + "author", "url", - "description" + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "plugins/linkedin/job-detail.js", - "sourceFile": "plugins/linkedin/job-detail.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/reddit/home.js", + "sourceFile": "plugins/reddit/home.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "jobs-preferences", - "description": "Read visible LinkedIn Jobs preferences and alert settings without changing them", + "site": "reddit", + "name": "hot", + "description": "Reddit hot posts", "access": "read", - "domain": "www.linkedin.com", + "domain": "www.reddit.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "subreddit", + "type": "str", + "default": "", + "required": false, + "help": "Subreddit name (e.g. programming). Empty for frontpage" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts" + } + ], "columns": [ - "open_to_work", - "job_titles", - "locations", - "job_alerts", - "preferences_url", - "alerts_url", - "raw_preferences" + "rank", + "title", + "subreddit", + "score", + "comments", + "postId", + "author", + "url", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "plugins/linkedin/jobs-preferences.js", - "sourceFile": "plugins/linkedin/jobs-preferences.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/reddit/hot.js", + "sourceFile": "plugins/reddit/hot.js", + "navigateBefore": "https://www.reddit.com" }, { - "site": "linkedin", + "site": "reddit", "name": "login", - "description": "Open linkedin login", + "description": "Open reddit login", "access": "write", - "domain": "www.linkedin.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [], @@ -11463,2696 +20073,2910 @@ "status", "logged_in", "site", - "public_id", - "plain_id", - "name", + "username", + "id", "action", "verify_command" ], "type": "js", - "modulePath": "plugins/linkedin/auth.js", - "sourceFile": "plugins/linkedin/auth.js", + "modulePath": "plugins/reddit/auth.js", + "sourceFile": "plugins/reddit/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "linkedin", - "name": "people-search", - "description": "Search standard LinkedIn (not Sales Navigator) for people by keyword. Each invocation consumes against LinkedIn's monthly Commercial Use Limit on people search; throttle accordingly.", + "site": "reddit", + "name": "popular", + "description": "Reddit Popular posts (/r/popular)", "access": "read", - "domain": "www.linkedin.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ - { - "name": "keywords", - "type": "string", - "required": true, - "positional": true, - "help": "People search keywords, e.g. \"site reliability engineer berlin\"" - }, { "name": "limit", "type": "int", - "default": 5, + "default": 20, "required": false, - "help": "Maximum people to return (1-10); each query counts toward LinkedIn's monthly CUL" + "help": "" } ], "columns": [ "rank", - "name", - "headline", - "location", - "profile_url" - ], - "tags": [ - "search" + "id", + "title", + "subreddit", + "score", + "comments", + "author", + "url", + "created_utc", + "selftext", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "plugins/linkedin/people-search.js", - "sourceFile": "plugins/linkedin/people-search.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/reddit/popular.js", + "sourceFile": "plugins/reddit/popular.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "post-analytics", - "description": "Summarize raw visible LinkedIn post counters without custom scoring or classification", + "site": "reddit", + "name": "read", + "description": "Read a Reddit post and its comments", "access": "read", - "domain": "www.linkedin.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "profile-url", - "type": "string", + "name": "post-id", + "type": "str", + "required": true, + "positional": true, + "help": "Post ID (e.g. 1abc123) or full URL" + }, + { + "name": "sort", + "type": "str", + "default": "best", "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "Comment sort: best, top, new, controversial, old, qa" }, { "name": "limit", "type": "int", - "default": 30, + "default": 25, "required": false, - "help": "Maximum posts to summarize (1-100)" + "help": "Number of top-level comments" + }, + { + "name": "depth", + "type": "int", + "default": 2, + "required": false, + "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + }, + { + "name": "replies", + "type": "int", + "default": 5, + "required": false, + "help": "Max replies shown per comment at each level (sorted by score)" + }, + { + "name": "max-length", + "type": "int", + "default": 2000, + "required": false, + "help": "Max characters per comment body (min 100)" + }, + { + "name": "expand-more", + "type": "bool", + "default": false, + "required": false, + "help": "Follow Reddit \"more comments\" stubs by calling /api/morechildren.json" + }, + { + "name": "expand-rounds", + "type": "int", + "default": 2, + "required": false, + "help": "Max expansion passes when --expand-more is on (1–5; each round can fan out new \"more\" stubs)" } ], "columns": [ - "posts_analyzed", - "total_reactions", - "total_comments", - "total_reposts", - "total_impressions", - "posts_with_media", - "posts_with_urls", - "latest_posted_at", - "latest_reactions", - "latest_comments", - "latest_reposts", - "latest_impressions", - "latest_url" + "type", + "author", + "score", + "text", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "plugins/linkedin/post-analytics.js", - "sourceFile": "plugins/linkedin/post-analytics.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/reddit/read.js", + "sourceFile": "plugins/reddit/read.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "post-comments", - "description": "List unique commenters and reply authors from one exact LinkedIn post URL", - "access": "read", - "domain": "www.linkedin.com", + "site": "reddit", + "name": "reply", + "description": "Reply to a Reddit comment", + "access": "write", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "post-url", + "name": "comment-id", "type": "string", "required": true, "positional": true, - "help": "Exact LinkedIn post URL" + "help": "Comment ID (e.g. okf3s7u) or fullname (t1_xxx)" }, { - "name": "limit", - "type": "int", - "required": false, - "help": "Maximum unique commenters to return; omit to fetch all" + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "Reply text" } ], "columns": [ - "rank", - "name", - "headline", - "profile_url", - "comment_count", - "sample_comment", - "commented_at", - "source_post" + "status", + "message" ], "type": "js", - "modulePath": "plugins/linkedin/post-comments.js", - "sourceFile": "plugins/linkedin/post-comments.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/reddit/reply.js", + "sourceFile": "plugins/reddit/reply.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "posts", - "description": "Export visible posts from a LinkedIn profile activity page with engagement metrics", - "access": "read", - "domain": "www.linkedin.com", + "site": "reddit", + "name": "save", + "description": "Save or unsave a Reddit post", + "access": "write", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "profile-url", + "name": "post-id", "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "required": true, + "positional": true, + "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "undo", + "type": "boolean", + "default": false, "required": false, - "help": "Maximum posts to return (1-100)" + "help": "Unsave instead of save" } ], "columns": [ - "rank", - "author", - "posted_at", - "body", - "reactions", - "comments", - "reposts", - "impressions", - "media", - "media_urls", - "url", - "raw_text" + "status", + "message" ], "type": "js", - "modulePath": "plugins/linkedin/posts.js", - "sourceFile": "plugins/linkedin/posts.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/reddit/save.js", + "sourceFile": "plugins/reddit/save.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "profile-analytics", - "description": "Read visible LinkedIn profile dashboard metrics such as profile views, post impressions, and search appearances", + "site": "reddit", + "name": "saved", + "description": "Browse your saved Reddit posts", "access": "read", - "domain": "www.linkedin.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "profile-url", - "type": "string", + "name": "limit", + "type": "int", + "default": 15, "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "" } ], "columns": [ - "profile_url", - "profile_views", - "post_impressions", - "search_appearances", - "followers", - "connections", - "raw_analytics" + "title", + "subreddit", + "score", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/profile-analytics.js", - "sourceFile": "plugins/linkedin/profile-analytics.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/reddit/saved.js", + "sourceFile": "plugins/reddit/saved.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "profile-experience", - "description": "Read visible LinkedIn profile experience entries with titles, dates, locations, skills, media, and URLs", + "site": "reddit", + "name": "search", + "description": "Search Reddit Posts", "access": "read", - "domain": "www.linkedin.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "profile-url", + "name": "query", + "type": "string", + "required": true, + "positional": true, + "help": "Reddit search query" + }, + { + "name": "subreddit", + "type": "string", + "default": "", + "required": false, + "help": "Search within a specific subreddit" + }, + { + "name": "sort", + "type": "string", + "default": "relevance", + "required": false, + "help": "Sort order: relevance, hot, top, new, comments" + }, + { + "name": "time", "type": "string", + "default": "all", "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "Time filter: hour, day, week, month, year, all" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "" } ], "columns": [ - "rank", - "total_count", + "id", "title", - "employment_type", - "company", - "date_range", - "start_date", - "end_date", - "location", - "location_type", - "description", - "skills", - "media", - "urls", - "skill_url", - "media_url", - "profile_url", - "raw_text" + "subreddit", + "author", + "score", + "comments", + "url", + "created_utc", + "selftext", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/linkedin/profile-experience.js", - "sourceFile": "plugins/linkedin/profile-experience.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/reddit/search.js", + "sourceFile": "plugins/reddit/search.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "profile-projects", - "description": "Read visible LinkedIn profile projects with descriptions, dates, skills, media, and URLs", + "site": "reddit", + "name": "subreddit", + "description": "Get posts from a specific Subreddit", "access": "read", - "domain": "www.linkedin.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "profile-url", + "name": "name", + "type": "string", + "required": true, + "positional": true, + "help": "Subreddit name (no `r/` prefix; e.g. `python`)" + }, + { + "name": "sort", "type": "string", + "default": "hot", "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "Sorting method: hot, new, top, rising, controversial" + }, + { + "name": "time", + "type": "string", + "default": "all", + "required": false, + "help": "Time filter for top/controversial: hour, day, week, month, year, all" + }, + { + "name": "limit", + "type": "int", + "default": 15, + "required": false, + "help": "" } ], "columns": [ - "rank", + "id", "title", - "date_range", - "associated_with", - "description", - "skills", - "media", - "urls", - "profile_url", - "raw_text" + "subreddit", + "author", + "upvotes", + "comments", + "url", + "created_utc", + "selftext", + "post_hint", + "url_overridden_by_dest", + "preview_image_url", + "gallery_urls" ], "type": "js", - "modulePath": "plugins/linkedin/profile-projects.js", - "sourceFile": "plugins/linkedin/profile-projects.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/reddit/subreddit.js", + "sourceFile": "plugins/reddit/subreddit.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "profile-read", - "description": "Read visible LinkedIn profile sections: headline, About, experience, education, services, and featured sections", + "site": "reddit", + "name": "subreddit-info", + "description": "Show metadata for a Reddit subreddit (subscribers, description, created date, NSFW)", "access": "read", - "domain": "www.linkedin.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "profile-url", + "name": "name", "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "required": true, + "positional": true, + "help": "Subreddit name (no `r/` prefix needed)" } ], "columns": [ - "profile_url", - "name", - "headline", - "location", - "about", - "about_character_count", - "about_skills", - "experience", - "education", - "services", - "featured" + "field", + "value" ], "type": "js", - "modulePath": "plugins/linkedin/profile-read.js", - "sourceFile": "plugins/linkedin/profile-read.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/reddit/subreddit-info.js", + "sourceFile": "plugins/reddit/subreddit-info.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "safe-send", - "description": "Fail-closed LinkedIn message sender that verifies exact thread, recipient, and latest message before filling/sending", + "site": "reddit", + "name": "subscribe", + "description": "Subscribe or unsubscribe to a subreddit", "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", + "domain": "reddit.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "thread-url", - "type": "str", - "required": true, - "help": "Exact LinkedIn messaging thread URL to open and verify" - }, - { - "name": "expected-name", - "type": "str", - "required": true, - "help": "Expected visible recipient name in the active thread header" - }, - { - "name": "message", - "type": "str", + "name": "subreddit", + "type": "string", "required": true, - "help": "Message body to send or dry-run" - }, - { - "name": "expected-last-text", - "type": "str", - "required": false, - "help": "Substring expected in the currently visible latest conversation context" - }, - { - "name": "expected-last-hash", - "type": "str", - "required": false, - "help": "SHA-256 hash of expected latest visible message text" - }, - { - "name": "send", - "type": "bool", - "default": false, - "required": false, - "help": "Actually click Send. Default is dry-run verification only." + "positional": true, + "help": "Subreddit name (e.g. python)" }, { - "name": "screenshot", - "type": "bool", + "name": "undo", + "type": "boolean", "default": false, "required": false, - "help": "Capture a screenshot during verification" + "help": "Unsubscribe instead of subscribe" } ], "columns": [ "status", - "recipient", - "reason", - "thread_url", - "message_chars", - "screenshot" + "message" ], "type": "js", - "modulePath": "plugins/linkedin/safe-send.js", - "sourceFile": "plugins/linkedin/safe-send.js", - "navigateBefore": true + "modulePath": "plugins/reddit/subscribe.js", + "sourceFile": "plugins/reddit/subscribe.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "salesnav-inbox", - "description": "List LinkedIn Sales Navigator message conversations with API pagination", + "site": "reddit", + "name": "subscribed", + "description": "List subreddits you are subscribed to", "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", + "domain": "reddit.com", + "strategy": "cookie", "browser": true, "args": [ { "name": "limit", - "type": "number", - "default": 40, - "required": false, - "help": "Maximum conversations to return (1-500)" - }, - { - "name": "max-pages", - "type": "number", - "default": 30, - "required": false, - "help": "Maximum Sales Navigator API pages to fetch" - }, - { - "name": "unread-only", - "type": "bool", - "default": false, + "type": "int", + "default": 100, "required": false, - "help": "Return only unread conversations" + "help": "Max subreddits to return (1-1000, auto-paginates)" } ], "columns": [ - "rank", - "thread_id", - "thread_url", - "person_name", - "last_message_snippet", - "last_activity_time", - "unread", - "unread_count", - "total_message_count", - "archived", - "participants", - "next_page_starts_at" + "id", + "subreddit", + "title", + "subscribers", + "description", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-inbox.js", - "sourceFile": "plugins/linkedin/salesnav-inbox.js", - "navigateBefore": true + "modulePath": "plugins/reddit/subscribed.js", + "sourceFile": "plugins/reddit/subscribed.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "salesnav-message", - "description": "Send or dry-run a LinkedIn Sales Navigator InMail to a lead using the Sales Navigator messaging API", + "site": "reddit", + "name": "upvote", + "description": "Upvote or downvote a Reddit post", "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", + "domain": "reddit.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "recipient", + "name": "post-id", "type": "string", "required": true, "positional": true, - "help": "Sales Navigator lead URL, LinkedIn /in/ URL from salesnav-search, or urn:li:fs_salesProfile:(...)" - }, - { - "name": "subject", - "type": "string", - "required": true, - "help": "InMail subject" + "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" }, { - "name": "body", + "name": "direction", "type": "string", - "required": true, - "help": "InMail body" - }, - { - "name": "send", - "type": "bool", - "default": false, + "default": "up", "required": false, - "help": "Actually send the InMail. Default is dry-run validation only." - }, + "help": "Vote direction: up, down, none" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "plugins/reddit/upvote.js", + "sourceFile": "plugins/reddit/upvote.js", + "navigateBefore": "https://reddit.com" + }, + { + "site": "reddit", + "name": "upvoted", + "description": "Browse your upvoted Reddit posts", + "access": "read", + "domain": "reddit.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "copy-to-crm", - "type": "bool", - "default": false, + "name": "limit", + "type": "int", + "default": 15, "required": false, - "help": "Set Sales Navigator copyToCrm on the message request" + "help": "" } ], "columns": [ - "status", - "recipient", "title", - "company", - "credits_remaining", - "credits_before", - "credits_after", - "sent_in_salesnav", - "message_chars", - "subject_chars", - "recipient_urn", - "degree", - "inmail_restriction", - "open_link" + "subreddit", + "score", + "comments", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-message.js", - "sourceFile": "plugins/linkedin/salesnav-message.js", - "navigateBefore": true + "modulePath": "plugins/reddit/upvoted.js", + "sourceFile": "plugins/reddit/upvoted.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "salesnav-search", - "description": "Search LinkedIn Sales Navigator for people leads by keyword", + "site": "reddit", + "name": "user", + "description": "View a Reddit user profile", "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", + "domain": "reddit.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "keywords", + "name": "username", "type": "string", "required": true, "positional": true, - "help": "People search keywords, e.g. \"quality manager food manufacturing\"" - }, - { - "name": "limit", - "type": "number", - "default": 25, - "required": false, - "help": "Maximum leads to return (1-500, fetched 25 per request)" + "help": "Reddit username (no `u/` prefix needed)" } ], "columns": [ - "rank", - "name", - "title", - "company", - "location", - "degree", - "profile_url", - "lead_url", - "recipient_urn" - ], - "tags": [ - "search" + "field", + "value" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-search.js", - "sourceFile": "plugins/linkedin/salesnav-search.js", - "navigateBefore": true + "modulePath": "plugins/reddit/user.js", + "sourceFile": "plugins/reddit/user.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "salesnav-thread", - "description": "Return full Sales Navigator message history for a thread id, Sales Navigator inbox URL, lead URL, recipient urn, or exact recipient name", + "site": "reddit", + "name": "user-comments", + "description": "View a Reddit user's comment history", "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", + "domain": "reddit.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "thread-or-recipient", + "name": "username", "type": "string", "required": true, "positional": true, - "help": "Sales Navigator inbox URL/thread id, Sales Navigator lead URL, recipient urn, or exact participant name" + "help": "Reddit username (no `u/` prefix needed)" }, { "name": "limit", - "type": "number", - "default": 200, - "required": false, - "help": "Maximum messages to return (1-500)" - }, - { - "name": "max-pages", - "type": "number", - "default": 30, + "type": "int", + "default": 15, "required": false, - "help": "Maximum inbox pages to scan when resolving a recipient" + "help": "" } ], "columns": [ - "index", - "thread_id", - "thread_url", - "sender", - "text", - "timestamp", - "subject", - "message_id", - "sender_urn", - "delivered_at", - "type", - "total_message_count" + "subreddit", + "score", + "body", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/salesnav-thread.js", - "sourceFile": "plugins/linkedin/salesnav-thread.js", - "navigateBefore": true + "modulePath": "plugins/reddit/user-comments.js", + "sourceFile": "plugins/reddit/user-comments.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "search", - "description": "Search LinkedIn jobs", + "site": "reddit", + "name": "user-posts", + "description": "View a Reddit user's submitted posts", "access": "read", - "domain": "www.linkedin.com", + "domain": "reddit.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", + "name": "username", "type": "string", "required": true, "positional": true, - "help": "Job search keywords" - }, - { - "name": "location", - "type": "string", - "required": false, - "help": "Location text such as San Francisco Bay Area" + "help": "Reddit username (no `u/` prefix needed)" }, { "name": "limit", "type": "int", - "default": 10, - "required": false, - "help": "Number of jobs to return (max 100)" - }, - { - "name": "start", - "type": "int", - "default": 0, - "required": false, - "help": "Result offset for pagination" - }, - { - "name": "details", - "type": "bool", - "default": false, - "required": false, - "help": "Include full job description and apply URL (slower)" - }, - { - "name": "company", - "type": "string", - "required": false, - "help": "Comma-separated company names or LinkedIn company IDs" - }, - { - "name": "experience-level", - "type": "string", - "required": false, - "help": "Comma-separated: internship, entry, associate, mid-senior, director, executive" - }, - { - "name": "job-type", - "type": "string", - "required": false, - "help": "Comma-separated: full-time, part-time, contract, temporary, volunteer, internship, other" - }, - { - "name": "date-posted", - "type": "string", - "required": false, - "help": "One of: any, month, week, 24h" - }, - { - "name": "remote", - "type": "string", + "default": 15, "required": false, - "help": "Comma-separated: on-site, hybrid, remote" + "help": "" } ], "columns": [ - "rank", "title", - "company", - "location", - "listed", - "salary", + "subreddit", + "score", + "comments", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/linkedin/search.js", - "sourceFile": "plugins/linkedin/search.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/reddit/user-posts.js", + "sourceFile": "plugins/reddit/user-posts.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "sent-invitations", - "description": "List pending LinkedIn sent invitations for CRM reconciliation", + "site": "reddit", + "name": "whoami", + "description": "Show the currently logged-in Reddit user", "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", + "domain": "reddit.com", + "strategy": "cookie", "browser": true, "args": [], "columns": [ - "rank", - "name", - "profile_url", - "invited_date_text" + "field", + "value" ], "type": "js", - "modulePath": "plugins/linkedin/sent-invitations.js", - "sourceFile": "plugins/linkedin/sent-invitations.js", - "navigateBefore": true + "modulePath": "plugins/reddit/whoami.js", + "sourceFile": "plugins/reddit/whoami.js", + "navigateBefore": "https://reddit.com" }, { - "site": "linkedin", - "name": "services-read", - "description": "Read LinkedIn Services page details including services, overview, availability, pricing, and media titles/descriptions", + "site": "rest-countries", + "name": "country", + "description": "Look up countries by name (common / official, substring match)", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "restcountries.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "profile-url", - "type": "string", + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Country name (e.g. \"japan\", \"united kingdom\")" + }, + { + "name": "limit", + "type": "int", + "default": 25, "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." + "help": "Max rows (1-250)" + } + ], + "columns": [ + "rank", + "commonName", + "officialName", + "cca2", + "cca3", + "ccn3", + "capital", + "region", + "subregion", + "population", + "area", + "languages", + "currencies", + "latitude", + "longitude", + "timezones", + "independent", + "unMember", + "landlocked", + "flag", + "url" + ], + "type": "js", + "modulePath": "plugins/rest-countries/country.js", + "sourceFile": "plugins/rest-countries/country.js" + }, + { + "site": "rest-countries", + "name": "region", + "description": "List countries in a region (africa / americas / asia / europe / oceania / antarctic)", + "access": "read", + "domain": "restcountries.com", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "region", + "type": "str", + "required": true, + "positional": true, + "help": "Region name (case-insensitive)" }, { - "name": "services-url", - "type": "string", + "name": "limit", + "type": "int", + "default": 250, "required": false, - "help": "LinkedIn /services/page// URL. If omitted, it is discovered from the profile." + "help": "Max rows (1-250)" } ], "columns": [ - "service_url", - "page_title", - "overview", - "availability", - "work_locations", - "pricing", - "services_provided", - "services_count", - "media", - "media_count", - "messages", - "reviews_visibility" + "rank", + "commonName", + "officialName", + "cca2", + "cca3", + "ccn3", + "capital", + "region", + "subregion", + "population", + "area", + "languages", + "currencies", + "latitude", + "longitude", + "timezones", + "independent", + "unMember", + "landlocked", + "flag", + "url" ], "type": "js", - "modulePath": "plugins/linkedin/services-read.js", - "sourceFile": "plugins/linkedin/services-read.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/rest-countries/region.js", + "sourceFile": "plugins/rest-countries/region.js" }, { - "site": "linkedin", - "name": "thread-snapshot", - "description": "Load a LinkedIn messaging thread, scroll for available history, and return a full context snapshot", + "site": "reuters", + "name": "article-detail", + "description": "Reuters Reuters article detail:title/author/body text", "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", + "domain": "www.reuters.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "thread-url", + "name": "url", "type": "str", "required": true, - "help": "Exact LinkedIn messaging thread URL to open and snapshot" - }, - { - "name": "max-scrolls", - "type": "number", - "default": 30, - "required": false, - "help": "Maximum upward scroll attempts to load older messages" - }, - { - "name": "json", - "type": "bool", - "default": false, - "required": false, - "help": "Return only JSON snapshot string in the snapshot_json field" + "positional": true, + "help": "Reuters article URL (must be on reuters.com)" } ], "columns": [ - "thread_url", - "recipient", - "message_count", - "latest_text", - "snapshot_json" + "title", + "date", + "section", + "section_path", + "authors", + "description", + "word_count", + "url", + "body" ], "type": "js", - "modulePath": "plugins/linkedin/thread-snapshot.js", - "sourceFile": "plugins/linkedin/thread-snapshot.js", - "navigateBefore": true + "modulePath": "plugins/reuters/article-detail.js", + "sourceFile": "plugins/reuters/article-detail.js", + "navigateBefore": "https://www.reuters.com" }, { - "site": "linkedin", - "name": "timeline", - "description": "Read LinkedIn home timeline posts", + "site": "reuters", + "name": "login", + "description": "Open reuters login", + "access": "write", + "domain": "reuters.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "user_id", + "subscribed", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/reuters/auth.js", + "sourceFile": "plugins/reuters/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "reuters", + "name": "search", + "description": "Reuters Reuters news search", "access": "read", - "domain": "www.linkedin.com", + "domain": "www.reuters.com", "strategy": "cookie", "browser": true, "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Number of posts to return (max 100)" + "help": "Number of results (1-40)" } ], "columns": [ "rank", - "author", - "author_url", - "headline", - "text", - "posted_at", - "reactions", - "comments", + "title", + "date", + "section", + "section_path", + "authors", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/linkedin/timeline.js", - "sourceFile": "plugins/linkedin/timeline.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/reuters/search.js", + "sourceFile": "plugins/reuters/search.js", + "navigateBefore": "https://www.reuters.com" }, { - "site": "linkedin", + "site": "reuters", "name": "whoami", - "description": "Show the current logged-in linkedin account", + "description": "Show the current logged-in reuters account", "access": "read", - "domain": "www.linkedin.com", + "domain": "reuters.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ "logged_in", "site", - "public_id", - "plain_id", - "name" + "user_id", + "subscribed" ], "type": "js", - "modulePath": "plugins/linkedin/auth.js", - "sourceFile": "plugins/linkedin/auth.js", + "modulePath": "plugins/reuters/auth.js", + "sourceFile": "plugins/reuters/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "linkedin-learning", - "name": "course", - "description": "Get LinkedIn Learning course detail by slug or course URL", + "site": "rfc", + "name": "rfc", + "description": "Single IETF RFC metadata (title, abstract, working group, authors, std level)", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "datatracker.ietf.org", + "strategy": "public", + "browser": false, "args": [ { - "name": "slug", - "type": "string", + "name": "number", + "type": "int", "required": true, "positional": true, - "help": "Course slug (e.g. agentic-ai-build-your-first-agentic-ai-system) or full /learning/ URL" + "help": "RFC number (e.g. 9000, 791, 2616)" } ], "columns": [ + "rfc", "title", - "slug", - "description", - "difficulty", - "duration_sec", - "videos_count", - "rating", - "rating_count", - "released", + "state", + "stdLevel", + "group", + "groupType", + "pages", + "published", + "authors", + "abstract", + "rfcEditorUrl", "url" ], "type": "js", - "modulePath": "plugins/linkedin-learning/course.js", - "sourceFile": "plugins/linkedin-learning/course.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/rfc/rfc.js", + "sourceFile": "plugins/rfc/rfc.js" }, { - "site": "linkedin-learning", - "name": "login", - "description": "Open linkedin-learning login", - "access": "write", - "domain": "linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], + "site": "rubygems", + "name": "gem", + "description": "Fetch a RubyGems.org gem's metadata (version, downloads, license, links)", + "access": "read", + "domain": "rubygems.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "name", + "type": "str", + "required": true, + "positional": true, + "help": "Gem name (e.g. \"rails\", \"sidekiq\")" + } + ], "columns": [ - "status", - "logged_in", - "site", - "public_id", - "plain_id", - "name", - "action", - "verify_command" + "gem", + "version", + "releasedAt", + "downloads", + "versionDownloads", + "license", + "authors", + "homepage", + "source", + "bugs", + "info", + "url" ], "type": "js", - "modulePath": "plugins/linkedin-learning/auth.js", - "sourceFile": "plugins/linkedin-learning/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/rubygems/gem.js", + "sourceFile": "plugins/rubygems/gem.js" }, { - "site": "linkedin-learning", + "site": "rubygems", "name": "search", - "description": "Search LinkedIn Learning courses, videos, and learning paths by keyword", + "description": "Search RubyGems.org gems by keyword", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "rubygems.org", + "strategy": "public", + "browser": false, "args": [ { - "name": "keywords", - "type": "string", + "name": "query", + "type": "str", "required": true, "positional": true, - "help": "Search keywords, e.g. \"AI agent\"" + "help": "Search keyword (e.g. \"rails\", \"redis\")" }, { "name": "limit", "type": "int", - "default": 10, + "default": 30, "required": false, - "help": "Maximum results to return (1-50)" + "help": "Max gems (1-100, single RubyGems page)" } ], "columns": [ "rank", - "type", - "title", - "instructor", - "difficulty", - "duration_sec", - "rating", - "rating_count", - "viewers", + "gem", + "version", + "downloads", + "license", + "authors", + "info", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/linkedin-learning/search.js", - "sourceFile": "plugins/linkedin-learning/search.js", - "navigateBefore": "https://www.linkedin.com" + "modulePath": "plugins/rubygems/search.js", + "sourceFile": "plugins/rubygems/search.js" }, { - "site": "linkedin-learning", - "name": "trending", - "description": "Browse LinkedIn Learning recommended courses across personalized carousels", + "site": "semanticscholar", + "name": "citations", + "description": "List papers that cite a Semantic Scholar paper (paginated)", "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, + "domain": "api.semanticscholar.org", + "strategy": "public", + "browser": false, "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" + }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Maximum results to return (1-50)" + "help": "Max citing papers (1-1000, single Semantic Scholar page)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Page offset (0-based)" } ], "columns": [ "rank", - "group", - "type", + "paperId", + "doi", "title", - "difficulty", - "viewers", + "year", + "firstAuthor", + "citationCount", "url" ], "type": "js", - "modulePath": "plugins/linkedin-learning/trending.js", - "sourceFile": "plugins/linkedin-learning/trending.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin-learning", - "name": "whoami", - "description": "Show the current logged-in linkedin-learning account", - "access": "read", - "domain": "linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "public_id", - "plain_id", - "name" - ], - "type": "js", - "modulePath": "plugins/linkedin-learning/auth.js", - "sourceFile": "plugins/linkedin-learning/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/semanticscholar/citations.js", + "sourceFile": "plugins/semanticscholar/citations.js" }, { - "site": "lobsters", - "name": "active", - "description": "Lobste.rs most active discussions", + "site": "semanticscholar", + "name": "paper", + "description": "Semantic Scholar paper detail (citation graph + AI tldr) by paperId, DOI, or arXiv id", "access": "read", - "domain": "lobste.rs", + "domain": "api.semanticscholar.org", "strategy": "public", "browser": false, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id (e.g. \"ARXIV:1706.03762\", \"PMID:12345\")" } ], "columns": [ - "rank", - "id", + "paperId", + "doi", "title", - "score", - "author", - "comments", - "created_at", - "tags", + "year", + "firstAuthor", + "citationCount", + "influentialCitationCount", + "referenceCount", + "tldr", "url" ], "type": "js", - "modulePath": "plugins/lobsters/active.js", - "sourceFile": "plugins/lobsters/active.js" + "modulePath": "plugins/semanticscholar/paper.js", + "sourceFile": "plugins/semanticscholar/paper.js" }, { - "site": "lobsters", - "name": "domain", - "description": "Lobste.rs stories submitted from a specific domain", + "site": "semanticscholar", + "name": "recommendations", + "description": "Semantic Scholar AI-curated related papers for a paperId, DOI, or arXiv id", "access": "read", - "domain": "lobste.rs", + "domain": "api.semanticscholar.org", "strategy": "public", "browser": false, "args": [ { - "name": "domain", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Source domain (e.g. github.com, arxiv.org, blog.cloudflare.com)" + "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Number of stories (1-25 — single page)" + "help": "Max recommendations (1-500)" } ], "columns": [ "rank", - "id", + "paperId", + "doi", "title", - "score", - "author", - "comments", - "created_at", - "tags", - "submission_url", - "comments_url" + "year", + "firstAuthor", + "citationCount", + "url" ], "type": "js", - "modulePath": "plugins/lobsters/domain.js", - "sourceFile": "plugins/lobsters/domain.js" + "modulePath": "plugins/semanticscholar/recommendations.js", + "sourceFile": "plugins/semanticscholar/recommendations.js" }, { - "site": "lobsters", - "name": "hot", - "description": "Lobste.rs hottest stories", + "site": "semanticscholar", + "name": "search", + "description": "Search Semantic Scholar papers by free text", "access": "read", - "domain": "lobste.rs", + "domain": "api.semanticscholar.org", "strategy": "public", "browser": false, "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search text (e.g. \"attention is all you need\", \"diffusion model\")" + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of stories" + "help": "Max papers (1-100, single Semantic Scholar page)" } ], "columns": [ "rank", - "id", + "paperId", + "doi", "title", - "score", - "author", - "comments", - "created_at", - "tags", + "year", + "firstAuthor", + "citationCount", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/lobsters/hot.js", - "sourceFile": "plugins/lobsters/hot.js" + "modulePath": "plugins/semanticscholar/search.js", + "sourceFile": "plugins/semanticscholar/search.js" }, { - "site": "lobsters", - "name": "newest", - "description": "Lobste.rs newest stories", + "site": "skyscanner", + "name": "flights", + "description": "Skyscanner visible round-trip flight results from a warmed browser session", "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, + "domain": "www.skyscanner.com", + "strategy": "ui", + "browser": true, "args": [ + { + "name": "origin", + "type": "str", + "required": true, + "positional": true, + "help": "Skyscanner origin route code, for example nyca" + }, + { + "name": "destination", + "type": "str", + "required": true, + "positional": true, + "help": "Skyscanner destination route code, for example lond" + }, + { + "name": "depart-date", + "type": "str", + "required": true, + "help": "Outbound date as YYYY-MM-DD" + }, + { + "name": "return-date", + "type": "str", + "required": true, + "help": "Return date as YYYY-MM-DD" + }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Number of stories" + "help": "Maximum flight rows to return (1-30)" } ], "columns": [ "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", + "priceText", + "airlines", + "outboundTime", + "outboundRoute", + "outboundDuration", + "outboundStops", + "returnTime", + "returnRoute", + "returnDuration", + "returnStops", "url" ], "type": "js", - "modulePath": "plugins/lobsters/newest.js", - "sourceFile": "plugins/lobsters/newest.js" + "modulePath": "plugins/skyscanner/flights.js", + "sourceFile": "plugins/skyscanner/flights.js", + "navigateBefore": false }, { - "site": "lobsters", - "name": "read", - "description": "Read a Lobste.rs story and its comment tree", + "site": "slock", + "name": "attachment-download", + "description": "Download an attachment to a local file. Resolves a signed CDN URL in the page, then fetches bytes node-side (no CORS).", "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "attachmentId", "type": "str", "required": true, "positional": true, - "help": "Lobste.rs short_id (e.g. 6cmh6h)" + "help": "Attachment UUID" }, { - "name": "limit", - "type": "int", - "default": 25, + "name": "out", + "type": "str", "required": false, - "help": "Max top-level comments" + "help": "Local path to write to. Defaults to ./.bin" }, { - "name": "depth", - "type": "int", - "default": 2, + "name": "server", + "type": "str", "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" + "help": "Override active server slug" + } + ], + "columns": [ + "attachmentId", + "out", + "sizeBytes" + ], + "type": "js", + "modulePath": "plugins/slock/attachment-download.js", + "sourceFile": "plugins/slock/attachment-download.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "attachment-upload", + "description": "Upload a local file to Slock attachments. Prints the attachmentId for use with `message-send --attach`.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "file", + "type": "str", + "required": true, + "positional": true, + "help": "Local file path to upload (single file; max 50 MB)" }, { - "name": "replies", - "type": "int", - "default": 5, - "required": false, - "help": "Max replies shown per comment at each level" + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name — server requires the attachment be scoped to a channel" }, { - "name": "max-length", - "type": "int", - "default": 2000, + "name": "server", + "type": "str", "required": false, - "help": "Max characters per comment body (min 100)" + "help": "Override active server slug" } ], "columns": [ - "type", - "author", - "score", - "text" + "attachmentId", + "filename", + "mimeType", + "sizeBytes" ], "type": "js", - "modulePath": "plugins/lobsters/read.js", - "sourceFile": "plugins/lobsters/read.js" + "modulePath": "plugins/slock/attachment-upload.js", + "sourceFile": "plugins/slock/attachment-upload.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "lobsters", - "name": "tag", - "description": "Lobste.rs stories by tag", + "site": "slock", + "name": "attachment-url", + "description": "Get a short-lived signed CDN URL for an attachment (does not download bytes).", "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "tag", + "name": "attachmentId", "type": "str", "required": true, "positional": true, - "help": "Tag name (e.g. programming, rust, security, ai)" + "help": "Attachment UUID" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "server", + "type": "str", "required": false, - "help": "Number of stories" + "help": "Override active server slug" } ], "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "url" + "attachmentId", + "url", + "expiresAt" ], "type": "js", - "modulePath": "plugins/lobsters/tag.js", - "sourceFile": "plugins/lobsters/tag.js" + "modulePath": "plugins/slock/attachment-url.js", + "sourceFile": "plugins/slock/attachment-url.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "luma", - "name": "create-event", - "description": "Create a free single-session Luma event", + "site": "slock", + "name": "bookmark-add", + "description": "Bookmark a message (POST /channels/saved). Requires full messageId UUID.", "access": "write", - "domain": "luma.com", - "strategy": "ui", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, "args": [ { - "name": "name", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "start", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "end", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "timezone", + "name": "messageId", "type": "str", "required": true, - "help": "" + "positional": true, + "help": "Full messageId UUID (short ids rejected)" }, { - "name": "calendar", + "name": "server", "type": "str", "required": false, - "help": "" - }, + "help": "Override active server" + } + ], + "columns": [ + "messageId", + "saved" + ], + "type": "js", + "modulePath": "plugins/slock/bookmark-add.js", + "sourceFile": "plugins/slock/bookmark-add.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "bookmark-list", + "description": "List bookmarks (saved messages) in the active server", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "description", - "type": "str", + "name": "limit", + "type": "int", + "default": 50, "required": false, - "help": "" + "help": "Max results" }, { - "name": "location", - "type": "str", + "name": "offset", + "type": "int", + "default": 0, "required": false, - "help": "" + "help": "Offset" }, { - "name": "virtual-url", + "name": "server", "type": "str", "required": false, - "help": "" - }, + "help": "Override active server" + } + ], + "columns": [ + "id", + "messageId", + "content", + "savedAt" + ], + "type": "js", + "modulePath": "plugins/slock/bookmark-list.js", + "sourceFile": "plugins/slock/bookmark-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "bookmark-remove", + "description": "Remove a bookmark (DELETE /channels/saved/:messageId). 404 is treated as already-removed.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "visibility", + "name": "messageId", "type": "str", - "default": "public", - "required": false, - "help": "", - "choices": [ - "public", - "private", - "members-only" - ] - }, - { - "name": "capacity", - "type": "int", - "required": false, - "help": "" - }, - { - "name": "require-approval", - "type": "boolean", - "default": false, - "required": false, - "help": "" + "required": true, + "positional": true, + "help": "Full messageId UUID" }, { - "name": "confirm", - "type": "boolean", - "default": false, + "name": "server", + "type": "str", "required": false, - "help": "" + "help": "Override active server" } ], "columns": [ - "eventId", - "name", - "startsAt", - "endsAt", - "timezone", - "visibility", - "requireApproval", - "capacity", - "eventUrl", - "manageUrl" + "messageId", + "removed", + "note" ], "type": "js", - "modulePath": "plugins/luma/create-event.js", - "sourceFile": "plugins/luma/create-event.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "modulePath": "plugins/slock/bookmark-remove.js", + "sourceFile": "plugins/slock/bookmark-remove.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "luma", - "name": "events", - "description": "List upcoming or past Luma events managed by the logged-in account", - "access": "read", - "example": "webcmd luma events --period future --limit 25 -f json", - "domain": "luma.com", + "site": "slock", + "name": "channel-archive", + "description": "Archive a channel — admin only (POST /channels/:id/archive)", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "period", + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", "type": "str", - "default": "future", - "required": false, - "help": "List future or past events", - "choices": [ - "future", - "past" - ] - }, - { - "name": "limit", - "type": "int", - "default": 25, "required": false, - "help": "Maximum number of events to request" + "help": "Override active server" } ], "columns": [ - "eventId", - "name", - "startsAt", - "endsAt", - "timezone", - "guestCount", - "requireApproval", - "managerLevel", - "location", - "manageUrl", - "eventUrl" + "channel", + "id", + "archivedAt", + "result" ], "type": "js", - "modulePath": "plugins/luma/events.js", - "sourceFile": "plugins/luma/events.js", - "navigateBefore": false, + "modulePath": "plugins/slock/channel-archive.js", + "sourceFile": "plugins/slock/channel-archive.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "luma", - "name": "guests", - "description": "List guests and all custom registration answers for a managed Luma event", - "access": "read", - "example": "webcmd luma guests evt-abc --status pending_approval --limit 100 -f json", - "domain": "luma.com", + "site": "slock", + "name": "channel-create", + "description": "Create a channel — admin only (POST /channels/). Public unless --private.", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "eventId", + "name": "name", "type": "str", "required": true, "positional": true, - "help": "Luma event ID returned by webcmd luma events" + "help": "Channel name" }, { - "name": "status", + "name": "description", "type": "str", - "default": "all", "required": false, - "help": "Filter by guest approval status", - "choices": [ - "all", - "approved", - "pending_approval", - "declined", - "waitlist", - "invited" - ] + "help": "Channel description / topic (≤500 chars)" }, { - "name": "limit", - "type": "int", - "default": 100, + "name": "private", + "type": "bool", + "default": false, "required": false, - "help": "Maximum matching guests to return" + "help": "Create a private channel instead of public" }, { - "name": "query", + "name": "server", "type": "str", - "default": "", "required": false, - "help": "Search text passed to Luma guest search" + "help": "Override active server" } ], "columns": [ - "eventId", - "guestId", - "userId", + "id", "name", - "email", - "phone", - "status", - "registeredAt", - "profiles", - "answers" + "type", + "result" ], "type": "js", - "modulePath": "plugins/luma/guests.js", - "sourceFile": "plugins/luma/guests.js", - "navigateBefore": false, + "modulePath": "plugins/slock/channel-create.js", + "sourceFile": "plugins/slock/channel-create.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "luma", - "name": "login", - "description": "Open Luma sign in", - "access": "write", - "domain": "luma.com", + "site": "slock", + "name": "channel-files", + "description": "List files shared in a channel (GET /channels/:id/files)", + "access": "read", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "name", - "email", - "url", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/luma/auth.js", - "sourceFile": "plugins/luma/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "luma", - "name": "set-registration-questions", - "description": "Append or replace custom registration questions on a managed Luma event", - "access": "write", - "domain": "luma.com", - "strategy": "ui", - "browser": true, "args": [ { - "name": "eventId", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "" + "help": "channelId UUID or #name" }, { - "name": "questions-file", - "type": "str", - "required": true, - "help": "" + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max files" }, { - "name": "mode", + "name": "server", "type": "str", - "required": true, - "help": "", - "choices": [ - "append", - "replace" - ] - }, - { - "name": "confirm", - "type": "boolean", - "default": false, "required": false, - "help": "" + "help": "Override active server" } ], "columns": [ - "eventId", - "mode", - "previousCount", - "questionCount", - "questions", - "registrationUrl" + "id", + "filename", + "mimeType", + "sizeBytes", + "messageId", + "createdAt" ], "type": "js", - "modulePath": "plugins/luma/set-registration-questions.js", - "sourceFile": "plugins/luma/set-registration-questions.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "modulePath": "plugins/slock/channel-files.js", + "sourceFile": "plugins/slock/channel-files.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "luma", - "name": "update-guest-status", - "description": "Approve or decline a pending Luma guest after explicit confirmation", - "access": "write", - "example": "webcmd luma update-guest-status evt-abc gst-abc --status approved --confirm true -f json", - "domain": "luma.com", + "site": "slock", + "name": "channel-info", + "description": "Show one channel's details (GET /channels/:id)", + "access": "read", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "eventId", - "type": "str", - "required": true, - "positional": true, - "help": "Luma event ID returned by webcmd luma events" - }, - { - "name": "guestId", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Luma guest ID returned by webcmd luma guests" + "help": "channelId UUID or #name" }, { - "name": "status", + "name": "server", "type": "str", - "required": true, - "help": "New guest status", - "choices": [ - "approved", - "declined" - ] - }, - { - "name": "suppress-email", - "type": "boolean", - "default": false, - "required": false, - "help": "Set true to prevent Luma from emailing the guest" - }, - { - "name": "confirm", - "type": "boolean", - "default": false, "required": false, - "help": "Required. Set --confirm true to change the real guest status" + "help": "Override active server" } ], "columns": [ - "eventId", - "guestId", + "id", "name", - "email", - "previousStatus", - "status", - "emailSuppressed" + "type", + "topic", + "joined", + "archivedAt" ], "type": "js", - "modulePath": "plugins/luma/update-guest-status.js", - "sourceFile": "plugins/luma/update-guest-status.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true + "modulePath": "plugins/slock/channel-info.js", + "sourceFile": "plugins/slock/channel-info.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "luma", - "name": "whoami", - "description": "Show the current logged-in Luma account", - "access": "read", - "domain": "luma.com", + "site": "slock", + "name": "channel-join", + "description": "Join a public channel (POST /channels/:id/join)", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], "columns": [ - "logged_in", - "site", - "name", - "email", - "url" + "channel", + "id", + "archivedAt", + "result" ], "type": "js", - "modulePath": "plugins/luma/auth.js", - "sourceFile": "plugins/luma/auth.js", - "navigateBefore": false, + "modulePath": "plugins/slock/channel-join.js", + "sourceFile": "plugins/slock/channel-join.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "manus", - "name": "connectors", - "description": "List available Manus connectors (integrations).", - "access": "read", - "domain": "manus.im", + "site": "slock", + "name": "channel-leave", + "description": "Leave a channel (POST /channels/:id/leave)", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 50, + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", "required": false, - "help": "Max connectors to return" + "help": "Override active server" } ], "columns": [ - "UID", - "Name", - "Brief" + "channel", + "id", + "archivedAt", + "result" ], "type": "js", - "modulePath": "plugins/manus/connectors.js", - "sourceFile": "plugins/manus/connectors.js", - "navigateBefore": true, + "modulePath": "plugins/slock/channel-leave.js", + "sourceFile": "plugins/slock/channel-leave.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "manus", - "name": "credits", - "description": "Show Manus credit balance and refresh details.", + "site": "slock", + "name": "channel-list", + "description": "List channels in the active slock server", "access": "read", - "domain": "manus.im", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "Field", - "Value" + "args": [ + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server (slug or id) for this call" + } + ], + "columns": [ + "id", + "name", + "topic" ], "type": "js", - "modulePath": "plugins/manus/credits.js", - "sourceFile": "plugins/manus/credits.js", - "navigateBefore": true, + "modulePath": "plugins/slock/channel-list.js", + "sourceFile": "plugins/slock/channel-list.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "manus", - "name": "list", - "description": "List Manus sessions (tasks).", - "access": "read", - "domain": "manus.im", + "site": "slock", + "name": "channel-mark", + "description": "Mark a channel read (default), read up to --seq, or --unread.", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "seq", "type": "int", - "default": 20, "required": false, - "help": "Max sessions to return" + "help": "Mark read up to this seq (omit for read-all)" }, { - "name": "archived", + "name": "unread", "type": "bool", "default": false, "required": false, - "help": "Include archived sessions" + "help": "Mark the channel unread instead of read" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "id", - "Title", - "Status", - "Last Message", - "Last Updated", - "Credits" + "channel", + "action", + "result" ], "type": "js", - "modulePath": "plugins/manus/list.js", - "sourceFile": "plugins/manus/list.js", - "navigateBefore": true, + "modulePath": "plugins/slock/channel-mark.js", + "sourceFile": "plugins/slock/channel-mark.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "manus", - "name": "login", - "description": "Open manus login", - "access": "write", - "domain": "manus.im", + "site": "slock", + "name": "channel-members", + "description": "List members of a channel", + "access": "read", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "channel", + "type": "str", + "required": true, + "positional": true, + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server (slug or id)" + } + ], "columns": [ - "status", - "logged_in", - "site", - "user_id", + "userId", "name", - "action", - "verify_command" + "kind", + "role" ], "type": "js", - "modulePath": "plugins/manus/auth.js", - "sourceFile": "plugins/manus/auth.js", - "navigateBefore": false, + "modulePath": "plugins/slock/channel-members.js", + "sourceFile": "plugins/slock/channel-members.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "manus", - "name": "read", - "description": "Show details for a specific Manus session.", - "access": "read", - "domain": "manus.im", + "site": "slock", + "name": "channel-unarchive", + "description": "Unarchive a channel — admin only (POST /channels/:id/unarchive). #name lookups exclude archived channels; pass the channelId UUID for archived ones.", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "uid", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Session UID" + "help": "channelId UUID or #name" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "Field", - "Value" + "channel", + "id", + "archivedAt", + "result" ], "type": "js", - "modulePath": "plugins/manus/read.js", - "sourceFile": "plugins/manus/read.js", - "navigateBefore": true, + "modulePath": "plugins/slock/channel-unarchive.js", + "sourceFile": "plugins/slock/channel-unarchive.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "manus", - "name": "skills", - "description": "List Manus skills (user-added and system).", + "site": "slock", + "name": "dm-list", + "description": "List DM channels in the active server (GET /channels/dm)", "access": "read", - "domain": "manus.im", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server (slug or id)" + } + ], "columns": [ - "ID", - "Name", - "Description", - "Source" + "channelId", + "peerName", + "peerId", + "createdAt" ], "type": "js", - "modulePath": "plugins/manus/skills.js", - "sourceFile": "plugins/manus/skills.js", - "navigateBefore": true, + "modulePath": "plugins/slock/dm-list.js", + "sourceFile": "plugins/slock/dm-list.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "manus", - "name": "status", - "description": "Show current Manus user profile and credit summary.", + "site": "slock", + "name": "inbox", + "description": "List unified inbox items (channels, DMs, followed threads) that need attention.", "access": "read", - "domain": "manus.im", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "filter", + "type": "str", + "default": "all", + "required": false, + "help": "all | unread | mentions" + }, + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max items (server caps at 100)" + }, + { + "name": "offset", + "type": "int", + "default": 0, + "required": false, + "help": "Pagination offset" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } + ], "columns": [ - "Field", - "Value" + "kind", + "id", + "name", + "unreadCount", + "hasMention", + "lastActivityAt", + "preview" ], "type": "js", - "modulePath": "plugins/manus/status.js", - "sourceFile": "plugins/manus/status.js", - "navigateBefore": true, + "modulePath": "plugins/slock/inbox.js", + "sourceFile": "plugins/slock/inbox.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "manus", - "name": "whoami", - "description": "Show the current logged-in manus account", - "access": "read", - "domain": "manus.im", + "site": "slock", + "name": "inbox-done", + "description": "Mark one chat as done / clear it from the inbox (POST /channels/inbox/done)", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "plugins/manus/auth.js", - "sourceFile": "plugins/manus/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "maven", - "name": "artifact", - "description": "Fetch a Maven Central artifact's version history (groupId:artifactId[:version])", - "access": "read", - "domain": "search.maven.org", - "strategy": "public", - "browser": false, "args": [ { - "name": "coordinate", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Maven coord \"groupId:artifactId\" or \"groupId:artifactId:version\"" + "help": "channelId UUID or #name" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "server", + "type": "str", "required": false, - "help": "Max versions (1-200, ignored when version is pinned)" + "help": "Override active server" } ], "columns": [ - "groupId", - "artifactId", - "version", - "packaging", - "publishedAt", - "tags", - "url" + "channel", + "result" ], "type": "js", - "modulePath": "plugins/maven/artifact.js", - "sourceFile": "plugins/maven/artifact.js" + "modulePath": "plugins/slock/inbox-done.js", + "sourceFile": "plugins/slock/inbox-done.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "maven", - "name": "search", - "description": "Search Maven Central by keyword (artifact name, groupId, tag)", - "access": "read", - "domain": "search.maven.org", - "strategy": "public", - "browser": false, + "site": "slock", + "name": "inbox-read-all", + "description": "Mark the entire inbox as read (POST /channels/inbox/read-all)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "server", "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"jackson\", \"guava\", \"ai.koog\")" - }, - { - "name": "limit", - "type": "int", - "default": 30, "required": false, - "help": "Max artifacts (1-200)" + "help": "Override active server" } ], "columns": [ - "rank", - "coordinate", - "groupId", - "artifactId", - "latestVersion", - "packaging", - "versions", - "lastPublished", - "repository", - "url" - ], - "tags": [ - "search" + "result", + "markedCount" ], "type": "js", - "modulePath": "plugins/maven/search.js", - "sourceFile": "plugins/maven/search.js" + "modulePath": "plugins/slock/inbox-read-all.js", + "sourceFile": "plugins/slock/inbox-read-all.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "mdn", - "name": "search", - "description": "Search MDN Web Docs by keyword", - "access": "read", - "domain": "developer.mozilla.org", - "strategy": "public", - "browser": false, + "site": "slock", + "name": "login", + "description": "Open slock login", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "id", + "name", + "email", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/slock/whoami.js", + "sourceFile": "plugins/slock/whoami.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "message-read", + "description": "Read messages in a channel or thread. Thread form: \"#channel:msgIdOrShort\". Use --after seq|UUID for cursor.", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"fetch\", \"flexbox\", \"Array.prototype.map\")" + "help": "channelId UUID, \"#name\", or \"#channel:msgIdOrShort\"" + }, + { + "name": "after", + "type": "str", + "required": false, + "help": "Cursor: seq number or messageId UUID (exclusive)" + }, + { + "name": "before", + "type": "str", + "required": false, + "help": "seq to page before" }, { "name": "limit", "type": "int", - "default": 10, + "default": 50, "required": false, - "help": "Max results (1-50)" + "help": "Max messages" }, { - "name": "locale", + "name": "no-threads", + "type": "bool", + "default": false, + "required": false, + "help": "Skip /threads enrichment" + }, + { + "name": "server", "type": "str", - "default": "en-US", "required": false, - "help": "Doc locale (en-US default; de / es / fr / ja / ko / pt-BR / ru / zh-CN / zh-TW)" + "help": "Override active server" } ], "columns": [ - "rank", - "title", - "slug", - "locale", - "summary", - "url" - ], - "tags": [ - "search" + "id", + "seq", + "createdAt", + "senderName", + "content", + "threadChannelId", + "replyCount", + "unreadCount", + "lastReplyAt" ], "type": "js", - "modulePath": "plugins/mdn/search.js", - "sourceFile": "plugins/mdn/search.js" + "modulePath": "plugins/slock/message-read.js", + "sourceFile": "plugins/slock/message-read.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "medium", - "name": "feed", - "description": "Medium popular posts Feed", + "site": "slock", + "name": "message-search", + "description": "Search messages", "access": "read", - "domain": "medium.com", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "topic", + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search query" + }, + { + "name": "channel", "type": "str", - "default": "", "required": false, - "help": "Topic (for example technology, programming, ai)" + "help": "Restrict to a channel (UUID or #name)" }, { "name": "limit", "type": "int", - "default": 20, + "default": 50, "required": false, - "help": "Number of posts to return" + "help": "Max results" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "rank", - "title", - "author", - "date", - "readTime", - "claps" + "id", + "channelId", + "createdAt", + "senderName", + "content" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/medium/feed.js", - "sourceFile": "plugins/medium/feed.js", - "navigateBefore": "https://medium.com" + "modulePath": "plugins/slock/message-search.js", + "sourceFile": "plugins/slock/message-search.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "medium", - "name": "search", - "description": "Search Medium posts", - "access": "read", - "domain": "medium.com", + "site": "slock", + "name": "message-send", + "description": "Send a message to a channel, DM, or thread (content sent verbatim)", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "keyword", + "name": "target", "type": "str", "required": true, "positional": true, - "help": "Search keyword" + "help": "\"#channel\", \"#channel:msgIdOrShort\", \"dm:@name\", \"dm:\", or channel UUID" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "content", + "type": "str", + "required": true, + "positional": true, + "help": "Message body (sent verbatim, no marker)" + }, + { + "name": "dry-run", + "type": "bool", + "default": false, "required": false, - "help": "Number of posts to return" + "help": "Print the planned payload without sending" + }, + { + "name": "as-task", + "type": "bool", + "default": false, + "required": false, + "help": "Create the message as a task (asTask)" + }, + { + "name": "attach", + "type": "str", + "required": false, + "help": "Comma-separated attachmentId UUIDs (upload separately first)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server (slug or id)" } ], "columns": [ - "rank", - "title", - "author", - "date", - "readTime", - "claps", - "url" - ], - "tags": [ - "search" + "target", + "channelId", + "content", + "result", + "messageId" ], "type": "js", - "modulePath": "plugins/medium/search.js", - "sourceFile": "plugins/medium/search.js", - "navigateBefore": "https://medium.com" + "modulePath": "plugins/slock/message-send.js", + "sourceFile": "plugins/slock/message-send.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "medium", - "name": "tag", - "description": "Latest Medium articles tagged with a given keyword (RSS feed)", - "access": "read", - "domain": "medium.com", - "strategy": "public", - "browser": false, + "site": "slock", + "name": "reaction-add", + "description": "Add an emoji reaction to a message (POST /messages/:id/reactions). Idempotent server-side.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "tag", + "name": "messageId", "type": "str", "required": true, "positional": true, - "help": "Lowercase tag slug (e.g. \"programming\", \"machine-learning\")" + "help": "Full messageId UUID (short ids rejected)" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "emoji", + "type": "str", + "required": true, + "positional": true, + "help": "A single unicode emoji, e.g. 👍" + }, + { + "name": "server", + "type": "str", "required": false, - "help": "Max articles (1-25 — single RSS page)" + "help": "Override active server" } ], "columns": [ - "rank", - "title", - "author", - "description", - "categories", - "published", - "url" + "messageId", + "emoji", + "result" ], "type": "js", - "modulePath": "plugins/medium/tag.js", - "sourceFile": "plugins/medium/tag.js" + "modulePath": "plugins/slock/reaction-add.js", + "sourceFile": "plugins/slock/reaction-add.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "medium", - "name": "user", - "description": "Get Medium user posts", - "access": "read", - "domain": "medium.com", + "site": "slock", + "name": "reaction-remove", + "description": "Remove your emoji reaction from a message (DELETE /messages/:id/reactions).", + "access": "write", + "domain": "app.slock.ai", "strategy": "cookie", "browser": true, "args": [ { - "name": "username", + "name": "messageId", "type": "str", "required": true, "positional": true, - "help": "Medium username(for example @username or username)" + "help": "Full messageId UUID (short ids rejected)" }, { - "name": "limit", - "type": "int", - "default": 20, + "name": "emoji", + "type": "str", + "required": true, + "positional": true, + "help": "The unicode emoji to remove, e.g. 👍" + }, + { + "name": "server", + "type": "str", "required": false, - "help": "Number of posts to return" + "help": "Override active server" } ], "columns": [ - "rank", - "title", - "date", - "readTime", - "claps", - "url" + "messageId", + "emoji", + "result" ], "type": "js", - "modulePath": "plugins/medium/user.js", - "sourceFile": "plugins/medium/user.js", - "navigateBefore": "https://medium.com" + "modulePath": "plugins/slock/reaction-remove.js", + "sourceFile": "plugins/slock/reaction-remove.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "mercury", - "name": "check-login", - "description": "Open Mercury reimbursements and report whether the active browser profile is logged in", + "site": "slock", + "name": "server-list", + "description": "List slock servers you belong to; marks active per localStorage slug", "access": "read", - "example": "webcmd --profile mercury check-login -f json", - "domain": "app.mercury.com", - "strategy": "ui", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, "args": [], "columns": [ - "status", - "loggedIn", - "url", - "hasSubmitExpense", - "hasReimbursements", - "title" + "id", + "slug", + "name", + "active" ], "type": "js", - "modulePath": "plugins/mercury/check-login.js", - "sourceFile": "plugins/mercury/check-login.js", - "navigateBefore": false, + "modulePath": "plugins/slock/server-list.js", + "sourceFile": "plugins/slock/server-list.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "mercury", - "name": "reimbursement-draft", - "description": "Create a Mercury reimbursement draft from a local receipt, correct OCR fields, and stop at Review", + "site": "slock", + "name": "server-use", + "description": "Set the active slock server (writes localStorage.slock_last_server_slug)", "access": "write", - "example": "webcmd --profile mercury reimbursement-draft --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", - "domain": "app.mercury.com", - "strategy": "ui", + "domain": "app.slock.ai", + "strategy": "cookie", "browser": true, "args": [ { - "name": "receipt", + "name": "input", "type": "str", "required": true, - "help": "Local receipt/proof file path", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp", - "application/pdf" - ], - "maxBytes": 26214400 - } - }, + "positional": true, + "help": "server slug, \"#slug\", or UUID id" + } + ], + "columns": [ + "id", + "slug", + "name", + "written" + ], + "type": "js", + "modulePath": "plugins/slock/server-use.js", + "sourceFile": "plugins/slock/server-use.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-claim", + "description": "Claim a chat task (PATCH /tasks/:id/claim). Requires full task UUID (= message id).", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "amount", + "name": "taskId", "type": "str", "required": true, - "help": "Original-currency amount, e.g. 140.00" + "positional": true, + "help": "Full task UUID (= message id; short ids rejected)" }, { - "name": "currency", + "name": "server", "type": "str", - "default": "CNY", "required": false, - "help": "Original currency code" - }, + "help": "Override active server" + } + ], + "columns": [ + "taskId", + "taskStatus", + "assigneeId", + "taskNumber" + ], + "type": "js", + "modulePath": "plugins/slock/task-claim.js", + "sourceFile": "plugins/slock/task-claim.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-convert", + "description": "Convert a message into a chat task (POST /tasks/convert-message). Accepts a message UUID or \"#channel:shortId\".", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "date", + "name": "messageId", "type": "str", "required": true, - "help": "Expense date as YYYY-MM-DD" + "positional": true, + "help": "Full message UUID, or \"#channel:shortId\" (short id expanded via /messages/context)" }, { - "name": "merchant", + "name": "server", "type": "str", - "required": true, - "help": "Merchant shown on the reimbursement" - }, + "required": false, + "help": "Override active server" + } + ], + "columns": [ + "id", + "taskNumber", + "title", + "taskStatus", + "channelId" + ], + "type": "js", + "modulePath": "plugins/slock/task-convert.js", + "sourceFile": "plugins/slock/task-convert.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-create", + "description": "Create a task in a channel (single title; batch 1-50 is server-supported but client surface is single — see backlog R4).", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "category", + "name": "channel", "type": "str", - "default": "Marketing & Advertising", - "required": false, - "help": "Mercury expense category" + "required": true, + "positional": true, + "help": "channelId UUID or #name" }, { - "name": "notes", + "name": "title", "type": "str", "required": true, - "help": "Business purpose / reimbursement notes" + "positional": true, + "help": "Task title (single; batch TODO via R4)" }, { - "name": "ocr-wait-seconds", + "name": "desc", "type": "str", - "default": "8", "required": false, - "help": "Seconds to wait after receipt upload before correcting OCR-overwritten fields" + "help": "Optional description body for the task" }, { - "name": "close-after-review", - "type": "boolean", - "default": false, + "name": "server", + "type": "str", "required": false, - "help": "Close the Review dialog after verification; final Submit is still never clicked" + "help": "Override active server" } ], "columns": [ - "status", - "url", - "receipt", - "uploaded", - "fieldsTouched", - "reviewReady", - "submitBlocked", - "warnings" + "id", + "taskNumber", + "title", + "taskStatus", + "channelId" ], "type": "js", - "modulePath": "plugins/mercury/reimbursement-draft.js", - "sourceFile": "plugins/mercury/reimbursement-draft.js", - "navigateBefore": false, + "modulePath": "plugins/slock/task-create.js", + "sourceFile": "plugins/slock/task-create.js", + "navigateBefore": "https://app.slock.ai", "siteSession": "persistent" }, { - "site": "mercury", - "name": "reimbursement-plan", - "description": "Validate Mercury reimbursement inputs and print the draft plan without opening a browser", - "access": "read", - "example": "webcmd mercury reimbursement-plan --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", - "strategy": "local", - "browser": false, + "site": "slock", + "name": "task-delete", + "description": "Delete a chat task (DELETE /tasks/:taskId). Requires --confirm — destructive, irreversible.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "receipt", + "name": "taskId", "type": "str", "required": true, - "help": "Local receipt/proof file path" + "positional": true, + "help": "Full task UUID (= message id; short ids rejected)" }, { - "name": "amount", - "type": "str", - "required": true, - "help": "Original-currency amount, e.g. 140.00" + "name": "confirm", + "type": "bool", + "default": false, + "required": false, + "help": "Required acknowledgement: deletion is irreversible" }, { - "name": "currency", + "name": "server", "type": "str", - "default": "CNY", "required": false, - "help": "Original currency code" - }, + "help": "Override active server" + } + ], + "columns": [ + "taskId", + "deleted" + ], + "type": "js", + "modulePath": "plugins/slock/task-delete.js", + "sourceFile": "plugins/slock/task-delete.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-get", + "description": "Fetch a task by channel + taskNumber (GET /tasks/channel/:channelId/number/:taskNumber).", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "date", + "name": "channel", "type": "str", "required": true, - "help": "Expense date as YYYY-MM-DD" + "positional": true, + "help": "channelId UUID or #name" }, { - "name": "merchant", + "name": "number", "type": "str", "required": true, - "help": "Merchant shown on the reimbursement" + "positional": true, + "help": "taskNumber (per-channel integer, as shown in \"task #N\")" }, { - "name": "category", + "name": "server", "type": "str", - "default": "Marketing & Advertising", "required": false, - "help": "Mercury expense category" - }, + "help": "Override active server" + } + ], + "columns": [ + "id", + "taskNumber", + "title", + "taskStatus", + "assigneeId" + ], + "type": "js", + "modulePath": "plugins/slock/task-get.js", + "sourceFile": "plugins/slock/task-get.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "task-list", + "description": "List tasks (chat tasks = messages with task fields) attached to a channel. Optional --status filter.", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "notes", + "name": "channel", "type": "str", "required": true, - "help": "Business purpose / reimbursement notes" + "positional": true, + "help": "channelId UUID or #name" }, { - "name": "ocr-wait-seconds", + "name": "status", "type": "str", - "default": "8", "required": false, - "help": "Seconds the draft command waits after receipt upload before correcting OCR fields" + "help": "Filter by status: todo|in_progress|in_review|done|closed" }, { - "name": "close-after-review", - "type": "boolean", - "default": false, + "name": "server", + "type": "str", "required": false, - "help": "For draft command: close the Review dialog after verification" + "help": "Override active server" } ], "columns": [ - "status", - "receipt", - "amount", - "currency", - "date", - "merchant", - "category", - "notes", - "safety" + "id", + "taskNumber", + "title", + "taskStatus", + "assigneeId" ], "type": "js", - "modulePath": "plugins/mercury/reimbursement-plan.js", - "sourceFile": "plugins/mercury/reimbursement-plan.js" + "modulePath": "plugins/slock/task-list.js", + "sourceFile": "plugins/slock/task-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "npm", - "name": "downloads", - "description": "Daily download counts for an npm package over a window", + "site": "slock", + "name": "task-list-server", + "description": "List tasks across all channels in the active server (GET /tasks/server). Optional --status filter.", "access": "read", - "domain": "api.npmjs.org", - "strategy": "public", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "name", + "name": "status", "type": "str", - "required": true, - "positional": true, - "help": "npm package name (e.g. \"react\", \"@vercel/og\")" + "required": false, + "help": "Filter by status: todo|in_progress|in_review|done|closed" }, { - "name": "period", + "name": "server", "type": "str", - "default": "last-week", "required": false, - "help": "last-day / last-week / last-month / last-year, or YYYY-MM-DD:YYYY-MM-DD" + "help": "Override active server" } ], "columns": [ - "rank", - "package", - "day", - "downloads" + "id", + "taskNumber", + "title", + "taskStatus", + "channelId", + "assigneeId" ], "type": "js", - "modulePath": "plugins/npm/downloads.js", - "sourceFile": "plugins/npm/downloads.js" + "modulePath": "plugins/slock/task-list-server.js", + "sourceFile": "plugins/slock/task-list-server.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "npm", - "name": "package", - "description": "Single npm package metadata (latest version, license, homepage, repository). Use `npm downloads` for stats.", - "access": "read", - "domain": "registry.npmjs.org", - "strategy": "public", - "browser": false, + "site": "slock", + "name": "task-status", + "description": "Set a task's status (PATCH /tasks/:taskId/status, body {status}). One of todo|in_progress|in_review|done|closed.", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "name", + "name": "taskId", "type": "str", "required": true, "positional": true, - "help": "npm package name (e.g. \"react\", \"@vercel/og\")" + "help": "Full task UUID (= message id; short ids rejected)" + }, + { + "name": "status", + "type": "str", + "required": true, + "positional": true, + "help": "One of: todo|in_progress|in_review|done|closed" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "name", - "latestVersion", - "description", - "license", - "homepage", - "repository", - "bugs", - "maintainers", - "keywords", - "created", - "modified", - "url" + "taskId", + "taskStatus", + "assigneeId", + "taskNumber" ], "type": "js", - "modulePath": "plugins/npm/package.js", - "sourceFile": "plugins/npm/package.js" + "modulePath": "plugins/slock/task-status.js", + "sourceFile": "plugins/slock/task-status.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "npm", - "name": "search", - "description": "Search the public npm registry by keyword", - "access": "read", - "domain": "registry.npmjs.org", - "strategy": "public", - "browser": false, + "site": "slock", + "name": "task-unclaim", + "description": "Release ownership of a chat task (PATCH /tasks/:id/unclaim).", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "taskId", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"react\", \"graphql client\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-250)" - } - ], - "columns": [ - "rank", - "name", - "version", - "description", - "weeklyDownloads", - "dependents", - "license", - "publisher", - "updated", - "url" + "help": "Full task UUID (= message id; short ids rejected)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" + } ], - "tags": [ - "search" + "columns": [ + "taskId", + "taskStatus", + "assigneeId", + "taskNumber" ], "type": "js", - "modulePath": "plugins/npm/search.js", - "sourceFile": "plugins/npm/search.js" + "modulePath": "plugins/slock/task-unclaim.js", + "sourceFile": "plugins/slock/task-unclaim.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "nuget", - "name": "package", - "description": "Full NuGet package version history (catalogEntry per release)", - "access": "read", - "domain": "api.nuget.org", - "strategy": "public", - "browser": false, + "site": "slock", + "name": "thread-done", + "description": "Mark a thread as done / hide it from the active list (POST /channels/threads/done)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "threadChannelId", "type": "str", "required": true, "positional": true, - "help": "NuGet package id (e.g. \"Newtonsoft.Json\", case-insensitive)" + "help": "Thread channel UUID (from thread-list / message-read)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "rank", - "id", - "version", - "title", - "authors", - "tags", - "language", - "licenseExpression", - "projectUrl", - "published", - "listed", - "url" + "threadChannelId", + "result" ], "type": "js", - "modulePath": "plugins/nuget/package.js", - "sourceFile": "plugins/nuget/package.js" + "modulePath": "plugins/slock/thread-done.js", + "sourceFile": "plugins/slock/thread-done.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "nuget", - "name": "search", - "description": "Search NuGet packages by keyword", - "access": "read", - "domain": "api.nuget.org", - "strategy": "public", - "browser": false, + "site": "slock", + "name": "thread-follow", + "description": "Follow the thread on a parent message (POST /channels/threads/follow)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "parentMessageId", "type": "str", "required": true, "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max packages (1-1000)" + "help": "Full parent messageId UUID (short ids rejected)" }, { - "name": "prerelease", - "type": "boolean", - "default": false, + "name": "server", + "type": "str", "required": false, - "help": "Include prerelease versions" + "help": "Override active server" } ], "columns": [ - "rank", - "id", - "version", - "title", - "description", - "authors", - "tags", - "totalDownloads", - "verified", - "projectUrl", - "url" - ], - "tags": [ - "search" + "parentMessageId", + "threadChannelId", + "result" ], "type": "js", - "modulePath": "plugins/nuget/search.js", - "sourceFile": "plugins/nuget/search.js" + "modulePath": "plugins/slock/thread-follow.js", + "sourceFile": "plugins/slock/thread-follow.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "nvd", - "name": "cve", - "description": "NIST NVD CVE detail (description, CVSS, CWE, KEV flag)", + "site": "slock", + "name": "thread-list", + "description": "List followed threads in the active server (GET /channels/threads/followed)", "access": "read", - "domain": "services.nvd.nist.gov", - "strategy": "public", - "browser": false, + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "server", "type": "str", - "required": true, - "positional": true, - "help": "CVE identifier (e.g. \"CVE-2021-44228\")" + "required": false, + "help": "Override active server" } ], "columns": [ - "id", - "published", - "lastModified", - "vulnStatus", - "baseScore", - "severity", - "attackVector", - "cwe", - "kevAdded", - "description", - "url" + "threadChannelId", + "parentMessageId", + "parentChannelName", + "unreadCount", + "replyCount", + "lastReplyAt" ], "type": "js", - "modulePath": "plugins/nvd/cve.js", - "sourceFile": "plugins/nvd/cve.js" + "modulePath": "plugins/slock/thread-list.js", + "sourceFile": "plugins/slock/thread-list.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "oeis", - "name": "search", - "description": "Search OEIS sequences by keyword or numeric pattern", - "access": "read", - "domain": "oeis.org", - "strategy": "public", - "browser": false, + "site": "slock", + "name": "thread-undone", + "description": "Restore a done thread to the active list (POST /channels/threads/undone)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "threadChannelId", "type": "str", "required": true, "positional": true, - "help": "Search keyword or comma-separated terms (e.g. \"fibonacci\", \"1,1,2,3,5,8\")" + "help": "Thread channel UUID (from thread-list / message-read)" }, { - "name": "limit", - "type": "int", - "default": 10, + "name": "server", + "type": "str", "required": false, - "help": "Max sequences (1-100)" + "help": "Override active server" } ], "columns": [ - "rank", - "id", - "name", - "keywords", - "preview", - "author", - "created", - "url" - ], - "tags": [ - "search" + "threadChannelId", + "result" ], "type": "js", - "modulePath": "plugins/oeis/search.js", - "sourceFile": "plugins/oeis/search.js" + "modulePath": "plugins/slock/thread-undone.js", + "sourceFile": "plugins/slock/thread-undone.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "oeis", - "name": "sequence", - "description": "Full OEIS sequence detail by A-number (terms, name, keywords, formula counts)", - "access": "read", - "domain": "oeis.org", - "strategy": "public", - "browser": false, + "site": "slock", + "name": "thread-unfollow", + "description": "Stop following a thread (POST /channels/threads/unfollow)", + "access": "write", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "threadChannelId", "type": "str", "required": true, "positional": true, - "help": "OEIS sequence id (e.g. \"A000045\" for Fibonacci)" + "help": "Thread channel UUID (from thread-list / message-read)" + }, + { + "name": "server", + "type": "str", + "required": false, + "help": "Override active server" } ], "columns": [ - "id", + "threadChannelId", + "result" + ], + "type": "js", + "modulePath": "plugins/slock/thread-unfollow.js", + "sourceFile": "plugins/slock/thread-unfollow.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" + }, + { + "site": "slock", + "name": "unread-summary", + "description": "Global unread counts across every server you belong to.", + "access": "read", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "serverId", + "slug", "name", - "keywords", - "preview", - "termCount", - "offset", - "author", - "created", - "revision", - "commentCount", - "formulaCount", - "referenceCount", - "xrefCount", - "linkCount", - "url" + "unreadCount" ], "type": "js", - "modulePath": "plugins/oeis/sequence.js", - "sourceFile": "plugins/oeis/sequence.js" + "modulePath": "plugins/slock/unread-summary.js", + "sourceFile": "plugins/slock/unread-summary.js", + "navigateBefore": "https://app.slock.ai", + "siteSession": "persistent" }, { - "site": "openalex", - "name": "search", - "description": "Search OpenAlex Works (papers, books, preprints) by keyword", + "site": "slock", + "name": "whoami", + "description": "Show the current logged-in slock account", "access": "read", - "domain": "api.openalex.org", - "strategy": "public", + "domain": "app.slock.ai", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "id", + "name", + "email" + ], + "type": "js", + "modulePath": "plugins/slock/whoami.js", + "sourceFile": "plugins/slock/whoami.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "spotify", + "name": "auth", + "description": "Authenticate with Spotify (OAuth — run once)", + "access": "write", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" + }, + { + "site": "spotify", + "name": "next", + "description": "Skip to next track", + "access": "write", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" + }, + { + "site": "spotify", + "name": "pause", + "description": "Pause playback", + "access": "write", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "status" + ], + "type": "js", + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" + }, + { + "site": "spotify", + "name": "play", + "description": "Resume playback or search and play a track/artist", + "access": "write", + "strategy": "local", "browser": false, "args": [ { "name": "query", "type": "str", - "required": true, - "positional": true, - "help": "Search text (e.g. \"transformers\", \"open access scholarly\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, + "default": "", "required": false, - "help": "Max works (1-200, single OpenAlex page)" + "positional": true, + "help": "Track or artist to play (optional)" } ], "columns": [ - "rank", - "id", - "title", - "year", - "citations", - "firstAuthor", - "venue", - "openAccess", - "type", - "doi", - "url" - ], - "tags": [ - "search" + "track", + "artist", + "status" ], "type": "js", - "modulePath": "plugins/openalex/search.js", - "sourceFile": "plugins/openalex/search.js" + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" }, { - "site": "openalex", - "name": "work", - "description": "Fetch a single OpenAlex Work (paper / preprint / book) — metadata + abstract", - "access": "read", - "domain": "api.openalex.org", - "strategy": "public", + "site": "spotify", + "name": "prev", + "description": "Skip to previous track", + "access": "write", + "strategy": "local", "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "OpenAlex Work id (\"W2741809807\"), DOI (\"10.7717/peerj.4375\"), or full URL" - } - ], + "args": [], "columns": [ - "id", - "title", - "type", - "year", - "date", - "language", - "authors", - "venue", - "citations", - "openAccess", - "openAccessUrl", - "referencedCount", - "doi", - "abstract", - "url" + "status" ], "type": "js", - "modulePath": "plugins/openalex/work.js", - "sourceFile": "plugins/openalex/work.js" + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" }, { - "site": "openfda", - "name": "drug-label", - "description": "Search FDA-approved drug labels (brand or generic name)", - "access": "read", - "domain": "fda.gov", - "strategy": "public", + "site": "spotify", + "name": "queue", + "description": "Add a track to the playback queue", + "access": "write", + "strategy": "local", "browser": false, "args": [ { @@ -14160,345 +22984,278 @@ "type": "str", "required": true, "positional": true, - "help": "Brand or generic drug name (e.g. \"aspirin\", \"lisinopril\")" - }, - { - "name": "limit", - "type": "int", - "default": 5, - "required": false, - "help": "Max rows (1-25, default 5; openFDA caps anonymous tier at 25/page)" + "help": "Track to add to queue" } ], "columns": [ - "rank", - "id", - "brandName", - "genericName", - "manufacturer", - "productType", - "route", - "productNdc", - "pharmClass", - "purpose", - "indications", - "warnings", - "dosage", - "effectiveTime" + "track", + "artist", + "status" ], "type": "js", - "modulePath": "plugins/openfda/drug-label.js", - "sourceFile": "plugins/openfda/drug-label.js" + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" }, { - "site": "openfda", - "name": "food-recall", - "description": "FDA food recall and enforcement actions (most recent first)", - "access": "read", - "domain": "fda.gov", - "strategy": "public", + "site": "spotify", + "name": "repeat", + "description": "Set repeat mode (off / track / context)", + "access": "write", + "strategy": "local", "browser": false, "args": [ { - "name": "query", - "type": "str", - "required": false, - "help": "Free-text Lucene query (e.g. \"salmonella\", \"listeria\"); default: all recent recalls" - }, - { - "name": "status", - "type": "str", - "required": false, - "help": "Filter by status: \"Ongoing\", \"Completed\", \"Terminated\"" - }, - { - "name": "classification", + "name": "mode", "type": "str", + "default": "context", "required": false, - "help": "Filter by class: \"Class I\" (most serious), \"Class II\", \"Class III\"" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max rows (1-100, default 10; openFDA caps anonymous tier at 100/page)" + "positional": true, + "help": "off / track / context", + "choices": [ + "off", + "track", + "context" + ] } ], "columns": [ - "rank", - "recallNumber", - "status", - "classification", - "voluntary", - "recallingFirm", - "city", - "state", - "country", - "productDescription", - "reasonForRecall", - "productQuantity", - "distributionPattern", - "reportDate", - "recallInitiationDate", - "terminationDate" + "repeat" ], "type": "js", - "modulePath": "plugins/openfda/food-recall.js", - "sourceFile": "plugins/openfda/food-recall.js" + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" }, { - "site": "openreview", - "name": "author", - "description": "List OpenReview submissions by an author profile id (newest first)", + "site": "spotify", + "name": "search", + "description": "Search for tracks", "access": "read", - "domain": "openreview.net", - "strategy": "public", + "strategy": "local", "browser": false, "args": [ { - "name": "profile", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "OpenReview profile id (e.g. \"~Yoshua_Bengio1\"). Find it on the author profile URL on openreview.net." + "help": "Search query" }, { "name": "limit", "type": "int", - "default": 50, + "default": 10, "required": false, - "help": "Max submissions (1-1000)" + "help": "Number of results (default: 10)" } ], "columns": [ - "rank", - "id", - "title", - "authors", - "venue", - "pdate", - "url" + "track", + "artist", + "album", + "uri" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/openreview/author.js", - "sourceFile": "plugins/openreview/author.js" + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" }, { - "site": "openreview", - "name": "paper", - "description": "Show full metadata for a single OpenReview paper", - "access": "read", - "domain": "openreview.net", - "strategy": "public", + "site": "spotify", + "name": "shuffle", + "description": "Toggle shuffle on/off", + "access": "write", + "strategy": "local", "browser": false, "args": [ { - "name": "id", + "name": "state", "type": "str", - "required": true, + "default": "on", + "required": false, "positional": true, - "help": "OpenReview note id (e.g. \"5sRnsubyAK\")" + "help": "on or off", + "choices": [ + "on", + "off" + ] } ], "columns": [ - "id", - "title", - "authors", - "keywords", - "venue", - "venueid", - "primary_area", - "abstract", - "pdate", - "pdf", - "url" + "shuffle" + ], + "type": "js", + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" + }, + { + "site": "spotify", + "name": "status", + "description": "Show current playback status", + "access": "read", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "track", + "artist", + "album", + "status", + "progress" ], "type": "js", - "modulePath": "plugins/openreview/paper.js", - "sourceFile": "plugins/openreview/paper.js" + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" }, { - "site": "openreview", - "name": "reviews", - "description": "Show full review thread (paper + reviews + decisions) for an OpenReview forum", - "access": "read", - "domain": "openreview.net", - "strategy": "public", + "site": "spotify", + "name": "volume", + "description": "Set playback volume (0-100)", + "access": "write", + "strategy": "local", "browser": false, "args": [ { - "name": "forum", - "type": "str", + "name": "level", + "type": "int", + "default": 50, "required": true, "positional": true, - "help": "OpenReview forum id (same as paper id)" - }, - { - "name": "max-length", - "type": "int", - "default": 4000, - "required": false, - "help": "Per-row text truncation (min 200)" + "help": "Volume 0–100" } ], "columns": [ - "type", - "author", - "rating", - "confidence", - "text" + "volume" ], "type": "js", - "modulePath": "plugins/openreview/reviews.js", - "sourceFile": "plugins/openreview/reviews.js" + "modulePath": "plugins/spotify/spotify.js", + "sourceFile": "plugins/spotify/spotify.js" }, { - "site": "openreview", - "name": "search", - "description": "Search OpenReview papers by free-text query", + "site": "stackoverflow", + "name": "bounties", + "description": "Active bounties on Stack Overflow", "access": "read", - "domain": "openreview.net", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"diffusion model\")" - }, { "name": "limit", "type": "int", - "default": 25, + "default": 10, "required": false, - "help": "Max results (max 50)" + "help": "Max number of results" } ], "columns": [ "rank", "id", + "bounty", "title", - "authors", - "venue", - "pdate", + "score", + "answers", + "views", + "is_answered", + "tags", + "author", + "creation_date", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/openreview/search.js", - "sourceFile": "plugins/openreview/search.js" + "modulePath": "plugins/stackoverflow/bounties.js", + "sourceFile": "plugins/stackoverflow/bounties.js" }, { - "site": "openreview", - "name": "venue", - "description": "List papers at an OpenReview venue (e.g. \"ICLR 2024 oral\" or full invitation id)", + "site": "stackoverflow", + "name": "hot", + "description": "Hot Stack Overflow questions", "access": "read", - "domain": "openreview.net", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ - { - "name": "venue", - "type": "str", - "required": true, - "positional": true, - "help": "Venue name (\"ICLR 2024 oral\") or invitation (\"ICLR.cc/2025/Conference/-/Submission\")" - }, { "name": "limit", "type": "int", - "default": 25, - "required": false, - "help": "Max results (max 200)" - }, - { - "name": "offset", - "type": "int", - "default": 0, + "default": 10, "required": false, - "help": "Pagination offset" + "help": "Max number of results" } ], "columns": [ "rank", "id", "title", - "authors", - "keywords", - "primary_area", - "pdate", - "pdf", + "score", + "answers", + "views", + "is_answered", + "tags", + "author", + "creation_date", "url" ], "type": "js", - "modulePath": "plugins/openreview/venue.js", - "sourceFile": "plugins/openreview/venue.js" + "modulePath": "plugins/stackoverflow/hot.js", + "sourceFile": "plugins/stackoverflow/hot.js" }, { - "site": "osv", - "name": "query", - "description": "OSV.dev vulnerabilities affecting a package (optionally pinned to a version)", + "site": "stackoverflow", + "name": "read", + "description": "Read a Stack Overflow question with answers and comments", "access": "read", - "domain": "osv.dev", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ { - "name": "package", - "type": "string", + "name": "id", + "type": "str", "required": true, "positional": true, - "help": "Package name (e.g. \"lodash\", \"django\")" + "help": "Stack Overflow question id (numeric, e.g. 79935770)" }, { - "name": "ecosystem", - "type": "string", - "required": true, - "help": "OSV ecosystem (npm / PyPI / Go / Maven / NuGet / RubyGems / crates.io / Packagist / ...)" + "name": "answers-limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max answers to include (1-100; accepted answer always included first)" }, { - "name": "version", - "type": "string", + "name": "comments-limit", + "type": "int", + "default": 5, "required": false, - "help": "Pin to a specific version (e.g. \"4.17.20\"); omit for all known vulns" + "help": "Max comments per question/answer (1-100)" }, { - "name": "limit", + "name": "max-length", "type": "int", - "default": 30, + "default": 4000, "required": false, - "help": "Max rows to return (1-200)" + "help": "Max characters per body / answer / comment (min 100)" } ], "columns": [ - "rank", - "id", - "summary", - "severity", - "aliases", - "published", - "modified", - "affectedPackages", - "url" - ], - "tags": [ - "search" + "type", + "author", + "score", + "accepted", + "text" ], "type": "js", - "modulePath": "plugins/osv/query.js", - "sourceFile": "plugins/osv/query.js" + "modulePath": "plugins/stackoverflow/read.js", + "sourceFile": "plugins/stackoverflow/read.js" }, { - "site": "osv", - "name": "vulnerability", - "description": "Single OSV.dev vulnerability detail (severity, affected packages, CVE/GHSA aliases)", + "site": "stackoverflow", + "name": "related", + "description": "List Stack Overflow questions related to a given question id.", "access": "read", - "domain": "osv.dev", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ @@ -14507,1772 +23264,1707 @@ "type": "string", "required": true, "positional": true, - "help": "OSV vulnerability id (e.g. \"GHSA-29mw-wpgm-hmr9\", \"CVE-2020-28500\")" - } - ], - "columns": [ - "id", - "summary", - "severity", - "aliases", - "published", - "modified", - "affectedPackages", - "cwes", - "referenceCount", - "url" - ], - "type": "js", - "modulePath": "plugins/osv/vulnerability.js", - "sourceFile": "plugins/osv/vulnerability.js" - }, - { - "site": "packagist", - "name": "package", - "description": "Fetch a Packagist package's metadata (version, downloads, license, repo, GitHub stars)", - "access": "read", - "domain": "packagist.org", - "strategy": "public", - "browser": false, - "args": [ + "help": "Stack Overflow question id (numeric, e.g. 79935770)." + }, { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Composer package \"/\" (e.g. \"symfony/console\", \"monolog/monolog\")" + "name": "sort", + "type": "string", + "default": "rank", + "required": false, + "help": "Sort key: rank, activity, votes, creation (rank = SO relevance default)." + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max related questions (1-100)." } - ], - "columns": [ - "package", - "version", - "releasedAt", - "license", - "description", - "repository", - "githubStars", - "favers", - "downloads", - "monthlyDownloads", - "dailyDownloads", + ], + "columns": [ + "rank", + "id", + "title", + "score", + "answers", + "views", + "isAnswered", + "tags", + "author", + "createdAt", + "lastActivityAt", "url" ], "type": "js", - "modulePath": "plugins/packagist/package.js", - "sourceFile": "plugins/packagist/package.js" + "modulePath": "plugins/stackoverflow/related.js", + "sourceFile": "plugins/stackoverflow/related.js" }, { - "site": "packagist", + "site": "stackoverflow", "name": "search", - "description": "Search Packagist (PHP / Composer) packages by keyword", + "description": "Search Stack Overflow questions", "access": "read", - "domain": "packagist.org", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ { "name": "query", - "type": "str", + "type": "string", "required": true, "positional": true, - "help": "Search keyword (e.g. \"symfony\", \"laravel http\")" + "help": "Search query" }, { "name": "limit", "type": "int", - "default": 30, + "default": 10, "required": false, - "help": "Max packages (1-100, single Packagist page)" + "help": "Max number of results" } ], "columns": [ "rank", - "package", - "description", - "downloads", - "favers", - "repository", + "id", + "title", + "score", + "answers", + "views", + "is_answered", + "tags", + "author", + "creation_date", "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/packagist/search.js", - "sourceFile": "plugins/packagist/search.js" + "modulePath": "plugins/stackoverflow/search.js", + "sourceFile": "plugins/stackoverflow/search.js" }, { - "site": "paperreview", - "name": "feedback", - "description": "Submit feedback for a paperreview.ai review token", - "access": "write", - "domain": "paperreview.ai", + "site": "stackoverflow", + "name": "tag", + "description": "List Stack Overflow questions tagged with a given tag (most active first).", + "access": "read", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ { - "name": "token", - "type": "str", + "name": "tag", + "type": "string", "required": true, "positional": true, - "help": "Review token returned by paperreview.ai" - }, - { - "name": "helpfulness", - "type": "int", - "required": true, - "help": "Helpfulness score from 1 to 5" - }, - { - "name": "critical-error", - "type": "str", - "required": true, - "help": "Whether the review contains a critical error", - "choices": [ - "yes", - "no" - ] - }, - { - "name": "actionable-suggestions", - "type": "str", - "required": true, - "help": "Whether the review contains actionable suggestions", - "choices": [ - "yes", - "no" - ] + "help": "Tag slug (e.g. python, rust, typescript)." }, { - "name": "additional-comments", - "type": "str", + "name": "sort", + "type": "string", + "default": "activity", "required": false, - "help": "Optional free-text feedback" + "help": "Sort key: activity, votes, creation, hot, week, month" }, { - "name": "timeout", + "name": "limit", "type": "int", - "default": 30, + "default": 20, "required": false, - "help": "Max seconds for the overall command (default: 30)" + "help": "Max questions to return (max 100)." } ], "columns": [ - "status", - "token", - "helpfulness", - "critical_error", - "actionable_suggestions", - "message" + "rank", + "id", + "title", + "score", + "answers", + "views", + "isAnswered", + "tags", + "author", + "createdAt", + "lastActivityAt", + "url" ], "type": "js", - "modulePath": "plugins/paperreview/feedback.js", - "sourceFile": "plugins/paperreview/feedback.js" + "modulePath": "plugins/stackoverflow/tag.js", + "sourceFile": "plugins/stackoverflow/tag.js" }, { - "site": "paperreview", - "name": "review", - "description": "Fetch a paperreview.ai review by token", + "site": "stackoverflow", + "name": "unanswered", + "description": "Top voted unanswered questions on Stack Overflow", "access": "read", - "domain": "paperreview.ai", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ { - "name": "token", - "type": "str", - "required": true, - "positional": true, - "help": "Review token returned by paperreview.ai" - }, - { - "name": "timeout", + "name": "limit", "type": "int", - "default": 30, + "default": 10, "required": false, - "help": "Max seconds for the overall command (default: 30)" + "help": "Max number of results" } ], "columns": [ - "status", + "rank", + "id", "title", - "venue", - "numerical_score", - "has_feedback", - "review_url" + "score", + "answers", + "views", + "tags", + "author", + "creation_date", + "url" ], "type": "js", - "modulePath": "plugins/paperreview/review.js", - "sourceFile": "plugins/paperreview/review.js" + "modulePath": "plugins/stackoverflow/unanswered.js", + "sourceFile": "plugins/stackoverflow/unanswered.js" }, { - "site": "paperreview", - "name": "submit", - "description": "Submit a PDF to paperreview.ai for review", - "access": "write", - "domain": "paperreview.ai", + "site": "stackoverflow", + "name": "user", + "description": "Find Stack Overflow users by display name (highest reputation first).", + "access": "read", + "domain": "stackoverflow.com", "strategy": "public", "browser": false, "args": [ { - "name": "pdf", - "type": "str", + "name": "name", + "type": "string", "required": true, "positional": true, - "help": "Path to the paper PDF" - }, - { - "name": "email", - "type": "str", - "required": true, - "help": "Email address for the submission" - }, - { - "name": "venue", - "type": "str", - "required": false, - "help": "Optional target venue such as ICLR or NeurIPS" - }, - { - "name": "dry-run", - "type": "bool", - "default": false, - "required": false, - "help": "Validate the input and stop before remote submission" - }, - { - "name": "prepare-only", - "type": "bool", - "default": false, - "required": false, - "help": "Request an upload slot but stop before uploading the PDF" + "help": "Display name (or substring) to search." }, { - "name": "timeout", + "name": "limit", "type": "int", - "default": 120, - "required": false, - "help": "Max seconds for the overall command (default: 120)" - } - ], - "columns": [ - "status", - "file", - "email", - "venue", - "token", - "review_url", - "message" - ], - "type": "js", - "modulePath": "plugins/paperreview/submit.js", - "sourceFile": "plugins/paperreview/submit.js" - }, - { - "site": "pixiv", - "name": "detail", - "description": "View illustration details (tags, stats, URLs)", - "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Illustration ID" - } - ], - "columns": [ - "illust_id", - "title", - "author", - "type", - "pages", - "bookmarks", - "likes", - "views", - "tags", - "created", + "default": 10, + "required": false, + "help": "Max users to return (max 100)." + } + ], + "columns": [ + "userId", + "displayName", + "reputation", + "goldBadges", + "silverBadges", + "bronzeBadges", + "location", + "createdAt", + "lastAccessAt", "url" ], "type": "js", - "modulePath": "plugins/pixiv/detail.js", - "sourceFile": "plugins/pixiv/detail.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "plugins/stackoverflow/user.js", + "sourceFile": "plugins/stackoverflow/user.js" }, { - "site": "pixiv", - "name": "download", - "description": "Download illustration images from Pixiv", + "site": "steam", + "name": "app", + "description": "Steam storefront detail for a single app id", "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", - "browser": true, + "domain": "store.steampowered.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "illust-id", + "name": "id", "type": "str", "required": true, "positional": true, - "help": "Illustration ID" + "help": "Numeric Steam app id (e.g. \"620\" for Portal 2)" }, { - "name": "output", + "name": "currency", "type": "str", - "default": "./pixiv-downloads", + "default": "us", "required": false, - "help": "Output directory" + "help": "Storefront country code (e.g. us / cn / jp / de)" } ], "columns": [ - "index", + "id", + "name", "type", - "status", - "size" + "isFree", + "releaseDate", + "developers", + "publishers", + "price", + "currency", + "metacritic", + "recommendations", + "genres", + "categories", + "shortDescription", + "website", + "url" ], "type": "js", - "modulePath": "plugins/pixiv/download.js", - "sourceFile": "plugins/pixiv/download.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "plugins/steam/app.js", + "sourceFile": "plugins/steam/app.js" }, { - "site": "pixiv", - "name": "illusts", - "description": "List a Pixiv artist's illustrations", + "site": "steam", + "name": "search", + "description": "Search the Steam storefront by name keyword", "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", - "browser": true, + "domain": "store.steampowered.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "user-id", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Pixiv user ID" + "help": "Search keyword (e.g. \"portal\", \"stardew\")" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of results" + "help": "Max results (1-50)" + }, + { + "name": "currency", + "type": "str", + "default": "us", + "required": false, + "help": "Storefront country code (e.g. us / cn / jp / de)" } ], "columns": [ "rank", - "title", - "illust_id", - "pages", - "bookmarks", - "tags", - "created", + "id", + "name", + "price", + "currency", + "metascore", + "platforms", "url" ], - "type": "js", - "modulePath": "plugins/pixiv/illusts.js", - "sourceFile": "plugins/pixiv/illusts.js", - "navigateBefore": "https://www.pixiv.net" - }, - { - "site": "pixiv", - "name": "login", - "description": "Open pixiv login", - "access": "write", - "domain": "pixiv.net", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/pixiv/auth.js", - "sourceFile": "plugins/pixiv/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/steam/search.js", + "sourceFile": "plugins/steam/search.js" }, { - "site": "pixiv", - "name": "ranking", - "description": "Pixiv illustration rankings (daily/weekly/monthly)", + "site": "steam", + "name": "top-sellers", + "description": "Steam top selling games", "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", - "browser": true, + "domain": "store.steampowered.com", + "strategy": "public", + "browser": false, "args": [ - { - "name": "mode", - "type": "str", - "default": "daily", - "required": false, - "help": "Ranking mode", - "choices": [ - "daily", - "weekly", - "monthly", - "rookie", - "original", - "male", - "female", - "daily_r18", - "weekly_r18" - ] - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number" - }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Number of results" + "help": "Number of games" } ], "columns": [ "rank", - "title", - "author", - "user_id", - "illust_id", - "pages", - "bookmarks", + "name", + "price", + "discount", "url" ], "type": "js", - "modulePath": "plugins/pixiv/ranking.js", - "sourceFile": "plugins/pixiv/ranking.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "plugins/steam/top-sellers.js", + "sourceFile": "plugins/steam/top-sellers.js" }, { - "site": "pixiv", - "name": "search", - "description": "Search Pixiv illustrations by keyword", + "site": "substack", + "name": "feed", + "description": "Substack popular posts Feed", "access": "read", - "domain": "www.pixiv.net", + "domain": "substack.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword or tag" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - }, - { - "name": "order", - "type": "str", - "default": "date_d", - "required": false, - "help": "Sort order", - "choices": [ - "date_d", - "date", - "popular_d", - "popular_male_d", - "popular_female_d" - ] - }, - { - "name": "mode", + "name": "category", "type": "str", "default": "all", "required": false, - "help": "Search mode", - "choices": [ - "all", - "safe", - "r18" - ] + "help": "Post category: all, tech, business, culture, politics, science, health" }, { - "name": "page", + "name": "limit", "type": "int", - "default": 1, + "default": 20, "required": false, - "help": "Page number" + "help": "Number of posts to return" } ], "columns": [ "rank", "title", "author", - "user_id", - "illust_id", - "pages", - "bookmarks", - "tags", + "date", + "readTime", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/pixiv/search.js", - "sourceFile": "plugins/pixiv/search.js", - "navigateBefore": "https://www.pixiv.net" + "modulePath": "plugins/substack/feed.js", + "sourceFile": "plugins/substack/feed.js", + "navigateBefore": "https://substack.com" }, { - "site": "pixiv", - "name": "user", - "description": "View Pixiv artist profile", + "site": "substack", + "name": "publication", + "description": "Get a specific Substack Newsletter latest posts", "access": "read", - "domain": "www.pixiv.net", + "domain": "substack.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "uid", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Pixiv user ID" + "help": "Newsletter URL(for example https://example.substack.com)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of posts to return" } ], "columns": [ - "user_id", - "name", - "premium", - "following", - "illusts", - "manga", - "novels", - "comment", + "rank", + "title", + "date", + "description", "url" ], "type": "js", - "modulePath": "plugins/pixiv/user.js", - "sourceFile": "plugins/pixiv/user.js", - "navigateBefore": "https://www.pixiv.net" - }, - { - "site": "pixiv", - "name": "whoami", - "description": "Show the current logged-in pixiv account", - "access": "read", - "domain": "pixiv.net", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "plugins/pixiv/auth.js", - "sourceFile": "plugins/pixiv/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/substack/publication.js", + "sourceFile": "plugins/substack/publication.js", + "navigateBefore": "https://substack.com" }, { - "site": "practo", - "name": "appointment", - "description": "Show logged-in Practo Drive appointment details", + "site": "substack", + "name": "search", + "description": "Search Substack posts and newsletters", "access": "read", - "domain": "drive.practo.com", - "strategy": "cookie", - "browser": true, + "domain": "substack.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "appointment_id", + "name": "keyword", "type": "str", "required": true, "positional": true, - "help": "Appointment id from `practo appointments`" + "help": "Search keyword" + }, + { + "name": "type", + "type": "str", + "default": "posts", + "required": false, + "help": "Search type(posts=posts, publications=Newsletter)", + "choices": [ + "posts", + "publications" + ] + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of results to return" } ], "columns": [ - "appointment_id", - "status", - "summary" + "rank", + "title", + "author", + "date", + "description", + "url" ], - "type": "js", - "modulePath": "plugins/practo/appointment.js", - "sourceFile": "plugins/practo/appointment.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "practo", - "name": "appointments", - "description": "List logged-in Practo Drive appointments", - "access": "read", - "domain": "drive.practo.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "appointment_id", - "doctor", - "practice", - "time", - "status" + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/practo/appointments.js", - "sourceFile": "plugins/practo/appointments.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/substack/search.js", + "sourceFile": "plugins/substack/search.js" }, { - "site": "practo", - "name": "book-confirm", - "description": "Confirm a Practo clinic visit booking after explicit confirmation", + "site": "suno", + "name": "download", + "description": "Download an existing Suno clip (MP3 + optional WAV/M4A/video) by id", "access": "write", - "domain": "www.practo.com", + "domain": "suno.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "practice_doctor_id", + "name": "clip", "type": "str", "required": true, "positional": true, - "help": "Practo practice_doctor_id" + "help": "Clip UUID or https://suno.com/song/ URL" }, { - "name": "time", + "name": "formats", "type": "str", - "required": true, - "help": "Slot time YYYY-MM-DD HH:mm:ss" + "required": false, + "help": "Comma-separated formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" }, { - "name": "profile-url", + "name": "op", "type": "str", "required": false, - "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" + "help": "Output directory (default: ~/Music/suno)" }, { - "name": "confirm", + "name": "confirm-paid", "type": "boolean", "default": false, "required": false, - "help": "Required. Set --confirm true to create the appointment." + "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." } ], "columns": [ "status", - "practice_doctor_id", - "time", - "url" + "clip", + "title", + "files", + "link" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/practo/book-confirm.js", - "sourceFile": "plugins/practo/book-confirm.js", + "modulePath": "plugins/suno/download.js", + "sourceFile": "plugins/suno/download.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "practo", - "name": "book-preview", - "description": "Preview Practo booking details for a selected slot without confirming", - "access": "read", - "domain": "www.practo.com", + "site": "suno", + "name": "generate", + "description": "Generate music with Suno (V5.5 chirp-fenix by default) and download clips locally", + "access": "write", + "domain": "suno.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "practice_doctor_id", + "name": "prompt", "type": "str", - "required": true, + "required": false, "positional": true, - "help": "Practo practice_doctor_id" + "help": "Simple-mode description (ignored when --lyrics is provided)" }, { - "name": "time", + "name": "lyrics", "type": "str", - "required": true, - "help": "Slot time YYYY-MM-DD HH:mm:ss" + "required": false, + "help": "Custom-mode lyrics (with [Verse]/[Chorus] metatags). Triggers Custom mode." + }, + { + "name": "tags", + "type": "str", + "required": false, + "help": "Custom-mode style tags (genre, BPM, instruments...). Used with --lyrics." + }, + { + "name": "negative-tags", + "type": "str", + "required": false, + "help": "Custom-mode style exclusions (e.g. \"no vocals, no autotune\"). Used with --lyrics." + }, + { + "name": "title", + "type": "str", + "required": false, + "help": "Song title (default: auto-derived from prompt)" + }, + { + "name": "instrumental", + "type": "boolean", + "default": false, + "required": false, + "help": "No vocals" + }, + { + "name": "model", + "type": "str", + "required": false, + "help": "Model id: chirp-fenix, chirp-bluejay, chirp-v4, chirp-v3-5. Default: chirp-fenix" + }, + { + "name": "weirdness", + "type": "str", + "required": false, + "help": "Creative weirdness slider (0..1). Default: 0.5" + }, + { + "name": "style-weight", + "type": "str", + "required": false, + "help": "Style adherence slider (0..1). Default: 0.5" + }, + { + "name": "formats", + "type": "str", + "required": false, + "help": "Comma-separated download formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" + }, + { + "name": "op", + "type": "str", + "required": false, + "help": "Output directory (default: ~/Music/suno)" + }, + { + "name": "timeout", + "type": "int", + "default": 300, + "required": false, + "help": "Max seconds to wait for clips to finish (default: 300)" + }, + { + "name": "sd", + "type": "boolean", + "default": false, + "required": false, + "help": "Skip download; only print clip ids and Suno URLs" }, { - "name": "profile-url", - "type": "str", + "name": "confirm-paid", + "type": "boolean", + "default": false, "required": false, - "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" + "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." } ], "columns": [ - "practice_doctor_id", - "time", - "amount", - "prepaid", - "payment_mode", - "requires_payment", - "confirm_button", - "booking_url" + "status", + "clip", + "title", + "files", + "link" ], + "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/practo/book-preview.js", - "sourceFile": "plugins/practo/book-preview.js", + "modulePath": "plugins/suno/generate.js", + "sourceFile": "plugins/suno/generate.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "practo", - "name": "booking-link", - "description": "Build a Practo booking URL for a selected slot without confirming it", + "site": "suno", + "name": "list", + "description": "List recent Suno clips in your library (id, title, status, created_at, link)", "access": "read", - "domain": "www.practo.com", + "domain": "suno.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "practice_doctor_id", - "type": "str", - "required": true, - "positional": true, - "help": "Practo practice_doctor_id" - }, - { - "name": "time", - "type": "str", - "required": true, - "help": "Slot time YYYY-MM-DD HH:mm:ss" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max clips to list (default: 20)" }, { - "name": "profile-url", - "type": "str", + "name": "page", + "type": "int", + "default": 0, "required": false, - "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" + "help": "Pagination offset, 0-based (default: 0)" } ], "columns": [ - "practice_doctor_id", - "time", - "booking_url" + "rank", + "clip", + "title", + "status", + "created", + "link" ], "type": "js", - "modulePath": "plugins/practo/booking-link.js", - "sourceFile": "plugins/practo/booking-link.js", - "navigateBefore": false + "modulePath": "plugins/suno/list.js", + "sourceFile": "plugins/suno/list.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "practo", - "name": "cancel", - "description": "Cancel a logged-in Practo Drive appointment after explicit confirmation", + "site": "suno", + "name": "login", + "description": "Open suno login", "access": "write", - "domain": "drive.practo.com", + "domain": "suno.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "appointment_id", - "type": "str", - "required": true, - "positional": true, - "help": "Appointment id from `practo appointments`" - }, - { - "name": "confirm", - "type": "boolean", - "default": false, - "required": false, - "help": "Required. Set --confirm true to cancel the appointment." - } - ], + "args": [], "columns": [ "status", - "appointment_id" + "logged_in", + "site", + "user_id", + "name", + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/practo/cancel.js", - "sourceFile": "plugins/practo/cancel.js", + "modulePath": "plugins/suno/auth.js", + "sourceFile": "plugins/suno/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "practo", - "name": "contact", - "description": "Get Practo virtual contact number for a practice_doctor_id", + "site": "suno", + "name": "status", + "description": "Check Suno login, plan, credit balance, and captcha readiness", "access": "read", - "domain": "www.practo.com", + "domain": "suno.com", "strategy": "cookie", "browser": true, - "args": [ - { - "name": "practice_doctor_id", - "type": "str", - "required": true, - "positional": true, - "help": "Practo practice_doctor_id from search results" - } - ], + "args": [], "columns": [ - "practice_doctor_id", - "phone", - "raw" + "Status", + "Plan", + "Credits", + "Monthly", + "Captcha" ], "type": "js", - "modulePath": "plugins/practo/contact.js", - "sourceFile": "plugins/practo/contact.js", - "navigateBefore": false + "modulePath": "plugins/suno/status.js", + "sourceFile": "plugins/suno/status.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "practo", - "name": "login", - "description": "Open practo login", - "access": "write", - "domain": "www.practo.com", + "site": "suno", + "name": "whoami", + "description": "Show the current logged-in suno account", + "access": "read", + "domain": "suno.com", "strategy": "cookie", "browser": true, "args": [], "columns": [ - "status", "logged_in", "site", - "name", - "action", - "verify_command" + "user_id", + "name" ], "type": "js", - "modulePath": "plugins/practo/login.js", - "sourceFile": "plugins/practo/login.js", + "modulePath": "plugins/suno/auth.js", + "sourceFile": "plugins/suno/auth.js", "navigateBefore": false, "siteSession": "persistent" }, { - "site": "practo", - "name": "profile", - "description": "Read public details from a Practo doctor profile URL", + "site": "techcrunch", + "name": "article", + "description": "Read a TechCrunch article from its URL", "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, + "domain": "techcrunch.com", + "strategy": "public", + "browser": false, "args": [ { "name": "url", - "type": "str", + "type": "string", "required": true, "positional": true, - "help": "Practo doctor profile URL" + "help": "TechCrunch article URL" } ], "columns": [ - "name", - "specialty", - "experience", - "fee", - "profile_url" + "title", + "author", + "publishedAt", + "categories", + "description", + "content", + "url" ], "type": "js", - "modulePath": "plugins/practo/profile.js", - "sourceFile": "plugins/practo/profile.js", - "navigateBefore": false + "modulePath": "plugins/techcrunch/article.js", + "sourceFile": "plugins/techcrunch/article.js" }, { - "site": "practo", + "site": "techcrunch", "name": "search", - "description": "Search Practo doctors by specialty, city, and optional locality", + "description": "Search TechCrunch stories or list the latest stories", "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, + "domain": "techcrunch.com", + "strategy": "public", + "browser": false, "args": [ { - "name": "specialty", - "type": "str", - "required": true, - "positional": true, - "help": "Doctor specialty, e.g. orthopedist or dermatologist" - }, - { - "name": "city", - "type": "str", - "default": "bangalore", + "name": "query", + "type": "string", "required": false, - "help": "City, e.g. bangalore" + "positional": true, + "help": "Words to search for" }, { - "name": "locality", - "type": "str", + "name": "latest", + "type": "boolean", + "default": false, "required": false, - "help": "Optional locality, e.g. indiranagar" + "help": "List the latest stories instead of searching" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Max doctors to return (1-25)" + "help": "Maximum stories to return (1-50)" } ], "columns": [ "rank", - "practice_doctor_id", - "doctor_id", - "practice_id", - "name", - "specialty", - "experience_years", - "locality", - "clinic", - "fee", - "next_available", - "profile_url" + "title", + "author", + "publishedAt", + "description", + "url" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/practo/search.js", - "sourceFile": "plugins/practo/search.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "slots", - "description": "List available Practo appointment slots for a practice_doctor_id", - "access": "read", - "domain": "www.practo.com", + "modulePath": "plugins/techcrunch/search.js", + "sourceFile": "plugins/techcrunch/search.js" + }, + { + "site": "tiktok", + "name": "comment", + "description": "Post a comment on a TikTok video", + "access": "write", + "domain": "www.tiktok.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "practice_doctor_id", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Practo practice_doctor_id from search results" + "help": "TikTok video URL (https://www.tiktok.com/@user/video/)" }, { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max slots to return (1-25)" + "name": "text", + "type": "str", + "required": true, + "positional": true, + "help": "Comment text (≤150 chars)" } ], "columns": [ - "practice_doctor_id", - "time", - "available", - "amount", - "prepaid", - "appointment_token" + "url", + "text", + "result" ], "type": "js", - "modulePath": "plugins/practo/slots.js", - "sourceFile": "plugins/practo/slots.js", - "navigateBefore": false + "modulePath": "plugins/tiktok/comment.js", + "sourceFile": "plugins/tiktok/comment.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "practo", - "name": "whoami", - "aliases": [ - "auth-status" - ], - "description": "Show the current logged-in practo account", + "site": "tiktok", + "name": "creator-videos", + "description": "TikTok Studio creator content list (views/likes/comments/saves/shares)", "access": "read", - "domain": "www.practo.com", + "domain": "www.tiktok.com", "strategy": "cookie", "browser": true, - "args": [], + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of creator videos to return (max 250)" + }, + { + "name": "cursor", + "type": "string", + "default": "0", + "required": false, + "help": "Non-negative TikTok Studio pagination cursor" + } + ], "columns": [ - "logged_in", - "site", - "name" + "video_id", + "title", + "date", + "views", + "likes", + "comments", + "saves", + "shares", + "url" ], "type": "js", - "modulePath": "plugins/practo/login.js", - "sourceFile": "plugins/practo/login.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/tiktok/creator-videos.js", + "sourceFile": "plugins/tiktok/creator-videos.js", + "navigateBefore": "https://www.tiktok.com/tiktokstudio/content" }, { - "site": "producthunt", - "name": "browse", - "description": "Best products in a Product Hunt category", + "site": "tiktok", + "name": "explore", + "description": "Get trending TikTok videos from the recommend feed via page-context APIs", "access": "read", - "domain": "www.producthunt.com", - "strategy": "intercept", + "domain": "www.tiktok.com", + "strategy": "cookie", "browser": true, "args": [ - { - "name": "category", - "type": "string", - "required": true, - "positional": true, - "help": "Category slug, e.g. vibe-coding, ai-agents, developer-tools" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of results (max 50)" + "help": "Number of videos to return (max 120)" } ], "columns": [ - "rank", - "name", - "tagline", - "reviews", - "url" + "index", + "id", + "author", + "url", + "cover", + "title", + "desc", + "plays", + "likes", + "comments", + "shares", + "createTime" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/producthunt/browse.js", - "sourceFile": "plugins/producthunt/browse.js", - "navigateBefore": true + "modulePath": "plugins/tiktok/explore.js", + "sourceFile": "plugins/tiktok/explore.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "producthunt", - "name": "hot", - "description": "Today's top Product Hunt launches with vote counts", - "access": "read", - "domain": "www.producthunt.com", - "strategy": "intercept", + "site": "tiktok", + "name": "follow", + "description": "Follow a TikTok user by username", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results (max 50)" + "name": "username", + "type": "str", + "required": true, + "positional": true, + "help": "TikTok username (without @)" } ], "columns": [ - "rank", - "name", - "votes", - "url" + "username", + "url", + "result" ], "type": "js", - "modulePath": "plugins/producthunt/hot.js", - "sourceFile": "plugins/producthunt/hot.js", - "navigateBefore": true + "modulePath": "plugins/tiktok/follow.js", + "sourceFile": "plugins/tiktok/follow.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "producthunt", - "name": "posts", - "description": "Latest Product Hunt launches (optional category filter)", + "site": "tiktok", + "name": "following", + "description": "List accounts the logged-in user follows on TikTok via page-context APIs", "access": "read", - "domain": "www.producthunt.com", - "strategy": "public", - "browser": false, + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Number of results (max 50)" - }, - { - "name": "category", - "type": "string", - "default": "", - "required": false, - "help": "Category filter: ai-agents, ai-coding-agents, ai-code-editors, ai-chatbots, ai-workflow-automation, vibe-coding, developer-tools, productivity, design-creative, marketing-sales, no-code-platforms, llms, finance, social-community, engineering-development" + "help": "Number of accounts (max 200)" } ], "columns": [ - "rank", + "index", + "username", "name", - "tagline", - "author", - "date", + "secUid", + "verified", + "followers", + "following", "url" ], "type": "js", - "modulePath": "plugins/producthunt/posts.js", - "sourceFile": "plugins/producthunt/posts.js" + "modulePath": "plugins/tiktok/following.js", + "sourceFile": "plugins/tiktok/following.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "producthunt", - "name": "today", - "description": "Today's Product Hunt launches (most recent day in feed)", + "site": "tiktok", + "name": "friends", + "description": "Get TikTok friend / who-to-follow suggestions via page-context APIs", "access": "read", - "domain": "www.producthunt.com", - "strategy": "public", - "browser": false, + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max results" + "help": "Number of suggestions (max 100)" } ], "columns": [ - "rank", + "index", + "username", "name", - "tagline", - "author", + "secUid", + "verified", + "followers", + "following", "url" ], "type": "js", - "modulePath": "plugins/producthunt/today.js", - "sourceFile": "plugins/producthunt/today.js" + "modulePath": "plugins/tiktok/friends.js", + "sourceFile": "plugins/tiktok/friends.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "pubmed", - "name": "article", - "aliases": [ - "paper", - "read" - ], - "description": "Get detailed information for a PubMed article by PMID", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, + "site": "tiktok", + "name": "like", + "description": "Like a TikTok video", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "pmid", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "PubMed ID, e.g. 37780221" - }, - { - "name": "full-abstract", - "type": "boolean", - "default": false, - "required": false, - "help": "Do not truncate the abstract in table output" + "help": "TikTok video URL" } ], "columns": [ - "pmid", - "title", - "authors", - "journal", - "year", - "date", - "article_type", - "language", - "doi", - "pmc", - "affiliations", - "grants", - "mesh_terms", - "keywords", - "abstract", + "status", + "likes", "url" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/pubmed/article.js", - "sourceFile": "plugins/pubmed/article.js" + "modulePath": "plugins/tiktok/like.js", + "sourceFile": "plugins/tiktok/like.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "pubmed", - "name": "author", - "description": "Search PubMed articles by author name and optional affiliation", + "site": "tiktok", + "name": "live", + "description": "Browse TikTok live streams via page-context APIs", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Author name, e.g. \"Smith J\"" - }, + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Number of streams (max 60)" + } + ], + "columns": [ + "index", + "streamer", + "name", + "title", + "viewers", + "likes", + "secUid", + "url" + ], + "type": "js", + "modulePath": "plugins/tiktok/live.js", + "sourceFile": "plugins/tiktok/live.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "login", + "description": "Open tiktok login", + "access": "write", + "domain": "tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "sec_uid", + "username", + "nickname", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/tiktok/auth.js", + "sourceFile": "plugins/tiktok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "tiktok", + "name": "notifications", + "description": "Read TikTok inbox notifications (likes, comments, mentions, followers) via page-context APIs", + "access": "read", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ { "name": "limit", "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "affiliation", - "type": "str", - "required": false, - "help": "Filter by author affiliation" - }, - { - "name": "position", - "type": "str", - "default": "any", - "required": false, - "help": "Author position: any, first, or last", - "choices": [ - "any", - "first", - "last" - ] - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", + "default": 15, "required": false, - "help": "Filter publication year to" + "help": "Number of notifications (max 100)" }, { - "name": "sort", + "name": "type", "type": "str", - "default": "date", + "default": "all", "required": false, - "help": "Sort by date or relevance", + "help": "Notification type", "choices": [ - "date", - "relevance" + "all", + "likes", + "comments", + "mentions", + "followers" ] } ], "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" + "index", + "id", + "from", + "text", + "createTime" ], "type": "js", - "modulePath": "plugins/pubmed/author.js", - "sourceFile": "plugins/pubmed/author.js" + "modulePath": "plugins/tiktok/notifications.js", + "sourceFile": "plugins/tiktok/notifications.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "pubmed", - "name": "citations", - "description": "Get PubMed citation relationships for an article", + "site": "tiktok", + "name": "profile", + "description": "Get TikTok user profile info", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "pmid", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "PubMed ID, e.g. 37780221" - }, + "help": "TikTok username (without @)" + } + ], + "columns": [ + "username", + "name", + "followers", + "following", + "likes", + "videos", + "verified", + "bio" + ], + "type": "js", + "modulePath": "plugins/tiktok/profile.js", + "sourceFile": "plugins/tiktok/profile.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "save", + "description": "Add a TikTok video to Favorites", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "direction", + "name": "url", "type": "str", - "default": "citedby", - "required": false, - "help": "citedby or references", - "choices": [ - "citedby", - "references" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" + "required": true, + "positional": true, + "help": "TikTok video URL" } ], "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", + "status", "url" ], "type": "js", - "modulePath": "plugins/pubmed/citations.js", - "sourceFile": "plugins/pubmed/citations.js" + "modulePath": "plugins/tiktok/save.js", + "sourceFile": "plugins/tiktok/save.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "pubmed", - "name": "clinical-trial", - "description": "Search PubMed clinical trials with a trial-study preset", + "site": "tiktok", + "name": "search", + "description": "Search TikTok videos", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "query", "type": "str", "required": true, "positional": true, - "help": "Clinical topic query, e.g. \"breast cancer\"" + "help": "Search query" }, { "name": "limit", "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, - { - "name": "free-full-text", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include free full text articles" - }, - { - "name": "sort", - "type": "str", - "default": "date", + "default": 10, "required": false, - "help": "Sort by date or relevance", - "choices": [ - "date", - "relevance" - ] + "help": "Number of results" } ], "columns": [ "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" + "desc", + "author", + "url", + "plays", + "likes", + "comments", + "shares" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/pubmed/clinical-trial.js", - "sourceFile": "plugins/pubmed/clinical-trial.js" + "modulePath": "plugins/tiktok/search.js", + "sourceFile": "plugins/tiktok/search.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "pubmed", - "name": "journal", - "description": "Search PubMed articles by journal name", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, + "site": "tiktok", + "name": "unfollow", + "description": "Unfollow a TikTok user by username", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "journal", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "Journal name, e.g. \"Nature\" or \"The Lancet\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, + "help": "TikTok username (without @)" + } + ], + "columns": [ + "username", + "url", + "result" + ], + "type": "js", + "modulePath": "plugins/tiktok/unfollow.js", + "sourceFile": "plugins/tiktok/unfollow.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "unlike", + "description": "Unlike a TikTok video", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "sort", + "name": "url", "type": "str", - "default": "relevance", - "required": false, - "help": "Sort by relevance or date", - "choices": [ - "relevance", - "date" - ] + "required": true, + "positional": true, + "help": "TikTok video URL" + } + ], + "columns": [ + "status", + "likes", + "url" + ], + "type": "js", + "modulePath": "plugins/tiktok/unlike.js", + "sourceFile": "plugins/tiktok/unlike.js", + "navigateBefore": "https://www.tiktok.com" + }, + { + "site": "tiktok", + "name": "unsave", + "description": "Remove a TikTok video from Favorites", + "access": "write", + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "TikTok video URL" } ], "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", + "status", "url" ], "type": "js", - "modulePath": "plugins/pubmed/journal.js", - "sourceFile": "plugins/pubmed/journal.js" + "modulePath": "plugins/tiktok/unsave.js", + "sourceFile": "plugins/tiktok/unsave.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "pubmed", - "name": "mesh", - "description": "Search PubMed articles by MeSH term", + "site": "tiktok", + "name": "user", + "description": "Get recent videos from a TikTok user via page-context APIs", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, + "domain": "www.tiktok.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "term", + "name": "username", "type": "str", "required": true, "positional": true, - "help": "MeSH term, e.g. \"Neoplasms\" or \"Machine Learning\"" + "help": "TikTok username (without @)" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max results (1-100)" - }, - { - "name": "major", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include articles where this is a major MeSH topic" - }, - { - "name": "sort", - "type": "str", - "default": "relevance", - "required": false, - "help": "Sort by relevance or date", - "choices": [ - "relevance", - "date" - ] + "help": "Number of videos to return (max 120)" } ], "columns": [ - "rank", - "pmid", + "index", + "id", + "source", + "author", + "url", + "cover", "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" + "desc", + "plays", + "likes", + "comments", + "shares", + "createTime" ], "type": "js", - "modulePath": "plugins/pubmed/mesh.js", - "sourceFile": "plugins/pubmed/mesh.js" + "modulePath": "plugins/tiktok/user.js", + "sourceFile": "plugins/tiktok/user.js", + "navigateBefore": "https://www.tiktok.com" }, { - "site": "pubmed", - "name": "related", - "description": "Find articles related to a PubMed article", + "site": "tiktok", + "name": "whoami", + "description": "Show the current logged-in tiktok account", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, + "domain": "tiktok.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "sec_uid", + "username", + "nickname" + ], + "type": "js", + "modulePath": "plugins/tiktok/auth.js", + "sourceFile": "plugins/tiktok/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "trae-solo", + "name": "automation-list", + "description": "List Trae SOLO Automation tab content. Default tab is \"Configured\"; pass --tab to switch.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "pmid", + "name": "tab", "type": "str", - "required": true, - "positional": true, - "help": "PubMed ID, e.g. 37780221" + "default": "configured", + "required": false, + "help": "Tab to view: configured / run-history / task-template" }, { "name": "limit", "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "score", - "type": "boolean", - "default": false, + "default": 50, "required": false, - "help": "Show similarity scores when available" + "help": "" } ], "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "score", - "doi", - "url" + "Index", + "Title", + "Summary" ], "type": "js", - "modulePath": "plugins/pubmed/related.js", - "sourceFile": "plugins/pubmed/related.js" + "modulePath": "plugins/trae-solo/automation.js", + "sourceFile": "plugins/trae-solo/automation.js", + "navigateBefore": true }, { - "site": "pubmed", - "name": "review", - "description": "Search PubMed review articles with a review preset", + "site": "trae-solo", + "name": "cookies", + "description": "List cookies on the Trae SOLO renderer (JS-visible via document.cookie; httpOnly cookies not shown).", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version" + ], + "type": "js", + "modulePath": "plugins/trae-solo/renderer-storage.js", + "sourceFile": "plugins/trae-solo/renderer-storage.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "extensions-list", + "description": "List VSCode extensions installed in Trae SOLO (~/.trae/extensions/extensions.json). Works while Trae is closed.", + "access": "read", + "domain": "localhost", + "strategy": "local", "browser": false, + "args": [], + "columns": [ + "Index", + "Workspace Id", + "Kind", + "Target", + "Modified", + "Id", + "Version", + "Source", + "Installed" + ], + "type": "js", + "modulePath": "plugins/trae-solo/workspaces-fs.js", + "sourceFile": "plugins/trae-solo/workspaces-fs.js" + }, + { + "site": "trae-solo", + "name": "history", + "description": "List Trae SOLO projects and the tasks within each (from the project-list view sidebar).", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", + "name": "project", "type": "str", - "required": true, - "positional": true, - "help": "Review topic query, e.g. \"immunotherapy\"" + "required": false, + "help": "Filter by project name (substring, case-insensitive)" }, { "name": "limit", "type": "int", - "default": 20, + "default": 100, "required": false, - "help": "Max results (1-100)" - }, + "help": "Max tasks per project" + } + ], + "columns": [ + "Project", + "Task Index", + "Task" + ], + "type": "js", + "modulePath": "plugins/trae-solo/history.js", + "sourceFile": "plugins/trae-solo/history.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "idb-list", + "description": "List IndexedDB databases on the Trae SOLO renderer. Trae ships an @byted/ve-rtc DB used by the Volcengine RTC voice/video infrastructure.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [], + "columns": [ + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version" + ], + "type": "js", + "modulePath": "plugins/trae-solo/renderer-storage.js", + "sourceFile": "plugins/trae-solo/renderer-storage.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "mode", + "description": "Read or switch TRAE SOLO between Code mode and Work mode.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ { - "name": "year-from", - "type": "int", + "name": "target", + "type": "str", "required": false, - "help": "Filter publication year from" - }, + "positional": true, + "help": "Target mode: code or work. Omit to read current." + } + ], + "columns": [ + "Status", + "Mode" + ], + "type": "js", + "modulePath": "plugins/trae-solo/mode.js", + "sourceFile": "plugins/trae-solo/mode.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "model", + "description": "Read or switch the current AI model in TRAE SOLO. Without arguments, reports the current model. With argument (substring, case-insensitive), switches to a matching model. Pass --list to enumerate available models.", + "access": "write", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ { - "name": "year-to", - "type": "int", + "name": "name", + "type": "str", "required": false, - "help": "Filter publication year to" + "positional": true, + "help": "Target model name (substring match, case-insensitive). Omit to read current." }, { - "name": "has-abstract", + "name": "list", "type": "boolean", "default": false, "required": false, - "help": "Only include articles with abstracts" - }, - { - "name": "sort", - "type": "str", - "default": "date", - "required": false, - "help": "Sort by date or relevance", - "choices": [ - "date", - "relevance" - ] + "help": "List all available models (does not switch)" } ], "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" + "Status", + "Model" ], "type": "js", - "modulePath": "plugins/pubmed/review.js", - "sourceFile": "plugins/pubmed/review.js" + "modulePath": "plugins/trae-solo/model.js", + "sourceFile": "plugins/trae-solo/model.js", + "navigateBefore": true }, { - "site": "pubmed", - "name": "search", - "description": "Search PubMed articles with advanced filters", + "site": "trae-solo", + "name": "recent-workspaces", + "description": "Show Trae SOLO's recently-opened workspaces (the File → Open Recent menu, stored under key \"history.recentlyOpenedPathsList\" in state.vscdb).", "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query, e.g. \"machine learning cancer\"" - }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max results (1-100)" - }, - { - "name": "author", - "type": "str", - "required": false, - "help": "Filter by author name" - }, - { - "name": "journal", - "type": "str", - "required": false, - "help": "Filter by journal name" - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, + "help": "" + } + ], + "columns": [ + "Index", + "Key", + "Kind", + "Path" + ], + "type": "js", + "modulePath": "plugins/trae-solo/state-fs.js", + "sourceFile": "plugins/trae-solo/state-fs.js" + }, + { + "site": "trae-solo", + "name": "settings-read", + "description": "Parse and pretty-print Trae SOLO user settings.json (~/Library/Application Support/TRAE SOLO/User/settings.json). Handles VSCode JSONC syntax (line comments + trailing commas).", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "plugins/trae-solo/settings.js", + "sourceFile": "plugins/trae-solo/settings.js" + }, + { + "site": "trae-solo", + "name": "skill-category", + "description": "Filter Skills Marketplace by category. Pass --list to see categories.", + "access": "read", + "domain": "localhost", + "strategy": "ui", + "browser": true, + "args": [ { - "name": "article-type", + "name": "name", "type": "str", "required": false, - "help": "Filter by publication type, e.g. Review or Clinical Trial" - }, - { - "name": "has-abstract", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include articles with abstracts" - }, - { - "name": "free-full-text", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include free full text articles" - }, - { - "name": "humans-only", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include human studies" + "positional": true, + "help": "Category name (substring; case-insensitive). Common: All / Developer Tools / Data Analysis / UI Design / Content Creation / Productivity" }, { - "name": "english-only", + "name": "list", "type": "boolean", "default": false, "required": false, - "help": "Only include English articles" + "help": "List available categories" }, { - "name": "sort", - "type": "str", - "default": "relevance", + "name": "limit", + "type": "int", + "default": 100, "required": false, - "help": "Sort by relevance, date, author, or journal", - "choices": [ - "relevance", - "date", - "author", - "journal" - ] + "help": "" } ], "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" + "Index", + "Name", + "Description" ], - "tags": [ - "search" + "type": "js", + "modulePath": "plugins/trae-solo/skill.js", + "sourceFile": "plugins/trae-solo/skill.js", + "navigateBefore": true + }, + { + "site": "trae-solo", + "name": "skill-fs-installed", + "description": "List INSTALLED Trae SOLO skills (managedSkills entry in ~/.trae/skill-config.json).", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "Index", + "Name", + "Description", + "Source" ], "type": "js", - "modulePath": "plugins/pubmed/search.js", - "sourceFile": "plugins/pubmed/search.js" + "modulePath": "plugins/trae-solo/skill-fs.js", + "sourceFile": "plugins/trae-solo/skill-fs.js" }, { - "site": "pypi", - "name": "downloads", - "description": "PyPI download stats for a package (recent totals or full daily history)", + "site": "trae-solo", + "name": "skill-fs-list", + "description": "List all Trae SOLO skills present on disk under ~/.trae/skills/. Reads SKILL.md front-matter for descriptions. Works while Trae is closed.", "access": "read", - "domain": "pypistats.org", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "PyPI package name (e.g. \"requests\", \"pandas\")" - }, - { - "name": "period", - "type": "str", - "default": "recent", + "name": "limit", + "type": "int", + "default": 200, "required": false, - "help": "recent (default — 1 row, last day/week/month) or overall (1 row per day)" + "help": "Max rows" } ], "columns": [ - "rank", - "package", - "period", - "date", - "downloads" + "Index", + "Name", + "Description", + "Source" ], "type": "js", - "modulePath": "plugins/pypi/downloads.js", - "sourceFile": "plugins/pypi/downloads.js" + "modulePath": "plugins/trae-solo/skill-fs.js", + "sourceFile": "plugins/trae-solo/skill-fs.js" }, { - "site": "pypi", - "name": "package", - "description": "Single PyPI package metadata (latest version, license, homepage, classifiers)", + "site": "trae-solo", + "name": "skill-fs-show", + "description": "Print a skill's SKILL.md content + on-disk path.", "access": "read", - "domain": "pypi.org", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ { @@ -16280,761 +24972,815 @@ "type": "str", "required": true, "positional": true, - "help": "PyPI package name (e.g. \"requests\", \"pandas\")" + "help": "Skill name (folder under ~/.trae/skills/)" } ], "columns": [ - "name", - "latestVersion", - "summary", - "author", - "license", - "homepage", - "repository", - "requiresPython", - "keywords", - "releases", - "firstReleased", - "lastReleased", - "url" + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/pypi/package.js", - "sourceFile": "plugins/pypi/package.js" + "modulePath": "plugins/trae-solo/skill-fs.js", + "sourceFile": "plugins/trae-solo/skill-fs.js" }, { - "site": "pypi", - "name": "releases", - "description": "List recent public PyPI package releases", + "site": "trae-solo", + "name": "skill-list", + "description": "List Trae SOLO Skills — by default the Marketplace; pass --installed to list installed ones.", "access": "read", - "domain": "pypi.org", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "name", - "type": "string", - "required": true, - "positional": true, - "help": "Python package name, for example django" + "name": "installed", + "type": "boolean", + "default": false, + "required": false, + "help": "List installed skills instead of the marketplace" }, { "name": "limit", "type": "int", - "default": 10, + "default": 100, "required": false, - "help": "Maximum releases to return (1-50)" + "help": "Max rows to return" } ], "columns": [ - "version", - "uploadedAt", - "fileCount", - "pythonVersions", - "yanked", - "url" + "Index", + "Name", + "Description" ], "type": "js", - "modulePath": "plugins/pypi/releases.js", - "sourceFile": "plugins/pypi/releases.js" + "modulePath": "plugins/trae-solo/skill.js", + "sourceFile": "plugins/trae-solo/skill.js", + "navigateBefore": true }, { - "site": "rest-countries", - "name": "country", - "description": "Look up countries by name (common / official, substring match)", + "site": "trae-solo", + "name": "skill-search", + "description": "Filter Skills Marketplace by keyword.", "access": "read", - "domain": "restcountries.com", - "strategy": "public", - "browser": false, + "domain": "localhost", + "strategy": "ui", + "browser": true, "args": [ { - "name": "name", + "name": "keyword", "type": "str", "required": true, "positional": true, - "help": "Country name (e.g. \"japan\", \"united kingdom\")" + "help": "Search keyword (substring)" }, { "name": "limit", "type": "int", - "default": 25, + "default": 50, "required": false, - "help": "Max rows (1-250)" + "help": "Max rows" } ], "columns": [ - "rank", - "commonName", - "officialName", - "cca2", - "cca3", - "ccn3", - "capital", - "region", - "subregion", - "population", - "area", - "languages", - "currencies", - "latitude", - "longitude", - "timezones", - "independent", - "unMember", - "landlocked", - "flag", - "url" + "Index", + "Name", + "Description" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/rest-countries/country.js", - "sourceFile": "plugins/rest-countries/country.js" + "modulePath": "plugins/trae-solo/skill.js", + "sourceFile": "plugins/trae-solo/skill.js", + "navigateBefore": true }, { - "site": "rest-countries", - "name": "region", - "description": "List countries in a region (africa / americas / asia / europe / oceania / antarctic)", + "site": "trae-solo", + "name": "state-get", + "description": "Read a single key from Trae SOLO's globalStorage state.vscdb. Pass --workspace to query a per-workspace DB instead. Returns parsed JSON if the value is JSON.", "access": "read", - "domain": "restcountries.com", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ { - "name": "region", + "name": "key", "type": "str", "required": true, "positional": true, - "help": "Region name (case-insensitive)" + "help": "State key (use state-keys to discover)" }, { - "name": "limit", + "name": "workspace", + "type": "str", + "required": false, + "help": "Workspace id (from workspaces-list) to query a per-workspace DB" + }, + { + "name": "max-bytes", "type": "int", - "default": 250, + "default": 8000, "required": false, - "help": "Max rows (1-250)" + "help": "Truncate value to this many bytes" } ], "columns": [ - "rank", - "commonName", - "officialName", - "cca2", - "cca3", - "ccn3", - "capital", - "region", - "subregion", - "population", - "area", - "languages", - "currencies", - "latitude", - "longitude", - "timezones", - "independent", - "unMember", - "landlocked", - "flag", - "url" + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/rest-countries/region.js", - "sourceFile": "plugins/rest-countries/region.js" + "modulePath": "plugins/trae-solo/state-fs.js", + "sourceFile": "plugins/trae-solo/state-fs.js" }, { - "site": "reuters", - "name": "article-detail", - "description": "Reuters Reuters article detail:title/author/body text", + "site": "trae-solo", + "name": "state-keys", + "description": "List all keys present in Trae SOLO's globalStorage state.vscdb (VSCode-style UI/agent state). Pass --workspace to query a per-workspace DB instead. Use state-get to read a specific value. (See renderer storage-keys for browser-side LS/SS.)", "access": "read", - "domain": "www.reuters.com", - "strategy": "cookie", - "browser": true, + "domain": "localhost", + "strategy": "local", + "browser": false, "args": [ { - "name": "url", + "name": "filter", "type": "str", - "required": true, - "positional": true, - "help": "Reuters article URL (must be on reuters.com)" + "required": false, + "help": "Case-insensitive substring filter over keys" + }, + { + "name": "workspace", + "type": "str", + "required": false, + "help": "Workspace id (from workspaces-list) to query a per-workspace DB" + }, + { + "name": "limit", + "type": "int", + "default": 200, + "required": false, + "help": "" } ], "columns": [ - "title", - "date", - "section", - "section_path", - "authors", - "description", - "word_count", - "url", - "body" + "Index", + "Key", + "Kind", + "Path" ], "type": "js", - "modulePath": "plugins/reuters/article-detail.js", - "sourceFile": "plugins/reuters/article-detail.js", - "navigateBefore": "https://www.reuters.com" + "modulePath": "plugins/trae-solo/state-fs.js", + "sourceFile": "plugins/trae-solo/state-fs.js" }, { - "site": "reuters", - "name": "login", - "description": "Open reuters login", - "access": "write", - "domain": "reuters.com", - "strategy": "cookie", + "site": "trae-solo", + "name": "status", + "description": "Check active CDP connection to Trae SOLO Desktop", + "access": "read", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [], "columns": [ - "status", - "logged_in", - "site", - "user_id", - "subscribed", - "action", - "verify_command" + "Status", + "Url", + "Title" ], "type": "js", - "modulePath": "plugins/reuters/auth.js", - "sourceFile": "plugins/reuters/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/trae-solo/status.js", + "sourceFile": "plugins/trae-solo/status.js", + "navigateBefore": true }, { - "site": "reuters", - "name": "search", - "description": "Reuters Reuters news search", + "site": "trae-solo", + "name": "storage-get", + "description": "Read a single localStorage / sessionStorage value on the Trae SOLO renderer.", "access": "read", - "domain": "www.reuters.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, "args": [ { - "name": "query", + "name": "key", "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "Storage key (use storage-keys to discover)" }, { - "name": "limit", + "name": "storage", + "type": "str", + "default": "local", + "required": false, + "help": "\"local\" or \"session\"" + }, + { + "name": "max-bytes", "type": "int", - "default": 10, + "default": 4000, "required": false, - "help": "Number of results (1-40)" + "help": "Truncate value to this many chars" } ], "columns": [ - "rank", - "title", - "date", - "section", - "section_path", - "authors", - "url" - ], - "tags": [ - "search" + "Field", + "Value" ], "type": "js", - "modulePath": "plugins/reuters/search.js", - "sourceFile": "plugins/reuters/search.js", - "navigateBefore": "https://www.reuters.com" + "modulePath": "plugins/trae-solo/renderer-storage.js", + "sourceFile": "plugins/trae-solo/renderer-storage.js", + "navigateBefore": true }, { - "site": "reuters", - "name": "whoami", - "description": "Show the current logged-in reuters account", + "site": "trae-solo", + "name": "storage-keys", + "description": "List localStorage / sessionStorage keys on the Trae SOLO renderer (CDP). For the on-disk VSCode state.vscdb, see state-keys.", "access": "read", - "domain": "reuters.com", - "strategy": "cookie", + "domain": "localhost", + "strategy": "ui", "browser": true, - "args": [], + "args": [ + { + "name": "storage", + "type": "str", + "default": "local", + "required": false, + "help": "\"local\" or \"session\"" + }, + { + "name": "filter", + "type": "str", + "required": false, + "help": "Case-insensitive substring filter" + }, + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "Max rows to return" + } + ], "columns": [ - "logged_in", - "site", - "user_id", - "subscribed" + "Index", + "Key", + "Bytes", + "Name", + "Preview", + "Database", + "Version" ], "type": "js", - "modulePath": "plugins/reuters/auth.js", - "sourceFile": "plugins/reuters/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/trae-solo/renderer-storage.js", + "sourceFile": "plugins/trae-solo/renderer-storage.js", + "navigateBefore": true }, { - "site": "rfc", - "name": "rfc", - "description": "Single IETF RFC metadata (title, abstract, working group, authors, std level)", + "site": "trae-solo", + "name": "task-fs-list", + "description": "List Trae SOLO task ids from disk (snapshot/ + agentconfig/.json). Works while Trae is closed.", "access": "read", - "domain": "datatracker.ietf.org", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ { - "name": "number", + "name": "limit", "type": "int", - "required": true, - "positional": true, - "help": "RFC number (e.g. 9000, 791, 2616)" + "default": 100, + "required": false, + "help": "" } ], "columns": [ - "rfc", - "title", - "state", - "stdLevel", - "group", - "groupType", - "pages", - "published", - "authors", - "abstract", - "rfcEditorUrl", - "url" + "Index", + "Task Id", + "Has Snapshot", + "Has Config", + "Modified", + "Phase", + "Turn Id", + "Commit" ], "type": "js", - "modulePath": "plugins/rfc/rfc.js", - "sourceFile": "plugins/rfc/rfc.js" + "modulePath": "plugins/trae-solo/task-fs.js", + "sourceFile": "plugins/trae-solo/task-fs.js" }, { - "site": "rubygems", - "name": "gem", - "description": "Fetch a RubyGems.org gem's metadata (version, downloads, license, links)", + "site": "trae-solo", + "name": "task-fs-show", + "description": "Show the workspace tree at a given chat-turn ref (via git ls-tree). Pass --turn to pick a turn; otherwise the latest after-chat-turn ref.", "access": "read", - "domain": "rubygems.org", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ { - "name": "name", + "name": "task-id", "type": "str", "required": true, "positional": true, - "help": "Gem name (e.g. \"rails\", \"sidekiq\")" + "help": "Task UUID" + }, + { + "name": "turn", + "type": "str", + "required": false, + "help": "Specific turn id (omit for latest after-chat-turn)" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "" } ], "columns": [ - "gem", - "version", - "releasedAt", - "downloads", - "versionDownloads", - "license", - "authors", - "homepage", - "source", - "bugs", - "info", - "url" + "Mode", + "Path", + "Size" ], "type": "js", - "modulePath": "plugins/rubygems/gem.js", - "sourceFile": "plugins/rubygems/gem.js" + "modulePath": "plugins/trae-solo/task-fs.js", + "sourceFile": "plugins/trae-solo/task-fs.js" }, { - "site": "rubygems", - "name": "search", - "description": "Search RubyGems.org gems by keyword", + "site": "trae-solo", + "name": "task-fs-turns", + "description": "Show the chat-turn timeline for a Trae SOLO task as git tags (before-chat-turn-* / after-chat-turn-*).", "access": "read", - "domain": "rubygems.org", - "strategy": "public", + "domain": "localhost", + "strategy": "local", "browser": false, "args": [ { - "name": "query", + "name": "task-id", "type": "str", "required": true, "positional": true, - "help": "Search keyword (e.g. \"rails\", \"redis\")" + "help": "Task UUID (folder name under snapshot/)" }, { "name": "limit", "type": "int", - "default": 30, + "default": 50, "required": false, - "help": "Max gems (1-100, single RubyGems page)" + "help": "" } ], "columns": [ - "rank", - "gem", - "version", - "downloads", - "license", - "authors", - "info", - "url" + "Index", + "Task Id", + "Has Snapshot", + "Has Config", + "Modified", + "Phase", + "Turn Id", + "Commit" ], - "tags": [ - "search" + "type": "js", + "modulePath": "plugins/trae-solo/task-fs.js", + "sourceFile": "plugins/trae-solo/task-fs.js" + }, + { + "site": "trae-solo", + "name": "user-rules", + "description": "Print Trae SOLO user rules (~/.trae/user_rules.md).", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [], + "columns": [ + "Field", + "Value" + ], + "type": "js", + "modulePath": "plugins/trae-solo/user-rules.js", + "sourceFile": "plugins/trae-solo/user-rules.js" + }, + { + "site": "trae-solo", + "name": "workspaces-list", + "description": "List Trae SOLO workspaceStorage entries (~/Library/.../TRAE SOLO/User/workspaceStorage//), resolving each workspace.json to its single-folder path or multi-folder workspace target. Works while Trae is closed.", + "access": "read", + "domain": "localhost", + "strategy": "local", + "browser": false, + "args": [ + { + "name": "limit", + "type": "int", + "default": 100, + "required": false, + "help": "" + } + ], + "columns": [ + "Index", + "Workspace Id", + "Kind", + "Target", + "Modified", + "Id", + "Version", + "Source", + "Installed" ], "type": "js", - "modulePath": "plugins/rubygems/search.js", - "sourceFile": "plugins/rubygems/search.js" + "modulePath": "plugins/trae-solo/workspaces-fs.js", + "sourceFile": "plugins/trae-solo/workspaces-fs.js" }, { - "site": "semanticscholar", - "name": "citations", - "description": "List papers that cite a Semantic Scholar paper (paginated)", + "site": "trip", + "name": "attraction", + "description": "Search Trip.com attractions and experiences by destination keyword", "access": "read", - "domain": "api.semanticscholar.org", - "strategy": "public", - "browser": false, + "domain": "trip.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "query", "type": "str", "required": true, "positional": true, - "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" + "help": "Destination or attraction keyword (e.g. Tokyo / Paris / Louvre)" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max citing papers (1-1000, single Semantic Scholar page)" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Page offset (0-based)" + "help": "Number of results (1-50)" } ], "columns": [ "rank", - "paperId", - "doi", - "title", - "year", - "firstAuthor", - "citationCount", + "name", + "rating", + "reviews", + "booked", + "price", + "currency", "url" ], "type": "js", - "modulePath": "plugins/semanticscholar/citations.js", - "sourceFile": "plugins/semanticscholar/citations.js" + "modulePath": "plugins/trip/attraction.js", + "sourceFile": "plugins/trip/attraction.js", + "navigateBefore": false }, { - "site": "semanticscholar", - "name": "paper", - "description": "Semantic Scholar paper detail (citation graph + AI tldr) by paperId, DOI, or arXiv id", + "site": "trip", + "name": "car", + "description": "List Trip.com car-rental vehicles for a city (category, model, seats, daily price)", "access": "read", - "domain": "api.semanticscholar.org", - "strategy": "public", - "browser": false, + "domain": "trip.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "city", "type": "str", "required": true, "positional": true, - "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id (e.g. \"ARXIV:1706.03762\", \"PMID:12345\")" + "help": "Numeric Trip.com carhire city id (discover via the carhire search box)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of vehicles (1-50)" } ], "columns": [ - "paperId", - "doi", - "title", - "year", - "firstAuthor", - "citationCount", - "influentialCitationCount", - "referenceCount", - "tldr", + "rank", + "category", + "vehicle", + "seats", + "price", + "currency", "url" ], "type": "js", - "modulePath": "plugins/semanticscholar/paper.js", - "sourceFile": "plugins/semanticscholar/paper.js" + "modulePath": "plugins/trip/car.js", + "sourceFile": "plugins/trip/car.js", + "navigateBefore": false }, { - "site": "semanticscholar", - "name": "recommendations", - "description": "Semantic Scholar AI-curated related papers for a paperId, DOI, or arXiv id", + "site": "trip", + "name": "deals", + "description": "List Trip.com live promotions from the Top Deals hub: campaign title, offer, discount, and link", "access": "read", - "domain": "api.semanticscholar.org", - "strategy": "public", - "browser": false, + "domain": "trip.com", + "strategy": "cookie", + "browser": true, "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" - }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Max recommendations (1-500)" + "help": "Number of deals (1-50)" } ], "columns": [ "rank", - "paperId", - "doi", "title", - "year", - "firstAuthor", - "citationCount", + "offer", + "discount", "url" ], "type": "js", - "modulePath": "plugins/semanticscholar/recommendations.js", - "sourceFile": "plugins/semanticscholar/recommendations.js" + "modulePath": "plugins/trip/deals.js", + "sourceFile": "plugins/trip/deals.js", + "navigateBefore": false }, { - "site": "semanticscholar", - "name": "search", - "description": "Search Semantic Scholar papers by free text", + "site": "trip", + "name": "flight", + "description": "Search Trip.com one-way flights by IATA route + departure date", "access": "read", - "domain": "api.semanticscholar.org", - "strategy": "public", - "browser": false, + "domain": "trip.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "from", "type": "str", "required": true, "positional": true, - "help": "Search text (e.g. \"attention is all you need\", \"diffusion model\")" + "help": "Departure IATA code (e.g. LON / LHR)" + }, + { + "name": "to", + "type": "str", + "required": true, + "positional": true, + "help": "Arrival IATA code (e.g. NYC / JFK)" + }, + { + "name": "date", + "type": "str", + "required": true, + "help": "Departure date (YYYY-MM-DD)" }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max papers (1-100, single Semantic Scholar page)" + "help": "Number of flights (1-50)" } ], "columns": [ "rank", - "paperId", - "doi", - "title", - "year", - "firstAuthor", - "citationCount", + "airline", + "departureTime", + "departureAirport", + "arrivalTime", + "arrivalAirport", + "duration", + "stops", + "price", + "currency", "url" ], - "tags": [ - "search" - ], "type": "js", - "modulePath": "plugins/semanticscholar/search.js", - "sourceFile": "plugins/semanticscholar/search.js" + "modulePath": "plugins/trip/flight.js", + "sourceFile": "plugins/trip/flight.js", + "navigateBefore": false }, { - "site": "skyscanner", - "name": "flights", - "description": "Skyscanner visible round-trip flight results from a warmed browser session", + "site": "trip", + "name": "flight-round", + "description": "Search Trip.com round-trip flights by IATA route + depart/return dates", "access": "read", - "domain": "www.skyscanner.com", - "strategy": "ui", + "domain": "trip.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "origin", + "name": "from", "type": "str", "required": true, "positional": true, - "help": "Skyscanner origin route code, for example nyca" + "help": "Departure IATA code (e.g. LON / LHR)" }, { - "name": "destination", + "name": "to", "type": "str", "required": true, "positional": true, - "help": "Skyscanner destination route code, for example lond" + "help": "Arrival IATA code (e.g. NYC / JFK)" }, { - "name": "depart-date", + "name": "depart", "type": "str", "required": true, - "help": "Outbound date as YYYY-MM-DD" + "help": "Outbound date (YYYY-MM-DD)" }, { - "name": "return-date", + "name": "return", "type": "str", "required": true, - "help": "Return date as YYYY-MM-DD" + "help": "Return date (YYYY-MM-DD)" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Maximum flight rows to return (1-30)" + "help": "Number of flights (1-50)" } ], "columns": [ "rank", - "priceText", - "airlines", - "outboundTime", - "outboundRoute", - "outboundDuration", - "outboundStops", - "returnTime", - "returnRoute", - "returnDuration", - "returnStops", + "airline", + "departureTime", + "departureAirport", + "arrivalTime", + "arrivalAirport", + "duration", + "stops", + "price", + "currency", "url" ], "type": "js", - "modulePath": "plugins/skyscanner/flights.js", - "sourceFile": "plugins/skyscanner/flights.js", + "modulePath": "plugins/trip/flight-round.js", + "sourceFile": "plugins/trip/flight-round.js", "navigateBefore": false }, { - "site": "spotify", - "name": "auth", - "description": "Authenticate with Spotify (OAuth — run once)", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "spotify", - "name": "next", - "description": "Skip to next track", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "spotify", - "name": "pause", - "description": "Pause playback", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "spotify", - "name": "play", - "description": "Resume playback or search and play a track/artist", - "access": "write", - "strategy": "local", - "browser": false, + "site": "trip", + "name": "hotel", + "description": "Show a Trip.com hotel detail by id (rating breakdown, amenities, check-in/out policy)", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "id", "type": "str", - "default": "", - "required": false, - "positional": true, - "help": "Track or artist to play (optional)" - } - ], - "columns": [ - "track", - "artist", - "status" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "spotify", - "name": "prev", - "description": "Skip to previous track", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], + "required": true, + "positional": true, + "help": "Numeric Trip.com hotel id (discover via the hotels list; e.g. 715233)" + } + ], "columns": [ - "status" + "hotelId", + "name", + "enName", + "star", + "score", + "scoreLabel", + "reviewCount", + "ratingBreakdown", + "facilities", + "checkInOut", + "cityName", + "address", + "lat", + "lon", + "url" ], "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" + "modulePath": "plugins/trip/hotel.js", + "sourceFile": "plugins/trip/hotel.js", + "navigateBefore": false }, { - "site": "spotify", - "name": "queue", - "description": "Add a track to the playback queue", - "access": "write", - "strategy": "local", - "browser": false, + "site": "trip", + "name": "hotel-search", + "description": "List Trip.com hotels for a city id + check-in/out date range", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "city", "type": "str", "required": true, "positional": true, - "help": "Track to add to queue" + "help": "Numeric Trip.com city id (discover via the hotels search box; e.g. 338 for London)" + }, + { + "name": "checkin", + "type": "str", + "required": true, + "help": "Check-in date (YYYY-MM-DD)" + }, + { + "name": "checkout", + "type": "str", + "required": true, + "help": "Check-out date (YYYY-MM-DD)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of hotels (1-50)" } ], "columns": [ - "track", - "artist", - "status" + "rank", + "name", + "score", + "reviewLabel", + "reviews", + "location", + "room", + "price", + "currency", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" + "modulePath": "plugins/trip/hotel-search.js", + "sourceFile": "plugins/trip/hotel-search.js", + "navigateBefore": false }, { - "site": "spotify", - "name": "repeat", - "description": "Set repeat mode (off / track / context)", - "access": "write", - "strategy": "local", + "site": "trip", + "name": "package", + "description": "Search Trip.com flight+hotel packages by route + dates; lists the package flight options priced at the bundle rate", + "access": "read", + "domain": "trip.com", + "strategy": "public", "browser": false, "args": [ { - "name": "mode", + "name": "from", "type": "str", - "default": "context", - "required": false, + "required": true, "positional": true, - "help": "off / track / context", - "choices": [ - "off", - "track", - "context" - ] + "help": "Origin city keyword (e.g. Seoul / London / Bangkok)" + }, + { + "name": "to", + "type": "str", + "required": true, + "positional": true, + "help": "Destination city keyword (e.g. Tokyo / Paris / Singapore)" + }, + { + "name": "depart", + "type": "str", + "required": true, + "help": "Outbound date (YYYY-MM-DD)" + }, + { + "name": "return", + "type": "str", + "required": true, + "help": "Return date (YYYY-MM-DD)" + }, + { + "name": "adults", + "type": "int", + "default": 2, + "required": false, + "help": "Number of adults (1-9, default 2)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of packages (1-50)" } ], "columns": [ - "repeat" + "rank", + "airline", + "flightNo", + "from", + "to", + "departure", + "arrival", + "stops", + "price", + "currency" ], "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" + "modulePath": "plugins/trip/package.js", + "sourceFile": "plugins/trip/package.js" }, { - "site": "spotify", + "site": "trip", "name": "search", - "description": "Search for tracks", + "description": "Suggest Trip.com destinations (cities, airports) for a keyword; resolves the ids the other commands take", "access": "read", - "strategy": "local", + "domain": "trip.com", + "strategy": "public", "browser": false, "args": [ { @@ -17042,1870 +25788,1968 @@ "type": "str", "required": true, "positional": true, - "help": "Search query" + "help": "Destination keyword (e.g. Tokyo / Bali / London)" }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Number of results (default: 10)" + "help": "Number of suggestions (1-50)" } ], "columns": [ - "track", - "artist", - "album", - "uri" + "rank", + "name", + "type", + "cityId", + "airportCode", + "province", + "country" ], "tags": [ "search" ], "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" + "modulePath": "plugins/trip/search.js", + "sourceFile": "plugins/trip/search.js" }, { - "site": "spotify", - "name": "shuffle", - "description": "Toggle shuffle on/off", - "access": "write", - "strategy": "local", - "browser": false, + "site": "trip", + "name": "tour", + "description": "Search Trip.com tour packages by destination keyword (private or group tours)", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "state", + "name": "query", "type": "str", - "default": "on", - "required": false, + "required": true, "positional": true, - "help": "on or off", - "choices": [ - "on", - "off" - ] + "help": "Destination or tour keyword (e.g. Tokyo / Kyoto / Bali)" + }, + { + "name": "type", + "type": "str", + "default": "private", + "required": false, + "help": "Tour line: private or group (default private)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of tours (1-50)" } ], "columns": [ - "shuffle" + "rank", + "name", + "type", + "rating", + "reviews", + "price", + "currency", + "url" ], "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" + "modulePath": "plugins/trip/tour.js", + "sourceFile": "plugins/trip/tour.js", + "navigateBefore": false }, { - "site": "spotify", - "name": "status", - "description": "Show current playback status", + "site": "trip", + "name": "train", + "description": "Show a Trip.com train route timetable (departure/arrival times, duration, changes)", "access": "read", - "strategy": "local", - "browser": false, - "args": [], + "domain": "trip.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "from", + "type": "str", + "required": true, + "positional": true, + "help": "Departure city (e.g. London / Paris / Shanghai)" + }, + { + "name": "to", + "type": "str", + "required": true, + "positional": true, + "help": "Arrival city (e.g. Manchester / Lyon / Beijing)" + }, + { + "name": "country", + "type": "str", + "required": true, + "help": "Route country slug (e.g. uk / france / italy / spain / germany / china)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of journeys (1-50)" + } + ], "columns": [ - "track", - "artist", - "album", - "status", - "progress" + "rank", + "departureTime", + "fromStation", + "arrivalTime", + "toStation", + "duration", + "changes", + "url" ], "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" + "modulePath": "plugins/trip/train.js", + "sourceFile": "plugins/trip/train.js", + "navigateBefore": false }, { - "site": "spotify", - "name": "volume", - "description": "Set playback volume (0-100)", - "access": "write", - "strategy": "local", - "browser": false, + "site": "trip", + "name": "transfer", + "description": "List Trip.com airport-transfer vehicles for a city + airport (type, seats, from-price)", + "access": "read", + "domain": "trip.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "level", - "type": "int", - "default": 50, + "name": "city", + "type": "str", "required": true, "positional": true, - "help": "Volume 0–100" + "help": "Airport city (e.g. Bangkok / Beijing / Da Nang)" + }, + { + "name": "airport", + "type": "str", + "required": true, + "positional": true, + "help": "3-letter airport IATA code (e.g. DMK / PKX / DAD)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Number of vehicles (1-50)" } ], "columns": [ - "volume" + "rank", + "type", + "passengers", + "luggage", + "price", + "currency", + "url" ], "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" + "modulePath": "plugins/trip/transfer.js", + "sourceFile": "plugins/trip/transfer.js", + "navigateBefore": false }, { - "site": "stackoverflow", - "name": "bounties", - "description": "Active bounties on Stack Overflow", + "site": "tvmaze", + "name": "search", + "description": "TVmaze TV show search by title (returns id, name, network, premiered/ended, rating)", "access": "read", - "domain": "stackoverflow.com", + "domain": "tvmaze.com", "strategy": "public", "browser": false, "args": [ + { + "name": "query", + "type": "string", + "required": true, + "positional": true, + "help": "TV show title or fragment to search for" + }, { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Max number of results" + "help": "Max rows to return (1-50)" } ], "columns": [ "rank", "id", - "bounty", - "title", - "score", - "answers", - "views", - "is_answered", - "tags", - "author", - "creation_date", + "name", + "type", + "language", + "genres", + "status", + "premiered", + "ended", + "network", + "rating", + "matchScore", + "summary", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/stackoverflow/bounties.js", - "sourceFile": "plugins/stackoverflow/bounties.js" + "modulePath": "plugins/tvmaze/search.js", + "sourceFile": "plugins/tvmaze/search.js" }, { - "site": "stackoverflow", - "name": "hot", - "description": "Hot Stack Overflow questions", + "site": "tvmaze", + "name": "show", + "description": "Single TVmaze TV show detail by id (network, schedule, rating, IMDB/TheTVDB cross-refs)", "access": "read", - "domain": "stackoverflow.com", + "domain": "tvmaze.com", "strategy": "public", "browser": false, "args": [ { - "name": "limit", + "name": "id", "type": "int", - "default": 10, - "required": false, - "help": "Max number of results" + "required": true, + "positional": true, + "help": "TVmaze show id (positive integer)" } ], "columns": [ - "rank", "id", - "title", - "score", - "answers", - "views", - "is_answered", - "tags", - "author", - "creation_date", + "name", + "type", + "language", + "genres", + "status", + "premiered", + "ended", + "runtime", + "averageRuntime", + "network", + "country", + "schedule", + "rating", + "imdb", + "thetvdb", + "officialSite", + "summary", "url" ], "type": "js", - "modulePath": "plugins/stackoverflow/hot.js", - "sourceFile": "plugins/stackoverflow/hot.js" + "modulePath": "plugins/tvmaze/show.js", + "sourceFile": "plugins/tvmaze/show.js" }, { - "site": "stackoverflow", - "name": "read", - "description": "Read a Stack Overflow question with answers and comments", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, + "site": "twitter", + "name": "accept", + "description": "Auto-accept DM requests containing specific keywords", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "id", - "type": "str", + "name": "query", + "type": "string", "required": true, "positional": true, - "help": "Stack Overflow question id (numeric, e.g. 79935770)" - }, - { - "name": "answers-limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max answers to include (1-100; accepted answer always included first)" + "help": "Keywords to match (comma-separated for OR, e.g. \"invoice,urgent\")" }, { - "name": "comments-limit", + "name": "max", "type": "int", - "default": 5, + "default": 20, "required": false, - "help": "Max comments per question/answer (1-100)" + "help": "Maximum number of requests to accept (default: 20)" }, { - "name": "max-length", + "name": "timeout", "type": "int", - "default": 4000, + "default": 600, "required": false, - "help": "Max characters per body / answer / comment (min 100)" + "help": "Max seconds for the overall command (default: 600 — batch op)" } ], "columns": [ - "type", - "author", - "score", - "accepted", - "text" + "index", + "status", + "user", + "message" ], "type": "js", - "modulePath": "plugins/stackoverflow/read.js", - "sourceFile": "plugins/stackoverflow/read.js" + "modulePath": "plugins/twitter/accept.js", + "sourceFile": "plugins/twitter/accept.js", + "navigateBefore": true }, { - "site": "stackoverflow", - "name": "related", - "description": "List Stack Overflow questions related to a given question id.", + "site": "twitter", + "name": "article", + "description": "Fetch a Twitter Article (long-form content) and export as Markdown", "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, + "domain": "x.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", + "name": "tweet-id", "type": "string", "required": true, "positional": true, - "help": "Stack Overflow question id (numeric, e.g. 79935770)." - }, - { - "name": "sort", - "type": "string", - "default": "rank", - "required": false, - "help": "Sort key: rank, activity, votes, creation (rank = SO relevance default)." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max related questions (1-100)." + "help": "Tweet ID or URL containing the article" } ], "columns": [ - "rank", - "id", "title", - "score", - "answers", - "views", - "isAnswered", - "tags", "author", - "createdAt", - "lastActivityAt", + "content", "url" ], "type": "js", - "modulePath": "plugins/stackoverflow/related.js", - "sourceFile": "plugins/stackoverflow/related.js" + "modulePath": "plugins/twitter/article.js", + "sourceFile": "plugins/twitter/article.js", + "navigateBefore": "https://x.com" }, { - "site": "stackoverflow", - "name": "search", - "description": "Search Stack Overflow questions", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, + "site": "twitter", + "name": "block", + "description": "Block a Twitter user", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", + "name": "username", "type": "string", "required": true, "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max number of results" + "help": "Twitter screen name (without @)" } ], "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "is_answered", - "tags", - "author", - "creation_date", - "url" + "status", + "message" ], - "tags": [ - "search" + "type": "js", + "modulePath": "plugins/twitter/block.js", + "sourceFile": "plugins/twitter/block.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "bookmark", + "description": "Bookmark a tweet", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "Tweet URL to bookmark" + } + ], + "columns": [ + "status", + "message" ], "type": "js", - "modulePath": "plugins/stackoverflow/search.js", - "sourceFile": "plugins/stackoverflow/search.js" + "modulePath": "plugins/twitter/bookmark.js", + "sourceFile": "plugins/twitter/bookmark.js", + "navigateBefore": true }, { - "site": "stackoverflow", - "name": "tag", - "description": "List Stack Overflow questions tagged with a given tag (most active first).", + "site": "twitter", + "name": "bookmark-folder", + "description": "Read the tweets inside a single Twitter/X bookmark folder. Get the folder id from `webcmd twitter bookmark-folders`.", "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, + "domain": "x.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "tag", + "name": "folder-id", "type": "string", "required": true, "positional": true, - "help": "Tag slug (e.g. python, rust, typescript)." + "help": "Folder id from `webcmd twitter bookmark-folders`." }, { - "name": "sort", - "type": "string", - "default": "activity", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Sort key: activity, votes, creation, hot, week, month" + "help": "Maximum number of bookmarks to return (default 20)." }, { - "name": "limit", + "name": "top-by-engagement", "type": "int", - "default": 20, + "default": 0, "required": false, - "help": "Max questions to return (max 100)." + "help": "When set to N>0, re-rank the folder by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (saved-time) ordering." } ], "columns": [ - "rank", "id", - "title", - "score", - "answers", - "views", - "isAnswered", - "tags", - "author", - "createdAt", - "lastActivityAt", - "url" + "author", + "text", + "likes", + "retweets", + "bookmarks", + "created_at", + "url", + "has_media", + "media_urls", + "media_posters" + ], + "type": "js", + "modulePath": "plugins/twitter/bookmark-folder.js", + "sourceFile": "plugins/twitter/bookmark-folder.js", + "navigateBefore": "https://x.com" + }, + { + "site": "twitter", + "name": "bookmark-folders", + "description": "List your Twitter/X bookmark folders (the user-created collections under Bookmarks). Returns folder id, name, item count, and created_at.", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "id", + "name", + "items", + "created_at" ], "type": "js", - "modulePath": "plugins/stackoverflow/tag.js", - "sourceFile": "plugins/stackoverflow/tag.js" + "modulePath": "plugins/twitter/bookmark-folders.js", + "sourceFile": "plugins/twitter/bookmark-folders.js", + "navigateBefore": "https://x.com" }, { - "site": "stackoverflow", - "name": "unanswered", - "description": "Top voted unanswered questions on Stack Overflow", + "site": "twitter", + "name": "bookmarks", + "description": "Fetch your Twitter/X bookmarks (the logged-in user's saved tweets, newest first)", "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, + "domain": "x.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 10, + "default": 20, "required": false, - "help": "Max number of results" + "help": "Maximum number of bookmarks to return (default 20)." + }, + { + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank the bookmarks by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (saved-time) ordering." } ], "columns": [ - "rank", "id", - "title", - "score", - "answers", - "views", - "tags", "author", - "creation_date", - "url" + "text", + "likes", + "retweets", + "bookmarks", + "created_at", + "url", + "has_media", + "media_urls", + "media_posters" ], "type": "js", - "modulePath": "plugins/stackoverflow/unanswered.js", - "sourceFile": "plugins/stackoverflow/unanswered.js" + "modulePath": "plugins/twitter/bookmarks.js", + "sourceFile": "plugins/twitter/bookmarks.js", + "navigateBefore": "https://x.com" }, { - "site": "stackoverflow", - "name": "user", - "description": "Find Stack Overflow users by display name (highest reputation first).", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, + "site": "twitter", + "name": "delete", + "description": "Delete a specific tweet by URL", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "name", + "name": "url", "type": "string", "required": true, "positional": true, - "help": "Display name (or substring) to search." - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max users to return (max 100)." + "help": "The URL of the tweet to delete" } ], "columns": [ - "userId", - "displayName", - "reputation", - "goldBadges", - "silverBadges", - "bronzeBadges", - "location", - "createdAt", - "lastAccessAt", - "url" + "status", + "message" ], "type": "js", - "modulePath": "plugins/stackoverflow/user.js", - "sourceFile": "plugins/stackoverflow/user.js" + "modulePath": "plugins/twitter/delete.js", + "sourceFile": "plugins/twitter/delete.js", + "navigateBefore": true }, { - "site": "steam", - "name": "app", - "description": "Steam storefront detail for a single app id", + "site": "twitter", + "name": "device-follow", + "description": "Read the /i/timeline device-follow notification stream (tweets aggregated under a bell-icon \"new posts from @userA and N others\" notification)", "access": "read", - "domain": "store.steampowered.com", - "strategy": "public", - "browser": false, + "domain": "x.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Numeric Steam app id (e.g. \"620\" for Portal 2)" + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum number of tweets to return (1-200, default 20)" }, { - "name": "currency", - "type": "str", - "default": "us", + "name": "top-by-engagement", + "type": "int", + "default": 0, "required": false, - "help": "Storefront country code (e.g. us / cn / jp / de)" + "help": "When set to N>0, re-rank by weighted engagement and return the top N. Default 0 keeps upstream ordering." } ], "columns": [ "id", - "name", - "type", - "isFree", - "releaseDate", - "developers", - "publishers", - "price", - "currency", - "metacritic", - "recommendations", - "genres", - "categories", - "shortDescription", - "website", + "author", + "text", + "likes", + "retweets", + "replies", + "views", + "created_at", "url" ], "type": "js", - "modulePath": "plugins/steam/app.js", - "sourceFile": "plugins/steam/app.js" + "modulePath": "plugins/twitter/device-follow.js", + "sourceFile": "plugins/twitter/device-follow.js", + "navigateBefore": "https://x.com" }, { - "site": "steam", - "name": "search", - "description": "Search the Steam storefront by name keyword", + "site": "twitter", + "name": "download", + "description": "Download Twitter/X media (images and videos). Provide either to fetch every media item from their profile via the GraphQL UserMedia endpoint with cursor pagination, or --tweet-url to download a single tweet.", "access": "read", - "domain": "store.steampowered.com", - "strategy": "public", - "browser": false, + "domain": "x.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "query", + "name": "username", "type": "str", - "required": true, + "required": false, "positional": true, - "help": "Search keyword (e.g. \"portal\", \"stardew\")" + "help": "Twitter username (with or without @) to scan their profile media. Either or --tweet-url is required." + }, + { + "name": "tweet-url", + "type": "str", + "required": false, + "help": "Single tweet URL to download. Use this OR , not both required at once." }, { "name": "limit", "type": "int", - "default": 20, + "default": 10, "required": false, - "help": "Max results (1-50)" + "help": "Maximum number of media items to download when scanning a profile (default 10). Ignored when --tweet-url is used." }, { - "name": "currency", + "name": "output", "type": "str", - "default": "us", + "default": "./twitter-downloads", "required": false, - "help": "Storefront country code (e.g. us / cn / jp / de)" + "help": "Output directory (default ./twitter-downloads). A per-source subdir is created inside.", + "file": { + "direction": "output", + "pathKind": "directory", + "multiple": false + } } ], "columns": [ - "rank", - "id", - "name", - "price", - "currency", - "metascore", - "platforms", - "url" - ], - "tags": [ - "search" + "index", + "tweet_id", + "url", + "type", + "status", + "size" ], "type": "js", - "modulePath": "plugins/steam/search.js", - "sourceFile": "plugins/steam/search.js" + "modulePath": "plugins/twitter/download.js", + "sourceFile": "plugins/twitter/download.js", + "navigateBefore": "https://x.com" }, { - "site": "steam", - "name": "top-sellers", - "description": "Steam top selling games", - "access": "read", - "domain": "store.steampowered.com", - "strategy": "public", - "browser": false, + "site": "twitter", + "name": "follow", + "description": "Follow a Twitter user", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of games" + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Twitter screen name (without @)" } ], "columns": [ - "rank", - "name", - "price", - "discount", - "url" + "status", + "message" ], "type": "js", - "modulePath": "plugins/steam/top-sellers.js", - "sourceFile": "plugins/steam/top-sellers.js" + "modulePath": "plugins/twitter/follow.js", + "sourceFile": "plugins/twitter/follow.js", + "navigateBefore": true }, { - "site": "substack", - "name": "feed", - "description": "Substack popular posts Feed", - "access": "read", - "domain": "substack.com", - "strategy": "cookie", + "site": "twitter", + "name": "follow-batch", + "description": "Follow multiple Twitter/X users from a comma-separated username list", + "access": "write", + "domain": "x.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "category", - "type": "str", - "default": "all", - "required": false, - "help": "Post category: all, tech, business, culture, politics, science, health" + "name": "usernames", + "type": "string", + "required": true, + "positional": true, + "help": "Comma-separated Twitter/X screen names, with or without @" }, { - "name": "limit", + "name": "delay-ms", "type": "int", - "default": 20, + "default": 3000, "required": false, - "help": "Number of posts to return" + "help": "Delay between follow attempts in milliseconds" } ], "columns": [ - "rank", - "title", - "author", - "date", - "readTime", - "url" + "username", + "status", + "message" ], "type": "js", - "modulePath": "plugins/substack/feed.js", - "sourceFile": "plugins/substack/feed.js", - "navigateBefore": "https://substack.com" + "modulePath": "plugins/twitter/follow-batch.js", + "sourceFile": "plugins/twitter/follow-batch.js", + "navigateBefore": true }, { - "site": "substack", - "name": "publication", - "description": "Get a specific Substack Newsletter latest posts", + "site": "twitter", + "name": "followers", + "description": "Get accounts following a Twitter/X user (defaults to the logged-in user when no user is given)", "access": "read", - "domain": "substack.com", - "strategy": "cookie", + "domain": "x.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "url", - "type": "str", - "required": true, + "name": "user", + "type": "string", + "required": false, "positional": true, - "help": "Newsletter URL(for example https://example.substack.com)" + "help": "Twitter/X handle (with or without @). Omit to fetch followers of the currently logged-in account." }, { "name": "limit", "type": "int", - "default": 20, + "default": 50, "required": false, - "help": "Number of posts to return" + "help": "Maximum number of follower rows to return (default 50). Must be a positive integer." } ], - "columns": [ - "rank", - "title", - "date", - "description", - "url" - ], + "columns": [ + "screen_name", + "name", + "bio" + ], "type": "js", - "modulePath": "plugins/substack/publication.js", - "sourceFile": "plugins/substack/publication.js", - "navigateBefore": "https://substack.com" + "modulePath": "plugins/twitter/followers.js", + "sourceFile": "plugins/twitter/followers.js", + "navigateBefore": true }, { - "site": "substack", - "name": "search", - "description": "Search Substack posts and newsletters", + "site": "twitter", + "name": "following", + "description": "Get accounts a Twitter/X user is following (defaults to the logged-in user when no user is given)", "access": "read", - "domain": "substack.com", - "strategy": "public", - "browser": false, + "domain": "x.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "type", - "type": "str", - "default": "posts", + "name": "user", + "type": "string", "required": false, - "help": "Search type(posts=posts, publications=Newsletter)", - "choices": [ - "posts", - "publications" - ] + "positional": true, + "help": "Twitter/X handle (with or without @). Omit to fetch the accounts the currently logged-in user follows." }, { "name": "limit", "type": "int", - "default": 20, + "default": 50, "required": false, - "help": "Number of results to return" + "help": "Maximum number of following rows to return (default 50). Must be a positive integer." } ], "columns": [ - "rank", - "title", - "author", - "date", - "description", - "url" - ], - "tags": [ - "search" + "screen_name", + "name", + "bio", + "followers" ], "type": "js", - "modulePath": "plugins/substack/search.js", - "sourceFile": "plugins/substack/search.js" + "modulePath": "plugins/twitter/following.js", + "sourceFile": "plugins/twitter/following.js", + "navigateBefore": "https://x.com" }, { - "site": "suno", - "name": "download", - "description": "Download an existing Suno clip (MP3 + optional WAV/M4A/video) by id", + "site": "twitter", + "name": "hide-reply", + "description": "Hide a reply on your tweet (useful for hiding bot/spam replies)", "access": "write", - "domain": "suno.com", - "strategy": "cookie", + "domain": "x.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "clip", - "type": "str", + "name": "url", + "type": "string", "required": true, "positional": true, - "help": "Clip UUID or https://suno.com/song/ URL" - }, - { - "name": "formats", - "type": "str", - "required": false, - "help": "Comma-separated formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" - }, - { - "name": "op", - "type": "str", - "required": false, - "help": "Output directory (default: ~/Music/suno)" - }, - { - "name": "confirm-paid", - "type": "boolean", - "default": false, - "required": false, - "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." + "help": "The URL of the reply tweet to hide" } ], "columns": [ "status", - "clip", - "title", - "files", - "link" + "message" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/suno/download.js", - "sourceFile": "plugins/suno/download.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/twitter/hide-reply.js", + "sourceFile": "plugins/twitter/hide-reply.js", + "navigateBefore": true }, { - "site": "suno", - "name": "generate", - "description": "Generate music with Suno (V5.5 chirp-fenix by default) and download clips locally", + "site": "twitter", + "name": "like", + "description": "Like a specific tweet", "access": "write", - "domain": "suno.com", - "strategy": "cookie", + "domain": "x.com", + "strategy": "ui", "browser": true, "args": [ { - "name": "prompt", - "type": "str", - "required": false, + "name": "url", + "type": "string", + "required": true, "positional": true, - "help": "Simple-mode description (ignored when --lyrics is provided)" - }, - { - "name": "lyrics", - "type": "str", - "required": false, - "help": "Custom-mode lyrics (with [Verse]/[Chorus] metatags). Triggers Custom mode." - }, - { - "name": "tags", - "type": "str", - "required": false, - "help": "Custom-mode style tags (genre, BPM, instruments...). Used with --lyrics." - }, - { - "name": "negative-tags", - "type": "str", - "required": false, - "help": "Custom-mode style exclusions (e.g. \"no vocals, no autotune\"). Used with --lyrics." - }, - { - "name": "title", - "type": "str", - "required": false, - "help": "Song title (default: auto-derived from prompt)" - }, - { - "name": "instrumental", - "type": "boolean", - "default": false, - "required": false, - "help": "No vocals" - }, - { - "name": "model", - "type": "str", - "required": false, - "help": "Model id: chirp-fenix, chirp-bluejay, chirp-v4, chirp-v3-5. Default: chirp-fenix" - }, - { - "name": "weirdness", - "type": "str", - "required": false, - "help": "Creative weirdness slider (0..1). Default: 0.5" - }, - { - "name": "style-weight", - "type": "str", - "required": false, - "help": "Style adherence slider (0..1). Default: 0.5" - }, - { - "name": "formats", - "type": "str", - "required": false, - "help": "Comma-separated download formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" - }, - { - "name": "op", - "type": "str", - "required": false, - "help": "Output directory (default: ~/Music/suno)" - }, - { - "name": "timeout", - "type": "int", - "default": 300, - "required": false, - "help": "Max seconds to wait for clips to finish (default: 300)" - }, - { - "name": "sd", - "type": "boolean", - "default": false, - "required": false, - "help": "Skip download; only print clip ids and Suno URLs" - }, - { - "name": "confirm-paid", - "type": "boolean", - "default": false, - "required": false, - "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." + "help": "The URL of the tweet to like" } ], "columns": [ "status", - "clip", - "title", - "files", - "link" + "message" ], - "defaultFormat": "plain", "type": "js", - "modulePath": "plugins/suno/generate.js", - "sourceFile": "plugins/suno/generate.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/twitter/like.js", + "sourceFile": "plugins/twitter/like.js", + "navigateBefore": true }, { - "site": "suno", - "name": "list", - "description": "List recent Suno clips in your library (id, title, status, created_at, link)", + "site": "twitter", + "name": "likes", + "description": "Fetch liked tweets of a Twitter user (defaults to the logged-in user when no username is given)", "access": "read", - "domain": "suno.com", + "domain": "x.com", "strategy": "cookie", "browser": true, "args": [ + { + "name": "username", + "type": "string", + "required": false, + "positional": true, + "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." + }, { "name": "limit", "type": "int", "default": 20, "required": false, - "help": "Max clips to list (default: 20)" + "help": "Maximum number of liked tweets to return (default 20)." }, { - "name": "page", + "name": "top-by-engagement", "type": "int", "default": 0, "required": false, - "help": "Pagination offset, 0-based (default: 0)" + "help": "When set to N>0, re-rank the liked tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (recency) ordering." } ], "columns": [ - "rank", - "clip", - "title", - "status", - "created", - "link" + "id", + "author", + "name", + "text", + "likes", + "retweets", + "created_at", + "url", + "has_media", + "media_urls", + "media_posters" ], "type": "js", - "modulePath": "plugins/suno/list.js", - "sourceFile": "plugins/suno/list.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/twitter/likes.js", + "sourceFile": "plugins/twitter/likes.js", + "navigateBefore": "https://x.com" }, { - "site": "suno", - "name": "login", - "description": "Open suno login", + "site": "twitter", + "name": "list-add", + "description": "Add a user to a Twitter/X list you own (no-op if already a member)", "access": "write", - "domain": "suno.com", - "strategy": "cookie", + "domain": "x.com", + "strategy": "ui", "browser": true, - "args": [], + "args": [ + { + "name": "listId", + "type": "string", + "required": true, + "positional": true, + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" + }, + { + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Twitter/X handle to add (with or without @)" + } + ], "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" + "listId", + "username", + "userId", + "status", + "message" ], "type": "js", - "modulePath": "plugins/suno/auth.js", - "sourceFile": "plugins/suno/auth.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/twitter/list-add.js", + "sourceFile": "plugins/twitter/list-add.js", + "navigateBefore": true }, { - "site": "suno", - "name": "status", - "description": "Check Suno login, plan, credit balance, and captcha readiness", - "access": "read", - "domain": "suno.com", - "strategy": "cookie", + "site": "twitter", + "name": "list-add-batch", + "description": "Add multiple users to a Twitter/X list you own from a comma-separated username list", + "access": "write", + "domain": "x.com", + "strategy": "ui", "browser": true, - "args": [], + "args": [ + { + "name": "listId", + "type": "string", + "required": true, + "positional": true, + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" + }, + { + "name": "usernames", + "type": "string", + "required": true, + "positional": true, + "help": "Comma-separated Twitter/X handles to add (with or without @)" + }, + { + "name": "interval", + "type": "int", + "default": 5, + "required": false, + "help": "Seconds to wait between account additions (default: 5)" + }, + { + "name": "timeout", + "type": "int", + "default": 600, + "required": false, + "help": "Max seconds for the overall batch command (default: 600)" + } + ], "columns": [ - "Status", - "Plan", - "Credits", - "Monthly", - "Captcha" + "listId", + "username", + "userId", + "status", + "message" ], "type": "js", - "modulePath": "plugins/suno/status.js", - "sourceFile": "plugins/suno/status.js", - "navigateBefore": false, - "siteSession": "persistent" + "modulePath": "plugins/twitter/list-add-batch.js", + "sourceFile": "plugins/twitter/list-add-batch.js", + "navigateBefore": true }, { - "site": "suno", - "name": "whoami", - "description": "Show the current logged-in suno account", - "access": "read", - "domain": "suno.com", + "site": "twitter", + "name": "list-create", + "description": "Create a new Twitter/X list (returns the new list id)", + "access": "write", + "domain": "x.com", "strategy": "cookie", "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "plugins/suno/auth.js", - "sourceFile": "plugins/suno/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "techcrunch", - "name": "article", - "description": "Read a TechCrunch article from its URL", - "access": "read", - "domain": "techcrunch.com", - "strategy": "public", - "browser": false, "args": [ { - "name": "url", + "name": "name", "type": "string", "required": true, "positional": true, - "help": "TechCrunch article URL" + "help": "List name (max 25 chars)" + }, + { + "name": "description", + "type": "string", + "default": "", + "required": false, + "help": "Optional list description (max 100 chars)" + }, + { + "name": "mode", + "type": "string", + "default": "public", + "required": false, + "help": "public | private" } ], "columns": [ - "title", - "author", - "publishedAt", - "categories", + "id", + "name", "description", - "content", - "url" + "mode", + "status" ], "type": "js", - "modulePath": "plugins/techcrunch/article.js", - "sourceFile": "plugins/techcrunch/article.js" + "modulePath": "plugins/twitter/list-create.js", + "sourceFile": "plugins/twitter/list-create.js", + "navigateBefore": "https://x.com" }, { - "site": "techcrunch", - "name": "search", - "description": "Search TechCrunch stories or list the latest stories", - "access": "read", - "domain": "techcrunch.com", - "strategy": "public", - "browser": false, + "site": "twitter", + "name": "list-delete", + "description": "Delete a Twitter/X list you own after explicit confirmation", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", + "name": "listId", "type": "string", - "required": false, + "required": true, "positional": true, - "help": "Words to search for" + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" }, { - "name": "latest", + "name": "confirm", "type": "boolean", "default": false, "required": false, - "help": "List the latest stories instead of searching" + "help": "Required. Set --confirm true to delete the list." }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 20, + "default": 300, "required": false, - "help": "Maximum stories to return (1-50)" + "help": "Max seconds for the overall delete command (default: 300)" } ], "columns": [ - "rank", - "title", - "author", - "publishedAt", - "description", - "url" - ], - "tags": [ - "search" + "listId", + "name", + "members", + "status", + "message" ], "type": "js", - "modulePath": "plugins/techcrunch/search.js", - "sourceFile": "plugins/techcrunch/search.js" + "modulePath": "plugins/twitter/list-delete.js", + "sourceFile": "plugins/twitter/list-delete.js", + "navigateBefore": true }, { - "site": "trae-solo", - "name": "automation-list", - "description": "List Trae SOLO Automation tab content. Default tab is \"Configured\"; pass --tab to switch.", - "access": "read", - "domain": "localhost", + "site": "twitter", + "name": "list-remove", + "description": "Remove a user from a Twitter/X list you own (toggles via UI; no-op if not currently a member)", + "access": "write", + "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "tab", - "type": "str", - "default": "configured", - "required": false, - "help": "Tab to view: configured / run-history / task-template" + "name": "listId", + "type": "string", + "required": true, + "positional": true, + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" }, { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "" + "name": "username", + "type": "string", + "required": true, + "positional": true, + "help": "Twitter/X handle to remove (with or without @)" } ], "columns": [ - "Index", - "Title", - "Summary" + "listId", + "username", + "userId", + "status", + "message" ], "type": "js", - "modulePath": "plugins/trae-solo/automation.js", - "sourceFile": "plugins/trae-solo/automation.js", + "modulePath": "plugins/twitter/list-remove.js", + "sourceFile": "plugins/twitter/list-remove.js", "navigateBefore": true }, { - "site": "trae-solo", - "name": "cookies", - "description": "List cookies on the Trae SOLO renderer (JS-visible via document.cookie; httpOnly cookies not shown).", - "access": "read", - "domain": "localhost", + "site": "twitter", + "name": "list-remove-batch", + "description": "Remove multiple users from a Twitter/X list you own from a comma-separated username list", + "access": "write", + "domain": "x.com", "strategy": "ui", "browser": true, - "args": [], + "args": [ + { + "name": "listId", + "type": "string", + "required": true, + "positional": true, + "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" + }, + { + "name": "usernames", + "type": "string", + "required": true, + "positional": true, + "help": "Comma-separated Twitter/X handles to remove (with or without @)" + }, + { + "name": "interval", + "type": "int", + "default": 5, + "required": false, + "help": "Seconds to wait between account removals (default: 5)" + }, + { + "name": "timeout", + "type": "int", + "default": 600, + "required": false, + "help": "Max seconds for the overall batch command (default: 600)" + } + ], "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version" + "listId", + "username", + "userId", + "status", + "message" ], "type": "js", - "modulePath": "plugins/trae-solo/renderer-storage.js", - "sourceFile": "plugins/trae-solo/renderer-storage.js", + "modulePath": "plugins/twitter/list-remove-batch.js", + "sourceFile": "plugins/twitter/list-remove-batch.js", "navigateBefore": true }, { - "site": "trae-solo", - "name": "extensions-list", - "description": "List VSCode extensions installed in Trae SOLO (~/.trae/extensions/extensions.json). Works while Trae is closed.", + "site": "twitter", + "name": "list-tweets", + "description": "Fetch tweets from a Twitter/X list timeline", "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "Index", - "Workspace Id", - "Kind", - "Target", - "Modified", - "Id", - "Version", - "Source", - "Installed" + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "listId", + "type": "string", + "required": true, + "positional": true, + "help": "Numeric ID of a Twitter/X list (e.g. from `webcmd twitter lists`)" + }, + { + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "" + }, + { + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank the list timeline by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the list's native (recency) ordering." + } + ], + "columns": [ + "id", + "author", + "bio", + "text", + "likes", + "retweets", + "replies", + "created_at", + "url", + "has_media", + "media_urls", + "media_posters", + "card", + "quoted_tweet" ], "type": "js", - "modulePath": "plugins/trae-solo/workspaces-fs.js", - "sourceFile": "plugins/trae-solo/workspaces-fs.js" + "modulePath": "plugins/twitter/list-tweets.js", + "sourceFile": "plugins/twitter/list-tweets.js", + "navigateBefore": "https://x.com" }, { - "site": "trae-solo", - "name": "history", - "description": "List Trae SOLO projects and the tasks within each (from the project-list view sidebar).", + "site": "twitter", + "name": "lists", + "description": "Get Twitter/X lists for the logged-in user (owned + subscribed)", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "x.com", + "strategy": "cookie", "browser": true, "args": [ - { - "name": "project", - "type": "str", - "required": false, - "help": "Filter by project name (substring, case-insensitive)" - }, { "name": "limit", "type": "int", - "default": 100, + "default": 50, "required": false, - "help": "Max tasks per project" + "help": "Maximum number of lists to return (default 50)." } ], "columns": [ - "Project", - "Task Index", - "Task" + "id", + "name", + "members", + "followers", + "mode" ], "type": "js", - "modulePath": "plugins/trae-solo/history.js", - "sourceFile": "plugins/trae-solo/history.js", - "navigateBefore": true + "modulePath": "plugins/twitter/lists.js", + "sourceFile": "plugins/twitter/lists.js", + "navigateBefore": "https://x.com" }, { - "site": "trae-solo", - "name": "idb-list", - "description": "List IndexedDB databases on the Trae SOLO renderer. Trae ships an @byted/ve-rtc DB used by the Volcengine RTC voice/video infrastructure.", - "access": "read", - "domain": "localhost", - "strategy": "ui", + "site": "twitter", + "name": "login", + "description": "Open twitter login", + "access": "write", + "domain": "x.com", + "strategy": "cookie", "browser": true, "args": [], "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version" + "status", + "logged_in", + "site", + "username", + "url", + "action", + "verify_command" ], "type": "js", - "modulePath": "plugins/trae-solo/renderer-storage.js", - "sourceFile": "plugins/trae-solo/renderer-storage.js", - "navigateBefore": true + "modulePath": "plugins/twitter/auth.js", + "sourceFile": "plugins/twitter/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { - "site": "trae-solo", - "name": "mode", - "description": "Read or switch TRAE SOLO between Code mode and Work mode.", - "access": "write", - "domain": "localhost", - "strategy": "ui", + "site": "twitter", + "name": "notifications", + "description": "Get your Twitter/X notifications (the logged-in user's likes/replies/follows feed, newest first)", + "access": "read", + "domain": "x.com", + "strategy": "intercept", "browser": true, "args": [ { - "name": "target", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "positional": true, - "help": "Target mode: code or work. Omit to read current." + "help": "Maximum number of notifications to return (default 20)." } ], "columns": [ - "Status", - "Mode" + "id", + "action", + "author", + "text", + "url" ], "type": "js", - "modulePath": "plugins/trae-solo/mode.js", - "sourceFile": "plugins/trae-solo/mode.js", + "modulePath": "plugins/twitter/notifications.js", + "sourceFile": "plugins/twitter/notifications.js", "navigateBefore": true }, { - "site": "trae-solo", - "name": "model", - "description": "Read or switch the current AI model in TRAE SOLO. Without arguments, reports the current model. With argument (substring, case-insensitive), switches to a matching model. Pass --list to enumerate available models.", + "site": "twitter", + "name": "post", + "description": "Post a new tweet/thread", "access": "write", - "domain": "localhost", + "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "name", - "type": "str", - "required": false, + "name": "text", + "type": "string", + "required": true, "positional": true, - "help": "Target model name (substring match, case-insensitive). Omit to read current." + "help": "The text content of the tweet" }, { - "name": "list", - "type": "boolean", - "default": false, + "name": "images", + "type": "string", "required": false, - "help": "List all available models (does not switch)" + "help": "Image paths, comma-separated, max 4 (jpg/png/gif/webp)", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": true, + "separator": ",", + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } } ], "columns": [ - "Status", - "Model" + "status", + "message", + "text", + "id", + "url" ], "type": "js", - "modulePath": "plugins/trae-solo/model.js", - "sourceFile": "plugins/trae-solo/model.js", + "modulePath": "plugins/twitter/post.js", + "sourceFile": "plugins/twitter/post.js", "navigateBefore": true }, { - "site": "trae-solo", - "name": "recent-workspaces", - "description": "Show Trae SOLO's recently-opened workspaces (the File → Open Recent menu, stored under key \"history.recentlyOpenedPathsList\" in state.vscdb).", + "site": "twitter", + "name": "profile", + "description": "Fetch a Twitter user profile — bio, stats, etc. (defaults to the logged-in user when no username is given)", "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "domain": "x.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 20, + "name": "username", + "type": "string", "required": false, - "help": "" + "positional": true, + "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." } ], "columns": [ - "Index", - "Key", - "Kind", - "Path" - ], - "type": "js", - "modulePath": "plugins/trae-solo/state-fs.js", - "sourceFile": "plugins/trae-solo/state-fs.js" - }, - { - "site": "trae-solo", - "name": "settings-read", - "description": "Parse and pretty-print Trae SOLO user settings.json (~/Library/Application Support/TRAE SOLO/User/settings.json). Handles VSCode JSONC syntax (line comments + trailing commas).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "Field", - "Value" + "screen_name", + "name", + "bio", + "location", + "url", + "followers", + "following", + "tweets", + "likes", + "verified", + "created_at" ], "type": "js", - "modulePath": "plugins/trae-solo/settings.js", - "sourceFile": "plugins/trae-solo/settings.js" + "modulePath": "plugins/twitter/profile.js", + "sourceFile": "plugins/twitter/profile.js", + "navigateBefore": "https://x.com" }, { - "site": "trae-solo", - "name": "skill-category", - "description": "Filter Skills Marketplace by category. Pass --list to see categories.", - "access": "read", - "domain": "localhost", + "site": "twitter", + "name": "quote", + "description": "Quote-tweet a specific tweet with your own text, optionally with a local or remote image", + "access": "write", + "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "name", - "type": "str", - "required": false, + "name": "url", + "type": "string", + "required": true, "positional": true, - "help": "Category name (substring; case-insensitive). Common: All / Developer Tools / Data Analysis / UI Design / Content Creation / Productivity" + "help": "The URL of the tweet to quote" }, { - "name": "list", - "type": "boolean", - "default": false, - "required": false, - "help": "List available categories" + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "The text content of your quote" }, { - "name": "limit", - "type": "int", - "default": 100, + "name": "image", + "type": "str", "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Name", - "Description" - ], - "type": "js", - "modulePath": "plugins/trae-solo/skill.js", - "sourceFile": "plugins/trae-solo/skill.js", - "navigateBefore": true - }, - { - "site": "trae-solo", - "name": "skill-fs-installed", - "description": "List INSTALLED Trae SOLO skills (managedSkills entry in ~/.trae/skill-config.json).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "Index", - "Name", - "Description", - "Source" - ], - "type": "js", - "modulePath": "plugins/trae-solo/skill-fs.js", - "sourceFile": "plugins/trae-solo/skill-fs.js" - }, - { - "site": "trae-solo", - "name": "skill-fs-list", - "description": "List all Trae SOLO skills present on disk under ~/.trae/skills/. Reads SKILL.md front-matter for descriptions. Works while Trae is closed.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ + "help": "Optional local image path to attach to the quote tweet", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + }, { - "name": "limit", - "type": "int", - "default": 200, + "name": "image-url", + "type": "str", "required": false, - "help": "Max rows" + "help": "Optional remote image URL to download and attach to the quote tweet" } ], "columns": [ - "Index", - "Name", - "Description", - "Source" + "status", + "message", + "text" ], "type": "js", - "modulePath": "plugins/trae-solo/skill-fs.js", - "sourceFile": "plugins/trae-solo/skill-fs.js" + "modulePath": "plugins/twitter/quote.js", + "sourceFile": "plugins/twitter/quote.js", + "navigateBefore": true }, { - "site": "trae-solo", - "name": "skill-fs-show", - "description": "Print a skill's SKILL.md content + on-disk path.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "site": "twitter", + "name": "reply", + "description": "Reply to a specific tweet, optionally with a local or remote image", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "name", - "type": "str", + "name": "url", + "type": "string", "required": true, "positional": true, - "help": "Skill name (folder under ~/.trae/skills/)" + "help": "The URL of the tweet to reply to" + }, + { + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "The text content of your reply" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Optional local image path to attach to the reply", + "file": { + "direction": "input", + "pathKind": "file", + "multiple": false, + "contentTypes": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp" + ], + "maxBytes": 26214400 + } + }, + { + "name": "image-url", + "type": "str", + "required": false, + "help": "Optional remote image URL to download and attach to the reply" } ], "columns": [ - "Field", - "Value" + "status", + "message", + "text", + "url" ], "type": "js", - "modulePath": "plugins/trae-solo/skill-fs.js", - "sourceFile": "plugins/trae-solo/skill-fs.js" + "modulePath": "plugins/twitter/reply.js", + "sourceFile": "plugins/twitter/reply.js", + "navigateBefore": true }, { - "site": "trae-solo", - "name": "skill-list", - "description": "List Trae SOLO Skills — by default the Marketplace; pass --installed to list installed ones.", - "access": "read", - "domain": "localhost", + "site": "twitter", + "name": "reply-dm", + "description": "Send a message to recent DM conversations", + "access": "write", + "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "installed", + "name": "text", + "type": "string", + "required": true, + "positional": true, + "help": "Message text to send (e.g. \"my messaging handle wxkabi\")" + }, + { + "name": "max", + "type": "int", + "default": 20, + "required": false, + "help": "Maximum number of conversations to reply to (default: 20)" + }, + { + "name": "skip-replied", "type": "boolean", - "default": false, + "default": true, "required": false, - "help": "List installed skills instead of the marketplace" + "help": "Skip conversations where you already sent the same text (default: true)" }, { - "name": "limit", + "name": "timeout", "type": "int", - "default": 100, + "default": 600, "required": false, - "help": "Max rows to return" + "help": "Max seconds for the overall command (default: 600 — batch op)" } ], "columns": [ - "Index", - "Name", - "Description" + "index", + "status", + "user", + "message" ], "type": "js", - "modulePath": "plugins/trae-solo/skill.js", - "sourceFile": "plugins/trae-solo/skill.js", + "modulePath": "plugins/twitter/reply-dm.js", + "sourceFile": "plugins/twitter/reply-dm.js", "navigateBefore": true }, { - "site": "trae-solo", - "name": "skill-search", - "description": "Filter Skills Marketplace by keyword.", - "access": "read", - "domain": "localhost", + "site": "twitter", + "name": "retweet", + "description": "Retweet a specific tweet", + "access": "write", + "domain": "x.com", "strategy": "ui", "browser": true, "args": [ { - "name": "keyword", - "type": "str", + "name": "url", + "type": "string", "required": true, "positional": true, - "help": "Search keyword (substring)" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max rows" + "help": "The URL of the tweet to retweet" } ], "columns": [ - "Index", - "Name", - "Description" - ], - "tags": [ - "search" + "status", + "message" ], "type": "js", - "modulePath": "plugins/trae-solo/skill.js", - "sourceFile": "plugins/trae-solo/skill.js", + "modulePath": "plugins/twitter/retweet.js", + "sourceFile": "plugins/twitter/retweet.js", "navigateBefore": true }, { - "site": "trae-solo", - "name": "state-get", - "description": "Read a single key from Trae SOLO's globalStorage state.vscdb. Pass --workspace to query a per-workspace DB instead. Returns parsed JSON if the value is JSON.", + "site": "twitter", + "name": "search", + "description": "Search Twitter/X for tweets, with optional --from / --has / --exclude / --product filters mapped to X's search operators", "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "domain": "x.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "key", - "type": "str", + "name": "query", + "type": "string", "required": true, "positional": true, - "help": "State key (use state-keys to discover)" + "help": "Search query. Raw X operators (e.g. \"exact phrase\", #tag, OR, lang:en, since:YYYY-MM-DD, from:, since:) are passed through unchanged." }, { - "name": "workspace", - "type": "str", + "name": "filter", + "type": "string", + "default": "top", "required": false, - "help": "Workspace id (from workspaces-list) to query a per-workspace DB" + "help": "Legacy alias for --product. Kept for backwards compatibility; if --product is set it wins.", + "choices": [ + "top", + "live" + ] }, { - "name": "max-bytes", - "type": "int", - "default": 8000, + "name": "product", + "type": "string", "required": false, - "help": "Truncate value to this many bytes" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/trae-solo/state-fs.js", - "sourceFile": "plugins/trae-solo/state-fs.js" - }, - { - "site": "trae-solo", - "name": "state-keys", - "description": "List all keys present in Trae SOLO's globalStorage state.vscdb (VSCode-style UI/agent state). Pass --workspace to query a per-workspace DB instead. Use state-get to read a specific value. (See renderer storage-keys for browser-side LS/SS.)", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ + "help": "Which X search tab to read: top (default), live (Latest), photos, videos. Maps to the f= URL param.", + "choices": [ + "top", + "live", + "photos", + "videos" + ] + }, { - "name": "filter", - "type": "str", + "name": "from", + "type": "string", "required": false, - "help": "Case-insensitive substring filter over keys" + "help": "Restrict to tweets authored by . Leading @ is stripped. Equivalent to appending `from:` to the query." }, { - "name": "workspace", - "type": "str", + "name": "has", + "type": "string", "required": false, - "help": "Workspace id (from workspaces-list) to query a per-workspace DB" + "help": "Restrict to tweets that have media|images|videos|links|replies. Maps to X's `filter:` operator.", + "choices": [ + "media", + "images", + "videos", + "links", + "replies" + ] + }, + { + "name": "exclude", + "type": "string", + "required": false, + "help": "Exclude tweets matching : replies|retweets|media|links. Maps to X's `-filter:` operator (retweets → -filter:nativeretweets).", + "choices": [ + "replies", + "retweets", + "media", + "links" + ] }, { "name": "limit", "type": "int", - "default": 200, + "default": 15, "required": false, - "help": "" + "help": "Maximum number of tweets to return (default 15). Result count after server-side filtering." + }, + { + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank the results by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X's native ordering." } ], "columns": [ - "Index", - "Key", - "Kind", - "Path" + "id", + "author", + "bio", + "text", + "created_at", + "likes", + "views", + "url", + "has_media", + "media_urls", + "media_posters", + "card", + "quoted_tweet" ], - "type": "js", - "modulePath": "plugins/trae-solo/state-fs.js", - "sourceFile": "plugins/trae-solo/state-fs.js" - }, - { - "site": "trae-solo", - "name": "status", - "description": "Check active CDP connection to Trae SOLO Desktop", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/trae-solo/status.js", - "sourceFile": "plugins/trae-solo/status.js", - "navigateBefore": true + "modulePath": "plugins/twitter/search.js", + "sourceFile": "plugins/twitter/search.js", + "navigateBefore": "https://x.com" }, { - "site": "trae-solo", - "name": "storage-get", - "description": "Read a single localStorage / sessionStorage value on the Trae SOLO renderer.", + "site": "twitter", + "name": "thread", + "description": "Get a tweet thread (original + all replies)", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "x.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "key", - "type": "str", + "name": "tweet-id", + "type": "string", "required": true, "positional": true, - "help": "Storage key (use storage-keys to discover)" + "help": "Tweet numeric ID (e.g. 1234567890) or full status URL" }, { - "name": "storage", - "type": "str", - "default": "local", + "name": "limit", + "type": "int", + "default": 50, "required": false, - "help": "\"local\" or \"session\"" + "help": "" }, { - "name": "max-bytes", + "name": "top-by-engagement", "type": "int", - "default": 4000, + "default": 0, "required": false, - "help": "Truncate value to this many chars" + "help": "When set to N>0, re-rank the thread by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the conversation's structural ordering." } ], "columns": [ - "Field", - "Value" + "id", + "author", + "bio", + "text", + "likes", + "retweets", + "url", + "has_media", + "media_urls", + "media_posters", + "card", + "quoted_tweet" ], "type": "js", - "modulePath": "plugins/trae-solo/renderer-storage.js", - "sourceFile": "plugins/trae-solo/renderer-storage.js", - "navigateBefore": true + "modulePath": "plugins/twitter/thread.js", + "sourceFile": "plugins/twitter/thread.js", + "navigateBefore": "https://x.com" }, { - "site": "trae-solo", - "name": "storage-keys", - "description": "List localStorage / sessionStorage keys on the Trae SOLO renderer (CDP). For the on-disk VSCode state.vscdb, see state-keys.", + "site": "twitter", + "name": "timeline", + "description": "Fetch the logged-in user's home timeline (for-you algorithmic feed by default; pass --type following for the chronological feed of accounts you follow)", "access": "read", - "domain": "localhost", - "strategy": "ui", + "domain": "x.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "storage", + "name": "type", "type": "str", - "default": "local", + "default": "for-you", "required": false, - "help": "\"local\" or \"session\"" + "help": "Which home-timeline feed to read. Default for-you (algorithmic). Use following for the chronological feed of accounts you follow.", + "choices": [ + "for-you", + "following" + ] }, { - "name": "filter", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Case-insensitive substring filter" + "help": "Maximum number of tweets to return (default 20)." }, { - "name": "limit", + "name": "top-by-engagement", "type": "int", - "default": 100, + "default": 0, "required": false, - "help": "Max rows to return" + "help": "When set to N>0, re-rank the timeline by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X's native ordering." } ], "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version" + "id", + "author", + "bio", + "text", + "likes", + "retweets", + "replies", + "quotes", + "bookmarks", + "views", + "created_at", + "url", + "has_media", + "media_urls", + "media_posters", + "card", + "quoted_tweet" ], "type": "js", - "modulePath": "plugins/trae-solo/renderer-storage.js", - "sourceFile": "plugins/trae-solo/renderer-storage.js", - "navigateBefore": true + "modulePath": "plugins/twitter/timeline.js", + "sourceFile": "plugins/twitter/timeline.js", + "navigateBefore": "https://x.com" }, { - "site": "trae-solo", - "name": "task-fs-list", - "description": "List Trae SOLO task ids from disk (snapshot/ + agentconfig/.json). Works while Trae is closed.", + "site": "twitter", + "name": "trending", + "description": "Twitter/X trending topics", "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "domain": "x.com", + "strategy": "cookie", + "browser": true, "args": [ { "name": "limit", "type": "int", - "default": 100, + "default": 20, "required": false, - "help": "" + "help": "Number of trends to show" } ], "columns": [ - "Index", - "Task Id", - "Has Snapshot", - "Has Config", - "Modified", - "Phase", - "Turn Id", - "Commit" + "rank", + "topic", + "category" ], "type": "js", - "modulePath": "plugins/trae-solo/task-fs.js", - "sourceFile": "plugins/trae-solo/task-fs.js" + "modulePath": "plugins/twitter/trending.js", + "sourceFile": "plugins/twitter/trending.js", + "navigateBefore": "https://x.com" }, { - "site": "trae-solo", - "name": "task-fs-show", - "description": "Show the workspace tree at a given chat-turn ref (via git ls-tree). Pass --turn to pick a turn; otherwise the latest after-chat-turn ref.", + "site": "twitter", + "name": "tweets", + "description": "Fetch a Twitter user's most recent tweets (chronological, excludes pinned; defaults to the logged-in user when no username is given)", "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "domain": "x.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "task-id", - "type": "str", - "required": true, + "name": "username", + "type": "string", + "required": false, "positional": true, - "help": "Task UUID" + "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." }, { - "name": "turn", - "type": "str", + "name": "limit", + "type": "int", + "default": 20, "required": false, - "help": "Specific turn id (omit for latest after-chat-turn)" + "help": "Max tweets to return (1-10000; fetched across cursor pages)" }, { - "name": "limit", + "name": "page-delay", "type": "int", - "default": 50, + "default": 2, "required": false, - "help": "" + "help": "Seconds to wait between paginated timeline requests to reduce rate-limit risk. Use 0 to disable." + }, + { + "name": "top-by-engagement", + "type": "int", + "default": 0, + "required": false, + "help": "When set to N>0, re-rank the tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the chronological ordering." } ], "columns": [ - "Mode", - "Path", - "Size" + "id", + "author", + "created_at", + "is_retweet", + "text", + "likes", + "retweets", + "replies", + "views", + "url", + "has_media", + "media_urls", + "media_posters", + "quoted_tweet" ], "type": "js", - "modulePath": "plugins/trae-solo/task-fs.js", - "sourceFile": "plugins/trae-solo/task-fs.js" + "modulePath": "plugins/twitter/tweets.js", + "sourceFile": "plugins/twitter/tweets.js", + "navigateBefore": "https://x.com" }, { - "site": "trae-solo", - "name": "task-fs-turns", - "description": "Show the chat-turn timeline for a Trae SOLO task as git tags (before-chat-turn-* / after-chat-turn-*).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "site": "twitter", + "name": "unblock", + "description": "Unblock a Twitter user", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "task-id", - "type": "str", + "name": "username", + "type": "string", "required": true, "positional": true, - "help": "Task UUID (folder name under snapshot/)" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "" + "help": "Twitter screen name (without @)" } ], "columns": [ - "Index", - "Task Id", - "Has Snapshot", - "Has Config", - "Modified", - "Phase", - "Turn Id", - "Commit" - ], - "type": "js", - "modulePath": "plugins/trae-solo/task-fs.js", - "sourceFile": "plugins/trae-solo/task-fs.js" - }, - { - "site": "trae-solo", - "name": "user-rules", - "description": "Print Trae SOLO user rules (~/.trae/user_rules.md).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "Field", - "Value" + "status", + "message" ], "type": "js", - "modulePath": "plugins/trae-solo/user-rules.js", - "sourceFile": "plugins/trae-solo/user-rules.js" + "modulePath": "plugins/twitter/unblock.js", + "sourceFile": "plugins/twitter/unblock.js", + "navigateBefore": true }, { - "site": "trae-solo", - "name": "workspaces-list", - "description": "List Trae SOLO workspaceStorage entries (~/Library/.../TRAE SOLO/User/workspaceStorage//), resolving each workspace.json to its single-folder path or multi-folder workspace target. Works while Trae is closed.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, + "site": "twitter", + "name": "unbookmark", + "description": "Remove a tweet from bookmarks", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "" + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "Tweet URL to unbookmark" } ], "columns": [ - "Index", - "Workspace Id", - "Kind", - "Target", - "Modified", - "Id", - "Version", - "Source", - "Installed" + "status", + "message" ], "type": "js", - "modulePath": "plugins/trae-solo/workspaces-fs.js", - "sourceFile": "plugins/trae-solo/workspaces-fs.js" + "modulePath": "plugins/twitter/unbookmark.js", + "sourceFile": "plugins/twitter/unbookmark.js", + "navigateBefore": true }, { - "site": "tvmaze", - "name": "search", - "description": "TVmaze TV show search by title (returns id, name, network, premiered/ended, rating)", - "access": "read", - "domain": "tvmaze.com", - "strategy": "public", - "browser": false, + "site": "twitter", + "name": "unfollow", + "description": "Unfollow a Twitter user", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "query", + "name": "username", "type": "string", "required": true, "positional": true, - "help": "TV show title or fragment to search for" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max rows to return (1-50)" + "help": "Twitter screen name (without @)" } ], "columns": [ - "rank", - "id", - "name", - "type", - "language", - "genres", "status", - "premiered", - "ended", - "network", - "rating", - "matchScore", - "summary", - "url" + "message" + ], + "type": "js", + "modulePath": "plugins/twitter/unfollow.js", + "sourceFile": "plugins/twitter/unfollow.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "unlike", + "description": "Remove a like from a specific tweet", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, + "args": [ + { + "name": "url", + "type": "string", + "required": true, + "positional": true, + "help": "The URL of the tweet to unlike" + } ], - "tags": [ - "search" + "columns": [ + "status", + "message" ], "type": "js", - "modulePath": "plugins/tvmaze/search.js", - "sourceFile": "plugins/tvmaze/search.js" + "modulePath": "plugins/twitter/unlike.js", + "sourceFile": "plugins/twitter/unlike.js", + "navigateBefore": true }, { - "site": "tvmaze", - "name": "show", - "description": "Single TVmaze TV show detail by id (network, schedule, rating, IMDB/TheTVDB cross-refs)", - "access": "read", - "domain": "tvmaze.com", - "strategy": "public", - "browser": false, + "site": "twitter", + "name": "unretweet", + "description": "Undo a retweet on a specific tweet", + "access": "write", + "domain": "x.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "id", - "type": "int", + "name": "url", + "type": "string", "required": true, "positional": true, - "help": "TVmaze show id (positive integer)" + "help": "The URL of the tweet to unretweet" } ], "columns": [ - "id", - "name", - "type", - "language", - "genres", "status", - "premiered", - "ended", - "runtime", - "averageRuntime", - "network", - "country", - "schedule", - "rating", - "imdb", - "thetvdb", - "officialSite", - "summary", + "message" + ], + "type": "js", + "modulePath": "plugins/twitter/unretweet.js", + "sourceFile": "plugins/twitter/unretweet.js", + "navigateBefore": true + }, + { + "site": "twitter", + "name": "whoami", + "description": "Show the current logged-in twitter account", + "access": "read", + "domain": "x.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "username", "url" ], "type": "js", - "modulePath": "plugins/tvmaze/show.js", - "sourceFile": "plugins/tvmaze/show.js" + "modulePath": "plugins/twitter/auth.js", + "sourceFile": "plugins/twitter/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "ualberta", @@ -19337,652 +28181,1002 @@ ] }, { - "name": "frames", + "name": "frames", + "type": "str", + "default": "same-origin", + "required": false, + "help": "Iframe handling mode: relevant same-origin, all-same-origin, or none", + "choices": [ + "same-origin", + "all-same-origin", + "none" + ] + }, + { + "name": "diagnose", + "type": "boolean", + "default": false, + "required": false, + "help": "Print render diagnostics (frames, empty containers, XHR/API-like requests) to stderr" + }, + { + "name": "stdout", + "type": "boolean", + "default": false, + "required": false, + "help": "Print markdown to stdout instead of saving to a file" + } + ], + "columns": [ + "title", + "author", + "publish_time", + "status", + "size", + "saved" + ], + "type": "js", + "modulePath": "plugins/web/fetch-browser.js", + "sourceFile": "plugins/web/fetch-browser.js", + "navigateBefore": false + }, + { + "site": "wikidata", + "name": "entity", + "description": "Fetch a Wikidata entity by Q/P/L id (label, description, aliases, claim summary)", + "access": "read", + "domain": "www.wikidata.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Entity id (e.g. Q937 = Albert Einstein, P31 = instance of)" + }, + { + "name": "language", + "type": "str", + "default": "en", + "required": false, + "help": "Display language (ISO 639, falls back to English when missing)" + } + ], + "columns": [ + "qid", + "type", + "label", + "description", + "aliases", + "claimPropertyCount", + "sitelinkCount", + "enwikiTitle", + "modified", + "url" + ], + "type": "js", + "modulePath": "plugins/wikidata/entity.js", + "sourceFile": "plugins/wikidata/entity.js" + }, + { + "site": "wikidata", + "name": "search", + "description": "Search Wikidata items by keyword (returns Q-IDs)", + "access": "read", + "domain": "www.wikidata.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", + "type": "str", + "required": true, + "positional": true, + "help": "Search keyword (label / alias)" + }, + { + "name": "language", + "type": "str", + "default": "en", + "required": false, + "help": "Search & display language (ISO 639, e.g. en, fr, zh)" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max items (1-50)" + } + ], + "columns": [ + "rank", + "qid", + "label", + "description", + "matchType", + "matchText", + "url" + ], + "tags": [ + "search" + ], + "type": "js", + "modulePath": "plugins/wikidata/search.js", + "sourceFile": "plugins/wikidata/search.js" + }, + { + "site": "wikipedia", + "name": "page", + "description": "Full plain-text extract of a Wikipedia article (optional paragraph cap).", + "access": "read", + "domain": "wikipedia.org", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "title", + "type": "string", + "required": true, + "positional": true, + "help": "Article title (e.g. \"Transformer (machine learning model)\")" + }, + { + "name": "lang", + "type": "string", + "default": "en", + "required": false, + "help": "Language code (en, zh, ja, de, ...)." + }, + { + "name": "paragraphs", + "type": "int", + "default": 0, + "required": false, + "help": "Cap to first N paragraphs (0 = full article)." + } + ], + "columns": [ + "title", + "description", + "pageId", + "paragraphs", + "extract", + "url" + ], + "type": "js", + "modulePath": "plugins/wikipedia/page.js", + "sourceFile": "plugins/wikipedia/page.js" + }, + { + "site": "wikipedia", + "name": "random", + "description": "Get a random Wikipedia article", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "lang", + "type": "str", + "default": "en", + "required": false, + "help": "Language code (e.g. en, zh, ja)" + } + ], + "columns": [ + "title", + "description", + "extract", + "url" + ], + "type": "js", + "modulePath": "plugins/wikipedia/random.js", + "sourceFile": "plugins/wikipedia/random.js" + }, + { + "site": "wikipedia", + "name": "search", + "description": "Search Wikipedia articles", + "access": "read", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "query", "type": "str", - "default": "same-origin", - "required": false, - "help": "Iframe handling mode: relevant same-origin, all-same-origin, or none", - "choices": [ - "same-origin", - "all-same-origin", - "none" - ] + "required": true, + "positional": true, + "help": "Search keyword" }, { - "name": "diagnose", - "type": "boolean", - "default": false, + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Print render diagnostics (frames, empty containers, XHR/API-like requests) to stderr" + "help": "Max results" }, { - "name": "stdout", - "type": "boolean", - "default": false, + "name": "lang", + "type": "str", + "default": "en", "required": false, - "help": "Print markdown to stdout instead of saving to a file" + "help": "Language code (e.g. en, zh, ja)" } ], "columns": [ "title", - "author", - "publish_time", - "status", - "size", - "saved" + "snippet", + "url" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/web/fetch-browser.js", - "sourceFile": "plugins/web/fetch-browser.js", - "navigateBefore": false + "modulePath": "plugins/wikipedia/search.js", + "sourceFile": "plugins/wikipedia/search.js" }, { - "site": "wikidata", - "name": "entity", - "description": "Fetch a Wikidata entity by Q/P/L id (label, description, aliases, claim summary)", + "site": "wikipedia", + "name": "summary", + "description": "Get Wikipedia article summary", "access": "read", - "domain": "www.wikidata.org", "strategy": "public", "browser": false, "args": [ { - "name": "id", + "name": "title", "type": "str", "required": true, "positional": true, - "help": "Entity id (e.g. Q937 = Albert Einstein, P31 = instance of)" + "help": "Article title (e.g. \"Transformer (machine learning model)\")" }, { - "name": "language", + "name": "lang", "type": "str", "default": "en", "required": false, - "help": "Display language (ISO 639, falls back to English when missing)" + "help": "Language code (e.g. en, zh, ja)" } ], "columns": [ - "qid", - "type", - "label", + "title", "description", - "aliases", - "claimPropertyCount", - "sitelinkCount", - "enwikiTitle", - "modified", + "extract", "url" ], "type": "js", - "modulePath": "plugins/wikidata/entity.js", - "sourceFile": "plugins/wikidata/entity.js" + "modulePath": "plugins/wikipedia/summary.js", + "sourceFile": "plugins/wikipedia/summary.js" }, { - "site": "wikidata", - "name": "search", - "description": "Search Wikidata items by keyword (returns Q-IDs)", + "site": "wikipedia", + "name": "trending", + "description": "Most-read Wikipedia articles (yesterday)", "access": "read", - "domain": "www.wikidata.org", "strategy": "public", "browser": false, "args": [ { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (label / alias)" + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Max results" }, { - "name": "language", + "name": "lang", "type": "str", "default": "en", "required": false, - "help": "Search & display language (ISO 639, e.g. en, fr, zh)" + "help": "Language code (e.g. en, zh, ja)" + } + ], + "columns": [ + "rank", + "title", + "description", + "views" + ], + "type": "js", + "modulePath": "plugins/wikipedia/trending.js", + "sourceFile": "plugins/wikipedia/trending.js" + }, + { + "site": "wttr", + "name": "current", + "description": "Current weather conditions for a location (city, lat,lon, or airport code)", + "access": "read", + "domain": "wttr.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "location", + "type": "str", + "required": true, + "positional": true, + "help": "City name, \"lat,lon\", airport ICAO code, or \"@domain\"" + } + ], + "columns": [ + "location", + "region", + "country", + "latitude", + "longitude", + "observedAt", + "tempC", + "tempF", + "feelsLikeC", + "feelsLikeF", + "description", + "humidity", + "cloudCover", + "pressure", + "precipMm", + "visibilityKm", + "uvIndex", + "windKmph", + "windDirection", + "windDirectionDegree" + ], + "type": "js", + "modulePath": "plugins/wttr/current.js", + "sourceFile": "plugins/wttr/current.js" + }, + { + "site": "wttr", + "name": "forecast", + "description": "Multi-day weather forecast (up to 3 days, wttr.in free tier max)", + "access": "read", + "domain": "wttr.in", + "strategy": "public", + "browser": false, + "args": [ + { + "name": "location", + "type": "str", + "required": true, + "positional": true, + "help": "City name, \"lat,lon\", airport ICAO code, or \"@domain\"" }, { - "name": "limit", + "name": "days", "type": "int", - "default": 20, + "default": 3, "required": false, - "help": "Max items (1-50)" + "help": "Max forecast days (1-3, wttr.in caps the response at 3 days)" } ], "columns": [ "rank", - "qid", - "label", + "date", + "minTempC", + "maxTempC", + "avgTempC", + "minTempF", + "maxTempF", + "avgTempF", + "sunHour", + "totalSnowCm", + "uvIndex", "description", - "matchType", - "matchText", - "url" - ], - "tags": [ - "search" + "sunrise", + "sunset" ], "type": "js", - "modulePath": "plugins/wikidata/search.js", - "sourceFile": "plugins/wikidata/search.js" + "modulePath": "plugins/wttr/forecast.js", + "sourceFile": "plugins/wttr/forecast.js" }, { - "site": "wikipedia", - "name": "page", - "description": "Full plain-text extract of a Wikipedia article (optional paragraph cap).", + "site": "yahoo", + "name": "search", + "description": "Search Yahoo (powered by Bing)", "access": "read", - "domain": "wikipedia.org", + "domain": "search.yahoo.com", "strategy": "public", - "browser": false, + "browser": true, "args": [ { - "name": "title", - "type": "string", + "name": "keyword", + "type": "str", "required": true, "positional": true, - "help": "Article title (e.g. \"Transformer (machine learning model)\")" + "help": "Search query" }, { - "name": "lang", - "type": "string", - "default": "en", + "name": "limit", + "type": "int", + "default": 7, "required": false, - "help": "Language code (en, zh, ja, de, ...)." + "help": "Number of results per page (max 7)" }, { - "name": "paragraphs", + "name": "page", "type": "int", - "default": 0, + "default": 1, "required": false, - "help": "Cap to first N paragraphs (0 = full article)." + "help": "Page number (1, 2, 3...). Yahoo returns ~7 results per page" } ], "columns": [ + "rank", "title", - "description", - "pageId", - "paragraphs", - "extract", - "url" + "url", + "snippet" + ], + "tags": [ + "search" ], "type": "js", - "modulePath": "plugins/wikipedia/page.js", - "sourceFile": "plugins/wikipedia/page.js" + "modulePath": "plugins/yahoo/search.js", + "sourceFile": "plugins/yahoo/search.js" }, { - "site": "wikipedia", - "name": "random", - "description": "Get a random Wikipedia article", + "site": "yahoo-finance", + "name": "quote", + "description": "Yahoo Finance stock quote", "access": "read", - "strategy": "public", - "browser": false, + "domain": "finance.yahoo.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "lang", + "name": "symbol", "type": "str", - "default": "en", - "required": false, - "help": "Language code (e.g. en, zh, ja)" + "required": true, + "positional": true, + "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" } ], "columns": [ - "title", - "description", - "extract", - "url" + "symbol", + "name", + "price", + "change", + "changePercent", + "open", + "high", + "low", + "volume", + "marketCap" ], "type": "js", - "modulePath": "plugins/wikipedia/random.js", - "sourceFile": "plugins/wikipedia/random.js" + "modulePath": "plugins/yahoo-finance/quote.js", + "sourceFile": "plugins/yahoo-finance/quote.js", + "navigateBefore": "https://finance.yahoo.com" }, { - "site": "wikipedia", - "name": "search", - "description": "Search Wikipedia articles", + "site": "yale", + "name": "export-postgraduate-courses", + "description": "Export Yale University postgraduate and professional programs from official Yale sources.", "access": "read", + "example": "webcmd yale export-postgraduate-courses --degree-level masters --count 10 -f csv", + "domain": "yale.edu", "strategy": "public", "browser": false, "args": [ { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 10, + "name": "degree-level", + "type": "string", + "default": "all", "required": false, - "help": "Max results" + "help": "all, masters, certificate, diploma, professional, or doctorate" }, { - "name": "lang", - "type": "str", - "default": "en", + "name": "count", + "type": "int", "required": false, - "help": "Language code (e.g. en, zh, ja)" + "help": "Positive maximum number of programs after filtering and deduplication" } ], "columns": [ - "title", - "snippet", - "url" - ], - "tags": [ - "search" + "Course Name", + "Course URL", + "University \nname", + "Intake Month", + "Substream/\nSpecialisation", + "App fees", + "Degree Level", + "Study Level", + "Duration\n(in months)", + "Study option", + "Program Type", + "Partner", + "Tution fees \n(per year)", + "Total Tution \nFees", + "IELTS \n(Overall & Subscores)", + "ielts_reading_score", + "ielts_writing_score", + "ielts_listening_score", + "ielts_speaking_score", + "TOEFL\n(Overall & Subscores)", + "toefl_reading_score", + "toefl_writing_score", + "toefl_listening_score", + "toefl_speaking_score", + "PTE\n(Overall & Subscores)", + "pte_reading_score", + "pte_writing_score", + "pte_listening_score", + "pte_speaking_score", + "Duolingo\n(Overall & Subscores)", + "duolingo_comprehension_score", + "duolingo_literacy_score", + "duolingo_conversation_score", + "duolingo_production_score", + "Is Waiver \nProvided?", + "Waiver Info", + "Is MOI \naccepted?", + "Share list, if any", + "GRE Required", + "GMAT Required", + "GRE/GMAT Scores", + "12th scores", + "Min UG score", + "15 years of\nEducation Allowed?", + "Gap Years", + "Backlogs", + "Work \nExperience \nRequired?", + "Main Entry \nRequirements", + "Status", + "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", + "Remarks (if any)", + "Reference Links (if any)" ], "type": "js", - "modulePath": "plugins/wikipedia/search.js", - "sourceFile": "plugins/wikipedia/search.js" + "modulePath": "plugins/yale/export-postgraduate-courses.js", + "sourceFile": "plugins/yale/export-postgraduate-courses.js" }, { - "site": "wikipedia", - "name": "summary", - "description": "Get Wikipedia article summary", + "site": "ycombinator", + "name": "companies", + "description": "Search the public Y Combinator startup directory", "access": "read", - "strategy": "public", - "browser": false, + "domain": "www.ycombinator.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "title", + "name": "query", "type": "str", - "required": true, + "required": false, "positional": true, - "help": "Article title (e.g. \"Transformer (machine learning model)\")" + "help": "Company name, product, or keyword such as AI" }, { - "name": "lang", + "name": "batch", "type": "str", - "default": "en", "required": false, - "help": "Language code (e.g. en, zh, ja)" + "help": "Exact YC batch, for example Spring 2026" + }, + { + "name": "industry", + "type": "str", + "required": false, + "help": "Exact YC industry, for example B2B" + }, + { + "name": "recent", + "type": "boolean", + "default": false, + "required": false, + "help": "Sort matches by launch date, newest first" + }, + { + "name": "limit", + "type": "int", + "default": 10, + "required": false, + "help": "Maximum companies to return (1-40)" } ], "columns": [ - "title", + "rank", + "name", + "batch", + "location", "description", - "extract", + "industries", "url" ], "type": "js", - "modulePath": "plugins/wikipedia/summary.js", - "sourceFile": "plugins/wikipedia/summary.js" + "modulePath": "plugins/ycombinator/companies.js", + "sourceFile": "plugins/ycombinator/companies.js", + "navigateBefore": false }, { - "site": "wikipedia", - "name": "trending", - "description": "Most-read Wikipedia articles (yesterday)", + "site": "ycombinator", + "name": "company", + "description": "Read a public Y Combinator company profile", "access": "read", - "strategy": "public", - "browser": false, + "domain": "www.ycombinator.com", + "strategy": "ui", + "browser": true, "args": [ { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max results" - }, - { - "name": "lang", + "name": "company", "type": "str", - "default": "en", - "required": false, - "help": "Language code (e.g. en, zh, ja)" + "required": true, + "positional": true, + "help": "YC company slug or full company URL" } ], "columns": [ - "rank", - "title", + "name", "description", - "views" + "batch", + "status", + "location", + "founded", + "teamSize", + "website", + "founders", + "jobCount", + "url" ], "type": "js", - "modulePath": "plugins/wikipedia/trending.js", - "sourceFile": "plugins/wikipedia/trending.js" + "modulePath": "plugins/ycombinator/company.js", + "sourceFile": "plugins/ycombinator/company.js", + "navigateBefore": false }, { - "site": "wttr", - "name": "current", - "description": "Current weather conditions for a location (city, lat,lon, or airport code)", - "access": "read", - "domain": "wttr.in", - "strategy": "public", - "browser": false, + "site": "yollomi", + "name": "background", + "description": "Generate AI background for a product/object image (5 credits)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "location", + "name": "image", "type": "str", "required": true, "positional": true, - "help": "City name, \"lat,lon\", airport ICAO code, or \"@domain\"" + "help": "Image URL (upload via \"webcmd yollomi upload\" first)" + }, + { + "name": "prompt", + "type": "str", + "default": "", + "required": false, + "help": "Background description (optional)" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" } ], "columns": [ - "location", - "region", - "country", - "latitude", - "longitude", - "observedAt", - "tempC", - "tempF", - "feelsLikeC", - "feelsLikeF", - "description", - "humidity", - "cloudCover", - "pressure", - "precipMm", - "visibilityKm", - "uvIndex", - "windKmph", - "windDirection", - "windDirectionDegree" + "status", + "file", + "size", + "url" ], "type": "js", - "modulePath": "plugins/wttr/current.js", - "sourceFile": "plugins/wttr/current.js" + "modulePath": "plugins/yollomi/background.js", + "sourceFile": "plugins/yollomi/background.js", + "navigateBefore": "https://yollomi.com" }, { - "site": "wttr", - "name": "forecast", - "description": "Multi-day weather forecast (up to 3 days, wttr.in free tier max)", - "access": "read", - "domain": "wttr.in", - "strategy": "public", - "browser": false, + "site": "yollomi", + "name": "edit", + "description": "Edit images with AI text prompts (Qwen image edit)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "location", + "name": "image", "type": "str", "required": true, "positional": true, - "help": "City name, \"lat,lon\", airport ICAO code, or \"@domain\"" + "help": "Input image URL (upload via \"webcmd yollomi upload\" first)" }, { - "name": "days", - "type": "int", - "default": 3, + "name": "prompt", + "type": "str", + "required": true, + "positional": true, + "help": "Editing instruction (e.g. \"Make it look vintage\")" + }, + { + "name": "model", + "type": "str", + "default": "qwen-image-edit", "required": false, - "help": "Max forecast days (1-3, wttr.in caps the response at 3 days)" + "help": "Edit model", + "choices": [ + "qwen-image-edit", + "qwen-image-edit-plus" + ] + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" } ], "columns": [ - "rank", - "date", - "minTempC", - "maxTempC", - "avgTempC", - "minTempF", - "maxTempF", - "avgTempF", - "sunHour", - "totalSnowCm", - "uvIndex", - "description", - "sunrise", - "sunset" + "status", + "file", + "size", + "credits", + "url" ], "type": "js", - "modulePath": "plugins/wttr/forecast.js", - "sourceFile": "plugins/wttr/forecast.js" + "modulePath": "plugins/yollomi/edit.js", + "sourceFile": "plugins/yollomi/edit.js", + "navigateBefore": "https://yollomi.com" }, { - "site": "yahoo", - "name": "search", - "description": "Search Yahoo (powered by Bing)", - "access": "read", - "domain": "search.yahoo.com", - "strategy": "public", + "site": "yollomi", + "name": "face-swap", + "description": "Swap faces between two photos (3 credits)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "keyword", + "name": "source", "type": "str", "required": true, - "positional": true, - "help": "Search query" + "help": "Source face image URL" }, { - "name": "limit", - "type": "int", - "default": 7, + "name": "target", + "type": "str", + "required": true, + "help": "Target photo URL" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", "required": false, - "help": "Number of results per page (max 7)" + "help": "Output directory" }, { - "name": "page", - "type": "int", - "default": 1, + "name": "no-download", + "type": "boolean", + "default": false, "required": false, - "help": "Page number (1, 2, 3...). Yahoo returns ~7 results per page" + "help": "Only show URL" } ], "columns": [ - "rank", - "title", - "url", - "snippet" - ], - "tags": [ - "search" + "status", + "file", + "size", + "url" ], "type": "js", - "modulePath": "plugins/yahoo/search.js", - "sourceFile": "plugins/yahoo/search.js" + "modulePath": "plugins/yollomi/face-swap.js", + "sourceFile": "plugins/yollomi/face-swap.js", + "navigateBefore": "https://yollomi.com" }, { - "site": "yahoo-finance", - "name": "quote", - "description": "Yahoo Finance stock quote", - "access": "read", - "domain": "finance.yahoo.com", + "site": "yollomi", + "name": "generate", + "description": "Generate images with AI (text-to-image or image-to-image)", + "access": "write", + "domain": "yollomi.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "symbol", + "name": "prompt", "type": "str", "required": true, "positional": true, - "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" + "help": "Text prompt describing the image" + }, + { + "name": "model", + "type": "str", + "default": "z-image-turbo", + "required": false, + "help": "Model ID (z-image-turbo, flux-schnell, nano-banana, flux-2-pro, ...)" + }, + { + "name": "ratio", + "type": "str", + "default": "1:1", + "required": false, + "help": "Aspect ratio", + "choices": [ + "1:1", + "16:9", + "9:16", + "4:3", + "3:4" + ] + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Input image URL for image-to-image (upload via \"webcmd yollomi upload\" first)" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URLs, skip download" } ], "columns": [ - "symbol", - "name", - "price", - "change", - "changePercent", - "open", - "high", - "low", - "volume", - "marketCap" + "index", + "status", + "file", + "size", + "url" ], "type": "js", - "modulePath": "plugins/yahoo-finance/quote.js", - "sourceFile": "plugins/yahoo-finance/quote.js", - "navigateBefore": "https://finance.yahoo.com" + "modulePath": "plugins/yollomi/generate.js", + "sourceFile": "plugins/yollomi/generate.js", + "navigateBefore": "https://yollomi.com" }, { - "site": "yale", - "name": "export-postgraduate-courses", - "description": "Export Yale University postgraduate and professional programs from official Yale sources.", + "site": "yollomi", + "name": "models", + "description": "List available Yollomi AI models (image, video, tools)", "access": "read", - "example": "webcmd yale export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "yale.edu", "strategy": "public", "browser": false, "args": [ { - "name": "degree-level", - "type": "string", + "name": "type", + "type": "str", "default": "all", "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" + "help": "Filter by model type", + "choices": [ + "all", + "image", + "video", + "tool" + ] } ], "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" + "type", + "model", + "credits", + "description" ], "type": "js", - "modulePath": "plugins/yale/export-postgraduate-courses.js", - "sourceFile": "plugins/yale/export-postgraduate-courses.js" + "modulePath": "plugins/yollomi/models.js", + "sourceFile": "plugins/yollomi/models.js" }, { - "site": "ycombinator", - "name": "companies", - "description": "Search the public Y Combinator startup directory", - "access": "read", - "domain": "www.ycombinator.com", - "strategy": "ui", + "site": "yollomi", + "name": "object-remover", + "description": "Remove unwanted objects from images (3 credits)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "query", + "name": "image", "type": "str", - "required": false, + "required": true, "positional": true, - "help": "Company name, product, or keyword such as AI" + "help": "Image URL" }, { - "name": "batch", + "name": "mask", "type": "str", - "required": false, - "help": "Exact YC batch, for example Spring 2026" + "required": true, + "positional": true, + "help": "Mask image URL (white = area to remove)" }, { - "name": "industry", + "name": "output", "type": "str", + "default": "./yollomi-output", "required": false, - "help": "Exact YC industry, for example B2B" + "help": "Output directory" }, { - "name": "recent", + "name": "no-download", "type": "boolean", "default": false, "required": false, - "help": "Sort matches by launch date, newest first" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum companies to return (1-40)" + "help": "Only show URL" } ], "columns": [ - "rank", - "name", - "batch", - "location", - "description", - "industries", + "status", + "file", + "size", "url" ], "type": "js", - "modulePath": "plugins/ycombinator/companies.js", - "sourceFile": "plugins/ycombinator/companies.js", - "navigateBefore": false + "modulePath": "plugins/yollomi/object-remover.js", + "sourceFile": "plugins/yollomi/object-remover.js", + "navigateBefore": "https://yollomi.com" }, { - "site": "ycombinator", - "name": "company", - "description": "Read a public Y Combinator company profile", - "access": "read", - "domain": "www.ycombinator.com", - "strategy": "ui", + "site": "yollomi", + "name": "remove-bg", + "description": "Remove image background with AI (free)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", "browser": true, "args": [ { - "name": "company", + "name": "image", "type": "str", "required": true, "positional": true, - "help": "YC company slug or full company URL" + "help": "Image URL to remove background from" + }, + { + "name": "output", + "type": "str", + "default": "./yollomi-output", + "required": false, + "help": "Output directory" + }, + { + "name": "no-download", + "type": "boolean", + "default": false, + "required": false, + "help": "Only show URL" } ], "columns": [ - "name", - "description", - "batch", "status", - "location", - "founded", - "teamSize", - "website", - "founders", - "jobCount", + "file", + "size", "url" ], "type": "js", - "modulePath": "plugins/ycombinator/company.js", - "sourceFile": "plugins/ycombinator/company.js", - "navigateBefore": false + "modulePath": "plugins/yollomi/remove-bg.js", + "sourceFile": "plugins/yollomi/remove-bg.js", + "navigateBefore": "https://yollomi.com" }, { "site": "yollomi", - "name": "background", - "description": "Generate AI background for a product/object image (5 credits)", + "name": "restore", + "description": "Restore old or damaged photos with AI (4 credits)", "access": "write", "domain": "yollomi.com", "strategy": "cookie", @@ -19993,14 +29187,7 @@ "type": "str", "required": true, "positional": true, - "help": "Image URL (upload via \"webcmd yollomi upload\" first)" - }, - { - "name": "prompt", - "type": "str", - "default": "", - "required": false, - "help": "Background description (optional)" + "help": "Image URL to restore" }, { "name": "output", @@ -20024,42 +29211,41 @@ "url" ], "type": "js", - "modulePath": "plugins/yollomi/background.js", - "sourceFile": "plugins/yollomi/background.js", + "modulePath": "plugins/yollomi/restore.js", + "sourceFile": "plugins/yollomi/restore.js", "navigateBefore": "https://yollomi.com" }, { "site": "yollomi", - "name": "edit", - "description": "Edit images with AI text prompts (Qwen image edit)", + "name": "try-on", + "description": "Virtual try-on — see how clothes look on a person (3 credits)", "access": "write", "domain": "yollomi.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "image", + "name": "person", "type": "str", "required": true, - "positional": true, - "help": "Input image URL (upload via \"webcmd yollomi upload\" first)" + "help": "Person photo URL (upload via \"webcmd yollomi upload\" first)" }, { - "name": "prompt", + "name": "cloth", "type": "str", "required": true, - "positional": true, - "help": "Editing instruction (e.g. \"Make it look vintage\")" + "help": "Clothing image URL" }, { - "name": "model", + "name": "cloth-type", "type": "str", - "default": "qwen-image-edit", + "default": "upper", "required": false, - "help": "Edit model", + "help": "Clothing type", "choices": [ - "qwen-image-edit", - "qwen-image-edit-plus" + "upper", + "lower", + "overall" ] }, { @@ -20081,34 +29267,67 @@ "status", "file", "size", - "credits", "url" ], "type": "js", - "modulePath": "plugins/yollomi/edit.js", - "sourceFile": "plugins/yollomi/edit.js", + "modulePath": "plugins/yollomi/try-on.js", + "sourceFile": "plugins/yollomi/try-on.js", "navigateBefore": "https://yollomi.com" }, { "site": "yollomi", - "name": "face-swap", - "description": "Swap faces between two photos (3 credits)", + "name": "upload", + "description": "Upload an image or video to Yollomi (returns URL for other commands)", "access": "write", "domain": "yollomi.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "source", + "name": "file", "type": "str", "required": true, - "help": "Source face image URL" + "positional": true, + "help": "Local file path to upload" + } + ], + "columns": [ + "status", + "file", + "size", + "url" + ], + "type": "js", + "modulePath": "plugins/yollomi/upload.js", + "sourceFile": "plugins/yollomi/upload.js", + "navigateBefore": "https://yollomi.com" + }, + { + "site": "yollomi", + "name": "upscale", + "description": "Upscale image resolution with AI (1 credit)", + "access": "write", + "domain": "yollomi.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "image", + "type": "str", + "required": true, + "positional": true, + "help": "Image URL to upscale" }, { - "name": "target", + "name": "scale", "type": "str", - "required": true, - "help": "Target photo URL" + "default": "2", + "required": false, + "help": "Upscale factor (2 or 4)", + "choices": [ + "2", + "4" + ] }, { "name": "output", @@ -20129,17 +29348,18 @@ "status", "file", "size", + "scale", "url" ], "type": "js", - "modulePath": "plugins/yollomi/face-swap.js", - "sourceFile": "plugins/yollomi/face-swap.js", + "modulePath": "plugins/yollomi/upscale.js", + "sourceFile": "plugins/yollomi/upscale.js", "navigateBefore": "https://yollomi.com" }, { "site": "yollomi", - "name": "generate", - "description": "Generate images with AI (text-to-image or image-to-image)", + "name": "video", + "description": "Generate videos with AI (text-to-video or image-to-video)", "access": "write", "domain": "yollomi.com", "strategy": "cookie", @@ -20150,19 +29370,25 @@ "type": "str", "required": true, "positional": true, - "help": "Text prompt describing the image" + "help": "Text prompt describing the video" }, { "name": "model", "type": "str", - "default": "z-image-turbo", + "default": "kling-2-1", "required": false, - "help": "Model ID (z-image-turbo, flux-schnell, nano-banana, flux-2-pro, ...)" + "help": "Model (kling-2-1, openai-sora-2, google-veo-3-1, wan-2-5-t2v, ...)" + }, + { + "name": "image", + "type": "str", + "required": false, + "help": "Input image URL for image-to-video" }, { "name": "ratio", "type": "str", - "default": "1:1", + "default": "16:9", "required": false, "help": "Aspect ratio", "choices": [ @@ -20173,12 +29399,6 @@ "3:4" ] }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Input image URL for image-to-image (upload via \"webcmd yollomi upload\" first)" - }, { "name": "output", "type": "str", @@ -20191,396 +29411,520 @@ "type": "boolean", "default": false, "required": false, - "help": "Only show URLs, skip download" + "help": "Only show URL, skip download" } ], "columns": [ - "index", "status", "file", "size", + "credits", "url" ], "type": "js", - "modulePath": "plugins/yollomi/generate.js", - "sourceFile": "plugins/yollomi/generate.js", + "modulePath": "plugins/yollomi/video.js", + "sourceFile": "plugins/yollomi/video.js", "navigateBefore": "https://yollomi.com" }, { - "site": "yollomi", - "name": "models", - "description": "List available Yollomi AI models (image, video, tools)", + "site": "youtube", + "name": "channel", + "description": "Get YouTube channel info and recent videos", "access": "read", - "strategy": "public", - "browser": false, + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, "args": [ { - "name": "type", + "name": "id", "type": "str", - "default": "all", + "required": true, + "positional": true, + "help": "Channel ID (UCxxxx) or handle (@name)" + }, + { + "name": "limit", + "type": "int", + "default": 10, "required": false, - "help": "Filter by model type", - "choices": [ - "all", - "image", - "video", - "tool" - ] + "help": "Max recent videos (max 30)" } ], "columns": [ - "type", - "model", - "credits", - "description" + "field", + "value" ], "type": "js", - "modulePath": "plugins/yollomi/models.js", - "sourceFile": "plugins/yollomi/models.js" + "modulePath": "plugins/youtube/channel.js", + "sourceFile": "plugins/youtube/channel.js", + "navigateBefore": "https://www.youtube.com" }, { - "site": "yollomi", - "name": "object-remover", - "description": "Remove unwanted objects from images (3 credits)", + "site": "youtube", + "name": "comments", + "description": "Get YouTube video comments", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "url", + "type": "str", + "required": true, + "positional": true, + "help": "YouTube video URL or video ID" + }, + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max comments (max 100)" + } + ], + "columns": [ + "rank", + "author", + "text", + "likes", + "replies", + "time" + ], + "type": "js", + "modulePath": "plugins/youtube/comments.js", + "sourceFile": "plugins/youtube/comments.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "feed", + "description": "Get YouTube homepage recommended videos", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max videos to return (default 20, max 100)" + } + ], + "columns": [ + "rank", + "title", + "channel", + "video_id", + "views", + "duration", + "published", + "url" + ], + "type": "js", + "modulePath": "plugins/youtube/feed.js", + "sourceFile": "plugins/youtube/feed.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "history", + "description": "Get YouTube watch history", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "limit", + "type": "int", + "default": 30, + "required": false, + "help": "Max videos to return (default 30, max 200)" + } + ], + "columns": [ + "rank", + "title", + "channel", + "views", + "duration", + "url" + ], + "type": "js", + "modulePath": "plugins/youtube/history.js", + "sourceFile": "plugins/youtube/history.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "like", + "description": "Like a YouTube video", "access": "write", - "domain": "yollomi.com", + "domain": "www.youtube.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "image", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Image URL" + "help": "YouTube video URL or video ID" + } + ], + "columns": [ + "status", + "message" + ], + "type": "js", + "modulePath": "plugins/youtube/like.js", + "sourceFile": "plugins/youtube/like.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "login", + "description": "Open youtube login", + "access": "write", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "status", + "logged_in", + "site", + "name", + "action", + "verify_command" + ], + "type": "js", + "modulePath": "plugins/youtube/auth.js", + "sourceFile": "plugins/youtube/auth.js", + "navigateBefore": false, + "siteSession": "persistent" + }, + { + "site": "youtube", + "name": "playlist", + "description": "Get YouTube playlist info and video list", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "id", + "type": "str", + "required": true, + "positional": true, + "help": "Playlist URL or playlist ID (PLxxxxxx)" }, { - "name": "mask", + "name": "limit", + "type": "int", + "default": 50, + "required": false, + "help": "Max videos to return (default 50, max 200)" + } + ], + "columns": [ + "rank", + "title", + "channel", + "duration", + "views", + "published", + "url" + ], + "type": "js", + "modulePath": "plugins/youtube/playlist.js", + "sourceFile": "plugins/youtube/playlist.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "search", + "description": "Search YouTube videos", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ + { + "name": "query", "type": "str", "required": true, "positional": true, - "help": "Mask image URL (white = area to remove)" + "help": "Search query" }, { - "name": "output", + "name": "limit", + "type": "int", + "default": 20, + "required": false, + "help": "Max results (max 50)" + }, + { + "name": "type", "type": "str", - "default": "./yollomi-output", + "default": "", "required": false, - "help": "Output directory" + "help": "Filter type: shorts, video, channel, playlist" }, { - "name": "no-download", - "type": "boolean", - "default": false, + "name": "upload", + "type": "str", + "default": "", "required": false, - "help": "Only show URL" + "help": "Upload date: hour, today, week, month, year" + }, + { + "name": "sort", + "type": "str", + "default": "", + "required": false, + "help": "Sort by: relevance, date, views, rating" } ], "columns": [ - "status", - "file", - "size", + "rank", + "title", + "channel", + "views", + "duration", + "published", "url" ], + "tags": [ + "search" + ], "type": "js", - "modulePath": "plugins/yollomi/object-remover.js", - "sourceFile": "plugins/yollomi/object-remover.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "plugins/youtube/search.js", + "sourceFile": "plugins/youtube/search.js", + "navigateBefore": "https://www.youtube.com" }, { - "site": "yollomi", - "name": "remove-bg", - "description": "Remove image background with AI (free)", + "site": "youtube", + "name": "subscribe", + "description": "Subscribe to a YouTube channel", "access": "write", - "domain": "yollomi.com", + "domain": "www.youtube.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "image", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Image URL to remove background from" - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL" + "help": "Channel ID (UCxxxx) or handle (@name)" } ], "columns": [ "status", - "file", - "size", - "url" + "message" ], "type": "js", - "modulePath": "plugins/yollomi/remove-bg.js", - "sourceFile": "plugins/yollomi/remove-bg.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "plugins/youtube/subscribe.js", + "sourceFile": "plugins/youtube/subscribe.js", + "navigateBefore": "https://www.youtube.com" }, { - "site": "yollomi", - "name": "restore", - "description": "Restore old or damaged photos with AI (4 credits)", - "access": "write", - "domain": "yollomi.com", + "site": "youtube", + "name": "subscriptions", + "description": "List subscribed YouTube channels", + "access": "read", + "domain": "www.youtube.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "image", - "type": "str", - "required": true, - "positional": true, - "help": "Image URL to restore" - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, + "name": "limit", + "type": "int", + "default": 50, "required": false, - "help": "Only show URL" + "help": "Max channels to return (default 50)" } ], "columns": [ - "status", - "file", - "size", + "rank", + "name", + "handle", + "subscribers", "url" ], "type": "js", - "modulePath": "plugins/yollomi/restore.js", - "sourceFile": "plugins/yollomi/restore.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "plugins/youtube/subscriptions.js", + "sourceFile": "plugins/youtube/subscriptions.js", + "navigateBefore": "https://www.youtube.com" }, { - "site": "yollomi", - "name": "try-on", - "description": "Virtual try-on — see how clothes look on a person (3 credits)", - "access": "write", - "domain": "yollomi.com", + "site": "youtube", + "name": "transcript", + "description": "Get YouTube video transcript/subtitles", + "access": "read", + "domain": "www.youtube.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "person", - "type": "str", - "required": true, - "help": "Person photo URL (upload via \"webcmd yollomi upload\" first)" - }, - { - "name": "cloth", + "name": "url", "type": "str", "required": true, - "help": "Clothing image URL" + "positional": true, + "help": "YouTube video URL or video ID" }, { - "name": "cloth-type", + "name": "lang", "type": "str", - "default": "upper", "required": false, - "help": "Clothing type", - "choices": [ - "upper", - "lower", - "overall" - ] + "help": "Language code (e.g. en, zh-Hans). Omit to auto-select" }, { - "name": "output", + "name": "mode", "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, + "default": "grouped", "required": false, - "help": "Only show URL" + "help": "Output mode: grouped (readable paragraphs) or raw (every segment)" } ], - "columns": [ - "status", - "file", - "size", - "url" - ], "type": "js", - "modulePath": "plugins/yollomi/try-on.js", - "sourceFile": "plugins/yollomi/try-on.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "plugins/youtube/transcript.js", + "sourceFile": "plugins/youtube/transcript.js", + "navigateBefore": "https://www.youtube.com" }, { - "site": "yollomi", - "name": "upload", - "description": "Upload an image or video to Yollomi (returns URL for other commands)", + "site": "youtube", + "name": "unlike", + "description": "Remove like from a YouTube video", "access": "write", - "domain": "yollomi.com", + "domain": "www.youtube.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "file", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Local file path to upload" + "help": "YouTube video URL or video ID" } ], "columns": [ "status", - "file", - "size", - "url" + "message" ], "type": "js", - "modulePath": "plugins/yollomi/upload.js", - "sourceFile": "plugins/yollomi/upload.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "plugins/youtube/unlike.js", + "sourceFile": "plugins/youtube/unlike.js", + "navigateBefore": "https://www.youtube.com" }, { - "site": "yollomi", - "name": "upscale", - "description": "Upscale image resolution with AI (1 credit)", + "site": "youtube", + "name": "unsubscribe", + "description": "Unsubscribe from a YouTube channel", "access": "write", - "domain": "yollomi.com", + "domain": "www.youtube.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "image", + "name": "channel", "type": "str", "required": true, "positional": true, - "help": "Image URL to upscale" - }, - { - "name": "scale", - "type": "str", - "default": "2", - "required": false, - "help": "Upscale factor (2 or 4)", - "choices": [ - "2", - "4" - ] - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL" + "help": "Channel ID (UCxxxx) or handle (@name)" } ], "columns": [ "status", - "file", - "size", - "scale", - "url" + "message" ], "type": "js", - "modulePath": "plugins/yollomi/upscale.js", - "sourceFile": "plugins/yollomi/upscale.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "plugins/youtube/unsubscribe.js", + "sourceFile": "plugins/youtube/unsubscribe.js", + "navigateBefore": "https://www.youtube.com" }, { - "site": "yollomi", + "site": "youtube", "name": "video", - "description": "Generate videos with AI (text-to-video or image-to-video)", - "access": "write", - "domain": "yollomi.com", + "description": "Get YouTube video metadata (title, views, description, etc.)", + "access": "read", + "domain": "www.youtube.com", "strategy": "cookie", "browser": true, "args": [ { - "name": "prompt", + "name": "url", "type": "str", "required": true, "positional": true, - "help": "Text prompt describing the video" - }, - { - "name": "model", - "type": "str", - "default": "kling-2-1", - "required": false, - "help": "Model (kling-2-1, openai-sora-2, google-veo-3-1, wan-2-5-t2v, ...)" - }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Input image URL for image-to-video" - }, - { - "name": "ratio", - "type": "str", - "default": "16:9", - "required": false, - "help": "Aspect ratio", - "choices": [ - "1:1", - "16:9", - "9:16", - "4:3", - "3:4" - ] - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, + "help": "YouTube video URL or video ID" + } + ], + "columns": [ + "field", + "value" + ], + "type": "js", + "modulePath": "plugins/youtube/video.js", + "sourceFile": "plugins/youtube/video.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "watch-later", + "description": "Get your YouTube Watch Later queue", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [ { - "name": "no-download", - "type": "boolean", - "default": false, + "name": "limit", + "type": "int", + "default": 50, "required": false, - "help": "Only show URL, skip download" + "help": "Max videos to return (default 50, max 200)" } ], "columns": [ - "status", - "file", - "size", - "credits", + "rank", + "title", + "channel", + "duration", + "views", + "published", "url" ], "type": "js", - "modulePath": "plugins/yollomi/video.js", - "sourceFile": "plugins/yollomi/video.js", - "navigateBefore": "https://yollomi.com" + "modulePath": "plugins/youtube/watch-later.js", + "sourceFile": "plugins/youtube/watch-later.js", + "navigateBefore": "https://www.youtube.com" + }, + { + "site": "youtube", + "name": "whoami", + "description": "Show the current logged-in youtube account", + "access": "read", + "domain": "www.youtube.com", + "strategy": "cookie", + "browser": true, + "args": [], + "columns": [ + "logged_in", + "site", + "name" + ], + "type": "js", + "modulePath": "plugins/youtube/auth.js", + "sourceFile": "plugins/youtube/auth.js", + "navigateBefore": false, + "siteSession": "persistent" }, { "site": "zepto", diff --git a/plugins/antigravity/README.md b/plugins/antigravity/README.md new file mode 100644 index 00000000..6ecb5890 --- /dev/null +++ b/plugins/antigravity/README.md @@ -0,0 +1,45 @@ +# webcmd-plugin-antigravity + +Webcmd commands for antigravity. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/antigravity +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd antigravity add-context` | Click the Add context button in the composer (opens file/URL picker for context attachment). | +| `webcmd antigravity cookies` | List cookies on the Antigravity renderer (JS-visible via document.cookie). | +| `webcmd antigravity copy-code` | Return the text of a code block in the current conversation. Default: last code block; pass --index N (1-based from top) to pick a specific one. | +| `webcmd antigravity copy-message` | Return the text of the last assistant message (best-effort: walks up from the last visible Copy button). | +| `webcmd antigravity delete` | Delete an Antigravity conversation by ID. Antigravity asks for confirmation; we click through it. Require --yes to actually delete. | +| `webcmd antigravity display-options` | Open the Display Options menu and list its items. | +| `webcmd antigravity dump` | Dump the DOM to help AI understand the UI | +| `webcmd antigravity extract-code` | Extract multi-line code blocks from the current Antigravity conversation | +| `webcmd antigravity history` | List visible Antigravity conversations from the sidebar | +| `webcmd antigravity idb-list` | List IndexedDB databases on the Antigravity renderer. | +| `webcmd antigravity mark-read` | Mark an unread Antigravity conversation as read. Fails if the row is already read or the postcondition cannot be verified. | +| `webcmd antigravity model` | Read or switch the active model in Antigravity. Without arguments, reports the current model. With (substring, case-insensitive), switches. | +| `webcmd antigravity nav` | Click Go Back or Go Forward (Antigravity in-app history). | +| `webcmd antigravity new` | Start a new conversation / clear context in Antigravity | +| `webcmd antigravity react` | Click "Good response" or "Bad response" on the LAST assistant message. | +| `webcmd antigravity read` | Read the latest chat messages from Antigravity AI | +| `webcmd antigravity recent-paths` | Show Antigravity's recently-opened folders/files (history.recentlyOpenedPathsList). | +| `webcmd antigravity rename` | Rename an Antigravity conversation by ID (NOT YET IMPLEMENTED — see source comment). | +| `webcmd antigravity revert` | Click the revert button (per-message revert for agent changes). Requires --yes (this modifies your workspace). | +| `webcmd antigravity send` | Send a message to Antigravity AI via the internal Lexical editor | +| `webcmd antigravity settings` | Click the Antigravity settings button (matched by data-testid="settings-button"). | +| `webcmd antigravity settings-read` | Read Antigravity's user settings.json (theme, proxy, agCockpit, tfa.system.autoAccept, etc.). | +| `webcmd antigravity sidebar-toggle` | Click Toggle Sidebar (collapses/expands the Antigravity sidebar). | +| `webcmd antigravity state-get` | Read one value from Antigravity's state.vscdb. Pass --workspace for per-workspace. | +| `webcmd antigravity state-keys` | List keys in Antigravity's globalStorage state.vscdb (VSCode-style). Pass --workspace to query a per-workspace DB. Works while Antigravity is closed. | +| `webcmd antigravity status` | Check Antigravity CDP connection and get current page state | +| `webcmd antigravity storage-get` | Read a single localStorage / sessionStorage value on the Antigravity renderer. | +| `webcmd antigravity storage-keys` | List localStorage / sessionStorage keys on the Antigravity renderer (CDP). | +| `webcmd antigravity toggle-aux` | Toggle the Auxiliary Pane (Antigravity's secondary panel for code/preview). | +| `webcmd antigravity watch` | Stream new chat messages from Antigravity in real-time | +| `webcmd antigravity workspaces-list` | List Antigravity workspaceStorage entries (each represents a previously-opened folder). | diff --git a/clis/antigravity/_actions.js b/plugins/antigravity/_actions.js similarity index 100% rename from clis/antigravity/_actions.js rename to plugins/antigravity/_actions.js diff --git a/clis/antigravity/audit-extras.js b/plugins/antigravity/audit-extras.js similarity index 100% rename from clis/antigravity/audit-extras.js rename to plugins/antigravity/audit-extras.js diff --git a/clis/antigravity/delete.js b/plugins/antigravity/delete.js similarity index 100% rename from clis/antigravity/delete.js rename to plugins/antigravity/delete.js diff --git a/clis/antigravity/dump.js b/plugins/antigravity/dump.js similarity index 100% rename from clis/antigravity/dump.js rename to plugins/antigravity/dump.js diff --git a/clis/antigravity/extract-code.js b/plugins/antigravity/extract-code.js similarity index 100% rename from clis/antigravity/extract-code.js rename to plugins/antigravity/extract-code.js diff --git a/clis/antigravity/history.js b/plugins/antigravity/history.js similarity index 100% rename from clis/antigravity/history.js rename to plugins/antigravity/history.js diff --git a/clis/antigravity/mark-read.js b/plugins/antigravity/mark-read.js similarity index 100% rename from clis/antigravity/mark-read.js rename to plugins/antigravity/mark-read.js diff --git a/clis/antigravity/model.js b/plugins/antigravity/model.js similarity index 100% rename from clis/antigravity/model.js rename to plugins/antigravity/model.js diff --git a/clis/antigravity/new.js b/plugins/antigravity/new.js similarity index 100% rename from clis/antigravity/new.js rename to plugins/antigravity/new.js diff --git a/plugins/antigravity/package.json b/plugins/antigravity/package.json new file mode 100644 index 00000000..3b278cc4 --- /dev/null +++ b/plugins/antigravity/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-antigravity", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for antigravity", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/antigravity/read.js b/plugins/antigravity/read.js similarity index 100% rename from clis/antigravity/read.js rename to plugins/antigravity/read.js diff --git a/clis/antigravity/rename.js b/plugins/antigravity/rename.js similarity index 100% rename from clis/antigravity/rename.js rename to plugins/antigravity/rename.js diff --git a/clis/antigravity/send.js b/plugins/antigravity/send.js similarity index 100% rename from clis/antigravity/send.js rename to plugins/antigravity/send.js diff --git a/clis/antigravity/serve.js b/plugins/antigravity/serve.js similarity index 100% rename from clis/antigravity/serve.js rename to plugins/antigravity/serve.js diff --git a/clis/antigravity/status.js b/plugins/antigravity/status.js similarity index 100% rename from clis/antigravity/status.js rename to plugins/antigravity/status.js diff --git a/clis/antigravity/storage.js b/plugins/antigravity/storage.js similarity index 100% rename from clis/antigravity/storage.js rename to plugins/antigravity/storage.js diff --git a/clis/antigravity/antigravity.test.js b/plugins/antigravity/test/antigravity.test.js similarity index 96% rename from clis/antigravity/antigravity.test.js rename to plugins/antigravity/test/antigravity.test.js index ce902240..bc0178c5 100644 --- a/clis/antigravity/antigravity.test.js +++ b/plugins/antigravity/test/antigravity.test.js @@ -1,14 +1,14 @@ import { beforeAll, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { listConversations } from './_actions.js'; -import './audit-extras.js'; -import './delete.js'; -import './history.js'; -import './mark-read.js'; -import './model.js'; -import './rename.js'; -import './storage.js'; +import { listConversations } from '../_actions.js'; +import '../audit-extras.js'; +import '../delete.js'; +import '../history.js'; +import '../mark-read.js'; +import '../model.js'; +import '../rename.js'; +import '../storage.js'; function makePage(evaluateResults = []) { const queue = [...evaluateResults]; diff --git a/clis/antigravity/watch.js b/plugins/antigravity/watch.js similarity index 100% rename from clis/antigravity/watch.js rename to plugins/antigravity/watch.js diff --git a/plugins/antigravity/webcmd-plugin.json b/plugins/antigravity/webcmd-plugin.json new file mode 100644 index 00000000..109ccdcf --- /dev/null +++ b/plugins/antigravity/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "antigravity", + "version": "0.1.0", + "description": "Webcmd commands for antigravity", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/facebook/README.md b/plugins/facebook/README.md new file mode 100644 index 00000000..cfab825c --- /dev/null +++ b/plugins/facebook/README.md @@ -0,0 +1,28 @@ +# webcmd-plugin-facebook + +Webcmd commands for facebook. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/facebook +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd facebook add-friend` | Send a friend request on Facebook | +| `webcmd facebook events` | Browse Facebook event categories | +| `webcmd facebook feed` | Get your Facebook news feed | +| `webcmd facebook friends` | Get Facebook friend suggestions | +| `webcmd facebook groups` | List your Facebook groups | +| `webcmd facebook join-group` | Join a Facebook group | +| `webcmd facebook login` | Open facebook login | +| `webcmd facebook marketplace-inbox` | List recent Facebook Marketplace buyer/seller conversations | +| `webcmd facebook marketplace-listings` | List your Facebook Marketplace seller listings | +| `webcmd facebook memories` | Get your Facebook memories (On This Day) | +| `webcmd facebook notifications` | Get recent Facebook notifications (includes unread / time / url / notif_id / notif_type columns) | +| `webcmd facebook profile` | Get Facebook user/page profile info | +| `webcmd facebook search` | Search Facebook for people, pages, or posts | +| `webcmd facebook whoami` | Show the current logged-in facebook account | diff --git a/clis/facebook/__fixtures__/notifications-page.html b/plugins/facebook/__fixtures__/notifications-page.html similarity index 100% rename from clis/facebook/__fixtures__/notifications-page.html rename to plugins/facebook/__fixtures__/notifications-page.html diff --git a/clis/facebook/add-friend.js b/plugins/facebook/add-friend.js similarity index 100% rename from clis/facebook/add-friend.js rename to plugins/facebook/add-friend.js diff --git a/clis/facebook/auth.js b/plugins/facebook/auth.js similarity index 95% rename from clis/facebook/auth.js rename to plugins/facebook/auth.js index 98182b86..29714fa0 100644 --- a/clis/facebook/auth.js +++ b/plugins/facebook/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasFacebookCUserCookie(page) { const cookies = await page.getCookies({ url: 'https://www.facebook.com' }); diff --git a/clis/facebook/events.js b/plugins/facebook/events.js similarity index 100% rename from clis/facebook/events.js rename to plugins/facebook/events.js diff --git a/clis/facebook/feed.js b/plugins/facebook/feed.js similarity index 100% rename from clis/facebook/feed.js rename to plugins/facebook/feed.js diff --git a/clis/facebook/friends.js b/plugins/facebook/friends.js similarity index 100% rename from clis/facebook/friends.js rename to plugins/facebook/friends.js diff --git a/clis/facebook/groups.js b/plugins/facebook/groups.js similarity index 100% rename from clis/facebook/groups.js rename to plugins/facebook/groups.js diff --git a/clis/facebook/join-group.js b/plugins/facebook/join-group.js similarity index 100% rename from clis/facebook/join-group.js rename to plugins/facebook/join-group.js diff --git a/clis/facebook/marketplace-inbox.js b/plugins/facebook/marketplace-inbox.js similarity index 100% rename from clis/facebook/marketplace-inbox.js rename to plugins/facebook/marketplace-inbox.js diff --git a/clis/facebook/marketplace-listings.js b/plugins/facebook/marketplace-listings.js similarity index 100% rename from clis/facebook/marketplace-listings.js rename to plugins/facebook/marketplace-listings.js diff --git a/clis/facebook/memories.js b/plugins/facebook/memories.js similarity index 100% rename from clis/facebook/memories.js rename to plugins/facebook/memories.js diff --git a/clis/facebook/notifications.js b/plugins/facebook/notifications.js similarity index 100% rename from clis/facebook/notifications.js rename to plugins/facebook/notifications.js diff --git a/plugins/facebook/package.json b/plugins/facebook/package.json new file mode 100644 index 00000000..b7a769a8 --- /dev/null +++ b/plugins/facebook/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-facebook", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for facebook", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/facebook/profile.js b/plugins/facebook/profile.js similarity index 100% rename from clis/facebook/profile.js rename to plugins/facebook/profile.js diff --git a/clis/facebook/search.js b/plugins/facebook/search.js similarity index 100% rename from clis/facebook/search.js rename to plugins/facebook/search.js diff --git a/clis/facebook/feed.test.js b/plugins/facebook/test/feed.test.js similarity index 99% rename from clis/facebook/feed.test.js rename to plugins/facebook/test/feed.test.js index edfbee92..e274e44b 100644 --- a/clis/facebook/feed.test.js +++ b/plugins/facebook/test/feed.test.js @@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest'; import { JSDOM } from 'jsdom'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ } from './feed.js'; +import { __test__ } from '../feed.js'; function runExtract(html, limit = 10, url = 'https://www.facebook.com/') { const dom = new JSDOM(html, { url }); diff --git a/clis/facebook/marketplace.test.js b/plugins/facebook/test/marketplace.test.js similarity index 98% rename from clis/facebook/marketplace.test.js rename to plugins/facebook/test/marketplace.test.js index 2c6c22a5..3ce47f3e 100644 --- a/clis/facebook/marketplace.test.js +++ b/plugins/facebook/test/marketplace.test.js @@ -1,8 +1,8 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './marketplace-listings.js'; -import './marketplace-inbox.js'; +import '../marketplace-listings.js'; +import '../marketplace-inbox.js'; function makePage(overrides = {}) { return { diff --git a/clis/facebook/notifications.test.js b/plugins/facebook/test/notifications.test.js similarity index 99% rename from clis/facebook/notifications.test.js rename to plugins/facebook/test/notifications.test.js index f6300238..ae9269b3 100644 --- a/clis/facebook/notifications.test.js +++ b/plugins/facebook/test/notifications.test.js @@ -44,13 +44,13 @@ import { extractNotificationRowsFromDoc, buildNotificationsScript, notificationsCommand, -} from './notifications.js'; +} from '../notifications.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -const fixturePath = resolve(__dirname, '__fixtures__/notifications-page.html'); +const fixturePath = resolve(__dirname, '../__fixtures__/notifications-page.html'); const fixtureHtml = readFileSync(fixturePath, 'utf8'); -const manifestPath = resolve(__dirname, '../../cli-manifest.json'); +const manifestPath = resolve(__dirname, '../../../plugin-command-manifest.json'); function loadFixtureDoc() { return new JSDOM(fixtureHtml, { url: 'https://www.facebook.com/notifications' }).window.document; diff --git a/clis/facebook/search.test.js b/plugins/facebook/test/search.test.js similarity index 99% rename from clis/facebook/search.test.js rename to plugins/facebook/test/search.test.js index 6365cdfb..53145b85 100644 --- a/clis/facebook/search.test.js +++ b/plugins/facebook/test/search.test.js @@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest'; import { JSDOM } from 'jsdom'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './search.js'; +import '../search.js'; function createPage(payload) { return { diff --git a/plugins/facebook/webcmd-plugin.json b/plugins/facebook/webcmd-plugin.json new file mode 100644 index 00000000..cbd41c19 --- /dev/null +++ b/plugins/facebook/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "facebook", + "version": "0.1.0", + "description": "Webcmd commands for facebook", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/grok/README.md b/plugins/grok/README.md new file mode 100644 index 00000000..f6875fc1 --- /dev/null +++ b/plugins/grok/README.md @@ -0,0 +1,29 @@ +# webcmd-plugin-grok + +Webcmd commands for grok. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/grok +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd grok ask` | Send a message to Grok and get response | +| `webcmd grok delete` | Delete a Grok conversation by ID. Grok takes effect immediately with no confirmation dialog — require --yes to actually delete. | +| `webcmd grok detail` | Open a Grok conversation by ID and read its messages | +| `webcmd grok export` | Export all visible Grok conversation history metadata | +| `webcmd grok export-all` | Export Grok conversation history and each conversation transcript | +| `webcmd grok history` | List recent Grok conversations from the sidebar (requires login) | +| `webcmd grok image` | Generate images on grok.com and return image URLs | +| `webcmd grok login` | Open grok login | +| `webcmd grok new` | Start a new conversation in Grok | +| `webcmd grok pin` | Pin a Grok conversation by ID | +| `webcmd grok read` | Read messages in the current Grok conversation | +| `webcmd grok send` | Fire-and-forget: send a prompt to Grok without waiting for the reply | +| `webcmd grok status` | Check Grok page availability, login state, current session and model | +| `webcmd grok unpin` | Unpin a Grok conversation by ID | +| `webcmd grok whoami` | Show the current logged-in grok account | diff --git a/clis/grok/ask.js b/plugins/grok/ask.js similarity index 100% rename from clis/grok/ask.js rename to plugins/grok/ask.js diff --git a/clis/grok/auth.js b/plugins/grok/auth.js similarity index 96% rename from clis/grok/auth.js rename to plugins/grok/auth.js index 1aa7c744..df30ced1 100644 --- a/clis/grok/auth.js +++ b/plugins/grok/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasGrokSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://grok.com' }); diff --git a/clis/grok/delete.js b/plugins/grok/delete.js similarity index 100% rename from clis/grok/delete.js rename to plugins/grok/delete.js diff --git a/clis/grok/detail.js b/plugins/grok/detail.js similarity index 100% rename from clis/grok/detail.js rename to plugins/grok/detail.js diff --git a/clis/grok/export-all.js b/plugins/grok/export-all.js similarity index 100% rename from clis/grok/export-all.js rename to plugins/grok/export-all.js diff --git a/clis/grok/export-utils.js b/plugins/grok/export-utils.js similarity index 100% rename from clis/grok/export-utils.js rename to plugins/grok/export-utils.js diff --git a/clis/grok/export.js b/plugins/grok/export.js similarity index 100% rename from clis/grok/export.js rename to plugins/grok/export.js diff --git a/clis/grok/history.js b/plugins/grok/history.js similarity index 100% rename from clis/grok/history.js rename to plugins/grok/history.js diff --git a/clis/grok/image.js b/plugins/grok/image.js similarity index 100% rename from clis/grok/image.js rename to plugins/grok/image.js diff --git a/clis/grok/image.test.ts b/plugins/grok/image.test.ts similarity index 100% rename from clis/grok/image.test.ts rename to plugins/grok/image.test.ts diff --git a/clis/grok/new.js b/plugins/grok/new.js similarity index 100% rename from clis/grok/new.js rename to plugins/grok/new.js diff --git a/plugins/grok/package.json b/plugins/grok/package.json new file mode 100644 index 00000000..bb1ec11a --- /dev/null +++ b/plugins/grok/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-grok", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for grok", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/grok/pin.js b/plugins/grok/pin.js similarity index 100% rename from clis/grok/pin.js rename to plugins/grok/pin.js diff --git a/clis/grok/read.js b/plugins/grok/read.js similarity index 100% rename from clis/grok/read.js rename to plugins/grok/read.js diff --git a/clis/grok/send.js b/plugins/grok/send.js similarity index 100% rename from clis/grok/send.js rename to plugins/grok/send.js diff --git a/clis/grok/status.js b/plugins/grok/status.js similarity index 100% rename from clis/grok/status.js rename to plugins/grok/status.js diff --git a/clis/grok/ask.test.js b/plugins/grok/test/ask.test.js similarity index 97% rename from clis/grok/ask.test.js rename to plugins/grok/test/ask.test.js index 21b5ad4c..0d65f0f1 100644 --- a/clis/grok/ask.test.js +++ b/plugins/grok/test/ask.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { __test__ } from './ask.js'; +import { __test__ } from '../ask.js'; describe('grok ask helpers', () => { describe('getBaselineLastAssistantId', () => { diff --git a/clis/grok/export.test.js b/plugins/grok/test/export.test.js similarity index 98% rename from clis/grok/export.test.js rename to plugins/grok/test/export.test.js index c89554e6..9394a5d0 100644 --- a/clis/grok/export.test.js +++ b/plugins/grok/test/export.test.js @@ -3,13 +3,13 @@ import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { __test__ as exportTest, grokExportCommand } from './export.js'; -import { __test__ as exportAllTest, grokExportAllCommand } from './export-all.js'; +import { __test__ as exportTest, grokExportCommand } from '../export.js'; +import { __test__ as exportAllTest, grokExportAllCommand } from '../export-all.js'; import { normalizeConversationRows, normalizeManifestRows, requireObjectEvaluateResult, -} from './export-utils.js'; +} from '../export-utils.js'; const ID = '7c4197f2-10a1-4ebb-a84a-fea89f4f1d06'; const ID2 = '8c4197f2-10a1-4ebb-a84a-fea89f4f1d07'; diff --git a/clis/grok/utils.test.js b/plugins/grok/test/utils.test.js similarity index 99% rename from clis/grok/utils.test.js rename to plugins/grok/test/utils.test.js index 712cd1df..82af0737 100644 --- a/clis/grok/utils.test.js +++ b/plugins/grok/test/utils.test.js @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; -import { __test__, isOnGrok, normalizeBooleanFlag, parseGrokSessionId, sendMessage } from './utils.js'; +import { __test__, isOnGrok, normalizeBooleanFlag, parseGrokSessionId, sendMessage } from '../utils.js'; describe('grok parseGrokSessionId', () => { const id = '7c4197f2-10a1-4ebb-a84a-fea89f4f1d06'; diff --git a/clis/grok/utils.js b/plugins/grok/utils.js similarity index 100% rename from clis/grok/utils.js rename to plugins/grok/utils.js diff --git a/plugins/grok/webcmd-plugin.json b/plugins/grok/webcmd-plugin.json new file mode 100644 index 00000000..b1247076 --- /dev/null +++ b/plugins/grok/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "grok", + "version": "0.1.0", + "description": "Webcmd commands for grok", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/instagram/README.md b/plugins/instagram/README.md new file mode 100644 index 00000000..aadb67a0 --- /dev/null +++ b/plugins/instagram/README.md @@ -0,0 +1,37 @@ +# webcmd-plugin-instagram + +Webcmd commands for instagram. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/instagram +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd instagram collection-create` | Create a new Instagram saved-posts collection (folder) | +| `webcmd instagram collection-delete` | Delete an Instagram saved-posts collection (folder) by name or id | +| `webcmd instagram comment` | Comment on an Instagram post | +| `webcmd instagram download` | Download images and videos from Instagram posts and reels | +| `webcmd instagram explore` | Instagram explore/discover trending posts | +| `webcmd instagram follow` | Follow an Instagram user | +| `webcmd instagram followers` | List followers of an Instagram user | +| `webcmd instagram following` | List accounts an Instagram user is following | +| `webcmd instagram like` | Like an Instagram post | +| `webcmd instagram login` | Open instagram login | +| `webcmd instagram note` | Publish a text Instagram note | +| `webcmd instagram post` | Post an Instagram feed image or mixed-media carousel | +| `webcmd instagram profile` | Get Instagram user profile info | +| `webcmd instagram reel` | Post an Instagram reel video | +| `webcmd instagram save` | Save (bookmark) an Instagram post | +| `webcmd instagram saved` | Get your saved Instagram posts (optionally from a specific collection) | +| `webcmd instagram search` | Search Instagram users | +| `webcmd instagram story` | Post a single Instagram story image or video | +| `webcmd instagram unfollow` | Unfollow an Instagram user | +| `webcmd instagram unlike` | Unlike an Instagram post | +| `webcmd instagram unsave` | Unsave (remove bookmark) an Instagram post | +| `webcmd instagram user` | Get recent posts from an Instagram user | +| `webcmd instagram whoami` | Show the current logged-in instagram account | diff --git a/clis/instagram/_shared/private-publish.js b/plugins/instagram/_shared/private-publish.js similarity index 100% rename from clis/instagram/_shared/private-publish.js rename to plugins/instagram/_shared/private-publish.js diff --git a/clis/instagram/_shared/protocol-capture.js b/plugins/instagram/_shared/protocol-capture.js similarity index 100% rename from clis/instagram/_shared/protocol-capture.js rename to plugins/instagram/_shared/protocol-capture.js diff --git a/clis/instagram/_shared/runtime-info.js b/plugins/instagram/_shared/runtime-info.js similarity index 100% rename from clis/instagram/_shared/runtime-info.js rename to plugins/instagram/_shared/runtime-info.js diff --git a/clis/instagram/auth.js b/plugins/instagram/auth.js similarity index 96% rename from clis/instagram/auth.js rename to plugins/instagram/auth.js index eef97fcc..f8e4b964 100644 --- a/clis/instagram/auth.js +++ b/plugins/instagram/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasInstagramSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://www.instagram.com' }); diff --git a/clis/instagram/collection-create.js b/plugins/instagram/collection-create.js similarity index 100% rename from clis/instagram/collection-create.js rename to plugins/instagram/collection-create.js diff --git a/clis/instagram/collection-delete.js b/plugins/instagram/collection-delete.js similarity index 100% rename from clis/instagram/collection-delete.js rename to plugins/instagram/collection-delete.js diff --git a/clis/instagram/comment.js b/plugins/instagram/comment.js similarity index 100% rename from clis/instagram/comment.js rename to plugins/instagram/comment.js diff --git a/clis/instagram/download.js b/plugins/instagram/download.js similarity index 100% rename from clis/instagram/download.js rename to plugins/instagram/download.js diff --git a/clis/instagram/explore.js b/plugins/instagram/explore.js similarity index 100% rename from clis/instagram/explore.js rename to plugins/instagram/explore.js diff --git a/clis/instagram/follow.js b/plugins/instagram/follow.js similarity index 100% rename from clis/instagram/follow.js rename to plugins/instagram/follow.js diff --git a/clis/instagram/followers.js b/plugins/instagram/followers.js similarity index 100% rename from clis/instagram/followers.js rename to plugins/instagram/followers.js diff --git a/clis/instagram/following.js b/plugins/instagram/following.js similarity index 100% rename from clis/instagram/following.js rename to plugins/instagram/following.js diff --git a/clis/instagram/like.js b/plugins/instagram/like.js similarity index 100% rename from clis/instagram/like.js rename to plugins/instagram/like.js diff --git a/clis/instagram/note.js b/plugins/instagram/note.js similarity index 100% rename from clis/instagram/note.js rename to plugins/instagram/note.js diff --git a/plugins/instagram/package.json b/plugins/instagram/package.json new file mode 100644 index 00000000..4ad2309d --- /dev/null +++ b/plugins/instagram/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-instagram", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for instagram", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/instagram/post.js b/plugins/instagram/post.js similarity index 100% rename from clis/instagram/post.js rename to plugins/instagram/post.js diff --git a/clis/instagram/profile.js b/plugins/instagram/profile.js similarity index 100% rename from clis/instagram/profile.js rename to plugins/instagram/profile.js diff --git a/clis/instagram/reel.js b/plugins/instagram/reel.js similarity index 100% rename from clis/instagram/reel.js rename to plugins/instagram/reel.js diff --git a/clis/instagram/save.js b/plugins/instagram/save.js similarity index 100% rename from clis/instagram/save.js rename to plugins/instagram/save.js diff --git a/clis/instagram/saved.js b/plugins/instagram/saved.js similarity index 100% rename from clis/instagram/saved.js rename to plugins/instagram/saved.js diff --git a/clis/instagram/search.js b/plugins/instagram/search.js similarity index 100% rename from clis/instagram/search.js rename to plugins/instagram/search.js diff --git a/clis/instagram/story.js b/plugins/instagram/story.js similarity index 100% rename from clis/instagram/story.js rename to plugins/instagram/story.js diff --git a/clis/instagram/download.test.js b/plugins/instagram/test/download.test.js similarity index 99% rename from clis/instagram/download.test.js rename to plugins/instagram/test/download.test.js index 9106ac93..4fca6f35 100644 --- a/clis/instagram/download.test.js +++ b/plugins/instagram/test/download.test.js @@ -12,7 +12,7 @@ vi.mock('@agentrhq/webcmd/download', async () => { const actual = await vi.importActual('@agentrhq/webcmd/download'); return { ...actual, httpDownload: mockHttpDownload }; }); -const { buildInstagramDownloadItems, parseInstagramMediaTarget, } = await import('./download.js'); +const { buildInstagramDownloadItems, parseInstagramMediaTarget, } = await import('../download.js'); let cmd; const tempDirs = []; const repoInstagramTestDir = path.resolve('instagram-test'); diff --git a/clis/instagram/explore.test.js b/plugins/instagram/test/explore.test.js similarity index 98% rename from clis/instagram/explore.test.js rename to plugins/instagram/test/explore.test.js index f7a04f9e..413a1036 100644 --- a/clis/instagram/explore.test.js +++ b/plugins/instagram/test/explore.test.js @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './explore.js'; +import '../explore.js'; async function runExplore(payload, limit = 20) { const command = getRegistry().get('instagram/explore'); diff --git a/clis/instagram/instagram.test.js b/plugins/instagram/test/instagram.test.js similarity index 99% rename from clis/instagram/instagram.test.js rename to plugins/instagram/test/instagram.test.js index 856ea682..88bcb275 100644 --- a/clis/instagram/instagram.test.js +++ b/plugins/instagram/test/instagram.test.js @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from 'vitest'; -import './following.js'; +import '../following.js'; import { getRegistry } from '@agentrhq/webcmd/registry'; /** diff --git a/clis/instagram/note.test.js b/plugins/instagram/test/note.test.js similarity index 97% rename from clis/instagram/note.test.js rename to plugins/instagram/test/note.test.js index 36189bef..49181788 100644 --- a/clis/instagram/note.test.js +++ b/plugins/instagram/test/note.test.js @@ -1,8 +1,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './note.js'; -import { createPageMock } from '../test-utils.js'; +import '../note.js'; +import { createPageMock } from './page-mock.js'; describe('instagram note registration', () => { beforeEach(() => { vi.restoreAllMocks(); diff --git a/plugins/instagram/test/page-mock.js b/plugins/instagram/test/page-mock.js new file mode 100644 index 00000000..86a0e98b --- /dev/null +++ b/plugins/instagram/test/page-mock.js @@ -0,0 +1,12 @@ +import { vi } from 'vitest'; + +export function createPageMock(evaluateResults = [], overrides = {}) { + const evaluate = vi.fn(); + for (const result of evaluateResults) evaluate.mockResolvedValueOnce(result); + return { + goto: vi.fn().mockResolvedValue(undefined), + evaluate, + wait: vi.fn().mockResolvedValue(undefined), + ...overrides, + }; +} diff --git a/clis/instagram/post.test.js b/plugins/instagram/test/post.test.js similarity index 99% rename from clis/instagram/post.test.js rename to plugins/instagram/test/post.test.js index f45a6218..64def043 100644 --- a/clis/instagram/post.test.js +++ b/plugins/instagram/test/post.test.js @@ -4,9 +4,9 @@ import * as path from 'node:path'; import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import * as privatePublish from './_shared/private-publish.js'; -import { buildClickActionJs, buildEnsureComposerOpenJs, buildInspectUploadStageJs, buildPublishStatusProbeJs } from './post.js'; -import './post.js'; +import * as privatePublish from '../_shared/private-publish.js'; +import { buildClickActionJs, buildEnsureComposerOpenJs, buildInspectUploadStageJs, buildPublishStatusProbeJs } from '../post.js'; +import '../post.js'; const tempDirs = []; function createTempImage(name = 'demo.jpg', bytes = Buffer.from([0xff, 0xd8, 0xff, 0xd9])) { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-instagram-post-')); diff --git a/clis/instagram/_shared/private-publish.test.js b/plugins/instagram/test/private-publish.test.js similarity index 99% rename from clis/instagram/_shared/private-publish.test.js rename to plugins/instagram/test/private-publish.test.js index ea6c4a3d..84f987c4 100644 --- a/clis/instagram/_shared/private-publish.test.js +++ b/plugins/instagram/test/private-publish.test.js @@ -2,7 +2,7 @@ import * as fs from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; import { afterAll, describe, expect, it } from 'vitest'; -import { buildConfigureBody, buildConfigureSidecarPayload, buildConfigureToStoryPhotoPayload, buildConfigureToStoryVideoPayload, deriveInstagramJazoest, derivePrivateApiContextFromCapture, extractInstagramRuntimeInfo, getInstagramFeedNormalizedDimensions, getInstagramStoryNormalizedDimensions, isInstagramFeedAspectRatioAllowed, isInstagramStoryAspectRatioAllowed, publishStoryViaPrivateApi, publishMediaViaPrivateApi, publishImagesViaPrivateApi, readImageAsset, resolveInstagramPrivatePublishConfig, } from './private-publish.js'; +import { buildConfigureBody, buildConfigureSidecarPayload, buildConfigureToStoryPhotoPayload, buildConfigureToStoryVideoPayload, deriveInstagramJazoest, derivePrivateApiContextFromCapture, extractInstagramRuntimeInfo, getInstagramFeedNormalizedDimensions, getInstagramStoryNormalizedDimensions, isInstagramFeedAspectRatioAllowed, isInstagramStoryAspectRatioAllowed, publishStoryViaPrivateApi, publishMediaViaPrivateApi, publishImagesViaPrivateApi, readImageAsset, resolveInstagramPrivatePublishConfig, } from '../_shared/private-publish.js'; const tempDirs = []; function createTempFile(name, bytes) { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-instagram-private-')); diff --git a/clis/instagram/_shared/protocol-capture.test.js b/plugins/instagram/test/protocol-capture.test.js similarity index 98% rename from clis/instagram/_shared/protocol-capture.test.js rename to plugins/instagram/test/protocol-capture.test.js index 9febc4e3..5a4006ea 100644 --- a/clis/instagram/_shared/protocol-capture.test.js +++ b/plugins/instagram/test/protocol-capture.test.js @@ -1,6 +1,6 @@ import * as fs from 'node:fs'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import { buildInstallInstagramProtocolCaptureJs, buildReadInstagramProtocolCaptureJs, dumpInstagramProtocolCaptureIfEnabled, instagramPrivateApiFetch, installInstagramProtocolCapture, readInstagramProtocolCapture, } from './protocol-capture.js'; +import { buildInstallInstagramProtocolCaptureJs, buildReadInstagramProtocolCaptureJs, dumpInstagramProtocolCaptureIfEnabled, instagramPrivateApiFetch, installInstagramProtocolCapture, readInstagramProtocolCapture, } from '../_shared/protocol-capture.js'; describe('instagram protocol capture helpers', () => { afterEach(() => { vi.restoreAllMocks(); diff --git a/clis/instagram/reel.test.js b/plugins/instagram/test/reel.test.js similarity index 99% rename from clis/instagram/reel.test.js rename to plugins/instagram/test/reel.test.js index a134bb3c..91996cac 100644 --- a/clis/instagram/reel.test.js +++ b/plugins/instagram/test/reel.test.js @@ -4,7 +4,7 @@ import * as path from 'node:path'; import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './reel.js'; +import '../reel.js'; const tempDirs = []; function createTempVideo(name = 'demo.mp4', bytes = Buffer.from('video')) { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-instagram-reel-')); diff --git a/clis/instagram/story.test.js b/plugins/instagram/test/story.test.js similarity index 97% rename from clis/instagram/story.test.js rename to plugins/instagram/test/story.test.js index a868819a..7fbe92a2 100644 --- a/clis/instagram/story.test.js +++ b/plugins/instagram/test/story.test.js @@ -4,9 +4,9 @@ import * as path from 'node:path'; import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import * as privatePublish from './_shared/private-publish.js'; -import './story.js'; -import { createPageMock } from '../test-utils.js'; +import * as privatePublish from '../_shared/private-publish.js'; +import '../story.js'; +import { createPageMock } from './page-mock.js'; const tempDirs = []; function createTempFile(name, bytes = Buffer.from('story-media')) { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-instagram-story-')); diff --git a/clis/instagram/user.test.js b/plugins/instagram/test/user.test.js similarity index 99% rename from clis/instagram/user.test.js rename to plugins/instagram/test/user.test.js index 960af5d7..de882037 100644 --- a/clis/instagram/user.test.js +++ b/plugins/instagram/test/user.test.js @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './user.js'; +import '../user.js'; function getUserEvaluateJs() { const cmd = getRegistry().get('instagram/user'); diff --git a/clis/instagram/unfollow.js b/plugins/instagram/unfollow.js similarity index 100% rename from clis/instagram/unfollow.js rename to plugins/instagram/unfollow.js diff --git a/clis/instagram/unlike.js b/plugins/instagram/unlike.js similarity index 100% rename from clis/instagram/unlike.js rename to plugins/instagram/unlike.js diff --git a/clis/instagram/unsave.js b/plugins/instagram/unsave.js similarity index 100% rename from clis/instagram/unsave.js rename to plugins/instagram/unsave.js diff --git a/clis/instagram/user.js b/plugins/instagram/user.js similarity index 100% rename from clis/instagram/user.js rename to plugins/instagram/user.js diff --git a/plugins/instagram/webcmd-plugin.json b/plugins/instagram/webcmd-plugin.json new file mode 100644 index 00000000..aec08f8a --- /dev/null +++ b/plugins/instagram/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "instagram", + "version": "0.1.0", + "description": "Webcmd commands for instagram", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/linkedin/auth.js b/plugins/linkedin/auth.js index e1b070b6..13fd29f1 100644 --- a/plugins/linkedin/auth.js +++ b/plugins/linkedin/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from './site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasLinkedinSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://www.linkedin.com' }); diff --git a/plugins/linkedin/site-auth.js b/plugins/linkedin/site-auth.js deleted file mode 100644 index 8c3281b2..00000000 --- a/plugins/linkedin/site-auth.js +++ /dev/null @@ -1,119 +0,0 @@ -import { AuthRequiredError } from '@agentrhq/webcmd/errors'; -import { cli, Strategy } from '@agentrhq/webcmd/registry'; - -const LOGIN_ACTION = 'Complete sign-in in the opened Webcmd browser, then tell the agent when you are done.'; - -function normalizeIdentity(config, identity) { - const row = identity && typeof identity === 'object' && !Array.isArray(identity) - ? identity - : {}; - return { ...blankIdentity(config), ...row, logged_in: true, site: config.site }; -} - -function isAuthRequired(error) { - return error instanceof AuthRequiredError; -} - -async function tryProbe(config, page) { - return normalizeIdentity(config, await config.verify(page, { phase: 'identity' })); -} - -function identityColumns(config) { - return config.columns ?? ['id', 'username', 'name']; -} - -function blankIdentity(config) { - return Object.fromEntries(identityColumns(config).map((column) => [column, ''])); -} - -function commandColumns(config) { - return ['logged_in', 'site', ...identityColumns(config)]; -} - -function loginColumns(config) { - return ['status', ...commandColumns(config), 'action', 'verify_command']; -} - -function normalizeQuickCheck(result) { - if (typeof result === 'boolean') return { logged_in: result }; - if (result && typeof result === 'object' && !Array.isArray(result)) { - return { logged_in: !!result.logged_in, ...result }; - } - return { logged_in: false }; -} - -function normalizeRefreshResult(result) { - if (result && typeof result === 'object' && !Array.isArray(result)) return result; - return { touched: true }; -} - -export function registerSiteAuthCommands(config) { - if (!config?.site || !config?.domain || !config?.loginUrl || typeof config.verify !== 'function') { - throw new Error('registerSiteAuthCommands requires site, domain, loginUrl, and verify(page)'); - } - // Sites whose login is a modal/flow rather than a page can pass - // openLogin(page) to bring the login UI up; default is a plain navigation. - const openLogin = typeof config.openLogin === 'function' - ? config.openLogin - : async (page) => { await page.goto(config.loginUrl); }; - - cli({ - site: config.site, - name: 'whoami', - access: 'read', - description: config.whoamiDescription ?? `Show the current logged-in ${config.site} account`, - domain: config.domain, - strategy: Strategy.COOKIE, - browser: true, - navigateBefore: false, - siteSession: 'persistent', - aliases: config.whoamiAliases ?? [], - args: [], - columns: commandColumns(config), - authStatus: { - ...(typeof config.quickCheck === 'function' - ? { quickCheck: async (page) => normalizeQuickCheck(await config.quickCheck(page)) } - : {}), - ...(typeof config.refresh === 'function' - ? { refresh: async (page, kwargs) => normalizeRefreshResult(await config.refresh(page, kwargs)) } - : {}), - }, - func: async (page) => [await tryProbe(config, page)], - }); - - cli({ - site: config.site, - name: 'login', - access: 'write', - description: config.loginDescription ?? `Open ${config.site} login`, - domain: config.domain, - strategy: Strategy.COOKIE, - browser: true, - navigateBefore: false, - siteSession: 'persistent', - args: [], - columns: loginColumns(config), - func: async (page) => { - try { - return [{ - status: 'already_logged_in', - ...await tryProbe(config, page), - action: '', - verify_command: '', - }]; - } catch (error) { - if (!isAuthRequired(error)) throw error; - } - - await openLogin(page); - return [{ - status: 'action_required', - logged_in: false, - site: config.site, - ...blankIdentity(config), - action: LOGIN_ACTION, - verify_command: `webcmd ${config.site} whoami`, - }]; - }, - }); -} diff --git a/plugins/notebooklm/README.md b/plugins/notebooklm/README.md new file mode 100644 index 00000000..c9eb75ff --- /dev/null +++ b/plugins/notebooklm/README.md @@ -0,0 +1,34 @@ +# webcmd-plugin-notebooklm + +Webcmd commands for notebooklm. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/notebooklm +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd notebooklm add-source` | Add a URL, text, or local file source to an existing NotebookLM notebook | +| `webcmd notebooklm create` | Create a new NotebookLM notebook with the given title | +| `webcmd notebooklm current` | Show metadata for the currently opened NotebookLM notebook tab | +| `webcmd notebooklm generate-audio` | Trigger an Audio Overview (Deep Dive podcast) generation for a NotebookLM notebook, using all of its sources | +| `webcmd notebooklm generate-slides` | Trigger a Slide Deck (AI presentation) generation for a NotebookLM notebook, using all of its sources | +| `webcmd notebooklm get` | Get rich metadata for the currently opened NotebookLM notebook | +| `webcmd notebooklm history` | List NotebookLM conversation history threads in the current notebook | +| `webcmd notebooklm list` | List NotebookLM notebooks via in-page batchexecute RPC in the current logged-in session | +| `webcmd notebooklm login` | Open notebooklm login | +| `webcmd notebooklm note-list` | List saved notes from the Studio panel of the current NotebookLM notebook | +| `webcmd notebooklm notes-get` | Get one note from the current NotebookLM notebook by title from the visible note editor | +| `webcmd notebooklm open` | Open one NotebookLM notebook in the adapter session by id or URL | +| `webcmd notebooklm source-fulltext` | Get the extracted fulltext for one source in the currently opened NotebookLM notebook | +| `webcmd notebooklm source-get` | Get one source from the currently opened NotebookLM notebook by id or title | +| `webcmd notebooklm source-guide` | Get the guide summary and keywords for one source in the currently opened NotebookLM notebook | +| `webcmd notebooklm source-list` | List sources for the currently opened NotebookLM notebook | +| `webcmd notebooklm status` | Check NotebookLM page availability and login state in the current Chrome session | +| `webcmd notebooklm summary` | Get the summary block from the currently opened NotebookLM notebook | +| `webcmd notebooklm whoami` | Show the current logged-in notebooklm account | +| `webcmd notebooklm write-note` | Create a Studio note in an existing NotebookLM notebook with the given title and Markdown content | diff --git a/clis/notebooklm/add-source.js b/plugins/notebooklm/add-source.js similarity index 100% rename from clis/notebooklm/add-source.js rename to plugins/notebooklm/add-source.js diff --git a/clis/notebooklm/auth.js b/plugins/notebooklm/auth.js similarity index 96% rename from clis/notebooklm/auth.js rename to plugins/notebooklm/auth.js index 0b49b4e6..f0523c2a 100644 --- a/clis/notebooklm/auth.js +++ b/plugins/notebooklm/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasNotebookLmSsoCookies(page) { const cookies = await page.getCookies({ url: 'https://notebooklm.google.com' }); diff --git a/clis/notebooklm/create.js b/plugins/notebooklm/create.js similarity index 100% rename from clis/notebooklm/create.js rename to plugins/notebooklm/create.js diff --git a/clis/notebooklm/current.js b/plugins/notebooklm/current.js similarity index 100% rename from clis/notebooklm/current.js rename to plugins/notebooklm/current.js diff --git a/clis/notebooklm/generate-audio.js b/plugins/notebooklm/generate-audio.js similarity index 100% rename from clis/notebooklm/generate-audio.js rename to plugins/notebooklm/generate-audio.js diff --git a/clis/notebooklm/generate-slides.js b/plugins/notebooklm/generate-slides.js similarity index 100% rename from clis/notebooklm/generate-slides.js rename to plugins/notebooklm/generate-slides.js diff --git a/clis/notebooklm/get.js b/plugins/notebooklm/get.js similarity index 100% rename from clis/notebooklm/get.js rename to plugins/notebooklm/get.js diff --git a/clis/notebooklm/history.js b/plugins/notebooklm/history.js similarity index 100% rename from clis/notebooklm/history.js rename to plugins/notebooklm/history.js diff --git a/clis/notebooklm/list.js b/plugins/notebooklm/list.js similarity index 100% rename from clis/notebooklm/list.js rename to plugins/notebooklm/list.js diff --git a/clis/notebooklm/note-list.js b/plugins/notebooklm/note-list.js similarity index 100% rename from clis/notebooklm/note-list.js rename to plugins/notebooklm/note-list.js diff --git a/clis/notebooklm/notes-get.js b/plugins/notebooklm/notes-get.js similarity index 100% rename from clis/notebooklm/notes-get.js rename to plugins/notebooklm/notes-get.js diff --git a/clis/notebooklm/open.js b/plugins/notebooklm/open.js similarity index 100% rename from clis/notebooklm/open.js rename to plugins/notebooklm/open.js diff --git a/plugins/notebooklm/package.json b/plugins/notebooklm/package.json new file mode 100644 index 00000000..722b3499 --- /dev/null +++ b/plugins/notebooklm/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-notebooklm", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for notebooklm", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/notebooklm/rpc.js b/plugins/notebooklm/rpc.js similarity index 100% rename from clis/notebooklm/rpc.js rename to plugins/notebooklm/rpc.js diff --git a/clis/notebooklm/shared.js b/plugins/notebooklm/shared.js similarity index 100% rename from clis/notebooklm/shared.js rename to plugins/notebooklm/shared.js diff --git a/clis/notebooklm/source-fulltext.js b/plugins/notebooklm/source-fulltext.js similarity index 100% rename from clis/notebooklm/source-fulltext.js rename to plugins/notebooklm/source-fulltext.js diff --git a/clis/notebooklm/source-get.js b/plugins/notebooklm/source-get.js similarity index 100% rename from clis/notebooklm/source-get.js rename to plugins/notebooklm/source-get.js diff --git a/clis/notebooklm/source-guide.js b/plugins/notebooklm/source-guide.js similarity index 100% rename from clis/notebooklm/source-guide.js rename to plugins/notebooklm/source-guide.js diff --git a/clis/notebooklm/source-list.js b/plugins/notebooklm/source-list.js similarity index 100% rename from clis/notebooklm/source-list.js rename to plugins/notebooklm/source-list.js diff --git a/clis/notebooklm/status.js b/plugins/notebooklm/status.js similarity index 100% rename from clis/notebooklm/status.js rename to plugins/notebooklm/status.js diff --git a/clis/notebooklm/summary.js b/plugins/notebooklm/summary.js similarity index 100% rename from clis/notebooklm/summary.js rename to plugins/notebooklm/summary.js diff --git a/clis/notebooklm/add-source.test.js b/plugins/notebooklm/test/add-source.test.js similarity index 99% rename from clis/notebooklm/add-source.test.js rename to plugins/notebooklm/test/add-source.test.js index 57093a72..96e09bcf 100644 --- a/clis/notebooklm/add-source.test.js +++ b/plugins/notebooklm/test/add-source.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ } from './add-source.js'; +import { __test__ } from '../add-source.js'; const { parseSourceUrl, diff --git a/clis/notebooklm/compat.test.js b/plugins/notebooklm/test/compat.test.js similarity index 91% rename from clis/notebooklm/compat.test.js rename to plugins/notebooklm/test/compat.test.js index ee266e62..1d4b1d52 100644 --- a/clis/notebooklm/compat.test.js +++ b/plugins/notebooklm/test/compat.test.js @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './get.js'; -import './note-list.js'; -import './open.js'; +import '../get.js'; +import '../note-list.js'; +import '../open.js'; describe('notebooklm compatibility aliases', () => { it('registers select as a compatibility alias for open', () => { expect(getRegistry().get('notebooklm/select')).toBe(getRegistry().get('notebooklm/open')); diff --git a/clis/notebooklm/create.test.js b/plugins/notebooklm/test/create.test.js similarity index 98% rename from clis/notebooklm/create.test.js rename to plugins/notebooklm/test/create.test.js index 9f89a17e..e4b4e633 100644 --- a/clis/notebooklm/create.test.js +++ b/plugins/notebooklm/test/create.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ } from './create.js'; +import { __test__ } from '../create.js'; const { parseCreateTitle, parseCreateEmoji, parseCreateProjectResult } = __test__; diff --git a/clis/notebooklm/generate-audio.test.js b/plugins/notebooklm/test/generate-audio.test.js similarity index 98% rename from clis/notebooklm/generate-audio.test.js rename to plugins/notebooklm/test/generate-audio.test.js index 8043d080..72abc21f 100644 --- a/clis/notebooklm/generate-audio.test.js +++ b/plugins/notebooklm/test/generate-audio.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ } from './generate-audio.js'; +import { __test__ } from '../generate-audio.js'; const { AUDIO_OVERVIEW_CONFIG_BLOCK, buildCreateAudioArgs, parseAudioIdFromResult } = __test__; diff --git a/clis/notebooklm/generate-slides.test.js b/plugins/notebooklm/test/generate-slides.test.js similarity index 98% rename from clis/notebooklm/generate-slides.test.js rename to plugins/notebooklm/test/generate-slides.test.js index f6b78e59..2ee2032d 100644 --- a/clis/notebooklm/generate-slides.test.js +++ b/plugins/notebooklm/test/generate-slides.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ } from './generate-slides.js'; +import { __test__ } from '../generate-slides.js'; const { SLIDE_DECK_CONFIG_BLOCK, buildCreateSlidesArgs, parseSlideDeckLength, parseSlidesIdFromResult } = __test__; diff --git a/clis/notebooklm/history.test.js b/plugins/notebooklm/test/history.test.js similarity index 94% rename from clis/notebooklm/history.test.js rename to plugins/notebooklm/test/history.test.js index 9d3d0b65..ff3b9da9 100644 --- a/clis/notebooklm/history.test.js +++ b/plugins/notebooklm/test/history.test.js @@ -4,8 +4,8 @@ const { mockListNotebooklmHistoryViaRpc, mockGetNotebooklmPageState, mockRequire mockGetNotebooklmPageState: vi.fn(), mockRequireNotebooklmSession: vi.fn(), })); -vi.mock('./utils.js', async () => { - const actual = await vi.importActual('./utils.js'); +vi.mock('../utils.js', async () => { + const actual = await vi.importActual('../utils.js'); return { ...actual, getNotebooklmPageState: mockGetNotebooklmPageState, @@ -14,7 +14,7 @@ vi.mock('./utils.js', async () => { }; }); import { getRegistry } from '@agentrhq/webcmd/registry'; -import './history.js'; +import '../history.js'; describe('notebooklm history', () => { const history = getRegistry().get('notebooklm/history'); beforeEach(() => { diff --git a/clis/notebooklm/note-list.test.js b/plugins/notebooklm/test/note-list.test.js similarity index 94% rename from clis/notebooklm/note-list.test.js rename to plugins/notebooklm/test/note-list.test.js index ae889743..0e802cd2 100644 --- a/clis/notebooklm/note-list.test.js +++ b/plugins/notebooklm/test/note-list.test.js @@ -4,8 +4,8 @@ const { mockListNotebooklmNotesFromPage, mockGetNotebooklmPageState, mockRequire mockGetNotebooklmPageState: vi.fn(), mockRequireNotebooklmSession: vi.fn(), })); -vi.mock('./utils.js', async () => { - const actual = await vi.importActual('./utils.js'); +vi.mock('../utils.js', async () => { + const actual = await vi.importActual('../utils.js'); return { ...actual, getNotebooklmPageState: mockGetNotebooklmPageState, @@ -14,7 +14,7 @@ vi.mock('./utils.js', async () => { }; }); import { getRegistry } from '@agentrhq/webcmd/registry'; -import './note-list.js'; +import '../note-list.js'; describe('notebooklm note-list', () => { const command = getRegistry().get('notebooklm/note-list'); beforeEach(() => { diff --git a/clis/notebooklm/notes-get.test.js b/plugins/notebooklm/test/notes-get.test.js similarity index 96% rename from clis/notebooklm/notes-get.test.js rename to plugins/notebooklm/test/notes-get.test.js index 080c3bba..3809bdbd 100644 --- a/clis/notebooklm/notes-get.test.js +++ b/plugins/notebooklm/test/notes-get.test.js @@ -5,8 +5,8 @@ const { mockListNotebooklmNotesFromPage, mockReadNotebooklmVisibleNoteFromPage, mockGetNotebooklmPageState: vi.fn(), mockRequireNotebooklmSession: vi.fn(), })); -vi.mock('./utils.js', async () => { - const actual = await vi.importActual('./utils.js'); +vi.mock('../utils.js', async () => { + const actual = await vi.importActual('../utils.js'); return { ...actual, listNotebooklmNotesFromPage: mockListNotebooklmNotesFromPage, @@ -16,7 +16,7 @@ vi.mock('./utils.js', async () => { }; }); import { getRegistry } from '@agentrhq/webcmd/registry'; -import './notes-get.js'; +import '../notes-get.js'; describe('notebooklm notes-get', () => { const command = getRegistry().get('notebooklm/notes-get'); beforeEach(() => { diff --git a/clis/notebooklm/open.test.js b/plugins/notebooklm/test/open.test.js similarity index 96% rename from clis/notebooklm/open.test.js rename to plugins/notebooklm/test/open.test.js index 80a35e17..422795b5 100644 --- a/clis/notebooklm/open.test.js +++ b/plugins/notebooklm/test/open.test.js @@ -4,8 +4,8 @@ const { mockGetNotebooklmPageState, mockReadCurrentNotebooklm, mockRequireNotebo mockReadCurrentNotebooklm: vi.fn(), mockRequireNotebooklmSession: vi.fn(), })); -vi.mock('./utils.js', async () => { - const actual = await vi.importActual('./utils.js'); +vi.mock('../utils.js', async () => { + const actual = await vi.importActual('../utils.js'); return { ...actual, getNotebooklmPageState: mockGetNotebooklmPageState, @@ -14,7 +14,7 @@ vi.mock('./utils.js', async () => { }; }); import { getRegistry } from '@agentrhq/webcmd/registry'; -import './open.js'; +import '../open.js'; describe('notebooklm open', () => { const command = getRegistry().get('notebooklm/open'); beforeEach(() => { diff --git a/clis/notebooklm/rpc.test.js b/plugins/notebooklm/test/rpc.test.js similarity index 99% rename from clis/notebooklm/rpc.test.js rename to plugins/notebooklm/test/rpc.test.js index 8c21063f..73b240c9 100644 --- a/clis/notebooklm/rpc.test.js +++ b/plugins/notebooklm/test/rpc.test.js @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; import { AuthRequiredError } from '@agentrhq/webcmd/errors'; -import { buildNotebooklmRpcBody, extractNotebooklmRpcResult, getNotebooklmPageAuth, parseNotebooklmChunkedResponse, unwrapNotebooklmEvaluateResult, } from './rpc.js'; +import { buildNotebooklmRpcBody, extractNotebooklmRpcResult, getNotebooklmPageAuth, parseNotebooklmChunkedResponse, unwrapNotebooklmEvaluateResult, } from '../rpc.js'; describe('notebooklm rpc transport', () => { it('unwraps Browser Bridge evaluate envelopes', () => { const data = { ok: true }; diff --git a/clis/notebooklm/source-fulltext.test.js b/plugins/notebooklm/test/source-fulltext.test.js similarity index 97% rename from clis/notebooklm/source-fulltext.test.js rename to plugins/notebooklm/test/source-fulltext.test.js index 71a3c801..1da91e22 100644 --- a/clis/notebooklm/source-fulltext.test.js +++ b/plugins/notebooklm/test/source-fulltext.test.js @@ -6,8 +6,8 @@ const { mockListNotebooklmSourcesViaRpc, mockListNotebooklmSourcesFromPage, mock mockGetNotebooklmPageState: vi.fn(), mockRequireNotebooklmSession: vi.fn(), })); -vi.mock('./utils.js', async () => { - const actual = await vi.importActual('./utils.js'); +vi.mock('../utils.js', async () => { + const actual = await vi.importActual('../utils.js'); return { ...actual, listNotebooklmSourcesViaRpc: mockListNotebooklmSourcesViaRpc, @@ -18,7 +18,7 @@ vi.mock('./utils.js', async () => { }; }); import { getRegistry } from '@agentrhq/webcmd/registry'; -import './source-fulltext.js'; +import '../source-fulltext.js'; describe('notebooklm source-fulltext', () => { const command = getRegistry().get('notebooklm/source-fulltext'); beforeEach(() => { diff --git a/clis/notebooklm/source-get.test.js b/plugins/notebooklm/test/source-get.test.js similarity index 96% rename from clis/notebooklm/source-get.test.js rename to plugins/notebooklm/test/source-get.test.js index 4dc33680..da3d8b5b 100644 --- a/clis/notebooklm/source-get.test.js +++ b/plugins/notebooklm/test/source-get.test.js @@ -5,8 +5,8 @@ const { mockListNotebooklmSourcesViaRpc, mockListNotebooklmSourcesFromPage, mock mockGetNotebooklmPageState: vi.fn(), mockRequireNotebooklmSession: vi.fn(), })); -vi.mock('./utils.js', async () => { - const actual = await vi.importActual('./utils.js'); +vi.mock('../utils.js', async () => { + const actual = await vi.importActual('../utils.js'); return { ...actual, getNotebooklmPageState: mockGetNotebooklmPageState, @@ -16,7 +16,7 @@ vi.mock('./utils.js', async () => { }; }); import { getRegistry } from '@agentrhq/webcmd/registry'; -import './source-get.js'; +import '../source-get.js'; describe('notebooklm source-get', () => { const command = getRegistry().get('notebooklm/source-get'); beforeEach(() => { diff --git a/clis/notebooklm/source-guide.test.js b/plugins/notebooklm/test/source-guide.test.js similarity index 97% rename from clis/notebooklm/source-guide.test.js rename to plugins/notebooklm/test/source-guide.test.js index 0efb8567..13b29fa7 100644 --- a/clis/notebooklm/source-guide.test.js +++ b/plugins/notebooklm/test/source-guide.test.js @@ -6,8 +6,8 @@ const { mockListNotebooklmSourcesViaRpc, mockListNotebooklmSourcesFromPage, mock mockGetNotebooklmPageState: vi.fn(), mockRequireNotebooklmSession: vi.fn(), })); -vi.mock('./utils.js', async () => { - const actual = await vi.importActual('./utils.js'); +vi.mock('../utils.js', async () => { + const actual = await vi.importActual('../utils.js'); return { ...actual, listNotebooklmSourcesViaRpc: mockListNotebooklmSourcesViaRpc, @@ -18,7 +18,7 @@ vi.mock('./utils.js', async () => { }; }); import { getRegistry } from '@agentrhq/webcmd/registry'; -import './source-guide.js'; +import '../source-guide.js'; describe('notebooklm source-guide', () => { const command = getRegistry().get('notebooklm/source-guide'); beforeEach(() => { diff --git a/clis/notebooklm/summary.test.js b/plugins/notebooklm/test/summary.test.js similarity index 96% rename from clis/notebooklm/summary.test.js rename to plugins/notebooklm/test/summary.test.js index e7e46469..f42170a3 100644 --- a/clis/notebooklm/summary.test.js +++ b/plugins/notebooklm/test/summary.test.js @@ -5,8 +5,8 @@ const { mockReadNotebooklmSummaryFromPage, mockGetNotebooklmSummaryViaRpc, mockG mockGetNotebooklmPageState: vi.fn(), mockRequireNotebooklmSession: vi.fn(), })); -vi.mock('./utils.js', async () => { - const actual = await vi.importActual('./utils.js'); +vi.mock('../utils.js', async () => { + const actual = await vi.importActual('../utils.js'); return { ...actual, readNotebooklmSummaryFromPage: mockReadNotebooklmSummaryFromPage, @@ -16,7 +16,7 @@ vi.mock('./utils.js', async () => { }; }); import { getRegistry } from '@agentrhq/webcmd/registry'; -import './summary.js'; +import '../summary.js'; describe('notebooklm summary', () => { const command = getRegistry().get('notebooklm/summary'); beforeEach(() => { diff --git a/clis/notebooklm/utils.test.js b/plugins/notebooklm/test/utils.test.js similarity index 99% rename from clis/notebooklm/utils.test.js rename to plugins/notebooklm/test/utils.test.js index 7393af20..5c122e0f 100644 --- a/clis/notebooklm/utils.test.js +++ b/plugins/notebooklm/test/utils.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { buildNotebooklmRpcBody, classifyNotebooklmPage, extractNotebooklmHistoryPreview, extractNotebooklmRpcResult, getNotebooklmPageState, isPlainObject, normalizeNotebooklmTitle, parseNotebooklmHistoryThreadIdsResult, parseNotebooklmIdFromUrl, parseNotebooklmListResult, parseNotebooklmNoteListRawRows, parseNotebooklmNotebookDetailResult, parseNotebooklmNotebookTarget, parseNotebooklmSourceFulltextResult, parseNotebooklmSourceGuideResult, parseNotebooklmSourceListResult, } from './utils.js'; +import { buildNotebooklmRpcBody, classifyNotebooklmPage, extractNotebooklmHistoryPreview, extractNotebooklmRpcResult, getNotebooklmPageState, isPlainObject, normalizeNotebooklmTitle, parseNotebooklmHistoryThreadIdsResult, parseNotebooklmIdFromUrl, parseNotebooklmListResult, parseNotebooklmNoteListRawRows, parseNotebooklmNotebookDetailResult, parseNotebooklmNotebookTarget, parseNotebooklmSourceFulltextResult, parseNotebooklmSourceGuideResult, parseNotebooklmSourceListResult, } from '../utils.js'; import { CliError } from '@agentrhq/webcmd/errors'; describe('notebooklm utils', () => { it('isPlainObject distinguishes objects from arrays / null / primitives', () => { diff --git a/clis/notebooklm/write-note.test.js b/plugins/notebooklm/test/write-note.test.js similarity index 98% rename from clis/notebooklm/write-note.test.js rename to plugins/notebooklm/test/write-note.test.js index 35794e82..5afcde6f 100644 --- a/clis/notebooklm/write-note.test.js +++ b/plugins/notebooklm/test/write-note.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ } from './write-note.js'; +import { __test__ } from '../write-note.js'; const { parseNoteTitle, parseNoteContent, buildCreateNoteShellArgs, buildMutateNoteArgs, parseNoteIdFromResult } = __test__; diff --git a/clis/notebooklm/utils.js b/plugins/notebooklm/utils.js similarity index 100% rename from clis/notebooklm/utils.js rename to plugins/notebooklm/utils.js diff --git a/plugins/notebooklm/webcmd-plugin.json b/plugins/notebooklm/webcmd-plugin.json new file mode 100644 index 00000000..ee5d535f --- /dev/null +++ b/plugins/notebooklm/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "notebooklm", + "version": "0.1.0", + "description": "Webcmd commands for notebooklm", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/clis/notebooklm/write-note.js b/plugins/notebooklm/write-note.js similarity index 100% rename from clis/notebooklm/write-note.js rename to plugins/notebooklm/write-note.js diff --git a/plugins/qoder/README.md b/plugins/qoder/README.md new file mode 100644 index 00000000..76d6499c --- /dev/null +++ b/plugins/qoder/README.md @@ -0,0 +1,33 @@ +# webcmd-plugin-qoder + +Webcmd commands for qoder. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/qoder +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd qoder account` | Click the account button (username) in the Qoder sidebar and return the visible account dropdown items. | +| `webcmd qoder add-workspace` | Click "Add Workspace" — opens the folder picker. Note: this opens a system file-picker dialog that Qoder controls; the actual folder selection must be done in the UI by the user. | +| `webcmd qoder ask` | Send a prompt to Qoder and wait up to --timeout seconds for the reply (best-effort: polls for the chat turn count to grow + stabilize). | +| `webcmd qoder credits` | Click "Credits Usage" and return the credits-usage display text. | +| `webcmd qoder history` | List Quests visible in the Qoder sidebar. Returns title + visible metadata. | +| `webcmd qoder knowledge` | Open the Knowledge view (Qoder's personal/team knowledge base). | +| `webcmd qoder marketplace` | Open the Qoder Marketplace. | +| `webcmd qoder more-actions` | Click the "More Actions" button and list its menu items. | +| `webcmd qoder new` | Start a new Qoder Quest (conversation). Clicks the "New Quest" button in the sidebar (or its ⌘N variant). | +| `webcmd qoder open-editor` | Click "Open Editor" — opens the current draft in a full editor pane. | +| `webcmd qoder open-panel` | Open / close the Qoder bottom panel (Output / Terminal / Debug Console). ⌥⌘B equivalent. | +| `webcmd qoder prompt-enhance` | Click "Prompt Enhance" — Qoder rewrites the current composer draft for better LLM consumption. | +| `webcmd qoder read` | Read messages in the current Qoder Quest. Returns role + text for each visible turn. | +| `webcmd qoder search` | Open Qoder Search palette (⌘P), type a query, return matched options. | +| `webcmd qoder send` | Type text into the Qoder composer and click "Send message" (fire-and-forget). | +| `webcmd qoder settings` | Click the Settings button in the Qoder sidebar. | +| `webcmd qoder sidebar-toggle` | Collapse / Expand the Qoder Quest List sidebar (⌘B). | +| `webcmd qoder status` | Check Qoder CDP connection and report the current renderer URL + title. | +| `webcmd qoder view-all` | Click "View all" to show all Quests. | diff --git a/clis/qoder/_utils.js b/plugins/qoder/_utils.js similarity index 100% rename from clis/qoder/_utils.js rename to plugins/qoder/_utils.js diff --git a/clis/qoder/composer.js b/plugins/qoder/composer.js similarity index 100% rename from clis/qoder/composer.js rename to plugins/qoder/composer.js diff --git a/clis/qoder/history.js b/plugins/qoder/history.js similarity index 100% rename from clis/qoder/history.js rename to plugins/qoder/history.js diff --git a/plugins/qoder/package.json b/plugins/qoder/package.json new file mode 100644 index 00000000..ca9f37d4 --- /dev/null +++ b/plugins/qoder/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-qoder", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for qoder", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/qoder/quest.js b/plugins/qoder/quest.js similarity index 100% rename from clis/qoder/quest.js rename to plugins/qoder/quest.js diff --git a/clis/qoder/read.js b/plugins/qoder/read.js similarity index 100% rename from clis/qoder/read.js rename to plugins/qoder/read.js diff --git a/clis/qoder/status.js b/plugins/qoder/status.js similarity index 100% rename from clis/qoder/status.js rename to plugins/qoder/status.js diff --git a/clis/qoder/qoder.test.js b/plugins/qoder/test/qoder.test.js similarity index 96% rename from clis/qoder/qoder.test.js rename to plugins/qoder/test/qoder.test.js index f3702376..f295cef2 100644 --- a/clis/qoder/qoder.test.js +++ b/plugins/qoder/test/qoder.test.js @@ -2,18 +2,18 @@ import { JSDOM } from 'jsdom'; import { describe, expect, it, vi } from 'vitest'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './quest.js'; -import './history.js'; -import './read.js'; -import './status.js'; -import './ui.js'; -import './composer.js'; +import '../quest.js'; +import '../history.js'; +import '../read.js'; +import '../status.js'; +import '../ui.js'; +import '../composer.js'; import { buildQoderInjectTextScript, evaluateQoder, parsePositiveInt, unwrapEvaluateResult, -} from './_utils.js'; +} from '../_utils.js'; function makePage(evaluateResults = []) { const evaluate = vi.fn(); diff --git a/clis/qoder/ui.js b/plugins/qoder/ui.js similarity index 100% rename from clis/qoder/ui.js rename to plugins/qoder/ui.js diff --git a/plugins/qoder/webcmd-plugin.json b/plugins/qoder/webcmd-plugin.json new file mode 100644 index 00000000..7028d075 --- /dev/null +++ b/plugins/qoder/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "qoder", + "version": "0.1.0", + "description": "Webcmd commands for qoder", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/reddit/README.md b/plugins/reddit/README.md new file mode 100644 index 00000000..f0a40595 --- /dev/null +++ b/plugins/reddit/README.md @@ -0,0 +1,35 @@ +# webcmd-plugin-reddit + +Webcmd commands for reddit. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/reddit +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd reddit comment` | Post a comment on a Reddit post | +| `webcmd reddit frontpage` | Reddit Frontpage / r/all | +| `webcmd reddit home` | Reddit personalized home feed (Best, requires login) | +| `webcmd reddit hot` | Reddit hot posts | +| `webcmd reddit login` | Open reddit login | +| `webcmd reddit popular` | Reddit Popular posts (/r/popular) | +| `webcmd reddit read` | Read a Reddit post and its comments | +| `webcmd reddit reply` | Reply to a Reddit comment | +| `webcmd reddit save` | Save or unsave a Reddit post | +| `webcmd reddit saved` | Browse your saved Reddit posts | +| `webcmd reddit search` | Search Reddit Posts | +| `webcmd reddit subreddit` | Get posts from a specific Subreddit | +| `webcmd reddit subreddit-info` | Show metadata for a Reddit subreddit (subscribers, description, created date, NSFW) | +| `webcmd reddit subscribe` | Subscribe or unsubscribe to a subreddit | +| `webcmd reddit subscribed` | List subreddits you are subscribed to | +| `webcmd reddit upvote` | Upvote or downvote a Reddit post | +| `webcmd reddit upvoted` | Browse your upvoted Reddit posts | +| `webcmd reddit user` | View a Reddit user profile | +| `webcmd reddit user-comments` | View a Reddit user's comment history | +| `webcmd reddit user-posts` | View a Reddit user's submitted posts | +| `webcmd reddit whoami` | Show the currently logged-in Reddit user | diff --git a/clis/reddit/auth.js b/plugins/reddit/auth.js similarity index 95% rename from clis/reddit/auth.js rename to plugins/reddit/auth.js index 23dae52f..f7ea35b0 100644 --- a/clis/reddit/auth.js +++ b/plugins/reddit/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasRedditSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://www.reddit.com' }); @@ -40,6 +40,7 @@ registerSiteAuthCommands({ site: 'reddit', domain: 'reddit.com', loginUrl: 'https://www.reddit.com/login', + registerWhoami: false, columns: ['username', 'id'], quickCheck: hasRedditSessionCookie, verify: verifyRedditIdentity, diff --git a/clis/reddit/comment.js b/plugins/reddit/comment.js similarity index 100% rename from clis/reddit/comment.js rename to plugins/reddit/comment.js diff --git a/clis/reddit/frontpage.js b/plugins/reddit/frontpage.js similarity index 100% rename from clis/reddit/frontpage.js rename to plugins/reddit/frontpage.js diff --git a/clis/reddit/home.js b/plugins/reddit/home.js similarity index 100% rename from clis/reddit/home.js rename to plugins/reddit/home.js diff --git a/clis/reddit/hot.js b/plugins/reddit/hot.js similarity index 100% rename from clis/reddit/hot.js rename to plugins/reddit/hot.js diff --git a/plugins/reddit/package.json b/plugins/reddit/package.json new file mode 100644 index 00000000..d8ecedf6 --- /dev/null +++ b/plugins/reddit/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-reddit", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for reddit", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/reddit/popular.js b/plugins/reddit/popular.js similarity index 100% rename from clis/reddit/popular.js rename to plugins/reddit/popular.js diff --git a/clis/reddit/read.js b/plugins/reddit/read.js similarity index 100% rename from clis/reddit/read.js rename to plugins/reddit/read.js diff --git a/clis/reddit/reply.js b/plugins/reddit/reply.js similarity index 100% rename from clis/reddit/reply.js rename to plugins/reddit/reply.js diff --git a/clis/reddit/save.js b/plugins/reddit/save.js similarity index 100% rename from clis/reddit/save.js rename to plugins/reddit/save.js diff --git a/clis/reddit/saved.js b/plugins/reddit/saved.js similarity index 100% rename from clis/reddit/saved.js rename to plugins/reddit/saved.js diff --git a/clis/reddit/search.js b/plugins/reddit/search.js similarity index 100% rename from clis/reddit/search.js rename to plugins/reddit/search.js diff --git a/clis/reddit/subreddit-info.js b/plugins/reddit/subreddit-info.js similarity index 100% rename from clis/reddit/subreddit-info.js rename to plugins/reddit/subreddit-info.js diff --git a/clis/reddit/subreddit.js b/plugins/reddit/subreddit.js similarity index 100% rename from clis/reddit/subreddit.js rename to plugins/reddit/subreddit.js diff --git a/clis/reddit/subscribe.js b/plugins/reddit/subscribe.js similarity index 100% rename from clis/reddit/subscribe.js rename to plugins/reddit/subscribe.js diff --git a/clis/reddit/subscribed.js b/plugins/reddit/subscribed.js similarity index 100% rename from clis/reddit/subscribed.js rename to plugins/reddit/subscribed.js diff --git a/clis/reddit/extract-media.test.js b/plugins/reddit/test/extract-media.test.js similarity index 100% rename from clis/reddit/extract-media.test.js rename to plugins/reddit/test/extract-media.test.js diff --git a/clis/reddit/frontpage.test.js b/plugins/reddit/test/frontpage.test.js similarity index 98% rename from clis/reddit/frontpage.test.js rename to plugins/reddit/test/frontpage.test.js index d7303ab6..70b9dd95 100644 --- a/clis/reddit/frontpage.test.js +++ b/plugins/reddit/test/frontpage.test.js @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './frontpage.js'; +import '../frontpage.js'; describe('reddit frontpage adapter', () => { const command = getRegistry().get('reddit/frontpage'); diff --git a/clis/reddit/home.test.js b/plugins/reddit/test/home.test.js similarity index 98% rename from clis/reddit/home.test.js rename to plugins/reddit/test/home.test.js index 85e893c4..1bd299ec 100644 --- a/clis/reddit/home.test.js +++ b/plugins/reddit/test/home.test.js @@ -1,8 +1,8 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { extractRedditMedia, parseRedditHomeLimit } from './home.js'; -import './home.js'; +import { extractRedditMedia, parseRedditHomeLimit } from '../home.js'; +import '../home.js'; function makePage(result) { return { diff --git a/clis/reddit/hot.test.js b/plugins/reddit/test/hot.test.js similarity index 98% rename from clis/reddit/hot.test.js rename to plugins/reddit/test/hot.test.js index 649c0666..4aadb261 100644 --- a/clis/reddit/hot.test.js +++ b/plugins/reddit/test/hot.test.js @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './hot.js'; +import '../hot.js'; describe('reddit hot adapter', () => { const command = getRegistry().get('reddit/hot'); diff --git a/clis/reddit/popular.test.js b/plugins/reddit/test/popular.test.js similarity index 98% rename from clis/reddit/popular.test.js rename to plugins/reddit/test/popular.test.js index aac76738..272c197c 100644 --- a/clis/reddit/popular.test.js +++ b/plugins/reddit/test/popular.test.js @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './popular.js'; +import '../popular.js'; describe('reddit popular adapter', () => { const command = getRegistry().get('reddit/popular'); diff --git a/clis/reddit/read.test.js b/plugins/reddit/test/read.test.js similarity index 99% rename from clis/reddit/read.test.js rename to plugins/reddit/test/read.test.js index 4d3403ca..6703c8c9 100644 --- a/clis/reddit/read.test.js +++ b/plugins/reddit/test/read.test.js @@ -1,8 +1,8 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { normalizeRedditPostId, parseExpandRounds } from './read.js'; -import './read.js'; +import { normalizeRedditPostId, parseExpandRounds } from '../read.js'; +import '../read.js'; function makePage(result) { return { diff --git a/clis/reddit/reply.test.js b/plugins/reddit/test/reply.test.js similarity index 99% rename from clis/reddit/reply.test.js rename to plugins/reddit/test/reply.test.js index 78ec752b..a96ebc55 100644 --- a/clis/reddit/reply.test.js +++ b/plugins/reddit/test/reply.test.js @@ -1,8 +1,8 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { normalizeRedditCommentFullname, requireReplyText } from './reply.js'; -import './reply.js'; +import { normalizeRedditCommentFullname, requireReplyText } from '../reply.js'; +import '../reply.js'; function makePage(result = { kind: 'ok', detail: 'Reply posted on t1_okf3s7u as t1_reply123' }) { return { diff --git a/clis/reddit/search.test.js b/plugins/reddit/test/search.test.js similarity index 97% rename from clis/reddit/search.test.js rename to plugins/reddit/test/search.test.js index 15587e78..701842d6 100644 --- a/clis/reddit/search.test.js +++ b/plugins/reddit/test/search.test.js @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './search.js'; +import '../search.js'; describe('reddit search adapter', () => { const command = getRegistry().get('reddit/search'); diff --git a/clis/reddit/subreddit-info.test.js b/plugins/reddit/test/subreddit-info.test.js similarity index 98% rename from clis/reddit/subreddit-info.test.js rename to plugins/reddit/test/subreddit-info.test.js index 0cdb9079..02d1ba6a 100644 --- a/clis/reddit/subreddit-info.test.js +++ b/plugins/reddit/test/subreddit-info.test.js @@ -1,8 +1,8 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { parseSubredditName } from './subreddit-info.js'; -import './subreddit-info.js'; +import { parseSubredditName } from '../subreddit-info.js'; +import '../subreddit-info.js'; function makePage(result) { return { diff --git a/clis/reddit/subreddit.test.js b/plugins/reddit/test/subreddit.test.js similarity index 97% rename from clis/reddit/subreddit.test.js rename to plugins/reddit/test/subreddit.test.js index 21317122..4c7f25bc 100644 --- a/clis/reddit/subreddit.test.js +++ b/plugins/reddit/test/subreddit.test.js @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './subreddit.js'; +import '../subreddit.js'; describe('reddit subreddit adapter', () => { const command = getRegistry().get('reddit/subreddit'); diff --git a/clis/reddit/subscribed.test.js b/plugins/reddit/test/subscribed.test.js similarity index 99% rename from clis/reddit/subscribed.test.js rename to plugins/reddit/test/subscribed.test.js index 40ba6471..d1b7c09d 100644 --- a/clis/reddit/subscribed.test.js +++ b/plugins/reddit/test/subscribed.test.js @@ -1,8 +1,8 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError, LoginWallError } from '@agentrhq/webcmd/errors'; -import { parseRedditSubscribedLimit, unwrapEvaluateResult } from './subscribed.js'; -import './subscribed.js'; +import { parseRedditSubscribedLimit, unwrapEvaluateResult } from '../subscribed.js'; +import '../subscribed.js'; function subredditThing(id, overrides = {}) { const displayName = `sub${id}`; diff --git a/clis/reddit/whoami.test.js b/plugins/reddit/test/whoami.test.js similarity index 99% rename from clis/reddit/whoami.test.js rename to plugins/reddit/test/whoami.test.js index 4e00b02f..ed2b5594 100644 --- a/clis/reddit/whoami.test.js +++ b/plugins/reddit/test/whoami.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './whoami.js'; +import '../whoami.js'; function makePage(result) { return { diff --git a/clis/reddit/upvote.js b/plugins/reddit/upvote.js similarity index 100% rename from clis/reddit/upvote.js rename to plugins/reddit/upvote.js diff --git a/clis/reddit/upvoted.js b/plugins/reddit/upvoted.js similarity index 100% rename from clis/reddit/upvoted.js rename to plugins/reddit/upvoted.js diff --git a/clis/reddit/user-comments.js b/plugins/reddit/user-comments.js similarity index 100% rename from clis/reddit/user-comments.js rename to plugins/reddit/user-comments.js diff --git a/clis/reddit/user-posts.js b/plugins/reddit/user-posts.js similarity index 100% rename from clis/reddit/user-posts.js rename to plugins/reddit/user-posts.js diff --git a/clis/reddit/user.js b/plugins/reddit/user.js similarity index 100% rename from clis/reddit/user.js rename to plugins/reddit/user.js diff --git a/plugins/reddit/webcmd-plugin.json b/plugins/reddit/webcmd-plugin.json new file mode 100644 index 00000000..baf80511 --- /dev/null +++ b/plugins/reddit/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "reddit", + "version": "0.1.0", + "description": "Webcmd commands for reddit", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/clis/reddit/whoami.js b/plugins/reddit/whoami.js similarity index 100% rename from clis/reddit/whoami.js rename to plugins/reddit/whoami.js diff --git a/plugins/slock/README.md b/plugins/slock/README.md new file mode 100644 index 00000000..23f31d5a --- /dev/null +++ b/plugins/slock/README.md @@ -0,0 +1,58 @@ +# webcmd-plugin-slock + +Webcmd commands for slock. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/slock +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd slock attachment-download` | Download an attachment to a local file. Resolves a signed CDN URL in the page, then fetches bytes node-side (no CORS). | +| `webcmd slock attachment-upload` | Upload a local file to Slock attachments. Prints the attachmentId for use with `message-send --attach`. | +| `webcmd slock attachment-url` | Get a short-lived signed CDN URL for an attachment (does not download bytes). | +| `webcmd slock bookmark-add` | Bookmark a message (POST /channels/saved). Requires full messageId UUID. | +| `webcmd slock bookmark-list` | List bookmarks (saved messages) in the active server | +| `webcmd slock bookmark-remove` | Remove a bookmark (DELETE /channels/saved/:messageId). 404 is treated as already-removed. | +| `webcmd slock channel-archive` | Archive a channel — admin only (POST /channels/:id/archive) | +| `webcmd slock channel-create` | Create a channel — admin only (POST /channels/). Public unless --private. | +| `webcmd slock channel-files` | List files shared in a channel (GET /channels/:id/files) | +| `webcmd slock channel-info` | Show one channel's details (GET /channels/:id) | +| `webcmd slock channel-join` | Join a public channel (POST /channels/:id/join) | +| `webcmd slock channel-leave` | Leave a channel (POST /channels/:id/leave) | +| `webcmd slock channel-list` | List channels in the active slock server | +| `webcmd slock channel-mark` | Mark a channel read (default), read up to --seq, or --unread. | +| `webcmd slock channel-members` | List members of a channel | +| `webcmd slock channel-unarchive` | Unarchive a channel — admin only (POST /channels/:id/unarchive). #name lookups exclude archived channels; pass the channelId UUID for archived ones. | +| `webcmd slock dm-list` | List DM channels in the active server (GET /channels/dm) | +| `webcmd slock inbox` | List unified inbox items (channels, DMs, followed threads) that need attention. | +| `webcmd slock inbox-done` | Mark one chat as done / clear it from the inbox (POST /channels/inbox/done) | +| `webcmd slock inbox-read-all` | Mark the entire inbox as read (POST /channels/inbox/read-all) | +| `webcmd slock login` | Open slock login | +| `webcmd slock message-read` | Read messages in a channel or thread. Thread form: "#channel:msgIdOrShort". Use --after seq\|UUID for cursor. | +| `webcmd slock message-search` | Search messages | +| `webcmd slock message-send` | Send a message to a channel, DM, or thread (content sent verbatim) | +| `webcmd slock reaction-add` | Add an emoji reaction to a message (POST /messages/:id/reactions). Idempotent server-side. | +| `webcmd slock reaction-remove` | Remove your emoji reaction from a message (DELETE /messages/:id/reactions). | +| `webcmd slock server-list` | List slock servers you belong to; marks active per localStorage slug | +| `webcmd slock server-use` | Set the active slock server (writes localStorage.slock_last_server_slug) | +| `webcmd slock task-claim` | Claim a chat task (PATCH /tasks/:id/claim). Requires full task UUID (= message id). | +| `webcmd slock task-convert` | Convert a message into a chat task (POST /tasks/convert-message). Accepts a message UUID or "#channel:shortId". | +| `webcmd slock task-create` | Create a task in a channel (single title; batch 1-50 is server-supported but client surface is single — see backlog R4). | +| `webcmd slock task-delete` | Delete a chat task (DELETE /tasks/:taskId). Requires --confirm — destructive, irreversible. | +| `webcmd slock task-get` | Fetch a task by channel + taskNumber (GET /tasks/channel/:channelId/number/:taskNumber). | +| `webcmd slock task-list` | List tasks (chat tasks = messages with task fields) attached to a channel. Optional --status filter. | +| `webcmd slock task-list-server` | List tasks across all channels in the active server (GET /tasks/server). Optional --status filter. | +| `webcmd slock task-status` | Set a task's status (PATCH /tasks/:taskId/status, body {status}). One of todo\|in_progress\|in_review\|done\|closed. | +| `webcmd slock task-unclaim` | Release ownership of a chat task (PATCH /tasks/:id/unclaim). | +| `webcmd slock thread-done` | Mark a thread as done / hide it from the active list (POST /channels/threads/done) | +| `webcmd slock thread-follow` | Follow the thread on a parent message (POST /channels/threads/follow) | +| `webcmd slock thread-list` | List followed threads in the active server (GET /channels/threads/followed) | +| `webcmd slock thread-undone` | Restore a done thread to the active list (POST /channels/threads/undone) | +| `webcmd slock thread-unfollow` | Stop following a thread (POST /channels/threads/unfollow) | +| `webcmd slock unread-summary` | Global unread counts across every server you belong to. | +| `webcmd slock whoami` | Show the current logged-in slock account | diff --git a/clis/slock/attachment-download.js b/plugins/slock/attachment-download.js similarity index 100% rename from clis/slock/attachment-download.js rename to plugins/slock/attachment-download.js diff --git a/clis/slock/attachment-upload.js b/plugins/slock/attachment-upload.js similarity index 100% rename from clis/slock/attachment-upload.js rename to plugins/slock/attachment-upload.js diff --git a/clis/slock/attachment-url.js b/plugins/slock/attachment-url.js similarity index 100% rename from clis/slock/attachment-url.js rename to plugins/slock/attachment-url.js diff --git a/clis/slock/auth-verify.js b/plugins/slock/auth-verify.js similarity index 100% rename from clis/slock/auth-verify.js rename to plugins/slock/auth-verify.js diff --git a/clis/slock/bookmark-add.js b/plugins/slock/bookmark-add.js similarity index 100% rename from clis/slock/bookmark-add.js rename to plugins/slock/bookmark-add.js diff --git a/clis/slock/bookmark-list.js b/plugins/slock/bookmark-list.js similarity index 100% rename from clis/slock/bookmark-list.js rename to plugins/slock/bookmark-list.js diff --git a/clis/slock/bookmark-remove.js b/plugins/slock/bookmark-remove.js similarity index 100% rename from clis/slock/bookmark-remove.js rename to plugins/slock/bookmark-remove.js diff --git a/clis/slock/channel-action.js b/plugins/slock/channel-action.js similarity index 100% rename from clis/slock/channel-action.js rename to plugins/slock/channel-action.js diff --git a/clis/slock/channel-archive.js b/plugins/slock/channel-archive.js similarity index 100% rename from clis/slock/channel-archive.js rename to plugins/slock/channel-archive.js diff --git a/clis/slock/channel-create.js b/plugins/slock/channel-create.js similarity index 100% rename from clis/slock/channel-create.js rename to plugins/slock/channel-create.js diff --git a/clis/slock/channel-files.js b/plugins/slock/channel-files.js similarity index 100% rename from clis/slock/channel-files.js rename to plugins/slock/channel-files.js diff --git a/clis/slock/channel-info.js b/plugins/slock/channel-info.js similarity index 100% rename from clis/slock/channel-info.js rename to plugins/slock/channel-info.js diff --git a/clis/slock/channel-join.js b/plugins/slock/channel-join.js similarity index 100% rename from clis/slock/channel-join.js rename to plugins/slock/channel-join.js diff --git a/clis/slock/channel-leave.js b/plugins/slock/channel-leave.js similarity index 100% rename from clis/slock/channel-leave.js rename to plugins/slock/channel-leave.js diff --git a/clis/slock/channel-list.js b/plugins/slock/channel-list.js similarity index 100% rename from clis/slock/channel-list.js rename to plugins/slock/channel-list.js diff --git a/clis/slock/channel-mark.js b/plugins/slock/channel-mark.js similarity index 100% rename from clis/slock/channel-mark.js rename to plugins/slock/channel-mark.js diff --git a/clis/slock/channel-members.js b/plugins/slock/channel-members.js similarity index 100% rename from clis/slock/channel-members.js rename to plugins/slock/channel-members.js diff --git a/clis/slock/channel-unarchive.js b/plugins/slock/channel-unarchive.js similarity index 100% rename from clis/slock/channel-unarchive.js rename to plugins/slock/channel-unarchive.js diff --git a/clis/slock/dm-list.js b/plugins/slock/dm-list.js similarity index 100% rename from clis/slock/dm-list.js rename to plugins/slock/dm-list.js diff --git a/clis/slock/errors.js b/plugins/slock/errors.js similarity index 100% rename from clis/slock/errors.js rename to plugins/slock/errors.js diff --git a/clis/slock/in-page.js b/plugins/slock/in-page.js similarity index 99% rename from clis/slock/in-page.js rename to plugins/slock/in-page.js index 048f5652..8640f4a9 100644 --- a/clis/slock/in-page.js +++ b/plugins/slock/in-page.js @@ -151,7 +151,7 @@ export function channelResolveFragment(channelInput) { // (e.g. `"#general"` becomes `' in #' + 'general'`). // Pass `"''"` to omit the suffix entirely. // -// Drift-guarded by clis/slock/short-id-canary.test.js — any other file that +// Drift-guarded by plugins/slock/test/short-id-canary.test.js — any other file that // hand-rolls `cxd.targetMessageId` short-id resolution fails the canary. export function resolveShortIdFragment({ shortIdVar, parentChannelIdVar, contextDescription = "''" }) { return ` diff --git a/clis/slock/inbox-done.js b/plugins/slock/inbox-done.js similarity index 100% rename from clis/slock/inbox-done.js rename to plugins/slock/inbox-done.js diff --git a/clis/slock/inbox-read-all.js b/plugins/slock/inbox-read-all.js similarity index 100% rename from clis/slock/inbox-read-all.js rename to plugins/slock/inbox-read-all.js diff --git a/clis/slock/inbox.js b/plugins/slock/inbox.js similarity index 100% rename from clis/slock/inbox.js rename to plugins/slock/inbox.js diff --git a/clis/slock/login.js b/plugins/slock/login.js similarity index 100% rename from clis/slock/login.js rename to plugins/slock/login.js diff --git a/clis/slock/message-read.js b/plugins/slock/message-read.js similarity index 100% rename from clis/slock/message-read.js rename to plugins/slock/message-read.js diff --git a/clis/slock/message-search.js b/plugins/slock/message-search.js similarity index 100% rename from clis/slock/message-search.js rename to plugins/slock/message-search.js diff --git a/clis/slock/message-send.js b/plugins/slock/message-send.js similarity index 100% rename from clis/slock/message-send.js rename to plugins/slock/message-send.js diff --git a/plugins/slock/package.json b/plugins/slock/package.json new file mode 100644 index 00000000..e4c5230b --- /dev/null +++ b/plugins/slock/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-slock", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for slock", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/slock/reaction-add.js b/plugins/slock/reaction-add.js similarity index 100% rename from clis/slock/reaction-add.js rename to plugins/slock/reaction-add.js diff --git a/clis/slock/reaction-remove.js b/plugins/slock/reaction-remove.js similarity index 100% rename from clis/slock/reaction-remove.js rename to plugins/slock/reaction-remove.js diff --git a/clis/slock/resolve.js b/plugins/slock/resolve.js similarity index 100% rename from clis/slock/resolve.js rename to plugins/slock/resolve.js diff --git a/clis/slock/server-list.js b/plugins/slock/server-list.js similarity index 100% rename from clis/slock/server-list.js rename to plugins/slock/server-list.js diff --git a/clis/slock/server-use.js b/plugins/slock/server-use.js similarity index 100% rename from clis/slock/server-use.js rename to plugins/slock/server-use.js diff --git a/clis/slock/shared.js b/plugins/slock/shared.js similarity index 100% rename from clis/slock/shared.js rename to plugins/slock/shared.js diff --git a/clis/slock/task-claim.js b/plugins/slock/task-claim.js similarity index 100% rename from clis/slock/task-claim.js rename to plugins/slock/task-claim.js diff --git a/clis/slock/task-convert.js b/plugins/slock/task-convert.js similarity index 100% rename from clis/slock/task-convert.js rename to plugins/slock/task-convert.js diff --git a/clis/slock/task-create.js b/plugins/slock/task-create.js similarity index 100% rename from clis/slock/task-create.js rename to plugins/slock/task-create.js diff --git a/clis/slock/task-delete.js b/plugins/slock/task-delete.js similarity index 100% rename from clis/slock/task-delete.js rename to plugins/slock/task-delete.js diff --git a/clis/slock/task-get.js b/plugins/slock/task-get.js similarity index 100% rename from clis/slock/task-get.js rename to plugins/slock/task-get.js diff --git a/clis/slock/task-list-server.js b/plugins/slock/task-list-server.js similarity index 100% rename from clis/slock/task-list-server.js rename to plugins/slock/task-list-server.js diff --git a/clis/slock/task-list.js b/plugins/slock/task-list.js similarity index 100% rename from clis/slock/task-list.js rename to plugins/slock/task-list.js diff --git a/clis/slock/task-status.js b/plugins/slock/task-status.js similarity index 100% rename from clis/slock/task-status.js rename to plugins/slock/task-status.js diff --git a/clis/slock/task-unclaim.js b/plugins/slock/task-unclaim.js similarity index 100% rename from clis/slock/task-unclaim.js rename to plugins/slock/task-unclaim.js diff --git a/clis/slock/api-base-canary.test.js b/plugins/slock/test/api-base-canary.test.js similarity index 88% rename from clis/slock/api-base-canary.test.js rename to plugins/slock/test/api-base-canary.test.js index cdb0d98a..47c23cd2 100644 --- a/clis/slock/api-base-canary.test.js +++ b/plugins/slock/test/api-base-canary.test.js @@ -1,7 +1,7 @@ // api-base-canary.test.js // // Origin drift catch: slock is split-origin (token at app.slock.ai, API at -// api.slock.ai). Every fetch in clis/slock MUST go through SLOCK_API_BASE +// api.slock.ai). Every fetch in plugins/slock MUST go through SLOCK_API_BASE // — a hardcoded '/api/...' literal would silently land on the SPA host // (app.slock.ai/api/...) instead of the API host, producing the exact // AUTH_REQUIRED / HTML-instead-of-JSON / "identity empty" symptoms we just @@ -18,10 +18,10 @@ import { readdirSync, readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import path from 'node:path'; -const DIR = path.dirname(fileURLToPath(import.meta.url)); +const DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); describe('slock SLOCK_API_BASE canary', () => { - it('no clis/slock/*.js file contains a hardcoded "/api/..." string literal (must use SLOCK_API_BASE)', () => { + it('no plugins/slock/*.js file contains a hardcoded "/api/..." string literal (must use SLOCK_API_BASE)', () => { const files = readdirSync(DIR) .filter((f) => f.endsWith('.js') && !f.endsWith('.test.js')); @@ -52,7 +52,7 @@ describe('slock SLOCK_API_BASE canary', () => { }); it('SLOCK_API_BASE is the absolute prod URL — never a relative "/api"', async () => { - const { SLOCK_API_BASE } = await import('./shared.js'); + const { SLOCK_API_BASE } = await import('../shared.js'); // Must start with https:// so fetches inside page.evaluate (which runs // on app.slock.ai) actually hit the API host instead of the SPA. expect(SLOCK_API_BASE.startsWith('https://')).toBe(true); diff --git a/clis/slock/attachment-download.test.js b/plugins/slock/test/attachment-download.test.js similarity index 99% rename from clis/slock/attachment-download.test.js rename to plugins/slock/test/attachment-download.test.js index 0e7a695d..a0908474 100644 --- a/clis/slock/attachment-download.test.js +++ b/plugins/slock/test/attachment-download.test.js @@ -3,7 +3,7 @@ import fs from 'node:fs'; import path from 'node:path'; import os from 'node:os'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './attachment-download.js'; +import '../attachment-download.js'; function makePage(envelope) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(envelope) }; diff --git a/clis/slock/attachment-upload.test.js b/plugins/slock/test/attachment-upload.test.js similarity index 99% rename from clis/slock/attachment-upload.test.js rename to plugins/slock/test/attachment-upload.test.js index 9c698eb7..49806055 100644 --- a/clis/slock/attachment-upload.test.js +++ b/plugins/slock/test/attachment-upload.test.js @@ -3,7 +3,7 @@ import fs from 'node:fs'; import path from 'node:path'; import os from 'node:os'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './attachment-upload.js'; +import '../attachment-upload.js'; function makePage(envelope) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(envelope) }; diff --git a/clis/slock/attachment-url.test.js b/plugins/slock/test/attachment-url.test.js similarity index 98% rename from clis/slock/attachment-url.test.js rename to plugins/slock/test/attachment-url.test.js index c86c7186..1da341a4 100644 --- a/clis/slock/attachment-url.test.js +++ b/plugins/slock/test/attachment-url.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './attachment-url.js'; +import '../attachment-url.js'; function makePage(envelope) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(envelope) }; diff --git a/clis/slock/bookmark-add.test.js b/plugins/slock/test/bookmark-add.test.js similarity index 98% rename from clis/slock/bookmark-add.test.js rename to plugins/slock/test/bookmark-add.test.js index 53754fb1..90102637 100644 --- a/clis/slock/bookmark-add.test.js +++ b/plugins/slock/test/bookmark-add.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './bookmark-add.js'; +import '../bookmark-add.js'; function makePage(result = { kind: 'ok', rows: [{ id: 'b1' }] }) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/bookmark-list.test.js b/plugins/slock/test/bookmark-list.test.js similarity index 98% rename from clis/slock/bookmark-list.test.js rename to plugins/slock/test/bookmark-list.test.js index 0e9cacb8..c8ded11b 100644 --- a/clis/slock/bookmark-list.test.js +++ b/plugins/slock/test/bookmark-list.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './bookmark-list.js'; +import '../bookmark-list.js'; describe('slock bookmark-list', () => { const command = getRegistry().get('slock/bookmark-list'); diff --git a/clis/slock/bookmark-remove.test.js b/plugins/slock/test/bookmark-remove.test.js similarity index 96% rename from clis/slock/bookmark-remove.test.js rename to plugins/slock/test/bookmark-remove.test.js index 7954b7e5..4ff9ec21 100644 --- a/clis/slock/bookmark-remove.test.js +++ b/plugins/slock/test/bookmark-remove.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './bookmark-remove.js'; +import '../bookmark-remove.js'; function makePage(result = { kind: 'ok', rows: [{ removed: true }] }) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/channel-action.test.js b/plugins/slock/test/channel-action.test.js similarity index 96% rename from clis/slock/channel-action.test.js rename to plugins/slock/test/channel-action.test.js index 69ef3872..4266d2bd 100644 --- a/clis/slock/channel-action.test.js +++ b/plugins/slock/test/channel-action.test.js @@ -1,9 +1,9 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './channel-join.js'; -import './channel-leave.js'; -import './channel-archive.js'; -import './channel-unarchive.js'; +import '../channel-join.js'; +import '../channel-leave.js'; +import '../channel-archive.js'; +import '../channel-unarchive.js'; function makePage(result = { kind: 'ok', rows: { ok: true } }) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/channel-create.test.js b/plugins/slock/test/channel-create.test.js similarity index 98% rename from clis/slock/channel-create.test.js rename to plugins/slock/test/channel-create.test.js index b538c5dc..83353c7c 100644 --- a/clis/slock/channel-create.test.js +++ b/plugins/slock/test/channel-create.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './channel-create.js'; +import '../channel-create.js'; function makePage(result = { kind: 'ok', rows: { id: 'c1', name: 'launch', type: 'channel' } }) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/channel-files.test.js b/plugins/slock/test/channel-files.test.js similarity index 98% rename from clis/slock/channel-files.test.js rename to plugins/slock/test/channel-files.test.js index 372df8e1..eb4fd259 100644 --- a/clis/slock/channel-files.test.js +++ b/plugins/slock/test/channel-files.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './channel-files.js'; +import '../channel-files.js'; function makePage(result) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/channel-info.test.js b/plugins/slock/test/channel-info.test.js similarity index 97% rename from clis/slock/channel-info.test.js rename to plugins/slock/test/channel-info.test.js index 74cce2da..0b7fba15 100644 --- a/clis/slock/channel-info.test.js +++ b/plugins/slock/test/channel-info.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './channel-info.js'; +import '../channel-info.js'; function makePage(result) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/channel-list.test.js b/plugins/slock/test/channel-list.test.js similarity index 97% rename from clis/slock/channel-list.test.js rename to plugins/slock/test/channel-list.test.js index 863c7474..614d07b6 100644 --- a/clis/slock/channel-list.test.js +++ b/plugins/slock/test/channel-list.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { AuthRequiredError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './channel-list.js'; +import '../channel-list.js'; function makePage(result = { kind: 'ok', rows: [] }) { return { diff --git a/clis/slock/channel-mark.test.js b/plugins/slock/test/channel-mark.test.js similarity index 98% rename from clis/slock/channel-mark.test.js rename to plugins/slock/test/channel-mark.test.js index 7b6d7120..02eecbc0 100644 --- a/clis/slock/channel-mark.test.js +++ b/plugins/slock/test/channel-mark.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './channel-mark.js'; +import '../channel-mark.js'; function makePage(result = { kind: 'ok', rows: { ok: true } }) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/channel-members.test.js b/plugins/slock/test/channel-members.test.js similarity index 98% rename from clis/slock/channel-members.test.js rename to plugins/slock/test/channel-members.test.js index 5a79b1a3..75a1b17d 100644 --- a/clis/slock/channel-members.test.js +++ b/plugins/slock/test/channel-members.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './channel-members.js'; +import '../channel-members.js'; function makePage(result) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/cross-command.test.js b/plugins/slock/test/cross-command.test.js similarity index 96% rename from clis/slock/cross-command.test.js rename to plugins/slock/test/cross-command.test.js index 4ba7539b..e25c062e 100644 --- a/clis/slock/cross-command.test.js +++ b/plugins/slock/test/cross-command.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './channel-list.js'; +import '../channel-list.js'; describe('[anti-drift] X-Server-Id does not leak across commands', () => { it('two channel-list calls with different --server use distinct sids in their snippets', async () => { diff --git a/clis/slock/dm-list.test.js b/plugins/slock/test/dm-list.test.js similarity index 97% rename from clis/slock/dm-list.test.js rename to plugins/slock/test/dm-list.test.js index a003aaee..82a77896 100644 --- a/clis/slock/dm-list.test.js +++ b/plugins/slock/test/dm-list.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './dm-list.js'; +import '../dm-list.js'; function makePage(result) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/error-detail-canary.test.js b/plugins/slock/test/error-detail-canary.test.js similarity index 97% rename from clis/slock/error-detail-canary.test.js rename to plugins/slock/test/error-detail-canary.test.js index b6e6e050..4263f503 100644 --- a/clis/slock/error-detail-canary.test.js +++ b/plugins/slock/test/error-detail-canary.test.js @@ -28,7 +28,7 @@ import { readdirSync, readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import path from 'node:path'; -const DIR = path.dirname(fileURLToPath(import.meta.url)); +const DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const RAW_INTERPOLATION = /\$\{(?!SLOCK_API_BASE\})/; @@ -78,7 +78,7 @@ function findOffenders(src) { } describe('slock error-detail injection canary', () => { - it('no detail:/where: literal in clis/slock/*.js interpolates raw input inside its quotes', () => { + it('no detail:/where: literal in plugins/slock/*.js interpolates raw input inside its quotes', () => { const files = readdirSync(DIR) .filter((f) => f.endsWith('.js') && !f.endsWith('.test.js')); const offenders = []; diff --git a/clis/slock/errors.test.js b/plugins/slock/test/errors.test.js similarity index 95% rename from clis/slock/errors.test.js rename to plugins/slock/test/errors.test.js index eaa75ffd..e0a7a00a 100644 --- a/clis/slock/errors.test.js +++ b/plugins/slock/test/errors.test.js @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; import { ArgumentError, AuthRequiredError, CommandExecutionError, ConfigError } from '@agentrhq/webcmd/errors'; -import { dispatchEvaluateResult } from './errors.js'; +import { dispatchEvaluateResult } from '../errors.js'; describe('dispatchEvaluateResult', () => { it('returns rows on kind:"ok"', () => { diff --git a/clis/slock/in-page.test.js b/plugins/slock/test/in-page.test.js similarity index 99% rename from clis/slock/in-page.test.js rename to plugins/slock/test/in-page.test.js index c0f6b04d..ed3df034 100644 --- a/clis/slock/in-page.test.js +++ b/plugins/slock/test/in-page.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; -import { buildFetchSnippet, buildChannelScopedSnippet, channelResolveFragment } from './in-page.js'; -import { SLOCK_API_BASE } from './shared.js'; +import { buildFetchSnippet, buildChannelScopedSnippet, channelResolveFragment } from '../in-page.js'; +import { SLOCK_API_BASE } from '../shared.js'; const UUID_A = '11111111-1111-1111-1111-111111111111'; diff --git a/clis/slock/inbox-done.test.js b/plugins/slock/test/inbox-done.test.js similarity index 97% rename from clis/slock/inbox-done.test.js rename to plugins/slock/test/inbox-done.test.js index 4bc47070..9cd182a2 100644 --- a/clis/slock/inbox-done.test.js +++ b/plugins/slock/test/inbox-done.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './inbox-done.js'; +import '../inbox-done.js'; function makePage(result = { kind: 'ok', rows: { ok: true } }) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/inbox-read-all.test.js b/plugins/slock/test/inbox-read-all.test.js similarity index 96% rename from clis/slock/inbox-read-all.test.js rename to plugins/slock/test/inbox-read-all.test.js index 261df59c..adde93b8 100644 --- a/clis/slock/inbox-read-all.test.js +++ b/plugins/slock/test/inbox-read-all.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './inbox-read-all.js'; +import '../inbox-read-all.js'; function makePage(result = { kind: 'ok', rows: { ok: true, markedCount: 7 } }) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/inbox.test.js b/plugins/slock/test/inbox.test.js similarity index 99% rename from clis/slock/inbox.test.js rename to plugins/slock/test/inbox.test.js index af72065f..6ae083b0 100644 --- a/clis/slock/inbox.test.js +++ b/plugins/slock/test/inbox.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './inbox.js'; +import '../inbox.js'; function makePage(result) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/message-read.test.js b/plugins/slock/test/message-read.test.js similarity index 99% rename from clis/slock/message-read.test.js rename to plugins/slock/test/message-read.test.js index 51de7506..be435e8d 100644 --- a/clis/slock/message-read.test.js +++ b/plugins/slock/test/message-read.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './message-read.js'; +import '../message-read.js'; function makePage(result) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/message-search.test.js b/plugins/slock/test/message-search.test.js similarity index 98% rename from clis/slock/message-search.test.js rename to plugins/slock/test/message-search.test.js index 0d651115..253d2c19 100644 --- a/clis/slock/message-search.test.js +++ b/plugins/slock/test/message-search.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './message-search.js'; +import '../message-search.js'; function makePage(result) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/message-send.test.js b/plugins/slock/test/message-send.test.js similarity index 99% rename from clis/slock/message-send.test.js rename to plugins/slock/test/message-send.test.js index be284988..a60e48d9 100644 --- a/clis/slock/message-send.test.js +++ b/plugins/slock/test/message-send.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './message-send.js'; +import '../message-send.js'; function makePage(result = { kind: 'ok', rows: [{ id: 'm1' }] }) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/reaction-add.test.js b/plugins/slock/test/reaction-add.test.js similarity index 98% rename from clis/slock/reaction-add.test.js rename to plugins/slock/test/reaction-add.test.js index 280b6414..818394b6 100644 --- a/clis/slock/reaction-add.test.js +++ b/plugins/slock/test/reaction-add.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './reaction-add.js'; +import '../reaction-add.js'; const UUID = '550e8400-e29b-41d4-a716-446655440000'; function makePage(result = { kind: 'ok', rows: { id: 'm1' } }) { diff --git a/clis/slock/reaction-remove.test.js b/plugins/slock/test/reaction-remove.test.js similarity index 97% rename from clis/slock/reaction-remove.test.js rename to plugins/slock/test/reaction-remove.test.js index 3842d00f..049ea42b 100644 --- a/clis/slock/reaction-remove.test.js +++ b/plugins/slock/test/reaction-remove.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './reaction-remove.js'; +import '../reaction-remove.js'; const UUID = '550e8400-e29b-41d4-a716-446655440000'; function makePage(result = { kind: 'ok', rows: { id: 'm1' } }) { diff --git a/clis/slock/resolve.test.js b/plugins/slock/test/resolve.test.js similarity index 95% rename from clis/slock/resolve.test.js rename to plugins/slock/test/resolve.test.js index 8cce37a9..c9c39e4a 100644 --- a/clis/slock/resolve.test.js +++ b/plugins/slock/test/resolve.test.js @@ -1,10 +1,10 @@ import { describe, it, expect } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; -import { UUID_RE } from './resolve.js'; -import { classifyThreadTarget } from './resolve.js'; -import { classifyTarget } from './resolve.js'; -import { assertMessageIdShape } from './resolve.js'; -import { parseNonNegativeInteger, parsePositiveInteger } from './resolve.js'; +import { UUID_RE } from '../resolve.js'; +import { classifyThreadTarget } from '../resolve.js'; +import { classifyTarget } from '../resolve.js'; +import { assertMessageIdShape } from '../resolve.js'; +import { parseNonNegativeInteger, parsePositiveInteger } from '../resolve.js'; describe('UUID_RE', () => { it('matches a v4-shaped uuid', () => { diff --git a/clis/slock/server-list.test.js b/plugins/slock/test/server-list.test.js similarity index 96% rename from clis/slock/server-list.test.js rename to plugins/slock/test/server-list.test.js index ab827b00..61e96393 100644 --- a/clis/slock/server-list.test.js +++ b/plugins/slock/test/server-list.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './server-list.js'; +import '../server-list.js'; describe('slock server-list', () => { const command = getRegistry().get('slock/server-list'); diff --git a/clis/slock/server-override-canary.test.js b/plugins/slock/test/server-override-canary.test.js similarity index 92% rename from clis/slock/server-override-canary.test.js rename to plugins/slock/test/server-override-canary.test.js index 374fc30a..6d733dc5 100644 --- a/clis/slock/server-override-canary.test.js +++ b/plugins/slock/test/server-override-canary.test.js @@ -7,7 +7,7 @@ // command file is the bug class that landed Jacky's `--server community` // at HTTP 500 even though the active-slug path was fine. // -// This canary scans every clis/slock/*.js file for the offending shape +// This canary scans every plugins/slock/*.js file for the offending shape // and asserts that any file that mentions `x-server-id` (the request // header) does so via authHeadersFragment, not its own header object. // It catches the message-read / message-search / message-send class of @@ -19,7 +19,7 @@ import { readdirSync, readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import path from 'node:path'; -const DIR = path.dirname(fileURLToPath(import.meta.url)); +const DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); function stripComments(src) { return src @@ -28,7 +28,7 @@ function stripComments(src) { } describe('slock R1 server-override canary', () => { - it('no clis/slock/*.js (except in-page.js) hand-rolls a `let sid = ; if (!sid)` server-resolve copy', () => { + it('no plugins/slock/*.js (except in-page.js) hand-rolls a `let sid = ; if (!sid)` server-resolve copy', () => { const files = readdirSync(DIR) .filter((f) => f.endsWith('.js') && !f.endsWith('.test.js')); diff --git a/clis/slock/server-use.test.js b/plugins/slock/test/server-use.test.js similarity index 98% rename from clis/slock/server-use.test.js rename to plugins/slock/test/server-use.test.js index ad01ffd2..d3eeb5d4 100644 --- a/clis/slock/server-use.test.js +++ b/plugins/slock/test/server-use.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './server-use.js'; +import '../server-use.js'; describe('slock server-use', () => { const command = getRegistry().get('slock/server-use'); diff --git a/clis/slock/short-id-canary.test.js b/plugins/slock/test/short-id-canary.test.js similarity index 93% rename from clis/slock/short-id-canary.test.js rename to plugins/slock/test/short-id-canary.test.js index ce379e00..9d769a29 100644 --- a/clis/slock/short-id-canary.test.js +++ b/plugins/slock/test/short-id-canary.test.js @@ -8,7 +8,7 @@ // in every previous rewrite because each consumer carried its own copy. // // R3 consolidates that resolution into a single helper in in-page.js -// (`resolveShortIdFragment`). This canary scans every other clis/slock/*.js +// (`resolveShortIdFragment`). This canary scans every other plugins/slock/*.js // for the offending copy shape and asserts that any file referencing // `cxd.targetMessageId` does so via the helper, not by hand. // @@ -24,7 +24,7 @@ import { readdirSync, readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import path from 'node:path'; -const DIR = path.dirname(fileURLToPath(import.meta.url)); +const DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); function stripComments(src) { return src @@ -33,7 +33,7 @@ function stripComments(src) { } describe('slock R3 short-id resolution canary', () => { - it('no clis/slock/*.js (except in-page.js) hand-rolls a short-id → cxd.targetMessageId resolution', () => { + it('no plugins/slock/*.js (except in-page.js) hand-rolls a short-id → cxd.targetMessageId resolution', () => { const files = readdirSync(DIR) .filter((f) => f.endsWith('.js') && !f.endsWith('.test.js')); diff --git a/clis/slock/site-session-canary.test.js b/plugins/slock/test/site-session-canary.test.js similarity index 90% rename from clis/slock/site-session-canary.test.js rename to plugins/slock/test/site-session-canary.test.js index 27c51a87..ac7a0731 100644 --- a/clis/slock/site-session-canary.test.js +++ b/plugins/slock/test/site-session-canary.test.js @@ -8,8 +8,8 @@ // Result: a fully successful `login` leaves every other command reading an // empty profile → AUTH_REQUIRED. // -// We discover commands via the registry (not grep over clis/slock/*.js) -// because `whoami` and `login` are registered from clis/_shared/site-auth.js, +// We discover commands via the registry (not grep over plugins/slock/*.js) +// because `whoami` and `login` are registered through plugin-runtime, // and a grep approach would miss them. Registry iteration also covers any // future Ph10+ commands the moment they get registered — drift-proof. @@ -19,7 +19,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'; import path from 'node:path'; import { getRegistry } from '@agentrhq/webcmd/registry'; -const DIR = path.dirname(fileURLToPath(import.meta.url)); +const DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); describe('slock site-session canary', () => { it('every cookie/browser slock command sets siteSession="persistent" (shares login\'s profile)', async () => { diff --git a/clis/slock/task-claim.test.js b/plugins/slock/test/task-claim.test.js similarity index 99% rename from clis/slock/task-claim.test.js rename to plugins/slock/test/task-claim.test.js index f06bb8fb..418ba7fa 100644 --- a/clis/slock/task-claim.test.js +++ b/plugins/slock/test/task-claim.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './task-claim.js'; +import '../task-claim.js'; function makePage(envelope) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(envelope) }; diff --git a/clis/slock/task-convert.test.js b/plugins/slock/test/task-convert.test.js similarity index 99% rename from clis/slock/task-convert.test.js rename to plugins/slock/test/task-convert.test.js index 0453024a..cbbd25a5 100644 --- a/clis/slock/task-convert.test.js +++ b/plugins/slock/test/task-convert.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError } from '@agentrhq/webcmd/errors'; -import './task-convert.js'; +import '../task-convert.js'; function makePage(envelope) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(envelope) }; diff --git a/clis/slock/task-create.test.js b/plugins/slock/test/task-create.test.js similarity index 99% rename from clis/slock/task-create.test.js rename to plugins/slock/test/task-create.test.js index b0e49ec9..35287489 100644 --- a/clis/slock/task-create.test.js +++ b/plugins/slock/test/task-create.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './task-create.js'; +import '../task-create.js'; function makePage(envelope) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(envelope) }; diff --git a/clis/slock/task-delete.test.js b/plugins/slock/test/task-delete.test.js similarity index 98% rename from clis/slock/task-delete.test.js rename to plugins/slock/test/task-delete.test.js index 4c1386fc..cfe6fab1 100644 --- a/clis/slock/task-delete.test.js +++ b/plugins/slock/test/task-delete.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError } from '@agentrhq/webcmd/errors'; -import './task-delete.js'; +import '../task-delete.js'; function makePage(envelope) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(envelope) }; diff --git a/clis/slock/task-get.test.js b/plugins/slock/test/task-get.test.js similarity index 98% rename from clis/slock/task-get.test.js rename to plugins/slock/test/task-get.test.js index 8c79aab5..b18d97e1 100644 --- a/clis/slock/task-get.test.js +++ b/plugins/slock/test/task-get.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './task-get.js'; +import '../task-get.js'; function makePage(envelope) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(envelope) }; diff --git a/clis/slock/task-list-server.test.js b/plugins/slock/test/task-list-server.test.js similarity index 98% rename from clis/slock/task-list-server.test.js rename to plugins/slock/test/task-list-server.test.js index bc285253..37586abd 100644 --- a/clis/slock/task-list-server.test.js +++ b/plugins/slock/test/task-list-server.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './task-list-server.js'; +import '../task-list-server.js'; function makePage(envelope) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(envelope) }; diff --git a/clis/slock/task-list.test.js b/plugins/slock/test/task-list.test.js similarity index 99% rename from clis/slock/task-list.test.js rename to plugins/slock/test/task-list.test.js index 2ef486a6..6c61c896 100644 --- a/clis/slock/task-list.test.js +++ b/plugins/slock/test/task-list.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './task-list.js'; +import '../task-list.js'; function makePage(result) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/task-status.test.js b/plugins/slock/test/task-status.test.js similarity index 99% rename from clis/slock/task-status.test.js rename to plugins/slock/test/task-status.test.js index b5fad47a..55c7d313 100644 --- a/clis/slock/task-status.test.js +++ b/plugins/slock/test/task-status.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './task-status.js'; +import '../task-status.js'; function makePage(envelope) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(envelope) }; diff --git a/clis/slock/task-unclaim.test.js b/plugins/slock/test/task-unclaim.test.js similarity index 98% rename from clis/slock/task-unclaim.test.js rename to plugins/slock/test/task-unclaim.test.js index c5884ba5..12696166 100644 --- a/clis/slock/task-unclaim.test.js +++ b/plugins/slock/test/task-unclaim.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './task-unclaim.js'; +import '../task-unclaim.js'; function makePage(envelope) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(envelope) }; diff --git a/clis/slock/thread-follow.test.js b/plugins/slock/test/thread-follow.test.js similarity index 98% rename from clis/slock/thread-follow.test.js rename to plugins/slock/test/thread-follow.test.js index e7520c86..7508800e 100644 --- a/clis/slock/thread-follow.test.js +++ b/plugins/slock/test/thread-follow.test.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './thread-follow.js'; +import '../thread-follow.js'; const UUID = '550e8400-e29b-41d4-a716-446655440000'; function makePage(result = { kind: 'ok', rows: { ok: true, threadChannelId: 'th1' } }) { diff --git a/clis/slock/thread-list.test.js b/plugins/slock/test/thread-list.test.js similarity index 97% rename from clis/slock/thread-list.test.js rename to plugins/slock/test/thread-list.test.js index 23c1ce91..b7623773 100644 --- a/clis/slock/thread-list.test.js +++ b/plugins/slock/test/thread-list.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './thread-list.js'; +import '../thread-list.js'; function makePage(result) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/thread-state.test.js b/plugins/slock/test/thread-state.test.js similarity index 94% rename from clis/slock/thread-state.test.js rename to plugins/slock/test/thread-state.test.js index 3b8353da..84a25ad1 100644 --- a/clis/slock/thread-state.test.js +++ b/plugins/slock/test/thread-state.test.js @@ -1,9 +1,9 @@ import { describe, it, expect, vi } from 'vitest'; import { ArgumentError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './thread-unfollow.js'; -import './thread-done.js'; -import './thread-undone.js'; +import '../thread-unfollow.js'; +import '../thread-done.js'; +import '../thread-undone.js'; const UUID = '550e8400-e29b-41d4-a716-446655440000'; function makePage(result = { kind: 'ok', rows: { ok: true } }) { diff --git a/clis/slock/unread-summary.test.js b/plugins/slock/test/unread-summary.test.js similarity index 97% rename from clis/slock/unread-summary.test.js rename to plugins/slock/test/unread-summary.test.js index a99648ac..4f02f8bd 100644 --- a/clis/slock/unread-summary.test.js +++ b/plugins/slock/test/unread-summary.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './unread-summary.js'; +import '../unread-summary.js'; function makePage(result) { return { goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result) }; diff --git a/clis/slock/whoami.test.js b/plugins/slock/test/whoami.test.js similarity index 97% rename from clis/slock/whoami.test.js rename to plugins/slock/test/whoami.test.js index 04c02b9c..a0a22c5c 100644 --- a/clis/slock/whoami.test.js +++ b/plugins/slock/test/whoami.test.js @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './whoami.js'; +import '../whoami.js'; function makePage(authMe, status = 200) { return { diff --git a/clis/slock/thread-done.js b/plugins/slock/thread-done.js similarity index 100% rename from clis/slock/thread-done.js rename to plugins/slock/thread-done.js diff --git a/clis/slock/thread-follow.js b/plugins/slock/thread-follow.js similarity index 100% rename from clis/slock/thread-follow.js rename to plugins/slock/thread-follow.js diff --git a/clis/slock/thread-list.js b/plugins/slock/thread-list.js similarity index 100% rename from clis/slock/thread-list.js rename to plugins/slock/thread-list.js diff --git a/clis/slock/thread-state.js b/plugins/slock/thread-state.js similarity index 100% rename from clis/slock/thread-state.js rename to plugins/slock/thread-state.js diff --git a/clis/slock/thread-undone.js b/plugins/slock/thread-undone.js similarity index 100% rename from clis/slock/thread-undone.js rename to plugins/slock/thread-undone.js diff --git a/clis/slock/thread-unfollow.js b/plugins/slock/thread-unfollow.js similarity index 100% rename from clis/slock/thread-unfollow.js rename to plugins/slock/thread-unfollow.js diff --git a/clis/slock/unread-summary.js b/plugins/slock/unread-summary.js similarity index 100% rename from clis/slock/unread-summary.js rename to plugins/slock/unread-summary.js diff --git a/plugins/slock/webcmd-plugin.json b/plugins/slock/webcmd-plugin.json new file mode 100644 index 00000000..8d11f27a --- /dev/null +++ b/plugins/slock/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "slock", + "version": "0.1.0", + "description": "Webcmd commands for slock", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/clis/slock/whoami.js b/plugins/slock/whoami.js similarity index 83% rename from clis/slock/whoami.js rename to plugins/slock/whoami.js index 808e0059..b40e4bf8 100644 --- a/clis/slock/whoami.js +++ b/plugins/slock/whoami.js @@ -1,5 +1,5 @@ // whoami.js — registers BOTH whoami and login via the shared helper -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; import { SLOCK_SITE, SLOCK_DOMAIN, SLOCK_HOME_URL } from './shared.js'; import { verifySlockSession } from './auth-verify.js'; diff --git a/plugins/tiktok/README.md b/plugins/tiktok/README.md new file mode 100644 index 00000000..2690641c --- /dev/null +++ b/plugins/tiktok/README.md @@ -0,0 +1,32 @@ +# webcmd-plugin-tiktok + +Webcmd commands for tiktok. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/tiktok +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd tiktok comment` | Post a comment on a TikTok video | +| `webcmd tiktok creator-videos` | TikTok Studio creator content list (views/likes/comments/saves/shares) | +| `webcmd tiktok explore` | Get trending TikTok videos from the recommend feed via page-context APIs | +| `webcmd tiktok follow` | Follow a TikTok user by username | +| `webcmd tiktok following` | List accounts the logged-in user follows on TikTok via page-context APIs | +| `webcmd tiktok friends` | Get TikTok friend / who-to-follow suggestions via page-context APIs | +| `webcmd tiktok like` | Like a TikTok video | +| `webcmd tiktok live` | Browse TikTok live streams via page-context APIs | +| `webcmd tiktok login` | Open tiktok login | +| `webcmd tiktok notifications` | Read TikTok inbox notifications (likes, comments, mentions, followers) via page-context APIs | +| `webcmd tiktok profile` | Get TikTok user profile info | +| `webcmd tiktok save` | Add a TikTok video to Favorites | +| `webcmd tiktok search` | Search TikTok videos | +| `webcmd tiktok unfollow` | Unfollow a TikTok user by username | +| `webcmd tiktok unlike` | Unlike a TikTok video | +| `webcmd tiktok unsave` | Remove a TikTok video from Favorites | +| `webcmd tiktok user` | Get recent videos from a TikTok user via page-context APIs | +| `webcmd tiktok whoami` | Show the current logged-in tiktok account | diff --git a/clis/tiktok/auth.js b/plugins/tiktok/auth.js similarity index 96% rename from clis/tiktok/auth.js rename to plugins/tiktok/auth.js index 6dc30aa1..cc9cc930 100644 --- a/clis/tiktok/auth.js +++ b/plugins/tiktok/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasTiktokSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://www.tiktok.com' }); diff --git a/clis/tiktok/comment.js b/plugins/tiktok/comment.js similarity index 100% rename from clis/tiktok/comment.js rename to plugins/tiktok/comment.js diff --git a/clis/tiktok/creator-videos.js b/plugins/tiktok/creator-videos.js similarity index 100% rename from clis/tiktok/creator-videos.js rename to plugins/tiktok/creator-videos.js diff --git a/clis/tiktok/explore.js b/plugins/tiktok/explore.js similarity index 100% rename from clis/tiktok/explore.js rename to plugins/tiktok/explore.js diff --git a/clis/tiktok/follow.js b/plugins/tiktok/follow.js similarity index 100% rename from clis/tiktok/follow.js rename to plugins/tiktok/follow.js diff --git a/clis/tiktok/following.js b/plugins/tiktok/following.js similarity index 100% rename from clis/tiktok/following.js rename to plugins/tiktok/following.js diff --git a/clis/tiktok/friends.js b/plugins/tiktok/friends.js similarity index 100% rename from clis/tiktok/friends.js rename to plugins/tiktok/friends.js diff --git a/clis/tiktok/like.js b/plugins/tiktok/like.js similarity index 100% rename from clis/tiktok/like.js rename to plugins/tiktok/like.js diff --git a/clis/tiktok/live.js b/plugins/tiktok/live.js similarity index 100% rename from clis/tiktok/live.js rename to plugins/tiktok/live.js diff --git a/clis/tiktok/notifications.js b/plugins/tiktok/notifications.js similarity index 100% rename from clis/tiktok/notifications.js rename to plugins/tiktok/notifications.js diff --git a/plugins/tiktok/package.json b/plugins/tiktok/package.json new file mode 100644 index 00000000..3dda3f26 --- /dev/null +++ b/plugins/tiktok/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-tiktok", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for tiktok", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/tiktok/profile.js b/plugins/tiktok/profile.js similarity index 100% rename from clis/tiktok/profile.js rename to plugins/tiktok/profile.js diff --git a/clis/tiktok/save.js b/plugins/tiktok/save.js similarity index 100% rename from clis/tiktok/save.js rename to plugins/tiktok/save.js diff --git a/clis/tiktok/search.js b/plugins/tiktok/search.js similarity index 100% rename from clis/tiktok/search.js rename to plugins/tiktok/search.js diff --git a/clis/tiktok/creator-videos.test.js b/plugins/tiktok/test/creator-videos.test.js similarity index 98% rename from clis/tiktok/creator-videos.test.js rename to plugins/tiktok/test/creator-videos.test.js index 1c1a3752..c890a05b 100644 --- a/clis/tiktok/creator-videos.test.js +++ b/plugins/tiktok/test/creator-videos.test.js @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { creatorVideosCommand, __test__ } from './creator-videos.js'; +import { creatorVideosCommand, __test__ } from '../creator-videos.js'; function makePage(evaluateResults = []) { const evaluate = vi.fn(); diff --git a/clis/tiktok/refactor.test.js b/plugins/tiktok/test/refactor.test.js similarity index 98% rename from clis/tiktok/refactor.test.js rename to plugins/tiktok/test/refactor.test.js index 62c4887b..fadbc32a 100644 --- a/clis/tiktok/refactor.test.js +++ b/plugins/tiktok/test/refactor.test.js @@ -12,12 +12,12 @@ import { CommandExecutionError, EmptyResultError, } from '@agentrhq/webcmd/errors'; -import { exploreCommand, __test__ as exploreTest } from './explore.js'; -import { friendsCommand, __test__ as friendsTest } from './friends.js'; -import { followingCommand, __test__ as followingTest } from './following.js'; -import { notificationsCommand, __test__ as notificationsTest } from './notifications.js'; -import { liveCommand, __test__ as liveTest } from './live.js'; -import { userCommand, __test__ as userTest } from './user.js'; +import { exploreCommand, __test__ as exploreTest } from '../explore.js'; +import { friendsCommand, __test__ as friendsTest } from '../friends.js'; +import { followingCommand, __test__ as followingTest } from '../following.js'; +import { notificationsCommand, __test__ as notificationsTest } from '../notifications.js'; +import { liveCommand, __test__ as liveTest } from '../live.js'; +import { userCommand, __test__ as userTest } from '../user.js'; import { BROWSER_HELPERS, NOTIFICATION_TYPES, @@ -28,7 +28,7 @@ import { normalizeUsername, requireLimit, requireNotificationType, -} from './utils.js'; +} from '../utils.js'; function makePage(rows) { return { diff --git a/clis/tiktok/write-refactor.test.js b/plugins/tiktok/test/write-refactor.test.js similarity index 98% rename from clis/tiktok/write-refactor.test.js rename to plugins/tiktok/test/write-refactor.test.js index 2c740b47..fcd0df4e 100644 --- a/clis/tiktok/write-refactor.test.js +++ b/plugins/tiktok/test/write-refactor.test.js @@ -15,9 +15,9 @@ import { AuthRequiredError, CommandExecutionError, } from '@agentrhq/webcmd/errors'; -import { commentCommand, __test__ as commentTest } from './comment.js'; -import { followCommand, __test__ as followTest } from './follow.js'; -import { unfollowCommand, __test__ as unfollowTest } from './unfollow.js'; +import { commentCommand, __test__ as commentTest } from '../comment.js'; +import { followCommand, __test__ as followTest } from '../follow.js'; +import { unfollowCommand, __test__ as unfollowTest } from '../unfollow.js'; import { BUTTON_WALKER_HELPERS, BUTTON_WALKER_SENTINELS, @@ -26,7 +26,7 @@ import { parseTikTokVideoUrl, requireCommentText, throwButtonWalkerError, -} from './utils.js'; +} from '../utils.js'; function makePage(rows) { return { diff --git a/clis/tiktok/unfollow.js b/plugins/tiktok/unfollow.js similarity index 100% rename from clis/tiktok/unfollow.js rename to plugins/tiktok/unfollow.js diff --git a/clis/tiktok/unlike.js b/plugins/tiktok/unlike.js similarity index 100% rename from clis/tiktok/unlike.js rename to plugins/tiktok/unlike.js diff --git a/clis/tiktok/unsave.js b/plugins/tiktok/unsave.js similarity index 100% rename from clis/tiktok/unsave.js rename to plugins/tiktok/unsave.js diff --git a/clis/tiktok/user.js b/plugins/tiktok/user.js similarity index 100% rename from clis/tiktok/user.js rename to plugins/tiktok/user.js diff --git a/clis/tiktok/utils.js b/plugins/tiktok/utils.js similarity index 100% rename from clis/tiktok/utils.js rename to plugins/tiktok/utils.js diff --git a/plugins/tiktok/webcmd-plugin.json b/plugins/tiktok/webcmd-plugin.json new file mode 100644 index 00000000..dc1c69d3 --- /dev/null +++ b/plugins/tiktok/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "tiktok", + "version": "0.1.0", + "description": "Webcmd commands for tiktok", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/trip/README.md b/plugins/trip/README.md new file mode 100644 index 00000000..e1de2da4 --- /dev/null +++ b/plugins/trip/README.md @@ -0,0 +1,26 @@ +# webcmd-plugin-trip + +Webcmd commands for trip. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/trip +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd trip attraction` | Search Trip.com attractions and experiences by destination keyword | +| `webcmd trip car` | List Trip.com car-rental vehicles for a city (category, model, seats, daily price) | +| `webcmd trip deals` | List Trip.com live promotions from the Top Deals hub: campaign title, offer, discount, and link | +| `webcmd trip flight` | Search Trip.com one-way flights by IATA route + departure date | +| `webcmd trip flight-round` | Search Trip.com round-trip flights by IATA route + depart/return dates | +| `webcmd trip hotel` | Show a Trip.com hotel detail by id (rating breakdown, amenities, check-in/out policy) | +| `webcmd trip hotel-search` | List Trip.com hotels for a city id + check-in/out date range | +| `webcmd trip package` | Search Trip.com flight+hotel packages by route + dates; lists the package flight options priced at the bundle rate | +| `webcmd trip search` | Suggest Trip.com destinations (cities, airports) for a keyword; resolves the ids the other commands take | +| `webcmd trip tour` | Search Trip.com tour packages by destination keyword (private or group tours) | +| `webcmd trip train` | Show a Trip.com train route timetable (departure/arrival times, duration, changes) | +| `webcmd trip transfer` | List Trip.com airport-transfer vehicles for a city + airport (type, seats, from-price) | diff --git a/clis/trip/attraction.js b/plugins/trip/attraction.js similarity index 100% rename from clis/trip/attraction.js rename to plugins/trip/attraction.js diff --git a/clis/trip/car.js b/plugins/trip/car.js similarity index 100% rename from clis/trip/car.js rename to plugins/trip/car.js diff --git a/clis/trip/deals.js b/plugins/trip/deals.js similarity index 100% rename from clis/trip/deals.js rename to plugins/trip/deals.js diff --git a/clis/trip/flight-round.js b/plugins/trip/flight-round.js similarity index 100% rename from clis/trip/flight-round.js rename to plugins/trip/flight-round.js diff --git a/clis/trip/flight.js b/plugins/trip/flight.js similarity index 100% rename from clis/trip/flight.js rename to plugins/trip/flight.js diff --git a/clis/trip/hotel-search.js b/plugins/trip/hotel-search.js similarity index 100% rename from clis/trip/hotel-search.js rename to plugins/trip/hotel-search.js diff --git a/clis/trip/hotel.js b/plugins/trip/hotel.js similarity index 100% rename from clis/trip/hotel.js rename to plugins/trip/hotel.js diff --git a/clis/trip/package.js b/plugins/trip/package.js similarity index 100% rename from clis/trip/package.js rename to plugins/trip/package.js diff --git a/plugins/trip/package.json b/plugins/trip/package.json new file mode 100644 index 00000000..47834d8f --- /dev/null +++ b/plugins/trip/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-trip", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for trip", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/trip/search.js b/plugins/trip/search.js similarity index 100% rename from clis/trip/search.js rename to plugins/trip/search.js diff --git a/clis/trip/trip.test.js b/plugins/trip/test/trip.test.js similarity index 99% rename from clis/trip/trip.test.js rename to plugins/trip/test/trip.test.js index 0c99076c..532b80d4 100644 --- a/clis/trip/trip.test.js +++ b/plugins/trip/test/trip.test.js @@ -1,18 +1,18 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { JSDOM } from 'jsdom'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './flight.js'; -import './flight-round.js'; -import './hotel-search.js'; -import './hotel.js'; -import './attraction.js'; -import './train.js'; -import './car.js'; -import './transfer.js'; -import './tour.js'; -import './search.js'; -import './package.js'; -import './deals.js'; +import '../flight.js'; +import '../flight-round.js'; +import '../hotel-search.js'; +import '../hotel.js'; +import '../attraction.js'; +import '../train.js'; +import '../car.js'; +import '../transfer.js'; +import '../tour.js'; +import '../search.js'; +import '../package.js'; +import '../deals.js'; import { WAIT_FOR_ATTRACTIONS_JS, WAIT_FOR_CARS_JS, @@ -50,7 +50,7 @@ import { parseKeyword, parseListLimit, resolvePackageCity, -} from './utils.js'; +} from '../utils.js'; function createPageMock(evaluateResults) { const evaluate = vi.fn(); diff --git a/clis/trip/tour.js b/plugins/trip/tour.js similarity index 100% rename from clis/trip/tour.js rename to plugins/trip/tour.js diff --git a/clis/trip/train.js b/plugins/trip/train.js similarity index 100% rename from clis/trip/train.js rename to plugins/trip/train.js diff --git a/clis/trip/transfer.js b/plugins/trip/transfer.js similarity index 100% rename from clis/trip/transfer.js rename to plugins/trip/transfer.js diff --git a/clis/trip/utils.js b/plugins/trip/utils.js similarity index 100% rename from clis/trip/utils.js rename to plugins/trip/utils.js diff --git a/plugins/trip/webcmd-plugin.json b/plugins/trip/webcmd-plugin.json new file mode 100644 index 00000000..cd733165 --- /dev/null +++ b/plugins/trip/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "trip", + "version": "0.1.0", + "description": "Webcmd commands for trip", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/twitter/README.md b/plugins/twitter/README.md new file mode 100644 index 00000000..77905a37 --- /dev/null +++ b/plugins/twitter/README.md @@ -0,0 +1,58 @@ +# webcmd-plugin-twitter + +Webcmd commands for twitter. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/twitter +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd twitter accept` | Auto-accept DM requests containing specific keywords | +| `webcmd twitter article` | Fetch a Twitter Article (long-form content) and export as Markdown | +| `webcmd twitter block` | Block a Twitter user | +| `webcmd twitter bookmark` | Bookmark a tweet | +| `webcmd twitter bookmark-folder` | Read the tweets inside a single Twitter/X bookmark folder. Get the folder id from `webcmd twitter bookmark-folders`. | +| `webcmd twitter bookmark-folders` | List your Twitter/X bookmark folders (the user-created collections under Bookmarks). Returns folder id, name, item count, and created_at. | +| `webcmd twitter bookmarks` | Fetch your Twitter/X bookmarks (the logged-in user's saved tweets, newest first) | +| `webcmd twitter delete` | Delete a specific tweet by URL | +| `webcmd twitter device-follow` | Read the /i/timeline device-follow notification stream (tweets aggregated under a bell-icon "new posts from @userA and N others" notification) | +| `webcmd twitter download` | Download Twitter/X media (images and videos). Provide either to fetch every media item from their profile via the GraphQL UserMedia endpoint with cursor pagination, or --tweet-url to download a single tweet. | +| `webcmd twitter follow` | Follow a Twitter user | +| `webcmd twitter follow-batch` | Follow multiple Twitter/X users from a comma-separated username list | +| `webcmd twitter followers` | Get accounts following a Twitter/X user (defaults to the logged-in user when no user is given) | +| `webcmd twitter following` | Get accounts a Twitter/X user is following (defaults to the logged-in user when no user is given) | +| `webcmd twitter hide-reply` | Hide a reply on your tweet (useful for hiding bot/spam replies) | +| `webcmd twitter like` | Like a specific tweet | +| `webcmd twitter likes` | Fetch liked tweets of a Twitter user (defaults to the logged-in user when no username is given) | +| `webcmd twitter list-add` | Add a user to a Twitter/X list you own (no-op if already a member) | +| `webcmd twitter list-add-batch` | Add multiple users to a Twitter/X list you own from a comma-separated username list | +| `webcmd twitter list-create` | Create a new Twitter/X list (returns the new list id) | +| `webcmd twitter list-delete` | Delete a Twitter/X list you own after explicit confirmation | +| `webcmd twitter list-remove` | Remove a user from a Twitter/X list you own (toggles via UI; no-op if not currently a member) | +| `webcmd twitter list-remove-batch` | Remove multiple users from a Twitter/X list you own from a comma-separated username list | +| `webcmd twitter list-tweets` | Fetch tweets from a Twitter/X list timeline | +| `webcmd twitter lists` | Get Twitter/X lists for the logged-in user (owned + subscribed) | +| `webcmd twitter login` | Open twitter login | +| `webcmd twitter notifications` | Get your Twitter/X notifications (the logged-in user's likes/replies/follows feed, newest first) | +| `webcmd twitter post` | Post a new tweet/thread | +| `webcmd twitter profile` | Fetch a Twitter user profile — bio, stats, etc. (defaults to the logged-in user when no username is given) | +| `webcmd twitter quote` | Quote-tweet a specific tweet with your own text, optionally with a local or remote image | +| `webcmd twitter reply` | Reply to a specific tweet, optionally with a local or remote image | +| `webcmd twitter reply-dm` | Send a message to recent DM conversations | +| `webcmd twitter retweet` | Retweet a specific tweet | +| `webcmd twitter search` | Search Twitter/X for tweets, with optional --from / --has / --exclude / --product filters mapped to X's search operators | +| `webcmd twitter thread` | Get a tweet thread (original + all replies) | +| `webcmd twitter timeline` | Fetch the logged-in user's home timeline (for-you algorithmic feed by default; pass --type following for the chronological feed of accounts you follow) | +| `webcmd twitter trending` | Twitter/X trending topics | +| `webcmd twitter tweets` | Fetch a Twitter user's most recent tweets (chronological, excludes pinned; defaults to the logged-in user when no username is given) | +| `webcmd twitter unblock` | Unblock a Twitter user | +| `webcmd twitter unbookmark` | Remove a tweet from bookmarks | +| `webcmd twitter unfollow` | Unfollow a Twitter user | +| `webcmd twitter unlike` | Remove a like from a specific tweet | +| `webcmd twitter unretweet` | Undo a retweet on a specific tweet | +| `webcmd twitter whoami` | Show the current logged-in twitter account | diff --git a/clis/twitter/accept.js b/plugins/twitter/accept.js similarity index 100% rename from clis/twitter/accept.js rename to plugins/twitter/accept.js diff --git a/clis/twitter/article.js b/plugins/twitter/article.js similarity index 100% rename from clis/twitter/article.js rename to plugins/twitter/article.js diff --git a/clis/twitter/auth.js b/plugins/twitter/auth.js similarity index 94% rename from clis/twitter/auth.js rename to plugins/twitter/auth.js index c8e4aff3..f0489f91 100644 --- a/clis/twitter/auth.js +++ b/plugins/twitter/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; import { normalizeTwitterScreenName, unwrapBrowserResult } from './shared.js'; async function hasTwitterSessionCookies(page) { diff --git a/clis/twitter/block.js b/plugins/twitter/block.js similarity index 100% rename from clis/twitter/block.js rename to plugins/twitter/block.js diff --git a/clis/twitter/bookmark-folder.js b/plugins/twitter/bookmark-folder.js similarity index 100% rename from clis/twitter/bookmark-folder.js rename to plugins/twitter/bookmark-folder.js diff --git a/clis/twitter/bookmark-folders.js b/plugins/twitter/bookmark-folders.js similarity index 100% rename from clis/twitter/bookmark-folders.js rename to plugins/twitter/bookmark-folders.js diff --git a/clis/twitter/bookmark.js b/plugins/twitter/bookmark.js similarity index 100% rename from clis/twitter/bookmark.js rename to plugins/twitter/bookmark.js diff --git a/clis/twitter/bookmarks.js b/plugins/twitter/bookmarks.js similarity index 100% rename from clis/twitter/bookmarks.js rename to plugins/twitter/bookmarks.js diff --git a/clis/twitter/delete.js b/plugins/twitter/delete.js similarity index 100% rename from clis/twitter/delete.js rename to plugins/twitter/delete.js diff --git a/clis/twitter/device-follow.js b/plugins/twitter/device-follow.js similarity index 100% rename from clis/twitter/device-follow.js rename to plugins/twitter/device-follow.js diff --git a/clis/twitter/download.js b/plugins/twitter/download.js similarity index 100% rename from clis/twitter/download.js rename to plugins/twitter/download.js diff --git a/clis/twitter/follow-batch.js b/plugins/twitter/follow-batch.js similarity index 100% rename from clis/twitter/follow-batch.js rename to plugins/twitter/follow-batch.js diff --git a/clis/twitter/follow.js b/plugins/twitter/follow.js similarity index 100% rename from clis/twitter/follow.js rename to plugins/twitter/follow.js diff --git a/clis/twitter/followers.js b/plugins/twitter/followers.js similarity index 100% rename from clis/twitter/followers.js rename to plugins/twitter/followers.js diff --git a/clis/twitter/following.js b/plugins/twitter/following.js similarity index 100% rename from clis/twitter/following.js rename to plugins/twitter/following.js diff --git a/clis/twitter/hide-reply.js b/plugins/twitter/hide-reply.js similarity index 100% rename from clis/twitter/hide-reply.js rename to plugins/twitter/hide-reply.js diff --git a/clis/twitter/like.js b/plugins/twitter/like.js similarity index 100% rename from clis/twitter/like.js rename to plugins/twitter/like.js diff --git a/clis/twitter/likes.js b/plugins/twitter/likes.js similarity index 100% rename from clis/twitter/likes.js rename to plugins/twitter/likes.js diff --git a/clis/twitter/list-add-batch.js b/plugins/twitter/list-add-batch.js similarity index 100% rename from clis/twitter/list-add-batch.js rename to plugins/twitter/list-add-batch.js diff --git a/clis/twitter/list-add-core.js b/plugins/twitter/list-add-core.js similarity index 99% rename from clis/twitter/list-add-core.js rename to plugins/twitter/list-add-core.js index 5c8b5425..476185eb 100644 --- a/clis/twitter/list-add-core.js +++ b/plugins/twitter/list-add-core.js @@ -1,6 +1,6 @@ import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { resolveTwitterQueryId, unwrapBrowserResult } from './shared.js'; -import { parseListsManagement } from './lists.js'; +import { parseListsManagement } from './lists-parser.js'; import { TWITTER_BEARER_TOKEN } from './utils.js'; const USER_BY_SCREEN_NAME_QUERY_ID = 'IGgvgiOx4QZndDHuD3x9TQ'; diff --git a/clis/twitter/list-add.js b/plugins/twitter/list-add.js similarity index 100% rename from clis/twitter/list-add.js rename to plugins/twitter/list-add.js diff --git a/clis/twitter/list-batch-utils.js b/plugins/twitter/list-batch-utils.js similarity index 100% rename from clis/twitter/list-batch-utils.js rename to plugins/twitter/list-batch-utils.js diff --git a/clis/twitter/list-create.js b/plugins/twitter/list-create.js similarity index 100% rename from clis/twitter/list-create.js rename to plugins/twitter/list-create.js diff --git a/clis/twitter/list-delete.js b/plugins/twitter/list-delete.js similarity index 99% rename from clis/twitter/list-delete.js rename to plugins/twitter/list-delete.js index 90273e3f..050cac28 100644 --- a/clis/twitter/list-delete.js +++ b/plugins/twitter/list-delete.js @@ -1,7 +1,7 @@ import { cli, Strategy } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { resolveTwitterQueryId, unwrapBrowserResult } from './shared.js'; -import { parseListsManagement } from './lists.js'; +import { parseListsManagement } from './lists-parser.js'; import { TWITTER_BEARER_TOKEN } from './utils.js'; const LISTS_MANAGEMENT_QUERY_ID = '78UbkyXwXBD98IgUWXOy9g'; diff --git a/clis/twitter/list-remove-batch.js b/plugins/twitter/list-remove-batch.js similarity index 100% rename from clis/twitter/list-remove-batch.js rename to plugins/twitter/list-remove-batch.js diff --git a/clis/twitter/list-remove-core.js b/plugins/twitter/list-remove-core.js similarity index 99% rename from clis/twitter/list-remove-core.js rename to plugins/twitter/list-remove-core.js index 5b5edb94..3874e530 100644 --- a/clis/twitter/list-remove-core.js +++ b/plugins/twitter/list-remove-core.js @@ -1,6 +1,6 @@ import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { resolveTwitterQueryId, unwrapBrowserResult } from './shared.js'; -import { getListsManagementInstructions, parseListsManagement } from './lists.js'; +import { getListsManagementInstructions, parseListsManagement } from './lists-parser.js'; import { TWITTER_BEARER_TOKEN } from './utils.js'; const USER_BY_SCREEN_NAME_QUERY_ID = 'IGgvgiOx4QZndDHuD3x9TQ'; diff --git a/clis/twitter/list-remove.js b/plugins/twitter/list-remove.js similarity index 100% rename from clis/twitter/list-remove.js rename to plugins/twitter/list-remove.js diff --git a/clis/twitter/list-tweets.js b/plugins/twitter/list-tweets.js similarity index 100% rename from clis/twitter/list-tweets.js rename to plugins/twitter/list-tweets.js diff --git a/plugins/twitter/lists-parser.js b/plugins/twitter/lists-parser.js new file mode 100644 index 00000000..e96f6c99 --- /dev/null +++ b/plugins/twitter/lists-parser.js @@ -0,0 +1,54 @@ +export function extractListEntry(entry, seen) { + const list = entry?.content?.itemContent?.list + || entry?.content?.list + || entry?.item?.itemContent?.list; + if (!list) return null; + const id = list.id_str || list.id || ''; + if (!id || seen.has(id)) return null; + seen.add(id); + const mode = typeof list.mode === 'string' && /private/i.test(list.mode) ? 'private' : 'public'; + return { + id: String(id), + name: list.name || '', + members: String(list.member_count ?? 0), + followers: String(list.subscriber_count ?? 0), + mode, + }; +} + +const OWNED_SUBSCRIBED_ENTRY_PREFIX = 'owned-subscribed-list-module-'; + +export function isOwnedSubscribedEntry(entry) { + return typeof entry?.entryId === 'string' + && entry.entryId.startsWith(OWNED_SUBSCRIBED_ENTRY_PREFIX); +} + +export function getListsManagementInstructions(data) { + const instructions = data?.data?.viewer?.list_management_timeline?.timeline?.instructions + || data?.data?.viewer_v2?.user_results?.result?.list_management_timeline?.timeline?.instructions + || data?.data?.list_management_timeline?.timeline?.instructions + || data?.data?.data?.viewer?.list_management_timeline?.timeline?.instructions + || data?.data?.data?.viewer_v2?.user_results?.result?.list_management_timeline?.timeline?.instructions + || data?.data?.data?.list_management_timeline?.timeline?.instructions; + return Array.isArray(instructions) ? instructions : null; +} + +export function parseListsManagement(data, seen) { + const lists = []; + const instructions = getListsManagementInstructions(data) || []; + for (const inst of instructions) { + for (const entry of inst.entries || []) { + if (!isOwnedSubscribedEntry(entry)) continue; + const direct = extractListEntry(entry, seen); + if (direct) { + lists.push(direct); + continue; + } + for (const item of entry?.content?.items || []) { + const nested = extractListEntry(item, seen); + if (nested) lists.push(nested); + } + } + } + return lists; +} diff --git a/clis/twitter/lists.js b/plugins/twitter/lists.js similarity index 73% rename from clis/twitter/lists.js rename to plugins/twitter/lists.js index f4dbbcca..ce947357 100644 --- a/clis/twitter/lists.js +++ b/plugins/twitter/lists.js @@ -2,6 +2,9 @@ import { cli, Strategy } from '@agentrhq/webcmd/registry'; import { AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; import { TWITTER_BEARER_TOKEN } from './utils.js'; import { describeTwitterApiError } from './shared.js'; +import { getListsManagementInstructions, parseListsManagement } from './lists-parser.js'; + +export { extractListEntry, isOwnedSubscribedEntry, parseListsManagement } from './lists-parser.js'; const LISTS_QUERY_ID = '78UbkyXwXBD98IgUWXOy9g'; const OPERATION_NAME = 'ListsManagementPageTimeline'; @@ -45,67 +48,12 @@ function buildUrl(queryId) { + `?features=${encodeURIComponent(JSON.stringify(FEATURES))}`; } -export function extractListEntry(entry, seen) { - const list = entry?.content?.itemContent?.list - || entry?.content?.list - || entry?.item?.itemContent?.list; - if (!list) return null; - const id = list.id_str || list.id || ''; - if (!id || seen.has(id)) return null; - seen.add(id); - const mode = typeof list.mode === 'string' && /private/i.test(list.mode) ? 'private' : 'public'; - return { - id: String(id), - name: list.name || '', - members: String(list.member_count ?? 0), - followers: String(list.subscriber_count ?? 0), - mode, - }; -} - // X localized text ListsManagementPageTimeline put //lists all page section // into one TimelineAddEntries instruction localized text,by entry.entryId prefix distinction: // - `owned-subscribed-list-module-*` → user owned + subscribed list(should keep) // - `list-to-follow-module-*` → "Discover new Lists" algorithmic recommendations(should remove) // - `cursor-*` → pagination cursor(none list data) // legacy parser ignore entryId always drill down,caused recommendations list treated as user-created/subscriptions leaked. -const OWNED_SUBSCRIBED_ENTRY_PREFIX = 'owned-subscribed-list-module-'; - -export function isOwnedSubscribedEntry(entry) { - return typeof entry?.entryId === 'string' - && entry.entryId.startsWith(OWNED_SUBSCRIBED_ENTRY_PREFIX); -} - -export function getListsManagementInstructions(data) { - const instructions = data?.data?.viewer?.list_management_timeline?.timeline?.instructions - || data?.data?.viewer_v2?.user_results?.result?.list_management_timeline?.timeline?.instructions - || data?.data?.list_management_timeline?.timeline?.instructions - || data?.data?.data?.viewer?.list_management_timeline?.timeline?.instructions - || data?.data?.data?.viewer_v2?.user_results?.result?.list_management_timeline?.timeline?.instructions - || data?.data?.data?.list_management_timeline?.timeline?.instructions; - return Array.isArray(instructions) ? instructions : null; -} - -export function parseListsManagement(data, seen) { - const lists = []; - const instructions = getListsManagementInstructions(data) || []; - for (const inst of instructions) { - for (const entry of inst.entries || []) { - if (!isOwnedSubscribedEntry(entry)) continue; - const direct = extractListEntry(entry, seen); - if (direct) { - lists.push(direct); - continue; - } - for (const item of entry?.content?.items || []) { - const nested = extractListEntry(item, seen); - if (nested) lists.push(nested); - } - } - } - return lists; -} - export const command = cli({ site: 'twitter', name: 'lists', diff --git a/clis/twitter/notifications.js b/plugins/twitter/notifications.js similarity index 100% rename from clis/twitter/notifications.js rename to plugins/twitter/notifications.js diff --git a/plugins/twitter/package.json b/plugins/twitter/package.json new file mode 100644 index 00000000..0472e1bb --- /dev/null +++ b/plugins/twitter/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-twitter", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for twitter", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/twitter/post.js b/plugins/twitter/post.js similarity index 100% rename from clis/twitter/post.js rename to plugins/twitter/post.js diff --git a/clis/twitter/profile.js b/plugins/twitter/profile.js similarity index 100% rename from clis/twitter/profile.js rename to plugins/twitter/profile.js diff --git a/clis/twitter/quote.js b/plugins/twitter/quote.js similarity index 100% rename from clis/twitter/quote.js rename to plugins/twitter/quote.js diff --git a/clis/twitter/reply-dm.js b/plugins/twitter/reply-dm.js similarity index 100% rename from clis/twitter/reply-dm.js rename to plugins/twitter/reply-dm.js diff --git a/clis/twitter/reply.js b/plugins/twitter/reply.js similarity index 100% rename from clis/twitter/reply.js rename to plugins/twitter/reply.js diff --git a/clis/twitter/retweet.js b/plugins/twitter/retweet.js similarity index 100% rename from clis/twitter/retweet.js rename to plugins/twitter/retweet.js diff --git a/clis/twitter/search.js b/plugins/twitter/search.js similarity index 100% rename from clis/twitter/search.js rename to plugins/twitter/search.js diff --git a/clis/twitter/shared.js b/plugins/twitter/shared.js similarity index 100% rename from clis/twitter/shared.js rename to plugins/twitter/shared.js diff --git a/clis/twitter/article-evaluate.test.js b/plugins/twitter/test/article-evaluate.test.js similarity index 93% rename from clis/twitter/article-evaluate.test.js rename to plugins/twitter/test/article-evaluate.test.js index 95978622..558a5fc5 100644 --- a/clis/twitter/article-evaluate.test.js +++ b/plugins/twitter/test/article-evaluate.test.js @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { createPageMock } from '../test-utils.js'; -import './article.js'; +import { createPageMock } from './page-mock.js'; +import '../article.js'; describe('twitter article evaluated arguments', () => { it('serializes tweet-id before embedding it in page.evaluate', async () => { diff --git a/clis/twitter/article.test.js b/plugins/twitter/test/article.test.js similarity index 99% rename from clis/twitter/article.test.js rename to plugins/twitter/test/article.test.js index 6a756c12..75254a2d 100644 --- a/clis/twitter/article.test.js +++ b/plugins/twitter/test/article.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './article.js'; +import '../article.js'; const TWEET_ID = '1234567890'; diff --git a/clis/twitter/bookmark-folder.test.js b/plugins/twitter/test/bookmark-folder.test.js similarity index 99% rename from clis/twitter/bookmark-folder.test.js rename to plugins/twitter/test/bookmark-folder.test.js index 2f2b5132..f47d8224 100644 --- a/clis/twitter/bookmark-folder.test.js +++ b/plugins/twitter/test/bookmark-folder.test.js @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ } from './bookmark-folder.js'; +import { __test__ } from '../bookmark-folder.js'; const { parseBookmarkFolderTimeline, extractFolderTweet, buildFolderTimelineUrl, FOLDER_ID_PATTERN } = __test__; diff --git a/clis/twitter/bookmark-folders.test.js b/plugins/twitter/test/bookmark-folders.test.js similarity index 99% rename from clis/twitter/bookmark-folders.test.js rename to plugins/twitter/test/bookmark-folders.test.js index ff6179bc..e04c35c7 100644 --- a/clis/twitter/bookmark-folders.test.js +++ b/plugins/twitter/test/bookmark-folders.test.js @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ } from './bookmark-folders.js'; +import { __test__ } from '../bookmark-folders.js'; const { parseBookmarkFolders, buildUrl } = __test__; diff --git a/clis/twitter/bookmark.test.js b/plugins/twitter/test/bookmark.test.js similarity index 97% rename from clis/twitter/bookmark.test.js rename to plugins/twitter/test/bookmark.test.js index de1276a1..5df719be 100644 --- a/clis/twitter/bookmark.test.js +++ b/plugins/twitter/test/bookmark.test.js @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './bookmark.js'; -import { createPageMock } from '../test-utils.js'; +import '../bookmark.js'; +import { createPageMock } from './page-mock.js'; describe('twitter bookmark command', () => { it('navigates to the tweet URL and reports success when the bookmark script confirms', async () => { diff --git a/clis/twitter/bookmarks.test.js b/plugins/twitter/test/bookmarks.test.js similarity index 99% rename from clis/twitter/bookmarks.test.js rename to plugins/twitter/test/bookmarks.test.js index 57c82ea5..29e500e3 100644 --- a/clis/twitter/bookmarks.test.js +++ b/plugins/twitter/test/bookmarks.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { __test__ } from './bookmarks.js'; +import { __test__ } from '../bookmarks.js'; const { parseBookmarks, extractBookmarkTweet } = __test__; diff --git a/clis/twitter/delete.test.js b/plugins/twitter/test/delete.test.js similarity index 99% rename from clis/twitter/delete.test.js rename to plugins/twitter/test/delete.test.js index 74a0853f..5277c51a 100644 --- a/clis/twitter/delete.test.js +++ b/plugins/twitter/test/delete.test.js @@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest'; import { JSDOM } from 'jsdom'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ } from './delete.js'; +import { __test__ } from '../delete.js'; describe('twitter delete command', () => { it('targets the matched tweet article instead of the first More button on the page', async () => { const cmd = getRegistry().get('twitter/delete'); diff --git a/clis/twitter/device-follow.test.js b/plugins/twitter/test/device-follow.test.js similarity index 98% rename from clis/twitter/device-follow.test.js rename to plugins/twitter/test/device-follow.test.js index 5fc91603..5435d73c 100644 --- a/clis/twitter/device-follow.test.js +++ b/plugins/twitter/test/device-follow.test.js @@ -1,9 +1,9 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './device-follow.js'; +import '../device-follow.js'; -const { buildDeviceFollowUrl, extractEntries, joinEntryToTweet, shapeRow, parseDeviceFollow, parseLimit } = await import('./device-follow.js').then((m) => m.__test__); +const { buildDeviceFollowUrl, extractEntries, joinEntryToTweet, shapeRow, parseDeviceFollow, parseLimit } = await import('../device-follow.js').then((m) => m.__test__); function tweet(id, userId, overrides = {}) { return { diff --git a/clis/twitter/download.test.js b/plugins/twitter/test/download.test.js similarity index 99% rename from clis/twitter/download.test.js rename to plugins/twitter/test/download.test.js index 6c6a8cc4..c3ba1a70 100644 --- a/clis/twitter/download.test.js +++ b/plugins/twitter/test/download.test.js @@ -11,7 +11,7 @@ vi.mock('@agentrhq/webcmd/download', () => ({ })); import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { __test__ } from './download.js'; +import { __test__ } from '../download.js'; const { buildUserMediaUrl, diff --git a/clis/twitter/follow-batch.test.js b/plugins/twitter/test/follow-batch.test.js similarity index 98% rename from clis/twitter/follow-batch.test.js rename to plugins/twitter/test/follow-batch.test.js index 0ff3e2a7..7c0e3316 100644 --- a/clis/twitter/follow-batch.test.js +++ b/plugins/twitter/test/follow-batch.test.js @@ -1,8 +1,8 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError } from '@agentrhq/webcmd/errors'; -import { followOne, parseBatchUsernames, parseDelayMs } from './follow-batch.js'; -import './follow-batch.js'; +import { followOne, parseBatchUsernames, parseDelayMs } from '../follow-batch.js'; +import '../follow-batch.js'; describe('twitter follow-batch command', () => { it('registers with the expected shape', () => { diff --git a/clis/twitter/followers.test.js b/plugins/twitter/test/followers.test.js similarity index 98% rename from clis/twitter/followers.test.js rename to plugins/twitter/test/followers.test.js index bac91168..90718c22 100644 --- a/clis/twitter/followers.test.js +++ b/plugins/twitter/test/followers.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { __test__ } from './followers.js'; +import { __test__ } from '../followers.js'; describe('twitter followers command', () => { it('normalizes exact profile handles and rejects route-like hrefs', () => { diff --git a/clis/twitter/following.test.js b/plugins/twitter/test/following.test.js similarity index 99% rename from clis/twitter/following.test.js rename to plugins/twitter/test/following.test.js index 7f136f61..b515640f 100644 --- a/clis/twitter/following.test.js +++ b/plugins/twitter/test/following.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { __test__ } from './following.js'; +import { __test__ } from '../following.js'; describe('twitter following helpers', () => { it('falls back when queryId contains unsafe characters', () => { diff --git a/clis/twitter/hide-reply.test.js b/plugins/twitter/test/hide-reply.test.js similarity index 97% rename from clis/twitter/hide-reply.test.js rename to plugins/twitter/test/hide-reply.test.js index 44a99920..aefeac0f 100644 --- a/clis/twitter/hide-reply.test.js +++ b/plugins/twitter/test/hide-reply.test.js @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './hide-reply.js'; -import { createPageMock } from '../test-utils.js'; +import '../hide-reply.js'; +import { createPageMock } from './page-mock.js'; describe('twitter hide-reply command', () => { it('navigates to the reply URL and reports success when the hide-reply script confirms', async () => { diff --git a/clis/twitter/like.test.js b/plugins/twitter/test/like.test.js similarity index 98% rename from clis/twitter/like.test.js rename to plugins/twitter/test/like.test.js index cd5a3621..15bd68e0 100644 --- a/clis/twitter/like.test.js +++ b/plugins/twitter/test/like.test.js @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './like.js'; -import { createPageMock } from '../test-utils.js'; +import '../like.js'; +import { createPageMock } from './page-mock.js'; describe('twitter like command', () => { it('navigates to the tweet URL and reports success when the like script confirms', async () => { diff --git a/clis/twitter/likes.test.js b/plugins/twitter/test/likes.test.js similarity index 99% rename from clis/twitter/likes.test.js rename to plugins/twitter/test/likes.test.js index 01c9b429..d7385f7c 100644 --- a/clis/twitter/likes.test.js +++ b/plugins/twitter/test/likes.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { __test__ } from './likes.js'; +import { __test__ } from '../likes.js'; function likesPayload() { return { diff --git a/clis/twitter/list-add.test.js b/plugins/twitter/test/list-add.test.js similarity index 98% rename from clis/twitter/list-add.test.js rename to plugins/twitter/test/list-add.test.js index ea892e93..2127f2e7 100644 --- a/clis/twitter/list-add.test.js +++ b/plugins/twitter/test/list-add.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { buildListAddMemberRow } from './list-add.js'; +import { buildListAddMemberRow } from '../list-add.js'; describe('twitter list-add registration', () => { it('registers the list-add command with the expected shape', () => { diff --git a/clis/twitter/list-batch.test.js b/plugins/twitter/test/list-batch.test.js similarity index 97% rename from clis/twitter/list-batch.test.js rename to plugins/twitter/test/list-batch.test.js index 55401b2b..c77b9276 100644 --- a/clis/twitter/list-batch.test.js +++ b/plugins/twitter/test/list-batch.test.js @@ -1,13 +1,13 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './list-add-batch.js'; -import './list-remove-batch.js'; +import '../list-add-batch.js'; +import '../list-remove-batch.js'; import { parseBatchIntervalSeconds, parseCommaSeparatedUsernames, runListBatch, -} from './list-batch-utils.js'; +} from '../list-batch-utils.js'; describe('twitter list batch utilities', () => { it('parses comma-separated usernames, strips @, and dedupes case-insensitively', () => { diff --git a/clis/twitter/list-create.test.js b/plugins/twitter/test/list-create.test.js similarity index 98% rename from clis/twitter/list-create.test.js rename to plugins/twitter/test/list-create.test.js index 4fa2d0d2..9e3b40aa 100644 --- a/clis/twitter/list-create.test.js +++ b/plugins/twitter/test/list-create.test.js @@ -1,8 +1,8 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { buildListCreateRow, parseListCreateArgs } from './list-create.js'; -import './list-create.js'; +import { buildListCreateRow, parseListCreateArgs } from '../list-create.js'; +import '../list-create.js'; function createPayload(overrides = {}) { return { diff --git a/clis/twitter/list-delete.test.js b/plugins/twitter/test/list-delete.test.js similarity index 98% rename from clis/twitter/list-delete.test.js rename to plugins/twitter/test/list-delete.test.js index fdc1bf8d..4ed680be 100644 --- a/clis/twitter/list-delete.test.js +++ b/plugins/twitter/test/list-delete.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError } from '@agentrhq/webcmd/errors'; -import { buildListDeleteRow } from './list-delete.js'; +import { buildListDeleteRow } from '../list-delete.js'; describe('twitter list-delete registration', () => { it('registers the list-delete command with explicit confirmation', () => { diff --git a/clis/twitter/list-remove.test.js b/plugins/twitter/test/list-remove.test.js similarity index 99% rename from clis/twitter/list-remove.test.js rename to plugins/twitter/test/list-remove.test.js index 5f500188..d3a754dd 100644 --- a/clis/twitter/list-remove.test.js +++ b/plugins/twitter/test/list-remove.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import './list-remove.js'; +import '../list-remove.js'; function buildListsPayload(listId = '123', memberCount = 10) { return { diff --git a/clis/twitter/list-tweets.test.js b/plugins/twitter/test/list-tweets.test.js similarity index 99% rename from clis/twitter/list-tweets.test.js rename to plugins/twitter/test/list-tweets.test.js index ac24ef55..baf6cc05 100644 --- a/clis/twitter/list-tweets.test.js +++ b/plugins/twitter/test/list-tweets.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { extractTimelineTweet, parseListTimeline } from './list-tweets.js'; +import { extractTimelineTweet, parseListTimeline } from '../list-tweets.js'; describe('twitter list-tweets parser', () => { it('extracts core tweet fields from a ListLatestTweetsTimeline result', () => { diff --git a/clis/twitter/lists.test.js b/plugins/twitter/test/lists.test.js similarity index 99% rename from clis/twitter/lists.test.js rename to plugins/twitter/test/lists.test.js index b121031a..4ab32849 100644 --- a/clis/twitter/lists.test.js +++ b/plugins/twitter/test/lists.test.js @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { extractListEntry, isOwnedSubscribedEntry, parseListsManagement } from './lists.js'; +import { extractListEntry, isOwnedSubscribedEntry, parseListsManagement } from '../lists.js'; describe('twitter lists parser', () => { it('extracts a list entry with full metadata', () => { diff --git a/plugins/twitter/test/page-mock.js b/plugins/twitter/test/page-mock.js new file mode 100644 index 00000000..86a0e98b --- /dev/null +++ b/plugins/twitter/test/page-mock.js @@ -0,0 +1,12 @@ +import { vi } from 'vitest'; + +export function createPageMock(evaluateResults = [], overrides = {}) { + const evaluate = vi.fn(); + for (const result of evaluateResults) evaluate.mockResolvedValueOnce(result); + return { + goto: vi.fn().mockResolvedValue(undefined), + evaluate, + wait: vi.fn().mockResolvedValue(undefined), + ...overrides, + }; +} diff --git a/clis/twitter/post.test.js b/plugins/twitter/test/post.test.js similarity index 99% rename from clis/twitter/post.test.js rename to plugins/twitter/test/post.test.js index ced22b3f..1cfedf3e 100644 --- a/clis/twitter/post.test.js +++ b/plugins/twitter/test/post.test.js @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './post.js'; +import '../post.js'; vi.mock('node:fs', async (importOriginal) => { const actual = await importOriginal(); diff --git a/clis/twitter/profile.test.js b/plugins/twitter/test/profile.test.js similarity index 99% rename from clis/twitter/profile.test.js rename to plugins/twitter/test/profile.test.js index 0d059fd0..9d2ec761 100644 --- a/clis/twitter/profile.test.js +++ b/plugins/twitter/test/profile.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import { __test__ } from './profile.js'; +import { __test__ } from '../profile.js'; describe('twitter profile command', () => { it('serializes the validated screen name before embedding it in page.evaluate', () => { diff --git a/clis/twitter/quote.test.js b/plugins/twitter/test/quote.test.js similarity index 98% rename from clis/twitter/quote.test.js rename to plugins/twitter/test/quote.test.js index 72872c6d..78e037bb 100644 --- a/clis/twitter/quote.test.js +++ b/plugins/twitter/test/quote.test.js @@ -4,9 +4,9 @@ import * as path from 'node:path'; import { describe, expect, it, vi } from 'vitest'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ } from './quote.js'; -import './quote.js'; -import { createPageMock } from '../test-utils.js'; +import { __test__ } from '../quote.js'; +import '../quote.js'; +import { createPageMock } from './page-mock.js'; describe('twitter quote helpers', () => { it('builds the quote composer URL with the source tweet attached as ?url=...', () => { diff --git a/clis/twitter/reply.test.js b/plugins/twitter/test/reply.test.js similarity index 98% rename from clis/twitter/reply.test.js rename to plugins/twitter/test/reply.test.js index d2db847d..1843cbf4 100644 --- a/clis/twitter/reply.test.js +++ b/plugins/twitter/test/reply.test.js @@ -5,9 +5,9 @@ import { JSDOM } from 'jsdom'; import { describe, expect, it, vi } from 'vitest'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ } from './reply.js'; -import { __test__ as utilsTest } from './utils.js'; -import { createPageMock } from '../test-utils.js'; +import { __test__ } from '../reply.js'; +import { __test__ as utilsTest } from '../utils.js'; +import { createPageMock } from './page-mock.js'; describe('twitter reply command', () => { it('uses the dedicated reply composer for text-only replies too', async () => { diff --git a/clis/twitter/retweet.test.js b/plugins/twitter/test/retweet.test.js similarity index 98% rename from clis/twitter/retweet.test.js rename to plugins/twitter/test/retweet.test.js index bbaaa848..038a1ad8 100644 --- a/clis/twitter/retweet.test.js +++ b/plugins/twitter/test/retweet.test.js @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './retweet.js'; -import { createPageMock } from '../test-utils.js'; +import '../retweet.js'; +import { createPageMock } from './page-mock.js'; describe('twitter retweet command', () => { it('clicks the retweet button then the confirm menu item and reports success', async () => { diff --git a/clis/twitter/search.test.js b/plugins/twitter/test/search.test.js similarity index 99% rename from clis/twitter/search.test.js rename to plugins/twitter/test/search.test.js index 77551980..d7c785d3 100644 --- a/clis/twitter/search.test.js +++ b/plugins/twitter/test/search.test.js @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ } from './search.js'; +import { __test__ } from '../search.js'; const { buildSearchQuery, resolveSearchFParam, resolveSearchProduct, buildSearchTimelineRequest, parseSearchTimeline, HAS_CHOICES, EXCLUDE_CHOICES, PRODUCT_CHOICES, EXCLUDE_TO_OPERATOR, PRODUCT_TO_F_PARAM, FROM_USER_PATTERN } = __test__; describe('twitter search command', () => { diff --git a/clis/twitter/shared.test.js b/plugins/twitter/test/shared.test.js similarity index 99% rename from clis/twitter/shared.test.js rename to plugins/twitter/test/shared.test.js index c8d8b704..575d4809 100644 --- a/clis/twitter/shared.test.js +++ b/plugins/twitter/test/shared.test.js @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { JSDOM } from 'jsdom'; -import { __test__ } from './shared.js'; +import { __test__ } from '../shared.js'; import { ArgumentError } from '@agentrhq/webcmd/errors'; const { diff --git a/clis/twitter/thread.test.js b/plugins/twitter/test/thread.test.js similarity index 98% rename from clis/twitter/thread.test.js rename to plugins/twitter/test/thread.test.js index fdeff1bf..9bae0c0c 100644 --- a/clis/twitter/thread.test.js +++ b/plugins/twitter/test/thread.test.js @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import { __test__ } from './thread.js'; +import { __test__ } from '../thread.js'; describe('twitter thread parser', () => { it('extracts author bio from tweet user entity', () => { diff --git a/clis/twitter/timeline.test.js b/plugins/twitter/test/timeline.test.js similarity index 99% rename from clis/twitter/timeline.test.js rename to plugins/twitter/test/timeline.test.js index e249ca9d..fbfcefef 100644 --- a/clis/twitter/timeline.test.js +++ b/plugins/twitter/test/timeline.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { __test__ } from './timeline.js'; +import { __test__ } from '../timeline.js'; describe('twitter timeline helpers', () => { it('builds for-you variables with withCommunity', () => { expect(__test__.buildTimelineVariables('for-you', 20)).toEqual({ diff --git a/clis/twitter/trending.test.js b/plugins/twitter/test/trending.test.js similarity index 96% rename from clis/twitter/trending.test.js rename to plugins/twitter/test/trending.test.js index ced61907..b17306fd 100644 --- a/clis/twitter/trending.test.js +++ b/plugins/twitter/test/trending.test.js @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './trending.js'; +import '../trending.js'; describe('twitter trending', () => { it('registers the trending command with rank/topic/category columns only', () => { diff --git a/clis/twitter/tweets.test.js b/plugins/twitter/test/tweets.test.js similarity index 99% rename from clis/twitter/tweets.test.js rename to plugins/twitter/test/tweets.test.js index b63d47d4..5e1e97f4 100644 --- a/clis/twitter/tweets.test.js +++ b/plugins/twitter/test/tweets.test.js @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError } from '@agentrhq/webcmd/errors'; -import { __test__ } from './tweets.js'; +import { __test__ } from '../tweets.js'; function makeTweetEntry(id, author = 'jakevin7') { return { diff --git a/clis/twitter/unbookmark.test.js b/plugins/twitter/test/unbookmark.test.js similarity index 97% rename from clis/twitter/unbookmark.test.js rename to plugins/twitter/test/unbookmark.test.js index e66ce402..aa7ecab5 100644 --- a/clis/twitter/unbookmark.test.js +++ b/plugins/twitter/test/unbookmark.test.js @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './unbookmark.js'; -import { createPageMock } from '../test-utils.js'; +import '../unbookmark.js'; +import { createPageMock } from './page-mock.js'; describe('twitter unbookmark command', () => { it('navigates to the tweet URL and reports success when the unbookmark script confirms', async () => { diff --git a/clis/twitter/unlike.test.js b/plugins/twitter/test/unlike.test.js similarity index 98% rename from clis/twitter/unlike.test.js rename to plugins/twitter/test/unlike.test.js index c53e03ef..58210557 100644 --- a/clis/twitter/unlike.test.js +++ b/plugins/twitter/test/unlike.test.js @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './unlike.js'; -import { createPageMock } from '../test-utils.js'; +import '../unlike.js'; +import { createPageMock } from './page-mock.js'; describe('twitter unlike command', () => { it('navigates to the tweet URL and reports success when the unlike script confirms', async () => { diff --git a/clis/twitter/unretweet.test.js b/plugins/twitter/test/unretweet.test.js similarity index 98% rename from clis/twitter/unretweet.test.js rename to plugins/twitter/test/unretweet.test.js index 107a07d7..8e3f7340 100644 --- a/clis/twitter/unretweet.test.js +++ b/plugins/twitter/test/unretweet.test.js @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest'; import { ArgumentError, CommandExecutionError } from '@agentrhq/webcmd/errors'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './unretweet.js'; -import { createPageMock } from '../test-utils.js'; +import '../unretweet.js'; +import { createPageMock } from './page-mock.js'; describe('twitter unretweet command', () => { it('clicks the unretweet button then the confirm menu item and reports success', async () => { diff --git a/clis/twitter/utils.test.js b/plugins/twitter/test/utils.test.js similarity index 99% rename from clis/twitter/utils.test.js rename to plugins/twitter/test/utils.test.js index ae6cf572..8980be22 100644 --- a/clis/twitter/utils.test.js +++ b/plugins/twitter/test/utils.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { __test__ } from './utils.js'; +import { __test__ } from '../utils.js'; const { computeEngagementScore, applyTopByEngagement, ENGAGEMENT_WEIGHTS } = __test__; diff --git a/clis/twitter/thread.js b/plugins/twitter/thread.js similarity index 100% rename from clis/twitter/thread.js rename to plugins/twitter/thread.js diff --git a/clis/twitter/timeline.js b/plugins/twitter/timeline.js similarity index 100% rename from clis/twitter/timeline.js rename to plugins/twitter/timeline.js diff --git a/clis/twitter/trending.js b/plugins/twitter/trending.js similarity index 100% rename from clis/twitter/trending.js rename to plugins/twitter/trending.js diff --git a/clis/twitter/tweets.js b/plugins/twitter/tweets.js similarity index 100% rename from clis/twitter/tweets.js rename to plugins/twitter/tweets.js diff --git a/clis/twitter/unblock.js b/plugins/twitter/unblock.js similarity index 100% rename from clis/twitter/unblock.js rename to plugins/twitter/unblock.js diff --git a/clis/twitter/unbookmark.js b/plugins/twitter/unbookmark.js similarity index 100% rename from clis/twitter/unbookmark.js rename to plugins/twitter/unbookmark.js diff --git a/clis/twitter/unfollow.js b/plugins/twitter/unfollow.js similarity index 100% rename from clis/twitter/unfollow.js rename to plugins/twitter/unfollow.js diff --git a/clis/twitter/unlike.js b/plugins/twitter/unlike.js similarity index 100% rename from clis/twitter/unlike.js rename to plugins/twitter/unlike.js diff --git a/clis/twitter/unretweet.js b/plugins/twitter/unretweet.js similarity index 100% rename from clis/twitter/unretweet.js rename to plugins/twitter/unretweet.js diff --git a/clis/twitter/utils.js b/plugins/twitter/utils.js similarity index 100% rename from clis/twitter/utils.js rename to plugins/twitter/utils.js diff --git a/plugins/twitter/webcmd-plugin.json b/plugins/twitter/webcmd-plugin.json new file mode 100644 index 00000000..ffd70a23 --- /dev/null +++ b/plugins/twitter/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "twitter", + "version": "0.1.0", + "description": "Webcmd commands for twitter", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/plugins/youtube/README.md b/plugins/youtube/README.md new file mode 100644 index 00000000..22a927df --- /dev/null +++ b/plugins/youtube/README.md @@ -0,0 +1,30 @@ +# webcmd-plugin-youtube + +Webcmd commands for youtube. + +## Install + +```bash +webcmd plugin install github:agentrhq/webcmd/plugins/youtube +``` + +## Commands + +| Command | Description | +| --- | --- | +| `webcmd youtube channel` | Get YouTube channel info and recent videos | +| `webcmd youtube comments` | Get YouTube video comments | +| `webcmd youtube feed` | Get YouTube homepage recommended videos | +| `webcmd youtube history` | Get YouTube watch history | +| `webcmd youtube like` | Like a YouTube video | +| `webcmd youtube login` | Open youtube login | +| `webcmd youtube playlist` | Get YouTube playlist info and video list | +| `webcmd youtube search` | Search YouTube videos | +| `webcmd youtube subscribe` | Subscribe to a YouTube channel | +| `webcmd youtube subscriptions` | List subscribed YouTube channels | +| `webcmd youtube transcript` | Get YouTube video transcript/subtitles | +| `webcmd youtube unlike` | Remove like from a YouTube video | +| `webcmd youtube unsubscribe` | Unsubscribe from a YouTube channel | +| `webcmd youtube video` | Get YouTube video metadata (title, views, description, etc.) | +| `webcmd youtube watch-later` | Get your YouTube Watch Later queue | +| `webcmd youtube whoami` | Show the current logged-in youtube account | diff --git a/clis/youtube/auth.js b/plugins/youtube/auth.js similarity index 96% rename from clis/youtube/auth.js rename to plugins/youtube/auth.js index 28f67e42..eebb3bba 100644 --- a/clis/youtube/auth.js +++ b/plugins/youtube/auth.js @@ -1,5 +1,5 @@ import { AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { registerSiteAuthCommands } from '../_shared/site-auth.js'; +import { registerSiteAuthCommands } from '@agentrhq/webcmd/plugin-runtime'; async function hasGoogleSessionCookie(page) { const cookies = await page.getCookies({ url: 'https://www.youtube.com' }); diff --git a/clis/youtube/channel.js b/plugins/youtube/channel.js similarity index 100% rename from clis/youtube/channel.js rename to plugins/youtube/channel.js diff --git a/clis/youtube/comments.js b/plugins/youtube/comments.js similarity index 100% rename from clis/youtube/comments.js rename to plugins/youtube/comments.js diff --git a/clis/youtube/feed.js b/plugins/youtube/feed.js similarity index 100% rename from clis/youtube/feed.js rename to plugins/youtube/feed.js diff --git a/clis/youtube/history.js b/plugins/youtube/history.js similarity index 100% rename from clis/youtube/history.js rename to plugins/youtube/history.js diff --git a/clis/youtube/like.js b/plugins/youtube/like.js similarity index 100% rename from clis/youtube/like.js rename to plugins/youtube/like.js diff --git a/plugins/youtube/package.json b/plugins/youtube/package.json new file mode 100644 index 00000000..1d369e72 --- /dev/null +++ b/plugins/youtube/package.json @@ -0,0 +1,9 @@ +{ + "name": "webcmd-plugin-youtube", + "version": "0.1.0", + "type": "module", + "description": "Webcmd commands for youtube", + "peerDependencies": { + "@agentrhq/webcmd": ">=0.6.0" + } +} diff --git a/clis/youtube/playlist.js b/plugins/youtube/playlist.js similarity index 100% rename from clis/youtube/playlist.js rename to plugins/youtube/playlist.js diff --git a/clis/youtube/search.js b/plugins/youtube/search.js similarity index 100% rename from clis/youtube/search.js rename to plugins/youtube/search.js diff --git a/clis/youtube/subscribe.js b/plugins/youtube/subscribe.js similarity index 100% rename from clis/youtube/subscribe.js rename to plugins/youtube/subscribe.js diff --git a/clis/youtube/subscriptions.js b/plugins/youtube/subscriptions.js similarity index 100% rename from clis/youtube/subscriptions.js rename to plugins/youtube/subscriptions.js diff --git a/clis/youtube/channel.test.js b/plugins/youtube/test/channel.test.js similarity index 99% rename from clis/youtube/channel.test.js rename to plugins/youtube/test/channel.test.js index 99c5c8a2..df74fb65 100644 --- a/clis/youtube/channel.test.js +++ b/plugins/youtube/test/channel.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { __test__ } from './channel.js'; +import { __test__ } from '../channel.js'; function tab(title, contents, selected = false) { return { diff --git a/clis/youtube/feed.test.js b/plugins/youtube/test/feed.test.js similarity index 99% rename from clis/youtube/feed.test.js rename to plugins/youtube/test/feed.test.js index 857e8d72..de9c5c8b 100644 --- a/clis/youtube/feed.test.js +++ b/plugins/youtube/test/feed.test.js @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { getRegistry } from '@agentrhq/webcmd/registry'; -import './feed.js'; +import '../feed.js'; function makePage({ initialData, continuationData, fetchImpl } = {}) { const fetchMock = fetchImpl || vi.fn().mockResolvedValue({ diff --git a/clis/youtube/transcript-group.test.js b/plugins/youtube/test/transcript-group.test.js similarity index 99% rename from clis/youtube/transcript-group.test.js rename to plugins/youtube/test/transcript-group.test.js index 511b8d30..7606181d 100644 --- a/clis/youtube/transcript-group.test.js +++ b/plugins/youtube/test/transcript-group.test.js @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { groupTranscriptSegments, formatGroupedTranscript } from './transcript-group.js'; +import { groupTranscriptSegments, formatGroupedTranscript } from '../transcript-group.js'; describe('groupTranscriptSegments', () => { it('groups segments by sentence boundaries', () => { const segments = [ diff --git a/clis/youtube/transcript.test.js b/plugins/youtube/test/transcript.test.js similarity index 99% rename from clis/youtube/transcript.test.js rename to plugins/youtube/test/transcript.test.js index 9600909b..35f5cf58 100644 --- a/clis/youtube/transcript.test.js +++ b/plugins/youtube/test/transcript.test.js @@ -4,10 +4,10 @@ import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { getRegistry } from '@agentrhq/webcmd/registry'; import { CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors'; -import './transcript.js'; +import '../transcript.js'; const __dirname = dirname(fileURLToPath(import.meta.url)); -const transcriptSource = readFileSync(resolve(__dirname, 'transcript.js'), 'utf8'); +const transcriptSource = readFileSync(resolve(__dirname, '../transcript.js'), 'utf8'); function createPageMock(captionUrl) { const page = { diff --git a/clis/youtube/utils.test.js b/plugins/youtube/test/utils.test.js similarity index 98% rename from clis/youtube/utils.test.js rename to plugins/youtube/test/utils.test.js index a9e6c261..e11c6060 100644 --- a/clis/youtube/utils.test.js +++ b/plugins/youtube/test/utils.test.js @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from 'vitest'; -import { extractJsonAssignmentFromHtml, extractSubscriptionChannel, prepareYoutubeApiPage, readYoutubeSapisid } from './utils.js'; +import { extractJsonAssignmentFromHtml, extractSubscriptionChannel, prepareYoutubeApiPage, readYoutubeSapisid } from '../utils.js'; describe('youtube utils', () => { it('extractJsonAssignmentFromHtml parses bootstrap objects with nested braces in strings', () => { const html = ` diff --git a/clis/youtube/video.test.js b/plugins/youtube/test/video.test.js similarity index 96% rename from clis/youtube/video.test.js rename to plugins/youtube/test/video.test.js index a8df9929..4b5834e6 100644 --- a/clis/youtube/video.test.js +++ b/plugins/youtube/test/video.test.js @@ -8,16 +8,16 @@ const { mockPrepare } = vi.hoisted(() => ({ mockPrepare: vi.fn(), })); -vi.mock('./utils.js', async (importOriginal) => ({ +vi.mock('../utils.js', async (importOriginal) => ({ ...(await importOriginal()), prepareYoutubeApiPage: mockPrepare, })); import { getRegistry } from '@agentrhq/webcmd/registry'; -import './video.js'; +import '../video.js'; const __dirname = dirname(fileURLToPath(import.meta.url)); -const videoSource = readFileSync(resolve(__dirname, 'video.js'), 'utf8'); +const videoSource = readFileSync(resolve(__dirname, '../video.js'), 'utf8'); describe('youtube video source contract', () => { it('extracts playability gate signals inside the watch bootstrap evaluate', () => { diff --git a/clis/youtube/transcript-group.js b/plugins/youtube/transcript-group.js similarity index 100% rename from clis/youtube/transcript-group.js rename to plugins/youtube/transcript-group.js diff --git a/clis/youtube/transcript.js b/plugins/youtube/transcript.js similarity index 100% rename from clis/youtube/transcript.js rename to plugins/youtube/transcript.js diff --git a/clis/youtube/unlike.js b/plugins/youtube/unlike.js similarity index 100% rename from clis/youtube/unlike.js rename to plugins/youtube/unlike.js diff --git a/clis/youtube/unsubscribe.js b/plugins/youtube/unsubscribe.js similarity index 100% rename from clis/youtube/unsubscribe.js rename to plugins/youtube/unsubscribe.js diff --git a/clis/youtube/utils.js b/plugins/youtube/utils.js similarity index 100% rename from clis/youtube/utils.js rename to plugins/youtube/utils.js diff --git a/clis/youtube/video.js b/plugins/youtube/video.js similarity index 100% rename from clis/youtube/video.js rename to plugins/youtube/video.js diff --git a/clis/youtube/watch-later.js b/plugins/youtube/watch-later.js similarity index 100% rename from clis/youtube/watch-later.js rename to plugins/youtube/watch-later.js diff --git a/plugins/youtube/webcmd-plugin.json b/plugins/youtube/webcmd-plugin.json new file mode 100644 index 00000000..da88fd9f --- /dev/null +++ b/plugins/youtube/webcmd-plugin.json @@ -0,0 +1,10 @@ +{ + "name": "youtube", + "version": "0.1.0", + "description": "Webcmd commands for youtube", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } +} diff --git a/scripts/silent-column-drop-baseline.json b/scripts/silent-column-drop-baseline.json index 45cce13e..258dfe8a 100644 --- a/scripts/silent-column-drop-baseline.json +++ b/scripts/silent-column-drop-baseline.json @@ -211,7 +211,7 @@ }, { "command": "instagram/note", - "file": "clis/instagram/note.js", + "file": "plugins/instagram/note.js", "missing": [ "stage", "text" @@ -219,7 +219,7 @@ }, { "command": "instagram/post", - "file": "clis/instagram/post.js", + "file": "plugins/instagram/post.js", "missing": [ "failed", "settled" @@ -227,14 +227,14 @@ }, { "command": "instagram/post", - "file": "clis/instagram/post.js", + "file": "plugins/instagram/post.js", "missing": [ "state" ] }, { "command": "instagram/reel", - "file": "clis/instagram/reel.js", + "file": "plugins/instagram/reel.js", "missing": [ "failed", "settled" @@ -242,7 +242,7 @@ }, { "command": "instagram/reel", - "file": "clis/instagram/reel.js", + "file": "plugins/instagram/reel.js", "missing": [ "state" ] @@ -324,7 +324,7 @@ }, { "command": "twitter/accept", - "file": "clis/twitter/accept.js", + "file": "plugins/twitter/accept.js", "missing": [ "href", "idx", @@ -333,21 +333,21 @@ }, { "command": "twitter/bookmarks", - "file": "clis/twitter/bookmarks.js", + "file": "plugins/twitter/bookmarks.js", "missing": [ "name" ] }, { "command": "twitter/list-tweets", - "file": "clis/twitter/list-tweets.js", + "file": "plugins/twitter/list-tweets.js", "missing": [ "name" ] }, { "command": "twitter/reply-dm", - "file": "clis/twitter/reply-dm.js", + "file": "plugins/twitter/reply-dm.js", "missing": [ "convId", "href", @@ -357,7 +357,7 @@ }, { "command": "twitter/thread", - "file": "clis/twitter/thread.js", + "file": "plugins/twitter/thread.js", "missing": [ "created_at", "in_reply_to" @@ -365,7 +365,7 @@ }, { "command": "twitter/tweets", - "file": "clis/twitter/tweets.js", + "file": "plugins/twitter/tweets.js", "missing": [ "name" ] @@ -432,7 +432,7 @@ }, { "command": "youtube/playlist", - "file": "clis/youtube/playlist.js", + "file": "plugins/youtube/playlist.js", "missing": [ "channelName", "stats", @@ -441,7 +441,7 @@ }, { "command": "youtube/watch-later", - "file": "clis/youtube/watch-later.js", + "file": "plugins/youtube/watch-later.js", "missing": [ "stats", "videos" diff --git a/scripts/typed-error-lint-baseline.json b/scripts/typed-error-lint-baseline.json index 0a59e29f..5d8ec0f2 100644 --- a/scripts/typed-error-lint-baseline.json +++ b/scripts/typed-error-lint-baseline.json @@ -34,7 +34,7 @@ { "rule": "silent-clamp", "command": "facebook/marketplace-inbox", - "file": "clis/facebook/marketplace-inbox.js", + "file": "plugins/facebook/marketplace-inbox.js", "line": 9, "text": "return Math.min(limit, 100);", "occurrence": 0 @@ -42,7 +42,7 @@ { "rule": "silent-clamp", "command": "facebook/marketplace-listings", - "file": "clis/facebook/marketplace-listings.js", + "file": "plugins/facebook/marketplace-listings.js", "line": 9, "text": "return Math.min(limit, 100);", "occurrence": 0 @@ -210,7 +210,7 @@ { "rule": "silent-clamp", "command": "reddit/read", - "file": "clis/reddit/read.js", + "file": "plugins/reddit/read.js", "line": 531, "text": "for (var i = 0; i < Math.min(t1TopLevel.length, limit); i++) {", "occurrence": 0 @@ -322,7 +322,7 @@ { "rule": "silent-clamp", "command": "twitter/bookmark-folder", - "file": "clis/twitter/bookmark-folder.js", + "file": "plugins/twitter/bookmark-folder.js", "line": 164, "text": "const fetchCount = Math.min(100, limit - allTweets.length + 10);", "occurrence": 0 @@ -330,7 +330,7 @@ { "rule": "silent-clamp", "command": "twitter/bookmarks", - "file": "clis/twitter/bookmarks.js", + "file": "plugins/twitter/bookmarks.js", "line": 157, "text": "const fetchCount = Math.min(100, limit - allTweets.length + 10);", "occurrence": 0 @@ -338,7 +338,7 @@ { "rule": "silent-clamp", "command": "twitter/following", - "file": "clis/twitter/following.js", + "file": "plugins/twitter/following.js", "line": 232, "text": "const fetchCount = Math.min(50, limit - allUsers.length + 10);", "occurrence": 0 @@ -346,7 +346,7 @@ { "rule": "silent-clamp", "command": "twitter/likes", - "file": "clis/twitter/likes.js", + "file": "plugins/twitter/likes.js", "line": 208, "text": "const fetchCount = Math.min(100, limit - allTweets.length + 10);", "occurrence": 0 @@ -354,7 +354,7 @@ { "rule": "silent-clamp", "command": "twitter/list-tweets", - "file": "clis/twitter/list-tweets.js", + "file": "plugins/twitter/list-tweets.js", "line": 179, "text": "const fetchCount = Math.min(100, limit - allTweets.length + 10);", "occurrence": 0 @@ -362,7 +362,7 @@ { "rule": "silent-clamp", "command": "twitter/timeline", - "file": "clis/twitter/timeline.js", + "file": "plugins/twitter/timeline.js", "line": 187, "text": "const fetchCount = Math.min(40, limit - allTweets.length + 5); // over-fetch slightly for promoted filtering", "occurrence": 0 @@ -370,7 +370,7 @@ { "rule": "silent-clamp", "command": "twitter/tweets", - "file": "clis/twitter/tweets.js", + "file": "plugins/twitter/tweets.js", "line": 316, "text": "const fetchCount = Math.min(USER_TWEETS_PAGE_SIZE, limit - all.length + 10);", "occurrence": 0 @@ -394,7 +394,7 @@ { "rule": "silent-clamp", "command": "youtube/channel", - "file": "clis/youtube/channel.js", + "file": "plugins/youtube/channel.js", "line": 81, "text": "const limit = Math.min(kwargs.limit || 10, 30);", "occurrence": 0 @@ -402,7 +402,7 @@ { "rule": "silent-clamp", "command": "youtube/comments", - "file": "clis/youtube/comments.js", + "file": "plugins/youtube/comments.js", "line": 21, "text": "const limit = Math.min(kwargs.limit || 20, 100);", "occurrence": 0 @@ -410,7 +410,7 @@ { "rule": "silent-clamp", "command": "youtube/feed", - "file": "clis/youtube/feed.js", + "file": "plugins/youtube/feed.js", "line": 20, "text": "const limit = Math.min(kwargs.limit || 20, 100);", "occurrence": 0 @@ -418,7 +418,7 @@ { "rule": "silent-clamp", "command": "youtube/history", - "file": "clis/youtube/history.js", + "file": "plugins/youtube/history.js", "line": 22, "text": "await page.autoScroll({ times: Math.min(Math.max(Math.ceil(limit / 20), 1), 8), delayMs: 1200 });", "occurrence": 0 @@ -426,7 +426,7 @@ { "rule": "silent-clamp", "command": "youtube/history", - "file": "clis/youtube/history.js", + "file": "plugins/youtube/history.js", "line": 19, "text": "const limit = Math.min(kwargs.limit || 30, 200);", "occurrence": 0 @@ -434,7 +434,7 @@ { "rule": "silent-clamp", "command": "youtube/playlist", - "file": "clis/youtube/playlist.js", + "file": "plugins/youtube/playlist.js", "line": 37, "text": "const limit = Math.min(kwargs.limit || 50, 200);", "occurrence": 0 @@ -442,7 +442,7 @@ { "rule": "silent-clamp", "command": "youtube/search", - "file": "clis/youtube/search.js", + "file": "plugins/youtube/search.js", "line": 22, "text": "const limit = Math.min(kwargs.limit || 20, 50);", "occurrence": 0 @@ -450,7 +450,7 @@ { "rule": "silent-clamp", "command": "youtube/subscriptions", - "file": "clis/youtube/subscriptions.js", + "file": "plugins/youtube/subscriptions.js", "line": 20, "text": "const limit = Math.min(kwargs.limit || 50, 1000);", "occurrence": 0 @@ -458,7 +458,7 @@ { "rule": "silent-clamp", "command": "youtube/watch-later", - "file": "clis/youtube/watch-later.js", + "file": "plugins/youtube/watch-later.js", "line": 21, "text": "const limit = Math.min(kwargs.limit || 50, 200);", "occurrence": 0 @@ -474,7 +474,7 @@ { "rule": "silent-sentinel", "command": "twitter/accept", - "file": "clis/twitter/accept.js", + "file": "plugins/twitter/accept.js", "line": 75, "text": "const user = lines[0] || 'Unknown';", "occurrence": 0 @@ -482,7 +482,7 @@ { "rule": "silent-sentinel", "command": "twitter/accept", - "file": "clis/twitter/accept.js", + "file": "plugins/twitter/accept.js", "line": 182, "text": "user: res.user || 'Unknown',", "occurrence": 0 @@ -490,7 +490,7 @@ { "rule": "silent-sentinel", "command": "twitter/bookmarks", - "file": "clis/twitter/bookmarks.js", + "file": "plugins/twitter/bookmarks.js", "line": 55, "text": "const screenName = user?.legacy?.screen_name || user?.core?.screen_name || 'unknown';", "occurrence": 0 @@ -498,7 +498,7 @@ { "rule": "silent-sentinel", "command": "twitter/likes", - "file": "clis/twitter/likes.js", + "file": "plugins/twitter/likes.js", "line": 91, "text": "const screenName = user?.legacy?.screen_name || user?.core?.screen_name || 'unknown';", "occurrence": 0 @@ -506,7 +506,7 @@ { "rule": "silent-sentinel", "command": "twitter/list-tweets", - "file": "clis/twitter/list-tweets.js", + "file": "plugins/twitter/list-tweets.js", "line": 63, "text": "const screenName = user?.legacy?.screen_name || user?.core?.screen_name || 'unknown';", "occurrence": 0 @@ -514,7 +514,7 @@ { "rule": "silent-sentinel", "command": "twitter/reply-dm", - "file": "clis/twitter/reply-dm.js", + "file": "plugins/twitter/reply-dm.js", "line": 84, "text": "const user = lines[0] || 'Unknown';", "occurrence": 0 @@ -522,7 +522,7 @@ { "rule": "silent-sentinel", "command": "twitter/thread", - "file": "clis/twitter/thread.js", + "file": "plugins/twitter/thread.js", "line": 49, "text": "const screenName = u?.legacy?.screen_name || u?.core?.screen_name || 'unknown';", "occurrence": 0 @@ -530,7 +530,7 @@ { "rule": "silent-sentinel", "command": "twitter/timeline", - "file": "clis/twitter/timeline.js", + "file": "plugins/twitter/timeline.js", "line": 76, "text": "const screenName = u?.legacy?.screen_name || u?.core?.screen_name || 'unknown';", "occurrence": 0 @@ -538,7 +538,7 @@ { "rule": "silent-sentinel", "command": "twitter/tweets", - "file": "clis/twitter/tweets.js", + "file": "plugins/twitter/tweets.js", "line": 163, "text": "const screenName = user?.legacy?.screen_name || user?.core?.screen_name || 'unknown';", "occurrence": 0 diff --git a/src/build-manifest.test.ts b/src/build-manifest.test.ts index 0ddd3c71..65f7a44f 100644 --- a/src/build-manifest.test.ts +++ b/src/build-manifest.test.ts @@ -512,7 +512,7 @@ describe('manifest helper rules', () => { it('does not publish per-command browser window defaults', () => { const manifest = JSON.parse( - fs.readFileSync(path.join(process.cwd(), 'cli-manifest.json'), 'utf8'), + fs.readFileSync(path.join(process.cwd(), 'plugin-command-manifest.json'), 'utf8'), ) as ManifestEntry[]; expect( @@ -521,7 +521,7 @@ describe('manifest helper rules', () => { }); it('keeps every browser login on the local handoff contract', () => { - const manifest = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'cli-manifest.json'), 'utf8')) as ManifestEntry[]; + const manifest = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'plugin-command-manifest.json'), 'utf8')) as ManifestEntry[]; const logins = manifest.filter((entry) => entry.browser === true && entry.name === 'login'); const keys = new Set(manifest.map((entry) => `${entry.site}/${entry.name}`)); diff --git a/src/cli.test.ts b/src/cli.test.ts index 0fa180d5..8439cca8 100644 --- a/src/cli.test.ts +++ b/src/cli.test.ts @@ -59,7 +59,24 @@ vi.mock('node:child_process', async () => { }; }); -import { createProgram, findPackageRoot, normalizeVerifyRows, renderVerifyPreview, resolveBrowserVerifyInvocation, resolveSitemapAvailabilityForUrl, selectFreshByTimestamp } from './cli.js'; +import { createProgram, findPackageRoot, loadAntigravityServe, normalizeVerifyRows, renderVerifyPreview, resolveBrowserVerifyInvocation, resolveSitemapAvailabilityForUrl, selectFreshByTimestamp } from './cli.js'; + +describe('Antigravity serve plugin loading', () => { + it('loads serve.js from the installed Antigravity plugin', async () => { + const pluginsDir = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-antigravity-plugins-')); + const pluginDir = path.join(pluginsDir, 'antigravity'); + fs.mkdirSync(pluginDir); + fs.writeFileSync(path.join(pluginDir, 'package.json'), '{"type":"module"}\n'); + fs.writeFileSync(path.join(pluginDir, 'serve.js'), 'export const loadedFrom = "installed-plugin";\n'); + try { + await expect(loadAntigravityServe(pluginsDir)).resolves.toMatchObject({ + loadedFrom: 'installed-plugin', + }); + } finally { + fs.rmSync(pluginsDir, { recursive: true, force: true }); + } + }); +}); describe('createProgram root help descriptions', () => { function descriptionFor(program: ReturnType, name: string): string | undefined { diff --git a/src/cli.ts b/src/cli.ts index 7821be00..f37470b9 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -9,7 +9,7 @@ import * as fs from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; import * as readline from 'node:readline/promises'; -import { fileURLToPath } from 'node:url'; +import { fileURLToPath, pathToFileURL } from 'node:url'; import { Command, Option } from 'commander'; import { findPackageRoot, getBuiltEntryCandidates } from './package-paths.js'; import { type CliCommand, getRegistry } from './registry.js'; @@ -47,7 +47,7 @@ import { CLI_COMMAND } from './brand.js'; import type { BrowserDownloadWaitResult, IPage, ScreenshotOptions } from './types.js'; import type { BrowserWindowMode } from './runtime.js'; import { configureRootCommandSurface } from './root-command-surface.js'; -import { missingPluginGuidance } from './discovery.js'; +import { missingPluginGuidance, PLUGINS_DIR } from './discovery.js'; const CLI_FILE = fileURLToPath(import.meta.url); const BROWSER_TAB_OPTION_DESCRIPTION = 'Target tab/page identity returned by "browser open", "browser tab new", or "browser tab list"'; @@ -3675,8 +3675,7 @@ cli({ .option('--port ', 'Server port (default: 8082)', '8082') .option('--timeout ', 'Maximum time to wait for a reply (default: 120s)') .action(async (opts) => { - // @ts-expect-error JS adapter — no type declarations - const { startServe } = await import('../../clis/antigravity/serve.js'); + const { startServe } = await loadAntigravityServe(); await startServe({ port: parseInt(opts.port, 10), timeout: opts.timeout ? parsePositiveIntOption(opts.timeout, '--timeout', 120) : undefined, @@ -3761,6 +3760,12 @@ cli({ return program; } +export async function loadAntigravityServe(pluginsDir: string = PLUGINS_DIR): Promise<{ + startServe(options: { port: number; timeout?: number }): Promise; +}> { + return import(pathToFileURL(path.join(pluginsDir, 'antigravity', 'serve.js')).href); +} + export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void { createProgram(BUILTIN_CLIS, USER_CLIS).parse(); } diff --git a/src/package-exports.test.ts b/src/package-exports.test.ts index 31eec317..1f17b00b 100644 --- a/src/package-exports.test.ts +++ b/src/package-exports.test.ts @@ -19,6 +19,7 @@ const CLIS_DIR = path.join(ROOT, 'clis'); /** Recursively collect all JS adapter files in a directory. */ function collectAdapterFiles(dir: string, opts?: { excludeTests?: boolean }): string[] { const results: string[] = []; + if (!fs.existsSync(dir)) return results; for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { const full = path.join(dir, entry.name); if (entry.isDirectory()) { @@ -60,8 +61,8 @@ describe('adapter imports use package exports', () => { const adapterFiles = collectAdapterFiles(CLIS_DIR); const runtimeAdapterFiles = collectAdapterFiles(CLIS_DIR, { excludeTests: true }); - it('found adapter files to check', () => { - expect(adapterFiles.length).toBeGreaterThan(100); + it('has no bundled core adapter files', () => { + expect(adapterFiles).toEqual([]); }); it('no adapter uses relative imports to src/, browser/, download/, or pipeline/', () => { diff --git a/src/plugin-runtime.test.ts b/src/plugin-runtime.test.ts index a7278ed8..6e8fcb1b 100644 --- a/src/plugin-runtime.test.ts +++ b/src/plugin-runtime.test.ts @@ -142,6 +142,19 @@ function pageMock() { } describe('site auth command helper', () => { + it('can register login without claiming a site-owned whoami command', () => { + registerSiteAuthCommands({ + site: 'auth-helper-login-only', + domain: 'example.com', + loginUrl: 'https://example.com/login', + registerWhoami: false, + verify: async () => ({ username: 'alice' }), + }); + + expect(getRegistry().has('auth-helper-login-only/whoami')).toBe(false); + expect(getRegistry().has('auth-helper-login-only/login')).toBe(true); + }); + it('registers whoami aliases and foreground login columns', () => { registerSiteAuthCommands({ site: 'auth-helper-registration', diff --git a/src/plugin-runtime.ts b/src/plugin-runtime.ts index 523cdd8b..32198321 100644 --- a/src/plugin-runtime.ts +++ b/src/plugin-runtime.ts @@ -184,6 +184,7 @@ export interface SiteAuthConfig { loginUrl: string; verify: (page: IPage, context: { phase: 'identity' }) => MaybePromise; columns?: string[]; + registerWhoami?: boolean; whoamiDescription?: string; whoamiAliases?: string[]; loginDescription?: string; @@ -239,29 +240,31 @@ export function registerSiteAuthCommands(config: SiteAuthConfig): void { const quickCheck = config.quickCheck; const refresh = config.refresh; - cli({ - site: config.site, - name: 'whoami', - access: 'read', - description: config.whoamiDescription ?? `Show the current logged-in ${config.site} account`, - domain: config.domain, - strategy: Strategy.COOKIE, - browser: true, - navigateBefore: false, - siteSession: 'persistent', - aliases: config.whoamiAliases ?? [], - args: [], - columns: commandColumns(config), - authStatus: { - ...(typeof quickCheck === 'function' - ? { quickCheck: async (page) => normalizeQuickCheck(await quickCheck(page)) } - : {}), - ...(typeof refresh === 'function' - ? { refresh: async (page, kwargs) => normalizeRefreshResult(await refresh(page, kwargs)) } - : {}), - }, - func: async (page) => [await tryProbe(page)], - }); + if (config.registerWhoami !== false) { + cli({ + site: config.site, + name: 'whoami', + access: 'read', + description: config.whoamiDescription ?? `Show the current logged-in ${config.site} account`, + domain: config.domain, + strategy: Strategy.COOKIE, + browser: true, + navigateBefore: false, + siteSession: 'persistent', + aliases: config.whoamiAliases ?? [], + args: [], + columns: commandColumns(config), + authStatus: { + ...(typeof quickCheck === 'function' + ? { quickCheck: async (page) => normalizeQuickCheck(await quickCheck(page)) } + : {}), + ...(typeof refresh === 'function' + ? { refresh: async (page, kwargs) => normalizeRefreshResult(await refresh(page, kwargs)) } + : {}), + }, + func: async (page) => [await tryProbe(page)], + }); + } cli({ site: config.site, diff --git a/webcmd-plugin.json b/webcmd-plugin.json index 761a1005..7ced18b3 100644 --- a/webcmd-plugin.json +++ b/webcmd-plugin.json @@ -24,6 +24,16 @@ "handle": "agentrhq" } }, + "antigravity": { + "path": "plugins/antigravity", + "version": "0.1.0", + "description": "Webcmd commands for antigravity", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "apple-podcasts": { "path": "plugins/apple-podcasts", "version": "0.1.0", @@ -384,6 +394,16 @@ "handle": "agentrhq" } }, + "facebook": { + "path": "plugins/facebook", + "version": "0.1.0", + "description": "Webcmd commands for facebook", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "flathub": { "path": "plugins/flathub", "version": "0.1.0", @@ -474,6 +494,16 @@ "handle": "agentrhq" } }, + "grok": { + "path": "plugins/grok", + "version": "0.1.0", + "description": "Webcmd commands for grok", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "hackernews": { "path": "plugins/hackernews", "version": "0.1.0", @@ -554,6 +584,16 @@ "handle": "agentrhq" } }, + "instagram": { + "path": "plugins/instagram", + "version": "0.1.0", + "description": "Webcmd commands for instagram", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "jhu": { "path": "plugins/jhu", "version": "0.1.0", @@ -684,6 +724,16 @@ "handle": "agentrhq" } }, + "notebooklm": { + "path": "plugins/notebooklm", + "version": "0.1.0", + "description": "Webcmd commands for notebooklm", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "npm": { "path": "plugins/npm", "version": "0.1.0", @@ -834,6 +884,26 @@ "handle": "yoldaolmak" } }, + "qoder": { + "path": "plugins/qoder", + "version": "0.1.0", + "description": "Webcmd commands for qoder", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, + "reddit": { + "path": "plugins/reddit", + "version": "0.1.0", + "description": "Webcmd commands for reddit", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "rest-countries": { "path": "plugins/rest-countries", "version": "0.1.0", @@ -894,6 +964,16 @@ "handle": "rishabhraj36" } }, + "slock": { + "path": "plugins/slock", + "version": "0.1.0", + "description": "Webcmd commands for slock", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "spotify": { "path": "plugins/spotify", "version": "0.1.0", @@ -954,6 +1034,16 @@ "handle": "agentrhq" } }, + "tiktok": { + "path": "plugins/tiktok", + "version": "0.1.0", + "description": "Webcmd commands for tiktok", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "trae-solo": { "path": "plugins/trae-solo", "version": "0.1.0", @@ -964,6 +1054,16 @@ "handle": "agentrhq" } }, + "trip": { + "path": "plugins/trip", + "version": "0.1.0", + "description": "Webcmd commands for trip", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "tvmaze": { "path": "plugins/tvmaze", "version": "0.1.0", @@ -974,6 +1074,16 @@ "handle": "agentrhq" } }, + "twitter": { + "path": "plugins/twitter", + "version": "0.1.0", + "description": "Webcmd commands for twitter", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "ualberta": { "path": "plugins/ualberta", "version": "0.1.0", @@ -1094,6 +1204,16 @@ "handle": "agentrhq" } }, + "youtube": { + "path": "plugins/youtube", + "version": "0.1.0", + "description": "Webcmd commands for youtube", + "webcmd": ">=0.6.0", + "author": { + "name": "WebCMD Agent", + "handle": "agentrhq" + } + }, "zepto": { "path": "plugins/zepto", "version": "0.1.0", From 52004ef023d79b9e2fcac599a9663237d5eb7a3c Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 18:11:32 +0530 Subject: [PATCH 20/39] refactor: make Webcmd core adapter-free --- .github/workflows/ci.yml | 25 ++- .github/workflows/release.yml | 12 ++ README.md | 12 +- TESTING.md | 6 + docs/authoring.mdx | 4 +- docs/cli-reference.mdx | 9 +- docs/concepts.mdx | 2 +- docs/create-plugin.mdx | 2 +- docs/publish-community-plugin.mdx | 3 + docs/troubleshooting.mdx | 8 + package.json | 9 +- scripts/check-hosted-contract.mjs | 4 +- scripts/check-package-bin.mjs | 8 + scripts/fetch-adapters.js | 293 ------------------------------ src/build-manifest.test.ts | 11 ++ src/build-manifest.ts | 27 ++- src/check-hosted-contract.test.ts | 21 ++- src/cli.test.ts | 11 +- src/cli.ts | 75 ++------ src/completion-fast.ts | 9 +- src/completion.test.ts | 53 ++++++ src/discovery.ts | 5 +- src/hosted/file-contract.test.ts | 18 +- src/hosted/runner.test.ts | 10 + src/hosted/runner.ts | 2 + src/main.ts | 32 ++-- src/package-exports.test.ts | 15 ++ src/package-paths.ts | 4 - vitest.config.ts | 5 +- 29 files changed, 255 insertions(+), 440 deletions(-) delete mode 100644 scripts/fetch-adapters.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 32dc4418..1d7dd4b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,10 +47,18 @@ jobs: - name: Build run: npm run build + - name: Build plugin command manifest + if: runner.os == 'Linux' + run: npm run build-plugin-manifest + - name: Check generated contract artifacts if: runner.os == 'Linux' run: npm run check:hosted-contract + - name: Check all generated artifacts are committed + if: runner.os == 'Linux' + run: git diff --exit-code -- cli-manifest.json hosted-contract.json plugin-command-manifest.json webcmd-plugin.json README.md + - name: Verify packed CLI executables run: npm run check:package-bin @@ -85,10 +93,14 @@ jobs: - name: Run unit tests run: npx vitest run --project unit --reporter=verbose --shard=${{ matrix.shard }}/2 - adapter-test: - name: Adapter tests + plugin-test: + name: Plugin tests (${{ matrix.os }}) needs: build - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@v6 @@ -100,8 +112,11 @@ jobs: - name: Install dependencies run: npm ci - - name: Run adapter tests - run: npm run test:adapter -- --reporter=verbose + - name: Build + run: npm run build + + - name: Run plugin tests + run: npm run test:plugin -- --reporter=verbose bun-test: name: Bun compatibility diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b8a3d181..3acfbe63 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,10 +68,22 @@ jobs: if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }} run: npm run build + - name: Build plugin command manifest + if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }} + run: npm run build-plugin-manifest + + - name: Check community plugin metadata + if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }} + run: npm run check-community-plugins + - name: Check generated contract artifacts if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }} run: npm run check:hosted-contract + - name: Check all generated artifacts are committed + if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }} + run: git diff --exit-code -- cli-manifest.json hosted-contract.json plugin-command-manifest.json webcmd-plugin.json README.md + - name: Check Codex plugin metadata if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }} run: npm run check:codex-plugin diff --git a/README.md b/README.md index 57e30797..93605407 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,15 @@ Webcmd requires Node.js 20+. npm install -g @agentrhq/webcmd ``` +The npm package ships the Webcmd core and browser commands, but no site +adapters. Search the plugin catalog and explicitly install the adapter you +need: + +```bash +webcmd plugin search -f json +webcmd plugin install +``` + ```bash webcmd skills add ``` @@ -101,7 +110,8 @@ Beyond website adapters, Webcmd can work through authenticated browser sessions, | AI tools | ChatGPT, Claude, Gemini, NotebookLM | Retrieve conversations, research outputs, notebooks, and generated materials from the tools you already use. | | shopping and bookings | Amazon, Blinkit, Zepto, BigBasket, District, Practo | Compare products, availability, prices, appointments, events, and delivery options. | -This list is illustrative; ask your agent to use webcmd to discover what is currently available. +This list is illustrative; availability comes from installed plugins. Ask your +agent to search and install the relevant plugin when a site is not installed. ## Learn More diff --git a/TESTING.md b/TESTING.md index b2f56aba..8f6f1a70 100644 --- a/TESTING.md +++ b/TESTING.md @@ -5,15 +5,21 @@ ```bash npm run typecheck npm run build +npm run build-plugin-manifest npm test ``` +`npm run build` must run before plugin tests because repository plugins import +the compiled public package exports. The core package contains no site +adapters; `npm test` runs the unit and generic plugin projects. + ## Focused Checks ```bash npx vitest run --project unit src/skills.test.ts npx vitest run --project unit src/package-exports.test.ts npx vitest run --project unit src/convention-audit.test.ts src/runtime-copy.test.ts +npm run test:plugin -- --reporter=verbose ``` ## Cloak Runtime Smoke diff --git a/docs/authoring.mdx b/docs/authoring.mdx index 09a8bbb3..11acd7fe 100644 --- a/docs/authoring.mdx +++ b/docs/authoring.mdx @@ -9,7 +9,7 @@ Describe the workflow you want. The agent can turn it into a command, keep its o ## Start With a Workflow -Use a command when a workflow is useful enough to repeat. Private adapters are the default for personal workflows. +Use a command when a workflow is useful enough to repeat. A private plugin is the default for personal workflows; Webcmd core does not bundle site adapters. ```text Create a private Webcmd adapter for the Acme supplier portal. @@ -58,4 +58,4 @@ Only add a new command when the workflow itself has changed. ## Private, Plugin, or Upstream -Keep personal or company workflows as private adapters. Package related adapters as a plugin when they should travel together or be shared. Prepare an adapter for upstream only when it is broadly useful and ready for repository review. +Keep personal or company workflows in private plugins. The legacy `~/.webcmd/clis/` path remains supported, but new portable work should use plugins. Prepare a plugin for upstream only when it is broadly useful and ready for repository review. diff --git a/docs/cli-reference.mdx b/docs/cli-reference.mdx index 4073a61d..3ab55554 100644 --- a/docs/cli-reference.mdx +++ b/docs/cli-reference.mdx @@ -59,14 +59,14 @@ The old `web read` command has been renamed to `web fetch-browser`. | Command | Purpose | | --- | --- | -| `list` | Show registered built-in, user, plugin, and external commands. | +| `list` | Show registered core, legacy user, plugin, and external commands. | | `setup` | Choose local or hosted mode interactively. | | `doctor` | Diagnose browser bridge and daemon connectivity. | | `browser` | Agent-facing browser runtime for exploration and verification. | | `web` | Local URL fetch and browser-backed page fetch helpers. | | `profile` | List, rename, and select browser runtime profiles. | | `plugin` | Install, update, list, create, and uninstall plugins. | -| `adapter` | Eject, reset, and inspect local adapter overrides. | +| `adapter` | Inspect or remove legacy adapters in `~/.webcmd/clis/`. | | `external` | Register or install external local CLIs. | | `validate` | Validate adapter definitions. | | `verify` | Validate and smoke test an adapter. | @@ -124,6 +124,9 @@ Use the `work` profile for this Webcmd task. If it is not authenticated, stop an Local mode supports plugin install, update, list, create, uninstall, catalog, search, and related marketplace commands. +The core package includes no site adapters. Search first, then explicitly use +the returned `installSource`: + ```bash webcmd plugin search ycombinator -f json webcmd plugin install github:agentrhq/webcmd/plugins/ycombinator @@ -146,7 +149,7 @@ Register our internal `releasectl` binary as a Webcmd external CLI with a short | Path | Purpose | | --- | --- | | `~/.webcmd/` | User-level Webcmd state. | -| `~/.webcmd/clis/` | Private adapters and local overrides. | +| `~/.webcmd/clis/` | Legacy private adapters retained for compatibility. | | `~/.webcmd/cache/browser-network/` | Browser network capture cache. | | `~/.webcmd/external-clis.yaml` | User external CLI registry. | | `skills/` | Bundled agent skills shipped with the package. | diff --git a/docs/concepts.mdx b/docs/concepts.mdx index ba3f0eb8..b545f986 100644 --- a/docs/concepts.mdx +++ b/docs/concepts.mdx @@ -21,7 +21,7 @@ Exploration is for understanding an unfamiliar surface. A verified, reusable wor ## Adapters and Stable Output -An adapter turns a website, app, API, or local surface into one or more commands. It can be built in, private to a user, shipped in a plugin, or backed by an external local CLI. +An adapter turns a website, app, API, or local surface into one or more commands. Site adapters are installed as plugins; the core package bundles none. Legacy private adapters in `~/.webcmd/clis/` remain discoverable, and local tools can also be exposed as external CLIs. Commands should have clear names, stable inputs, useful errors, and JSON-friendly output. Stable output is the contract that lets another agent reuse a successful workflow. diff --git a/docs/create-plugin.mdx b/docs/create-plugin.mdx index 840bade2..e6a09696 100644 --- a/docs/create-plugin.mdx +++ b/docs/create-plugin.mdx @@ -7,7 +7,7 @@ description: Ask an agent to package related Webcmd adapters into a reusable plu ## When a Plugin Is Worthwhile -A plugin is worthwhile when related adapters should travel together or be shared with a team. Keep a one-off personal workflow as a private adapter instead. +Webcmd core bundles no site adapters, so plugins are the normal delivery unit. Keep a one-off workflow in a private plugin; publish it only when it should be shared. ## Ask an Agent to Create It diff --git a/docs/publish-community-plugin.mdx b/docs/publish-community-plugin.mdx index 393c03f3..9e8c5982 100644 --- a/docs/publish-community-plugin.mdx +++ b/docs/publish-community-plugin.mdx @@ -19,6 +19,9 @@ Prepare my `webcmd-acme` plugin for contribution to the AgentR Webcmd repository Community plugins live under `plugins//`. Their `webcmd-plugin.json` must use the directory name, and include non-empty `version`, `description`, and `webcmd` compatibility fields. Author metadata needs both a display name and valid GitHub handle. +Repository plugins are catalog source, not npm package contents. Users install +an approved plugin explicitly; Webcmd core does not bundle it. + The root `webcmd-plugin.json` and the README community table are generated by repository tooling; do not edit them by hand. ## Validation and Pull Request diff --git a/docs/troubleshooting.mdx b/docs/troubleshooting.mdx index ec24f1af..2ee3f6d8 100644 --- a/docs/troubleshooting.mdx +++ b/docs/troubleshooting.mdx @@ -75,6 +75,14 @@ Stable output is more important than exposing every new site field. ## Plugin Problems +Webcmd core ships without site adapters. If a site is missing, search and +install it explicitly before diagnosing command code: + +```bash +webcmd plugin search -f json +webcmd plugin install +``` + ```text This Webcmd plugin is installed but its commands do not appear in `webcmd list -f json`. Diagnose the plugin manifest, adapter paths, and install state. ``` diff --git a/package.json b/package.json index b1e10fd4..984eb745 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ }, "files": [ "dist/src/", - "clis/", "skills/**", "cli-manifest.json", "hosted-contract.json", @@ -59,13 +58,13 @@ "start": "node dist/src/main.js", "start:bun": "bun dist/src/main.js", "preuninstall": "node -e \"fetch('http://127.0.0.1:9777/shutdown',{method:'POST',headers:{'X-Webcmd':'1'},signal:AbortSignal.timeout(3000)}).catch(()=>{})\" || true", - "postinstall": "node scripts/postinstall.js || true; node scripts/fetch-adapters.js || true", + "postinstall": "node scripts/postinstall.js || true", "typecheck": "tsc --noEmit", "prepare": "[ -d src ] && npm run build || true", "prepublishOnly": "npm run build", - "test": "vitest run --project unit --project adapter", - "test:bun": "bun vitest run --project unit --project adapter", - "test:adapter": "vitest run --project adapter", + "test": "vitest run --project unit --project plugin", + "test:bun": "bun vitest run --project unit --project plugin", + "test:plugin": "vitest run --project plugin", "test:all": "vitest run", "test:e2e": "vitest run --project e2e-fixed-port --project e2e", "check-community-plugins": "tsx scripts/sync-community-plugins.ts --check", diff --git a/scripts/check-hosted-contract.mjs b/scripts/check-hosted-contract.mjs index 693a9ee7..451835b7 100644 --- a/scripts/check-hosted-contract.mjs +++ b/scripts/check-hosted-contract.mjs @@ -11,7 +11,7 @@ const committedRoot = process.env.WEBCMD_CONTRACT_COMMITTED_ROOT ? path.resolve(process.env.WEBCMD_CONTRACT_COMMITTED_ROOT) : packageRoot; const generatedRoot = mkdtempSync(path.join(tmpdir(), 'webcmd-hosted-contract-')); -const committedArtifactNames = ['cli-manifest.json']; +const committedArtifactNames = ['cli-manifest.json', 'hosted-contract.json']; const generator = String.raw` import { readFile, writeFile } from 'node:fs/promises'; @@ -98,7 +98,7 @@ try { process.exitCode = 1; } else { process.stdout.write( - 'Generated cli-manifest.json matches committed bytes; hosted-contract.json generated successfully.\n', + 'Generated cli-manifest.json and hosted-contract.json match committed bytes; hosted-contract.json generated successfully.\n', ); } } diff --git a/scripts/check-package-bin.mjs b/scripts/check-package-bin.mjs index 88c3f60e..01c40594 100644 --- a/scripts/check-package-bin.mjs +++ b/scripts/check-package-bin.mjs @@ -70,6 +70,14 @@ try { } const packedPaths = new Set(packData.files.map((file) => file.path)); + for (const prefix of ['clis/', 'plugins/']) { + if ([...packedPaths].some((packedPath) => packedPath.startsWith(prefix))) { + fail(`packed tarball contains adapter source: ${prefix}`); + } + } + if (packedPaths.has('scripts/fetch-adapters.js')) { + fail('packed tarball contains the retired adapter fetch lifecycle'); + } for (const [name, target] of binEntries) { if (!packedPaths.has(String(target))) { fail(`packed tarball is missing bin "${name}" target: ${target}`); diff --git a/scripts/fetch-adapters.js b/scripts/fetch-adapters.js deleted file mode 100644 index e7235182..00000000 --- a/scripts/fetch-adapters.js +++ /dev/null @@ -1,293 +0,0 @@ -#!/usr/bin/env node - -/** - * Sparse adapter sync: keeps ~/.webcmd/clis/ clean by removing stale overrides. - * - * Strategy (hash-based, site-level granularity): - * - When an official site has upstream changes: DELETE the local override - * (do NOT copy new version — runtime falls back to package baseline) - * - When an official site has no changes: leave local override intact - * - User-created custom sites (not in package): always preserved - * - Skips entirely if already synced at the same version - * - * ~/.webcmd/clis/ is a sparse override layer, not a full copy. - * Only eject-ed or user-modified sites appear here. - * - * Only runs on global install (npm install -g) or explicit WEBCMD_FETCH=1. - * No network calls — reads hashes from clis/ in the installed package. - * - * This is an ESM script (package.json type: module). No TypeScript, no src/ imports. - */ - -import { existsSync, mkdirSync, rmSync, readFileSync, writeFileSync, readdirSync, statSync, unlinkSync } from 'node:fs'; -import { createHash } from 'node:crypto'; -import { join, resolve, dirname, relative } from 'node:path'; -import { homedir } from 'node:os'; - -const WEBCMD_DIR = join(homedir(), '.webcmd'); -const USER_CLIS_DIR = join(WEBCMD_DIR, 'clis'); -const MANIFEST_PATH = join(WEBCMD_DIR, 'adapter-manifest.json'); -const PACKAGE_ROOT = resolve(import.meta.dirname, '..'); -const BUILTIN_CLIS = join(PACKAGE_ROOT, 'clis'); - -function log(msg) { - console.log(`[webcmd] ${msg}`); -} - -function getPackageVersion() { - try { - return JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf-8')).version; - } catch { - return 'unknown'; - } -} - -/** - * Compute SHA-256 hash of file content. - */ -function fileHash(filePath) { - return createHash('sha256').update(readFileSync(filePath)).digest('hex'); -} - -/** - * Read existing manifest. Returns { version, files, hashes } or null. - */ -function readManifest() { - try { - return JSON.parse(readFileSync(MANIFEST_PATH, 'utf-8')); - } catch { - return null; - } -} - -/** - * Collect all relative file paths under a directory. - */ -function walkFiles(dir, prefix = '') { - const results = []; - if (!existsSync(dir)) return results; - for (const entry of readdirSync(dir)) { - const full = join(dir, entry); - const rel = prefix ? `${prefix}/${entry}` : entry; - if (statSync(full).isDirectory()) { - results.push(...walkFiles(full, rel)); - } else { - results.push(rel); - } - } - return results; -} - -/** - * Remove empty parent directories up to (but not including) stopAt. - */ -function pruneEmptyDirs(filePath, stopAt) { - const boundary = resolve(stopAt); - let dir = resolve(dirname(filePath)); - while (dir !== boundary) { - const rel = relative(boundary, dir); - if (!rel || rel.startsWith('..')) break; - try { - const entries = readdirSync(dir); - if (entries.length > 0) break; - rmSync(dir); - dir = dirname(dir); - } catch { - break; - } - } -} - -export function fetchAdapters() { - const currentVersion = getPackageVersion(); - const oldManifest = readManifest(); - - // Skip if already installed at the same version (unless forced via WEBCMD_FETCH=1) - const isForced = process.env.WEBCMD_FETCH === '1'; - if (!isForced && currentVersion !== 'unknown' && oldManifest?.version === currentVersion) { - log(`Adapters already up to date (v${currentVersion})`); - return; - } - - if (!existsSync(BUILTIN_CLIS)) { - log('Warning: clis/ not found in package — skipping adapter copy'); - return; - } - - const newOfficialFiles = new Set(walkFiles(BUILTIN_CLIS)); - const oldOfficialFiles = new Set(oldManifest?.files ?? []); - const rawHashes = oldManifest?.hashes; - // Guard against corrupted manifest: if hashes is a non-object type (string, number, - // array), skip sync to avoid false-positive "changed" detection that deletes overrides. - // null/undefined are treated as empty (old manifests may lack the field). - if (rawHashes != null && (typeof rawHashes !== 'object' || Array.isArray(rawHashes))) { - log('Warning: adapter-manifest.json has corrupted hashes — skipping sync. Will fix on next run.'); - return; - } - const oldHashes = rawHashes ?? {}; - mkdirSync(USER_CLIS_DIR, { recursive: true }); - - // 1. Compute new hashes and detect which sites have changes - const newHashes = {}; - const siteFiles = new Map(); // site -> [relPath, ...] - for (const relPath of newOfficialFiles) { - const src = join(BUILTIN_CLIS, relPath); - const srcHash = fileHash(src); - newHashes[relPath] = srcHash; - - const site = relPath.split('/')[0]; - if (!siteFiles.has(site)) siteFiles.set(site, []); - siteFiles.get(site).push(relPath); - } - - // Determine which sites have any changed/new/removed files - const changedSites = new Set(); - for (const [site, files] of siteFiles) { - for (const relPath of files) { - if (oldHashes[relPath] !== newHashes[relPath]) { - changedSites.add(site); - break; - } - } - } - // Also mark sites that had files removed - for (const relPath of oldOfficialFiles) { - if (!newOfficialFiles.has(relPath)) { - changedSites.add(relPath.split('/')[0]); - } - } - - // 2. Sparse cleanup: for changed/removed official sites, delete local overrides. - // Do NOT copy new versions — runtime falls back to package baseline. - // Only eject-ed sites live in ~/.webcmd/clis/. - let cleared = 0; - for (const site of changedSites) { - const siteDir = join(USER_CLIS_DIR, site); - if (existsSync(siteDir)) { - rmSync(siteDir, { recursive: true, force: true }); - cleared++; - } - } - - // 3. Clean up stale .ts adapter files left by older versions (pre-1.7.1) - // Older versions shipped adapters as .ts; current versions use .js only. - let tsCleaned = 0; - for (const relPath of walkFiles(USER_CLIS_DIR)) { - if (relPath.endsWith('.ts') && !relPath.endsWith('.d.ts')) { - const jsCounterpart = relPath.replace(/\.ts$/, '.js'); - if (newOfficialFiles.has(jsCounterpart)) { - try { - unlinkSync(join(USER_CLIS_DIR, relPath)); - pruneEmptyDirs(join(USER_CLIS_DIR, relPath), USER_CLIS_DIR); - tsCleaned++; - } catch { /* ignore */ } - } - } - } - if (tsCleaned > 0) log(`Cleaned up ${tsCleaned} stale .ts adapter files`); - - // 3b. Clean up stale .yaml/.yml adapter files left by older versions (pre-1.7.0) - // Older versions shipped adapters as YAML; current versions use .js only. - // These are no longer discoverable and can shadow the current .js adapter layout. - let yamlCleaned = 0; - for (const relPath of walkFiles(USER_CLIS_DIR)) { - if (relPath.endsWith('.yaml') || relPath.endsWith('.yml')) { - const jsCounterpart = relPath.replace(/\.ya?ml$/, '.js'); - if (newOfficialFiles.has(jsCounterpart)) { - try { - unlinkSync(join(USER_CLIS_DIR, relPath)); - pruneEmptyDirs(join(USER_CLIS_DIR, relPath), USER_CLIS_DIR); - yamlCleaned++; - } catch { /* ignore */ } - } - } - } - if (yamlCleaned > 0) log(`Cleaned up ${yamlCleaned} stale .yaml adapter files`); - - // 4. Clean up legacy compat shim files from ~/.webcmd/ - // These were created by an older approach that placed re-export shims directly - // in ~/.webcmd/ (e.g., registry.js, errors.js, browser/). The current approach - // uses a node_modules/@agentrhq/webcmd symlink instead. - const LEGACY_SHIM_FILES = [ - 'registry.js', 'errors.js', 'utils.js', 'launcher.js', 'logger.js', 'types.js', - ]; - const LEGACY_SHIM_DIRS = [ - 'browser', 'download', 'errors', 'launcher', 'logger', 'pipeline', 'registry', 'types', 'utils', - ]; - let legacyCleaned = 0; - for (const file of LEGACY_SHIM_FILES) { - const p = join(WEBCMD_DIR, file); - try { - const content = readFileSync(p, 'utf-8'); - // Only delete if it's a re-export shim, not a user-created file - if (content.includes("export * from 'file://")) { - unlinkSync(p); - legacyCleaned++; - } - } catch { /* doesn't exist */ } - } - for (const dir of LEGACY_SHIM_DIRS) { - const p = join(WEBCMD_DIR, dir); - try { - // Delete individual shim files, then prune empty directory - for (const entry of readdirSync(p)) { - const fp = join(p, entry); - try { - if (!statSync(fp).isFile()) continue; - const content = readFileSync(fp, 'utf-8'); - if (content.includes("export * from 'file://")) { - unlinkSync(fp); - legacyCleaned++; - } - } catch { /* skip unreadable entries */ } - } - // Remove directory only if now empty - try { - if (readdirSync(p).length === 0) rmSync(p); - } catch { /* ignore */ } - } catch { /* doesn't exist or not a directory */ } - } - - // 5. Clean up stale .plugins.lock.json.tmp-* files - let tmpCleaned = 0; - try { - for (const entry of readdirSync(WEBCMD_DIR)) { - if (entry.startsWith('.plugins.lock.json.tmp-')) { - try { - unlinkSync(join(WEBCMD_DIR, entry)); - tmpCleaned++; - } catch { /* ignore */ } - } - } - } catch { /* ignore */ } - - if (legacyCleaned > 0 || tmpCleaned > 0) { - log(`Cleaned up${legacyCleaned > 0 ? ` ${legacyCleaned} legacy shim files` : ''}${tmpCleaned > 0 ? `${legacyCleaned > 0 ? ',' : ''} ${tmpCleaned} stale tmp files` : ''}`); - } - - // 6. Write updated manifest (with per-file hashes for smart sync) - writeFileSync(MANIFEST_PATH, JSON.stringify({ - version: currentVersion, - files: [...newOfficialFiles].sort(), - hashes: newHashes, - updatedAt: new Date().toISOString(), - }, null, 2)); - - log(`Synced adapters: ${cleared} local override(s) cleared` + - (tsCleaned > 0 ? `, ${tsCleaned} stale .ts files removed` : '') + - (yamlCleaned > 0 ? `, ${yamlCleaned} stale .yaml files removed` : '')); -} - -function main() { - // Skip in CI - if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) return; - // Only run on global install, explicit trigger, or first-run fallback - const isGlobal = process.env.npm_config_global === 'true'; - const isExplicit = process.env.WEBCMD_FETCH === '1'; - const isFirstRun = process.env._WEBCMD_FIRST_RUN === '1'; - if (!isGlobal && !isExplicit && !isFirstRun) return; - - fetchAdapters(); -} - -main(); diff --git a/src/build-manifest.test.ts b/src/build-manifest.test.ts index 65f7a44f..403cae84 100644 --- a/src/build-manifest.test.ts +++ b/src/build-manifest.test.ts @@ -25,6 +25,17 @@ describe('manifest helper rules', () => { } }); + it('builds an empty core manifest when clis/ is absent', async () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-no-clis-')); + tempDirs.push(root); + + await expect(scanClisDir(path.join(root, 'clis'))).resolves.toEqual({ + entries: [], + failures: [], + }); + expect(serializeManifest([])).toBe('[]\n'); + }); + it('skips TS files that do not register a cli', () => { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-manifest-')); tempDirs.push(dir); diff --git a/src/build-manifest.ts b/src/build-manifest.ts index 36fe7db0..5cb93350 100644 --- a/src/build-manifest.ts +++ b/src/build-manifest.ts @@ -2,12 +2,12 @@ /** * Build-time CLI manifest compiler. * - * Scans all JS CLI definitions in clis/ and pre-compiles them into a single - * manifest.json for instant cold-start registration. + * Scans the optional legacy clis/ tree and compiles the core command manifest. + * Adapter-free packages intentionally serialize this manifest as `[]`. * * Usage: npx tsx src/build-manifest.ts [--allow-removals[=N]] * - * Output: cli-manifest.json next to clis/ + * Output: cli-manifest.json at the package root. * * Safety invariants: * - Adapters whose source file does not call `cli(...)` are silently @@ -27,7 +27,7 @@ import * as path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { getErrorMessage } from './errors.js'; import { fullName, getRegistry, type CliCommand } from './registry.js'; -import { findPackageRoot, getCliManifestPath } from './package-paths.js'; +import { findPackageRoot } from './package-paths.js'; import type { ManifestEntry } from './manifest-types.js'; import { isRecord } from './utils.js'; import { @@ -39,17 +39,13 @@ import { export type { ManifestEntry } from './manifest-types.js'; const PACKAGE_ROOT = findPackageRoot(fileURLToPath(import.meta.url)); -const CLIS_DIR = path.join(PACKAGE_ROOT, 'clis'); -// Write manifest next to clis/ so both dev and installed runtime can find it. -const OUTPUT = getCliManifestPath(CLIS_DIR); +const LEGACY_CLIS_DIR = path.join(PACKAGE_ROOT, 'clis'); +const OUTPUT = path.join(PACKAGE_ROOT, 'cli-manifest.json'); const HOSTED_CONTRACT_OUTPUT = path.join(PACKAGE_ROOT, 'hosted-contract.json'); // Module is treated as a CLI command source if it either: // 1. Calls `cli(...)` directly (the common case), or -// 2. Calls a factory `makeCommand(...)` from clis/_shared/ that -// wraps `cli(...)`. Without (2), shared-factory adapters -// (codex/cursor/chatwise new/status/dump/screenshot) match no `cli(` -// token at the top level and silently drop out of the manifest. +// 2. Calls a `makeCommand(...)` factory that wraps `cli(...)`. const CLI_MODULE_PATTERN = /\bcli\s*\(|\bregisterSiteAuthCommands\s*\(|\bmake[A-Z]\w*Command\s*\(/; /** @@ -149,14 +145,13 @@ function toManifestEntry(cmd: CliCommand, modulePath: string, sourceFile?: strin * surface or aggregate the failure. * * The third argument `clisDir` is used to compute the POSIX-style - * `sourceFile` relative path; it defaults to the package's `clis/` dir so - * existing test callers stay backward-compatible. + * `sourceFile` relative path; it defaults to the optional legacy `clis/` dir. */ export async function loadManifestEntries( filePath: string, site: string, importer: (moduleHref: string) => Promise = moduleHref => import(moduleHref), - clisDir: string = CLIS_DIR, + clisDir: string = LEGACY_CLIS_DIR, ): Promise { let src: string; try { @@ -207,7 +202,7 @@ export async function loadManifestEntries( } /** - * Scan a `clis/` directory and aggregate per-adapter results. Import + * Scan an adapter directory and aggregate per-adapter results. Import * failures are collected in `failures` instead of crashing the whole scan, * but the caller (e.g. `main()`) is expected to fail loud if any failure * is present. @@ -253,7 +248,7 @@ export async function scanClisDir( } export async function buildManifest(): Promise { - return scanClisDir(CLIS_DIR); + return scanClisDir(LEGACY_CLIS_DIR); } export function serializeManifest(manifest: ManifestEntry[]): string { diff --git a/src/check-hosted-contract.test.ts b/src/check-hosted-contract.test.ts index 6362c1c6..56b27ccb 100644 --- a/src/check-hosted-contract.test.ts +++ b/src/check-hosted-contract.test.ts @@ -5,10 +5,13 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { spawnSync } from 'node:child_process'; import { afterEach, describe, expect, it } from 'vitest'; +import { browserCommandCatalog } from './browser/command-catalog.js'; +import { buildManifestArtifacts } from './build-manifest.js'; +import { PKG_VERSION } from './version.js'; const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const checkerPath = path.join(packageRoot, 'scripts/check-hosted-contract.mjs'); -const committedArtifactNames = ['cli-manifest.json'] as const; +const committedArtifactNames = ['cli-manifest.json', 'hosted-contract.json'] as const; const fixtureRoots: string[] = []; afterEach(() => { @@ -20,14 +23,16 @@ afterEach(() => { function createCommittedArtifactFixture(): string { const fixtureRoot = mkdtempSync(path.join(tmpdir(), 'webcmd-contract-committed-')); fixtureRoots.push(fixtureRoot); - for (const artifactName of committedArtifactNames) { - copyFileSync(path.join(packageRoot, artifactName), path.join(fixtureRoot, artifactName)); - } + copyFileSync(path.join(packageRoot, 'cli-manifest.json'), path.join(fixtureRoot, 'cli-manifest.json')); + writeFileSync( + path.join(fixtureRoot, 'hosted-contract.json'), + buildManifestArtifacts([], PKG_VERSION, browserCommandCatalog).hostedContractJson, + ); return fixtureRoot; } function rootArtifactHashes(): Record { - return Object.fromEntries(committedArtifactNames.map((artifactName) => [ + return Object.fromEntries(['cli-manifest.json'].map((artifactName) => [ artifactName, createHash('sha256').update(readFileSync(path.join(packageRoot, artifactName))).digest('hex'), ])); @@ -49,10 +54,10 @@ describe('hosted contract reproducibility checker', () => { expect(result.stdout).toContain('hosted-contract.json generated successfully.'); }, 10_000); - it('rejects one stale byte without mutating root artifacts', () => { + it.each(committedArtifactNames)('rejects one stale byte in %s without mutating root artifacts', (artifactName) => { const before = rootArtifactHashes(); const fixtureRoot = createCommittedArtifactFixture(); - const stalePath = path.join(fixtureRoot, 'cli-manifest.json'); + const stalePath = path.join(fixtureRoot, artifactName); const staleBytes = readFileSync(stalePath); staleBytes[0] ^= 1; writeFileSync(stalePath, staleBytes); @@ -60,7 +65,7 @@ describe('hosted contract reproducibility checker', () => { const result = runChecker(fixtureRoot); expect(result.status).toBe(1); - expect(result.stderr).toContain('cli-manifest.json'); + expect(result.stderr).toContain(artifactName); expect(rootArtifactHashes()).toEqual(before); }, 10_000); }); diff --git a/src/cli.test.ts b/src/cli.test.ts index 8439cca8..5963aa90 100644 --- a/src/cli.test.ts +++ b/src/cli.test.ts @@ -92,7 +92,7 @@ describe('createProgram root help descriptions', () => { expect(descriptionFor(program, 'browser')).not.toContain('Browser control'); expect(descriptionFor(program, 'auth')).toBe('refresh, status'); expect(descriptionFor(program, 'plugin')).toBe('catalog, create, install, list, search, uninstall, update'); - expect(descriptionFor(program, 'adapter')).toBe('eject, reset, status'); + expect(descriptionFor(program, 'adapter')).toBe('reset, status'); expect(descriptionFor(program, 'profile')).toBe('list, rename, use'); expect(descriptionFor(program, 'daemon')).toBe('restart, status, stop'); expect(descriptionFor(program, 'external')).toBe('install, list, register'); @@ -105,6 +105,13 @@ describe('createProgram root help descriptions', () => { expect(skills.commands.find((command) => command.name() === 'add')?.aliases()).toEqual([]); }); + it('keeps legacy local adapters manageable without claiming a bundled baseline', () => { + const adapter = createProgram('', '').commands.find((command) => command.name() === 'adapter')!; + + expect(adapter.commands.map((command) => command.name())).toEqual(['status', 'reset']); + expect(adapter.helpInformation()).not.toMatch(/official|baseline|eject/i); + }); + it('renders auth namespace structured help', () => { const argv = process.argv; try { @@ -887,7 +894,7 @@ name: 'search', // applyRootSubcommandSummaries() rewrites .description() to a child-name listing; // structured help must surface the original product description via the snapshot. expect(data.description).toBe('Manage CLI adapters'); - expect(data.commands.map((cmd: any) => cmd.name)).toEqual(['eject', 'reset', 'status']); + expect(data.commands.map((cmd: any) => cmd.name)).toEqual(['reset', 'status']); const reset = data.commands.find((cmd: any) => cmd.name === 'reset'); expect(reset).toMatchObject({ usage: 'webcmd adapter reset [site] [options]', diff --git a/src/cli.ts b/src/cli.ts index f37470b9..978298f8 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -3376,89 +3376,41 @@ cli({ adapterCmd .command('status') - .description('Show which sites have local overrides vs using official baseline') + .description('List legacy local adapters in ~/.webcmd/clis/') .action(async () => { - const os = await import('node:os'); - const userClisDir = path.join(os.homedir(), '.webcmd', 'clis'); - const builtinClisDir = BUILTIN_CLIS; try { - const userEntries = await fs.promises.readdir(userClisDir, { withFileTypes: true }); + const userEntries = await fs.promises.readdir(USER_CLIS, { withFileTypes: true }); const userSites = userEntries.filter(e => e.isDirectory()).map(e => e.name).sort(); - let builtinSites: string[] = []; - try { - const builtinEntries = await fs.promises.readdir(builtinClisDir, { withFileTypes: true }); - builtinSites = builtinEntries.filter(e => e.isDirectory()).map(e => e.name).sort(); - } catch { /* no builtin dir */ } - if (userSites.length === 0) { - console.log('No local adapter overrides. All sites use the official baseline.'); + console.log('No legacy local adapters installed.'); return; } - console.log(`Local overrides in ~/.webcmd/clis/ (${userSites.length} sites):\n`); - for (const site of userSites) { - const isOfficial = builtinSites.includes(site); - const label = isOfficial ? 'override' : 'custom'; - console.log(` ${site} [${label}]`); - } - console.log(`\nOfficial baseline: ${builtinSites.length} sites in package`); + console.log(`Legacy local adapters in ~/.webcmd/clis/ (${userSites.length} sites):\n`); + for (const site of userSites) console.log(` ${site}`); } catch { - console.log('No local adapter overrides. All sites use the official baseline.'); + console.log('No legacy local adapters installed.'); } }); - adapterCmd - .command('eject') - .description('Copy an official adapter to ~/.webcmd/clis/ for local editing') - .argument('', 'Site name (e.g. twitter, youtube)') - .action(async (site: string) => { - const os = await import('node:os'); - const userClisDir = path.join(os.homedir(), '.webcmd', 'clis'); - const builtinSiteDir = path.join(BUILTIN_CLIS, site); - const userSiteDir = path.join(userClisDir, site); - - try { - await fs.promises.access(builtinSiteDir); - } catch { - console.error(`Error: Site "${site}" not found in official adapters.`); - process.exitCode = EXIT_CODES.USAGE_ERROR; - return; - } - - try { - await fs.promises.access(userSiteDir); - console.error(`Site "${site}" already exists in ~/.webcmd/clis/. Use "webcmd adapter reset ${site}" first to restore official version.`); - process.exitCode = EXIT_CODES.USAGE_ERROR; - return; - } catch { /* good, doesn't exist yet */ } - - fs.cpSync(builtinSiteDir, userSiteDir, { recursive: true }); - console.log(`✅ Ejected "${site}" to ~/.webcmd/clis/${site}/`); - console.log('You can now edit the adapter files. Changes take effect immediately.'); - console.log('Note: Official updates to this adapter will overwrite your changes.'); - }); - adapterCmd .command('reset') - .description('Remove local override and restore official adapter version') + .description('Remove a legacy local adapter') .argument('[site]', 'Site name (e.g. twitter, youtube)') .option('--all', 'Reset all local overrides') .action(async (site: string | undefined, opts: { all?: boolean }) => { - const os = await import('node:os'); - const userClisDir = path.join(os.homedir(), '.webcmd', 'clis'); - if (opts.all) { try { - const userEntries = await fs.promises.readdir(userClisDir, { withFileTypes: true }); + const userEntries = await fs.promises.readdir(USER_CLIS, { withFileTypes: true }); const dirs = userEntries.filter(e => e.isDirectory()); if (dirs.length === 0) { console.log('No local sites to reset.'); return; } for (const dir of dirs) { - fs.rmSync(path.join(userClisDir, dir.name), { recursive: true, force: true }); + fs.rmSync(path.join(USER_CLIS, dir.name), { recursive: true, force: true }); } - console.log(`✅ Reset ${dirs.length} site(s). All adapters now use official baseline.`); + console.log(`✅ Removed ${dirs.length} legacy local adapter(s).`); } catch { console.log('No local sites to reset.'); } @@ -3471,7 +3423,7 @@ cli({ return; } - const userSiteDir = path.join(userClisDir, site); + const userSiteDir = path.join(USER_CLIS, site); try { await fs.promises.access(userSiteDir); } catch { @@ -3479,11 +3431,8 @@ cli({ return; } - const isOfficial = fs.existsSync(path.join(BUILTIN_CLIS, site)); fs.rmSync(userSiteDir, { recursive: true, force: true }); - console.log(isOfficial - ? `✅ Reset "${site}". Now using official baseline.` - : `✅ Removed custom site "${site}".`); + console.log(`✅ Removed legacy local adapter "${site}".`); }); // ── Built-in: browser profile selection ────────────────────────────────── diff --git a/src/completion-fast.ts b/src/completion-fast.ts index 536f2728..a1dec4da 100644 --- a/src/completion-fast.ts +++ b/src/completion-fast.ts @@ -27,7 +27,7 @@ interface ManifestCompletionEntry { * the fast path must not be used — otherwise those adapters would silently * disappear from completion results. */ -export function hasAllManifests(manifestPaths: string[]): boolean { +export function hasAllManifests(manifestPaths: string[], uncoveredCommandRoots: string[] = []): boolean { for (const p of manifestPaths) { try { fs.accessSync(p); @@ -35,6 +35,13 @@ export function hasAllManifests(manifestPaths: string[]): boolean { return false; } } + for (const root of uncoveredCommandRoots) { + try { + if (fs.readdirSync(root, { withFileTypes: true }).some(entry => entry.isDirectory() || entry.isSymbolicLink())) { + return false; + } + } catch { /* absent command roots are empty */ } + } return manifestPaths.length > 0; } diff --git a/src/completion.test.ts b/src/completion.test.ts index 6140de59..f3041752 100644 --- a/src/completion.test.ts +++ b/src/completion.test.ts @@ -1,6 +1,8 @@ import * as fs from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; import { afterEach, describe, expect, it, vi } from 'vitest'; const { mockGetRegistry } = vi.hoisted(() => ({ @@ -28,6 +30,44 @@ import { getCompletionsFromManifest } from './completion-fast.js'; const tempDirs: string[] = []; +function installFixturePlugin(home: string): void { + const pluginDir = path.join(home, '.webcmd', 'plugins', 'fixture'); + fs.mkdirSync(pluginDir, { recursive: true }); + fs.writeFileSync(path.join(pluginDir, 'show.js'), ` +import { cli, Strategy } from '@agentrhq/webcmd/registry'; + +cli({ + site: 'fixture', + name: 'show', + description: 'Show an opaque identifier', + access: 'read', + strategy: Strategy.PUBLIC, + browser: false, + defaultFormat: 'json', + args: [{ name: 'id', positional: true, required: true, help: 'Opaque identifier' }], + columns: ['id'], + func: async ({ id }) => [{ id }], +}); +`); +} + +function createFixtureHome(): string { + const home = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-plugin-completion-')); + tempDirs.push(home); + installFixturePlugin(home); + return home; +} + +function runFixtureCli(home: string, args: string[]) { + return spawnSync(process.execPath, ['--import', 'tsx', path.join(ROOT, 'src/main.ts'), ...args], { + cwd: ROOT, + encoding: 'utf8', + env: { ...process.env, HOME: home, USERPROFILE: home, CI: '1' }, + }); +} + +const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); + afterEach(() => { for (const dir of tempDirs.splice(0)) fs.rmSync(dir, { recursive: true, force: true }); }); @@ -60,4 +100,17 @@ describe('getCompletions', () => { expect(getCompletionsFromManifest([], 1, [manifestPath])).toEqual(getCompletions([], 1)); expect(getCompletionsFromManifest(['github'], 2, [manifestPath])).toEqual(getCompletions(['github'], 2)); }); + + it('uses installed plugin metadata for completion and dash-leading positionals', () => { + const rootCompletion = runFixtureCli(createFixtureHome(), ['--get-completions', '--cursor', '1']); + const commandCompletion = runFixtureCli(createFixtureHome(), ['--get-completions', 'fixture', '--cursor', '2']); + const execution = runFixtureCli(createFixtureHome(), ['fixture', 'show', '-opaque']); + + expect(rootCompletion.status, rootCompletion.stderr).toBe(0); + expect(rootCompletion.stdout.split(/\r?\n/)).toContain('fixture'); + expect(commandCompletion.status, commandCompletion.stderr).toBe(0); + expect(commandCompletion.stdout.split(/\r?\n/)).toContain('show'); + expect(execution.status, execution.stderr).toBe(0); + expect(execution.stdout).toContain('"id": "-opaque"'); + }, 20_000); }); diff --git a/src/discovery.ts b/src/discovery.ts index 72fe8d1f..c3daad40 100644 --- a/src/discovery.ts +++ b/src/discovery.ts @@ -161,9 +161,8 @@ export async function ensureUserCliCompatShims(baseDir: string = USER_WEBCMD_DIR /** * Ensure the user adapters directory exists. * - * With smart sync, ~/.webcmd/clis/ only holds files that differ from the - * package baseline (upstream-synced cache + autofix output + user overrides). - * Built-in adapters are loaded directly from the installed package. + * This legacy directory remains available for private adapters and autofix + * output. Official adapters are installed as plugins instead. */ export async function ensureUserAdapters(): Promise { await fs.promises.mkdir(USER_CLIS_DIR, { recursive: true }); diff --git a/src/hosted/file-contract.test.ts b/src/hosted/file-contract.test.ts index 22968d92..a90c91ff 100644 --- a/src/hosted/file-contract.test.ts +++ b/src/hosted/file-contract.test.ts @@ -2,7 +2,7 @@ import { readFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; -import type { HostedContract, HostedFileArgumentContract } from './contract.js'; +import { buildHostedContract, type HostedContract, type HostedFileArgumentContract } from './contract.js'; import type { ManifestEntry } from '../manifest-types.js'; const MiB = 1024 * 1024; @@ -158,6 +158,13 @@ function manifestCommand(manifest: ManifestEntry[], command: string): ManifestEn return entry; } +function commandManifest(): ManifestEntry[] { + return [ + ...readJson('cli-manifest.json'), + ...readJson('plugin-command-manifest.json'), + ]; +} + function contractCommand(contract: HostedContract, command: string) { const entry = contract.commands.find(item => item.command === command); if (!entry) throw new Error(`Missing hosted contract command: ${command}`); @@ -166,11 +173,8 @@ function contractCommand(contract: HostedContract, command: string) { describe('hosted file argument contract', () => { it('declares every real local path argument in generated artifacts', () => { - const manifest = [ - ...readJson('cli-manifest.json'), - ...readJson('plugin-command-manifest.json'), - ]; - const contract = readJson('hosted-contract.json'); + const manifest = commandManifest(); + const contract = buildHostedContract(manifest, [], 'test'); for (const [command, expected] of Object.entries(EXPECTED_FILE_ARGUMENTS)) { expect(contractCommand(contract, command).fileArguments, command).toEqual(expected); @@ -182,7 +186,7 @@ describe('hosted file argument contract', () => { }); it('does not treat Twitter remote image URLs as file arguments', () => { - const contract = readJson('hosted-contract.json'); + const contract = buildHostedContract(commandManifest(), [], 'test'); expect(contractCommand(contract, 'twitter/quote').fileArguments.map(arg => arg.name)) .not.toContain('image-url'); diff --git a/src/hosted/runner.test.ts b/src/hosted/runner.test.ts index 0a1d1aed..6fcba43d 100644 --- a/src/hosted/runner.test.ts +++ b/src/hosted/runner.test.ts @@ -6,6 +6,7 @@ import { Writable, type WritableOptions } from 'node:stream'; import type { Command } from 'commander'; import { describe, expect, it, vi } from 'vitest'; import { browserCommandCatalog } from '../browser/command-catalog.js'; +import { buildHostedContract } from './contract.js'; import { rewriteBrowserArgv } from '../cli-argv-preprocess.js'; import { createProgram } from '../cli.js'; import { formatRootHelp } from '../command-presentation.js'; @@ -18,6 +19,15 @@ const [packageMajor, packageMinor] = PKG_VERSION.split('.'); const compatiblePatchVersion = `${packageMajor}.${packageMinor}.99`; const incompatibleMinorVersion = `${packageMajor}.${Number(packageMinor) + 1}.0`; +it('ships no default site commands while preserving the browser contract', () => { + const contract = buildHostedContract([], browserCommandCatalog, PKG_VERSION); + + expect(contract.commands).toEqual([]); + expect(contract.browserCommands.map(command => command.command)).toEqual( + browserCommandCatalog.map(command => command.command), + ); +}); + const manifest = { userId: 'user_demo', metadata: { diff --git a/src/hosted/runner.ts b/src/hosted/runner.ts index ffabc230..b40f601a 100644 --- a/src/hosted/runner.ts +++ b/src/hosted/runner.ts @@ -245,6 +245,8 @@ async function dispatchHosted( return; } + // The API manifest is tenant-scoped. Never merge package or local plugin + // commands into it: the installed package contract contains no site commands. const manifest = await client.getManifest(); validateManifestContractIdentity(manifest); diff --git a/src/main.ts b/src/main.ts index fe1c98b0..1e7a6e35 100644 --- a/src/main.ts +++ b/src/main.ts @@ -26,10 +26,11 @@ import { CONFIG_DIR_NAME } from './brand.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -// Adapters are JS-first and live at /clis/. -// Use findPackageRoot so the path works both in dev (src/main.ts) and prod (dist/src/main.js). +// The empty core manifest remains next to the retired clis/ location so older +// user-local adapter manifests keep the same lookup contract. const BUILTIN_CLIS = path.join(findPackageRoot(__filename), 'clis'); const USER_CLIS = path.join(os.homedir(), CONFIG_DIR_NAME, 'clis'); +const USER_PLUGINS = path.join(os.homedir(), CONFIG_DIR_NAME, 'plugins'); // ── Ultra-fast path: lightweight commands bypass full discovery ────────── // These are high-frequency or trivial paths that must not pay the startup tax. @@ -98,9 +99,10 @@ if (getCompIdx !== -1) { // Only include manifests that actually exist on disk. // With sparse override, the user clis dir may exist but have no manifest. const manifestPaths = [getCliManifestPath(BUILTIN_CLIS)]; + const uncoveredCommandRoots = [USER_PLUGINS]; const userManifest = getCliManifestPath(USER_CLIS); - try { fs.accessSync(userManifest); manifestPaths.push(userManifest); } catch { /* no user manifest */ } - if (hasAllManifests(manifestPaths)) { + try { fs.accessSync(userManifest); manifestPaths.push(userManifest); } catch { uncoveredCommandRoots.push(USER_CLIS); } + if (hasAllManifests(manifestPaths, uncoveredCommandRoots)) { const rest = process.argv.slice(getCompIdx + 1); let cursor: number | undefined; const words: string[] = []; @@ -132,13 +134,9 @@ const { registerUpdateNoticeOnExit, checkForUpdateBackground } = await import('. installNodeNetwork(); // Parallelise independent startup I/O: -// - Built-in adapter discovery has no dependency on user-dir setup. // - ensureUserCliCompatShims and ensureUserAdapters operate on different paths -// (~/.webcmd/node_modules/ vs ~/.webcmd/clis/ + adapter-manifest.json). -// - registerCommand() overwrites on name collision (see registry.ts), so -// user-CLI discovery MUST run after built-in discovery to preserve the -// intended override order (user adapters override built-in ones). -// - discoverPlugins runs last: plugins may override both built-in and user CLIs. +// (~/.webcmd/node_modules/ vs ~/.webcmd/clis/). +// - discoverPlugins runs last: installed plugins may override legacy user CLIs. const skipUserDiscovery = argv[0] === 'convention-audit'; if (skipUserDiscovery) { await discoverClis(BUILTIN_CLIS); @@ -183,16 +181,10 @@ if (getCompIdx !== -1) { const { rewriteBrowserArgv, BrowserSessionArgvError, escapeLeadingDashPositional } = await import('./cli-argv-preprocess.js'); try { let rewritten = rewriteBrowserArgv(process.argv.slice(2)); - // Insert a `--` separator before a required positional whose value starts - // with `-` (e.g. opaque securityId tokens; #1160). Skipped when the - // manifest is unavailable so the user-cli / dev paths still work. - try { - const manifestPath = getCliManifestPath(BUILTIN_CLIS); - if (fs.existsSync(manifestPath)) { - const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')); - if (Array.isArray(manifest)) rewritten = escapeLeadingDashPositional(rewritten, manifest); - } - } catch { /* manifest unavailable; skip the dash escape */ } + // Use the metadata that discovery actually registered. The core manifest is + // intentionally empty, while installed plugins and legacy user CLIs are not. + const { getRegistry } = await import('./registry.js'); + rewritten = escapeLeadingDashPositional(rewritten, [...new Set(getRegistry().values())]); process.argv.splice(2, process.argv.length - 2, ...rewritten); } catch (err) { if (err instanceof BrowserSessionArgvError) { diff --git a/src/package-exports.test.ts b/src/package-exports.test.ts index 1f17b00b..e14a146b 100644 --- a/src/package-exports.test.ts +++ b/src/package-exports.test.ts @@ -9,6 +9,7 @@ import { describe, it, expect } from 'vitest'; import * as fs from 'node:fs'; import * as path from 'node:path'; import { builtinModules } from 'node:module'; +import { spawnSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; import ts from 'typescript'; @@ -65,6 +66,20 @@ describe('adapter imports use package exports', () => { expect(adapterFiles).toEqual([]); }); + it('packs no core adapters, repository plugins, or adapter fetch lifecycle', () => { + const packed = spawnSync('npm', ['pack', '--ignore-scripts', '--dry-run', '--json'], { + cwd: ROOT, + encoding: 'utf8', + }); + expect(packed.status, packed.stderr).toBe(0); + const files = (JSON.parse(packed.stdout) as Array<{ files: Array<{ path: string }> }>)[0]!.files + .map(file => file.path); + + expect(files.some(file => file.startsWith('clis/'))).toBe(false); + expect(files.some(file => file.startsWith('plugins/'))).toBe(false); + expect(files).not.toContain('scripts/fetch-adapters.js'); + }, 15_000); + it('no adapter uses relative imports to src/, browser/, download/, or pipeline/', () => { const violations: string[] = []; for (const file of adapterFiles) { diff --git a/src/package-paths.ts b/src/package-paths.ts index 46272448..379c0410 100644 --- a/src/package-paths.ts +++ b/src/package-paths.ts @@ -52,7 +52,3 @@ export function getBuiltEntryCandidates( export function getCliManifestPath(clisDir: string): string { return path.resolve(clisDir, '..', 'cli-manifest.json'); } - -export function getFetchAdaptersScriptPath(packageRoot: string): string { - return path.join(packageRoot, 'scripts', 'fetch-adapters.js'); -} diff --git a/vitest.config.ts b/vitest.config.ts index 9936ebae..e47d31c2 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -8,14 +8,13 @@ export default defineConfig({ test: { name: 'unit', include: ['src/**/*.test.ts'], - exclude: ['clis/**/*.test.{ts,js}'], sequence: { groupOrder: 0 }, }, }, { test: { - name: 'adapter', - include: ['clis/**/*.test.{ts,js}', 'plugins/*/test/**/*.test.{ts,js}'], + name: 'plugin', + include: ['plugins/*/test/**/*.test.{ts,js}'], sequence: { groupOrder: 1 }, }, }, From 552f22f5a080227b01a08908c13ebdb83b9b6051 Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 19:01:30 +0530 Subject: [PATCH 21/39] fix: address plugin migration final review --- .github/workflows/ci.yml | 4 +++ .github/workflows/release.yml | 4 +++ README.md | 2 +- docs/cli-reference.mdx | 2 +- docs/quickstart.mdx | 2 +- plugins/amazon-in/README.md | 2 +- plugins/amazon/README.md | 2 +- plugins/antigravity/README.md | 2 +- plugins/apple-podcasts/README.md | 2 +- plugins/archive/README.md | 2 +- plugins/arxiv/README.md | 2 +- plugins/band/README.md | 2 +- plugins/barchart/README.md | 2 +- plugins/bbc/README.md | 2 +- plugins/bigbasket/README.md | 2 +- plugins/binance/README.md | 2 +- plugins/blinkit/README.md | 2 +- plugins/bloomberg/README.md | 2 +- plugins/bluesky/README.md | 2 +- plugins/booking/README.md | 2 +- plugins/brave/README.md | 2 +- plugins/chatgpt-app/README.md | 2 +- plugins/chatgpt/README.md | 2 +- plugins/chatwise/README.md | 2 +- plugins/chess/README.md | 2 +- plugins/cincinnati/README.md | 2 +- plugins/claude/README.md | 2 +- plugins/codex/README.md | 2 +- plugins/coingecko/README.md | 2 +- plugins/concordia/README.md | 2 +- plugins/confluence/README.md | 2 +- plugins/coupang/README.md | 2 +- plugins/crates/README.md | 2 +- plugins/cursor/README.md | 2 +- plugins/dblp/README.md | 2 +- plugins/defillama/README.md | 2 +- plugins/devto/README.md | 2 +- plugins/dictionary/README.md | 2 +- plugins/discord-app/README.md | 2 +- plugins/district/README.md | 2 +- plugins/dockerhub/README.md | 2 +- plugins/duckduckgo/README.md | 2 +- plugins/endoflife/README.md | 2 +- plugins/facebook/README.md | 2 +- plugins/flathub/README.md | 2 +- plugins/gemini/README.md | 2 +- plugins/geogebra/README.md | 2 +- plugins/github-trending/README.md | 2 +- plugins/github/README.md | 2 +- plugins/goettingen/README.md | 2 +- plugins/goettingen/package.json | 3 ++ plugins/google-scholar/README.md | 2 +- plugins/google/README.md | 2 +- plugins/goproxy/README.md | 2 +- plugins/grok/README.md | 2 +- plugins/hackernews/README.md | 2 +- plugins/heidelberg/README.md | 2 +- plugins/heidelberg/package.json | 3 ++ plugins/hf/README.md | 2 +- plugins/hft/README.md | 2 +- plugins/hft/package.json | 3 ++ plugins/homebrew/README.md | 2 +- plugins/iit/README.md | 2 +- plugins/imdb/README.md | 2 +- plugins/indeed/README.md | 2 +- plugins/instagram/README.md | 2 +- plugins/jhu/README.md | 2 +- plugins/jira/README.md | 2 +- plugins/lesswrong/README.md | 2 +- plugins/lichess/README.md | 2 +- plugins/linkedin-learning/README.md | 2 +- plugins/linkedin/package.json | 2 +- plugins/linkedin/webcmd-plugin.json | 2 +- plugins/lobsters/README.md | 2 +- plugins/manus/README.md | 2 +- plugins/maven/README.md | 2 +- plugins/mdn/README.md | 2 +- plugins/medium/README.md | 2 +- plugins/mercury/README.md | 2 +- plugins/notebooklm/README.md | 2 +- plugins/npm/README.md | 2 +- plugins/nuget/README.md | 2 +- plugins/nvd/README.md | 2 +- plugins/oeis/README.md | 2 +- plugins/openalex/README.md | 2 +- plugins/openfda/README.md | 2 +- plugins/openreview/README.md | 2 +- plugins/osv/README.md | 2 +- plugins/packagist/README.md | 2 +- plugins/paperreview/README.md | 2 +- plugins/pixiv/README.md | 2 +- plugins/practo/README.md | 2 +- plugins/producthunt/README.md | 2 +- plugins/pubmed/README.md | 2 +- plugins/pypi/README.md | 2 +- plugins/qoder/README.md | 2 +- plugins/reddit/README.md | 2 +- plugins/rest-countries/README.md | 2 +- plugins/reuters/README.md | 2 +- plugins/rfc/README.md | 2 +- plugins/rubygems/README.md | 2 +- plugins/semanticscholar/README.md | 2 +- plugins/slock/README.md | 2 +- plugins/spotify/README.md | 2 +- plugins/stackoverflow/README.md | 2 +- plugins/steam/README.md | 2 +- plugins/substack/README.md | 2 +- plugins/suno/README.md | 2 +- plugins/techcrunch/README.md | 2 +- plugins/tiktok/README.md | 2 +- plugins/trae-solo/README.md | 2 +- plugins/trip/README.md | 2 +- plugins/tvmaze/README.md | 2 +- plugins/twitter/README.md | 2 +- plugins/ualberta/README.md | 2 +- plugins/uiverse/README.md | 2 +- plugins/upwork/README.md | 2 +- plugins/web/README.md | 2 +- plugins/wikidata/README.md | 2 +- plugins/wikipedia/README.md | 2 +- plugins/wttr/README.md | 2 +- plugins/yahoo-finance/README.md | 2 +- plugins/yahoo/README.md | 2 +- plugins/yale/README.md | 2 +- plugins/ycombinator/README.md | 2 +- plugins/yollomi/README.md | 2 +- plugins/youtube/README.md | 2 +- plugins/zepto/README.md | 2 +- plugins/zlibrary/README.md | 2 +- scripts/migrate-cli-sites.mjs | 4 ++- scripts/postinstall.js | 25 +++---------- src/build-plugin-command-manifest.test.ts | 44 +++++++++++++++++++++++ src/cli.test.ts | 39 ++++++++++++++++++++ src/cli.ts | 32 +++++++++-------- src/migrate-cli-sites.test.ts | 18 +++++++++- src/postinstall.test.ts | 30 ++++++++++++++++ src/release-notes.test.ts | 2 +- src/release-notes.ts | 2 +- webcmd-plugin.json | 2 +- 139 files changed, 298 insertions(+), 165 deletions(-) create mode 100644 src/postinstall.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d7dd4b9..4eed3004 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,10 @@ jobs: if: runner.os == 'Linux' run: npm run build-plugin-manifest + - name: Check plugin command parity + if: runner.os == 'Linux' + run: npm run check:plugin-parity + - name: Check generated contract artifacts if: runner.os == 'Linux' run: npm run check:hosted-contract diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3acfbe63..ee4d0e42 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -72,6 +72,10 @@ jobs: if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }} run: npm run build-plugin-manifest + - name: Check plugin command parity + if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }} + run: npm run check:plugin-parity + - name: Check community plugin metadata if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }} run: npm run check-community-plugins diff --git a/README.md b/README.md index 93605407..11a55bf5 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ skills with `webcmd skills add` in Codex. ### Other agents or plugin-free setup -Webcmd requires Node.js 20+. +Webcmd requires Node.js 20.6+. ```bash npm install -g @agentrhq/webcmd diff --git a/docs/cli-reference.mdx b/docs/cli-reference.mdx index 3ab55554..9f87e2e4 100644 --- a/docs/cli-reference.mdx +++ b/docs/cli-reference.mdx @@ -129,7 +129,7 @@ the returned `installSource`: ```bash webcmd plugin search ycombinator -f json -webcmd plugin install github:agentrhq/webcmd/plugins/ycombinator +webcmd plugin install github:agentrhq/webcmd/ycombinator ``` Hosted mode supports the same `plugin search` and `plugin install` grammar for Webcmd-verified marketplace adapters. Other plugin management commands remain local-only in hosted mode. diff --git a/docs/quickstart.mdx b/docs/quickstart.mdx index e87ed2e1..94613ebc 100644 --- a/docs/quickstart.mdx +++ b/docs/quickstart.mdx @@ -17,7 +17,7 @@ skills with `webcmd skills add` in Codex. ## Other Agents or Plugin-Free Setup -Webcmd requires Node.js 20+ and a place where your agent harness can run its +Webcmd requires Node.js 20.6+ and a place where your agent harness can run its commands. ```bash diff --git a/plugins/amazon-in/README.md b/plugins/amazon-in/README.md index 7f2441de..74d985ef 100644 --- a/plugins/amazon-in/README.md +++ b/plugins/amazon-in/README.md @@ -5,7 +5,7 @@ Webcmd commands for amazon-in. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/amazon-in +webcmd plugin install github:agentrhq/webcmd/amazon-in ``` ## Commands diff --git a/plugins/amazon/README.md b/plugins/amazon/README.md index 2b0d214d..cb2f028c 100644 --- a/plugins/amazon/README.md +++ b/plugins/amazon/README.md @@ -5,7 +5,7 @@ Webcmd commands for amazon. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/amazon +webcmd plugin install github:agentrhq/webcmd/amazon ``` ## Commands diff --git a/plugins/antigravity/README.md b/plugins/antigravity/README.md index 6ecb5890..8197db59 100644 --- a/plugins/antigravity/README.md +++ b/plugins/antigravity/README.md @@ -5,7 +5,7 @@ Webcmd commands for antigravity. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/antigravity +webcmd plugin install github:agentrhq/webcmd/antigravity ``` ## Commands diff --git a/plugins/apple-podcasts/README.md b/plugins/apple-podcasts/README.md index b2d9c520..5df1433d 100644 --- a/plugins/apple-podcasts/README.md +++ b/plugins/apple-podcasts/README.md @@ -5,7 +5,7 @@ Webcmd commands for apple-podcasts. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/apple-podcasts +webcmd plugin install github:agentrhq/webcmd/apple-podcasts ``` ## Commands diff --git a/plugins/archive/README.md b/plugins/archive/README.md index 771c72ea..5c8d1440 100644 --- a/plugins/archive/README.md +++ b/plugins/archive/README.md @@ -5,7 +5,7 @@ Webcmd commands for archive. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/archive +webcmd plugin install github:agentrhq/webcmd/archive ``` ## Commands diff --git a/plugins/arxiv/README.md b/plugins/arxiv/README.md index 14797907..d3137852 100644 --- a/plugins/arxiv/README.md +++ b/plugins/arxiv/README.md @@ -5,7 +5,7 @@ Webcmd commands for arxiv. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/arxiv +webcmd plugin install github:agentrhq/webcmd/arxiv ``` ## Commands diff --git a/plugins/band/README.md b/plugins/band/README.md index 446ec885..4a06d833 100644 --- a/plugins/band/README.md +++ b/plugins/band/README.md @@ -5,7 +5,7 @@ Webcmd commands for band. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/band +webcmd plugin install github:agentrhq/webcmd/band ``` ## Commands diff --git a/plugins/barchart/README.md b/plugins/barchart/README.md index bdad5700..a963e990 100644 --- a/plugins/barchart/README.md +++ b/plugins/barchart/README.md @@ -5,7 +5,7 @@ Webcmd commands for barchart. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/barchart +webcmd plugin install github:agentrhq/webcmd/barchart ``` ## Commands diff --git a/plugins/bbc/README.md b/plugins/bbc/README.md index 6abe7098..fbf47abd 100644 --- a/plugins/bbc/README.md +++ b/plugins/bbc/README.md @@ -5,7 +5,7 @@ Webcmd commands for bbc. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/bbc +webcmd plugin install github:agentrhq/webcmd/bbc ``` ## Commands diff --git a/plugins/bigbasket/README.md b/plugins/bigbasket/README.md index 1e2035de..edf23ca9 100644 --- a/plugins/bigbasket/README.md +++ b/plugins/bigbasket/README.md @@ -5,7 +5,7 @@ Webcmd commands for bigbasket. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/bigbasket +webcmd plugin install github:agentrhq/webcmd/bigbasket ``` ## Commands diff --git a/plugins/binance/README.md b/plugins/binance/README.md index a07ff7e0..8f0ca64a 100644 --- a/plugins/binance/README.md +++ b/plugins/binance/README.md @@ -5,7 +5,7 @@ Webcmd commands for binance. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/binance +webcmd plugin install github:agentrhq/webcmd/binance ``` ## Commands diff --git a/plugins/blinkit/README.md b/plugins/blinkit/README.md index bf016a06..55d060d7 100644 --- a/plugins/blinkit/README.md +++ b/plugins/blinkit/README.md @@ -5,7 +5,7 @@ Webcmd commands for blinkit. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/blinkit +webcmd plugin install github:agentrhq/webcmd/blinkit ``` ## Commands diff --git a/plugins/bloomberg/README.md b/plugins/bloomberg/README.md index 4def2087..7b4da5de 100644 --- a/plugins/bloomberg/README.md +++ b/plugins/bloomberg/README.md @@ -5,7 +5,7 @@ Webcmd commands for bloomberg. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/bloomberg +webcmd plugin install github:agentrhq/webcmd/bloomberg ``` ## Commands diff --git a/plugins/bluesky/README.md b/plugins/bluesky/README.md index 1eed7705..70a3f093 100644 --- a/plugins/bluesky/README.md +++ b/plugins/bluesky/README.md @@ -5,7 +5,7 @@ Webcmd commands for bluesky. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/bluesky +webcmd plugin install github:agentrhq/webcmd/bluesky ``` ## Commands diff --git a/plugins/booking/README.md b/plugins/booking/README.md index 76b6badd..f88bb8b2 100644 --- a/plugins/booking/README.md +++ b/plugins/booking/README.md @@ -5,7 +5,7 @@ Webcmd commands for booking. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/booking +webcmd plugin install github:agentrhq/webcmd/booking ``` ## Commands diff --git a/plugins/brave/README.md b/plugins/brave/README.md index f2acf871..41dfbda7 100644 --- a/plugins/brave/README.md +++ b/plugins/brave/README.md @@ -5,7 +5,7 @@ Webcmd commands for brave. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/brave +webcmd plugin install github:agentrhq/webcmd/brave ``` ## Commands diff --git a/plugins/chatgpt-app/README.md b/plugins/chatgpt-app/README.md index 73fe84d2..20b2f374 100644 --- a/plugins/chatgpt-app/README.md +++ b/plugins/chatgpt-app/README.md @@ -5,7 +5,7 @@ Webcmd commands for chatgpt-app. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/chatgpt-app +webcmd plugin install github:agentrhq/webcmd/chatgpt-app ``` ## Commands diff --git a/plugins/chatgpt/README.md b/plugins/chatgpt/README.md index a1bcd75c..f82b721f 100644 --- a/plugins/chatgpt/README.md +++ b/plugins/chatgpt/README.md @@ -5,7 +5,7 @@ Webcmd commands for chatgpt. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/chatgpt +webcmd plugin install github:agentrhq/webcmd/chatgpt ``` ## Commands diff --git a/plugins/chatwise/README.md b/plugins/chatwise/README.md index cde46fc1..d7d327f3 100644 --- a/plugins/chatwise/README.md +++ b/plugins/chatwise/README.md @@ -5,7 +5,7 @@ Webcmd commands for chatwise. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/chatwise +webcmd plugin install github:agentrhq/webcmd/chatwise ``` ## Commands diff --git a/plugins/chess/README.md b/plugins/chess/README.md index ce01350b..4d41f8cc 100644 --- a/plugins/chess/README.md +++ b/plugins/chess/README.md @@ -5,7 +5,7 @@ Webcmd commands for chess. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/chess +webcmd plugin install github:agentrhq/webcmd/chess ``` ## Commands diff --git a/plugins/cincinnati/README.md b/plugins/cincinnati/README.md index 0260d362..99f14204 100644 --- a/plugins/cincinnati/README.md +++ b/plugins/cincinnati/README.md @@ -5,7 +5,7 @@ University of Cincinnati postgraduate course export adapter. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/cincinnati +webcmd plugin install github:agentrhq/webcmd/cincinnati ``` ## Command diff --git a/plugins/claude/README.md b/plugins/claude/README.md index dbf47738..5159a9f6 100644 --- a/plugins/claude/README.md +++ b/plugins/claude/README.md @@ -5,7 +5,7 @@ Webcmd commands for claude. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/claude +webcmd plugin install github:agentrhq/webcmd/claude ``` ## Commands diff --git a/plugins/codex/README.md b/plugins/codex/README.md index eccf5242..b66ca526 100644 --- a/plugins/codex/README.md +++ b/plugins/codex/README.md @@ -5,7 +5,7 @@ Webcmd commands for codex. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/codex +webcmd plugin install github:agentrhq/webcmd/codex ``` ## Commands diff --git a/plugins/coingecko/README.md b/plugins/coingecko/README.md index c22f4dcb..91760a53 100644 --- a/plugins/coingecko/README.md +++ b/plugins/coingecko/README.md @@ -5,7 +5,7 @@ Webcmd commands for coingecko. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/coingecko +webcmd plugin install github:agentrhq/webcmd/coingecko ``` ## Commands diff --git a/plugins/concordia/README.md b/plugins/concordia/README.md index 9d6783de..527ee1d3 100644 --- a/plugins/concordia/README.md +++ b/plugins/concordia/README.md @@ -5,7 +5,7 @@ Concordia University Montréal postgraduate course export adapter. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/concordia +webcmd plugin install github:agentrhq/webcmd/concordia ``` ## Command diff --git a/plugins/confluence/README.md b/plugins/confluence/README.md index 5a389552..c94ae035 100644 --- a/plugins/confluence/README.md +++ b/plugins/confluence/README.md @@ -5,7 +5,7 @@ Webcmd commands for confluence. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/confluence +webcmd plugin install github:agentrhq/webcmd/confluence ``` ## Commands diff --git a/plugins/coupang/README.md b/plugins/coupang/README.md index c6613765..3939b424 100644 --- a/plugins/coupang/README.md +++ b/plugins/coupang/README.md @@ -5,7 +5,7 @@ Webcmd commands for coupang. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/coupang +webcmd plugin install github:agentrhq/webcmd/coupang ``` ## Commands diff --git a/plugins/crates/README.md b/plugins/crates/README.md index 600ebadb..d2f2a97b 100644 --- a/plugins/crates/README.md +++ b/plugins/crates/README.md @@ -5,7 +5,7 @@ Webcmd commands for crates. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/crates +webcmd plugin install github:agentrhq/webcmd/crates ``` ## Commands diff --git a/plugins/cursor/README.md b/plugins/cursor/README.md index 0e879772..d9d8f6ba 100644 --- a/plugins/cursor/README.md +++ b/plugins/cursor/README.md @@ -5,7 +5,7 @@ Webcmd commands for cursor. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/cursor +webcmd plugin install github:agentrhq/webcmd/cursor ``` ## Commands diff --git a/plugins/dblp/README.md b/plugins/dblp/README.md index 0c5eebe5..f9c3dd39 100644 --- a/plugins/dblp/README.md +++ b/plugins/dblp/README.md @@ -5,7 +5,7 @@ Webcmd commands for dblp. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/dblp +webcmd plugin install github:agentrhq/webcmd/dblp ``` ## Commands diff --git a/plugins/defillama/README.md b/plugins/defillama/README.md index 2b96fb3a..84650e13 100644 --- a/plugins/defillama/README.md +++ b/plugins/defillama/README.md @@ -5,7 +5,7 @@ Webcmd commands for defillama. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/defillama +webcmd plugin install github:agentrhq/webcmd/defillama ``` ## Commands diff --git a/plugins/devto/README.md b/plugins/devto/README.md index 465947cb..1c3ff64d 100644 --- a/plugins/devto/README.md +++ b/plugins/devto/README.md @@ -5,7 +5,7 @@ Webcmd commands for devto. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/devto +webcmd plugin install github:agentrhq/webcmd/devto ``` ## Commands diff --git a/plugins/dictionary/README.md b/plugins/dictionary/README.md index 0906aa98..bf51fdb0 100644 --- a/plugins/dictionary/README.md +++ b/plugins/dictionary/README.md @@ -5,7 +5,7 @@ Webcmd commands for dictionary. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/dictionary +webcmd plugin install github:agentrhq/webcmd/dictionary ``` ## Commands diff --git a/plugins/discord-app/README.md b/plugins/discord-app/README.md index fb35ca1f..94988603 100644 --- a/plugins/discord-app/README.md +++ b/plugins/discord-app/README.md @@ -5,7 +5,7 @@ Webcmd commands for discord-app. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/discord-app +webcmd plugin install github:agentrhq/webcmd/discord-app ``` ## Commands diff --git a/plugins/district/README.md b/plugins/district/README.md index 9ed0d2c2..8ab5a51f 100644 --- a/plugins/district/README.md +++ b/plugins/district/README.md @@ -5,7 +5,7 @@ Webcmd commands for district. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/district +webcmd plugin install github:agentrhq/webcmd/district ``` ## Commands diff --git a/plugins/dockerhub/README.md b/plugins/dockerhub/README.md index 8fab128c..d8582c1e 100644 --- a/plugins/dockerhub/README.md +++ b/plugins/dockerhub/README.md @@ -5,7 +5,7 @@ Webcmd commands for dockerhub. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/dockerhub +webcmd plugin install github:agentrhq/webcmd/dockerhub ``` ## Commands diff --git a/plugins/duckduckgo/README.md b/plugins/duckduckgo/README.md index e9883fd0..c08b9919 100644 --- a/plugins/duckduckgo/README.md +++ b/plugins/duckduckgo/README.md @@ -5,7 +5,7 @@ Webcmd commands for duckduckgo. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/duckduckgo +webcmd plugin install github:agentrhq/webcmd/duckduckgo ``` ## Commands diff --git a/plugins/endoflife/README.md b/plugins/endoflife/README.md index 96dc3e64..df594374 100644 --- a/plugins/endoflife/README.md +++ b/plugins/endoflife/README.md @@ -5,7 +5,7 @@ Webcmd commands for endoflife. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/endoflife +webcmd plugin install github:agentrhq/webcmd/endoflife ``` ## Commands diff --git a/plugins/facebook/README.md b/plugins/facebook/README.md index cfab825c..6810dc19 100644 --- a/plugins/facebook/README.md +++ b/plugins/facebook/README.md @@ -5,7 +5,7 @@ Webcmd commands for facebook. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/facebook +webcmd plugin install github:agentrhq/webcmd/facebook ``` ## Commands diff --git a/plugins/flathub/README.md b/plugins/flathub/README.md index 0ec252d1..146f1b91 100644 --- a/plugins/flathub/README.md +++ b/plugins/flathub/README.md @@ -5,7 +5,7 @@ Webcmd commands for flathub. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/flathub +webcmd plugin install github:agentrhq/webcmd/flathub ``` ## Commands diff --git a/plugins/gemini/README.md b/plugins/gemini/README.md index b3f77ade..c2f10c80 100644 --- a/plugins/gemini/README.md +++ b/plugins/gemini/README.md @@ -5,7 +5,7 @@ Webcmd commands for gemini. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/gemini +webcmd plugin install github:agentrhq/webcmd/gemini ``` ## Commands diff --git a/plugins/geogebra/README.md b/plugins/geogebra/README.md index 73f81a8f..3f8295fb 100644 --- a/plugins/geogebra/README.md +++ b/plugins/geogebra/README.md @@ -5,7 +5,7 @@ Webcmd commands for geogebra. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/geogebra +webcmd plugin install github:agentrhq/webcmd/geogebra ``` ## Commands diff --git a/plugins/github-trending/README.md b/plugins/github-trending/README.md index 24305623..71b9f400 100644 --- a/plugins/github-trending/README.md +++ b/plugins/github-trending/README.md @@ -5,7 +5,7 @@ Webcmd commands for github-trending. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/github-trending +webcmd plugin install github:agentrhq/webcmd/github-trending ``` ## Commands diff --git a/plugins/github/README.md b/plugins/github/README.md index bd3ecc4b..74fc8a04 100644 --- a/plugins/github/README.md +++ b/plugins/github/README.md @@ -5,7 +5,7 @@ Webcmd commands for github. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/github +webcmd plugin install github:agentrhq/webcmd/github ``` ## Commands diff --git a/plugins/goettingen/README.md b/plugins/goettingen/README.md index 6d2b0447..4e013c2e 100644 --- a/plugins/goettingen/README.md +++ b/plugins/goettingen/README.md @@ -5,7 +5,7 @@ University of Göttingen postgraduate course export adapter. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/goettingen +webcmd plugin install github:agentrhq/webcmd/goettingen ``` ## Command diff --git a/plugins/goettingen/package.json b/plugins/goettingen/package.json index 50a7b45b..c2d63e14 100644 --- a/plugins/goettingen/package.json +++ b/plugins/goettingen/package.json @@ -5,5 +5,8 @@ "description": "University of Göttingen postgraduate course export adapter", "peerDependencies": { "@agentrhq/webcmd": ">=0.5.2" + }, + "dependencies": { + "undici": "^6.27.0" } } diff --git a/plugins/google-scholar/README.md b/plugins/google-scholar/README.md index d8f224f2..525d233e 100644 --- a/plugins/google-scholar/README.md +++ b/plugins/google-scholar/README.md @@ -5,7 +5,7 @@ Webcmd commands for google-scholar. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/google-scholar +webcmd plugin install github:agentrhq/webcmd/google-scholar ``` ## Commands diff --git a/plugins/google/README.md b/plugins/google/README.md index 0ceb3972..d5e3edb4 100644 --- a/plugins/google/README.md +++ b/plugins/google/README.md @@ -5,7 +5,7 @@ Webcmd commands for google. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/google +webcmd plugin install github:agentrhq/webcmd/google ``` ## Commands diff --git a/plugins/goproxy/README.md b/plugins/goproxy/README.md index c9a859b7..8162f7d1 100644 --- a/plugins/goproxy/README.md +++ b/plugins/goproxy/README.md @@ -5,7 +5,7 @@ Webcmd commands for goproxy. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/goproxy +webcmd plugin install github:agentrhq/webcmd/goproxy ``` ## Commands diff --git a/plugins/grok/README.md b/plugins/grok/README.md index f6875fc1..a49bd3f0 100644 --- a/plugins/grok/README.md +++ b/plugins/grok/README.md @@ -5,7 +5,7 @@ Webcmd commands for grok. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/grok +webcmd plugin install github:agentrhq/webcmd/grok ``` ## Commands diff --git a/plugins/hackernews/README.md b/plugins/hackernews/README.md index 47727023..280367c3 100644 --- a/plugins/hackernews/README.md +++ b/plugins/hackernews/README.md @@ -5,7 +5,7 @@ Webcmd commands for hackernews. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/hackernews +webcmd plugin install github:agentrhq/webcmd/hackernews ``` ## Commands diff --git a/plugins/heidelberg/README.md b/plugins/heidelberg/README.md index c2d0bcff..27502ddf 100644 --- a/plugins/heidelberg/README.md +++ b/plugins/heidelberg/README.md @@ -5,7 +5,7 @@ Heidelberg University postgraduate course export adapter. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/heidelberg +webcmd plugin install github:agentrhq/webcmd/heidelberg ``` ## Command diff --git a/plugins/heidelberg/package.json b/plugins/heidelberg/package.json index 9ed30a58..751d6878 100644 --- a/plugins/heidelberg/package.json +++ b/plugins/heidelberg/package.json @@ -5,5 +5,8 @@ "description": "Heidelberg University postgraduate course export adapter", "peerDependencies": { "@agentrhq/webcmd": ">=0.5.2" + }, + "dependencies": { + "undici": "^6.27.0" } } diff --git a/plugins/hf/README.md b/plugins/hf/README.md index 7f4eab26..74c32a13 100644 --- a/plugins/hf/README.md +++ b/plugins/hf/README.md @@ -5,7 +5,7 @@ Webcmd commands for hf. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/hf +webcmd plugin install github:agentrhq/webcmd/hf ``` ## Commands diff --git a/plugins/hft/README.md b/plugins/hft/README.md index 8fafa3e4..586d94a3 100644 --- a/plugins/hft/README.md +++ b/plugins/hft/README.md @@ -5,7 +5,7 @@ HFT Stuttgart postgraduate course export adapter. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/hft +webcmd plugin install github:agentrhq/webcmd/hft ``` ## Command diff --git a/plugins/hft/package.json b/plugins/hft/package.json index 6a8a89e5..2e295593 100644 --- a/plugins/hft/package.json +++ b/plugins/hft/package.json @@ -5,5 +5,8 @@ "description": "HFT Stuttgart postgraduate course export adapter", "peerDependencies": { "@agentrhq/webcmd": ">=0.5.2" + }, + "dependencies": { + "undici": "^6.27.0" } } diff --git a/plugins/homebrew/README.md b/plugins/homebrew/README.md index 87446015..6db0a4de 100644 --- a/plugins/homebrew/README.md +++ b/plugins/homebrew/README.md @@ -5,7 +5,7 @@ Webcmd commands for homebrew. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/homebrew +webcmd plugin install github:agentrhq/webcmd/homebrew ``` ## Commands diff --git a/plugins/iit/README.md b/plugins/iit/README.md index f6c50e91..562a5d72 100644 --- a/plugins/iit/README.md +++ b/plugins/iit/README.md @@ -5,7 +5,7 @@ Illinois Institute of Technology postgraduate course export adapter. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/iit +webcmd plugin install github:agentrhq/webcmd/iit ``` ## Command diff --git a/plugins/imdb/README.md b/plugins/imdb/README.md index 38dab5b4..1fbe9cf2 100644 --- a/plugins/imdb/README.md +++ b/plugins/imdb/README.md @@ -5,7 +5,7 @@ Webcmd commands for imdb. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/imdb +webcmd plugin install github:agentrhq/webcmd/imdb ``` ## Commands diff --git a/plugins/indeed/README.md b/plugins/indeed/README.md index 1fc6002a..5f944c27 100644 --- a/plugins/indeed/README.md +++ b/plugins/indeed/README.md @@ -5,7 +5,7 @@ Webcmd commands for indeed. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/indeed +webcmd plugin install github:agentrhq/webcmd/indeed ``` ## Commands diff --git a/plugins/instagram/README.md b/plugins/instagram/README.md index aadb67a0..e9bdaeac 100644 --- a/plugins/instagram/README.md +++ b/plugins/instagram/README.md @@ -5,7 +5,7 @@ Webcmd commands for instagram. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/instagram +webcmd plugin install github:agentrhq/webcmd/instagram ``` ## Commands diff --git a/plugins/jhu/README.md b/plugins/jhu/README.md index a72cbaef..2bc4370b 100644 --- a/plugins/jhu/README.md +++ b/plugins/jhu/README.md @@ -5,7 +5,7 @@ Johns Hopkins University postgraduate course export adapter. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/jhu +webcmd plugin install github:agentrhq/webcmd/jhu ``` ## Command diff --git a/plugins/jira/README.md b/plugins/jira/README.md index 1c9747f3..0f78ae98 100644 --- a/plugins/jira/README.md +++ b/plugins/jira/README.md @@ -5,7 +5,7 @@ Webcmd commands for jira. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/jira +webcmd plugin install github:agentrhq/webcmd/jira ``` ## Commands diff --git a/plugins/lesswrong/README.md b/plugins/lesswrong/README.md index 634cee21..9a2cabfe 100644 --- a/plugins/lesswrong/README.md +++ b/plugins/lesswrong/README.md @@ -5,7 +5,7 @@ Webcmd commands for lesswrong. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/lesswrong +webcmd plugin install github:agentrhq/webcmd/lesswrong ``` ## Commands diff --git a/plugins/lichess/README.md b/plugins/lichess/README.md index 5193b792..01c2427e 100644 --- a/plugins/lichess/README.md +++ b/plugins/lichess/README.md @@ -5,7 +5,7 @@ Webcmd commands for lichess. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/lichess +webcmd plugin install github:agentrhq/webcmd/lichess ``` ## Commands diff --git a/plugins/linkedin-learning/README.md b/plugins/linkedin-learning/README.md index 57799abf..bc49cdfc 100644 --- a/plugins/linkedin-learning/README.md +++ b/plugins/linkedin-learning/README.md @@ -5,7 +5,7 @@ Webcmd commands for linkedin-learning. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/linkedin-learning +webcmd plugin install github:agentrhq/webcmd/linkedin-learning ``` ## Commands diff --git a/plugins/linkedin/package.json b/plugins/linkedin/package.json index abfa5199..49c55c9a 100644 --- a/plugins/linkedin/package.json +++ b/plugins/linkedin/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "LinkedIn profile, network, messaging, job, and Sales Navigator commands for WebCMD", "peerDependencies": { - "@agentrhq/webcmd": ">=0.5.2" + "@agentrhq/webcmd": ">=0.6.0" } } diff --git a/plugins/linkedin/webcmd-plugin.json b/plugins/linkedin/webcmd-plugin.json index d0164be2..22cc0dbc 100644 --- a/plugins/linkedin/webcmd-plugin.json +++ b/plugins/linkedin/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "linkedin", "version": "0.1.0", "description": "LinkedIn profile, network, messaging, job, and Sales Navigator commands for WebCMD", - "webcmd": ">=0.5.2", + "webcmd": ">=0.6.0", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/lobsters/README.md b/plugins/lobsters/README.md index cc9bb02a..f4ff39c3 100644 --- a/plugins/lobsters/README.md +++ b/plugins/lobsters/README.md @@ -5,7 +5,7 @@ Webcmd commands for lobsters. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/lobsters +webcmd plugin install github:agentrhq/webcmd/lobsters ``` ## Commands diff --git a/plugins/manus/README.md b/plugins/manus/README.md index 769d22cf..d0e2c076 100644 --- a/plugins/manus/README.md +++ b/plugins/manus/README.md @@ -5,7 +5,7 @@ Webcmd commands for manus. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/manus +webcmd plugin install github:agentrhq/webcmd/manus ``` ## Commands diff --git a/plugins/maven/README.md b/plugins/maven/README.md index 6d5b8880..9179aa28 100644 --- a/plugins/maven/README.md +++ b/plugins/maven/README.md @@ -5,7 +5,7 @@ Webcmd commands for maven. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/maven +webcmd plugin install github:agentrhq/webcmd/maven ``` ## Commands diff --git a/plugins/mdn/README.md b/plugins/mdn/README.md index 18432a38..95566152 100644 --- a/plugins/mdn/README.md +++ b/plugins/mdn/README.md @@ -5,7 +5,7 @@ Webcmd commands for mdn. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/mdn +webcmd plugin install github:agentrhq/webcmd/mdn ``` ## Commands diff --git a/plugins/medium/README.md b/plugins/medium/README.md index 497a8c97..167ea69a 100644 --- a/plugins/medium/README.md +++ b/plugins/medium/README.md @@ -5,7 +5,7 @@ Webcmd commands for medium. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/medium +webcmd plugin install github:agentrhq/webcmd/medium ``` ## Commands diff --git a/plugins/mercury/README.md b/plugins/mercury/README.md index b396481a..91225cab 100644 --- a/plugins/mercury/README.md +++ b/plugins/mercury/README.md @@ -5,7 +5,7 @@ Webcmd commands for mercury. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/mercury +webcmd plugin install github:agentrhq/webcmd/mercury ``` ## Commands diff --git a/plugins/notebooklm/README.md b/plugins/notebooklm/README.md index c9eb75ff..409fd005 100644 --- a/plugins/notebooklm/README.md +++ b/plugins/notebooklm/README.md @@ -5,7 +5,7 @@ Webcmd commands for notebooklm. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/notebooklm +webcmd plugin install github:agentrhq/webcmd/notebooklm ``` ## Commands diff --git a/plugins/npm/README.md b/plugins/npm/README.md index db79befd..43e11f87 100644 --- a/plugins/npm/README.md +++ b/plugins/npm/README.md @@ -5,7 +5,7 @@ Webcmd commands for npm. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/npm +webcmd plugin install github:agentrhq/webcmd/npm ``` ## Commands diff --git a/plugins/nuget/README.md b/plugins/nuget/README.md index b39b2b49..fa6fe930 100644 --- a/plugins/nuget/README.md +++ b/plugins/nuget/README.md @@ -5,7 +5,7 @@ Webcmd commands for nuget. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/nuget +webcmd plugin install github:agentrhq/webcmd/nuget ``` ## Commands diff --git a/plugins/nvd/README.md b/plugins/nvd/README.md index 9055a120..e251c1b7 100644 --- a/plugins/nvd/README.md +++ b/plugins/nvd/README.md @@ -5,7 +5,7 @@ Webcmd commands for nvd. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/nvd +webcmd plugin install github:agentrhq/webcmd/nvd ``` ## Commands diff --git a/plugins/oeis/README.md b/plugins/oeis/README.md index 01fbd8e1..23317f38 100644 --- a/plugins/oeis/README.md +++ b/plugins/oeis/README.md @@ -5,7 +5,7 @@ Webcmd commands for oeis. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/oeis +webcmd plugin install github:agentrhq/webcmd/oeis ``` ## Commands diff --git a/plugins/openalex/README.md b/plugins/openalex/README.md index 42e9d9ae..d59f63c2 100644 --- a/plugins/openalex/README.md +++ b/plugins/openalex/README.md @@ -5,7 +5,7 @@ Webcmd commands for openalex. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/openalex +webcmd plugin install github:agentrhq/webcmd/openalex ``` ## Commands diff --git a/plugins/openfda/README.md b/plugins/openfda/README.md index a2e6d0b8..1b0f475b 100644 --- a/plugins/openfda/README.md +++ b/plugins/openfda/README.md @@ -5,7 +5,7 @@ Webcmd commands for openfda. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/openfda +webcmd plugin install github:agentrhq/webcmd/openfda ``` ## Commands diff --git a/plugins/openreview/README.md b/plugins/openreview/README.md index b2e048da..42b77adf 100644 --- a/plugins/openreview/README.md +++ b/plugins/openreview/README.md @@ -5,7 +5,7 @@ Webcmd commands for openreview. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/openreview +webcmd plugin install github:agentrhq/webcmd/openreview ``` ## Commands diff --git a/plugins/osv/README.md b/plugins/osv/README.md index 2c04eee3..a5e84829 100644 --- a/plugins/osv/README.md +++ b/plugins/osv/README.md @@ -5,7 +5,7 @@ Webcmd commands for osv. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/osv +webcmd plugin install github:agentrhq/webcmd/osv ``` ## Commands diff --git a/plugins/packagist/README.md b/plugins/packagist/README.md index 100cc0f2..f19aca0e 100644 --- a/plugins/packagist/README.md +++ b/plugins/packagist/README.md @@ -5,7 +5,7 @@ Webcmd commands for packagist. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/packagist +webcmd plugin install github:agentrhq/webcmd/packagist ``` ## Commands diff --git a/plugins/paperreview/README.md b/plugins/paperreview/README.md index 868805ce..aeb25c4d 100644 --- a/plugins/paperreview/README.md +++ b/plugins/paperreview/README.md @@ -5,7 +5,7 @@ Webcmd commands for paperreview. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/paperreview +webcmd plugin install github:agentrhq/webcmd/paperreview ``` ## Commands diff --git a/plugins/pixiv/README.md b/plugins/pixiv/README.md index b7a371fb..da5e3143 100644 --- a/plugins/pixiv/README.md +++ b/plugins/pixiv/README.md @@ -5,7 +5,7 @@ Webcmd commands for pixiv. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/pixiv +webcmd plugin install github:agentrhq/webcmd/pixiv ``` ## Commands diff --git a/plugins/practo/README.md b/plugins/practo/README.md index bd8237aa..0408201d 100644 --- a/plugins/practo/README.md +++ b/plugins/practo/README.md @@ -5,7 +5,7 @@ Webcmd commands for practo. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/practo +webcmd plugin install github:agentrhq/webcmd/practo ``` ## Commands diff --git a/plugins/producthunt/README.md b/plugins/producthunt/README.md index 458b56e9..68326709 100644 --- a/plugins/producthunt/README.md +++ b/plugins/producthunt/README.md @@ -5,7 +5,7 @@ Webcmd commands for producthunt. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/producthunt +webcmd plugin install github:agentrhq/webcmd/producthunt ``` ## Commands diff --git a/plugins/pubmed/README.md b/plugins/pubmed/README.md index 07dafd87..e75fae39 100644 --- a/plugins/pubmed/README.md +++ b/plugins/pubmed/README.md @@ -5,7 +5,7 @@ Webcmd commands for pubmed. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/pubmed +webcmd plugin install github:agentrhq/webcmd/pubmed ``` ## Commands diff --git a/plugins/pypi/README.md b/plugins/pypi/README.md index ef7b40ee..47683677 100644 --- a/plugins/pypi/README.md +++ b/plugins/pypi/README.md @@ -6,7 +6,7 @@ API key is required. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/pypi +webcmd plugin install github:agentrhq/webcmd/pypi ``` ## Commands diff --git a/plugins/qoder/README.md b/plugins/qoder/README.md index 76d6499c..06fb45ab 100644 --- a/plugins/qoder/README.md +++ b/plugins/qoder/README.md @@ -5,7 +5,7 @@ Webcmd commands for qoder. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/qoder +webcmd plugin install github:agentrhq/webcmd/qoder ``` ## Commands diff --git a/plugins/reddit/README.md b/plugins/reddit/README.md index f0a40595..0c0f5a1e 100644 --- a/plugins/reddit/README.md +++ b/plugins/reddit/README.md @@ -5,7 +5,7 @@ Webcmd commands for reddit. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/reddit +webcmd plugin install github:agentrhq/webcmd/reddit ``` ## Commands diff --git a/plugins/rest-countries/README.md b/plugins/rest-countries/README.md index 37e83a70..37aaa102 100644 --- a/plugins/rest-countries/README.md +++ b/plugins/rest-countries/README.md @@ -5,7 +5,7 @@ Webcmd commands for rest-countries. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/rest-countries +webcmd plugin install github:agentrhq/webcmd/rest-countries ``` ## Commands diff --git a/plugins/reuters/README.md b/plugins/reuters/README.md index 0f3e6b27..19dec53a 100644 --- a/plugins/reuters/README.md +++ b/plugins/reuters/README.md @@ -5,7 +5,7 @@ Webcmd commands for reuters. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/reuters +webcmd plugin install github:agentrhq/webcmd/reuters ``` ## Commands diff --git a/plugins/rfc/README.md b/plugins/rfc/README.md index 0f66be4e..7a6047e8 100644 --- a/plugins/rfc/README.md +++ b/plugins/rfc/README.md @@ -5,7 +5,7 @@ Webcmd commands for rfc. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/rfc +webcmd plugin install github:agentrhq/webcmd/rfc ``` ## Commands diff --git a/plugins/rubygems/README.md b/plugins/rubygems/README.md index 3eb20d5b..2cc14135 100644 --- a/plugins/rubygems/README.md +++ b/plugins/rubygems/README.md @@ -5,7 +5,7 @@ Webcmd commands for rubygems. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/rubygems +webcmd plugin install github:agentrhq/webcmd/rubygems ``` ## Commands diff --git a/plugins/semanticscholar/README.md b/plugins/semanticscholar/README.md index faa6adfb..de1d33e2 100644 --- a/plugins/semanticscholar/README.md +++ b/plugins/semanticscholar/README.md @@ -5,7 +5,7 @@ Webcmd commands for semanticscholar. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/semanticscholar +webcmd plugin install github:agentrhq/webcmd/semanticscholar ``` ## Commands diff --git a/plugins/slock/README.md b/plugins/slock/README.md index 23f31d5a..cd15f6b3 100644 --- a/plugins/slock/README.md +++ b/plugins/slock/README.md @@ -5,7 +5,7 @@ Webcmd commands for slock. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/slock +webcmd plugin install github:agentrhq/webcmd/slock ``` ## Commands diff --git a/plugins/spotify/README.md b/plugins/spotify/README.md index 5b1512bb..ea3183d6 100644 --- a/plugins/spotify/README.md +++ b/plugins/spotify/README.md @@ -5,7 +5,7 @@ Webcmd commands for spotify. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/spotify +webcmd plugin install github:agentrhq/webcmd/spotify ``` ## Commands diff --git a/plugins/stackoverflow/README.md b/plugins/stackoverflow/README.md index 9f1192fd..5cf72b2b 100644 --- a/plugins/stackoverflow/README.md +++ b/plugins/stackoverflow/README.md @@ -5,7 +5,7 @@ Webcmd commands for stackoverflow. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/stackoverflow +webcmd plugin install github:agentrhq/webcmd/stackoverflow ``` ## Commands diff --git a/plugins/steam/README.md b/plugins/steam/README.md index 5fe3252c..6534f861 100644 --- a/plugins/steam/README.md +++ b/plugins/steam/README.md @@ -5,7 +5,7 @@ Webcmd commands for steam. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/steam +webcmd plugin install github:agentrhq/webcmd/steam ``` ## Commands diff --git a/plugins/substack/README.md b/plugins/substack/README.md index 12f4a044..3eba2ee1 100644 --- a/plugins/substack/README.md +++ b/plugins/substack/README.md @@ -5,7 +5,7 @@ Webcmd commands for substack. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/substack +webcmd plugin install github:agentrhq/webcmd/substack ``` ## Commands diff --git a/plugins/suno/README.md b/plugins/suno/README.md index 0bc39d84..0bbffe0c 100644 --- a/plugins/suno/README.md +++ b/plugins/suno/README.md @@ -5,7 +5,7 @@ Webcmd commands for suno. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/suno +webcmd plugin install github:agentrhq/webcmd/suno ``` ## Commands diff --git a/plugins/techcrunch/README.md b/plugins/techcrunch/README.md index e18e1a85..551d0800 100644 --- a/plugins/techcrunch/README.md +++ b/plugins/techcrunch/README.md @@ -5,7 +5,7 @@ Search and read TechCrunch stories through WebCMD using TechCrunch's public API. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/techcrunch +webcmd plugin install github:agentrhq/webcmd/techcrunch ``` ## Commands diff --git a/plugins/tiktok/README.md b/plugins/tiktok/README.md index 2690641c..e0dbeb5a 100644 --- a/plugins/tiktok/README.md +++ b/plugins/tiktok/README.md @@ -5,7 +5,7 @@ Webcmd commands for tiktok. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/tiktok +webcmd plugin install github:agentrhq/webcmd/tiktok ``` ## Commands diff --git a/plugins/trae-solo/README.md b/plugins/trae-solo/README.md index 70972d5e..a81f1a82 100644 --- a/plugins/trae-solo/README.md +++ b/plugins/trae-solo/README.md @@ -5,7 +5,7 @@ Webcmd commands for trae-solo. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/trae-solo +webcmd plugin install github:agentrhq/webcmd/trae-solo ``` ## Commands diff --git a/plugins/trip/README.md b/plugins/trip/README.md index e1de2da4..20a85283 100644 --- a/plugins/trip/README.md +++ b/plugins/trip/README.md @@ -5,7 +5,7 @@ Webcmd commands for trip. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/trip +webcmd plugin install github:agentrhq/webcmd/trip ``` ## Commands diff --git a/plugins/tvmaze/README.md b/plugins/tvmaze/README.md index 0ad824fd..f8e54d09 100644 --- a/plugins/tvmaze/README.md +++ b/plugins/tvmaze/README.md @@ -5,7 +5,7 @@ Webcmd commands for tvmaze. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/tvmaze +webcmd plugin install github:agentrhq/webcmd/tvmaze ``` ## Commands diff --git a/plugins/twitter/README.md b/plugins/twitter/README.md index 77905a37..a6f9208a 100644 --- a/plugins/twitter/README.md +++ b/plugins/twitter/README.md @@ -5,7 +5,7 @@ Webcmd commands for twitter. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/twitter +webcmd plugin install github:agentrhq/webcmd/twitter ``` ## Commands diff --git a/plugins/ualberta/README.md b/plugins/ualberta/README.md index 15b5a65c..56985903 100644 --- a/plugins/ualberta/README.md +++ b/plugins/ualberta/README.md @@ -5,7 +5,7 @@ University of Alberta postgraduate course export adapter. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/ualberta +webcmd plugin install github:agentrhq/webcmd/ualberta ``` ## Command diff --git a/plugins/uiverse/README.md b/plugins/uiverse/README.md index d229cce5..28068375 100644 --- a/plugins/uiverse/README.md +++ b/plugins/uiverse/README.md @@ -5,7 +5,7 @@ Webcmd commands for uiverse. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/uiverse +webcmd plugin install github:agentrhq/webcmd/uiverse ``` ## Commands diff --git a/plugins/upwork/README.md b/plugins/upwork/README.md index ea314e05..da1d5a38 100644 --- a/plugins/upwork/README.md +++ b/plugins/upwork/README.md @@ -5,7 +5,7 @@ Webcmd commands for upwork. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/upwork +webcmd plugin install github:agentrhq/webcmd/upwork ``` ## Commands diff --git a/plugins/web/README.md b/plugins/web/README.md index 90009b99..cb431d77 100644 --- a/plugins/web/README.md +++ b/plugins/web/README.md @@ -5,7 +5,7 @@ Webcmd commands for web. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/web +webcmd plugin install github:agentrhq/webcmd/web ``` ## Commands diff --git a/plugins/wikidata/README.md b/plugins/wikidata/README.md index 0f8a8ea4..dfe74afb 100644 --- a/plugins/wikidata/README.md +++ b/plugins/wikidata/README.md @@ -5,7 +5,7 @@ Webcmd commands for wikidata. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/wikidata +webcmd plugin install github:agentrhq/webcmd/wikidata ``` ## Commands diff --git a/plugins/wikipedia/README.md b/plugins/wikipedia/README.md index 02b4a212..d1ff534f 100644 --- a/plugins/wikipedia/README.md +++ b/plugins/wikipedia/README.md @@ -5,7 +5,7 @@ Webcmd commands for wikipedia. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/wikipedia +webcmd plugin install github:agentrhq/webcmd/wikipedia ``` ## Commands diff --git a/plugins/wttr/README.md b/plugins/wttr/README.md index 4ed83852..74e753b0 100644 --- a/plugins/wttr/README.md +++ b/plugins/wttr/README.md @@ -5,7 +5,7 @@ Webcmd commands for wttr. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/wttr +webcmd plugin install github:agentrhq/webcmd/wttr ``` ## Commands diff --git a/plugins/yahoo-finance/README.md b/plugins/yahoo-finance/README.md index 9dc0cfdb..1beaeb2a 100644 --- a/plugins/yahoo-finance/README.md +++ b/plugins/yahoo-finance/README.md @@ -5,7 +5,7 @@ Webcmd commands for yahoo-finance. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/yahoo-finance +webcmd plugin install github:agentrhq/webcmd/yahoo-finance ``` ## Commands diff --git a/plugins/yahoo/README.md b/plugins/yahoo/README.md index ff828970..94f8fde2 100644 --- a/plugins/yahoo/README.md +++ b/plugins/yahoo/README.md @@ -5,7 +5,7 @@ Webcmd commands for yahoo. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/yahoo +webcmd plugin install github:agentrhq/webcmd/yahoo ``` ## Commands diff --git a/plugins/yale/README.md b/plugins/yale/README.md index 9eab4091..54fbbbe0 100644 --- a/plugins/yale/README.md +++ b/plugins/yale/README.md @@ -5,7 +5,7 @@ Yale University postgraduate course export adapter. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/yale +webcmd plugin install github:agentrhq/webcmd/yale ``` ## Command diff --git a/plugins/ycombinator/README.md b/plugins/ycombinator/README.md index c8588870..2bf1d799 100644 --- a/plugins/ycombinator/README.md +++ b/plugins/ycombinator/README.md @@ -5,7 +5,7 @@ Read-only access to the public Y Combinator startup directory. No login is requi ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/ycombinator +webcmd plugin install github:agentrhq/webcmd/ycombinator ``` ## Commands diff --git a/plugins/yollomi/README.md b/plugins/yollomi/README.md index 971b6c08..438800b5 100644 --- a/plugins/yollomi/README.md +++ b/plugins/yollomi/README.md @@ -5,7 +5,7 @@ Webcmd commands for yollomi. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/yollomi +webcmd plugin install github:agentrhq/webcmd/yollomi ``` ## Commands diff --git a/plugins/youtube/README.md b/plugins/youtube/README.md index 22a927df..31c04070 100644 --- a/plugins/youtube/README.md +++ b/plugins/youtube/README.md @@ -5,7 +5,7 @@ Webcmd commands for youtube. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/youtube +webcmd plugin install github:agentrhq/webcmd/youtube ``` ## Commands diff --git a/plugins/zepto/README.md b/plugins/zepto/README.md index 03d47366..7f996be2 100644 --- a/plugins/zepto/README.md +++ b/plugins/zepto/README.md @@ -5,7 +5,7 @@ Webcmd commands for zepto. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/zepto +webcmd plugin install github:agentrhq/webcmd/zepto ``` ## Commands diff --git a/plugins/zlibrary/README.md b/plugins/zlibrary/README.md index 36560bad..620d8786 100644 --- a/plugins/zlibrary/README.md +++ b/plugins/zlibrary/README.md @@ -5,7 +5,7 @@ Webcmd commands for zlibrary. ## Install ```bash -webcmd plugin install github:agentrhq/webcmd/plugins/zlibrary +webcmd plugin install github:agentrhq/webcmd/zlibrary ``` ## Commands diff --git a/scripts/migrate-cli-sites.mjs b/scripts/migrate-cli-sites.mjs index 3d31b3ef..b09f7a4c 100644 --- a/scripts/migrate-cli-sites.mjs +++ b/scripts/migrate-cli-sites.mjs @@ -8,6 +8,8 @@ const sites = process.argv.slice(2); const sharedRuntime = /((?:\.\.\/)+)_shared\/(?:common|desktop-commands|search-adapter|site-auth)\.js/g; if (sites.length === 0) fail('Usage: node scripts/migrate-cli-sites.mjs '); +const duplicate = sites.find((site, index) => sites.indexOf(site) !== index); +if (duplicate) fail(`Duplicate site name: ${duplicate}`); for (const site of sites) { if (!/^[a-z0-9][a-z0-9-]*$/.test(site)) fail(`Invalid site name: ${site}`); if (!fs.existsSync(path.join(root, 'clis', site))) fail(`clis/${site} does not exist`); @@ -119,7 +121,7 @@ function readme(site, description, commands) { .slice() .sort((a, b) => String(a.name).localeCompare(String(b.name))) .map(command => `| \`webcmd ${site} ${command.name}\` | ${String(command.description ?? '').replaceAll('|', '\\|')} |`); - return `# webcmd-plugin-${site}\n\n${description}.\n\n## Install\n\n\`\`\`bash\nwebcmd plugin install github:agentrhq/webcmd/plugins/${site}\n\`\`\`\n\n## Commands\n\n| Command | Description |\n| --- | --- |\n${rows.join('\n')}\n`; + return `# webcmd-plugin-${site}\n\n${description}.\n\n## Install\n\n\`\`\`bash\nwebcmd plugin install github:agentrhq/webcmd/${site}\n\`\`\`\n\n## Commands\n\n| Command | Description |\n| --- | --- |\n${rows.join('\n')}\n`; } function readJson(file, fallback) { diff --git a/scripts/postinstall.js b/scripts/postinstall.js index c59b0a4c..70bbff5f 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -143,28 +143,11 @@ function main() { } } - // ── Spotify credentials template ──────────────────────────────────── - const webcmdDir = join(home, '.webcmd'); - const spotifyEnvFile = join(webcmdDir, 'spotify.env'); - ensureDir(webcmdDir); - if (!existsSync(spotifyEnvFile)) { - writeFileSync(spotifyEnvFile, - `# Spotify credentials — get them at https://developer.spotify.com/dashboard\n` + - `# Add http://127.0.0.1:8888/callback as a Redirect URI in your Spotify app\n` + - `SPOTIFY_CLIENT_ID=your_spotify_client_id_here\n` + - `SPOTIFY_CLIENT_SECRET=your_spotify_client_secret_here\n`, - 'utf8' - ); - console.log(`✓ Spotify credentials template created at ${spotifyEnvFile}`); - console.log(` Edit the file and add your Client ID and Secret, then run: webcmd spotify auth`); - } - - // ── Browser runtime setup hint ────────────────────────────────────── + // ── Plugin discovery hint ─────────────────────────────────────────── console.log(''); - console.log(' \x1b[1mNext step — Browser runtime setup\x1b[0m'); - console.log(' Browser commands (youtube, reddit, twitter...) use a webcmd-managed CloakBrowser runtime.'); - console.log(' On first use, CloakBrowser downloads its Chromium binary.'); - console.log(' Existing Chrome logins are not imported, so run the site login command again when needed.'); + console.log(' \x1b[1mNext step — install a site plugin\x1b[0m'); + console.log(' Search: \x1b[36mwebcmd plugin search -f json\x1b[0m'); + console.log(' Install: \x1b[36mwebcmd plugin install \x1b[0m'); console.log(''); console.log(' Then run \x1b[36mwebcmd doctor\x1b[0m to verify.'); console.log(''); diff --git a/src/build-plugin-command-manifest.test.ts b/src/build-plugin-command-manifest.test.ts index 60545010..d9e0425c 100644 --- a/src/build-plugin-command-manifest.test.ts +++ b/src/build-plugin-command-manifest.test.ts @@ -4,6 +4,7 @@ import * as os from 'node:os'; import * as path from 'node:path'; import { pathToFileURL } from 'node:url'; import { afterEach, describe, expect, it } from 'vitest'; +import yaml from 'js-yaml'; import type { ManifestEntry } from './manifest-types.js'; const roots: string[] = []; @@ -197,4 +198,47 @@ describe('plugin command manifest', () => { expect(manifest.engines?.node).toBe('>=20.6.0'); }); + + it('documents the same minimum Node version as the package', () => { + expect(fs.readFileSync('README.md', 'utf8')).toContain('Node.js 20.6+'); + expect(fs.readFileSync('docs/quickstart.mdx', 'utf8')).toContain('Node.js 20.6+'); + }); + + it.each(['goettingen', 'heidelberg', 'hft'])('%s declares its direct undici import', (site) => { + const manifest = JSON.parse(fs.readFileSync(`plugins/${site}/package.json`, 'utf8')) as { + dependencies?: Record; + }; + + expect(manifest.dependencies?.undici).toBe('^6.27.0'); + }); + + it('requires the plugin-runtime release for LinkedIn', () => { + const packageManifest = JSON.parse(fs.readFileSync('plugins/linkedin/package.json', 'utf8')) as { + peerDependencies?: Record; + }; + const pluginManifest = JSON.parse(fs.readFileSync('plugins/linkedin/webcmd-plugin.json', 'utf8')) as { + webcmd?: string; + }; + const rootManifest = JSON.parse(fs.readFileSync('webcmd-plugin.json', 'utf8')) as { + plugins?: Record; + }; + + expect(packageManifest.peerDependencies?.['@agentrhq/webcmd']).toBe('>=0.6.0'); + expect(pluginManifest.webcmd).toBe('>=0.6.0'); + expect(rootManifest.plugins?.linkedin?.webcmd).toBe('>=0.6.0'); + }); + + it.each([ + ['CI', '.github/workflows/ci.yml', 'build'], + ['release', '.github/workflows/release.yml', 'release'], + ])('checks plugin parity immediately after manifest generation in %s', (_name, workflowPath, jobName) => { + const workflow = yaml.load(fs.readFileSync(workflowPath, 'utf8')) as { + jobs?: Record }>; + }; + const runs = workflow.jobs?.[jobName]?.steps?.map(step => step.run).filter(Boolean) ?? []; + const generation = runs.indexOf('npm run build-plugin-manifest'); + + expect(generation).toBeGreaterThanOrEqual(0); + expect(runs[generation + 1]).toBe('npm run check:plugin-parity'); + }); }); diff --git a/src/cli.test.ts b/src/cli.test.ts index 5963aa90..9a21bf8e 100644 --- a/src/cli.test.ts +++ b/src/cli.test.ts @@ -76,6 +76,45 @@ describe('Antigravity serve plugin loading', () => { fs.rmSync(pluginsDir, { recursive: true, force: true }); } }); + + it('omits the serve bridge and uses missing-plugin guidance when Antigravity is absent', async () => { + const pluginsDir = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-antigravity-absent-')); + const registry = getRegistry(); + const snapshot = new Map(registry); + const stderr = vi.spyOn(console, 'error').mockImplementation(() => {}); + const previousExitCode = process.exitCode; + registry.clear(); + try { + const program = createProgram('', '', pluginsDir); + program.outputHelp = vi.fn(); + + await program.parseAsync(['antigravity', 'serve'], { from: 'user' }); + + expect(program.commands.some(command => command.name() === 'antigravity')).toBe(false); + expect(stderr.mock.calls.map(([line]) => line).join('\n')).toContain('Search: webcmd plugin search antigravity'); + expect(process.exitCode).toBe(2); + } finally { + process.exitCode = previousExitCode; + stderr.mockRestore(); + registry.clear(); + for (const [key, value] of snapshot) registry.set(key, value); + fs.rmSync(pluginsDir, { recursive: true, force: true }); + } + }); + + it('registers the serve bridge when the installed Antigravity module exists', () => { + const pluginsDir = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-antigravity-present-')); + const pluginDir = path.join(pluginsDir, 'antigravity'); + fs.mkdirSync(pluginDir); + fs.writeFileSync(path.join(pluginDir, 'serve.js'), 'export async function startServe() {}\n'); + try { + const antigravity = createProgram('', '', pluginsDir).commands.find(command => command.name() === 'antigravity'); + + expect(antigravity?.commands.map(command => command.name())).toContain('serve'); + } finally { + fs.rmSync(pluginsDir, { recursive: true, force: true }); + } + }); }); describe('createProgram root help descriptions', () => { diff --git a/src/cli.ts b/src/cli.ts index 978298f8..e200e770 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -781,7 +781,7 @@ function applyRootSubcommandSummaries(program: Command): void { } } -export function createProgram(BUILTIN_CLIS: string, USER_CLIS: string): Command { +export function createProgram(BUILTIN_CLIS: string, USER_CLIS: string, pluginsDir: string = PLUGINS_DIR): Command { const program = new Command(); // enablePositionalOptions: prevents parent from consuming flags meant for subcommands; // prerequisite for passThroughOptions to forward --help/--version to external binaries @@ -3617,24 +3617,26 @@ cli({ // ── Antigravity serve (long-running, special case) ──────────────────────── - const antigravityCmd = program.command('antigravity').description('antigravity commands'); - antigravityCmd - .command('serve') - .description('Start Anthropic-compatible API proxy for Antigravity') - .option('--port ', 'Server port (default: 8082)', '8082') - .option('--timeout ', 'Maximum time to wait for a reply (default: 120s)') - .action(async (opts) => { - const { startServe } = await loadAntigravityServe(); - await startServe({ - port: parseInt(opts.port, 10), - timeout: opts.timeout ? parsePositiveIntOption(opts.timeout, '--timeout', 120) : undefined, + const siteGroups = new Map(); + if (fs.existsSync(path.join(pluginsDir, 'antigravity', 'serve.js'))) { + const antigravityCmd = program.command('antigravity').description('antigravity commands'); + antigravityCmd + .command('serve') + .description('Start Anthropic-compatible API proxy for Antigravity') + .option('--port ', 'Server port (default: 8082)', '8082') + .option('--timeout ', 'Maximum time to wait for a reply (default: 120s)') + .action(async (opts) => { + const { startServe } = await loadAntigravityServe(pluginsDir); + await startServe({ + port: parseInt(opts.port, 10), + timeout: opts.timeout ? parsePositiveIntOption(opts.timeout, '--timeout', 120) : undefined, + }); }); - }); + siteGroups.set('antigravity', antigravityCmd); + } // ── Dynamic adapter commands ────────────────────────────────────────────── - const siteGroups = new Map(); - siteGroups.set('antigravity', antigravityCmd); const siteNames = registerAllCommands(program, siteGroups); applyRootSubcommandSummaries(program); diff --git a/src/migrate-cli-sites.test.ts b/src/migrate-cli-sites.test.ts index 64f0e083..b8f03fa6 100644 --- a/src/migrate-cli-sites.test.ts +++ b/src/migrate-cli-sites.test.ts @@ -3,6 +3,7 @@ import * as fs from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; +import { _parseSource } from './plugin.js'; const script = path.resolve('scripts/migrate-cli-sites.mjs'); const roots: string[] = []; @@ -69,7 +70,11 @@ describe('migrate-cli-sites', () => { webcmd: '>=0.6.0', author: { name: 'WebCMD Agent', handle: 'agentrhq' }, }); - expect(fs.readFileSync(path.join(plugin, 'README.md'), 'utf8')).toContain('| `webcmd example search` | Search examples |'); + const readme = fs.readFileSync(path.join(plugin, 'README.md'), 'utf8'); + expect(readme).toContain('| `webcmd example search` | Search examples |'); + const installSource = readme.match(/webcmd plugin install (\S+)/)?.[1]; + expect(installSource).toBe('github:agentrhq/webcmd/example'); + expect(_parseSource(installSource!)).not.toBeNull(); expect(fs.readFileSync(path.join(root, 'plugins', 'sibling', 'keep.txt'), 'utf8')).toBe('unchanged\n'); expect(fs.readFileSync(path.join(root, 'scripts', 'silent-column-drop-baseline.json'), 'utf8')).toContain('plugins/example/search.js'); expect(fs.readFileSync(path.join(root, 'scripts', 'typed-error-lint-baseline.json'), 'utf8')).toContain('plugins/example/search.js'); @@ -88,6 +93,17 @@ describe('migrate-cli-sites', () => { expect(fs.existsSync(path.join(root, 'clis', 'example', 'search.js'))).toBe(true); }); + it('refuses duplicate site arguments before moving anything', () => { + const root = fixture(); + + const result = spawnSync(process.execPath, [script, 'example', 'example'], { cwd: root, encoding: 'utf8' }); + + expect(result.status).toBe(1); + expect(result.stderr).toContain('Duplicate site name: example'); + expect(fs.existsSync(path.join(root, 'clis', 'example', 'search.js'))).toBe(true); + expect(fs.existsSync(path.join(root, 'plugins', 'example'))).toBe(false); + }); + it('allows the planned PyPI merge and preserves existing plugin-only files', () => { const root = fixture(); fs.renameSync(path.join(root, 'clis', 'example'), path.join(root, 'clis', 'pypi')); diff --git a/src/postinstall.test.ts b/src/postinstall.test.ts new file mode 100644 index 00000000..567cba7c --- /dev/null +++ b/src/postinstall.test.ts @@ -0,0 +1,30 @@ +import { spawnSync } from 'node:child_process'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import { afterEach, describe, expect, it } from 'vitest'; + +const roots: string[] = []; + +afterEach(() => { + for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true }); +}); + +describe('postinstall', () => { + it('installs only core completion files and prints explicit plugin guidance', () => { + const home = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-postinstall-')); + roots.push(home); + const env: NodeJS.ProcessEnv = { ...process.env, HOME: home, SHELL: '/bin/zsh', npm_config_global: 'true' }; + delete env.CI; + delete env.CONTINUOUS_INTEGRATION; + + const result = spawnSync(process.execPath, ['scripts/postinstall.js'], { env, encoding: 'utf8' }); + + expect(result.status).toBe(0); + expect(fs.existsSync(path.join(home, '.zsh', 'completions', '_webcmd'))).toBe(true); + expect(fs.existsSync(path.join(home, '.webcmd', 'spotify.env'))).toBe(false); + expect(result.stdout).toContain('webcmd plugin search -f json'); + expect(result.stdout).toContain('webcmd plugin install '); + expect(result.stdout).not.toMatch(/spotify|youtube|reddit|twitter/i); + }); +}); diff --git a/src/release-notes.test.ts b/src/release-notes.test.ts index a989419a..45c4577e 100644 --- a/src/release-notes.test.ts +++ b/src/release-notes.test.ts @@ -261,7 +261,7 @@ describe('release notes helpers', () => { expect(prompt).toContain('Omit empty sections entirely'); expect(prompt).toContain('Do not include a Contributors section'); expect(prompt).toContain('CLI commands and adapters are the same thing'); - expect(prompt).toContain('files under clis/** as an adapter change'); + expect(prompt).toContain('files under clis/** or plugins/** as an adapter change'); expect(prompt).toContain('Put new site adapters/CLIs, adapter promotions, adapter hardening'); expect(prompt).toContain('## Reverts'); }); diff --git a/src/release-notes.ts b/src/release-notes.ts index d2b318c2..80d8a6d4 100644 --- a/src/release-notes.ts +++ b/src/release-notes.ts @@ -425,7 +425,7 @@ export function buildReleaseNotesPrompt(context: ReleaseContext): string { ...majorReleaseInstructions, 'Include only sections that have user-visible changes. Omit empty sections entirely; do not write "None", "N/A", or similar placeholder text.', 'Do not include a Contributors section.', - 'In this project, CLI commands and adapters are the same thing. Treat any PR that adds, removes, or changes files under clis/** as an adapter change, even if the PR title says "CLI" instead of "adapter".', + 'In this project, CLI commands and adapters are the same thing. Treat any PR that adds, removes, or changes files under clis/** or plugins/** as an adapter change, even if the PR title says "CLI" instead of "adapter".', 'Put new site adapters/CLIs, adapter promotions, adapter hardening, adapter output changes, selector/API updates, and site-specific workflow improvements in ## Adapters.', 'Use ## Improvements for non-adapter product, runtime, CLI, docs, or workflow improvements.', 'Use ## Reverts only when the release includes actual reverted changes.', diff --git a/webcmd-plugin.json b/webcmd-plugin.json index 7ced18b3..52d01f64 100644 --- a/webcmd-plugin.json +++ b/webcmd-plugin.json @@ -638,7 +638,7 @@ "path": "plugins/linkedin", "version": "0.1.0", "description": "LinkedIn profile, network, messaging, job, and Sales Navigator commands for WebCMD", - "webcmd": ">=0.5.2", + "webcmd": ">=0.6.0", "author": { "name": "WebCMD Agent", "handle": "agentrhq" From 9c01da4c6dd4cd117e45c9d79852f6eea0267a6e Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 19:20:17 +0530 Subject: [PATCH 22/39] test: make CI portability checks deterministic --- src/migrate-cli-sites.test.ts | 2 +- src/package-exports.test.ts | 20 ++++++-------------- src/postinstall.test.ts | 8 +++++++- vitest.config.ts | 16 ++++++++++++++++ 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/migrate-cli-sites.test.ts b/src/migrate-cli-sites.test.ts index b8f03fa6..369f3ff0 100644 --- a/src/migrate-cli-sites.test.ts +++ b/src/migrate-cli-sites.test.ts @@ -132,7 +132,7 @@ describe('migrate-cli-sites', () => { const result = spawnSync(process.execPath, [script, 'pypi'], { cwd: root, encoding: 'utf8' }); expect(result.status).toBe(1); - expect(result.stderr).toContain('plugins/pypi/search.js already exists; merge pypi manually'); + expect(result.stderr).toContain(`${path.join('plugins', 'pypi', 'search.js')} already exists; merge pypi manually`); expect(fs.readFileSync(path.join(root, 'plugins', 'pypi', 'search.js'), 'utf8')).toBe('existing plugin command\n'); expect(fs.readFileSync(path.join(root, 'plugins', 'pypi', 'webcmd-plugin.json'), 'utf8')).toContain('Kemal Kaya'); expect(fs.existsSync(path.join(root, 'clis', 'pypi', 'search.js'))).toBe(true); diff --git a/src/package-exports.test.ts b/src/package-exports.test.ts index e14a146b..e00de821 100644 --- a/src/package-exports.test.ts +++ b/src/package-exports.test.ts @@ -9,13 +9,13 @@ import { describe, it, expect } from 'vitest'; import * as fs from 'node:fs'; import * as path from 'node:path'; import { builtinModules } from 'node:module'; -import { spawnSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; import ts from 'typescript'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const ROOT = path.resolve(__dirname, '..'); const CLIS_DIR = path.join(ROOT, 'clis'); +const pkgJson = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf-8')); /** Recursively collect all JS adapter files in a directory. */ function collectAdapterFiles(dir: string, opts?: { excludeTests?: boolean }): string[] { @@ -66,19 +66,12 @@ describe('adapter imports use package exports', () => { expect(adapterFiles).toEqual([]); }); - it('packs no core adapters, repository plugins, or adapter fetch lifecycle', () => { - const packed = spawnSync('npm', ['pack', '--ignore-scripts', '--dry-run', '--json'], { - cwd: ROOT, - encoding: 'utf8', - }); - expect(packed.status, packed.stderr).toBe(0); - const files = (JSON.parse(packed.stdout) as Array<{ files: Array<{ path: string }> }>)[0]!.files - .map(file => file.path); + it('excludes adapters from package files and the install lifecycle', () => { + const files = pkgJson.files as string[]; - expect(files.some(file => file.startsWith('clis/'))).toBe(false); - expect(files.some(file => file.startsWith('plugins/'))).toBe(false); - expect(files).not.toContain('scripts/fetch-adapters.js'); - }, 15_000); + expect(files.some(file => /^(?:clis|plugins)(?:\/|$)/.test(file))).toBe(false); + expect(pkgJson.scripts.postinstall).not.toMatch(/fetch-adapters/); + }); it('no adapter uses relative imports to src/, browser/, download/, or pipeline/', () => { const violations: string[] = []; @@ -119,7 +112,6 @@ describe('adapter imports use package exports', () => { }); describe('package.json exports resolve to real files', () => { - const pkgJson = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf-8')); const exports = pkgJson.exports as Record; it('has exports defined', () => { diff --git a/src/postinstall.test.ts b/src/postinstall.test.ts index 567cba7c..2035fa44 100644 --- a/src/postinstall.test.ts +++ b/src/postinstall.test.ts @@ -14,7 +14,13 @@ describe('postinstall', () => { it('installs only core completion files and prints explicit plugin guidance', () => { const home = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-postinstall-')); roots.push(home); - const env: NodeJS.ProcessEnv = { ...process.env, HOME: home, SHELL: '/bin/zsh', npm_config_global: 'true' }; + const env: NodeJS.ProcessEnv = { + ...process.env, + HOME: home, + USERPROFILE: home, + SHELL: '/bin/zsh', + npm_config_global: 'true', + }; delete env.CI; delete env.CONTINUOUS_INTEGRATION; diff --git a/vitest.config.ts b/vitest.config.ts index e47d31c2..cbb61807 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,5 +1,20 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import { defineConfig } from 'vitest/config'; +const root = path.dirname(fileURLToPath(import.meta.url)); +const packageJson = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as { + name: string; + exports: Record; +}; +const packageAliases = Object.entries(packageJson.exports) + .map(([subpath, target]) => ({ + find: subpath === '.' ? packageJson.name : `${packageJson.name}${subpath.slice(1)}`, + replacement: path.resolve(root, target.replace(/^\.\/dist\//, '').replace(/\.js$/, '.ts')), + })) + .sort((a, b) => b.find.length - a.find.length); + const includeExtendedE2e = process.env.WEBCMD_E2E === '1'; export default defineConfig({ test: { @@ -12,6 +27,7 @@ export default defineConfig({ }, }, { + resolve: { alias: packageAliases }, test: { name: 'plugin', include: ['plugins/*/test/**/*.test.{ts,js}'], From 932486929865b9c366e78e66645f36106eaa89cc Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Wed, 5 Aug 2026 19:32:31 +0530 Subject: [PATCH 23/39] test: fix Windows plugin path portability --- plugins/instagram/_shared/protocol-capture.js | 6 ++-- plugins/instagram/post.js | 36 ++++++++++--------- plugins/instagram/reel.js | 7 ++-- plugins/instagram/test/download.test.js | 2 +- plugins/instagram/test/post.test.js | 8 +++-- .../instagram/test/protocol-capture.test.js | 11 +++--- plugins/instagram/test/reel.test.js | 4 +-- plugins/suno/test/utils.test.js | 3 +- plugins/twitter/test/quote.test.js | 3 +- plugins/twitter/test/reply.test.js | 3 +- 10 files changed, 50 insertions(+), 33 deletions(-) diff --git a/plugins/instagram/_shared/protocol-capture.js b/plugins/instagram/_shared/protocol-capture.js index 494640f7..5693cc8f 100644 --- a/plugins/instagram/_shared/protocol-capture.js +++ b/plugins/instagram/_shared/protocol-capture.js @@ -1,8 +1,10 @@ import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; import { resolveInstagramRuntimeInfo } from './runtime-info.js'; const DEFAULT_CAPTURE_VAR = '__webcmd_ig_protocol_capture'; const DEFAULT_CAPTURE_ERRORS_VAR = '__webcmd_ig_protocol_capture_errors'; -const TRACE_OUTPUT_PATH = '/tmp/instagram_post_protocol_trace.json'; +export const INSTAGRAM_PROTOCOL_TRACE_OUTPUT_PATH = path.join(os.tmpdir(), 'instagram_post_protocol_trace.json'); const INSTAGRAM_PROTOCOL_CAPTURE_PATTERN = [ '/rupload_igphoto/', '/rupload_igvideo/', @@ -238,7 +240,7 @@ export async function dumpInstagramProtocolCaptureIfEnabled(page) { if (process.env.WEBCMD_INSTAGRAM_CAPTURE !== '1') return; const payload = await readInstagramProtocolCapture(page); - fs.writeFileSync(TRACE_OUTPUT_PATH, JSON.stringify(payload, null, 2)); + fs.writeFileSync(INSTAGRAM_PROTOCOL_TRACE_OUTPUT_PATH, JSON.stringify(payload, null, 2)); } function buildCookieHeader(cookies) { return cookies diff --git a/plugins/instagram/post.js b/plugins/instagram/post.js index ad582e07..fc90fc51 100644 --- a/plugins/instagram/post.js +++ b/plugins/instagram/post.js @@ -1,15 +1,19 @@ import * as fs from 'node:fs'; +import * as os from 'node:os'; import * as path from 'node:path'; import { cli, Strategy } from '@agentrhq/webcmd/registry'; import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors'; -import { installInstagramProtocolCapture, readInstagramProtocolCapture, } from './_shared/protocol-capture.js'; +import { INSTAGRAM_PROTOCOL_TRACE_OUTPUT_PATH, installInstagramProtocolCapture, readInstagramProtocolCapture, } from './_shared/protocol-capture.js'; import { publishMediaViaPrivateApi, publishImagesViaPrivateApi, resolveInstagramPrivatePublishConfig, } from './_shared/private-publish.js'; import { resolveInstagramRuntimeInfo } from './_shared/runtime-info.js'; const INSTAGRAM_HOME_URL = 'https://www.instagram.com/'; const SUPPORTED_IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.webp']); const SUPPORTED_VIDEO_EXTENSIONS = new Set(['.mp4']); const MAX_MEDIA_ITEMS = 10; -const INSTAGRAM_PROTOCOL_TRACE_OUTPUT_PATH = '/tmp/instagram_post_protocol_trace.json'; +const INSTAGRAM_POST_PREVIEW_DEBUG_PATH = path.join(os.tmpdir(), 'instagram_post_preview_debug.png'); +const INSTAGRAM_POST_CAPTION_DEBUG_PATH = path.join(os.tmpdir(), 'instagram_post_caption_debug.png'); +const INSTAGRAM_POST_CAPTION_FILL_DEBUG_PATH = path.join(os.tmpdir(), 'instagram_post_caption_fill_debug.png'); +const INSTAGRAM_POST_SHARE_DEBUG_PATH = path.join(os.tmpdir(), 'instagram_post_share_debug.png'); async function gotoInstagramHome(page, forceReload = false) { if (forceReload) { await page.goto(`${INSTAGRAM_HOME_URL}?__webcmd_reset=${Date.now()}`); @@ -779,14 +783,14 @@ async function waitForPreview(page, maxWaitSeconds = 12) { if (state.state === 'preview') return; if (state.state === 'failed') { - await page.screenshot({ path: '/tmp/instagram_post_preview_debug.png' }); - throw makeUploadFailure('Inspect /tmp/instagram_post_preview_debug.png. ' + (state.detail || '')); + await page.screenshot({ path: INSTAGRAM_POST_PREVIEW_DEBUG_PATH }); + throw makeUploadFailure(`Inspect ${INSTAGRAM_POST_PREVIEW_DEBUG_PATH}. ` + (state.detail || '')); } if (attempt < attempts - 1) await page.wait({ time: 1 }); } - await page.screenshot({ path: '/tmp/instagram_post_preview_debug.png' }); - throw new CommandExecutionError('Instagram image preview did not appear after upload', 'The selected file input may not match the active composer; inspect /tmp/instagram_post_preview_debug.png'); + await page.screenshot({ path: INSTAGRAM_POST_PREVIEW_DEBUG_PATH }); + throw new CommandExecutionError('Instagram image preview did not appear after upload', `The selected file input may not match the active composer; inspect ${INSTAGRAM_POST_PREVIEW_DEBUG_PATH}`); } async function waitForPreviewMaybe(page, maxWaitSeconds = 4) { const attempts = Math.max(1, Math.ceil(maxWaitSeconds * 2)); @@ -966,13 +970,13 @@ async function advanceToCaptionEditor(page) { throw makeUploadFailure(uploadState.detail); } } - await page.screenshot({ path: '/tmp/instagram_post_caption_debug.png' }); - throw new CommandExecutionError('Instagram caption editor did not appear', 'Instagram may have changed the publish flow; inspect /tmp/instagram_post_caption_debug.png'); + await page.screenshot({ path: INSTAGRAM_POST_CAPTION_DEBUG_PATH }); + throw new CommandExecutionError('Instagram caption editor did not appear', `Instagram may have changed the publish flow; inspect ${INSTAGRAM_POST_CAPTION_DEBUG_PATH}`); } async function waitForCaptionEditor(page) { if (!(await hasCaptionEditor(page))) { - await page.screenshot({ path: '/tmp/instagram_post_caption_debug.png' }); - throw new CommandExecutionError('Instagram caption editor did not appear', 'Instagram may have changed the publish flow; inspect /tmp/instagram_post_caption_debug.png'); + await page.screenshot({ path: INSTAGRAM_POST_CAPTION_DEBUG_PATH }); + throw new CommandExecutionError('Instagram caption editor did not appear', `Instagram may have changed the publish flow; inspect ${INSTAGRAM_POST_CAPTION_DEBUG_PATH}`); } } async function rethrowUploadFailureIfPresent(page, originalError) { @@ -1242,16 +1246,16 @@ async function ensureCaptionFilled(page, content) { await page.wait({ time: 0.5 }); } } - await page.screenshot({ path: '/tmp/instagram_post_caption_fill_debug.png' }); - throw new CommandExecutionError('Instagram caption did not stick before sharing', 'Inspect /tmp/instagram_post_caption_fill_debug.png for the caption editor state'); + await page.screenshot({ path: INSTAGRAM_POST_CAPTION_FILL_DEBUG_PATH }); + throw new CommandExecutionError('Instagram caption did not stick before sharing', `Inspect ${INSTAGRAM_POST_CAPTION_FILL_DEBUG_PATH} for the caption editor state`); } async function waitForPublishSuccess(page) { let settledStreak = 0; for (let attempt = 0; attempt < 90; attempt++) { const result = await page.evaluate(buildPublishStatusProbeJs()); if (result?.failed) { - await page.screenshot({ path: '/tmp/instagram_post_share_debug.png' }); - throw new CommandExecutionError('Instagram post share failed', 'Inspect /tmp/instagram_post_share_debug.png for the share failure state'); + await page.screenshot({ path: INSTAGRAM_POST_SHARE_DEBUG_PATH }); + throw new CommandExecutionError('Instagram post share failed', `Inspect ${INSTAGRAM_POST_SHARE_DEBUG_PATH} for the share failure state`); } if (result?.ok) { return result.url || ''; @@ -1268,8 +1272,8 @@ async function waitForPublishSuccess(page) { await page.wait({ time: 1 }); } } - await page.screenshot({ path: '/tmp/instagram_post_share_debug.png' }); - throw new CommandExecutionError('Instagram post share confirmation did not appear', 'Inspect /tmp/instagram_post_share_debug.png for the final publish state'); + await page.screenshot({ path: INSTAGRAM_POST_SHARE_DEBUG_PATH }); + throw new CommandExecutionError('Instagram post share confirmation did not appear', `Inspect ${INSTAGRAM_POST_SHARE_DEBUG_PATH} for the final publish state`); } async function resolveCurrentUserId(page) { const cookies = await page.getCookies({ domain: 'instagram.com' }); diff --git a/plugins/instagram/reel.js b/plugins/instagram/reel.js index 45c59483..d987a8eb 100644 --- a/plugins/instagram/reel.js +++ b/plugins/instagram/reel.js @@ -9,6 +9,7 @@ import { resolveInstagramRuntimeInfo } from './_shared/runtime-info.js'; const INSTAGRAM_HOME_URL = 'https://www.instagram.com/'; const SUPPORTED_VIDEO_EXTENSIONS = new Set(['.mp4']); const INSTAGRAM_REEL_TIMEOUT_SECONDS = 600; +const INSTAGRAM_REEL_PREVIEW_DEBUG_PATH = path.join(os.tmpdir(), 'instagram_reel_preview_debug.png'); function requirePage(page) { if (!page) throw new CommandExecutionError('Browser session required for instagram reel'); @@ -223,10 +224,10 @@ async function waitForVideoPreview(page, maxWaitSeconds = 20) { if (attempt < maxWaitSeconds * 2 - 1) await page.wait({ time: 0.5 }); } - await page.screenshot({ path: '/tmp/instagram_reel_preview_debug.png' }); + await page.screenshot({ path: INSTAGRAM_REEL_PREVIEW_DEBUG_PATH }); throw new CommandExecutionError('Instagram reel preview did not appear after upload', lastDetail - ? `Inspect /tmp/instagram_reel_preview_debug.png. Last visible dialog text: ${lastDetail}` - : 'Inspect /tmp/instagram_reel_preview_debug.png for the upload state'); + ? `Inspect ${INSTAGRAM_REEL_PREVIEW_DEBUG_PATH}. Last visible dialog text: ${lastDetail}` + : `Inspect ${INSTAGRAM_REEL_PREVIEW_DEBUG_PATH} for the upload state`); } async function clickAction(page, labels, scope = 'any') { const result = await page.evaluate(buildClickActionJs(labels, scope)); diff --git a/plugins/instagram/test/download.test.js b/plugins/instagram/test/download.test.js index 4fca6f35..4d04de68 100644 --- a/plugins/instagram/test/download.test.js +++ b/plugins/instagram/test/download.test.js @@ -136,6 +136,6 @@ describe('instagram download command', () => { ], }); await cmd.func(page, { url: 'https://www.instagram.com/p/DWUR_azCWbN/' }); - expect(mockHttpDownload).toHaveBeenCalledWith('https://cdn.example.com/photo.webp?foo=1', expect.stringContaining(`${os.homedir()}/Downloads/Instagram/DWUR_azCWbN/DWUR_azCWbN_01.webp`), expect.objectContaining({ timeout: 60000 })); + expect(mockHttpDownload).toHaveBeenCalledWith('https://cdn.example.com/photo.webp?foo=1', path.join(os.homedir(), 'Downloads', 'Instagram', 'DWUR_azCWbN', 'DWUR_azCWbN_01.webp'), expect.objectContaining({ timeout: 60000 })); }); }); diff --git a/plugins/instagram/test/post.test.js b/plugins/instagram/test/post.test.js index 64def043..e5dd3fd7 100644 --- a/plugins/instagram/test/post.test.js +++ b/plugins/instagram/test/post.test.js @@ -8,6 +8,8 @@ import * as privatePublish from '../_shared/private-publish.js'; import { buildClickActionJs, buildEnsureComposerOpenJs, buildInspectUploadStageJs, buildPublishStatusProbeJs } from '../post.js'; import '../post.js'; const tempDirs = []; +const protocolTracePath = path.join(os.tmpdir(), 'instagram_post_protocol_trace.json'); +const previewDebugPath = path.join(os.tmpdir(), 'instagram_post_preview_debug.png'); function createTempImage(name = 'demo.jpg', bytes = Buffer.from([0xff, 0xd8, 0xff, 0xd9])) { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-instagram-post-')); tempDirs.push(dir); @@ -365,6 +367,8 @@ describe('instagram post registration', () => { }); afterEach(() => { vi.restoreAllMocks(); + delete process.env.WEBCMD_INSTAGRAM_CAPTURE; + fs.rmSync(protocolTracePath, { force: true }); }); it('registers the post command with a required-value media arg', () => { const cmd = getRegistry().get('instagram/post'); @@ -643,7 +647,7 @@ describe('instagram post registration', () => { url: 'https://www.instagram.com/p/CAPTURE123/', }, ]); - delete process.env.WEBCMD_INSTAGRAM_CAPTURE; + expect(fs.existsSync(protocolTracePath)).toBe(true); }); it('retries media Next when preview is visible before the button becomes clickable', async () => { const firstImagePath = createTempImage('carousel-delay-1.jpg'); @@ -933,7 +937,7 @@ describe('instagram post registration', () => { media: imagePath, content: 'preview missing', })).rejects.toThrow('Instagram image preview did not appear after upload'); - expect(page.screenshot).toHaveBeenCalledWith({ path: '/tmp/instagram_post_preview_debug.png' }); + expect(page.screenshot).toHaveBeenCalledWith({ path: previewDebugPath }); }); it('fails clearly when Instagram shows an upload-stage error dialog', async () => { const imagePath = createTempImage('upload-error.jpg'); diff --git a/plugins/instagram/test/protocol-capture.test.js b/plugins/instagram/test/protocol-capture.test.js index 5a4006ea..95019754 100644 --- a/plugins/instagram/test/protocol-capture.test.js +++ b/plugins/instagram/test/protocol-capture.test.js @@ -1,12 +1,15 @@ import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { buildInstallInstagramProtocolCaptureJs, buildReadInstagramProtocolCaptureJs, dumpInstagramProtocolCaptureIfEnabled, instagramPrivateApiFetch, installInstagramProtocolCapture, readInstagramProtocolCapture, } from '../_shared/protocol-capture.js'; +const traceOutputPath = path.join(os.tmpdir(), 'instagram_post_protocol_trace.json'); describe('instagram protocol capture helpers', () => { afterEach(() => { vi.restoreAllMocks(); delete process.env.WEBCMD_INSTAGRAM_CAPTURE; try { - fs.rmSync('/tmp/instagram_post_protocol_trace.json', { force: true }); + fs.rmSync(traceOutputPath, { force: true }); } catch { } }); @@ -53,7 +56,7 @@ describe('instagram protocol capture helpers', () => { errors: [], }); }); - it('dumps protocol traces to /tmp only when capture env is enabled', async () => { + it('dumps protocol traces to the system temp directory only when capture env is enabled', async () => { process.env.WEBCMD_INSTAGRAM_CAPTURE = '1'; const page = { evaluate: vi.fn().mockResolvedValue({ @@ -62,7 +65,7 @@ describe('instagram protocol capture helpers', () => { }), }; await dumpInstagramProtocolCaptureIfEnabled(page); - const raw = fs.readFileSync('/tmp/instagram_post_protocol_trace.json', 'utf8'); + const raw = fs.readFileSync(traceOutputPath, 'utf8'); expect(raw).toContain('rupload_igphoto'); }); it('does not dump protocol traces when capture env is disabled', async () => { @@ -71,7 +74,7 @@ describe('instagram protocol capture helpers', () => { }; await dumpInstagramProtocolCaptureIfEnabled(page); expect(page.evaluate).not.toHaveBeenCalled(); - expect(fs.existsSync('/tmp/instagram_post_protocol_trace.json')).toBe(false); + expect(fs.existsSync(traceOutputPath)).toBe(false); }); }); describe('instagram private api fetch', () => { diff --git a/plugins/instagram/test/reel.test.js b/plugins/instagram/test/reel.test.js index 91996cac..4961f0ec 100644 --- a/plugins/instagram/test/reel.test.js +++ b/plugins/instagram/test/reel.test.js @@ -108,8 +108,8 @@ describe('instagram reel registration', () => { }, ]); }); - it('copies query-style local video filenames to a safe temp upload path before setFileInput', async () => { - const videoPath = createTempVideo('demo.mp4?sign=abc&t=123video.MP4'); + it('copies unsafe local video filenames to a safe temp upload path before setFileInput', async () => { + const videoPath = createTempVideo('demo video.MP4'); const page = createPageMock([ { ok: false }, { ok: true }, diff --git a/plugins/suno/test/utils.test.js b/plugins/suno/test/utils.test.js index 1f9d5d44..494f37d5 100644 --- a/plugins/suno/test/utils.test.js +++ b/plugins/suno/test/utils.test.js @@ -63,7 +63,8 @@ describe('suno utils — resolveSunoOutputDir', () => { }); it('absolute paths are returned as-is (resolved)', () => { - expect(resolveSunoOutputDir('/tmp/suno')).toBe('/tmp/suno'); + const absolutePath = path.join(os.tmpdir(), 'suno'); + expect(resolveSunoOutputDir(absolutePath)).toBe(path.resolve(absolutePath)); }); }); diff --git a/plugins/twitter/test/quote.test.js b/plugins/twitter/test/quote.test.js index 78e037bb..f02d5f47 100644 --- a/plugins/twitter/test/quote.test.js +++ b/plugins/twitter/test/quote.test.js @@ -127,7 +127,8 @@ describe('twitter quote command', () => { expect(fetchMock).toHaveBeenCalledWith('https://example.com/banner'); expect(setFileInput).toHaveBeenCalledTimes(1); const uploadedPath = setFileInput.mock.calls[0][0][0]; - expect(uploadedPath).toMatch(/webcmd-twitter-.*\/image\.png$/); + expect(path.basename(path.dirname(uploadedPath))).toMatch(/^webcmd-twitter-/); + expect(path.basename(uploadedPath)).toBe('image.png'); // Per-call tmp dir is removed in the adapter's finally block. expect(fs.existsSync(uploadedPath)).toBe(false); expect(result).toEqual([ diff --git a/plugins/twitter/test/reply.test.js b/plugins/twitter/test/reply.test.js index 1843cbf4..a19c9d86 100644 --- a/plugins/twitter/test/reply.test.js +++ b/plugins/twitter/test/reply.test.js @@ -95,7 +95,8 @@ describe('twitter reply command', () => { const uploadedPath = setFileInput.mock.calls[0][0][0]; // Tmp dir is created by utils.downloadRemoteImage with the // 'webcmd-twitter-' prefix; final extension comes from Content-Type. - expect(uploadedPath).toMatch(/webcmd-twitter-.*\/image\.png$/); + expect(path.basename(path.dirname(uploadedPath))).toMatch(/^webcmd-twitter-/); + expect(path.basename(uploadedPath)).toBe('image.png'); // Per-call tmp dir is removed in the adapter's finally block, so the // downloaded file no longer exists once the command returns. expect(fs.existsSync(uploadedPath)).toBe(false); From c872b4741535ea84d5ef10252739adc8e1b3c2b6 Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Thu, 6 Aug 2026 15:28:11 +0530 Subject: [PATCH 24/39] fix: match migrated plugin webcmd version floor to the shipping release migrate-cli-sites.mjs hardcoded webcmd: ">=0.6.0" for every plugin it generated a fresh manifest for, but package.json ships as 0.5.3 in this release. Every one of the 108 affected plugins (including linkedin) was uninstallable: `webcmd plugin install` hard-fails checkCompatibility() with "Plugin requires webcmd >=0.6.0, but current version is incompatible" before it ever reaches npm. Derive the floor from package.json's actual version instead of a literal, and regenerate the already-committed manifests (all plugins/*/webcmd-plugin.json + package.json, and the aggregated root webcmd-plugin.json catalog) to match. Update the two unit tests that had the old literal baked into their expectations. --- plugins/amazon-in/package.json | 2 +- plugins/amazon-in/webcmd-plugin.json | 2 +- plugins/amazon/package.json | 2 +- plugins/amazon/webcmd-plugin.json | 2 +- plugins/antigravity/package.json | 2 +- plugins/antigravity/webcmd-plugin.json | 2 +- plugins/apple-podcasts/package.json | 2 +- plugins/apple-podcasts/webcmd-plugin.json | 2 +- plugins/archive/package.json | 2 +- plugins/archive/webcmd-plugin.json | 2 +- plugins/arxiv/package.json | 2 +- plugins/arxiv/webcmd-plugin.json | 2 +- plugins/band/package.json | 2 +- plugins/band/webcmd-plugin.json | 2 +- plugins/barchart/package.json | 2 +- plugins/barchart/webcmd-plugin.json | 2 +- plugins/bbc/package.json | 2 +- plugins/bbc/webcmd-plugin.json | 2 +- plugins/bigbasket/package.json | 2 +- plugins/bigbasket/webcmd-plugin.json | 2 +- plugins/binance/package.json | 2 +- plugins/binance/webcmd-plugin.json | 2 +- plugins/blinkit/package.json | 2 +- plugins/blinkit/webcmd-plugin.json | 2 +- plugins/bloomberg/package.json | 2 +- plugins/bloomberg/webcmd-plugin.json | 2 +- plugins/bluesky/package.json | 2 +- plugins/bluesky/webcmd-plugin.json | 2 +- plugins/booking/package.json | 2 +- plugins/booking/webcmd-plugin.json | 2 +- plugins/brave/package.json | 2 +- plugins/brave/webcmd-plugin.json | 2 +- plugins/chatgpt-app/package.json | 2 +- plugins/chatgpt-app/webcmd-plugin.json | 2 +- plugins/chatgpt/package.json | 2 +- plugins/chatgpt/webcmd-plugin.json | 2 +- plugins/chatwise/package.json | 2 +- plugins/chatwise/webcmd-plugin.json | 2 +- plugins/chess/package.json | 2 +- plugins/chess/webcmd-plugin.json | 2 +- plugins/claude/package.json | 2 +- plugins/claude/webcmd-plugin.json | 2 +- plugins/codex/package.json | 2 +- plugins/codex/webcmd-plugin.json | 2 +- plugins/coingecko/package.json | 2 +- plugins/coingecko/webcmd-plugin.json | 2 +- plugins/confluence/package.json | 2 +- plugins/confluence/webcmd-plugin.json | 2 +- plugins/coupang/package.json | 2 +- plugins/coupang/webcmd-plugin.json | 2 +- plugins/crates/package.json | 2 +- plugins/crates/webcmd-plugin.json | 2 +- plugins/cursor/package.json | 2 +- plugins/cursor/webcmd-plugin.json | 2 +- plugins/dblp/package.json | 2 +- plugins/dblp/webcmd-plugin.json | 2 +- plugins/defillama/package.json | 2 +- plugins/defillama/webcmd-plugin.json | 2 +- plugins/devto/package.json | 2 +- plugins/devto/webcmd-plugin.json | 2 +- plugins/dictionary/package.json | 2 +- plugins/dictionary/webcmd-plugin.json | 2 +- plugins/discord-app/package.json | 2 +- plugins/discord-app/webcmd-plugin.json | 2 +- plugins/district/package.json | 2 +- plugins/district/webcmd-plugin.json | 2 +- plugins/dockerhub/package.json | 2 +- plugins/dockerhub/webcmd-plugin.json | 2 +- plugins/duckduckgo/package.json | 2 +- plugins/duckduckgo/webcmd-plugin.json | 2 +- plugins/endoflife/package.json | 2 +- plugins/endoflife/webcmd-plugin.json | 2 +- plugins/facebook/package.json | 2 +- plugins/facebook/webcmd-plugin.json | 2 +- plugins/flathub/package.json | 2 +- plugins/flathub/webcmd-plugin.json | 2 +- plugins/gemini/package.json | 2 +- plugins/gemini/webcmd-plugin.json | 2 +- plugins/geogebra/package.json | 2 +- plugins/geogebra/webcmd-plugin.json | 2 +- plugins/github-trending/package.json | 2 +- plugins/github-trending/webcmd-plugin.json | 2 +- plugins/github/package.json | 2 +- plugins/github/webcmd-plugin.json | 2 +- plugins/google-scholar/package.json | 2 +- plugins/google-scholar/webcmd-plugin.json | 2 +- plugins/google/package.json | 2 +- plugins/google/webcmd-plugin.json | 2 +- plugins/goproxy/package.json | 2 +- plugins/goproxy/webcmd-plugin.json | 2 +- plugins/grok/package.json | 2 +- plugins/grok/webcmd-plugin.json | 2 +- plugins/hackernews/package.json | 2 +- plugins/hackernews/webcmd-plugin.json | 2 +- plugins/hf/package.json | 2 +- plugins/hf/webcmd-plugin.json | 2 +- plugins/homebrew/package.json | 2 +- plugins/homebrew/webcmd-plugin.json | 2 +- plugins/imdb/package.json | 2 +- plugins/imdb/webcmd-plugin.json | 2 +- plugins/indeed/package.json | 2 +- plugins/indeed/webcmd-plugin.json | 2 +- plugins/instagram/package.json | 2 +- plugins/instagram/webcmd-plugin.json | 2 +- plugins/jira/package.json | 2 +- plugins/jira/webcmd-plugin.json | 2 +- plugins/lesswrong/package.json | 2 +- plugins/lesswrong/webcmd-plugin.json | 2 +- plugins/lichess/package.json | 2 +- plugins/lichess/webcmd-plugin.json | 2 +- plugins/linkedin-learning/package.json | 2 +- plugins/linkedin-learning/webcmd-plugin.json | 2 +- plugins/linkedin/package.json | 2 +- plugins/linkedin/webcmd-plugin.json | 2 +- plugins/lobsters/package.json | 2 +- plugins/lobsters/webcmd-plugin.json | 2 +- plugins/manus/package.json | 2 +- plugins/manus/webcmd-plugin.json | 2 +- plugins/maven/package.json | 2 +- plugins/maven/webcmd-plugin.json | 2 +- plugins/mdn/package.json | 2 +- plugins/mdn/webcmd-plugin.json | 2 +- plugins/medium/package.json | 2 +- plugins/medium/webcmd-plugin.json | 2 +- plugins/mercury/package.json | 2 +- plugins/mercury/webcmd-plugin.json | 2 +- plugins/notebooklm/package.json | 2 +- plugins/notebooklm/webcmd-plugin.json | 2 +- plugins/npm/package.json | 2 +- plugins/npm/webcmd-plugin.json | 2 +- plugins/nuget/package.json | 2 +- plugins/nuget/webcmd-plugin.json | 2 +- plugins/nvd/package.json | 2 +- plugins/nvd/webcmd-plugin.json | 2 +- plugins/oeis/package.json | 2 +- plugins/oeis/webcmd-plugin.json | 2 +- plugins/openalex/package.json | 2 +- plugins/openalex/webcmd-plugin.json | 2 +- plugins/openfda/package.json | 2 +- plugins/openfda/webcmd-plugin.json | 2 +- plugins/openreview/package.json | 2 +- plugins/openreview/webcmd-plugin.json | 2 +- plugins/osv/package.json | 2 +- plugins/osv/webcmd-plugin.json | 2 +- plugins/packagist/package.json | 2 +- plugins/packagist/webcmd-plugin.json | 2 +- plugins/paperreview/package.json | 2 +- plugins/paperreview/webcmd-plugin.json | 2 +- plugins/pixiv/package.json | 2 +- plugins/pixiv/webcmd-plugin.json | 2 +- plugins/practo/package.json | 2 +- plugins/practo/webcmd-plugin.json | 2 +- plugins/producthunt/package.json | 2 +- plugins/producthunt/webcmd-plugin.json | 2 +- plugins/pubmed/package.json | 2 +- plugins/pubmed/webcmd-plugin.json | 2 +- plugins/qoder/package.json | 2 +- plugins/qoder/webcmd-plugin.json | 2 +- plugins/reddit/package.json | 2 +- plugins/reddit/webcmd-plugin.json | 2 +- plugins/rest-countries/package.json | 2 +- plugins/rest-countries/webcmd-plugin.json | 2 +- plugins/reuters/package.json | 2 +- plugins/reuters/webcmd-plugin.json | 2 +- plugins/rfc/package.json | 2 +- plugins/rfc/webcmd-plugin.json | 2 +- plugins/rubygems/package.json | 2 +- plugins/rubygems/webcmd-plugin.json | 2 +- plugins/semanticscholar/package.json | 2 +- plugins/semanticscholar/webcmd-plugin.json | 2 +- plugins/slock/package.json | 2 +- plugins/slock/webcmd-plugin.json | 2 +- plugins/spotify/package.json | 2 +- plugins/spotify/webcmd-plugin.json | 2 +- plugins/stackoverflow/package.json | 2 +- plugins/stackoverflow/webcmd-plugin.json | 2 +- plugins/steam/package.json | 2 +- plugins/steam/webcmd-plugin.json | 2 +- plugins/substack/package.json | 2 +- plugins/substack/webcmd-plugin.json | 2 +- plugins/suno/package.json | 2 +- plugins/suno/webcmd-plugin.json | 2 +- plugins/tiktok/package.json | 2 +- plugins/tiktok/webcmd-plugin.json | 2 +- plugins/trae-solo/package.json | 2 +- plugins/trae-solo/webcmd-plugin.json | 2 +- plugins/trip/package.json | 2 +- plugins/trip/webcmd-plugin.json | 2 +- plugins/tvmaze/package.json | 2 +- plugins/tvmaze/webcmd-plugin.json | 2 +- plugins/twitter/package.json | 2 +- plugins/twitter/webcmd-plugin.json | 2 +- plugins/uiverse/package.json | 2 +- plugins/uiverse/webcmd-plugin.json | 2 +- plugins/upwork/package.json | 2 +- plugins/upwork/webcmd-plugin.json | 2 +- plugins/web/package.json | 2 +- plugins/web/webcmd-plugin.json | 2 +- plugins/wikidata/package.json | 2 +- plugins/wikidata/webcmd-plugin.json | 2 +- plugins/wikipedia/package.json | 2 +- plugins/wikipedia/webcmd-plugin.json | 2 +- plugins/wttr/package.json | 2 +- plugins/wttr/webcmd-plugin.json | 2 +- plugins/yahoo-finance/package.json | 2 +- plugins/yahoo-finance/webcmd-plugin.json | 2 +- plugins/yahoo/package.json | 2 +- plugins/yahoo/webcmd-plugin.json | 2 +- plugins/yollomi/package.json | 2 +- plugins/yollomi/webcmd-plugin.json | 2 +- plugins/youtube/package.json | 2 +- plugins/youtube/webcmd-plugin.json | 2 +- plugins/zepto/package.json | 2 +- plugins/zepto/webcmd-plugin.json | 2 +- plugins/zlibrary/package.json | 2 +- plugins/zlibrary/webcmd-plugin.json | 2 +- scripts/migrate-cli-sites.mjs | 7 +- src/build-plugin-command-manifest.test.ts | 11 +- src/migrate-cli-sites.test.ts | 5 +- webcmd-plugin.json | 216 +++++++++---------- 220 files changed, 340 insertions(+), 331 deletions(-) diff --git a/plugins/amazon-in/package.json b/plugins/amazon-in/package.json index 6b596505..760f0d37 100644 --- a/plugins/amazon-in/package.json +++ b/plugins/amazon-in/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for amazon-in", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/amazon-in/webcmd-plugin.json b/plugins/amazon-in/webcmd-plugin.json index 0ee3eafc..200f5055 100644 --- a/plugins/amazon-in/webcmd-plugin.json +++ b/plugins/amazon-in/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "amazon-in", "version": "0.1.0", "description": "Webcmd commands for amazon-in", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/amazon/package.json b/plugins/amazon/package.json index b0a3c57e..4ac6204e 100644 --- a/plugins/amazon/package.json +++ b/plugins/amazon/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for amazon", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/amazon/webcmd-plugin.json b/plugins/amazon/webcmd-plugin.json index 6048297b..2aae0553 100644 --- a/plugins/amazon/webcmd-plugin.json +++ b/plugins/amazon/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "amazon", "version": "0.1.0", "description": "Webcmd commands for amazon", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/antigravity/package.json b/plugins/antigravity/package.json index 3b278cc4..ba5976f1 100644 --- a/plugins/antigravity/package.json +++ b/plugins/antigravity/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for antigravity", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/antigravity/webcmd-plugin.json b/plugins/antigravity/webcmd-plugin.json index 109ccdcf..cfd295d3 100644 --- a/plugins/antigravity/webcmd-plugin.json +++ b/plugins/antigravity/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "antigravity", "version": "0.1.0", "description": "Webcmd commands for antigravity", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/apple-podcasts/package.json b/plugins/apple-podcasts/package.json index 8972e683..ca74a8c4 100644 --- a/plugins/apple-podcasts/package.json +++ b/plugins/apple-podcasts/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for apple-podcasts", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/apple-podcasts/webcmd-plugin.json b/plugins/apple-podcasts/webcmd-plugin.json index ce335a80..f2537c70 100644 --- a/plugins/apple-podcasts/webcmd-plugin.json +++ b/plugins/apple-podcasts/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "apple-podcasts", "version": "0.1.0", "description": "Webcmd commands for apple-podcasts", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/archive/package.json b/plugins/archive/package.json index 08d9d2d3..66f9a9d4 100644 --- a/plugins/archive/package.json +++ b/plugins/archive/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for archive", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/archive/webcmd-plugin.json b/plugins/archive/webcmd-plugin.json index fcfaeff5..5e182a1f 100644 --- a/plugins/archive/webcmd-plugin.json +++ b/plugins/archive/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "archive", "version": "0.1.0", "description": "Webcmd commands for archive", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/arxiv/package.json b/plugins/arxiv/package.json index 1fdff537..7d179665 100644 --- a/plugins/arxiv/package.json +++ b/plugins/arxiv/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for arxiv", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/arxiv/webcmd-plugin.json b/plugins/arxiv/webcmd-plugin.json index dbbf12f1..08661c06 100644 --- a/plugins/arxiv/webcmd-plugin.json +++ b/plugins/arxiv/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "arxiv", "version": "0.1.0", "description": "Webcmd commands for arxiv", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/band/package.json b/plugins/band/package.json index f57c72e5..9c7d0748 100644 --- a/plugins/band/package.json +++ b/plugins/band/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for band", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/band/webcmd-plugin.json b/plugins/band/webcmd-plugin.json index 4d9ba6ee..7d271a7b 100644 --- a/plugins/band/webcmd-plugin.json +++ b/plugins/band/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "band", "version": "0.1.0", "description": "Webcmd commands for band", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/barchart/package.json b/plugins/barchart/package.json index 928221a3..cfd3a63f 100644 --- a/plugins/barchart/package.json +++ b/plugins/barchart/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for barchart", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/barchart/webcmd-plugin.json b/plugins/barchart/webcmd-plugin.json index 79a01b7a..18cc916f 100644 --- a/plugins/barchart/webcmd-plugin.json +++ b/plugins/barchart/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "barchart", "version": "0.1.0", "description": "Webcmd commands for barchart", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/bbc/package.json b/plugins/bbc/package.json index 0c52783a..9c75185d 100644 --- a/plugins/bbc/package.json +++ b/plugins/bbc/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for bbc", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/bbc/webcmd-plugin.json b/plugins/bbc/webcmd-plugin.json index 3976f34c..0344cf8e 100644 --- a/plugins/bbc/webcmd-plugin.json +++ b/plugins/bbc/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "bbc", "version": "0.1.0", "description": "Webcmd commands for bbc", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/bigbasket/package.json b/plugins/bigbasket/package.json index 48a655cd..3ddadfb3 100644 --- a/plugins/bigbasket/package.json +++ b/plugins/bigbasket/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for bigbasket", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/bigbasket/webcmd-plugin.json b/plugins/bigbasket/webcmd-plugin.json index 3ce7c498..9dd2b7a3 100644 --- a/plugins/bigbasket/webcmd-plugin.json +++ b/plugins/bigbasket/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "bigbasket", "version": "0.1.0", "description": "Webcmd commands for bigbasket", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/binance/package.json b/plugins/binance/package.json index f3d324e5..35ac8b8e 100644 --- a/plugins/binance/package.json +++ b/plugins/binance/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for binance", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/binance/webcmd-plugin.json b/plugins/binance/webcmd-plugin.json index 6b6ef765..041f9087 100644 --- a/plugins/binance/webcmd-plugin.json +++ b/plugins/binance/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "binance", "version": "0.1.0", "description": "Webcmd commands for binance", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/blinkit/package.json b/plugins/blinkit/package.json index 101bd164..d7e6192d 100644 --- a/plugins/blinkit/package.json +++ b/plugins/blinkit/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for blinkit", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/blinkit/webcmd-plugin.json b/plugins/blinkit/webcmd-plugin.json index c4346e5b..6fa4e409 100644 --- a/plugins/blinkit/webcmd-plugin.json +++ b/plugins/blinkit/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "blinkit", "version": "0.1.0", "description": "Webcmd commands for blinkit", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/bloomberg/package.json b/plugins/bloomberg/package.json index a4b6f43b..6acbfa4c 100644 --- a/plugins/bloomberg/package.json +++ b/plugins/bloomberg/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for bloomberg", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/bloomberg/webcmd-plugin.json b/plugins/bloomberg/webcmd-plugin.json index 5e682245..1d2c9bdd 100644 --- a/plugins/bloomberg/webcmd-plugin.json +++ b/plugins/bloomberg/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "bloomberg", "version": "0.1.0", "description": "Webcmd commands for bloomberg", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/bluesky/package.json b/plugins/bluesky/package.json index 923a54a7..07d1f33b 100644 --- a/plugins/bluesky/package.json +++ b/plugins/bluesky/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for bluesky", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/bluesky/webcmd-plugin.json b/plugins/bluesky/webcmd-plugin.json index cc1bcfcb..a0a2f351 100644 --- a/plugins/bluesky/webcmd-plugin.json +++ b/plugins/bluesky/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "bluesky", "version": "0.1.0", "description": "Webcmd commands for bluesky", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/booking/package.json b/plugins/booking/package.json index cb62e381..cd6666ee 100644 --- a/plugins/booking/package.json +++ b/plugins/booking/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for booking", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/booking/webcmd-plugin.json b/plugins/booking/webcmd-plugin.json index 8dd33ee4..83262771 100644 --- a/plugins/booking/webcmd-plugin.json +++ b/plugins/booking/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "booking", "version": "0.1.0", "description": "Webcmd commands for booking", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/brave/package.json b/plugins/brave/package.json index b3c31e59..8fe2d9ee 100644 --- a/plugins/brave/package.json +++ b/plugins/brave/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for brave", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/brave/webcmd-plugin.json b/plugins/brave/webcmd-plugin.json index cba10151..5f817d83 100644 --- a/plugins/brave/webcmd-plugin.json +++ b/plugins/brave/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "brave", "version": "0.1.0", "description": "Webcmd commands for brave", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/chatgpt-app/package.json b/plugins/chatgpt-app/package.json index bbaa0d5f..16f40a66 100644 --- a/plugins/chatgpt-app/package.json +++ b/plugins/chatgpt-app/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for chatgpt-app", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/chatgpt-app/webcmd-plugin.json b/plugins/chatgpt-app/webcmd-plugin.json index 36ea71bc..b5163d3b 100644 --- a/plugins/chatgpt-app/webcmd-plugin.json +++ b/plugins/chatgpt-app/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "chatgpt-app", "version": "0.1.0", "description": "Webcmd commands for chatgpt-app", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/chatgpt/package.json b/plugins/chatgpt/package.json index 62dd1668..6b1df665 100644 --- a/plugins/chatgpt/package.json +++ b/plugins/chatgpt/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for chatgpt", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/chatgpt/webcmd-plugin.json b/plugins/chatgpt/webcmd-plugin.json index c076c1d7..d19eea66 100644 --- a/plugins/chatgpt/webcmd-plugin.json +++ b/plugins/chatgpt/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "chatgpt", "version": "0.1.0", "description": "Webcmd commands for chatgpt", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/chatwise/package.json b/plugins/chatwise/package.json index eb939472..d67deaf3 100644 --- a/plugins/chatwise/package.json +++ b/plugins/chatwise/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for chatwise", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/chatwise/webcmd-plugin.json b/plugins/chatwise/webcmd-plugin.json index 00e0e74b..1f2fe225 100644 --- a/plugins/chatwise/webcmd-plugin.json +++ b/plugins/chatwise/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "chatwise", "version": "0.1.0", "description": "Webcmd commands for chatwise", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/chess/package.json b/plugins/chess/package.json index 4ffb8024..354ce6b1 100644 --- a/plugins/chess/package.json +++ b/plugins/chess/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for chess", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/chess/webcmd-plugin.json b/plugins/chess/webcmd-plugin.json index 4772e321..66c5d6bf 100644 --- a/plugins/chess/webcmd-plugin.json +++ b/plugins/chess/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "chess", "version": "0.1.0", "description": "Webcmd commands for chess", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/claude/package.json b/plugins/claude/package.json index f36d07bc..b676c54f 100644 --- a/plugins/claude/package.json +++ b/plugins/claude/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for claude", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/claude/webcmd-plugin.json b/plugins/claude/webcmd-plugin.json index 3dadcd7e..79ee9c2d 100644 --- a/plugins/claude/webcmd-plugin.json +++ b/plugins/claude/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "claude", "version": "0.1.0", "description": "Webcmd commands for claude", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/codex/package.json b/plugins/codex/package.json index 3162aaf7..80a3acf8 100644 --- a/plugins/codex/package.json +++ b/plugins/codex/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for codex", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/codex/webcmd-plugin.json b/plugins/codex/webcmd-plugin.json index ed317e7f..d10faa62 100644 --- a/plugins/codex/webcmd-plugin.json +++ b/plugins/codex/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "codex", "version": "0.1.0", "description": "Webcmd commands for codex", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/coingecko/package.json b/plugins/coingecko/package.json index 4e6f944f..615bd3d6 100644 --- a/plugins/coingecko/package.json +++ b/plugins/coingecko/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for coingecko", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/coingecko/webcmd-plugin.json b/plugins/coingecko/webcmd-plugin.json index 26920440..92c2b9de 100644 --- a/plugins/coingecko/webcmd-plugin.json +++ b/plugins/coingecko/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "coingecko", "version": "0.1.0", "description": "Webcmd commands for coingecko", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/confluence/package.json b/plugins/confluence/package.json index 6effe708..b3cc4f71 100644 --- a/plugins/confluence/package.json +++ b/plugins/confluence/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for confluence", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/confluence/webcmd-plugin.json b/plugins/confluence/webcmd-plugin.json index 33c22b31..07a91eee 100644 --- a/plugins/confluence/webcmd-plugin.json +++ b/plugins/confluence/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "confluence", "version": "0.1.0", "description": "Webcmd commands for confluence", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/coupang/package.json b/plugins/coupang/package.json index bcf8dddc..34b3b5a5 100644 --- a/plugins/coupang/package.json +++ b/plugins/coupang/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for coupang", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/coupang/webcmd-plugin.json b/plugins/coupang/webcmd-plugin.json index 0174e3c5..29764770 100644 --- a/plugins/coupang/webcmd-plugin.json +++ b/plugins/coupang/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "coupang", "version": "0.1.0", "description": "Webcmd commands for coupang", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/crates/package.json b/plugins/crates/package.json index 44a9684b..67d76e80 100644 --- a/plugins/crates/package.json +++ b/plugins/crates/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for crates", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/crates/webcmd-plugin.json b/plugins/crates/webcmd-plugin.json index 90d97b65..fe481355 100644 --- a/plugins/crates/webcmd-plugin.json +++ b/plugins/crates/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "crates", "version": "0.1.0", "description": "Webcmd commands for crates", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/cursor/package.json b/plugins/cursor/package.json index 661b1920..38e489c0 100644 --- a/plugins/cursor/package.json +++ b/plugins/cursor/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for cursor", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/cursor/webcmd-plugin.json b/plugins/cursor/webcmd-plugin.json index 2663daae..f9b6daeb 100644 --- a/plugins/cursor/webcmd-plugin.json +++ b/plugins/cursor/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "cursor", "version": "0.1.0", "description": "Webcmd commands for cursor", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/dblp/package.json b/plugins/dblp/package.json index 753a4eb4..611e50c0 100644 --- a/plugins/dblp/package.json +++ b/plugins/dblp/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for dblp", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/dblp/webcmd-plugin.json b/plugins/dblp/webcmd-plugin.json index 928f0674..10838dd3 100644 --- a/plugins/dblp/webcmd-plugin.json +++ b/plugins/dblp/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "dblp", "version": "0.1.0", "description": "Webcmd commands for dblp", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/defillama/package.json b/plugins/defillama/package.json index 58d2beac..f8a9fa2b 100644 --- a/plugins/defillama/package.json +++ b/plugins/defillama/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for defillama", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/defillama/webcmd-plugin.json b/plugins/defillama/webcmd-plugin.json index 2c5007a7..8dc0a06a 100644 --- a/plugins/defillama/webcmd-plugin.json +++ b/plugins/defillama/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "defillama", "version": "0.1.0", "description": "Webcmd commands for defillama", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/devto/package.json b/plugins/devto/package.json index eed8049b..8c17982c 100644 --- a/plugins/devto/package.json +++ b/plugins/devto/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for devto", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/devto/webcmd-plugin.json b/plugins/devto/webcmd-plugin.json index c15d6854..6d219b23 100644 --- a/plugins/devto/webcmd-plugin.json +++ b/plugins/devto/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "devto", "version": "0.1.0", "description": "Webcmd commands for devto", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/dictionary/package.json b/plugins/dictionary/package.json index 024cf89c..6a899036 100644 --- a/plugins/dictionary/package.json +++ b/plugins/dictionary/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for dictionary", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/dictionary/webcmd-plugin.json b/plugins/dictionary/webcmd-plugin.json index 4b68e1d0..ac54d058 100644 --- a/plugins/dictionary/webcmd-plugin.json +++ b/plugins/dictionary/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "dictionary", "version": "0.1.0", "description": "Webcmd commands for dictionary", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/discord-app/package.json b/plugins/discord-app/package.json index e315de0c..abc0dafe 100644 --- a/plugins/discord-app/package.json +++ b/plugins/discord-app/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for discord-app", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/discord-app/webcmd-plugin.json b/plugins/discord-app/webcmd-plugin.json index 6ca6fbb3..5527a662 100644 --- a/plugins/discord-app/webcmd-plugin.json +++ b/plugins/discord-app/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "discord-app", "version": "0.1.0", "description": "Webcmd commands for discord-app", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/district/package.json b/plugins/district/package.json index ea36f1f8..f70ab5d2 100644 --- a/plugins/district/package.json +++ b/plugins/district/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for district", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/district/webcmd-plugin.json b/plugins/district/webcmd-plugin.json index f21c8150..dd0cc994 100644 --- a/plugins/district/webcmd-plugin.json +++ b/plugins/district/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "district", "version": "0.1.0", "description": "Webcmd commands for district", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/dockerhub/package.json b/plugins/dockerhub/package.json index 79d57c1a..d35b775e 100644 --- a/plugins/dockerhub/package.json +++ b/plugins/dockerhub/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for dockerhub", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/dockerhub/webcmd-plugin.json b/plugins/dockerhub/webcmd-plugin.json index 8f6fe160..b02e6868 100644 --- a/plugins/dockerhub/webcmd-plugin.json +++ b/plugins/dockerhub/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "dockerhub", "version": "0.1.0", "description": "Webcmd commands for dockerhub", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/duckduckgo/package.json b/plugins/duckduckgo/package.json index 20331154..286efb5b 100644 --- a/plugins/duckduckgo/package.json +++ b/plugins/duckduckgo/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for duckduckgo", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/duckduckgo/webcmd-plugin.json b/plugins/duckduckgo/webcmd-plugin.json index a6e12bd4..6d914ae5 100644 --- a/plugins/duckduckgo/webcmd-plugin.json +++ b/plugins/duckduckgo/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "duckduckgo", "version": "0.1.0", "description": "Webcmd commands for duckduckgo", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/endoflife/package.json b/plugins/endoflife/package.json index 75f77dde..13393dfb 100644 --- a/plugins/endoflife/package.json +++ b/plugins/endoflife/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for endoflife", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/endoflife/webcmd-plugin.json b/plugins/endoflife/webcmd-plugin.json index 53ac5384..5df06ba0 100644 --- a/plugins/endoflife/webcmd-plugin.json +++ b/plugins/endoflife/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "endoflife", "version": "0.1.0", "description": "Webcmd commands for endoflife", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/facebook/package.json b/plugins/facebook/package.json index b7a769a8..5f3e9449 100644 --- a/plugins/facebook/package.json +++ b/plugins/facebook/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for facebook", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/facebook/webcmd-plugin.json b/plugins/facebook/webcmd-plugin.json index cbd41c19..8d5680ad 100644 --- a/plugins/facebook/webcmd-plugin.json +++ b/plugins/facebook/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "facebook", "version": "0.1.0", "description": "Webcmd commands for facebook", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/flathub/package.json b/plugins/flathub/package.json index 43dc53b2..6d124a13 100644 --- a/plugins/flathub/package.json +++ b/plugins/flathub/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for flathub", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/flathub/webcmd-plugin.json b/plugins/flathub/webcmd-plugin.json index 35e7a073..5219d3fb 100644 --- a/plugins/flathub/webcmd-plugin.json +++ b/plugins/flathub/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "flathub", "version": "0.1.0", "description": "Webcmd commands for flathub", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/gemini/package.json b/plugins/gemini/package.json index d21448d7..f68bb877 100644 --- a/plugins/gemini/package.json +++ b/plugins/gemini/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for gemini", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/gemini/webcmd-plugin.json b/plugins/gemini/webcmd-plugin.json index eb6d0b61..9f256de3 100644 --- a/plugins/gemini/webcmd-plugin.json +++ b/plugins/gemini/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "gemini", "version": "0.1.0", "description": "Webcmd commands for gemini", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/geogebra/package.json b/plugins/geogebra/package.json index 88de598e..798e7a86 100644 --- a/plugins/geogebra/package.json +++ b/plugins/geogebra/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for geogebra", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/geogebra/webcmd-plugin.json b/plugins/geogebra/webcmd-plugin.json index 8d2a3466..ba38435d 100644 --- a/plugins/geogebra/webcmd-plugin.json +++ b/plugins/geogebra/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "geogebra", "version": "0.1.0", "description": "Webcmd commands for geogebra", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/github-trending/package.json b/plugins/github-trending/package.json index 54981a4c..eaa4924b 100644 --- a/plugins/github-trending/package.json +++ b/plugins/github-trending/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for github-trending", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/github-trending/webcmd-plugin.json b/plugins/github-trending/webcmd-plugin.json index 955c4851..92280055 100644 --- a/plugins/github-trending/webcmd-plugin.json +++ b/plugins/github-trending/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "github-trending", "version": "0.1.0", "description": "Webcmd commands for github-trending", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/github/package.json b/plugins/github/package.json index c07bd0bb..e643cadd 100644 --- a/plugins/github/package.json +++ b/plugins/github/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for github", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/github/webcmd-plugin.json b/plugins/github/webcmd-plugin.json index d4cebbcb..c3f57e3f 100644 --- a/plugins/github/webcmd-plugin.json +++ b/plugins/github/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "github", "version": "0.1.0", "description": "Webcmd commands for github", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/google-scholar/package.json b/plugins/google-scholar/package.json index e6c70389..6e670578 100644 --- a/plugins/google-scholar/package.json +++ b/plugins/google-scholar/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for google-scholar", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/google-scholar/webcmd-plugin.json b/plugins/google-scholar/webcmd-plugin.json index c6b075aa..f692aa7e 100644 --- a/plugins/google-scholar/webcmd-plugin.json +++ b/plugins/google-scholar/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "google-scholar", "version": "0.1.0", "description": "Webcmd commands for google-scholar", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/google/package.json b/plugins/google/package.json index 3a0ee2e0..ffc7f672 100644 --- a/plugins/google/package.json +++ b/plugins/google/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for google", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/google/webcmd-plugin.json b/plugins/google/webcmd-plugin.json index b6c9df50..bcf09ada 100644 --- a/plugins/google/webcmd-plugin.json +++ b/plugins/google/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "google", "version": "0.1.0", "description": "Webcmd commands for google", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/goproxy/package.json b/plugins/goproxy/package.json index 1892470e..48d199d1 100644 --- a/plugins/goproxy/package.json +++ b/plugins/goproxy/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for goproxy", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/goproxy/webcmd-plugin.json b/plugins/goproxy/webcmd-plugin.json index 63a5e609..978b6c23 100644 --- a/plugins/goproxy/webcmd-plugin.json +++ b/plugins/goproxy/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "goproxy", "version": "0.1.0", "description": "Webcmd commands for goproxy", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/grok/package.json b/plugins/grok/package.json index bb1ec11a..fcde49e7 100644 --- a/plugins/grok/package.json +++ b/plugins/grok/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for grok", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/grok/webcmd-plugin.json b/plugins/grok/webcmd-plugin.json index b1247076..cc5556d3 100644 --- a/plugins/grok/webcmd-plugin.json +++ b/plugins/grok/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "grok", "version": "0.1.0", "description": "Webcmd commands for grok", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/hackernews/package.json b/plugins/hackernews/package.json index a887356e..d9c93a5d 100644 --- a/plugins/hackernews/package.json +++ b/plugins/hackernews/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for hackernews", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/hackernews/webcmd-plugin.json b/plugins/hackernews/webcmd-plugin.json index 352b6a28..8c23460c 100644 --- a/plugins/hackernews/webcmd-plugin.json +++ b/plugins/hackernews/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "hackernews", "version": "0.1.0", "description": "Webcmd commands for hackernews", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/hf/package.json b/plugins/hf/package.json index 1c83e389..85c55b29 100644 --- a/plugins/hf/package.json +++ b/plugins/hf/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for hf", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/hf/webcmd-plugin.json b/plugins/hf/webcmd-plugin.json index d71bd2c1..78ab285b 100644 --- a/plugins/hf/webcmd-plugin.json +++ b/plugins/hf/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "hf", "version": "0.1.0", "description": "Webcmd commands for hf", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/homebrew/package.json b/plugins/homebrew/package.json index 08fdd8e2..45ffc772 100644 --- a/plugins/homebrew/package.json +++ b/plugins/homebrew/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for homebrew", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/homebrew/webcmd-plugin.json b/plugins/homebrew/webcmd-plugin.json index 7af1dbc8..2a3fd3ff 100644 --- a/plugins/homebrew/webcmd-plugin.json +++ b/plugins/homebrew/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "homebrew", "version": "0.1.0", "description": "Webcmd commands for homebrew", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/imdb/package.json b/plugins/imdb/package.json index e93ef221..f57425cc 100644 --- a/plugins/imdb/package.json +++ b/plugins/imdb/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for imdb", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/imdb/webcmd-plugin.json b/plugins/imdb/webcmd-plugin.json index 0dd128df..8a561f2c 100644 --- a/plugins/imdb/webcmd-plugin.json +++ b/plugins/imdb/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "imdb", "version": "0.1.0", "description": "Webcmd commands for imdb", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/indeed/package.json b/plugins/indeed/package.json index 0af209d6..570b555e 100644 --- a/plugins/indeed/package.json +++ b/plugins/indeed/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for indeed", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/indeed/webcmd-plugin.json b/plugins/indeed/webcmd-plugin.json index 9086b718..e03231f7 100644 --- a/plugins/indeed/webcmd-plugin.json +++ b/plugins/indeed/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "indeed", "version": "0.1.0", "description": "Webcmd commands for indeed", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/instagram/package.json b/plugins/instagram/package.json index 4ad2309d..0328e879 100644 --- a/plugins/instagram/package.json +++ b/plugins/instagram/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for instagram", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/instagram/webcmd-plugin.json b/plugins/instagram/webcmd-plugin.json index aec08f8a..7cfddad4 100644 --- a/plugins/instagram/webcmd-plugin.json +++ b/plugins/instagram/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "instagram", "version": "0.1.0", "description": "Webcmd commands for instagram", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/jira/package.json b/plugins/jira/package.json index a86b72df..e85452bb 100644 --- a/plugins/jira/package.json +++ b/plugins/jira/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for jira", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/jira/webcmd-plugin.json b/plugins/jira/webcmd-plugin.json index 0e7064ff..9bf66772 100644 --- a/plugins/jira/webcmd-plugin.json +++ b/plugins/jira/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "jira", "version": "0.1.0", "description": "Webcmd commands for jira", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/lesswrong/package.json b/plugins/lesswrong/package.json index 16916178..d8c23d26 100644 --- a/plugins/lesswrong/package.json +++ b/plugins/lesswrong/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for lesswrong", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/lesswrong/webcmd-plugin.json b/plugins/lesswrong/webcmd-plugin.json index 07615531..61b29781 100644 --- a/plugins/lesswrong/webcmd-plugin.json +++ b/plugins/lesswrong/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "lesswrong", "version": "0.1.0", "description": "Webcmd commands for lesswrong", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/lichess/package.json b/plugins/lichess/package.json index 02ea271b..da3b192e 100644 --- a/plugins/lichess/package.json +++ b/plugins/lichess/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for lichess", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/lichess/webcmd-plugin.json b/plugins/lichess/webcmd-plugin.json index 22e91e58..db35c3a6 100644 --- a/plugins/lichess/webcmd-plugin.json +++ b/plugins/lichess/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "lichess", "version": "0.1.0", "description": "Webcmd commands for lichess", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/linkedin-learning/package.json b/plugins/linkedin-learning/package.json index 57da6a1f..69e16a1c 100644 --- a/plugins/linkedin-learning/package.json +++ b/plugins/linkedin-learning/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for linkedin-learning", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/linkedin-learning/webcmd-plugin.json b/plugins/linkedin-learning/webcmd-plugin.json index ad9281c1..4d114f76 100644 --- a/plugins/linkedin-learning/webcmd-plugin.json +++ b/plugins/linkedin-learning/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "linkedin-learning", "version": "0.1.0", "description": "Webcmd commands for linkedin-learning", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/linkedin/package.json b/plugins/linkedin/package.json index 49c55c9a..d63e934d 100644 --- a/plugins/linkedin/package.json +++ b/plugins/linkedin/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "LinkedIn profile, network, messaging, job, and Sales Navigator commands for WebCMD", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/linkedin/webcmd-plugin.json b/plugins/linkedin/webcmd-plugin.json index 22cc0dbc..5b7616e5 100644 --- a/plugins/linkedin/webcmd-plugin.json +++ b/plugins/linkedin/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "linkedin", "version": "0.1.0", "description": "LinkedIn profile, network, messaging, job, and Sales Navigator commands for WebCMD", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/lobsters/package.json b/plugins/lobsters/package.json index 594cfa0c..2b18c5a6 100644 --- a/plugins/lobsters/package.json +++ b/plugins/lobsters/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for lobsters", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/lobsters/webcmd-plugin.json b/plugins/lobsters/webcmd-plugin.json index 807f9446..1e6a3116 100644 --- a/plugins/lobsters/webcmd-plugin.json +++ b/plugins/lobsters/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "lobsters", "version": "0.1.0", "description": "Webcmd commands for lobsters", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/manus/package.json b/plugins/manus/package.json index 5c2d7839..0dfa5624 100644 --- a/plugins/manus/package.json +++ b/plugins/manus/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for manus", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/manus/webcmd-plugin.json b/plugins/manus/webcmd-plugin.json index f3abee47..1a12b113 100644 --- a/plugins/manus/webcmd-plugin.json +++ b/plugins/manus/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "manus", "version": "0.1.0", "description": "Webcmd commands for manus", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/maven/package.json b/plugins/maven/package.json index 9dbb871b..66d1d2f7 100644 --- a/plugins/maven/package.json +++ b/plugins/maven/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for maven", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/maven/webcmd-plugin.json b/plugins/maven/webcmd-plugin.json index cac95fdc..a00cbdec 100644 --- a/plugins/maven/webcmd-plugin.json +++ b/plugins/maven/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "maven", "version": "0.1.0", "description": "Webcmd commands for maven", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/mdn/package.json b/plugins/mdn/package.json index 3db96f0f..477ed0fd 100644 --- a/plugins/mdn/package.json +++ b/plugins/mdn/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for mdn", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/mdn/webcmd-plugin.json b/plugins/mdn/webcmd-plugin.json index dedc3b19..b0e5b7a7 100644 --- a/plugins/mdn/webcmd-plugin.json +++ b/plugins/mdn/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "mdn", "version": "0.1.0", "description": "Webcmd commands for mdn", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/medium/package.json b/plugins/medium/package.json index 14563e9e..0c91cc24 100644 --- a/plugins/medium/package.json +++ b/plugins/medium/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for medium", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/medium/webcmd-plugin.json b/plugins/medium/webcmd-plugin.json index 958ea917..62cbcc54 100644 --- a/plugins/medium/webcmd-plugin.json +++ b/plugins/medium/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "medium", "version": "0.1.0", "description": "Webcmd commands for medium", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/mercury/package.json b/plugins/mercury/package.json index 2de5b782..5318caee 100644 --- a/plugins/mercury/package.json +++ b/plugins/mercury/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for mercury", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/mercury/webcmd-plugin.json b/plugins/mercury/webcmd-plugin.json index 900dd7b9..a91e0677 100644 --- a/plugins/mercury/webcmd-plugin.json +++ b/plugins/mercury/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "mercury", "version": "0.1.0", "description": "Webcmd commands for mercury", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/notebooklm/package.json b/plugins/notebooklm/package.json index 722b3499..1b80661e 100644 --- a/plugins/notebooklm/package.json +++ b/plugins/notebooklm/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for notebooklm", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/notebooklm/webcmd-plugin.json b/plugins/notebooklm/webcmd-plugin.json index ee5d535f..abbe765a 100644 --- a/plugins/notebooklm/webcmd-plugin.json +++ b/plugins/notebooklm/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "notebooklm", "version": "0.1.0", "description": "Webcmd commands for notebooklm", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/npm/package.json b/plugins/npm/package.json index 70514fe0..401f80aa 100644 --- a/plugins/npm/package.json +++ b/plugins/npm/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for npm", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/npm/webcmd-plugin.json b/plugins/npm/webcmd-plugin.json index 770fabb9..621f1bdd 100644 --- a/plugins/npm/webcmd-plugin.json +++ b/plugins/npm/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "npm", "version": "0.1.0", "description": "Webcmd commands for npm", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/nuget/package.json b/plugins/nuget/package.json index c0218e0c..dba994b3 100644 --- a/plugins/nuget/package.json +++ b/plugins/nuget/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for nuget", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/nuget/webcmd-plugin.json b/plugins/nuget/webcmd-plugin.json index d41f90ca..33ab90c1 100644 --- a/plugins/nuget/webcmd-plugin.json +++ b/plugins/nuget/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "nuget", "version": "0.1.0", "description": "Webcmd commands for nuget", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/nvd/package.json b/plugins/nvd/package.json index cb1ac8cc..9d61f8c6 100644 --- a/plugins/nvd/package.json +++ b/plugins/nvd/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for nvd", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/nvd/webcmd-plugin.json b/plugins/nvd/webcmd-plugin.json index 9cf8aa11..4af57f33 100644 --- a/plugins/nvd/webcmd-plugin.json +++ b/plugins/nvd/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "nvd", "version": "0.1.0", "description": "Webcmd commands for nvd", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/oeis/package.json b/plugins/oeis/package.json index e6084f70..0f1e3037 100644 --- a/plugins/oeis/package.json +++ b/plugins/oeis/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for oeis", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/oeis/webcmd-plugin.json b/plugins/oeis/webcmd-plugin.json index e836bfff..da1465b9 100644 --- a/plugins/oeis/webcmd-plugin.json +++ b/plugins/oeis/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "oeis", "version": "0.1.0", "description": "Webcmd commands for oeis", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/openalex/package.json b/plugins/openalex/package.json index 6afc41df..0a3087a5 100644 --- a/plugins/openalex/package.json +++ b/plugins/openalex/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for openalex", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/openalex/webcmd-plugin.json b/plugins/openalex/webcmd-plugin.json index ff953e9e..1ed71a96 100644 --- a/plugins/openalex/webcmd-plugin.json +++ b/plugins/openalex/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "openalex", "version": "0.1.0", "description": "Webcmd commands for openalex", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/openfda/package.json b/plugins/openfda/package.json index 373eeb5b..8e17a011 100644 --- a/plugins/openfda/package.json +++ b/plugins/openfda/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for openfda", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/openfda/webcmd-plugin.json b/plugins/openfda/webcmd-plugin.json index 34261831..95b6880a 100644 --- a/plugins/openfda/webcmd-plugin.json +++ b/plugins/openfda/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "openfda", "version": "0.1.0", "description": "Webcmd commands for openfda", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/openreview/package.json b/plugins/openreview/package.json index ba5240b1..51d0249a 100644 --- a/plugins/openreview/package.json +++ b/plugins/openreview/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for openreview", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/openreview/webcmd-plugin.json b/plugins/openreview/webcmd-plugin.json index 3268b4dd..d68a5170 100644 --- a/plugins/openreview/webcmd-plugin.json +++ b/plugins/openreview/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "openreview", "version": "0.1.0", "description": "Webcmd commands for openreview", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/osv/package.json b/plugins/osv/package.json index 90611cf8..8aa6f6e0 100644 --- a/plugins/osv/package.json +++ b/plugins/osv/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for osv", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/osv/webcmd-plugin.json b/plugins/osv/webcmd-plugin.json index 1cd9bba6..90af1fd1 100644 --- a/plugins/osv/webcmd-plugin.json +++ b/plugins/osv/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "osv", "version": "0.1.0", "description": "Webcmd commands for osv", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/packagist/package.json b/plugins/packagist/package.json index ee49518b..c9381c72 100644 --- a/plugins/packagist/package.json +++ b/plugins/packagist/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for packagist", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/packagist/webcmd-plugin.json b/plugins/packagist/webcmd-plugin.json index 9609f212..65a4f914 100644 --- a/plugins/packagist/webcmd-plugin.json +++ b/plugins/packagist/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "packagist", "version": "0.1.0", "description": "Webcmd commands for packagist", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/paperreview/package.json b/plugins/paperreview/package.json index 5fe2869b..f3b9d67b 100644 --- a/plugins/paperreview/package.json +++ b/plugins/paperreview/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for paperreview", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/paperreview/webcmd-plugin.json b/plugins/paperreview/webcmd-plugin.json index a3a6d2f3..cb6f36b6 100644 --- a/plugins/paperreview/webcmd-plugin.json +++ b/plugins/paperreview/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "paperreview", "version": "0.1.0", "description": "Webcmd commands for paperreview", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/pixiv/package.json b/plugins/pixiv/package.json index 49cf60a3..e42ef3aa 100644 --- a/plugins/pixiv/package.json +++ b/plugins/pixiv/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for pixiv", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/pixiv/webcmd-plugin.json b/plugins/pixiv/webcmd-plugin.json index 5f6e3d53..6dadd603 100644 --- a/plugins/pixiv/webcmd-plugin.json +++ b/plugins/pixiv/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "pixiv", "version": "0.1.0", "description": "Webcmd commands for pixiv", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/practo/package.json b/plugins/practo/package.json index 6ea20f33..0f4b022f 100644 --- a/plugins/practo/package.json +++ b/plugins/practo/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for practo", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/practo/webcmd-plugin.json b/plugins/practo/webcmd-plugin.json index 176f73e0..259e7d16 100644 --- a/plugins/practo/webcmd-plugin.json +++ b/plugins/practo/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "practo", "version": "0.1.0", "description": "Webcmd commands for practo", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/producthunt/package.json b/plugins/producthunt/package.json index d8aa528e..c64f446d 100644 --- a/plugins/producthunt/package.json +++ b/plugins/producthunt/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for producthunt", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/producthunt/webcmd-plugin.json b/plugins/producthunt/webcmd-plugin.json index 0dfcf6b6..3c6d24cf 100644 --- a/plugins/producthunt/webcmd-plugin.json +++ b/plugins/producthunt/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "producthunt", "version": "0.1.0", "description": "Webcmd commands for producthunt", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/pubmed/package.json b/plugins/pubmed/package.json index eaf139d7..4dedad28 100644 --- a/plugins/pubmed/package.json +++ b/plugins/pubmed/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for pubmed", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/pubmed/webcmd-plugin.json b/plugins/pubmed/webcmd-plugin.json index 32e67167..b0ac0555 100644 --- a/plugins/pubmed/webcmd-plugin.json +++ b/plugins/pubmed/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "pubmed", "version": "0.1.0", "description": "Webcmd commands for pubmed", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/qoder/package.json b/plugins/qoder/package.json index ca9f37d4..31cf8ef0 100644 --- a/plugins/qoder/package.json +++ b/plugins/qoder/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for qoder", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/qoder/webcmd-plugin.json b/plugins/qoder/webcmd-plugin.json index 7028d075..b65983b3 100644 --- a/plugins/qoder/webcmd-plugin.json +++ b/plugins/qoder/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "qoder", "version": "0.1.0", "description": "Webcmd commands for qoder", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/reddit/package.json b/plugins/reddit/package.json index d8ecedf6..6bbf9a6f 100644 --- a/plugins/reddit/package.json +++ b/plugins/reddit/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for reddit", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/reddit/webcmd-plugin.json b/plugins/reddit/webcmd-plugin.json index baf80511..962ff510 100644 --- a/plugins/reddit/webcmd-plugin.json +++ b/plugins/reddit/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "reddit", "version": "0.1.0", "description": "Webcmd commands for reddit", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/rest-countries/package.json b/plugins/rest-countries/package.json index 0808e18f..dcba2529 100644 --- a/plugins/rest-countries/package.json +++ b/plugins/rest-countries/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for rest-countries", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/rest-countries/webcmd-plugin.json b/plugins/rest-countries/webcmd-plugin.json index 08078ba2..49f79a76 100644 --- a/plugins/rest-countries/webcmd-plugin.json +++ b/plugins/rest-countries/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "rest-countries", "version": "0.1.0", "description": "Webcmd commands for rest-countries", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/reuters/package.json b/plugins/reuters/package.json index b74d92f5..c17c6744 100644 --- a/plugins/reuters/package.json +++ b/plugins/reuters/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for reuters", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/reuters/webcmd-plugin.json b/plugins/reuters/webcmd-plugin.json index ee835d3d..248d4df6 100644 --- a/plugins/reuters/webcmd-plugin.json +++ b/plugins/reuters/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "reuters", "version": "0.1.0", "description": "Webcmd commands for reuters", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/rfc/package.json b/plugins/rfc/package.json index 9c638834..aabf4509 100644 --- a/plugins/rfc/package.json +++ b/plugins/rfc/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for rfc", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/rfc/webcmd-plugin.json b/plugins/rfc/webcmd-plugin.json index 9c156097..7ef646b6 100644 --- a/plugins/rfc/webcmd-plugin.json +++ b/plugins/rfc/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "rfc", "version": "0.1.0", "description": "Webcmd commands for rfc", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/rubygems/package.json b/plugins/rubygems/package.json index 251bfb69..f51b3967 100644 --- a/plugins/rubygems/package.json +++ b/plugins/rubygems/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for rubygems", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/rubygems/webcmd-plugin.json b/plugins/rubygems/webcmd-plugin.json index f09eba6e..1bdb2507 100644 --- a/plugins/rubygems/webcmd-plugin.json +++ b/plugins/rubygems/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "rubygems", "version": "0.1.0", "description": "Webcmd commands for rubygems", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/semanticscholar/package.json b/plugins/semanticscholar/package.json index bcd57b1f..18cbdaab 100644 --- a/plugins/semanticscholar/package.json +++ b/plugins/semanticscholar/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for semanticscholar", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/semanticscholar/webcmd-plugin.json b/plugins/semanticscholar/webcmd-plugin.json index 91587b09..7ad39a33 100644 --- a/plugins/semanticscholar/webcmd-plugin.json +++ b/plugins/semanticscholar/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "semanticscholar", "version": "0.1.0", "description": "Webcmd commands for semanticscholar", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/slock/package.json b/plugins/slock/package.json index e4c5230b..8040e4a3 100644 --- a/plugins/slock/package.json +++ b/plugins/slock/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for slock", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/slock/webcmd-plugin.json b/plugins/slock/webcmd-plugin.json index 8d11f27a..ac1fc698 100644 --- a/plugins/slock/webcmd-plugin.json +++ b/plugins/slock/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "slock", "version": "0.1.0", "description": "Webcmd commands for slock", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/spotify/package.json b/plugins/spotify/package.json index b74ab084..d81bcaec 100644 --- a/plugins/spotify/package.json +++ b/plugins/spotify/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for spotify", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/spotify/webcmd-plugin.json b/plugins/spotify/webcmd-plugin.json index a91d4433..a2e57488 100644 --- a/plugins/spotify/webcmd-plugin.json +++ b/plugins/spotify/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "spotify", "version": "0.1.0", "description": "Webcmd commands for spotify", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/stackoverflow/package.json b/plugins/stackoverflow/package.json index 49890f32..59d1c274 100644 --- a/plugins/stackoverflow/package.json +++ b/plugins/stackoverflow/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for stackoverflow", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/stackoverflow/webcmd-plugin.json b/plugins/stackoverflow/webcmd-plugin.json index 5a92fd74..ddafabbd 100644 --- a/plugins/stackoverflow/webcmd-plugin.json +++ b/plugins/stackoverflow/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "stackoverflow", "version": "0.1.0", "description": "Webcmd commands for stackoverflow", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/steam/package.json b/plugins/steam/package.json index c3f1f220..9d47d1c9 100644 --- a/plugins/steam/package.json +++ b/plugins/steam/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for steam", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/steam/webcmd-plugin.json b/plugins/steam/webcmd-plugin.json index 2f89676f..70db1491 100644 --- a/plugins/steam/webcmd-plugin.json +++ b/plugins/steam/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "steam", "version": "0.1.0", "description": "Webcmd commands for steam", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/substack/package.json b/plugins/substack/package.json index 038496ee..1c8b3f97 100644 --- a/plugins/substack/package.json +++ b/plugins/substack/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for substack", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/substack/webcmd-plugin.json b/plugins/substack/webcmd-plugin.json index 60ce4e4e..f873a772 100644 --- a/plugins/substack/webcmd-plugin.json +++ b/plugins/substack/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "substack", "version": "0.1.0", "description": "Webcmd commands for substack", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/suno/package.json b/plugins/suno/package.json index a3fc80b5..3478c81b 100644 --- a/plugins/suno/package.json +++ b/plugins/suno/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for suno", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/suno/webcmd-plugin.json b/plugins/suno/webcmd-plugin.json index 5ed86569..d3336237 100644 --- a/plugins/suno/webcmd-plugin.json +++ b/plugins/suno/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "suno", "version": "0.1.0", "description": "Webcmd commands for suno", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/tiktok/package.json b/plugins/tiktok/package.json index 3dda3f26..0ff730ce 100644 --- a/plugins/tiktok/package.json +++ b/plugins/tiktok/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for tiktok", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/tiktok/webcmd-plugin.json b/plugins/tiktok/webcmd-plugin.json index dc1c69d3..deb19bf6 100644 --- a/plugins/tiktok/webcmd-plugin.json +++ b/plugins/tiktok/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "tiktok", "version": "0.1.0", "description": "Webcmd commands for tiktok", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/trae-solo/package.json b/plugins/trae-solo/package.json index 5ebba65f..f5ca1168 100644 --- a/plugins/trae-solo/package.json +++ b/plugins/trae-solo/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for trae-solo", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/trae-solo/webcmd-plugin.json b/plugins/trae-solo/webcmd-plugin.json index ba46b5e2..61185156 100644 --- a/plugins/trae-solo/webcmd-plugin.json +++ b/plugins/trae-solo/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "trae-solo", "version": "0.1.0", "description": "Webcmd commands for trae-solo", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/trip/package.json b/plugins/trip/package.json index 47834d8f..2bfcf608 100644 --- a/plugins/trip/package.json +++ b/plugins/trip/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for trip", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/trip/webcmd-plugin.json b/plugins/trip/webcmd-plugin.json index cd733165..70f6b4a7 100644 --- a/plugins/trip/webcmd-plugin.json +++ b/plugins/trip/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "trip", "version": "0.1.0", "description": "Webcmd commands for trip", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/tvmaze/package.json b/plugins/tvmaze/package.json index 8e209d88..61bd5be9 100644 --- a/plugins/tvmaze/package.json +++ b/plugins/tvmaze/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for tvmaze", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/tvmaze/webcmd-plugin.json b/plugins/tvmaze/webcmd-plugin.json index 08722a28..32eee5ae 100644 --- a/plugins/tvmaze/webcmd-plugin.json +++ b/plugins/tvmaze/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "tvmaze", "version": "0.1.0", "description": "Webcmd commands for tvmaze", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/twitter/package.json b/plugins/twitter/package.json index 0472e1bb..3882906d 100644 --- a/plugins/twitter/package.json +++ b/plugins/twitter/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for twitter", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/twitter/webcmd-plugin.json b/plugins/twitter/webcmd-plugin.json index ffd70a23..c68da3c9 100644 --- a/plugins/twitter/webcmd-plugin.json +++ b/plugins/twitter/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "twitter", "version": "0.1.0", "description": "Webcmd commands for twitter", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/uiverse/package.json b/plugins/uiverse/package.json index c430d185..cb156356 100644 --- a/plugins/uiverse/package.json +++ b/plugins/uiverse/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for uiverse", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/uiverse/webcmd-plugin.json b/plugins/uiverse/webcmd-plugin.json index fdae328a..855b4da5 100644 --- a/plugins/uiverse/webcmd-plugin.json +++ b/plugins/uiverse/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "uiverse", "version": "0.1.0", "description": "Webcmd commands for uiverse", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/upwork/package.json b/plugins/upwork/package.json index 82001dd4..1c87eeae 100644 --- a/plugins/upwork/package.json +++ b/plugins/upwork/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for upwork", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/upwork/webcmd-plugin.json b/plugins/upwork/webcmd-plugin.json index 92dfeca3..3d1532c8 100644 --- a/plugins/upwork/webcmd-plugin.json +++ b/plugins/upwork/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "upwork", "version": "0.1.0", "description": "Webcmd commands for upwork", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/web/package.json b/plugins/web/package.json index 6f084e4e..43e82988 100644 --- a/plugins/web/package.json +++ b/plugins/web/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for web", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/web/webcmd-plugin.json b/plugins/web/webcmd-plugin.json index 5bfa40eb..a77c7cec 100644 --- a/plugins/web/webcmd-plugin.json +++ b/plugins/web/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "web", "version": "0.1.0", "description": "Webcmd commands for web", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/wikidata/package.json b/plugins/wikidata/package.json index f79e8ade..e3a3429a 100644 --- a/plugins/wikidata/package.json +++ b/plugins/wikidata/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for wikidata", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/wikidata/webcmd-plugin.json b/plugins/wikidata/webcmd-plugin.json index 6f26e148..082cc48b 100644 --- a/plugins/wikidata/webcmd-plugin.json +++ b/plugins/wikidata/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "wikidata", "version": "0.1.0", "description": "Webcmd commands for wikidata", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/wikipedia/package.json b/plugins/wikipedia/package.json index 96663cb2..aa64c3e9 100644 --- a/plugins/wikipedia/package.json +++ b/plugins/wikipedia/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for wikipedia", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/wikipedia/webcmd-plugin.json b/plugins/wikipedia/webcmd-plugin.json index ccd8264b..905352ca 100644 --- a/plugins/wikipedia/webcmd-plugin.json +++ b/plugins/wikipedia/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "wikipedia", "version": "0.1.0", "description": "Webcmd commands for wikipedia", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/wttr/package.json b/plugins/wttr/package.json index 0a99bfb4..4a81c54e 100644 --- a/plugins/wttr/package.json +++ b/plugins/wttr/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for wttr", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/wttr/webcmd-plugin.json b/plugins/wttr/webcmd-plugin.json index 8793ee39..622bb86e 100644 --- a/plugins/wttr/webcmd-plugin.json +++ b/plugins/wttr/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "wttr", "version": "0.1.0", "description": "Webcmd commands for wttr", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/yahoo-finance/package.json b/plugins/yahoo-finance/package.json index b31e02c0..abf03af2 100644 --- a/plugins/yahoo-finance/package.json +++ b/plugins/yahoo-finance/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for yahoo-finance", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/yahoo-finance/webcmd-plugin.json b/plugins/yahoo-finance/webcmd-plugin.json index 52f83215..f94860d1 100644 --- a/plugins/yahoo-finance/webcmd-plugin.json +++ b/plugins/yahoo-finance/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "yahoo-finance", "version": "0.1.0", "description": "Webcmd commands for yahoo-finance", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/yahoo/package.json b/plugins/yahoo/package.json index a212464a..cc5b41ae 100644 --- a/plugins/yahoo/package.json +++ b/plugins/yahoo/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for yahoo", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/yahoo/webcmd-plugin.json b/plugins/yahoo/webcmd-plugin.json index 25cf2323..52a32373 100644 --- a/plugins/yahoo/webcmd-plugin.json +++ b/plugins/yahoo/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "yahoo", "version": "0.1.0", "description": "Webcmd commands for yahoo", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/yollomi/package.json b/plugins/yollomi/package.json index 02b781a6..aecb87f1 100644 --- a/plugins/yollomi/package.json +++ b/plugins/yollomi/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for yollomi", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/yollomi/webcmd-plugin.json b/plugins/yollomi/webcmd-plugin.json index 27b0c60f..ef138d12 100644 --- a/plugins/yollomi/webcmd-plugin.json +++ b/plugins/yollomi/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "yollomi", "version": "0.1.0", "description": "Webcmd commands for yollomi", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/youtube/package.json b/plugins/youtube/package.json index 1d369e72..d41566d7 100644 --- a/plugins/youtube/package.json +++ b/plugins/youtube/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for youtube", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/youtube/webcmd-plugin.json b/plugins/youtube/webcmd-plugin.json index da88fd9f..9ea368e3 100644 --- a/plugins/youtube/webcmd-plugin.json +++ b/plugins/youtube/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "youtube", "version": "0.1.0", "description": "Webcmd commands for youtube", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/zepto/package.json b/plugins/zepto/package.json index ae8caed7..dee55142 100644 --- a/plugins/zepto/package.json +++ b/plugins/zepto/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for zepto", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/zepto/webcmd-plugin.json b/plugins/zepto/webcmd-plugin.json index cb17ce5c..9296c1fa 100644 --- a/plugins/zepto/webcmd-plugin.json +++ b/plugins/zepto/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "zepto", "version": "0.1.0", "description": "Webcmd commands for zepto", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/plugins/zlibrary/package.json b/plugins/zlibrary/package.json index 6de144f7..856cf595 100644 --- a/plugins/zlibrary/package.json +++ b/plugins/zlibrary/package.json @@ -4,6 +4,6 @@ "type": "module", "description": "Webcmd commands for zlibrary", "peerDependencies": { - "@agentrhq/webcmd": ">=0.6.0" + "@agentrhq/webcmd": ">=0.5.3" } } diff --git a/plugins/zlibrary/webcmd-plugin.json b/plugins/zlibrary/webcmd-plugin.json index fd0df836..b5a35f96 100644 --- a/plugins/zlibrary/webcmd-plugin.json +++ b/plugins/zlibrary/webcmd-plugin.json @@ -2,7 +2,7 @@ "name": "zlibrary", "version": "0.1.0", "description": "Webcmd commands for zlibrary", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" diff --git a/scripts/migrate-cli-sites.mjs b/scripts/migrate-cli-sites.mjs index b09f7a4c..02497dd1 100644 --- a/scripts/migrate-cli-sites.mjs +++ b/scripts/migrate-cli-sites.mjs @@ -5,6 +5,9 @@ import * as path from 'node:path'; const root = process.cwd(); const sites = process.argv.slice(2); +const webcmdVersion = readJson(path.join(root, 'package.json'), {}).version; +if (!webcmdVersion) fail('Could not read version from package.json'); +const webcmdRange = `>=${webcmdVersion}`; const sharedRuntime = /((?:\.\.\/)+)_shared\/(?:common|desktop-commands|search-adapter|site-auth)\.js/g; if (sites.length === 0) fail('Usage: node scripts/migrate-cli-sites.mjs '); @@ -66,7 +69,7 @@ function migrate(site, commands) { version: '0.1.0', type: 'module', description, - peerDependencies: { '@agentrhq/webcmd': '>=0.6.0' }, + peerDependencies: { '@agentrhq/webcmd': webcmdRange }, }); } const pluginManifest = path.join(plugin, 'webcmd-plugin.json'); @@ -75,7 +78,7 @@ function migrate(site, commands) { name: site, version: '0.1.0', description, - webcmd: '>=0.6.0', + webcmd: webcmdRange, author: { name: 'WebCMD Agent', handle: 'agentrhq' }, }); } diff --git a/src/build-plugin-command-manifest.test.ts b/src/build-plugin-command-manifest.test.ts index d9e0425c..ae53b82f 100644 --- a/src/build-plugin-command-manifest.test.ts +++ b/src/build-plugin-command-manifest.test.ts @@ -213,6 +213,11 @@ describe('plugin command manifest', () => { }); it('requires the plugin-runtime release for LinkedIn', () => { + // The floor must match the webcmd version that actually ships + // plugin-runtime, not a hardcoded literal that drifts from package.json. + const { version } = JSON.parse(fs.readFileSync('package.json', 'utf8')) as { version: string }; + const expectedRange = `>=${version}`; + const packageManifest = JSON.parse(fs.readFileSync('plugins/linkedin/package.json', 'utf8')) as { peerDependencies?: Record; }; @@ -223,9 +228,9 @@ describe('plugin command manifest', () => { plugins?: Record; }; - expect(packageManifest.peerDependencies?.['@agentrhq/webcmd']).toBe('>=0.6.0'); - expect(pluginManifest.webcmd).toBe('>=0.6.0'); - expect(rootManifest.plugins?.linkedin?.webcmd).toBe('>=0.6.0'); + expect(packageManifest.peerDependencies?.['@agentrhq/webcmd']).toBe(expectedRange); + expect(pluginManifest.webcmd).toBe(expectedRange); + expect(rootManifest.plugins?.linkedin?.webcmd).toBe(expectedRange); }); it.each([ diff --git a/src/migrate-cli-sites.test.ts b/src/migrate-cli-sites.test.ts index 369f3ff0..6451d18a 100644 --- a/src/migrate-cli-sites.test.ts +++ b/src/migrate-cli-sites.test.ts @@ -29,6 +29,7 @@ function fixture(): string { import { requireSearchQuery } from '../_shared/common.js'; `); fs.writeFileSync(path.join(root, 'plugins', 'sibling', 'keep.txt'), 'unchanged\n'); + fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify({ name: '@agentrhq/webcmd', version: '9.9.9' })); fs.writeFileSync(path.join(root, 'cli-manifest.json'), JSON.stringify([ { site: 'zeta', name: 'last', description: 'Last', sourceFile: 'zeta/last.js' }, { site: 'example', name: 'search', description: 'Search examples', sourceFile: 'example/search.js' }, @@ -61,13 +62,13 @@ describe('migrate-cli-sites', () => { version: '0.1.0', type: 'module', description: 'Webcmd commands for example', - peerDependencies: { '@agentrhq/webcmd': '>=0.6.0' }, + peerDependencies: { '@agentrhq/webcmd': '>=9.9.9' }, }); expect(JSON.parse(fs.readFileSync(path.join(plugin, 'webcmd-plugin.json'), 'utf8'))).toEqual({ name: 'example', version: '0.1.0', description: 'Webcmd commands for example', - webcmd: '>=0.6.0', + webcmd: '>=9.9.9', author: { name: 'WebCMD Agent', handle: 'agentrhq' }, }); const readme = fs.readFileSync(path.join(plugin, 'README.md'), 'utf8'); diff --git a/webcmd-plugin.json b/webcmd-plugin.json index 52d01f64..c60e5abf 100644 --- a/webcmd-plugin.json +++ b/webcmd-plugin.json @@ -8,7 +8,7 @@ "path": "plugins/amazon", "version": "0.1.0", "description": "Webcmd commands for amazon", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -18,7 +18,7 @@ "path": "plugins/amazon-in", "version": "0.1.0", "description": "Webcmd commands for amazon-in", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -28,7 +28,7 @@ "path": "plugins/antigravity", "version": "0.1.0", "description": "Webcmd commands for antigravity", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -38,7 +38,7 @@ "path": "plugins/apple-podcasts", "version": "0.1.0", "description": "Webcmd commands for apple-podcasts", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -48,7 +48,7 @@ "path": "plugins/archive", "version": "0.1.0", "description": "Webcmd commands for archive", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -58,7 +58,7 @@ "path": "plugins/arxiv", "version": "0.1.0", "description": "Webcmd commands for arxiv", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -68,7 +68,7 @@ "path": "plugins/band", "version": "0.1.0", "description": "Webcmd commands for band", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -78,7 +78,7 @@ "path": "plugins/barchart", "version": "0.1.0", "description": "Webcmd commands for barchart", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -88,7 +88,7 @@ "path": "plugins/bbc", "version": "0.1.0", "description": "Webcmd commands for bbc", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -98,7 +98,7 @@ "path": "plugins/bigbasket", "version": "0.1.0", "description": "Webcmd commands for bigbasket", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -108,7 +108,7 @@ "path": "plugins/binance", "version": "0.1.0", "description": "Webcmd commands for binance", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -118,7 +118,7 @@ "path": "plugins/blinkit", "version": "0.1.0", "description": "Webcmd commands for blinkit", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -128,7 +128,7 @@ "path": "plugins/bloomberg", "version": "0.1.0", "description": "Webcmd commands for bloomberg", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -138,7 +138,7 @@ "path": "plugins/bluesky", "version": "0.1.0", "description": "Webcmd commands for bluesky", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -158,7 +158,7 @@ "path": "plugins/booking", "version": "0.1.0", "description": "Webcmd commands for booking", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -168,7 +168,7 @@ "path": "plugins/brave", "version": "0.1.0", "description": "Webcmd commands for brave", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -178,7 +178,7 @@ "path": "plugins/chatgpt", "version": "0.1.0", "description": "Webcmd commands for chatgpt", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -188,7 +188,7 @@ "path": "plugins/chatgpt-app", "version": "0.1.0", "description": "Webcmd commands for chatgpt-app", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -198,7 +198,7 @@ "path": "plugins/chatwise", "version": "0.1.0", "description": "Webcmd commands for chatwise", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -208,7 +208,7 @@ "path": "plugins/chess", "version": "0.1.0", "description": "Webcmd commands for chess", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -228,7 +228,7 @@ "path": "plugins/claude", "version": "0.1.0", "description": "Webcmd commands for claude", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -238,7 +238,7 @@ "path": "plugins/codex", "version": "0.1.0", "description": "Webcmd commands for codex", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -248,7 +248,7 @@ "path": "plugins/coingecko", "version": "0.1.0", "description": "Webcmd commands for coingecko", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -268,7 +268,7 @@ "path": "plugins/confluence", "version": "0.1.0", "description": "Webcmd commands for confluence", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -278,7 +278,7 @@ "path": "plugins/coupang", "version": "0.1.0", "description": "Webcmd commands for coupang", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -288,7 +288,7 @@ "path": "plugins/crates", "version": "0.1.0", "description": "Webcmd commands for crates", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -298,7 +298,7 @@ "path": "plugins/cursor", "version": "0.1.0", "description": "Webcmd commands for cursor", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -308,7 +308,7 @@ "path": "plugins/dblp", "version": "0.1.0", "description": "Webcmd commands for dblp", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -318,7 +318,7 @@ "path": "plugins/defillama", "version": "0.1.0", "description": "Webcmd commands for defillama", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -328,7 +328,7 @@ "path": "plugins/devto", "version": "0.1.0", "description": "Webcmd commands for devto", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -338,7 +338,7 @@ "path": "plugins/dictionary", "version": "0.1.0", "description": "Webcmd commands for dictionary", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -348,7 +348,7 @@ "path": "plugins/discord-app", "version": "0.1.0", "description": "Webcmd commands for discord-app", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -358,7 +358,7 @@ "path": "plugins/district", "version": "0.1.0", "description": "Webcmd commands for district", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -368,7 +368,7 @@ "path": "plugins/dockerhub", "version": "0.1.0", "description": "Webcmd commands for dockerhub", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -378,7 +378,7 @@ "path": "plugins/duckduckgo", "version": "0.1.0", "description": "Webcmd commands for duckduckgo", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -388,7 +388,7 @@ "path": "plugins/endoflife", "version": "0.1.0", "description": "Webcmd commands for endoflife", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -398,7 +398,7 @@ "path": "plugins/facebook", "version": "0.1.0", "description": "Webcmd commands for facebook", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -408,7 +408,7 @@ "path": "plugins/flathub", "version": "0.1.0", "description": "Webcmd commands for flathub", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -418,7 +418,7 @@ "path": "plugins/gemini", "version": "0.1.0", "description": "Webcmd commands for gemini", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -428,7 +428,7 @@ "path": "plugins/geogebra", "version": "0.1.0", "description": "Webcmd commands for geogebra", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -438,7 +438,7 @@ "path": "plugins/github", "version": "0.1.0", "description": "Webcmd commands for github", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -448,7 +448,7 @@ "path": "plugins/github-trending", "version": "0.1.0", "description": "Webcmd commands for github-trending", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -468,7 +468,7 @@ "path": "plugins/google", "version": "0.1.0", "description": "Webcmd commands for google", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -478,7 +478,7 @@ "path": "plugins/google-scholar", "version": "0.1.0", "description": "Webcmd commands for google-scholar", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -488,7 +488,7 @@ "path": "plugins/goproxy", "version": "0.1.0", "description": "Webcmd commands for goproxy", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -498,7 +498,7 @@ "path": "plugins/grok", "version": "0.1.0", "description": "Webcmd commands for grok", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -508,7 +508,7 @@ "path": "plugins/hackernews", "version": "0.1.0", "description": "Webcmd commands for hackernews", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -528,7 +528,7 @@ "path": "plugins/hf", "version": "0.1.0", "description": "Webcmd commands for hf", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -548,7 +548,7 @@ "path": "plugins/homebrew", "version": "0.1.0", "description": "Webcmd commands for homebrew", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -568,7 +568,7 @@ "path": "plugins/imdb", "version": "0.1.0", "description": "Webcmd commands for imdb", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -578,7 +578,7 @@ "path": "plugins/indeed", "version": "0.1.0", "description": "Webcmd commands for indeed", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -588,7 +588,7 @@ "path": "plugins/instagram", "version": "0.1.0", "description": "Webcmd commands for instagram", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -608,7 +608,7 @@ "path": "plugins/jira", "version": "0.1.0", "description": "Webcmd commands for jira", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -618,7 +618,7 @@ "path": "plugins/lesswrong", "version": "0.1.0", "description": "Webcmd commands for lesswrong", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -628,7 +628,7 @@ "path": "plugins/lichess", "version": "0.1.0", "description": "Webcmd commands for lichess", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -638,7 +638,7 @@ "path": "plugins/linkedin", "version": "0.1.0", "description": "LinkedIn profile, network, messaging, job, and Sales Navigator commands for WebCMD", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -648,7 +648,7 @@ "path": "plugins/linkedin-learning", "version": "0.1.0", "description": "Webcmd commands for linkedin-learning", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -658,7 +658,7 @@ "path": "plugins/lobsters", "version": "0.1.0", "description": "Webcmd commands for lobsters", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -678,7 +678,7 @@ "path": "plugins/manus", "version": "0.1.0", "description": "Webcmd commands for manus", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -688,7 +688,7 @@ "path": "plugins/maven", "version": "0.1.0", "description": "Webcmd commands for maven", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -698,7 +698,7 @@ "path": "plugins/mdn", "version": "0.1.0", "description": "Webcmd commands for mdn", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -708,7 +708,7 @@ "path": "plugins/medium", "version": "0.1.0", "description": "Webcmd commands for medium", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -718,7 +718,7 @@ "path": "plugins/mercury", "version": "0.1.0", "description": "Webcmd commands for mercury", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -728,7 +728,7 @@ "path": "plugins/notebooklm", "version": "0.1.0", "description": "Webcmd commands for notebooklm", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -738,7 +738,7 @@ "path": "plugins/npm", "version": "0.1.0", "description": "Webcmd commands for npm", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -748,7 +748,7 @@ "path": "plugins/nuget", "version": "0.1.0", "description": "Webcmd commands for nuget", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -758,7 +758,7 @@ "path": "plugins/nvd", "version": "0.1.0", "description": "Webcmd commands for nvd", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -768,7 +768,7 @@ "path": "plugins/oeis", "version": "0.1.0", "description": "Webcmd commands for oeis", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -778,7 +778,7 @@ "path": "plugins/openalex", "version": "0.1.0", "description": "Webcmd commands for openalex", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -788,7 +788,7 @@ "path": "plugins/openfda", "version": "0.1.0", "description": "Webcmd commands for openfda", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -798,7 +798,7 @@ "path": "plugins/openreview", "version": "0.1.0", "description": "Webcmd commands for openreview", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -808,7 +808,7 @@ "path": "plugins/osv", "version": "0.1.0", "description": "Webcmd commands for osv", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -818,7 +818,7 @@ "path": "plugins/packagist", "version": "0.1.0", "description": "Webcmd commands for packagist", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -828,7 +828,7 @@ "path": "plugins/paperreview", "version": "0.1.0", "description": "Webcmd commands for paperreview", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -838,7 +838,7 @@ "path": "plugins/pixiv", "version": "0.1.0", "description": "Webcmd commands for pixiv", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -848,7 +848,7 @@ "path": "plugins/practo", "version": "0.1.0", "description": "Webcmd commands for practo", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -858,7 +858,7 @@ "path": "plugins/producthunt", "version": "0.1.0", "description": "Webcmd commands for producthunt", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -868,7 +868,7 @@ "path": "plugins/pubmed", "version": "0.1.0", "description": "Webcmd commands for pubmed", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -888,7 +888,7 @@ "path": "plugins/qoder", "version": "0.1.0", "description": "Webcmd commands for qoder", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -898,7 +898,7 @@ "path": "plugins/reddit", "version": "0.1.0", "description": "Webcmd commands for reddit", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -908,7 +908,7 @@ "path": "plugins/rest-countries", "version": "0.1.0", "description": "Webcmd commands for rest-countries", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -918,7 +918,7 @@ "path": "plugins/reuters", "version": "0.1.0", "description": "Webcmd commands for reuters", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -928,7 +928,7 @@ "path": "plugins/rfc", "version": "0.1.0", "description": "Webcmd commands for rfc", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -938,7 +938,7 @@ "path": "plugins/rubygems", "version": "0.1.0", "description": "Webcmd commands for rubygems", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -948,7 +948,7 @@ "path": "plugins/semanticscholar", "version": "0.1.0", "description": "Webcmd commands for semanticscholar", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -968,7 +968,7 @@ "path": "plugins/slock", "version": "0.1.0", "description": "Webcmd commands for slock", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -978,7 +978,7 @@ "path": "plugins/spotify", "version": "0.1.0", "description": "Webcmd commands for spotify", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -988,7 +988,7 @@ "path": "plugins/stackoverflow", "version": "0.1.0", "description": "Webcmd commands for stackoverflow", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -998,7 +998,7 @@ "path": "plugins/steam", "version": "0.1.0", "description": "Webcmd commands for steam", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1008,7 +1008,7 @@ "path": "plugins/substack", "version": "0.1.0", "description": "Webcmd commands for substack", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1018,7 +1018,7 @@ "path": "plugins/suno", "version": "0.1.0", "description": "Webcmd commands for suno", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1038,7 +1038,7 @@ "path": "plugins/tiktok", "version": "0.1.0", "description": "Webcmd commands for tiktok", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1048,7 +1048,7 @@ "path": "plugins/trae-solo", "version": "0.1.0", "description": "Webcmd commands for trae-solo", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1058,7 +1058,7 @@ "path": "plugins/trip", "version": "0.1.0", "description": "Webcmd commands for trip", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1068,7 +1068,7 @@ "path": "plugins/tvmaze", "version": "0.1.0", "description": "Webcmd commands for tvmaze", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1078,7 +1078,7 @@ "path": "plugins/twitter", "version": "0.1.0", "description": "Webcmd commands for twitter", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1098,7 +1098,7 @@ "path": "plugins/uiverse", "version": "0.1.0", "description": "Webcmd commands for uiverse", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1108,7 +1108,7 @@ "path": "plugins/upwork", "version": "0.1.0", "description": "Webcmd commands for upwork", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1118,7 +1118,7 @@ "path": "plugins/web", "version": "0.1.0", "description": "Webcmd commands for web", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1128,7 +1128,7 @@ "path": "plugins/wikidata", "version": "0.1.0", "description": "Webcmd commands for wikidata", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1138,7 +1138,7 @@ "path": "plugins/wikipedia", "version": "0.1.0", "description": "Webcmd commands for wikipedia", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1148,7 +1148,7 @@ "path": "plugins/wttr", "version": "0.1.0", "description": "Webcmd commands for wttr", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1158,7 +1158,7 @@ "path": "plugins/yahoo", "version": "0.1.0", "description": "Webcmd commands for yahoo", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1168,7 +1168,7 @@ "path": "plugins/yahoo-finance", "version": "0.1.0", "description": "Webcmd commands for yahoo-finance", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1198,7 +1198,7 @@ "path": "plugins/yollomi", "version": "0.1.0", "description": "Webcmd commands for yollomi", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1208,7 +1208,7 @@ "path": "plugins/youtube", "version": "0.1.0", "description": "Webcmd commands for youtube", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1218,7 +1218,7 @@ "path": "plugins/zepto", "version": "0.1.0", "description": "Webcmd commands for zepto", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" @@ -1228,7 +1228,7 @@ "path": "plugins/zlibrary", "version": "0.1.0", "description": "Webcmd commands for zlibrary", - "webcmd": ">=0.6.0", + "webcmd": ">=0.5.3", "author": { "name": "WebCMD Agent", "handle": "agentrhq" From f3246ae4046cd4044ae4b835b57a890e70700e1a Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Thu, 6 Aug 2026 15:28:26 +0530 Subject: [PATCH 25/39] test: install fixture plugins in e2e/smoke suites now that core ships no sites Several e2e and smoke tests invoked site commands (hackernews, dictionary, google, imdb, apple-podcasts, yollomi, paperreview) and asserted on `list` output size/contents, assuming site adapters were bundled in core. After this migration core registers zero site commands until a plugin is installed, so these tests failed deterministically in any clean environment (e.g. `list` returns 0 commands, not >50). Add installFixturePlugin() to place a repo-local plugin directly under an isolated HOME's .webcmd/plugins/ (skipping `plugin install`'s npm step, which only resolves a peerDependency these fixture plugins don't otherwise need and which fails until the in-progress release is actually published). Each affected suite now installs the specific plugin(s) it exercises before running. The smoke test's "all expected sites registered" check is rewritten against the plugin catalog under plugins/, since that's the equivalent invariant post-migration. --- tests/e2e/browser-public.test.ts | 25 ++++++++++++-- tests/e2e/helpers.ts | 17 +++++++++ tests/e2e/management.test.ts | 57 +++++++++++++++++++++---------- tests/e2e/output-formats.test.ts | 28 ++++++++++++--- tests/e2e/public-commands.test.ts | 24 +++++++++++-- tests/smoke/api-health.test.ts | 42 ++++++++++++++++++----- 6 files changed, 158 insertions(+), 35 deletions(-) diff --git a/tests/e2e/browser-public.test.ts b/tests/e2e/browser-public.test.ts index e690f658..22bc922b 100644 --- a/tests/e2e/browser-public.test.ts +++ b/tests/e2e/browser-public.test.ts @@ -4,10 +4,23 @@ * * NOTE: Some sites may block headless browsers with bot detection. * Tests are wrapped with tryBrowserCommand() which allows graceful failure. + * + * imdb is no longer bundled in core; it's installed as a local plugin into + * an isolated HOME before this suite runs. */ -import { describe, it, expect } from 'vitest'; -import { runCli, parseJsonOutput, type CliResult } from './helpers.js'; +import { afterAll, beforeAll, describe, it, expect } from 'vitest'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import { runCli as runCliBase, parseJsonOutput, installFixturePlugin, type CliResult } from './helpers.js'; + +const TEST_HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-browser-public-e2e-')); +const FIXTURE_ENV = { HOME: TEST_HOME, USERPROFILE: TEST_HOME }; + +function runCli(args: string[], opts: { timeout?: number; env?: Record } = {}) { + return runCliBase(args, { ...opts, env: { ...FIXTURE_ENV, ...opts.env } }); +} const BROWSER_UNAVAILABLE_ENV = { WEBCMD_BROWSER_CONNECT_TIMEOUT: '5' }; @@ -55,6 +68,14 @@ async function expectImdbDataOrChallengeSkip(args: string[], label: string): Pro } describe('browser public-data commands E2E', () => { + beforeAll(() => { + installFixturePlugin(TEST_HOME, 'imdb'); + }); + + afterAll(() => { + fs.rmSync(TEST_HOME, { recursive: true, force: true }); + }); + // ── imdb ── it('imdb top returns chart data', async () => { const data = await expectImdbDataOrChallengeSkip(['imdb', 'top', '--limit', '3', '-f', 'json'], 'imdb top'); diff --git a/tests/e2e/helpers.ts b/tests/e2e/helpers.ts index 4bd24ae2..36cea203 100644 --- a/tests/e2e/helpers.ts +++ b/tests/e2e/helpers.ts @@ -5,6 +5,7 @@ import { execFile } from 'node:child_process'; import { promisify } from 'node:util'; +import * as fs from 'node:fs'; import * as path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -65,6 +66,22 @@ export async function runCli( } } +/** + * Place a repo-local plugin directly under `/.webcmd/plugins/` + * for E2E fixtures, skipping `webcmd plugin install`'s `npm install` step. + * These fixture plugins declare no real dependencies (only a peer on + * @agentrhq/webcmd), so `npm install` only exists to resolve that peer + * against the published registry — which fails for whatever version is + * currently mid-release and not yet published. A plain file copy is what a + * real install produces once the release is out. + */ +export function installFixturePlugin(home: string, site: string): void { + const source = path.join(ROOT, 'plugins', site); + const target = path.join(home, '.webcmd', 'plugins', site); + fs.mkdirSync(path.dirname(target), { recursive: true }); + fs.cpSync(source, target, { recursive: true }); +} + /** * Parse JSON output from a CLI command. * Throws a descriptive error if parsing fails. diff --git a/tests/e2e/management.test.ts b/tests/e2e/management.test.ts index 09ff0332..a47e731f 100644 --- a/tests/e2e/management.test.ts +++ b/tests/e2e/management.test.ts @@ -1,21 +1,44 @@ /** * E2E tests for management/built-in commands. * These commands require no external network access (except verify --smoke). + * + * Site commands are no longer bundled in core (they ship as independent + * plugins), so `list`/`validate` need at least one plugin installed to have + * anything to render. A small local plugin is installed once into an + * isolated HOME for that purpose. */ -import { describe, it, expect } from 'vitest'; -import { runCli, parseJsonOutput } from './helpers.js'; +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import * as os from 'node:os'; +import { runCli, parseJsonOutput, installFixturePlugin } from './helpers.js'; + +const TEST_HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-management-e2e-')); +const FIXTURE_SITE = 'dictionary'; +const FIXTURE_ENV = { HOME: TEST_HOME, USERPROFILE: TEST_HOME }; + +function runManagementCli(args: string[], opts: { timeout?: number } = {}) { + return runCli(args, { ...opts, env: FIXTURE_ENV }); +} describe('management commands E2E', () => { + beforeAll(() => { + installFixturePlugin(TEST_HOME, FIXTURE_SITE); + }); + + afterAll(() => { + fs.rmSync(TEST_HOME, { recursive: true, force: true }); + }); // ── list ── it('list shows all registered commands', async () => { - const { stdout, code } = await runCli(['list', '-f', 'json']); + const { stdout, code } = await runManagementCli(['list', '-f', 'json']); expect(code).toBe(0); const data = parseJsonOutput(stdout); expect(Array.isArray(data)).toBe(true); - // Should have 50+ commands across 18 sites - expect(data.length).toBeGreaterThan(50); + // The fixture plugin (dictionary) contributes 3 commands: search, synonyms, examples. + expect(data.length).toBeGreaterThanOrEqual(3); // Each entry should have the standard fields expect(data[0]).toHaveProperty('command'); expect(data[0]).toHaveProperty('site'); @@ -25,31 +48,29 @@ describe('management commands E2E', () => { }); it('list default table format renders sites', async () => { - const { stdout, code } = await runCli(['list']); + const { stdout, code } = await runManagementCli(['list']); expect(code).toBe(0); - // Should contain site names - expect(stdout).toContain('hackernews'); - expect(stdout).toContain('youtube'); - expect(stdout).toContain('twitter'); + expect(stdout).toContain(FIXTURE_SITE); expect(stdout).toContain('commands across'); }); it('list -f yaml produces valid yaml', async () => { - const { stdout, code } = await runCli(['list', '-f', 'yaml']); + const { stdout, code } = await runManagementCli(['list', '-f', 'yaml']); expect(code).toBe(0); expect(stdout).toContain('command:'); expect(stdout).toContain('site:'); }); it('list -f csv produces valid csv', async () => { - const { stdout, code } = await runCli(['list', '-f', 'csv']); + const { stdout, code } = await runManagementCli(['list', '-f', 'csv']); expect(code).toBe(0); const lines = stdout.trim().split('\n'); - expect(lines.length).toBeGreaterThan(50); + // header + at least the 3 fixture commands + expect(lines.length).toBeGreaterThanOrEqual(4); }); it('list -f md produces markdown table', async () => { - const { stdout, code } = await runCli(['list', '-f', 'md']); + const { stdout, code } = await runManagementCli(['list', '-f', 'md']); expect(code).toBe(0); expect(stdout).toContain('|'); expect(stdout).toContain('command'); @@ -57,27 +78,27 @@ describe('management commands E2E', () => { // ── validate ── it('validate passes for all built-in adapters', async () => { - const { stdout, code } = await runCli(['validate']); + const { stdout, code } = await runManagementCli(['validate']); expect(code).toBe(0); expect(stdout).toContain('PASS'); expect(stdout).not.toContain('❌'); }); it('validate works for specific site', async () => { - const { stdout, code } = await runCli(['validate', 'hackernews']); + const { stdout, code } = await runManagementCli(['validate', FIXTURE_SITE]); expect(code).toBe(0); expect(stdout).toContain('PASS'); }); it('validate works for specific command', async () => { - const { stdout, code } = await runCli(['validate', 'hackernews/top']); + const { stdout, code } = await runManagementCli(['validate', `${FIXTURE_SITE}/search`]); expect(code).toBe(0); expect(stdout).toContain('PASS'); }); // ── verify ── it('verify runs validation without smoke tests', async () => { - const { stdout, code } = await runCli(['verify']); + const { stdout, code } = await runManagementCli(['verify']); expect(code).toBe(0); expect(stdout).toContain('PASS'); }); diff --git a/tests/e2e/output-formats.test.ts b/tests/e2e/output-formats.test.ts index 638863dd..45c6af89 100644 --- a/tests/e2e/output-formats.test.ts +++ b/tests/e2e/output-formats.test.ts @@ -2,24 +2,44 @@ * E2E tests for output format rendering. * Uses the built-in list command so renderer coverage does not depend on * external network availability. + * + * Site commands are no longer bundled in core, so a small local plugin is + * installed into an isolated HOME to give `list` something deterministic + * to render. */ -import { describe, it, expect } from 'vitest'; -import { runCli, parseJsonOutput } from './helpers.js'; +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import * as os from 'node:os'; +import { runCli, parseJsonOutput, installFixturePlugin } from './helpers.js'; + +const TEST_HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-output-formats-e2e-')); +const FIXTURE_SITE = 'dictionary'; +const FIXTURE_ENV = { HOME: TEST_HOME, USERPROFILE: TEST_HOME }; const FORMATS = ['json', 'yaml', 'csv', 'md'] as const; describe('output formats E2E', () => { + beforeAll(() => { + installFixturePlugin(TEST_HOME, FIXTURE_SITE); + }); + + afterAll(() => { + fs.rmSync(TEST_HOME, { recursive: true, force: true }); + }); + for (const fmt of FORMATS) { it(`list -f ${fmt} produces valid output`, async () => { - const { stdout, code } = await runCli(['list', '-f', fmt]); + const { stdout, code } = await runCli(['list', '-f', fmt], { env: FIXTURE_ENV }); expect(code).toBe(0); expect(stdout.trim().length).toBeGreaterThan(0); if (fmt === 'json') { const data = parseJsonOutput(stdout); expect(Array.isArray(data)).toBe(true); - expect(data.length).toBeGreaterThan(50); + // The fixture plugin (dictionary) contributes 3 commands. + expect(data.length).toBeGreaterThanOrEqual(3); expect(data[0]).toHaveProperty('command'); expect(data[0]).toHaveProperty('site'); } diff --git a/tests/e2e/public-commands.test.ts b/tests/e2e/public-commands.test.ts index 2b18ba6a..d5e84f98 100644 --- a/tests/e2e/public-commands.test.ts +++ b/tests/e2e/public-commands.test.ts @@ -1,13 +1,25 @@ /** * E2E tests for public API commands (browser: false). * These commands use Node.js fetch directly — no browser needed. + * + * Site commands are no longer bundled in core; each site under test here is + * installed as a local plugin into an isolated HOME before the suite runs. */ -import { describe, expect, it } from 'vitest'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import * as fs from 'node:fs/promises'; +import * as fsSync from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; -import { parseJsonOutput, runCli } from './helpers.js'; +import { parseJsonOutput, runCli as runCliBase, installFixturePlugin } from './helpers.js'; + +const TEST_HOME = fsSync.mkdtempSync(path.join(os.tmpdir(), 'webcmd-public-commands-e2e-')); +const FIXTURE_ENV = { HOME: TEST_HOME, USERPROFILE: TEST_HOME }; +const FIXTURE_SITES = ['apple-podcasts', 'paperreview', 'hackernews', 'google', 'yollomi', 'dictionary']; + +function runCli(args: string[], opts: { timeout?: number; env?: Record } = {}) { + return runCliBase(args, { ...opts, env: { ...FIXTURE_ENV, ...opts.env } }); +} function isExpectedApplePodcastsRestriction(code: number, stderr: string): boolean { if (code === 0) return false; @@ -33,6 +45,14 @@ describe('public command restriction detectors', () => { }); describe('public commands E2E', () => { + beforeAll(() => { + for (const site of FIXTURE_SITES) installFixturePlugin(TEST_HOME, site); + }); + + afterAll(() => { + fsSync.rmSync(TEST_HOME, { recursive: true, force: true }); + }); + // ── apple-podcasts ── it('apple-podcasts search returns structured podcast results', async () => { const { stdout, code } = await runCli(['apple-podcasts', 'search', 'technology', '--limit', '3', '-f', 'json']); diff --git a/tests/smoke/api-health.test.ts b/tests/smoke/api-health.test.ts index 39c005c6..b2cc7c90 100644 --- a/tests/smoke/api-health.test.ts +++ b/tests/smoke/api-health.test.ts @@ -2,12 +2,36 @@ * Smoke tests for external API health. * Only run on schedule or manual dispatch — NOT on every push/PR. * These verify that external APIs haven't changed their structure. + * + * hackernews is no longer bundled in core; it's installed as a local plugin + * into an isolated HOME before this suite runs. */ -import { describe, expect, it } from 'vitest'; -import { parseJsonOutput, runCli } from '../e2e/helpers.js'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import * as os from 'node:os'; +import { fileURLToPath } from 'node:url'; +import { parseJsonOutput, runCli as runCliBase, installFixturePlugin } from '../e2e/helpers.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const REPO_ROOT = path.resolve(__dirname, '../..'); +const TEST_HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-smoke-e2e-')); +const FIXTURE_ENV = { HOME: TEST_HOME, USERPROFILE: TEST_HOME }; + +function runCli(args: string[], opts: { timeout?: number; env?: Record } = {}) { + return runCliBase(args, { ...opts, env: { ...FIXTURE_ENV, ...opts.env } }); +} describe('API health smoke tests', () => { + beforeAll(() => { + installFixturePlugin(TEST_HOME, 'hackernews'); + }); + + afterAll(() => { + fs.rmSync(TEST_HOME, { recursive: true, force: true }); + }); + // ── Public API commands (should always work) ── it('hackernews API is responsive and returns expected structure', async () => { const { stdout, code } = await runCli(['hackernews', 'top', '--limit', '5', '-f', 'json']); @@ -29,12 +53,12 @@ describe('API health smoke tests', () => { expect(stdout).toContain('PASS'); }); - // ── Command registry integrity ── - it('all expected sites are registered', async () => { - const { stdout, code } = await runCli(['list', '-f', 'json']); - expect(code).toBe(0); - const data = parseJsonOutput(stdout); - const sites = new Set(data.map((d: any) => d.site)); + // ── Plugin catalog integrity ── + // Site adapters now ship as independent plugins rather than core-bundled + // commands, so the equivalent invariant is "these sites are cataloged + // under plugins/", not "these sites are registered by default". + it('all expected sites are cataloged as installable plugins', () => { + const catalogedSites = new Set(fs.readdirSync(path.join(REPO_ROOT, 'plugins'))); for (const expected of [ 'hackernews', 'bbc', @@ -47,7 +71,7 @@ describe('API health smoke tests', () => { 'google-scholar', 'yahoo-finance', ]) { - expect(sites.has(expected)).toBe(true); + expect(catalogedSites.has(expected)).toBe(true); } }); }); From dc4a4f6031a130a190a8ae97f452b18e352a0fe1 Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Thu, 6 Aug 2026 15:32:19 +0530 Subject: [PATCH 26/39] docs: fix bundled skills to describe the plugin architecture, not clis/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit webcmd-usage, webcmd-adapter-author, and webcmd-autofix (all shipped to npm under skills/**) still described a repo-root clis/ directory as the "official bundle" location for built-in adapters. That directory no longer exists — every main-repo site, official or community, lives under plugins// now. Left the ~/.webcmd/clis/ references alone: that private local-iteration directory is unaffected by this migration. --- skills/webcmd-adapter-author/SKILL.md | 6 +++--- .../references/jsdom-fixture-pattern.md | 6 +++--- skills/webcmd-autofix/SKILL.md | 4 ++-- skills/webcmd-usage/SKILL.md | 9 ++++----- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/skills/webcmd-adapter-author/SKILL.md b/skills/webcmd-adapter-author/SKILL.md index 7e061525..8da17167 100644 --- a/skills/webcmd-adapter-author/SKILL.md +++ b/skills/webcmd-adapter-author/SKILL.md @@ -244,7 +244,7 @@ Check these off step by step: | `references/site-memory.md` | Overview: in-repo seeds plus local `~/.webcmd/sites/` two-layer structure | | `references/site-memory/.md` | Step 2: public site knowledge when a seed file exists | | `references/success-rate-pitfalls.md` | Step 7 / 11: eleven silent failure modes where verify can pass with wrong data, including aria-label locale dependence | -| `references/jsdom-fixture-pattern.md` | When adapter uses DOM extraction inside `page.evaluate` and mocked-evaluate unit tests miss silent bugs; freeze HTML into `clis//__fixtures__/` and run JSDOM with the mandatory `awk 'NF>0'` tightening plus reverse-validation discipline | +| `references/jsdom-fixture-pattern.md` | When adapter uses DOM extraction inside `page.evaluate` and mocked-evaluate unit tests miss silent bugs; freeze HTML into `plugins//__fixtures__/` and run JSDOM with the mandatory `awk 'NF>0'` tightening plus reverse-validation discipline | | `references/typed-errors.md` | Read before writing `func`: five typed error classes (`ArgumentError`, `EmptyResultError`, `CommandExecutionError`, `AuthRequiredError`, `TimeoutError`) plus fixes for silent anti-patterns (`silent-clamp`, `sentinel-row`, `generic CliError`) | --- @@ -260,8 +260,8 @@ Check these off step by step: - For private iteration, write `~/.webcmd/clis//.js` to avoid a build. When the user says to promote a CLI, create a main-repo plugin with `webcmd plugin create --dir plugins/`, copy the real command files into it, delete scaffold sample commands, register it in root `webcmd-plugin.json`, remove the local `~/.webcmd/clis/` shadow, install the plugin, then run `webcmd validate ` and smoke commands. See `references/adapter-template.md` for details. - Write site memory every round: no memory -> use skill -> produce memory -> next time becomes a five-minute task. - **After a site's first command passes verify, stop and ask the user for their use cases before recommending next set of commands.** See Runbook Step 13. -- **Raw dumps, packet captures, and HTML samples from debugging may only be written to `~/.webcmd/sites//fixtures/` or `/tmp/`. Never leave `.dbg-*.html`, `raw-*.json`, `sample.*`, or similar temporary files in the repo root, `clis//`, or the current working directory.** -- **JSDOM unit-test fixtures (`clis//__fixtures__/.html`) are the exception.** They are intentional review artifacts committed to the repo, not temporary dumps. Because of that, the quality bar is higher: complete the five steps in `references/jsdom-fixture-pattern.md`, including the mandatory `awk 'NF>0'` blank-line tightening, and reverse-validate once to prove the regression guard can fail. +- **Raw dumps, packet captures, and HTML samples from debugging may only be written to `~/.webcmd/sites//fixtures/` or `/tmp/`. Never leave `.dbg-*.html`, `raw-*.json`, `sample.*`, or similar temporary files in the repo root, `plugins//`, or the current working directory.** +- **JSDOM unit-test fixtures (`plugins//__fixtures__/.html`) are the exception.** They are intentional review artifacts committed to the repo, not temporary dumps. Because of that, the quality bar is higher: complete the five steps in `references/jsdom-fixture-pattern.md`, including the mandatory `awk 'NF>0'` blank-line tightening, and reverse-validate once to prove the regression guard can fail. --- diff --git a/skills/webcmd-adapter-author/references/jsdom-fixture-pattern.md b/skills/webcmd-adapter-author/references/jsdom-fixture-pattern.md index 86002081..fc8fa786 100644 --- a/skills/webcmd-adapter-author/references/jsdom-fixture-pattern.md +++ b/skills/webcmd-adapter-author/references/jsdom-fixture-pattern.md @@ -18,7 +18,7 @@ Do not add JSDOM fixtures for simple JSON adapters. Commit intentional review fixtures under: ```text -clis//__fixtures__/.html +plugins//__fixtures__/.html ``` Temporary debug dumps still belong only in: @@ -45,8 +45,8 @@ Save only the required HTML for the parser. Run the mandatory blank-line tightening before committing: ```bash -awk 'NF>0' clis//__fixtures__/.html > /tmp/.html -mv /tmp/.html clis//__fixtures__/.html +awk 'NF>0' plugins//__fixtures__/.html > /tmp/.html +mv /tmp/.html plugins//__fixtures__/.html ``` This prevents fixture bloat and makes diffs readable. diff --git a/skills/webcmd-autofix/SKILL.md b/skills/webcmd-autofix/SKILL.md index eadb8e55..ab3fa4db 100644 --- a/skills/webcmd-autofix/SKILL.md +++ b/skills/webcmd-autofix/SKILL.md @@ -20,7 +20,7 @@ Hard stops before any code change: Scope constraint: -- Modify only the file at `adapterSourcePath` in the trace `summary.md` front matter. That path is authoritative and may be `clis//...` in the repo or `plugins//...` in a plugin repo or `~/.webcmd/clis//...` for user-local installs. +- Modify only the file at `adapterSourcePath` in the trace `summary.md` front matter. That path is authoritative and may be `plugins//...` in the main repo or a plugin repo, or `~/.webcmd/clis//...` for user-local installs. - Never modify `src/`, `extension/`, `tests/`, `package.json`, or `tsconfig.json` during autofix. Retry budget: maximum **3 repair rounds** per failure. A round is diagnose -> patch -> retry. If 3 rounds do not resolve it, stop and report what was tried. @@ -97,7 +97,7 @@ traceId: "..." status: failure site: "example" command: "example/search" -adapterSourcePath: "/path/to/clis/example/search.js" +adapterSourcePath: "/path/to/plugins/example/search.js" errorCode: "SELECTOR" errorMessage: "Could not find element: .old-selector" --- diff --git a/skills/webcmd-usage/SKILL.md b/skills/webcmd-usage/SKILL.md index da259239..00cb08b6 100644 --- a/skills/webcmd-usage/SKILL.md +++ b/skills/webcmd-usage/SKILL.md @@ -33,7 +33,7 @@ Do not install Node.js or silently fall back to `npx`. ## The Three Pillars -- **Adapter commands:** `webcmd [...]`. Built-in adapters live in `clis/`; community adapters promoted to the main repo live as plugins under `plugins/`; private iteration adapters live in `~/.webcmd/clis/`. Each command has a strategy such as `PUBLIC`, `COOKIE`, `INTERCEPT`, `UI`, or `LOCAL`. +- **Adapter commands:** `webcmd [...]`. Core ships no site adapters; every site — official and community — lives as an independently installable plugin under `plugins//` in the main repo, or `~/.webcmd/plugins//` once installed. Private iteration adapters live in `~/.webcmd/clis/`. Each command has a strategy such as `PUBLIC`, `COOKIE`, `INTERCEPT`, `UI`, or `LOCAL`. - **Browser driving:** `webcmd browser *` subcommands (`open`, `state`, `click`, `type`, `select`, `find`, `extract`, `network`) for ad-hoc interaction when no adapter covers the task. See `webcmd-browser`. - **External CLI passthrough:** `webcmd gh`, `webcmd docker`, `webcmd vercel`, and similar wrappers. Manage them with `webcmd external install ` or `webcmd external register `. @@ -161,10 +161,9 @@ argument, transient, or unreproduced failures. Storage paths: - Private: `~/.webcmd/clis//.js` -- Public (official bundle): `clis//.js` -- Public (community PRs): `plugins//` plus root `webcmd-plugin.json` registration +- Public (main repo, official or community): `plugins//` plus root `webcmd-plugin.json` registration -The main Webcmd repo is itself a plugin monorepo: promoted community CLIs belong under `plugins//` and must be registered in the root `webcmd-plugin.json`. +The main Webcmd repo is itself a plugin monorepo: there is no separate "official bundle" location. Every site belongs under `plugins//` and must be registered in the root `webcmd-plugin.json`. Scaffolding and checks: @@ -191,7 +190,7 @@ webcmd plugin catalog add webcmd plugin catalog remove ``` -Plugins are installable extensions pulled from git or local paths. Use `plugin search` for marketplace discovery and `plugin list` for already-installed plugins. Main-repo community CLIs are exposed through the root plugin catalog manifest, not bundled into npm's `clis/` set. +Plugins are installable extensions pulled from git or local paths. Use `plugin search` for marketplace discovery and `plugin list` for already-installed plugins. Main-repo sites (official and community alike) are exposed through the root plugin catalog manifest; none of them are bundled into the npm package. > **Note:** The repository's `plugins/` directory is not shipped in the npm package. Find the required plugin with `webcmd plugin search`, then install its `installSource` with `webcmd plugin install `. From 11efee50e1a3c17013bcc8643f03bb1d633c30de Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Thu, 6 Aug 2026 15:37:06 +0530 Subject: [PATCH 27/39] feat: hint at plugin search/install when webcmd list has no sites Now that core ships zero bundled site adapters, an empty `list` (0 sites) is the expected default state on a fresh install rather than a sign something's broken. Point the user at `plugin search`/`plugin install` instead of leaving them looking at a bare zero-count line. --- src/command-presentation.test.ts | 16 ++++++++++++++++ src/command-presentation.ts | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/src/command-presentation.test.ts b/src/command-presentation.test.ts index b381a60c..d5f3e83a 100644 --- a/src/command-presentation.test.ts +++ b/src/command-presentation.test.ts @@ -138,6 +138,22 @@ describe('shared command presentation', () => { expect(commandListPresentation([local], 'table', { externalClis }).displayLines).toEqual(expected); }); + it('hints at plugin search/install when no site plugins are registered', () => { + const { displayLines } = commandListPresentation([], 'table', { externalClis: [] }); + + expect(displayLines).toContain( + " No site plugins installed. Find one with 'webcmd plugin search '" + + " and install it with 'webcmd plugin install '.", + ); + }); + + it('omits the plugin hint once at least one site is registered', () => { + const local = toPresentableCommand(localCommand); + const { displayLines } = commandListPresentation([local], 'table', { externalClis: [] }); + + expect(displayLines?.some((line) => line.includes('No site plugins installed'))).toBe(false); + }); + it('builds byte-identical root, site, and alias completion candidates', () => { const local = [toPresentableCommand(localCommand)]; const hosted = [toPresentableCommand(hostedCommand)]; diff --git a/src/command-presentation.ts b/src/command-presentation.ts index 67942a10..f4c33599 100644 --- a/src/command-presentation.ts +++ b/src/command-presentation.ts @@ -339,6 +339,13 @@ function formatGroupedCommandList( + `${externalClis.length} external CLIs`, '', ); + if (sitesBySite.size === 0) { + lines.push( + ` No site plugins installed. Find one with '${CLI_COMMAND} plugin search '` + + ` and install it with '${CLI_COMMAND} plugin install '.`, + '', + ); + } return lines; } From 4aec6c5d5f761f1ee529f03738f5af8004b5a059 Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Thu, 6 Aug 2026 16:10:55 +0530 Subject: [PATCH 28/39] chore: stop committing the generated plugin-command-manifest.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's a build artifact (npm run build-plugin-manifest scans plugins/* via a Node loader and writes it fresh), read only by CI-time checks within the same run: check-plugin-command-parity, check-typed-error-lint, check-silent-column-drop, and convention-audit. Nothing reads it from a published package or at CLI/hosted runtime, so it doesn't need to be in the npm tarball or in git history — same treatment as hosted-contract.json, which was already gitignored for this reason. --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- .gitignore | 1 + plugin-command-manifest.json | 30242 -------------------------------- 4 files changed, 3 insertions(+), 30244 deletions(-) delete mode 100644 plugin-command-manifest.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4eed3004..bf32baa5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,7 @@ jobs: - name: Check all generated artifacts are committed if: runner.os == 'Linux' - run: git diff --exit-code -- cli-manifest.json hosted-contract.json plugin-command-manifest.json webcmd-plugin.json README.md + run: git diff --exit-code -- cli-manifest.json hosted-contract.json webcmd-plugin.json README.md - name: Verify packed CLI executables run: npm run check:package-bin diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ee4d0e42..f2ba540d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -86,7 +86,7 @@ jobs: - name: Check all generated artifacts are committed if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }} - run: git diff --exit-code -- cli-manifest.json hosted-contract.json plugin-command-manifest.json webcmd-plugin.json README.md + run: git diff --exit-code -- cli-manifest.json hosted-contract.json webcmd-plugin.json README.md - name: Check Codex plugin metadata if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }} diff --git a/.gitignore b/.gitignore index 12e2768a..3263178e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ dist/ !extension/dist/ *.tsbuildinfo hosted-contract.json +plugin-command-manifest.json .webcmd/ .superpowers/ .worktrees/ diff --git a/plugin-command-manifest.json b/plugin-command-manifest.json deleted file mode 100644 index 9101229e..00000000 --- a/plugin-command-manifest.json +++ /dev/null @@ -1,30242 +0,0 @@ -[ - { - "site": "amazon", - "name": "bestsellers", - "description": "Amazon Best Sellers pages for category candidate discovery", - "access": "read", - "domain": "amazon.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": false, - "positional": true, - "help": "Ranking URL or supported Amazon path. Omit to use the list root." - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Maximum number of ranked items to return (default 100)" - } - ], - "columns": [ - "list_type", - "rank", - "asin", - "title", - "price_text", - "rating_value", - "review_count" - ], - "type": "js", - "modulePath": "plugins/amazon/bestsellers.js", - "sourceFile": "plugins/amazon/bestsellers.js", - "navigateBefore": false - }, - { - "site": "amazon", - "name": "discussion", - "description": "Amazon review summary and sample customer discussion from product review pages", - "access": "read", - "domain": "amazon.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "ASIN or product URL, for example B0FJS72893" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum number of review samples to return (default 10)" - } - ], - "columns": [ - "asin", - "average_rating_value", - "total_review_count" - ], - "type": "js", - "modulePath": "plugins/amazon/discussion.js", - "sourceFile": "plugins/amazon/discussion.js", - "navigateBefore": false - }, - { - "site": "amazon", - "name": "login", - "description": "Open amazon login", - "access": "write", - "domain": "amazon.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/amazon/auth.js", - "sourceFile": "plugins/amazon/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "amazon", - "name": "movers-shakers", - "description": "Amazon Movers & Shakers pages for short-term growth signals", - "access": "read", - "domain": "amazon.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": false, - "positional": true, - "help": "Ranking URL or supported Amazon path. Omit to use the list root." - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Maximum number of ranked items to return (default 100)" - } - ], - "columns": [ - "list_type", - "rank", - "asin", - "title", - "price_text", - "rating_value", - "review_count" - ], - "type": "js", - "modulePath": "plugins/amazon/movers-shakers.js", - "sourceFile": "plugins/amazon/movers-shakers.js", - "navigateBefore": false - }, - { - "site": "amazon", - "name": "new-releases", - "description": "Amazon New Releases pages for early momentum discovery", - "access": "read", - "domain": "amazon.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": false, - "positional": true, - "help": "Ranking URL or supported Amazon path. Omit to use the list root." - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Maximum number of ranked items to return (default 100)" - } - ], - "columns": [ - "list_type", - "rank", - "asin", - "title", - "price_text", - "rating_value", - "review_count" - ], - "type": "js", - "modulePath": "plugins/amazon/new-releases.js", - "sourceFile": "plugins/amazon/new-releases.js", - "navigateBefore": false - }, - { - "site": "amazon", - "name": "offer", - "description": "Amazon seller, buy box, and fulfillment facts from the product page", - "access": "read", - "domain": "amazon.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "ASIN or product URL, for example B0FJS72893" - } - ], - "columns": [ - "asin", - "price_text", - "sold_by", - "ships_from", - "is_amazon_sold", - "is_amazon_fulfilled" - ], - "type": "js", - "modulePath": "plugins/amazon/offer.js", - "sourceFile": "plugins/amazon/offer.js", - "navigateBefore": false - }, - { - "site": "amazon", - "name": "product", - "description": "Amazon product page facts for candidate validation", - "access": "read", - "domain": "amazon.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "ASIN or product URL, for example B0FJS72893" - } - ], - "columns": [ - "asin", - "title", - "price_text", - "rating_value", - "review_count" - ], - "type": "js", - "modulePath": "plugins/amazon/product.js", - "sourceFile": "plugins/amazon/product.js", - "navigateBefore": false - }, - { - "site": "amazon", - "name": "search", - "description": "Amazon search results for product discovery and coarse filtering", - "access": "read", - "domain": "amazon.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query, for example \"desk shelf organizer\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of results to return (default 20)" - } - ], - "columns": [ - "rank", - "asin", - "title", - "price_text", - "rating_value", - "review_count" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/amazon/search.js", - "sourceFile": "plugins/amazon/search.js", - "navigateBefore": false - }, - { - "site": "amazon", - "name": "whoami", - "description": "Show the current logged-in amazon account", - "access": "read", - "domain": "amazon.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_name" - ], - "type": "js", - "modulePath": "plugins/amazon/auth.js", - "sourceFile": "plugins/amazon/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "amazon-in", - "name": "checkout", - "description": "Prepare a guarded Amazon.in checkout with browser-only payment handoff", - "access": "write", - "domain": "amazon.in", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "Amazon.in product URL or ASIN" - }, - { - "name": "quantity", - "type": "int", - "default": 1, - "required": false, - "help": "Quantity (1-10)" - }, - { - "name": "size", - "type": "str", - "required": false, - "help": "Exact visible size label" - }, - { - "name": "colour", - "type": "str", - "required": false, - "help": "Exact visible colour label" - }, - { - "name": "payment", - "type": "str", - "required": true, - "help": "Payment method; secrets remain browser-only", - "choices": [ - "upi", - "saved-card", - "new-card", - "cod" - ] - }, - { - "name": "card-last4", - "type": "str", - "required": false, - "help": "Saved-card selector: exactly four digits" - }, - { - "name": "place-order", - "type": "boolean", - "default": false, - "required": false, - "help": "Submit the final Amazon action once" - } - ], - "columns": [ - "status", - "asin", - "title", - "size", - "colour", - "quantity", - "item_price", - "total", - "payment_method", - "delivery_date", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/amazon-in/checkout.js", - "sourceFile": "plugins/amazon-in/checkout.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true - }, - { - "site": "amazon-in", - "name": "checkout-status", - "description": "Read the current Amazon.in checkout or payment state without clicking", - "access": "read", - "domain": "amazon.in", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "status", - "order_id", - "total", - "payment_method", - "action" - ], - "type": "js", - "modulePath": "plugins/amazon-in/checkout-status.js", - "sourceFile": "plugins/amazon-in/checkout-status.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "amazon-in", - "name": "login", - "description": "Open amazon-in login", - "access": "write", - "domain": "amazon.in", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/amazon-in/auth.js", - "sourceFile": "plugins/amazon-in/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "amazon-in", - "name": "product", - "description": "Fetch the current Amazon.in price and selected product variant", - "access": "read", - "domain": "amazon.in", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "Amazon.in product URL or ASIN" - } - ], - "columns": [ - "asin", - "title", - "price", - "mrp", - "discount", - "availability", - "size", - "colour", - "image_url", - "product_url" - ], - "type": "js", - "modulePath": "plugins/amazon-in/product.js", - "sourceFile": "plugins/amazon-in/product.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "amazon-in", - "name": "search", - "description": "Search Amazon.in products with inclusive INR price bounds and images", - "access": "read", - "domain": "amazon.in", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Product search query" - }, - { - "name": "min-price", - "type": "number", - "required": false, - "help": "Inclusive minimum price in rupees" - }, - { - "name": "max-price", - "type": "number", - "required": false, - "help": "Inclusive maximum price in rupees" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum results (1-50)" - } - ], - "columns": [ - "rank", - "asin", - "title", - "price", - "mrp", - "rating", - "review_count", - "image_url", - "product_url", - "is_sponsored" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/amazon-in/search.js", - "sourceFile": "plugins/amazon-in/search.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "amazon-in", - "name": "whoami", - "description": "Show the current logged-in amazon-in account", - "access": "read", - "domain": "amazon.in", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_name" - ], - "type": "js", - "modulePath": "plugins/amazon-in/auth.js", - "sourceFile": "plugins/amazon-in/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "amazon-in", - "name": "wishlist", - "description": "Fetch current prices for products in the default Amazon.in wishlist", - "access": "read", - "domain": "amazon.in", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "filter", - "type": "str", - "default": "unpurchased", - "required": false, - "help": "Wishlist items to include", - "choices": [ - "unpurchased", - "all" - ] - } - ], - "columns": [ - "list_name", - "item_id", - "asin", - "title", - "price", - "mrp", - "availability", - "size", - "colour", - "image_url", - "product_url" - ], - "type": "js", - "modulePath": "plugins/amazon-in/wishlist.js", - "sourceFile": "plugins/amazon-in/wishlist.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "antigravity", - "name": "add-context", - "description": "Click the Add context button in the composer (opens file/URL picker for context attachment).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/antigravity/audit-extras.js", - "sourceFile": "plugins/antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "cookies", - "description": "List cookies on the Antigravity renderer (JS-visible via document.cookie).", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/antigravity/storage.js", - "sourceFile": "plugins/antigravity/storage.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "copy-code", - "description": "Return the text of a code block in the current conversation. Default: last code block; pass --index N (1-based from top) to pick a specific one.", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "index", - "type": "int", - "required": false, - "help": "1-based index of code block (default: last)" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/antigravity/audit-extras.js", - "sourceFile": "plugins/antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "copy-message", - "description": "Return the text of the last assistant message (best-effort: walks up from the last visible Copy button).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "click-button", - "type": "boolean", - "default": false, - "required": false, - "help": "Also click the in-UI Copy button" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/antigravity/audit-extras.js", - "sourceFile": "plugins/antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "delete", - "description": "Delete an Antigravity conversation by ID. Antigravity asks for confirmation; we click through it. Require --yes to actually delete.", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" - }, - { - "name": "yes", - "type": "boolean", - "default": false, - "required": false, - "help": "Actually delete (default: dry-run preview)" - } - ], - "columns": [ - "status", - "id" - ], - "type": "js", - "modulePath": "plugins/antigravity/delete.js", - "sourceFile": "plugins/antigravity/delete.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "display-options", - "description": "Open the Display Options menu and list its items.", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Item" - ], - "type": "js", - "modulePath": "plugins/antigravity/audit-extras.js", - "sourceFile": "plugins/antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "dump", - "description": "Dump the DOM to help AI understand the UI", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "htmlFile", - "snapFile" - ], - "type": "js", - "modulePath": "plugins/antigravity/dump.js", - "sourceFile": "plugins/antigravity/dump.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "extract-code", - "description": "Extract multi-line code blocks from the current Antigravity conversation", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "code" - ], - "type": "js", - "modulePath": "plugins/antigravity/extract-code.js", - "sourceFile": "plugins/antigravity/extract-code.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "history", - "description": "List visible Antigravity conversations from the sidebar", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max conversations to return" - } - ], - "columns": [ - "Index", - "Id", - "Title" - ], - "type": "js", - "modulePath": "plugins/antigravity/history.js", - "sourceFile": "plugins/antigravity/history.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "idb-list", - "description": "List IndexedDB databases on the Antigravity renderer.", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/antigravity/storage.js", - "sourceFile": "plugins/antigravity/storage.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "mark-read", - "description": "Mark an unread Antigravity conversation as read. Fails if the row is already read or the postcondition cannot be verified.", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" - } - ], - "columns": [ - "status", - "id", - "clicked" - ], - "type": "js", - "modulePath": "plugins/antigravity/mark-read.js", - "sourceFile": "plugins/antigravity/mark-read.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "model", - "description": "Read or switch the active model in Antigravity. Without arguments, reports the current model. With (substring, case-insensitive), switches.", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": false, - "positional": true, - "help": "Substring (case-insensitive) of target model name. Omit to read current." - }, - { - "name": "list", - "type": "boolean", - "default": false, - "required": false, - "help": "List models in the picker (does not switch)" - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "plugins/antigravity/model.js", - "sourceFile": "plugins/antigravity/model.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "nav", - "description": "Click Go Back or Go Forward (Antigravity in-app history).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "direction", - "type": "str", - "required": true, - "positional": true, - "help": "back or forward" - } - ], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/antigravity/audit-extras.js", - "sourceFile": "plugins/antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "new", - "description": "Start a new conversation / clear context in Antigravity", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "plugins/antigravity/new.js", - "sourceFile": "plugins/antigravity/new.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "react", - "description": "Click \"Good response\" or \"Bad response\" on the LAST assistant message.", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "kind", - "type": "str", - "required": true, - "positional": true, - "help": "good or bad" - } - ], - "columns": [ - "Status", - "Reaction" - ], - "type": "js", - "modulePath": "plugins/antigravity/audit-extras.js", - "sourceFile": "plugins/antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "read", - "description": "Read the latest chat messages from Antigravity AI", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "last", - "type": "str", - "required": false, - "help": "Number of recent messages to read (not fully implemented due to generic structure, currently returns full history text or latest chunk)" - } - ], - "columns": [ - "role", - "content" - ], - "type": "js", - "modulePath": "plugins/antigravity/read.js", - "sourceFile": "plugins/antigravity/read.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "recent-paths", - "description": "Show Antigravity's recently-opened folders/files (history.recentlyOpenedPathsList).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max rows to return" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/antigravity/storage.js", - "sourceFile": "plugins/antigravity/storage.js" - }, - { - "site": "antigravity", - "name": "rename", - "description": "Rename an Antigravity conversation by ID (NOT YET IMPLEMENTED — see source comment).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID (the part after \"convo-pill-\" in the sidebar testid)" - }, - { - "name": "title", - "type": "string", - "required": true, - "positional": true, - "help": "New title" - } - ], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "plugins/antigravity/rename.js", - "sourceFile": "plugins/antigravity/rename.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "revert", - "description": "Click the revert button (per-message revert for agent changes). Requires --yes (this modifies your workspace).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "yes", - "type": "boolean", - "default": false, - "required": false, - "help": "Actually revert (default: dry-run)" - } - ], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/antigravity/audit-extras.js", - "sourceFile": "plugins/antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "send", - "description": "Send a message to Antigravity AI via the internal Lexical editor", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "message", - "type": "str", - "required": true, - "positional": true, - "help": "The message text to send" - } - ], - "columns": [ - "Status", - "Message" - ], - "type": "js", - "modulePath": "plugins/antigravity/send.js", - "sourceFile": "plugins/antigravity/send.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "settings", - "description": "Click the Antigravity settings button (matched by data-testid=\"settings-button\").", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/antigravity/audit-extras.js", - "sourceFile": "plugins/antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "settings-read", - "description": "Read Antigravity's user settings.json (theme, proxy, agCockpit, tfa.system.autoAccept, etc.).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/antigravity/storage.js", - "sourceFile": "plugins/antigravity/storage.js" - }, - { - "site": "antigravity", - "name": "sidebar-toggle", - "description": "Click Toggle Sidebar (collapses/expands the Antigravity sidebar).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/antigravity/audit-extras.js", - "sourceFile": "plugins/antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "state-get", - "description": "Read one value from Antigravity's state.vscdb. Pass --workspace for per-workspace.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Storage key name" - }, - { - "name": "workspace", - "type": "str", - "required": false, - "help": "Workspace id (from workspaces-list) to query per-workspace DB" - }, - { - "name": "max-bytes", - "type": "int", - "default": 8000, - "required": false, - "help": "Truncate value to this many chars" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/antigravity/storage.js", - "sourceFile": "plugins/antigravity/storage.js" - }, - { - "site": "antigravity", - "name": "state-keys", - "description": "List keys in Antigravity's globalStorage state.vscdb (VSCode-style). Pass --workspace to query a per-workspace DB. Works while Antigravity is closed.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "filter", - "type": "str", - "required": false, - "help": "Case-insensitive substring filter over keys" - }, - { - "name": "workspace", - "type": "str", - "required": false, - "help": "Workspace id (from workspaces-list) to query per-workspace DB" - }, - { - "name": "limit", - "type": "int", - "default": 200, - "required": false, - "help": "Max rows to return" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/antigravity/storage.js", - "sourceFile": "plugins/antigravity/storage.js" - }, - { - "site": "antigravity", - "name": "status", - "description": "Check Antigravity CDP connection and get current page state", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "status", - "url", - "title" - ], - "type": "js", - "modulePath": "plugins/antigravity/status.js", - "sourceFile": "plugins/antigravity/status.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "storage-get", - "description": "Read a single localStorage / sessionStorage value on the Antigravity renderer.", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Storage key name" - }, - { - "name": "storage", - "type": "str", - "default": "local", - "required": false, - "help": "\"local\" or \"session\"" - }, - { - "name": "max-bytes", - "type": "int", - "default": 4000, - "required": false, - "help": "Truncate value to this many chars" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/antigravity/storage.js", - "sourceFile": "plugins/antigravity/storage.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "storage-keys", - "description": "List localStorage / sessionStorage keys on the Antigravity renderer (CDP).", - "access": "read", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "storage", - "type": "str", - "default": "local", - "required": false, - "help": "\"local\" or \"session\"" - }, - { - "name": "filter", - "type": "str", - "required": false, - "help": "Case-insensitive substring filter" - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Max rows to return" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/antigravity/storage.js", - "sourceFile": "plugins/antigravity/storage.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "toggle-aux", - "description": "Toggle the Auxiliary Pane (Antigravity's secondary panel for code/preview).", - "access": "write", - "domain": "127.0.0.1", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/antigravity/audit-extras.js", - "sourceFile": "plugins/antigravity/audit-extras.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "watch", - "description": "Stream new chat messages from Antigravity in real-time", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "timeout", - "type": "int", - "default": 86400, - "required": false, - "help": "Max seconds to keep watching (default: 86400 — 24h)" - } - ], - "columns": [], - "type": "js", - "modulePath": "plugins/antigravity/watch.js", - "sourceFile": "plugins/antigravity/watch.js", - "navigateBefore": true - }, - { - "site": "antigravity", - "name": "workspaces-list", - "description": "List Antigravity workspaceStorage entries (each represents a previously-opened folder).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max rows to return" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version", - "Kind", - "Path", - "Workspace Id", - "Folder", - "Modified", - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/antigravity/storage.js", - "sourceFile": "plugins/antigravity/storage.js" - }, - { - "site": "apple-podcasts", - "name": "episodes", - "description": "List recent episodes of an Apple Podcast (use ID from search)", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Podcast ID (collectionId from search output)" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Max episodes to show" - } - ], - "columns": [ - "title", - "duration", - "date" - ], - "type": "js", - "modulePath": "plugins/apple-podcasts/episodes.js", - "sourceFile": "plugins/apple-podcasts/episodes.js" - }, - { - "site": "apple-podcasts", - "name": "search", - "description": "Search Apple Podcasts", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max results" - } - ], - "columns": [ - "id", - "title", - "author", - "episodes", - "genre", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/apple-podcasts/search.js", - "sourceFile": "plugins/apple-podcasts/search.js" - }, - { - "site": "apple-podcasts", - "name": "top", - "description": "Top podcasts chart on Apple Podcasts", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of podcasts (max 100)" - }, - { - "name": "country", - "type": "str", - "default": "us", - "required": false, - "help": "Country code (e.g. us, cn, gb, jp)" - } - ], - "columns": [ - "rank", - "title", - "author", - "id" - ], - "type": "js", - "modulePath": "plugins/apple-podcasts/top.js", - "sourceFile": "plugins/apple-podcasts/top.js" - }, - { - "site": "archive", - "name": "item", - "description": "Fetch metadata for a single Internet Archive item by identifier.", - "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "identifier", - "type": "str", - "required": true, - "positional": true, - "help": "Archive item identifier (e.g. \"open-syllabus\", \"FinalFantasy2_356\")." - } - ], - "columns": [ - "identifier", - "title", - "creator", - "date", - "mediatype", - "collection", - "description", - "file_count", - "url" - ], - "type": "js", - "modulePath": "plugins/archive/item.js", - "sourceFile": "plugins/archive/item.js" - }, - { - "site": "archive", - "name": "search", - "description": "Search Internet Archive items across books, movies, audio, software, and web.", - "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Full-text query (matches title, description, creator, subject)." - }, - { - "name": "mediatype", - "type": "string", - "required": false, - "help": "Restrict to mediatype: texts, movies, audio, software, image, web, data, collection" - }, - { - "name": "sort", - "type": "string", - "default": "downloads", - "required": false, - "help": "Sort key: downloads, date, addeddate, week, title" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max items (max 100; one API page)." - } - ], - "columns": [ - "rank", - "identifier", - "title", - "creator", - "date", - "mediatype", - "downloads", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/archive/search.js", - "sourceFile": "plugins/archive/search.js" - }, - { - "site": "archive", - "name": "snapshots", - "description": "List Wayback Machine snapshots over time for a URL via the CDX API.", - "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "URL to look up (with or without scheme)." - }, - { - "name": "from", - "type": "string", - "required": false, - "help": "Earliest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" - }, - { - "name": "to", - "type": "string", - "required": false, - "help": "Latest year/timestamp (YYYY[MM[DD[hh[mm[ss]]]]])" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max snapshots to return (max 1000)." - } - ], - "columns": [ - "timestamp", - "snapshot_url", - "status", - "mimetype", - "original_url" - ], - "type": "js", - "modulePath": "plugins/archive/snapshots.js", - "sourceFile": "plugins/archive/snapshots.js" - }, - { - "site": "archive", - "name": "wayback", - "description": "Look up the closest Wayback Machine snapshot for a URL.", - "access": "read", - "domain": "archive.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "URL to look up (with or without scheme)." - }, - { - "name": "timestamp", - "type": "string", - "required": false, - "help": "Target timestamp (YYYY[MM[DD[hh[mm[ss]]]]] or ISO date). Defaults to most recent snapshot." - } - ], - "columns": [ - "original_url", - "requested_timestamp", - "snapshot_timestamp", - "snapshot_url", - "status" - ], - "type": "js", - "modulePath": "plugins/archive/wayback.js", - "sourceFile": "plugins/archive/wayback.js" - }, - { - "site": "arxiv", - "name": "author", - "description": "List arXiv papers by a given author (newest first)", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "author", - "type": "str", - "required": true, - "positional": true, - "help": "Author name (e.g. \"Yoshua Bengio\" or \"Y Bengio\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max papers to return (max 50)" - } - ], - "columns": [ - "id", - "title", - "authors", - "published", - "primary_category", - "url" - ], - "type": "js", - "modulePath": "plugins/arxiv/author.js", - "sourceFile": "plugins/arxiv/author.js" - }, - { - "site": "arxiv", - "name": "paper", - "description": "Get arXiv paper details by ID", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "arXiv paper ID (e.g. 1706.03762)" - } - ], - "columns": [ - "id", - "title", - "authors", - "published", - "updated", - "primary_category", - "categories", - "abstract", - "comment", - "pdf", - "url" - ], - "type": "js", - "modulePath": "plugins/arxiv/paper.js", - "sourceFile": "plugins/arxiv/paper.js" - }, - { - "site": "arxiv", - "name": "recent", - "description": "List recent arXiv submissions in a category", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "category", - "type": "str", - "required": true, - "positional": true, - "help": "arXiv category (e.g. cs.CL, cs.LG, math.PR, q-bio.NC)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max results (max 50)" - } - ], - "columns": [ - "id", - "title", - "authors", - "published", - "primary_category", - "url" - ], - "type": "js", - "modulePath": "plugins/arxiv/recent.js", - "sourceFile": "plugins/arxiv/recent.js" - }, - { - "site": "arxiv", - "name": "search", - "description": "Search arXiv papers", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"attention is all you need\")" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max results (max 25)" - } - ], - "columns": [ - "id", - "title", - "authors", - "published", - "primary_category", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/arxiv/search.js", - "sourceFile": "plugins/arxiv/search.js" - }, - { - "site": "band", - "name": "bands", - "description": "List all Bands you belong to", - "access": "read", - "domain": "www.band.us", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "band_no", - "name", - "members" - ], - "type": "js", - "modulePath": "plugins/band/bands.js", - "sourceFile": "plugins/band/bands.js", - "navigateBefore": "https://www.band.us" - }, - { - "site": "band", - "name": "login", - "description": "Open band login", - "access": "write", - "domain": "band.us", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/band/auth.js", - "sourceFile": "plugins/band/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "band", - "name": "mentions", - "description": "Show Band notifications where you are @mentioned", - "access": "read", - "domain": "www.band.us", - "strategy": "intercept", - "browser": true, - "args": [ - { - "name": "filter", - "type": "str", - "default": "mentioned", - "required": false, - "help": "Filter: mentioned (default) | all | post | comment", - "choices": [ - "mentioned", - "all", - "post", - "comment" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results" - }, - { - "name": "unread", - "type": "bool", - "default": false, - "required": false, - "help": "Show only unread notifications" - } - ], - "columns": [ - "time", - "band", - "type", - "from", - "text", - "url" - ], - "type": "js", - "modulePath": "plugins/band/mentions.js", - "sourceFile": "plugins/band/mentions.js", - "navigateBefore": true - }, - { - "site": "band", - "name": "post", - "description": "Export full content of a post including comments", - "access": "read", - "domain": "www.band.us", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "band_no", - "type": "int", - "required": true, - "positional": true, - "help": "Band number" - }, - { - "name": "post_no", - "type": "int", - "required": true, - "positional": true, - "help": "Post number" - }, - { - "name": "output", - "type": "str", - "default": "", - "required": false, - "help": "Directory to save attached photos" - }, - { - "name": "comments", - "type": "bool", - "default": true, - "required": false, - "help": "Include comments (default: true)" - } - ], - "columns": [ - "type", - "author", - "date", - "text" - ], - "type": "js", - "modulePath": "plugins/band/post.js", - "sourceFile": "plugins/band/post.js", - "navigateBefore": false - }, - { - "site": "band", - "name": "posts", - "description": "List posts from a Band", - "access": "read", - "domain": "www.band.us", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "band_no", - "type": "int", - "required": true, - "positional": true, - "help": "Band number (get it from: band bands)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results" - } - ], - "columns": [ - "date", - "author", - "content", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/band/posts.js", - "sourceFile": "plugins/band/posts.js", - "navigateBefore": false - }, - { - "site": "band", - "name": "whoami", - "description": "Show the current logged-in band account", - "access": "read", - "domain": "band.us", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id" - ], - "type": "js", - "modulePath": "plugins/band/auth.js", - "sourceFile": "plugins/band/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "barchart", - "name": "flow", - "description": "Barchart unusual options activity / options flow", - "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "type", - "type": "str", - "default": "all", - "required": false, - "help": "Filter: all, call, or put", - "choices": [ - "all", - "call", - "put" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "symbol", - "type", - "strike", - "expiration", - "last", - "volume", - "openInterest", - "volOiRatio", - "iv" - ], - "type": "js", - "modulePath": "plugins/barchart/flow.js", - "sourceFile": "plugins/barchart/flow.js", - "navigateBefore": "https://www.barchart.com" - }, - { - "site": "barchart", - "name": "greeks", - "description": "Barchart options greeks overview (IV, delta, gamma, theta, vega)", - "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Stock ticker (e.g. AAPL)" - }, - { - "name": "expiration", - "type": "str", - "required": false, - "help": "Expiration date (YYYY-MM-DD). Defaults to the nearest available expiration." - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of near-the-money strikes per type (1-100)" - } - ], - "columns": [ - "type", - "strike", - "last", - "iv", - "delta", - "gamma", - "theta", - "vega", - "rho", - "volume", - "openInterest", - "expiration" - ], - "type": "js", - "modulePath": "plugins/barchart/greeks.js", - "sourceFile": "plugins/barchart/greeks.js", - "navigateBefore": "https://www.barchart.com" - }, - { - "site": "barchart", - "name": "options", - "description": "Barchart options chain with greeks, IV, volume, and open interest", - "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Stock ticker (e.g. AAPL)" - }, - { - "name": "type", - "type": "str", - "default": "Call", - "required": false, - "help": "Option type: Call or Put", - "choices": [ - "Call", - "Put" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max number of strikes to return" - } - ], - "columns": [ - "strike", - "bid", - "ask", - "last", - "change", - "volume", - "openInterest", - "iv", - "delta", - "gamma", - "theta", - "vega", - "expiration" - ], - "type": "js", - "modulePath": "plugins/barchart/options.js", - "sourceFile": "plugins/barchart/options.js", - "navigateBefore": "https://www.barchart.com" - }, - { - "site": "barchart", - "name": "quote", - "description": "Barchart stock quote with price, volume, and key metrics", - "access": "read", - "domain": "www.barchart.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" - } - ], - "columns": [ - "symbol", - "name", - "price", - "change", - "changePct", - "open", - "high", - "low", - "prevClose", - "volume", - "avgVolume", - "marketCap", - "peRatio", - "eps" - ], - "type": "js", - "modulePath": "plugins/barchart/quote.js", - "sourceFile": "plugins/barchart/quote.js", - "navigateBefore": "https://www.barchart.com" - }, - { - "site": "bbc", - "name": "news", - "description": "BBC News headlines (RSS)", - "access": "read", - "domain": "www.bbc.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of headlines (max 50)" - } - ], - "columns": [ - "rank", - "title", - "description", - "url" - ], - "type": "js", - "modulePath": "plugins/bbc/news.js", - "sourceFile": "plugins/bbc/news.js" - }, - { - "site": "bbc", - "name": "topic", - "description": "BBC News headlines for a specific section (RSS feed)", - "access": "read", - "domain": "www.bbc.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "topic", - "type": "str", - "required": true, - "positional": true, - "help": "Section name (world / business / politics / health / education / science_and_environment / technology / entertainment_and_arts)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max headlines (1-50)" - } - ], - "columns": [ - "rank", - "title", - "description", - "pubDate", - "url" - ], - "type": "js", - "modulePath": "plugins/bbc/topic.js", - "sourceFile": "plugins/bbc/topic.js" - }, - { - "site": "bigbasket", - "name": "add-to-cart", - "description": "Add a BigBasket product to cart", - "access": "write", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "product", - "type": "str", - "required": true, - "positional": true, - "help": "Product ID or URL" - }, - { - "name": "quantity", - "type": "int", - "default": 1, - "required": false, - "help": "Quantity to add (max 20)" - } - ], - "columns": [ - "ok", - "product_id", - "quantity", - "url", - "message" - ], - "type": "js", - "modulePath": "plugins/bigbasket/add-to-cart.js", - "sourceFile": "plugins/bigbasket/add-to-cart.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "cart", - "description": "Read BigBasket cart line items", - "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "product_id", - "title", - "quantity", - "price", - "line_total", - "availability", - "url" - ], - "type": "js", - "modulePath": "plugins/bigbasket/cart.js", - "sourceFile": "plugins/bigbasket/cart.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "category", - "description": "Read BigBasket category product cards", - "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "category", - "type": "str", - "required": true, - "positional": true, - "help": "Category URL or slug" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum products to return (max 50)" - } - ], - "columns": [ - "rank", - "product_id", - "title", - "brand", - "pack_size", - "price", - "mrp", - "discount", - "availability", - "url" - ], - "type": "js", - "modulePath": "plugins/bigbasket/category.js", - "sourceFile": "plugins/bigbasket/category.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "checkout", - "description": "Open BigBasket checkout review without placing an order", - "access": "write", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "ok", - "stage", - "cart_total", - "address_ready", - "delivery_ready", - "payment_ready", - "next_action", - "url" - ], - "type": "js", - "modulePath": "plugins/bigbasket/checkout.js", - "sourceFile": "plugins/bigbasket/checkout.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "location", - "description": "Show the selected BigBasket delivery location", - "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "selected", - "label", - "area", - "city", - "pincode", - "source" - ], - "type": "js", - "modulePath": "plugins/bigbasket/location.js", - "sourceFile": "plugins/bigbasket/location.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "product", - "description": "Read BigBasket product details", - "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "product", - "type": "str", - "required": true, - "positional": true, - "help": "Product ID or URL" - } - ], - "columns": [ - "product_id", - "title", - "brand", - "pack_size", - "price", - "mrp", - "discount", - "availability", - "delivery", - "image_url", - "url" - ], - "type": "js", - "modulePath": "plugins/bigbasket/product.js", - "sourceFile": "plugins/bigbasket/product.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "bigbasket", - "name": "search", - "description": "Search BigBasket products", - "access": "read", - "domain": "www.bigbasket.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum products to return (max 50)" - } - ], - "columns": [ - "rank", - "product_id", - "title", - "brand", - "pack_size", - "price", - "mrp", - "discount", - "availability", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/bigbasket/search.js", - "sourceFile": "plugins/bigbasket/search.js", - "navigateBefore": "https://www.bigbasket.com" - }, - { - "site": "binance", - "name": "asks", - "description": "Order book ask prices for a trading pair", - "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of price levels (5, 10, 20, 50, 100)" - } - ], - "columns": [ - "rank", - "ask_price", - "ask_qty" - ], - "type": "js", - "modulePath": "plugins/binance/asks.js", - "sourceFile": "plugins/binance/asks.js" - }, - { - "site": "binance", - "name": "depth", - "description": "Order book bid and ask prices for a trading pair", - "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of price levels (5, 10, 20, 50, 100)" - } - ], - "columns": [ - "rank", - "bid_price", - "bid_qty", - "ask_price", - "ask_qty" - ], - "type": "js", - "modulePath": "plugins/binance/depth.js", - "sourceFile": "plugins/binance/depth.js" - }, - { - "site": "binance", - "name": "gainers", - "description": "Top gaining trading pairs by 24h price change", - "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of trading pairs" - } - ], - "columns": [ - "rank", - "symbol", - "price", - "change_24h", - "volume" - ], - "type": "js", - "modulePath": "plugins/binance/gainers.js", - "sourceFile": "plugins/binance/gainers.js" - }, - { - "site": "binance", - "name": "klines", - "description": "Candlestick/kline data for a trading pair", - "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" - }, - { - "name": "interval", - "type": "str", - "default": "1d", - "required": false, - "help": "Kline interval (1m, 5m, 15m, 1h, 4h, 1d, 1w, 1M)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of klines (max 1000)" - } - ], - "columns": [ - "open", - "high", - "low", - "close", - "volume" - ], - "type": "js", - "modulePath": "plugins/binance/klines.js", - "sourceFile": "plugins/binance/klines.js" - }, - { - "site": "binance", - "name": "losers", - "description": "Top losing trading pairs by 24h price change", - "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of trading pairs" - } - ], - "columns": [ - "rank", - "symbol", - "price", - "change_24h", - "volume" - ], - "type": "js", - "modulePath": "plugins/binance/losers.js", - "sourceFile": "plugins/binance/losers.js" - }, - { - "site": "binance", - "name": "pairs", - "description": "List active trading pairs on Binance", - "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of trading pairs" - } - ], - "columns": [ - "symbol", - "base", - "quote", - "status" - ], - "type": "js", - "modulePath": "plugins/binance/pairs.js", - "sourceFile": "plugins/binance/pairs.js" - }, - { - "site": "binance", - "name": "price", - "description": "Quick price check for a trading pair", - "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" - } - ], - "columns": [ - "symbol", - "price", - "change", - "change_pct", - "high", - "low", - "volume", - "quote_volume", - "trades" - ], - "type": "js", - "modulePath": "plugins/binance/price.js", - "sourceFile": "plugins/binance/price.js" - }, - { - "site": "binance", - "name": "prices", - "description": "Latest prices for all trading pairs", - "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of prices" - } - ], - "columns": [ - "rank", - "symbol", - "price" - ], - "type": "js", - "modulePath": "plugins/binance/prices.js", - "sourceFile": "plugins/binance/prices.js" - }, - { - "site": "binance", - "name": "ticker", - "description": "24h ticker statistics for top trading pairs by volume", - "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of tickers" - } - ], - "columns": [ - "symbol", - "price", - "change_pct", - "high", - "low", - "volume", - "quote_vol", - "trades" - ], - "type": "js", - "modulePath": "plugins/binance/ticker.js", - "sourceFile": "plugins/binance/ticker.js" - }, - { - "site": "binance", - "name": "top", - "description": "Top trading pairs by 24h volume on Binance", - "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of trading pairs" - } - ], - "columns": [ - "rank", - "symbol", - "price", - "change_24h", - "high", - "low", - "volume" - ], - "type": "js", - "modulePath": "plugins/binance/top.js", - "sourceFile": "plugins/binance/top.js" - }, - { - "site": "binance", - "name": "trades", - "description": "Recent trades for a trading pair", - "access": "read", - "domain": "data-api.binance.vision", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of trades (max 1000)" - } - ], - "columns": [ - "id", - "price", - "qty", - "quote_qty", - "buyer_maker" - ], - "type": "js", - "modulePath": "plugins/binance/trades.js", - "sourceFile": "plugins/binance/trades.js" - }, - { - "site": "blinkit", - "name": "add-to-cart", - "description": "Add a Blinkit product to cart", - "access": "write", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "productId", - "type": "str", - "required": true, - "positional": true, - "help": "Blinkit product id" - }, - { - "name": "quantity", - "type": "int", - "default": 1, - "required": false, - "help": "Quantity to add (default 1, max 12)" - }, - { - "name": "lat", - "type": "str", - "required": false, - "help": "Delivery latitude (defaults to current Blinkit browser location)" - }, - { - "name": "lon", - "type": "str", - "required": false, - "help": "Delivery longitude (defaults to current Blinkit browser location)" - } - ], - "columns": [ - "status", - "productId", - "quantity", - "itemCount", - "itemsTotal", - "payable", - "message" - ], - "type": "js", - "modulePath": "plugins/blinkit/add-to-cart.js", - "sourceFile": "plugins/blinkit/add-to-cart.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "cart", - "description": "Show the current Blinkit cart", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "productId", - "name", - "variant", - "price", - "quantity", - "total", - "itemCount", - "payable", - "cartState" - ], - "type": "js", - "modulePath": "plugins/blinkit/cart.js", - "sourceFile": "plugins/blinkit/cart.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "checkout", - "description": "Review Blinkit checkout totals and blockers without placing an order", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "itemCount", - "itemsTotal", - "deliveryCharge", - "handlingCharge", - "payable", - "cartState", - "checkoutBlocked", - "validations" - ], - "type": "js", - "modulePath": "plugins/blinkit/checkout.js", - "sourceFile": "plugins/blinkit/checkout.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "location", - "description": "Show the selected Blinkit delivery location", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "selected", - "label", - "area", - "city", - "pincode", - "hasCoordinates", - "source" - ], - "type": "js", - "modulePath": "plugins/blinkit/location.js", - "sourceFile": "plugins/blinkit/location.js", - "navigateBefore": "https://blinkit.com" - }, - { - "site": "blinkit", - "name": "login", - "description": "Open blinkit login", - "access": "write", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "phone", - "user_id", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/blinkit/auth.js", - "sourceFile": "plugins/blinkit/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "blinkit", - "name": "place-order", - "description": "Submit the visible Blinkit final order/payment action. Requires --confirm.", - "access": "write", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "confirm", - "type": "bool", - "default": false, - "required": false, - "help": "Required acknowledgement that this may place/pay for a real order" - } - ], - "columns": [ - "status", - "confirmed", - "itemCount", - "payable", - "orderId", - "url", - "message" - ], - "type": "js", - "modulePath": "plugins/blinkit/place-order.js", - "sourceFile": "plugins/blinkit/place-order.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "product", - "description": "Read Blinkit product details for a delivery location", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "productId", - "type": "str", - "required": true, - "positional": true, - "help": "Blinkit product id" - }, - { - "name": "lat", - "type": "str", - "required": false, - "help": "Delivery latitude (defaults to current Blinkit browser location)" - }, - { - "name": "lon", - "type": "str", - "required": false, - "help": "Delivery longitude (defaults to current Blinkit browser location)" - } - ], - "columns": [ - "productId", - "name", - "brand", - "variant", - "price", - "mrp", - "currency", - "inventory", - "available", - "imageUrl", - "url" - ], - "type": "js", - "modulePath": "plugins/blinkit/product.js", - "sourceFile": "plugins/blinkit/product.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "search", - "description": "Search Blinkit products for a delivery location", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (max 48)" - }, - { - "name": "lat", - "type": "str", - "required": false, - "help": "Delivery latitude (defaults to current Blinkit browser location)" - }, - { - "name": "lon", - "type": "str", - "required": false, - "help": "Delivery longitude (defaults to current Blinkit browser location)" - } - ], - "columns": [ - "rank", - "productId", - "name", - "brand", - "variant", - "price", - "mrp", - "currency", - "inventory", - "available", - "imageUrl", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/blinkit/search.js", - "sourceFile": "plugins/blinkit/search.js", - "navigateBefore": false - }, - { - "site": "blinkit", - "name": "whoami", - "description": "Show the current logged-in blinkit account", - "access": "read", - "domain": "blinkit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "phone", - "user_id" - ], - "type": "js", - "modulePath": "plugins/blinkit/auth.js", - "sourceFile": "plugins/blinkit/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "bloomberg", - "name": "businessweek", - "description": "Bloomberg Businessweek top stories", - "access": "read", - "domain": "www.bloomberg.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of stories to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "plugins/bloomberg/businessweek.js", - "sourceFile": "plugins/bloomberg/businessweek.js" - }, - { - "site": "bloomberg", - "name": "crypto", - "description": "Bloomberg Crypto top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "plugins/bloomberg/crypto.js", - "sourceFile": "plugins/bloomberg/crypto.js" - }, - { - "site": "bloomberg", - "name": "economics", - "description": "Bloomberg Economics top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "plugins/bloomberg/economics.js", - "sourceFile": "plugins/bloomberg/economics.js" - }, - { - "site": "bloomberg", - "name": "feeds", - "description": "List the Bloomberg RSS feed aliases used by the adapter", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "name", - "url" - ], - "type": "js", - "modulePath": "plugins/bloomberg/feeds.js", - "sourceFile": "plugins/bloomberg/feeds.js" - }, - { - "site": "bloomberg", - "name": "green", - "description": "Bloomberg Green (climate & energy) top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "plugins/bloomberg/green.js", - "sourceFile": "plugins/bloomberg/green.js" - }, - { - "site": "bloomberg", - "name": "industries", - "description": "Bloomberg Industries top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "plugins/bloomberg/industries.js", - "sourceFile": "plugins/bloomberg/industries.js" - }, - { - "site": "bloomberg", - "name": "main", - "description": "Bloomberg homepage top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "plugins/bloomberg/main.js", - "sourceFile": "plugins/bloomberg/main.js" - }, - { - "site": "bloomberg", - "name": "markets", - "description": "Bloomberg Markets top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "plugins/bloomberg/markets.js", - "sourceFile": "plugins/bloomberg/markets.js" - }, - { - "site": "bloomberg", - "name": "news", - "description": "Read a Bloomberg story/article page and return title, full content, and media links", - "access": "read", - "domain": "www.bloomberg.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "link", - "type": "str", - "required": true, - "positional": true, - "help": "Bloomberg story/article URL or relative Bloomberg path" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks", - "content" - ], - "type": "js", - "modulePath": "plugins/bloomberg/news.js", - "sourceFile": "plugins/bloomberg/news.js", - "navigateBefore": "https://www.bloomberg.com" - }, - { - "site": "bloomberg", - "name": "opinions", - "description": "Bloomberg Opinion top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "plugins/bloomberg/opinions.js", - "sourceFile": "plugins/bloomberg/opinions.js" - }, - { - "site": "bloomberg", - "name": "politics", - "description": "Bloomberg Politics top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "plugins/bloomberg/politics.js", - "sourceFile": "plugins/bloomberg/politics.js" - }, - { - "site": "bloomberg", - "name": "pursuits", - "description": "Bloomberg Pursuits (lifestyle) top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "plugins/bloomberg/pursuits.js", - "sourceFile": "plugins/bloomberg/pursuits.js" - }, - { - "site": "bloomberg", - "name": "tech", - "description": "Bloomberg Tech top stories (RSS)", - "access": "read", - "domain": "feeds.bloomberg.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 1, - "required": false, - "help": "Number of feed items to return (max 20)" - } - ], - "columns": [ - "title", - "summary", - "link", - "mediaLinks" - ], - "type": "js", - "modulePath": "plugins/bloomberg/tech.js", - "sourceFile": "plugins/bloomberg/tech.js" - }, - { - "site": "bluesky", - "name": "feeds", - "description": "Popular Bluesky feed generators", - "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of feeds" - } - ], - "columns": [ - "rank", - "name", - "likes", - "creator", - "description" - ], - "type": "js", - "modulePath": "plugins/bluesky/feeds.js", - "sourceFile": "plugins/bluesky/feeds.js" - }, - { - "site": "bluesky", - "name": "followers", - "description": "List followers of a Bluesky user", - "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "handle", - "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of followers" - } - ], - "columns": [ - "rank", - "handle", - "name", - "description" - ], - "type": "js", - "modulePath": "plugins/bluesky/followers.js", - "sourceFile": "plugins/bluesky/followers.js" - }, - { - "site": "bluesky", - "name": "following", - "description": "List accounts a Bluesky user is following", - "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "handle", - "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of accounts" - } - ], - "columns": [ - "rank", - "handle", - "name", - "description" - ], - "type": "js", - "modulePath": "plugins/bluesky/following.js", - "sourceFile": "plugins/bluesky/following.js" - }, - { - "site": "bluesky", - "name": "profile", - "description": "Get Bluesky user profile info", - "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "handle", - "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle (e.g. bsky.app, jay.bsky.team)" - } - ], - "columns": [ - "handle", - "name", - "followers", - "following", - "posts", - "description" - ], - "type": "js", - "modulePath": "plugins/bluesky/profile.js", - "sourceFile": "plugins/bluesky/profile.js" - }, - { - "site": "bluesky", - "name": "search", - "description": "Search Bluesky users", - "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "handle", - "name", - "followers", - "description" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/bluesky/search.js", - "sourceFile": "plugins/bluesky/search.js" - }, - { - "site": "bluesky", - "name": "starter-packs", - "description": "Get starter packs created by a Bluesky user", - "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "handle", - "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of starter packs" - } - ], - "columns": [ - "rank", - "name", - "description", - "members", - "joins" - ], - "type": "js", - "modulePath": "plugins/bluesky/starter-packs.js", - "sourceFile": "plugins/bluesky/starter-packs.js" - }, - { - "site": "bluesky", - "name": "thread", - "description": "Get a Bluesky post thread with replies", - "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "uri", - "type": "str", - "required": true, - "positional": true, - "help": "Post AT URI (at://did:.../app.bsky.feed.post/...) or bsky.app URL" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of replies" - } - ], - "columns": [ - "author", - "text", - "likes", - "reposts", - "replies_count" - ], - "type": "js", - "modulePath": "plugins/bluesky/thread.js", - "sourceFile": "plugins/bluesky/thread.js" - }, - { - "site": "bluesky", - "name": "trending", - "description": "Trending topics on Bluesky", - "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of topics" - } - ], - "columns": [ - "rank", - "topic", - "link" - ], - "type": "js", - "modulePath": "plugins/bluesky/trending.js", - "sourceFile": "plugins/bluesky/trending.js" - }, - { - "site": "bluesky", - "name": "user", - "description": "Get recent posts from a Bluesky user", - "access": "read", - "domain": "public.api.bsky.app", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "handle", - "type": "str", - "required": true, - "positional": true, - "help": "Bluesky handle (e.g. bsky.app)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts" - } - ], - "columns": [ - "rank", - "uri", - "text", - "likes", - "reposts", - "replies" - ], - "type": "js", - "modulePath": "plugins/bluesky/user.js", - "sourceFile": "plugins/bluesky/user.js" - }, - { - "site": "bmwblog", - "name": "article", - "description": "Read a BMWBLOG article by URL or slug", - "access": "read", - "domain": "www.bmwblog.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "url-or-slug", - "type": "str", - "required": true, - "positional": true, - "help": "BMWBLOG article URL or slug" - } - ], - "columns": [ - "title", - "date", - "author", - "sections", - "excerpt", - "url", - "content" - ], - "type": "js", - "modulePath": "plugins/bmwblog/article.js", - "sourceFile": "plugins/bmwblog/article.js" - }, - { - "site": "bmwblog", - "name": "latest", - "description": "List the latest BMWBLOG articles", - "access": "read", - "domain": "www.bmwblog.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of articles (1-50)" - } - ], - "columns": [ - "rank", - "title", - "date", - "author", - "section", - "excerpt", - "url" - ], - "type": "js", - "modulePath": "plugins/bmwblog/latest.js", - "sourceFile": "plugins/bmwblog/latest.js" - }, - { - "site": "bmwblog", - "name": "search", - "description": "Search BMWBLOG articles", - "access": "read", - "domain": "www.bmwblog.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results (1-50)" - } - ], - "columns": [ - "rank", - "title", - "date", - "author", - "section", - "excerpt", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/bmwblog/search.js", - "sourceFile": "plugins/bmwblog/search.js" - }, - { - "site": "booking", - "name": "search", - "description": "Search Booking.com hotels by destination and dates (server-rendered card scrape).", - "access": "read", - "example": "webcmd booking search Tokyo --checkin 2026-06-15 --checkout 2026-06-17 -f yaml", - "domain": "www.booking.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "destination", - "type": "str", - "required": true, - "positional": true, - "help": "Destination keyword (city, district, or hotel name)" - }, - { - "name": "checkin", - "type": "str", - "required": true, - "help": "Check-in date YYYY-MM-DD" - }, - { - "name": "checkout", - "type": "str", - "required": true, - "help": "Check-out date YYYY-MM-DD" - }, - { - "name": "adults", - "type": "int", - "default": 2, - "required": false, - "help": "Number of adults (1-30)" - }, - { - "name": "rooms", - "type": "int", - "default": 1, - "required": false, - "help": "Number of rooms (1-30)" - }, - { - "name": "children", - "type": "int", - "default": 0, - "required": false, - "help": "Number of children (0-10)" - }, - { - "name": "currency", - "type": "str", - "required": false, - "help": "Force result currency (e.g. USD, JPY, CNY)" - }, - { - "name": "lang", - "type": "str", - "required": false, - "help": "Force result language (e.g. en-us, zh-cn, ja)" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max rows to return (1-100; Booking pages 25 per request)" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Result offset for pagination (multiple of 25)" - } - ], - "columns": [ - "rank", - "name", - "country", - "slug", - "star_rating", - "review_score", - "review_count", - "price_amount", - "price_currency", - "distance", - "recommended_room", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/booking/search.js", - "sourceFile": "plugins/booking/search.js" - }, - { - "site": "brave", - "name": "search", - "description": "Search Brave Search", - "access": "read", - "domain": "search.brave.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results per page (max 18)" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Page offset (0, 1, 2...). Brave returns ~18 results per page" - } - ], - "columns": [ - "rank", - "title", - "url", - "snippet" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/brave/search.js", - "sourceFile": "plugins/brave/search.js" - }, - { - "site": "chatgpt", - "name": "ask", - "description": "Send a prompt to ChatGPT web and wait for the response", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait for response" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Continue an existing ChatGPT conversation ID or /c/ URL" - }, - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" - }, - { - "name": "wait", - "type": "boolean", - "default": true, - "required": false, - "help": "Wait for the assistant response after sending" - }, - { - "name": "deep-research", - "type": "boolean", - "default": false, - "required": false, - "help": "Enable ChatGPT Deep Research (Deep Research)" - }, - { - "name": "web-search", - "type": "boolean", - "default": false, - "required": false, - "help": "Enable ChatGPT Web Search (Web Search)" - } - ], - "columns": [ - "conversationId", - "conversationUrl", - "tool", - "response" - ], - "type": "js", - "modulePath": "plugins/chatgpt/ask.js", - "sourceFile": "plugins/chatgpt/ask.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "deep-research-result", - "description": "Read a ChatGPT Deep Research report or progress from the conversation payload", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Conversation ID or full /c/ URL" - }, - { - "name": "wait", - "type": "boolean", - "default": false, - "required": false, - "help": "Wait until Deep Research completes or becomes extractable" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait when --wait is true" - }, - { - "name": "stable", - "type": "int", - "default": 6, - "required": false, - "help": "Seconds the report text must remain unchanged when --wait is true" - } - ], - "columns": [ - "conversationId", - "status", - "report", - "sources", - "progress", - "asyncTaskConversationId", - "widgetSessionId", - "asyncStatus", - "venusMessageType", - "venusStatus", - "waitingForUserUntil", - "planTitle", - "planId", - "url", - "method", - "diagnostics" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/chatgpt/deep-research-result.js", - "sourceFile": "plugins/chatgpt/deep-research-result.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "detail", - "description": "Open a ChatGPT web conversation by ID and read its messages", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Conversation ID or full /c/ URL" - }, - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" - }, - { - "name": "wait", - "type": "boolean", - "default": false, - "required": false, - "help": "Wait until the conversation stops generating and stabilizes" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait when --wait is true" - }, - { - "name": "stable", - "type": "int", - "default": 6, - "required": false, - "help": "Seconds the final messages must remain unchanged when --wait is true" - } - ], - "columns": [ - "Index", - "Role", - "Text", - "Generating", - "StableSeconds" - ], - "type": "js", - "modulePath": "plugins/chatgpt/detail.js", - "sourceFile": "plugins/chatgpt/detail.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "history", - "description": "List visible ChatGPT web conversation history from the sidebar", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max conversations to show" - } - ], - "columns": [ - "Index", - "Id", - "Title", - "Url" - ], - "type": "js", - "modulePath": "plugins/chatgpt/history.js", - "sourceFile": "plugins/chatgpt/history.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "image", - "description": "Generate images with ChatGPT web and save them locally", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Image prompt to send to ChatGPT" - }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Local image path to attach before prompting; comma-separated paths are supported", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - }, - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Start image generation inside a ChatGPT project ID or /g/g-p- URL" - }, - { - "name": "op", - "type": "str", - "required": false, - "help": "Output directory (default: ~/Pictures/chatgpt)", - "file": { - "direction": "output", - "pathKind": "directory", - "multiple": false, - "defaultPath": "~/Pictures/chatgpt" - } - }, - { - "name": "sd", - "type": "boolean", - "default": false, - "required": false, - "help": "Skip download shorthand; only show ChatGPT link" - }, - { - "name": "timeout", - "type": "int", - "default": 240, - "required": false, - "help": "Max seconds for the overall command (default: 240)" - } - ], - "columns": [ - "status", - "file", - "link" - ], - "defaultFormat": "plain", - "type": "js", - "modulePath": "plugins/chatgpt/image.js", - "sourceFile": "plugins/chatgpt/image.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "login", - "description": "Open chatgpt login", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/chatgpt/auth.js", - "sourceFile": "plugins/chatgpt/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "model", - "description": "Switch ChatGPT web model or intelligence level (GPT-5.6 Pro, fast, balanced, advanced, very-high, pro)", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "model", - "type": "str", - "required": true, - "positional": true, - "help": "ChatGPT model or intelligence level to switch to", - "choices": [ - "fast", - "speed", - "instant", - "balanced", - "balance", - "medium", - "advanced", - "high", - "thinking", - "very-high", - "ultra", - "xhigh", - "x-high", - "extra-high", - "very high", - "gpt-5.6-pro", - "gpt-5-6-pro", - "gpt-5.6-sol-pro", - "gpt-5-6-sol-pro", - "gpt-5.6", - "gpt-5-6", - "5.6-pro", - "5.6", - "pro", - "professional" - ] - }, - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Open a ChatGPT project ID or /g/g-p- URL before switching intelligence level" - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "plugins/chatgpt/model.js", - "sourceFile": "plugins/chatgpt/model.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "new", - "description": "Start a new ChatGPT web conversation", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" - } - ], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/chatgpt/new.js", - "sourceFile": "plugins/chatgpt/new.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "project-file-add", - "description": "Upload files to a ChatGPT project as project knowledge (not just conversation attachments)", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "file", - "type": "str", - "required": true, - "positional": true, - "help": "Local file path(s) to upload; comma-separated paths are supported", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "application/pdf", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - }, - { - "name": "id", - "type": "str", - "required": true, - "help": "Project ID or /g/g-p- URL" - } - ], - "columns": [ - "Status", - "File" - ], - "type": "js", - "modulePath": "plugins/chatgpt/project-file-add.js", - "sourceFile": "plugins/chatgpt/project-file-add.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "project-list", - "description": "List visible ChatGPT projects from the sidebar", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max projects to show" - } - ], - "columns": [ - "Index", - "Id", - "Title", - "Url" - ], - "type": "js", - "modulePath": "plugins/chatgpt/project-list.js", - "sourceFile": "plugins/chatgpt/project-list.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "read", - "description": "Read messages in the current ChatGPT web conversation", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" - } - ], - "columns": [ - "Index", - "Role", - "Text" - ], - "type": "js", - "modulePath": "plugins/chatgpt/read.js", - "sourceFile": "plugins/chatgpt/read.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "send", - "description": "Send a prompt to ChatGPT web without waiting for the response", - "access": "write", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Continue an existing ChatGPT conversation ID or /c/ URL" - }, - { - "name": "project", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Start a new chat inside a ChatGPT project ID or /g/g-p- URL" - } - ], - "columns": [ - "Status", - "InjectedText" - ], - "type": "js", - "modulePath": "plugins/chatgpt/send.js", - "sourceFile": "plugins/chatgpt/send.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "status", - "description": "Check ChatGPT web page availability and login state", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status", - "Login", - "Url" - ], - "type": "js", - "modulePath": "plugins/chatgpt/status.js", - "sourceFile": "plugins/chatgpt/status.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt", - "name": "whoami", - "description": "Show the current logged-in chatgpt account", - "access": "read", - "domain": "chatgpt.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "plugins/chatgpt/auth.js", - "sourceFile": "plugins/chatgpt/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "chatgpt-app", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", - "access": "write", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "model", - "type": "str", - "required": false, - "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] - }, - { - "name": "timeout", - "type": "int", - "default": 30, - "required": false, - "help": "Max seconds to wait for response (default: 30)" - }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Path to local image to attach (optional)" - } - ], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "plugins/chatgpt-app/ask.js", - "sourceFile": "plugins/chatgpt-app/ask.js" - }, - { - "site": "chatgpt-app", - "name": "model", - "description": "Switch ChatGPT Desktop model/mode (auto, instant, thinking, 5.2-instant, 5.2-thinking)", - "access": "read", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "model", - "type": "str", - "required": true, - "positional": true, - "help": "Model to switch to", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "plugins/chatgpt-app/model.js", - "sourceFile": "plugins/chatgpt-app/model.js" - }, - { - "site": "chatgpt-app", - "name": "new", - "description": "Open a new chat in ChatGPT Desktop App", - "access": "write", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "temp", - "type": "boolean", - "default": false, - "required": false, - "help": "Open a temporary chat with privacy protection" - } - ], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/chatgpt-app/new.js", - "sourceFile": "plugins/chatgpt-app/new.js" - }, - { - "site": "chatgpt-app", - "name": "read", - "description": "Read the last visible message from the focused ChatGPT Desktop window", - "access": "read", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "plugins/chatgpt-app/read.js", - "sourceFile": "plugins/chatgpt-app/read.js" - }, - { - "site": "chatgpt-app", - "name": "send", - "description": "Send a message to the active ChatGPT Desktop App window", - "access": "write", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Message to send" - }, - { - "name": "model", - "type": "str", - "required": false, - "help": "Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking", - "choices": [ - "auto", - "instant", - "thinking", - "5.2-instant", - "5.2-thinking" - ] - } - ], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/chatgpt-app/send.js", - "sourceFile": "plugins/chatgpt-app/send.js" - }, - { - "site": "chatgpt-app", - "name": "status", - "description": "Check if ChatGPT Desktop App is running natively on macOS", - "access": "read", - "domain": "localhost", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/chatgpt-app/status.js", - "sourceFile": "plugins/chatgpt-app/status.js" - }, - { - "site": "chatwise", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 30, - "required": false, - "help": "Max seconds to wait (default: 30)" - } - ], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "plugins/chatwise/ask.js", - "sourceFile": "plugins/chatwise/ask.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "export", - "description": "Export the current ChatWise conversation to a Markdown file", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "output", - "type": "str", - "required": false, - "help": "Output file (default: /tmp/chatwise-export.md)" - } - ], - "columns": [ - "Status", - "File", - "Messages" - ], - "type": "js", - "modulePath": "plugins/chatwise/export.js", - "sourceFile": "plugins/chatwise/export.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "history", - "description": "List conversation history in ChatWise sidebar", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Title" - ], - "type": "js", - "modulePath": "plugins/chatwise/history.js", - "sourceFile": "plugins/chatwise/history.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "model", - "description": "Get or switch the active AI model in ChatWise", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "model-name", - "type": "str", - "required": false, - "positional": true, - "help": "Model to switch to (e.g. gpt-4, claude-3)" - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "plugins/chatwise/model.js", - "sourceFile": "plugins/chatwise/model.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "new", - "description": "Start a new ChatWise conversation session", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/chatwise/new.js", - "sourceFile": "plugins/chatwise/new.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "read", - "description": "Read the current ChatWise conversation history", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Content" - ], - "type": "js", - "modulePath": "plugins/chatwise/read.js", - "sourceFile": "plugins/chatwise/read.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "screenshot", - "description": "Capture a snapshot of the current ChatWise window (DOM + Accessibility tree)", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "output", - "type": "str", - "required": false, - "help": "Output file path (default: /tmp/chatwise-snapshot.txt)" - } - ], - "columns": [ - "Status", - "File" - ], - "type": "js", - "modulePath": "plugins/chatwise/screenshot.js", - "sourceFile": "plugins/chatwise/screenshot.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "send", - "description": "Send a message to the active ChatWise conversation", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Message to send" - } - ], - "columns": [ - "Status", - "InjectedText" - ], - "type": "js", - "modulePath": "plugins/chatwise/send.js", - "sourceFile": "plugins/chatwise/send.js", - "navigateBefore": true - }, - { - "site": "chatwise", - "name": "status", - "description": "Check active CDP connection to ChatWise Desktop", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" - ], - "type": "js", - "modulePath": "plugins/chatwise/status.js", - "sourceFile": "plugins/chatwise/status.js", - "navigateBefore": true - }, - { - "site": "chess", - "name": "analyze", - "description": "Open a Chess.com game in the browser analysis board", - "access": "read", - "domain": "www.chess.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "game-url", - "type": "string", - "required": true, - "positional": true, - "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" - } - ], - "columns": [ - "kind", - "game_id", - "analysis_url" - ], - "type": "js", - "modulePath": "plugins/chess/analyze.js", - "sourceFile": "plugins/chess/analyze.js", - "navigateBefore": false - }, - { - "site": "chess", - "name": "game", - "description": "Chess.com single-game detail (white, black, result, ECO, time control) by full game URL", - "access": "read", - "domain": "www.chess.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "game-url", - "type": "string", - "required": true, - "positional": true, - "help": "Full game URL, e.g. https://www.chess.com/game/live/168842570216" - } - ], - "columns": [ - "kind", - "game_id", - "date", - "white", - "white_rating", - "black", - "black_rating", - "result", - "winner_color", - "termination", - "eco", - "time_control", - "rated", - "ply_count", - "url" - ], - "type": "js", - "modulePath": "plugins/chess/game.js", - "sourceFile": "plugins/chess/game.js" - }, - { - "site": "chess", - "name": "games", - "description": "Chess.com recent games for a player, newest first", - "access": "read", - "domain": "api.chess.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Chess.com username" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of recent games (1-100)" - } - ], - "columns": [ - "date", - "time_class", - "rated", - "my_color", - "my_rating", - "my_result", - "opponent", - "opponent_rating", - "accuracy_white", - "accuracy_black", - "eco", - "opening_name", - "url" - ], - "type": "js", - "modulePath": "plugins/chess/games.js", - "sourceFile": "plugins/chess/games.js" - }, - { - "site": "chess", - "name": "stats", - "description": "Chess.com player ratings + win/loss record across game kinds", - "access": "read", - "domain": "api.chess.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Chess.com username (case-insensitive)" - } - ], - "columns": [ - "kind", - "rating_current", - "rating_best", - "wins", - "losses", - "draws" - ], - "type": "js", - "modulePath": "plugins/chess/stats.js", - "sourceFile": "plugins/chess/stats.js" - }, - { - "site": "cincinnati", - "name": "export-postgraduate-courses", - "description": "Export University of Cincinnati graduate and professional programs from official public sources.", - "access": "read", - "example": "webcmd cincinnati export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.grad.uc.edu", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" - } - ], - "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" - ], - "type": "js", - "modulePath": "plugins/cincinnati/export-postgraduate-courses.js", - "sourceFile": "plugins/cincinnati/export-postgraduate-courses.js" - }, - { - "site": "claude", - "name": "ask", - "description": "Send a prompt to Claude and get the response", - "access": "write", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait for response" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - }, - { - "name": "model", - "type": "str", - "default": "sonnet", - "required": false, - "help": "Model to use: sonnet, opus, or haiku", - "choices": [ - "sonnet", - "opus", - "haiku" - ] - }, - { - "name": "think", - "type": "boolean", - "default": false, - "required": false, - "help": "Enable Adaptive thinking" - }, - { - "name": "file", - "type": "str", - "required": false, - "help": "Attach a file (image, PDF, text) with the prompt", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "application/pdf", - "text/plain", - "text/markdown", - "text/csv", - "application/json", - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - } - ], - "columns": [ - "response" - ], - "type": "js", - "modulePath": "plugins/claude/ask.js", - "sourceFile": "plugins/claude/ask.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "detail", - "description": "Open a Claude conversation by ID and read its messages", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Conversation ID (UUID from /chat/)" - } - ], - "columns": [ - "Index", - "Role", - "Text" - ], - "type": "js", - "modulePath": "plugins/claude/detail.js", - "sourceFile": "plugins/claude/detail.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "history", - "description": "List conversation history from Claude /recents", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max conversations to show" - } - ], - "columns": [ - "Index", - "Id", - "Title", - "Url" - ], - "type": "js", - "modulePath": "plugins/claude/history.js", - "sourceFile": "plugins/claude/history.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "login", - "description": "Open claude login", - "access": "write", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "org_name", - "org_uuid", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/claude/auth.js", - "sourceFile": "plugins/claude/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "new", - "description": "Start a new conversation in Claude", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/claude/new.js", - "sourceFile": "plugins/claude/new.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "read", - "description": "Read the current Claude conversation", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Index", - "Role", - "Text" - ], - "type": "js", - "modulePath": "plugins/claude/read.js", - "sourceFile": "plugins/claude/read.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "send", - "description": "Send a prompt to Claude without waiting for the response", - "access": "write", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - } - ], - "columns": [ - "Status", - "SubmittedBy", - "InjectedText" - ], - "type": "js", - "modulePath": "plugins/claude/send.js", - "sourceFile": "plugins/claude/send.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "status", - "description": "Check Claude page availability and login state", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status", - "Login", - "Url" - ], - "type": "js", - "modulePath": "plugins/claude/status.js", - "sourceFile": "plugins/claude/status.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "claude", - "name": "whoami", - "description": "Show the current logged-in claude account", - "access": "read", - "domain": "claude.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "org_name", - "org_uuid" - ], - "type": "js", - "modulePath": "plugins/claude/auth.js", - "sourceFile": "plugins/claude/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "codex", - "name": "archive", - "description": "Archive (Codex's term for delete) the selected conversation via the Chat actions header menu. No confirmation in UI — pass --yes to actually archive.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "yes", - "type": "boolean", - "default": false, - "required": false, - "help": "Actually archive (default: dry-run preview)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "status", - "thread_id", - "project", - "conversation" - ], - "type": "js", - "modulePath": "plugins/codex/archive.js", - "sourceFile": "plugins/codex/archive.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "ask", - "description": "Send a prompt to the current or selected Codex conversation and wait for the AI response", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 60, - "required": false, - "help": "Max seconds to wait for response (default: 60)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "Role", - "Project", - "Conversation", - "Text" - ], - "type": "js", - "modulePath": "plugins/codex/ask.js", - "sourceFile": "plugins/codex/ask.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "dump", - "description": "Dump the DOM and Accessibility tree of codex for reverse-engineering", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "action", - "files" - ], - "type": "js", - "modulePath": "plugins/codex/dump.js", - "sourceFile": "plugins/codex/dump.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "export", - "description": "Export the current Codex conversation to a Markdown file", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "output", - "type": "str", - "required": false, - "help": "Output file (default: /tmp/codex-export.md)" - } - ], - "columns": [ - "Status", - "File", - "Messages" - ], - "type": "js", - "modulePath": "plugins/codex/export.js", - "sourceFile": "plugins/codex/export.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "extract-diff", - "description": "Extract visual code review diff patches from Codex", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "File", - "Diff" - ], - "type": "js", - "modulePath": "plugins/codex/extract-diff.js", - "sourceFile": "plugins/codex/extract-diff.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "history", - "description": "List visible Codex conversation threads grouped by project", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "help": "Filter by project label or path" - }, - { - "name": "limit", - "type": "str", - "required": false, - "help": "Max conversations per project" - } - ], - "columns": [ - "Project", - "Index", - "Title", - "Updated", - "Active" - ], - "type": "js", - "modulePath": "plugins/codex/history.js", - "sourceFile": "plugins/codex/history.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "model", - "description": "Read, list, or switch the active model / reasoning level in Codex Desktop. The composer toolbar button toggles a menu that mixes model variants (GPT-5.5, Speed) with reasoning levels (Low/Medium/High/Extra High).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": false, - "positional": true, - "help": "Substring (case-insensitive) of a model / reasoning level to switch to. Omit to read current." - }, - { - "name": "list", - "type": "boolean", - "default": false, - "required": false, - "help": "List all menu options (does not switch)" - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "plugins/codex/model.js", - "sourceFile": "plugins/codex/model.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "new", - "description": "Start a new Codex conversation session", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/codex/new.js", - "sourceFile": "plugins/codex/new.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "pin", - "description": "Pin the selected Codex conversation via the Chat actions header menu.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "status", - "thread_id", - "project", - "conversation" - ], - "type": "js", - "modulePath": "plugins/codex/pin.js", - "sourceFile": "plugins/codex/pin.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "projects", - "description": "List Codex projects and visible conversations from the sidebar", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "help": "Filter by project label or path" - }, - { - "name": "limit", - "type": "str", - "required": false, - "help": "Max conversations per project" - } - ], - "columns": [ - "Project", - "Index", - "Title", - "Updated", - "Active" - ], - "type": "js", - "modulePath": "plugins/codex/projects.js", - "sourceFile": "plugins/codex/projects.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "read", - "description": "Read the contents of the current or selected Codex conversation thread", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "Project", - "Conversation", - "Content" - ], - "type": "js", - "modulePath": "plugins/codex/read.js", - "sourceFile": "plugins/codex/read.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "rename", - "description": "Rename the selected Codex conversation. Opens the Chat actions menu → \"Rename chat\", then types the new title.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "title", - "type": "str", - "required": true, - "positional": true, - "help": "New title (single line, no newlines)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "status", - "title", - "thread_id", - "project" - ], - "type": "js", - "modulePath": "plugins/codex/rename.js", - "sourceFile": "plugins/codex/rename.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "screenshot", - "description": "Capture a snapshot of the current Codex window (DOM + Accessibility tree)", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "output", - "type": "str", - "required": false, - "help": "Output file path (default: /tmp/codex-snapshot.txt)" - } - ], - "columns": [ - "Status", - "File" - ], - "type": "js", - "modulePath": "plugins/codex/screenshot.js", - "sourceFile": "plugins/codex/screenshot.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "send", - "description": "Send text/commands to the current or selected Codex AI composer", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Text, command (e.g. /review), or skill (e.g. $imagegen)" - }, - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "Status", - "Project", - "Conversation", - "InjectedText" - ], - "type": "js", - "modulePath": "plugins/codex/send.js", - "sourceFile": "plugins/codex/send.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "status", - "description": "Check active CDP connection to OpenAI Codex App", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" - ], - "type": "js", - "modulePath": "plugins/codex/status.js", - "sourceFile": "plugins/codex/status.js", - "navigateBefore": true - }, - { - "site": "codex", - "name": "unpin", - "description": "Unpin the selected Codex conversation via the Chat actions header menu.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "help": "Project label or path to select before running the command" - }, - { - "name": "conversation", - "type": "str", - "required": false, - "help": "Conversation title to select within --project" - }, - { - "name": "index", - "type": "str", - "required": false, - "help": "1-based conversation index within --project" - }, - { - "name": "thread-id", - "type": "str", - "required": false, - "help": "Exact Codex thread id to select" - } - ], - "columns": [ - "status", - "thread_id", - "project", - "conversation" - ], - "type": "js", - "modulePath": "plugins/codex/pin.js", - "sourceFile": "plugins/codex/pin.js", - "navigateBefore": true - }, - { - "site": "coingecko", - "name": "categories", - "description": "Crypto categories ranked by aggregated market cap", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "sort", - "type": "str", - "default": "market_cap_desc", - "required": false, - "help": "Sort order (market_cap_desc / market_cap_asc / name_desc / name_asc / market_cap_change_24h_desc / market_cap_change_24h_asc)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of categories (1-100; CoinGecko returns ~120 max)" - } - ], - "columns": [ - "rank", - "id", - "name", - "marketCap", - "volume24h", - "marketCapChange24hPct", - "top3Coins" - ], - "type": "js", - "modulePath": "plugins/coingecko/categories.js", - "sourceFile": "plugins/coingecko/categories.js" - }, - { - "site": "coingecko", - "name": "coin", - "description": "Fetch a single cryptocurrency's market data by CoinGecko id (e.g. bitcoin, ethereum).", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "CoinGecko coin id (lowercase, e.g. bitcoin / ethereum / solana)." - }, - { - "name": "currency", - "type": "string", - "default": "usd", - "required": false, - "help": "Quote currency (usd, cny, eur, jpy, ...)." - } - ], - "columns": [ - "id", - "symbol", - "name", - "rank", - "price", - "marketCap", - "volume24h", - "change24hPct", - "change7dPct", - "change30dPct", - "ath", - "athDate", - "atl", - "atlDate", - "circulatingSupply", - "totalSupply", - "maxSupply", - "genesisDate", - "homepage" - ], - "type": "js", - "modulePath": "plugins/coingecko/coin.js", - "sourceFile": "plugins/coingecko/coin.js" - }, - { - "site": "coingecko", - "name": "derivatives", - "description": "Top crypto derivative (perpetual / futures) markets by 24h volume", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max rows to return (1-500; CoinGecko returns one large page)." - }, - { - "name": "symbol", - "type": "string", - "required": false, - "help": "Optional symbol substring filter (e.g. \"BTC\", \"ETHUSDT\")." - } - ], - "columns": [ - "rank", - "market", - "symbol", - "indexId", - "contractType", - "price", - "change24hPct", - "fundingRate", - "openInterestUsd", - "volume24hUsd", - "expired" - ], - "type": "js", - "modulePath": "plugins/coingecko/derivatives.js", - "sourceFile": "plugins/coingecko/derivatives.js" - }, - { - "site": "coingecko", - "name": "exchanges", - "description": "Top crypto exchanges by 24h BTC trading volume", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of exchanges (1-250, CoinGecko per_page upper bound)" - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number (1-based)" - } - ], - "columns": [ - "rank", - "id", - "name", - "trustScore", - "volume24hBtc", - "country", - "yearEstablished", - "url" - ], - "type": "js", - "modulePath": "plugins/coingecko/exchanges.js", - "sourceFile": "plugins/coingecko/exchanges.js" - }, - { - "site": "coingecko", - "name": "global", - "description": "Aggregate crypto market stats: total market cap, volume, dominance", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "currency", - "type": "string", - "default": "usd", - "required": false, - "help": "Quote currency for total market cap / volume (usd, cny, eur, jpy, ...)" - } - ], - "columns": [ - "currency", - "totalMarketCap", - "totalVolume24h", - "marketCapChange24hPct", - "btcDominancePct", - "ethDominancePct", - "activeCryptocurrencies", - "markets", - "ongoingIcos", - "updatedAt" - ], - "type": "js", - "modulePath": "plugins/coingecko/global.js", - "sourceFile": "plugins/coingecko/global.js" - }, - { - "site": "coingecko", - "name": "top", - "description": "Cryptocurrency quotes by market cap (default USD)", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "currency", - "type": "string", - "default": "usd", - "required": false, - "help": "quote currency (usd / cny / eur / jpy ...)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number to return (default 10, maximum 250)" - } - ], - "columns": [ - "rank", - "symbol", - "name", - "price", - "change24hPct", - "marketCap", - "volume24h", - "high24h", - "low24h" - ], - "type": "js", - "modulePath": "plugins/coingecko/top.js", - "sourceFile": "plugins/coingecko/top.js" - }, - { - "site": "coingecko", - "name": "trending", - "description": "Top trending cryptocurrencies on CoinGecko in the last 24h (search-volume based).", - "access": "read", - "domain": "api.coingecko.com", - "strategy": "public", - "browser": false, - "args": [], - "columns": [ - "rank", - "id", - "symbol", - "name", - "marketCapRank", - "priceBtc", - "thumb" - ], - "type": "js", - "modulePath": "plugins/coingecko/trending.js", - "sourceFile": "plugins/coingecko/trending.js" - }, - { - "site": "concordia", - "name": "export-postgraduate-courses", - "description": "Export Concordia University Montreal postgraduate programs using official public sources.", - "access": "read", - "example": "webcmd concordia export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.concordia.ca", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" - } - ], - "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" - ], - "type": "js", - "modulePath": "plugins/concordia/export-postgraduate-courses.js", - "sourceFile": "plugins/concordia/export-postgraduate-courses.js" - }, - { - "site": "confluence", - "name": "create", - "description": "Create a Confluence page from Markdown or storage XHTML", - "access": "write", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "space", - "type": "string", - "required": true, - "help": "Cloud space id, or Data Center space key" - }, - { - "name": "title", - "type": "string", - "required": true, - "help": "Page title" - }, - { - "name": "file", - "type": "string", - "required": true, - "help": "Markdown file path" - }, - { - "name": "parent", - "type": "string", - "required": false, - "help": "Optional parent page id" - }, - { - "name": "representation", - "type": "string", - "default": "markdown", - "required": false, - "help": "Input file format", - "choices": [ - "markdown", - "storage" - ] - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually create the remote page" - } - ], - "columns": [ - "status", - "id", - "title", - "spaceId", - "spaceKey", - "version", - "url" - ], - "type": "js", - "modulePath": "plugins/confluence/create.js", - "sourceFile": "plugins/confluence/create.js" - }, - { - "site": "confluence", - "name": "page", - "description": "Confluence page by id with storage and Markdown body", - "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Confluence page id" - } - ], - "columns": [ - "id", - "title", - "status", - "spaceId", - "spaceKey", - "version", - "url" - ], - "type": "js", - "modulePath": "plugins/confluence/page.js", - "sourceFile": "plugins/confluence/page.js" - }, - { - "site": "confluence", - "name": "search", - "description": "Search Confluence content with CQL", - "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "cql", - "type": "str", - "required": true, - "positional": true, - "help": "CQL query, e.g. \"type = page and title ~ \\\"RCA\\\"\"" - }, - { - "name": "space", - "type": "string", - "required": false, - "help": "Limit search to a Confluence space key" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results to return (1-100)" - } - ], - "columns": [ - "id", - "title", - "type", - "spaceKey", - "status", - "lastModified", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/confluence/search.js", - "sourceFile": "plugins/confluence/search.js" - }, - { - "site": "confluence", - "name": "update", - "description": "Update a Confluence page body from Markdown or storage XHTML", - "access": "write", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Confluence page id" - }, - { - "name": "file", - "type": "string", - "required": true, - "help": "Markdown file path" - }, - { - "name": "title", - "type": "string", - "required": false, - "help": "Optional replacement title; defaults to current title" - }, - { - "name": "version-message", - "type": "string", - "required": false, - "help": "Confluence version message" - }, - { - "name": "representation", - "type": "string", - "default": "markdown", - "required": false, - "help": "Input file format", - "choices": [ - "markdown", - "storage" - ] - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually update the remote page" - } - ], - "columns": [ - "status", - "id", - "title", - "spaceId", - "spaceKey", - "version", - "url" - ], - "type": "js", - "modulePath": "plugins/confluence/update.js", - "sourceFile": "plugins/confluence/update.js" - }, - { - "site": "coupang", - "name": "add-to-cart", - "description": "Add a Coupang product to cart using logged-in browser session", - "access": "write", - "domain": "www.coupang.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "product-id", - "type": "str", - "required": false, - "positional": true, - "help": "Coupang product ID" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Canonical product URL" - } - ], - "columns": [ - "ok", - "product_id", - "url", - "message" - ], - "type": "js", - "modulePath": "plugins/coupang/add-to-cart.js", - "sourceFile": "plugins/coupang/add-to-cart.js", - "navigateBefore": "https://www.coupang.com" - }, - { - "site": "coupang", - "name": "login", - "description": "Open coupang login", - "access": "write", - "domain": "coupang.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/coupang/auth.js", - "sourceFile": "plugins/coupang/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "coupang", - "name": "product", - "description": "Read full product detail (price, rating, seller, delivery) for a Coupang product", - "access": "read", - "domain": "www.coupang.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "product-id", - "type": "str", - "required": false, - "positional": true, - "help": "Coupang product ID (digits only)" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Canonical Coupang product URL (alternative to --product-id)" - } - ], - "columns": [ - "product_id", - "title", - "price", - "original_price", - "discount_rate", - "rating", - "review_count", - "seller", - "brand", - "rocket", - "delivery_promise", - "image_url", - "url" - ], - "type": "js", - "modulePath": "plugins/coupang/product.js", - "sourceFile": "plugins/coupang/product.js", - "navigateBefore": "https://www.coupang.com" - }, - { - "site": "coupang", - "name": "search", - "description": "Search Coupang products with logged-in browser session", - "access": "read", - "domain": "www.coupang.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Search result page number" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (max 50)" - }, - { - "name": "filter", - "type": "str", - "required": false, - "help": "Optional search filter (currently supports: rocket)" - } - ], - "columns": [ - "rank", - "product_id", - "title", - "price", - "unit_price", - "rating", - "review_count", - "rocket", - "delivery_type", - "delivery_promise", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/coupang/search.js", - "sourceFile": "plugins/coupang/search.js", - "navigateBefore": "https://www.coupang.com" - }, - { - "site": "coupang", - "name": "whoami", - "description": "Show the current logged-in coupang account", - "access": "read", - "domain": "coupang.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name" - ], - "type": "js", - "modulePath": "plugins/coupang/auth.js", - "sourceFile": "plugins/coupang/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "crates", - "name": "crate", - "description": "Single crates.io crate metadata (latest version, downloads, license, repo)", - "access": "read", - "domain": "crates.io", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "crates.io crate name (e.g. \"serde\", \"tokio\")" - } - ], - "columns": [ - "name", - "latestVersion", - "description", - "downloads", - "recentDownloads", - "versions", - "license", - "homepage", - "documentation", - "repository", - "keywords", - "categories", - "created", - "updated", - "url" - ], - "type": "js", - "modulePath": "plugins/crates/crate.js", - "sourceFile": "plugins/crates/crate.js" - }, - { - "site": "crates", - "name": "search", - "description": "Search the public crates.io registry by keyword", - "access": "read", - "domain": "crates.io", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"serde\", \"async runtime\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - } - ], - "columns": [ - "rank", - "name", - "latestVersion", - "description", - "downloads", - "recentDownloads", - "repository", - "updated", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/crates/search.js", - "sourceFile": "plugins/crates/search.js" - }, - { - "site": "cursor", - "name": "ask", - "description": "Send a prompt and wait for the AI response (send + wait + read)", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 30, - "required": false, - "help": "Max seconds to wait for response (default: 30)" - } - ], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "plugins/cursor/ask.js", - "sourceFile": "plugins/cursor/ask.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "composer", - "description": "Send a prompt directly into Cursor Composer (Cmd+I shortcut)", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Text to send into Composer" - } - ], - "columns": [ - "Status", - "InjectedText" - ], - "type": "js", - "modulePath": "plugins/cursor/composer.js", - "sourceFile": "plugins/cursor/composer.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "dump", - "description": "Dump the DOM and Accessibility tree of cursor for reverse-engineering", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "action", - "files" - ], - "type": "js", - "modulePath": "plugins/cursor/dump.js", - "sourceFile": "plugins/cursor/dump.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "export", - "description": "Export the current cursor conversation to a Markdown file", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "output", - "type": "str", - "required": false, - "help": "Output file (default: /tmp/cursor-export.md)" - } - ], - "columns": [ - "Status", - "File", - "Messages" - ], - "type": "js", - "modulePath": "plugins/cursor/export.js", - "sourceFile": "plugins/cursor/export.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "extract-code", - "description": "Extract multi-line code blocks from the current Cursor conversation", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Code" - ], - "type": "js", - "modulePath": "plugins/cursor/extract-code.js", - "sourceFile": "plugins/cursor/extract-code.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "history", - "description": "List recent chat sessions from the Cursor sidebar", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Title" - ], - "type": "js", - "modulePath": "plugins/cursor/history.js", - "sourceFile": "plugins/cursor/history.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "model", - "description": "Get or switch the currently active AI model in Cursor", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "model-name", - "type": "str", - "required": false, - "positional": true, - "help": "The ID of the model to switch to (e.g. claude-3.5-sonnet)" - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "plugins/cursor/model.js", - "sourceFile": "plugins/cursor/model.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "new", - "description": "Start a new Cursor chat or Composer session", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/cursor/new.js", - "sourceFile": "plugins/cursor/new.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "read", - "description": "Read the current Cursor chat/composer conversation history", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "plugins/cursor/read.js", - "sourceFile": "plugins/cursor/read.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "screenshot", - "description": "Capture a snapshot of the current cursor window (DOM + Accessibility tree)", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "output", - "type": "str", - "required": false, - "help": "Output file path (default: /tmp/cursor-snapshot.txt)" - } - ], - "columns": [ - "Status", - "File" - ], - "type": "js", - "modulePath": "plugins/cursor/screenshot.js", - "sourceFile": "plugins/cursor/screenshot.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "send", - "description": "Send a prompt directly into Cursor Composer/Chat", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Text to send into Cursor" - } - ], - "columns": [ - "Status", - "InjectedText" - ], - "type": "js", - "modulePath": "plugins/cursor/send.js", - "sourceFile": "plugins/cursor/send.js", - "navigateBefore": true - }, - { - "site": "cursor", - "name": "status", - "description": "Check active CDP connection to Cursor AI Editor", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" - ], - "type": "js", - "modulePath": "plugins/cursor/status.js", - "sourceFile": "plugins/cursor/status.js", - "navigateBefore": true - }, - { - "site": "dblp", - "name": "author", - "description": "List dblp publications by a given author (newest first; resolves to top PID match)", - "access": "read", - "domain": "dblp.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "author", - "type": "str", - "required": false, - "positional": true, - "help": "Author name (e.g. \"Yoshua Bengio\"). Optional when --pid is given." - }, - { - "name": "pid", - "type": "str", - "required": false, - "help": "Canonical dblp PID (e.g. \"56/953\"). Bypasses author search." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max publications (1-200)" - } - ], - "columns": [ - "rank", - "key", - "title", - "authors", - "venue", - "year", - "type", - "doi", - "pid", - "url" - ], - "type": "js", - "modulePath": "plugins/dblp/author.js", - "sourceFile": "plugins/dblp/author.js" - }, - { - "site": "dblp", - "name": "paper", - "aliases": [ - "detail", - "view" - ], - "description": "Fetch a dblp record by canonical key (e.g. conf/nips/VaswaniSPUJGKP17)", - "access": "read", - "domain": "dblp.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "dblp record key (round-tripped from the `key` column of `dblp search`)" - } - ], - "columns": [ - "key", - "type", - "title", - "authors", - "venue", - "year", - "pages", - "doi", - "open_access_url", - "dblp_url" - ], - "type": "js", - "modulePath": "plugins/dblp/paper.js", - "sourceFile": "plugins/dblp/paper.js" - }, - { - "site": "dblp", - "name": "search", - "description": "Search dblp computer-science bibliography by free-text query", - "access": "read", - "domain": "dblp.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (title / author / venue, e.g. \"attention is all you need\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100, single dblp page)" - } - ], - "columns": [ - "rank", - "key", - "title", - "authors", - "venue", - "year", - "type", - "doi", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/dblp/search.js", - "sourceFile": "plugins/dblp/search.js" - }, - { - "site": "dblp", - "name": "venue", - "description": "Search dblp venue registry (conferences / journals) by name or acronym", - "access": "read", - "domain": "dblp.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Venue name or acronym (e.g. \"ICLR\", \"neural networks\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max venues (1-100, single dblp page)" - } - ], - "columns": [ - "rank", - "acronym", - "venue", - "type", - "url" - ], - "type": "js", - "modulePath": "plugins/dblp/venue.js", - "sourceFile": "plugins/dblp/venue.js" - }, - { - "site": "defillama", - "name": "protocol", - "description": "Single DefiLlama protocol details (current TVL, mcap, chains, twitter, github, description)", - "access": "read", - "domain": "defillama.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "slug", - "type": "string", - "required": true, - "positional": true, - "help": "DefiLlama protocol slug (e.g. \"aave\", \"lido\")" - } - ], - "columns": [ - "slug", - "name", - "category", - "isParent", - "tvl", - "tvlAt", - "mcap", - "chains", - "twitter", - "github", - "audits", - "listedAt", - "description", - "website", - "url" - ], - "type": "js", - "modulePath": "plugins/defillama/protocol.js", - "sourceFile": "plugins/defillama/protocol.js" - }, - { - "site": "defillama", - "name": "protocols", - "description": "Top DeFi protocols on DefiLlama by current TVL (slug, name, category, TVL, mcap, change_1d/7d, chains)", - "access": "read", - "domain": "defillama.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Number of rows to return (1-500)" - } - ], - "columns": [ - "rank", - "slug", - "name", - "category", - "tvl", - "mcap", - "change_1d", - "change_7d", - "chains", - "listedAt", - "url" - ], - "type": "js", - "modulePath": "plugins/defillama/protocols.js", - "sourceFile": "plugins/defillama/protocols.js" - }, - { - "site": "devto", - "name": "latest", - "description": "Newest dev.to articles (firehose, all tags)", - "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Articles per page (1-100)" - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number (1-based)" - } - ], - "columns": [ - "rank", - "id", - "title", - "author", - "tags", - "reactions", - "comments", - "published", - "url" - ], - "type": "js", - "modulePath": "plugins/devto/latest.js", - "sourceFile": "plugins/devto/latest.js" - }, - { - "site": "devto", - "name": "read", - "description": "Read a DEV.to article body by id", - "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "DEV.to article id (numeric, e.g. 3605688)" - }, - { - "name": "max-length", - "type": "int", - "default": 20000, - "required": false, - "help": "Max characters of body to return (min 100)" - } - ], - "columns": [ - "id", - "title", - "author", - "reactions", - "reading_time", - "tags", - "published_at", - "body", - "url" - ], - "type": "js", - "modulePath": "plugins/devto/read.js", - "sourceFile": "plugins/devto/read.js" - }, - { - "site": "devto", - "name": "tag", - "description": "Latest DEV.to articles for a specific tag", - "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "tag", - "type": "str", - "required": true, - "positional": true, - "help": "Tag name (e.g. javascript, python, webdev)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of articles" - } - ], - "columns": [ - "rank", - "id", - "title", - "author", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", - "url" - ], - "type": "js", - "modulePath": "plugins/devto/tag.js", - "sourceFile": "plugins/devto/tag.js" - }, - { - "site": "devto", - "name": "top", - "description": "Top DEV.to articles of the day", - "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of articles" - } - ], - "columns": [ - "rank", - "id", - "title", - "author", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", - "url" - ], - "type": "js", - "modulePath": "plugins/devto/top.js", - "sourceFile": "plugins/devto/top.js" - }, - { - "site": "devto", - "name": "user", - "description": "Recent DEV.to articles from a specific user", - "access": "read", - "domain": "dev.to", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "DEV.to username (e.g. ben, thepracticaldev)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of articles" - } - ], - "columns": [ - "rank", - "id", - "title", - "reactions", - "comments", - "reading_time", - "published_at", - "tags", - "url" - ], - "type": "js", - "modulePath": "plugins/devto/user.js", - "sourceFile": "plugins/devto/user.js" - }, - { - "site": "dictionary", - "name": "examples", - "description": "Read real-world example sentences utilizing the word", - "access": "read", - "domain": "api.dictionaryapi.dev", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "word", - "type": "string", - "required": true, - "positional": true, - "help": "Word to get example sentences for" - } - ], - "columns": [ - "word", - "example" - ], - "type": "js", - "modulePath": "plugins/dictionary/examples.js", - "sourceFile": "plugins/dictionary/examples.js" - }, - { - "site": "dictionary", - "name": "search", - "description": "Search the Free Dictionary API for definitions, parts of speech, and pronunciations.", - "access": "read", - "domain": "api.dictionaryapi.dev", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "word", - "type": "string", - "required": true, - "positional": true, - "help": "Word to define (e.g., serendipity)" - } - ], - "columns": [ - "word", - "phonetic", - "type", - "definition" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/dictionary/search.js", - "sourceFile": "plugins/dictionary/search.js" - }, - { - "site": "dictionary", - "name": "synonyms", - "description": "Find synonyms for a specific word", - "access": "read", - "domain": "api.dictionaryapi.dev", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "word", - "type": "string", - "required": true, - "positional": true, - "help": "Word to find synonyms for (e.g., serendipity)" - } - ], - "columns": [ - "word", - "synonyms" - ], - "type": "js", - "modulePath": "plugins/dictionary/synonyms.js", - "sourceFile": "plugins/dictionary/synonyms.js" - }, - { - "site": "discord-app", - "name": "channels", - "description": "List channels in the current Discord server", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Channel", - "Type", - "guild_id", - "channel_id", - "url" - ], - "type": "js", - "modulePath": "plugins/discord-app/channels.js", - "sourceFile": "plugins/discord-app/channels.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "delete", - "description": "Delete a message by its ID in the active Discord channel", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "message_id", - "type": "string", - "required": true, - "positional": true, - "help": "The ID of the message to delete (visible via Developer Mode or the read command)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/discord-app/delete.js", - "sourceFile": "plugins/discord-app/delete.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "goto", - "description": "Open a Discord channel by id/name/url without sending messages", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "guild", - "type": "str", - "required": false, - "help": "Guild/server id or visible name" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Channel id or visible name" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Discord channel URL" - }, - { - "name": "timeout", - "type": "str", - "default": "8", - "required": false, - "help": "Seconds to wait for Discord to show the route (default: 8)" - } - ], - "columns": [ - "Status", - "guild_id", - "channel_id", - "url" - ], - "type": "js", - "modulePath": "plugins/discord-app/goto.js", - "sourceFile": "plugins/discord-app/goto.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "members", - "description": "List online members in the current Discord channel", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Name", - "Status" - ], - "type": "js", - "modulePath": "plugins/discord-app/members.js", - "sourceFile": "plugins/discord-app/members.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "read", - "description": "Read recent messages from the active or targeted Discord channel", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "count", - "type": "str", - "default": "20", - "required": false, - "help": "Number of messages to read (default: 20)" - }, - { - "name": "guild", - "type": "str", - "required": false, - "help": "Guild/server id or visible name for targeted reads" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Channel id or visible name for targeted reads" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Discord channel URL to open before reading" - } - ], - "columns": [ - "Author", - "Time", - "Message", - "channel_id", - "message_id" - ], - "type": "js", - "modulePath": "plugins/discord-app/read.js", - "sourceFile": "plugins/discord-app/read.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "search", - "description": "Search messages in the current Discord server/channel (Cmd+F)", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - } - ], - "columns": [ - "Index", - "Author", - "Message" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/discord-app/search.js", - "sourceFile": "plugins/discord-app/search.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "send", - "description": "Send a message in the active Discord channel", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Message to send" - } - ], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/discord-app/send.js", - "sourceFile": "plugins/discord-app/send.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "servers", - "description": "List all Discord servers (guilds) in the sidebar", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Server", - "guild_id", - "url" - ], - "type": "js", - "modulePath": "plugins/discord-app/servers.js", - "sourceFile": "plugins/discord-app/servers.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "status", - "description": "Check active CDP connection to Discord Desktop", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" - ], - "type": "js", - "modulePath": "plugins/discord-app/status.js", - "sourceFile": "plugins/discord-app/status.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "thread-read", - "description": "Read recent messages from a Discord thread/post by id or URL", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "thread", - "type": "str", - "required": false, - "help": "Thread/post id, or a full Discord thread/post URL" - }, - { - "name": "count", - "type": "str", - "default": "20", - "required": false, - "help": "Number of messages to read (default: 20)" - }, - { - "name": "guild", - "type": "str", - "required": false, - "help": "Parent guild/server id or visible name" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Parent forum/channel id or visible name" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Discord thread/post URL" - } - ], - "columns": [ - "Author", - "Time", - "Message", - "channel_id", - "message_id" - ], - "type": "js", - "modulePath": "plugins/discord-app/thread-read.js", - "sourceFile": "plugins/discord-app/thread-read.js", - "navigateBefore": true - }, - { - "site": "discord-app", - "name": "threads", - "description": "List visible Discord forum/thread posts in the active or targeted channel", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "limit", - "type": "str", - "default": "30", - "required": false, - "help": "Maximum thread/post cards to return (default: 30)" - }, - { - "name": "guild", - "type": "str", - "required": false, - "help": "Guild/server id or visible name for targeted thread listing" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Forum/channel id or visible name for targeted thread listing" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Discord forum/channel URL to open before listing threads" - } - ], - "columns": [ - "Index", - "Thread", - "Author", - "Updated", - "Preview", - "guild_id", - "channel_id", - "thread_id", - "url" - ], - "type": "js", - "modulePath": "plugins/discord-app/threads.js", - "sourceFile": "plugins/discord-app/threads.js", - "navigateBefore": true - }, - { - "site": "district", - "name": "checkout", - "description": "Select District movie seats and open the UPI QR payment scanner", - "access": "write", - "domain": "www.district.in", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "show", - "type": "str", - "required": true, - "positional": true, - "help": "District seat-layout URL or showId from district showtimes" - }, - { - "name": "seats", - "type": "str", - "required": true, - "help": "Comma-separated seat labels to select, e.g. I22,I21" - }, - { - "name": "format-id", - "type": "str", - "required": false, - "help": "District formatId from showtimes; required when show is a showId" - }, - { - "name": "content-id", - "type": "str", - "required": false, - "help": "District content id; required when show is a showId" - }, - { - "name": "timeout", - "type": "int", - "default": 45, - "required": false, - "help": "Maximum seconds to wait for selection, review page, and payment handoff" - }, - { - "name": "payment", - "type": "str", - "default": "upi-qr", - "required": false, - "help": "Payment handoff target: upi-qr opens the scanner; review stops on order review" - } - ], - "columns": [ - "status", - "movie", - "cinema", - "date", - "time", - "seats", - "ticketCount", - "orderAmount", - "bookingCharge", - "total", - "paymentMethod", - "paymentState", - "upiQrVisible", - "paymentAmount", - "paymentUrl", - "showId" - ], - "type": "js", - "modulePath": "plugins/district/checkout.js", - "sourceFile": "plugins/district/checkout.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true - }, - { - "site": "district", - "name": "listings", - "aliases": [ - "ls" - ], - "description": "List public District by Zomato movies, events, and nearby going-out cards", - "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "input", - "type": "str", - "default": "home", - "required": false, - "positional": true, - "help": "home, movies, events, a district.in URL, or a District path" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum rows to return (1-100)" - } - ], - "columns": [ - "rank", - "title", - "category", - "date", - "venue", - "price", - "url" - ], - "type": "js", - "modulePath": "plugins/district/listings.js", - "sourceFile": "plugins/district/listings.js" - }, - { - "site": "district", - "name": "locations", - "aliases": [ - "location-search" - ], - "description": "Search District-supported cities, areas, malls, and places for booking filters", - "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "City, area, mall, or locality, for example \"bangalore\" or \"indiranagar\"" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum location rows to return (1-50)" - } - ], - "columns": [ - "rank", - "name", - "kind", - "city", - "state", - "cityKey", - "cityId", - "placeId", - "lat", - "lng", - "distanceKm", - "source" - ], - "type": "js", - "modulePath": "plugins/district/locations.js", - "sourceFile": "plugins/district/locations.js" - }, - { - "site": "district", - "name": "login", - "description": "Open district login", - "access": "write", - "domain": "www.district.in", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "phone_number", - "email", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/district/auth.js", - "sourceFile": "plugins/district/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "district", - "name": "search", - "aliases": [ - "s" - ], - "description": "Search District by Zomato across movies, events, dining, stores, activities, and play", - "access": "read", - "domain": "www.district.in", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query, for example \"hamlet\" or \"arijit\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum rows to return (1-100)" - }, - { - "name": "tab", - "type": "str", - "default": "all", - "required": false, - "help": "Search tab: all, dining, events, movies, stores, activities, or play" - } - ], - "columns": [ - "rank", - "title", - "category", - "date", - "venue", - "price", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/district/search.js", - "sourceFile": "plugins/district/search.js" - }, - { - "site": "district", - "name": "seats", - "description": "List available seats for a District movie showtime", - "access": "read", - "domain": "www.district.in", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "show", - "type": "str", - "required": true, - "positional": true, - "help": "District seat-layout URL or showId from district showtimes" - }, - { - "name": "format-id", - "type": "str", - "required": false, - "help": "District formatId from showtimes; required when show is a showId" - }, - { - "name": "content-id", - "type": "str", - "required": false, - "help": "District content id; required when show is a showId" - }, - { - "name": "class", - "type": "str", - "required": false, - "help": "Optional seat class filter, e.g. premium, premium xl, or recliner" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Number of seats to choose (1-10); without count, seats are listed normally" - }, - { - "name": "together", - "type": "str", - "required": false, - "help": "Require selected seats to be adjacent when count is provided" - }, - { - "name": "max-price", - "type": "float", - "required": false, - "help": "Maximum price per seat" - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Maximum seats to return (1-300)" - }, - { - "name": "timeout", - "type": "int", - "default": 30, - "required": false, - "help": "Maximum seconds to wait for the seat map to render" - } - ], - "columns": [ - "rank", - "seat", - "row", - "number", - "column", - "seatClass", - "price", - "status", - "flags", - "showId", - "formatId", - "url" - ], - "type": "js", - "modulePath": "plugins/district/seats.js", - "sourceFile": "plugins/district/seats.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "district", - "name": "set-location", - "aliases": [ - "setlocation" - ], - "description": "Set the District browser session location for movie booking filters", - "access": "write", - "domain": "www.district.in", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "location", - "type": "str", - "required": true, - "positional": true, - "help": "City, area, mall, or locality, for example \"Bangalore\" or \"Indiranagar\"" - }, - { - "name": "rank", - "type": "int", - "default": 1, - "required": false, - "help": "Pick the Nth District location result (1-20), default: 1" - }, - { - "name": "timeout", - "type": "int", - "default": 45, - "required": false, - "help": "Maximum seconds to wait for the picker and location change" - } - ], - "columns": [ - "status", - "name", - "city", - "state", - "cityKey", - "cityId", - "placeId", - "subzoneId", - "lat", - "lng", - "availableTabs", - "source" - ], - "type": "js", - "modulePath": "plugins/district/set-location.js", - "sourceFile": "plugins/district/set-location.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "district", - "name": "showtimes", - "aliases": [ - "shows" - ], - "description": "List District movie showtimes with location, time, cinema, language, price, and format filters", - "access": "read", - "domain": "www.district.in", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "movie", - "type": "str", - "required": true, - "positional": true, - "help": "Movie name or District movie URL" - }, - { - "name": "date", - "type": "str", - "required": false, - "help": "Show date in YYYY-MM-DD format; defaults to District selected date" - }, - { - "name": "city", - "type": "str", - "required": false, - "help": "District city name/key, for example Bangalore or Bengaluru" - }, - { - "name": "near", - "type": "str", - "required": false, - "help": "Area, mall, or locality to search near, for example Indiranagar" - }, - { - "name": "city-key", - "type": "str", - "required": false, - "help": "Legacy District city key override, for example bengaluru" - }, - { - "name": "after", - "type": "str", - "required": false, - "help": "Only shows at or after HH:MM, 24-hour time" - }, - { - "name": "before", - "type": "str", - "required": false, - "help": "Only shows at or before HH:MM, 24-hour time" - }, - { - "name": "cinema", - "type": "str", - "required": false, - "help": "Filter cinema/theatre name, for example PVR, INOX, Orion" - }, - { - "name": "language", - "type": "str", - "required": false, - "help": "Filter movie language, for example English, Hindi, Kannada" - }, - { - "name": "max-price", - "type": "float", - "required": false, - "help": "Only shows with at least one ticket class at or below this price" - }, - { - "name": "quality", - "type": "str", - "required": false, - "help": "Generic format/quality filter, for example 2D, 3D, IMAX, IMAX 3D, 4DX" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Maximum showtime rows to return (1-200)" - } - ], - "columns": [ - "rank", - "movie", - "language", - "date", - "time", - "cinema", - "format", - "priceRange", - "available", - "showId", - "formatId", - "url" - ], - "type": "js", - "modulePath": "plugins/district/showtimes.js", - "sourceFile": "plugins/district/showtimes.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "district", - "name": "whoami", - "description": "Show the current logged-in district account", - "access": "read", - "domain": "www.district.in", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name", - "phone_number", - "email" - ], - "type": "js", - "modulePath": "plugins/district/auth.js", - "sourceFile": "plugins/district/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "dockerhub", - "name": "image", - "description": "Fetch a Docker Hub repository's public metadata (stars, pulls, last updated, status)", - "access": "read", - "domain": "hub.docker.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "image", - "type": "str", - "required": true, - "positional": true, - "help": "Image name (e.g. \"nginx\", \"library/nginx\", \"bitnami/redis\")" - } - ], - "columns": [ - "image", - "official", - "stars", - "pulls", - "description", - "lastUpdated", - "lastModified", - "registered", - "status", - "url" - ], - "type": "js", - "modulePath": "plugins/dockerhub/image.js", - "sourceFile": "plugins/dockerhub/image.js" - }, - { - "site": "dockerhub", - "name": "search", - "description": "Search Docker Hub repositories by keyword", - "access": "read", - "domain": "hub.docker.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"nginx\", \"bitnami redis\")" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max repositories (1-100, single Docker Hub page)" - } - ], - "columns": [ - "rank", - "image", - "official", - "stars", - "pulls", - "description", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/dockerhub/search.js", - "sourceFile": "plugins/dockerhub/search.js" - }, - { - "site": "duckduckgo", - "name": "search", - "description": "Search DuckDuckGo", - "access": "read", - "domain": "html.duckduckgo.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results per page (1-10). For multi-page, use --offset" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Result offset for pagination (0, 10, 20...). Uses XHR POST internally" - }, - { - "name": "region", - "type": "str", - "required": false, - "help": "Region code (e.g. jp-jp, us-en, cn-zh). Default: all regions" - }, - { - "name": "time", - "type": "str", - "required": false, - "help": "Time range: d (day), w (week), m (month), y (year)" - } - ], - "columns": [ - "rank", - "title", - "url", - "snippet", - "displayUrl", - "icon", - "resultType" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/duckduckgo/search.js", - "sourceFile": "plugins/duckduckgo/search.js" - }, - { - "site": "duckduckgo", - "name": "suggest", - "description": "DuckDuckGo search suggestions", - "access": "read", - "domain": "duckduckgo.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search query prefix" - }, - { - "name": "limit", - "type": "int", - "default": 8, - "required": false, - "help": "Max number of suggestions" - } - ], - "columns": [ - "phrase" - ], - "type": "js", - "modulePath": "plugins/duckduckgo/suggest.js", - "sourceFile": "plugins/duckduckgo/suggest.js" - }, - { - "site": "endoflife", - "name": "product", - "description": "Release cycles + EOL / LTS / support dates for one product on endoflife.date", - "access": "read", - "domain": "endoflife.date", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "product", - "type": "string", - "required": true, - "positional": true, - "help": "endoflife.date product slug (e.g. \"nodejs\", \"python\", \"ubuntu\")" - } - ], - "columns": [ - "product", - "cycle", - "releaseDate", - "latest", - "latestReleaseDate", - "lts", - "support", - "eol", - "extendedSupport", - "eolStatus", - "url" - ], - "type": "js", - "modulePath": "plugins/endoflife/product.js", - "sourceFile": "plugins/endoflife/product.js" - }, - { - "site": "facebook", - "name": "add-friend", - "description": "Send a friend request on Facebook", - "access": "write", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Facebook username or profile URL" - } - ], - "columns": [ - "status", - "username" - ], - "type": "js", - "modulePath": "plugins/facebook/add-friend.js", - "sourceFile": "plugins/facebook/add-friend.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "events", - "description": "Browse Facebook event categories", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Number of categories" - } - ], - "columns": [ - "index", - "name" - ], - "type": "js", - "modulePath": "plugins/facebook/events.js", - "sourceFile": "plugins/facebook/events.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "feed", - "description": "Get your Facebook news feed", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of posts" - } - ], - "columns": [ - "index", - "author", - "content", - "likes", - "comments", - "shares" - ], - "type": "js", - "modulePath": "plugins/facebook/feed.js", - "sourceFile": "plugins/facebook/feed.js", - "navigateBefore": false - }, - { - "site": "facebook", - "name": "friends", - "description": "Get Facebook friend suggestions", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of friend suggestions" - } - ], - "columns": [ - "index", - "name", - "mutual" - ], - "type": "js", - "modulePath": "plugins/facebook/friends.js", - "sourceFile": "plugins/facebook/friends.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "groups", - "description": "List your Facebook groups", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of groups" - } - ], - "columns": [ - "index", - "name", - "last_post", - "url" - ], - "type": "js", - "modulePath": "plugins/facebook/groups.js", - "sourceFile": "plugins/facebook/groups.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "join-group", - "description": "Join a Facebook group", - "access": "write", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "group", - "type": "str", - "required": true, - "positional": true, - "help": "Group ID or URL path (e.g. '1876150192925481' or group name)" - } - ], - "columns": [ - "status", - "group" - ], - "type": "js", - "modulePath": "plugins/facebook/join-group.js", - "sourceFile": "plugins/facebook/join-group.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "login", - "description": "Open facebook login", - "access": "write", - "domain": "facebook.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "vanity", - "profile_url", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/facebook/auth.js", - "sourceFile": "plugins/facebook/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "facebook", - "name": "marketplace-inbox", - "description": "List recent Facebook Marketplace buyer/seller conversations", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of conversations to return" - } - ], - "columns": [ - "index", - "buyer", - "listing", - "snippet", - "time", - "unread" - ], - "type": "js", - "modulePath": "plugins/facebook/marketplace-inbox.js", - "sourceFile": "plugins/facebook/marketplace-inbox.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "marketplace-listings", - "description": "List your Facebook Marketplace seller listings", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of listings to return" - } - ], - "columns": [ - "index", - "title", - "price", - "status", - "listed", - "clicks", - "actions" - ], - "type": "js", - "modulePath": "plugins/facebook/marketplace-listings.js", - "sourceFile": "plugins/facebook/marketplace-listings.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "memories", - "description": "Get your Facebook memories (On This Day)", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of memories" - } - ], - "columns": [ - "index", - "source", - "content", - "time" - ], - "type": "js", - "modulePath": "plugins/facebook/memories.js", - "sourceFile": "plugins/facebook/memories.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "notifications", - "description": "Get recent Facebook notifications (includes unread / time / url / notif_id / notif_type columns)", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Number of notifications (1-100)" - } - ], - "columns": [ - "index", - "unread", - "text", - "time", - "url", - "notif_id", - "notif_type" - ], - "type": "js", - "modulePath": "plugins/facebook/notifications.js", - "sourceFile": "plugins/facebook/notifications.js", - "navigateBefore": false - }, - { - "site": "facebook", - "name": "profile", - "description": "Get Facebook user/page profile info", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Facebook username or page name" - } - ], - "columns": [ - "name", - "username", - "friends", - "followers", - "url" - ], - "type": "js", - "modulePath": "plugins/facebook/profile.js", - "sourceFile": "plugins/facebook/profile.js", - "navigateBefore": "https://www.facebook.com" - }, - { - "site": "facebook", - "name": "search", - "description": "Search Facebook for people, pages, or posts", - "access": "read", - "domain": "www.facebook.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "index", - "title", - "text", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/facebook/search.js", - "sourceFile": "plugins/facebook/search.js", - "navigateBefore": false - }, - { - "site": "facebook", - "name": "whoami", - "description": "Show the current logged-in facebook account", - "access": "read", - "domain": "facebook.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "vanity", - "profile_url" - ], - "type": "js", - "modulePath": "plugins/facebook/auth.js", - "sourceFile": "plugins/facebook/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "flathub", - "name": "app", - "description": "Full Flathub appstream metadata for an app id (license, categories, latest release)", - "access": "read", - "domain": "flathub.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "appId", - "type": "str", - "required": true, - "positional": true, - "help": "AppStream id (e.g. \"org.mozilla.firefox\", \"org.gnome.Calculator\")" - } - ], - "columns": [ - "appId", - "name", - "summary", - "developer", - "license", - "isFreeLicense", - "isEol", - "categories", - "keywords", - "latestVersion", - "latestReleaseDate", - "homepage", - "bugtracker", - "donation", - "url" - ], - "type": "js", - "modulePath": "plugins/flathub/app.js", - "sourceFile": "plugins/flathub/app.js" - }, - { - "site": "flathub", - "name": "search", - "description": "Search Flathub apps by keyword", - "access": "read", - "domain": "flathub.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max apps (1-100)" - } - ], - "columns": [ - "rank", - "appId", - "name", - "summary", - "developer", - "license", - "isFreeLicense", - "mainCategories", - "installsLastMonth", - "updatedAt", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/flathub/search.js", - "sourceFile": "plugins/flathub/search.js" - }, - { - "site": "gemini", - "name": "ask", - "description": "Send a prompt to Gemini and return only the assistant response", - "access": "write", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "model", - "type": "string", - "required": false, - "help": "Gemini model to use (e.g. \"2.5-flash\"). Use \"webcmd gemini models\" to list available values." - }, - { - "name": "timeout", - "type": "int", - "default": 60, - "required": false, - "help": "Max seconds to wait (default: 60)" - }, - { - "name": "new", - "type": "str", - "default": "false", - "required": false, - "help": "Start a new chat first (true/false, default: false)" - }, - { - "name": "thinking", - "type": "str", - "default": null, - "required": false, - "help": "Thinking level: standard or extended (omitted = leave unchanged)" - } - ], - "columns": [ - "response" - ], - "defaultFormat": "plain", - "type": "js", - "modulePath": "plugins/gemini/ask.js", - "sourceFile": "plugins/gemini/ask.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "deep-research", - "description": "Start a Gemini Deep Research run and confirm it", - "access": "write", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send" - }, - { - "name": "timeout", - "type": "int", - "default": 180, - "required": false, - "help": "Max seconds for the overall command (default: 180; confirm-wait clamps internally to 6-20s)" - }, - { - "name": "tool", - "type": "str", - "required": false, - "help": "Override tool label (default: Deep Research)" - }, - { - "name": "confirm", - "type": "str", - "required": false, - "help": "Override confirm button label (default: Start research)" - } - ], - "columns": [ - "status", - "url" - ], - "tags": [ - "search" - ], - "defaultFormat": "plain", - "type": "js", - "modulePath": "plugins/gemini/deep-research.js", - "sourceFile": "plugins/gemini/deep-research.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "deep-research-result", - "description": "Export Deep Research report URL from a Gemini conversation", - "access": "read", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": false, - "positional": true, - "help": "Conversation title or URL (optional; defaults to latest conversation)" - }, - { - "name": "match", - "type": "str", - "default": "contains", - "required": false, - "help": "Match mode", - "choices": [ - "contains", - "exact" - ] - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait for Docs export (default: 120)" - } - ], - "columns": [ - "response" - ], - "tags": [ - "search" - ], - "defaultFormat": "plain", - "type": "js", - "modulePath": "plugins/gemini/deep-research-result.js", - "sourceFile": "plugins/gemini/deep-research-result.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "detail", - "description": "Open a Gemini web conversation by id, URL, or sidebar title and read its turns", - "access": "read", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Conversation id, /app/ URL, or sidebar title" - } - ], - "columns": [ - "Index", - "Role", - "Text" - ], - "type": "js", - "modulePath": "plugins/gemini/detail.js", - "sourceFile": "plugins/gemini/detail.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "history", - "description": "List visible Gemini web conversation history from the sidebar", - "access": "read", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max conversations to show" - } - ], - "columns": [ - "Index", - "Id", - "Title", - "Url" - ], - "type": "js", - "modulePath": "plugins/gemini/history.js", - "sourceFile": "plugins/gemini/history.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "image", - "description": "Generate images with Gemini web and save them locally", - "access": "write", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Image prompt to send to Gemini" - }, - { - "name": "rt", - "type": "str", - "default": "1:1", - "required": false, - "help": "Ratio shorthand for aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3)" - }, - { - "name": "st", - "type": "str", - "default": "", - "required": false, - "help": "Style shorthand, e.g. anime, icon, watercolor" - }, - { - "name": "op", - "type": "str", - "default": "~/tmp/gemini-images", - "required": false, - "help": "Output directory shorthand" - }, - { - "name": "sd", - "type": "boolean", - "default": false, - "required": false, - "help": "Skip download shorthand; only show Gemini page link" - }, - { - "name": "timeout", - "type": "int", - "default": 240, - "required": false, - "help": "Max seconds for the overall command (default: 240)" - } - ], - "columns": [ - "status", - "file", - "link" - ], - "defaultFormat": "plain", - "type": "js", - "modulePath": "plugins/gemini/image.js", - "sourceFile": "plugins/gemini/image.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "login", - "description": "Open gemini login", - "access": "write", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/gemini/auth.js", - "sourceFile": "plugins/gemini/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "models", - "description": "List available Gemini models from the web UI", - "access": "read", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "model", - "thinkingValues" - ], - "type": "js", - "modulePath": "plugins/gemini/models.js", - "sourceFile": "plugins/gemini/models.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "new", - "description": "Start a new conversation in Gemini web chat", - "access": "read", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status", - "Action" - ], - "type": "js", - "modulePath": "plugins/gemini/new.js", - "sourceFile": "plugins/gemini/new.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "read", - "description": "Read the turns visible in the current Gemini web conversation", - "access": "read", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Index", - "Role", - "Text" - ], - "type": "js", - "modulePath": "plugins/gemini/read.js", - "sourceFile": "plugins/gemini/read.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "status", - "description": "Check Gemini web page availability and login state", - "access": "read", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status", - "Login", - "Url" - ], - "type": "js", - "modulePath": "plugins/gemini/status.js", - "sourceFile": "plugins/gemini/status.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "gemini", - "name": "whoami", - "description": "Show the current logged-in gemini account", - "access": "read", - "domain": "gemini.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name" - ], - "type": "js", - "modulePath": "plugins/gemini/auth.js", - "sourceFile": "plugins/gemini/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "geogebra", - "name": "add-circle", - "description": "Create a circle by center+radius or center+point", - "access": "write", - "example": "webcmd geogebra add-circle --center A --radius 3", - "domain": "www.geogebra.org", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "center", - "type": "str", - "required": true, - "help": "Center point label (e.g. A)" - }, - { - "name": "radius", - "type": "str", - "required": false, - "help": "Radius value (number) or a point label on the circle" - }, - { - "name": "point", - "type": "str", - "required": false, - "help": "Alternative: a point label on the circle (use instead of --radius for Circle(center,point))" - } - ], - "columns": [ - "label", - "center", - "radius" - ], - "type": "js", - "modulePath": "plugins/geogebra/add-circle.js", - "sourceFile": "plugins/geogebra/add-circle.js", - "navigateBefore": false - }, - { - "site": "geogebra", - "name": "add-line", - "description": "Create a line through two points or a segment between two points", - "access": "write", - "example": "webcmd geogebra add-line --points A,B --type segment", - "domain": "www.geogebra.org", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "points", - "type": "str", - "required": true, - "help": "Two point labels separated by comma (e.g. \"A,B\")" - }, - { - "name": "type", - "type": "str", - "default": "line", - "required": false, - "help": "Type: line, segment, or ray (default: line)", - "choices": [ - "line", - "segment", - "ray" - ] - } - ], - "columns": [ - "label", - "type", - "points" - ], - "type": "js", - "modulePath": "plugins/geogebra/add-line.js", - "sourceFile": "plugins/geogebra/add-line.js", - "navigateBefore": false - }, - { - "site": "geogebra", - "name": "add-point", - "description": "Create a point with given label and coordinates", - "access": "write", - "example": "webcmd geogebra add-point --name A --coords 1,2", - "domain": "www.geogebra.org", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "help": "Point label (e.g. A, B, P1)" - }, - { - "name": "coords", - "type": "str", - "required": true, - "help": "Coordinates as x,y (e.g. \"1,2\")" - } - ], - "columns": [ - "name", - "x", - "y" - ], - "type": "js", - "modulePath": "plugins/geogebra/add-point.js", - "sourceFile": "plugins/geogebra/add-point.js", - "navigateBefore": false - }, - { - "site": "geogebra", - "name": "add-polygon", - "description": "Create a polygon from a list of point labels", - "access": "write", - "example": "webcmd geogebra add-polygon --points A,B,C", - "domain": "www.geogebra.org", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "points", - "type": "str", - "required": true, - "help": "Comma-separated point labels (e.g. \"A,B,C\" or \"A,B,C,D\")" - } - ], - "columns": [ - "label", - "vertices" - ], - "type": "js", - "modulePath": "plugins/geogebra/add-polygon.js", - "sourceFile": "plugins/geogebra/add-polygon.js", - "navigateBefore": false - }, - { - "site": "geogebra", - "name": "eval", - "description": "Execute one or more GeoGebra command strings (semicolon-separated)", - "access": "write", - "example": "webcmd geogebra eval \"A=(0,0);B=(4,0);c=Circle(A,B);d=Circle(B,A);C=Intersect(c,d,1);Polygon(A,B,C)\"", - "domain": "www.geogebra.org", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "command", - "type": "str", - "required": true, - "positional": true, - "help": "GeoGebra command string (use ; to chain multiple commands)" - } - ], - "columns": [ - "command", - "result" - ], - "type": "js", - "modulePath": "plugins/geogebra/eval.js", - "sourceFile": "plugins/geogebra/eval.js", - "navigateBefore": false - }, - { - "site": "geogebra", - "name": "hexagon", - "description": "Draw a regular hexagon centered at the origin", - "access": "write", - "example": "webcmd geogebra hexagon --size 3", - "domain": "www.geogebra.org", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "size", - "type": "str", - "default": "2", - "required": false, - "help": "Radius of the hexagon (default: 2)" - } - ], - "columns": [ - "step", - "result" - ], - "type": "js", - "modulePath": "plugins/geogebra/hexagon.js", - "sourceFile": "plugins/geogebra/hexagon.js", - "navigateBefore": false - }, - { - "site": "geogebra", - "name": "info", - "description": "Get detailed properties of a GeoGebra object", - "access": "read", - "example": "webcmd geogebra info --name A", - "domain": "www.geogebra.org", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "help": "Object label (e.g. A, c1, poly1)" - } - ], - "columns": [ - "property", - "value" - ], - "type": "js", - "modulePath": "plugins/geogebra/info.js", - "sourceFile": "plugins/geogebra/info.js", - "navigateBefore": false - }, - { - "site": "geogebra", - "name": "list", - "description": "List all geometric objects on the GeoGebra canvas", - "access": "read", - "domain": "www.geogebra.org", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "type", - "type": "str", - "required": false, - "help": "Filter by object type (e.g. \"point\", \"line\", \"circle\")" - } - ], - "columns": [ - "name", - "type", - "value", - "visible" - ], - "type": "js", - "modulePath": "plugins/geogebra/list.js", - "sourceFile": "plugins/geogebra/list.js", - "navigateBefore": false - }, - { - "site": "geogebra", - "name": "triangle", - "description": "Draw an equilateral triangle from a horizontal base segment", - "access": "write", - "example": "webcmd geogebra triangle --size 4", - "domain": "www.geogebra.org", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "size", - "type": "str", - "default": "2", - "required": false, - "help": "Side length of the triangle (default: 2)" - } - ], - "columns": [ - "step", - "result" - ], - "type": "js", - "modulePath": "plugins/geogebra/triangle.js", - "sourceFile": "plugins/geogebra/triangle.js", - "navigateBefore": false - }, - { - "site": "github", - "name": "login", - "description": "Open github login", - "access": "write", - "domain": "github.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "id", - "username", - "name", - "url", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/github/auth.js", - "sourceFile": "plugins/github/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "github", - "name": "whoami", - "description": "Show the current logged-in github account", - "access": "read", - "domain": "github.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "id", - "username", - "name", - "url" - ], - "type": "js", - "modulePath": "plugins/github/auth.js", - "sourceFile": "plugins/github/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "github-trending", - "name": "repos", - "description": "GitHub Trending repositories (public, no login). Filter by --language and --since.", - "access": "read", - "domain": "github.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "since", - "type": "string", - "default": "daily", - "required": false, - "help": "Time range: daily / weekly / monthly" - }, - { - "name": "language", - "type": "string", - "default": "", - "required": false, - "help": "Filter by programming language slug, e.g. python, rust, \"c++\"" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Number of repositories to return (max 25)" - } - ], - "columns": [ - "rank", - "repo", - "description", - "language", - "stars", - "forks", - "starsSince", - "url" - ], - "type": "js", - "modulePath": "plugins/github-trending/repos.js", - "sourceFile": "plugins/github-trending/repos.js" - }, - { - "site": "goettingen", - "name": "export-postgraduate-courses", - "description": "Export University of Göttingen postgraduate programmes from the official A-Z API.", - "access": "read", - "example": "webcmd goettingen export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.uni-goettingen.de", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Positive maximum number of programmes after filtering and deduplication" - } - ], - "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" - ], - "type": "js", - "modulePath": "plugins/goettingen/export-postgraduate-courses.js", - "sourceFile": "plugins/goettingen/export-postgraduate-courses.js" - }, - { - "site": "google", - "name": "images", - "description": "Search Google Images for photos and image results", - "access": "read", - "domain": "google.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Image search query" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of image results (1-100)" - }, - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language short code (e.g. en, zh)" - }, - { - "name": "resolve", - "type": "bool", - "default": true, - "required": false, - "help": "Click image previews to resolve original imgurl values" - } - ], - "columns": [ - "rank", - "title", - "imageUrl", - "thumbnailUrl", - "sourceUrl", - "source", - "width", - "height" - ], - "type": "js", - "modulePath": "plugins/google/images.js", - "sourceFile": "plugins/google/images.js", - "navigateBefore": false - }, - { - "site": "google", - "name": "news", - "description": "Get Google News headlines", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "keyword", - "type": "str", - "required": false, - "positional": true, - "help": "Search query (omit for top stories)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - }, - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language short code (e.g. en, zh)" - }, - { - "name": "region", - "type": "str", - "default": "US", - "required": false, - "help": "Region code (e.g. US, CN)" - } - ], - "columns": [ - "title", - "source", - "date", - "url" - ], - "type": "js", - "modulePath": "plugins/google/news.js", - "sourceFile": "plugins/google/news.js" - }, - { - "site": "google", - "name": "search", - "description": "Search Google", - "access": "read", - "domain": "google.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results (1-100)" - }, - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language short code (e.g. en, zh)" - } - ], - "columns": [ - "type", - "title", - "url", - "snippet" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/google/search.js", - "sourceFile": "plugins/google/search.js" - }, - { - "site": "google", - "name": "suggest", - "description": "Get Google search suggestions", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "lang", - "type": "str", - "default": "zh-CN", - "required": false, - "help": "Language code" - } - ], - "columns": [ - "suggestion" - ], - "type": "js", - "modulePath": "plugins/google/suggest.js", - "sourceFile": "plugins/google/suggest.js" - }, - { - "site": "google", - "name": "trends", - "description": "Get Google Trends daily trending searches", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "region", - "type": "str", - "default": "US", - "required": false, - "help": "Region code (e.g. US, CN, JP)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "title", - "traffic", - "date" - ], - "type": "js", - "modulePath": "plugins/google/trends.js", - "sourceFile": "plugins/google/trends.js" - }, - { - "site": "google-scholar", - "name": "cite", - "description": "Get citation for a Google Scholar paper", - "access": "read", - "domain": "scholar.google.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Paper title to search for" - }, - { - "name": "style", - "type": "str", - "default": "bibtex", - "required": false, - "help": "Citation format", - "choices": [ - "bibtex", - "endnote", - "refman", - "refworks" - ] - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Which search result to cite (1-based)" - } - ], - "columns": [ - "title", - "format", - "citation" - ], - "type": "js", - "modulePath": "plugins/google-scholar/cite.js", - "sourceFile": "plugins/google-scholar/cite.js" - }, - { - "site": "google-scholar", - "name": "profile", - "description": "View a Google Scholar author profile", - "access": "read", - "domain": "scholar.google.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "author", - "type": "str", - "required": true, - "positional": true, - "help": "Author name or Scholar user ID (e.g. JicYPdAAAAAJ)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max papers to show (max 20)" - } - ], - "columns": [ - "rank", - "title", - "cited", - "year" - ], - "type": "js", - "modulePath": "plugins/google-scholar/profile.js", - "sourceFile": "plugins/google-scholar/profile.js" - }, - { - "site": "google-scholar", - "name": "search", - "description": "Google Scholar scholar search", - "access": "read", - "domain": "scholar.google.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results to return (max 20)" - } - ], - "columns": [ - "rank", - "title", - "authors", - "source", - "year", - "cited", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/google-scholar/search.js", - "sourceFile": "plugins/google-scholar/search.js" - }, - { - "site": "goproxy", - "name": "module", - "description": "Latest version + VCS origin metadata for a Go module on proxy.golang.org", - "access": "read", - "domain": "proxy.golang.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "module", - "type": "string", - "required": true, - "positional": true, - "help": "Go module path (e.g. \"github.com/gin-gonic/gin\", \"golang.org/x/net\")" - } - ], - "columns": [ - "module", - "version", - "publishedAt", - "vcs", - "repository", - "commit", - "ref", - "pkgGoDevUrl", - "url" - ], - "type": "js", - "modulePath": "plugins/goproxy/module.js", - "sourceFile": "plugins/goproxy/module.js" - }, - { - "site": "goproxy", - "name": "versions", - "description": "Published version tags for a Go module (newest first), optionally with publish times", - "access": "read", - "domain": "proxy.golang.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "module", - "type": "string", - "required": true, - "positional": true, - "help": "Go module path (e.g. \"github.com/gin-gonic/gin\")" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max rows to return (1-200)" - }, - { - "name": "with-time", - "type": "boolean", - "default": false, - "required": false, - "help": "Fetch each version's publish time (one extra request per row)" - } - ], - "columns": [ - "rank", - "module", - "version", - "publishedAt", - "url" - ], - "type": "js", - "modulePath": "plugins/goproxy/versions.js", - "sourceFile": "plugins/goproxy/versions.js" - }, - { - "site": "grok", - "name": "ask", - "description": "Send a message to Grok and get response", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "string", - "required": true, - "positional": true, - "help": "Prompt to send to Grok" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait for response (default: 120)" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending (default: false)" - } - ], - "columns": [ - "response" - ], - "type": "js", - "modulePath": "plugins/grok/ask.js", - "sourceFile": "plugins/grok/ask.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "delete", - "description": "Delete a Grok conversation by ID. Grok takes effect immediately with no confirmation dialog — require --yes to actually delete.", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" - }, - { - "name": "yes", - "type": "boolean", - "default": false, - "required": false, - "help": "Actually delete (default is a dry-run preview)" - } - ], - "columns": [ - "status", - "id" - ], - "type": "js", - "modulePath": "plugins/grok/delete.js", - "sourceFile": "plugins/grok/delete.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "detail", - "description": "Open a Grok conversation by ID and read its messages", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Session ID (UUID) or full https://grok.com/c/ URL" - }, - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" - } - ], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "plugins/grok/detail.js", - "sourceFile": "plugins/grok/detail.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "export", - "description": "Export all visible Grok conversation history metadata", - "access": "read", - "example": "webcmd grok export -f yaml", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 0, - "required": false, - "help": "Max conversations to export; 0 means all loaded history" - }, - { - "name": "maxScrolls", - "type": "int", - "default": 80, - "required": false, - "help": "Max history-list scroll rounds when limit is 0 (max 500)" - } - ], - "columns": [ - "index", - "id", - "title", - "date", - "url" - ], - "type": "js", - "modulePath": "plugins/grok/export.js", - "sourceFile": "plugins/grok/export.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "export-all", - "description": "Export Grok conversation history and each conversation transcript", - "access": "read", - "example": "webcmd grok export-all --limit 5 -f json", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 0, - "required": false, - "help": "Max conversations to export; 0 means all loaded history" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Skip this many conversations before exporting" - }, - { - "name": "manifestPath", - "type": "string", - "default": "", - "required": false, - "help": "Optional grok/export JSON manifest path; skips history dialog and visits listed /c pages directly" - }, - { - "name": "maxScrolls", - "type": "int", - "default": 80, - "required": false, - "help": "Max history-list scroll rounds when limit is 0 (max 500)" - }, - { - "name": "pageScrolls", - "type": "int", - "default": 30, - "required": false, - "help": "Max per-conversation scroll-to-bottom rounds (max 200)" - }, - { - "name": "pageTimeoutMs", - "type": "int", - "default": 30000, - "required": false, - "help": "Max wait for each conversation page to show messages" - }, - { - "name": "delayMinMs", - "type": "int", - "default": 0, - "required": false, - "help": "Minimum polite delay after a conversation page loads" - }, - { - "name": "delayMaxMs", - "type": "int", - "default": 5000, - "required": false, - "help": "Maximum polite delay after a conversation page loads" - } - ], - "columns": [ - "index", - "id", - "title", - "date", - "url", - "status", - "messageCount", - "error", - "messagesJson" - ], - "type": "js", - "modulePath": "plugins/grok/export-all.js", - "sourceFile": "plugins/grok/export-all.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "history", - "description": "List recent Grok conversations from the sidebar (requires login)", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max conversations to show (default 20, max 100)" - } - ], - "columns": [ - "Index", - "Title", - "Url" - ], - "type": "js", - "modulePath": "plugins/grok/history.js", - "sourceFile": "plugins/grok/history.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "image", - "description": "Generate images on grok.com and return image URLs", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "string", - "required": true, - "positional": true, - "help": "Image generation prompt" - }, - { - "name": "timeout", - "type": "int", - "default": 240, - "required": false, - "help": "Max seconds to wait for the image (default: 240)" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending (default: false)" - }, - { - "name": "count", - "type": "int", - "default": 1, - "required": false, - "help": "Minimum images to wait for before returning (default: 1)" - }, - { - "name": "out", - "type": "string", - "default": "", - "required": false, - "help": "Directory to save downloaded images (uses browser session to bypass auth)" - } - ], - "columns": [ - "url", - "width", - "height", - "path" - ], - "type": "js", - "modulePath": "plugins/grok/image.js", - "sourceFile": "plugins/grok/image.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "login", - "description": "Open grok login", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/grok/auth.js", - "sourceFile": "plugins/grok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "new", - "description": "Start a new conversation in Grok", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/grok/new.js", - "sourceFile": "plugins/grok/new.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "pin", - "description": "Pin a Grok conversation by ID", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" - } - ], - "columns": [ - "status", - "id" - ], - "type": "js", - "modulePath": "plugins/grok/pin.js", - "sourceFile": "plugins/grok/pin.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "read", - "description": "Read messages in the current Grok conversation", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "markdown", - "type": "boolean", - "default": false, - "required": false, - "help": "Emit assistant replies as markdown" - } - ], - "columns": [ - "Role", - "Text" - ], - "type": "js", - "modulePath": "plugins/grok/read.js", - "sourceFile": "plugins/grok/read.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "send", - "description": "Fire-and-forget: send a prompt to Grok without waiting for the reply", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt to send to Grok" - }, - { - "name": "new", - "type": "boolean", - "default": false, - "required": false, - "help": "Start a new chat before sending" - } - ], - "columns": [ - "Status", - "Prompt" - ], - "type": "js", - "modulePath": "plugins/grok/send.js", - "sourceFile": "plugins/grok/send.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "status", - "description": "Check Grok page availability, login state, current session and model", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status", - "Login", - "Model", - "SessionId", - "Url" - ], - "type": "js", - "modulePath": "plugins/grok/status.js", - "sourceFile": "plugins/grok/status.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "unpin", - "description": "Unpin a Grok conversation by ID", - "access": "write", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Conversation UUID or grok.com/c/ URL" - } - ], - "columns": [ - "status", - "id" - ], - "type": "js", - "modulePath": "plugins/grok/pin.js", - "sourceFile": "plugins/grok/pin.js", - "navigateBefore": "https://grok.com", - "siteSession": "persistent" - }, - { - "site": "grok", - "name": "whoami", - "description": "Show the current logged-in grok account", - "access": "read", - "domain": "grok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "plugins/grok/auth.js", - "sourceFile": "plugins/grok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "hackernews", - "name": "ask", - "description": "Hacker News Ask HN posts", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/hackernews/ask.js", - "sourceFile": "plugins/hackernews/ask.js" - }, - { - "site": "hackernews", - "name": "best", - "description": "Hacker News best stories", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/hackernews/best.js", - "sourceFile": "plugins/hackernews/best.js" - }, - { - "site": "hackernews", - "name": "jobs", - "description": "Hacker News job postings", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of job postings" - } - ], - "columns": [ - "rank", - "id", - "title", - "author", - "url" - ], - "type": "js", - "modulePath": "plugins/hackernews/jobs.js", - "sourceFile": "plugins/hackernews/jobs.js" - }, - { - "site": "hackernews", - "name": "new", - "description": "Hacker News newest stories", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/hackernews/new.js", - "sourceFile": "plugins/hackernews/new.js" - }, - { - "site": "hackernews", - "name": "read", - "description": "Read a Hacker News story and its comment tree", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "HN item ID (e.g. 39847301)" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max top-level comments" - }, - { - "name": "depth", - "type": "int", - "default": 2, - "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" - }, - { - "name": "replies", - "type": "int", - "default": 5, - "required": false, - "help": "Max replies shown per comment at each level" - }, - { - "name": "max-length", - "type": "int", - "default": 2000, - "required": false, - "help": "Max characters per comment body (min 100)" - } - ], - "columns": [ - "type", - "author", - "score", - "text" - ], - "type": "js", - "modulePath": "plugins/hackernews/read.js", - "sourceFile": "plugins/hackernews/read.js" - }, - { - "site": "hackernews", - "name": "search", - "description": "Search Hacker News stories", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - }, - { - "name": "sort", - "type": "str", - "default": "relevance", - "required": false, - "help": "Sort by relevance or date", - "choices": [ - "relevance", - "date" - ] - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/hackernews/search.js", - "sourceFile": "plugins/hackernews/search.js" - }, - { - "site": "hackernews", - "name": "show", - "description": "Hacker News Show HN posts", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/hackernews/show.js", - "sourceFile": "plugins/hackernews/show.js" - }, - { - "site": "hackernews", - "name": "top", - "description": "Hacker News top stories", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/hackernews/top.js", - "sourceFile": "plugins/hackernews/top.js" - }, - { - "site": "hackernews", - "name": "user", - "description": "Hacker News user profile", - "access": "read", - "domain": "news.ycombinator.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "HN username" - } - ], - "columns": [ - "username", - "karma", - "created", - "about" - ], - "type": "js", - "modulePath": "plugins/hackernews/user.js", - "sourceFile": "plugins/hackernews/user.js" - }, - { - "site": "heidelberg", - "name": "export-postgraduate-courses", - "description": "Export Heidelberg University non-bachelor/postgraduate programs using the official study finder.", - "access": "read", - "example": "webcmd heidelberg export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.uni-heidelberg.de", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" - } - ], - "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" - ], - "type": "js", - "modulePath": "plugins/heidelberg/export-postgraduate-courses.js", - "sourceFile": "plugins/heidelberg/export-postgraduate-courses.js" - }, - { - "site": "hf", - "name": "datasets", - "description": "Top Hugging Face datasets (downloads / likes / trending / freshness).", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "sort", - "type": "string", - "default": "downloads", - "required": false, - "help": "Sort key: downloads, likes, trending, created_at, last_modified" - }, - { - "name": "search", - "type": "string", - "required": false, - "help": "Optional name/owner substring filter." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max datasets (max 100; one API page)." - } - ], - "columns": [ - "rank", - "id", - "author", - "downloads", - "likes", - "tags", - "lastModified", - "url" - ], - "type": "js", - "modulePath": "plugins/hf/datasets.js", - "sourceFile": "plugins/hf/datasets.js" - }, - { - "site": "hf", - "name": "login", - "description": "Open hf login", - "access": "write", - "domain": "huggingface.co", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "username", - "fullname", - "type", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/hf/auth.js", - "sourceFile": "plugins/hf/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "hf", - "name": "models", - "description": "Top Hugging Face models (downloads / likes / trending / freshness).", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "sort", - "type": "string", - "default": "downloads", - "required": false, - "help": "Sort key: downloads, likes, trending, created_at, last_modified" - }, - { - "name": "search", - "type": "string", - "required": false, - "help": "Optional name/owner substring filter (e.g. \"llama\", \"mistralai/\")" - }, - { - "name": "pipeline", - "type": "string", - "required": false, - "help": "Filter by pipeline tag (e.g. text-generation, image-classification)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max models (max 100; one API page)." - } - ], - "columns": [ - "rank", - "id", - "author", - "pipelineTag", - "downloads", - "likes", - "tags", - "lastModified", - "url" - ], - "type": "js", - "modulePath": "plugins/hf/models.js", - "sourceFile": "plugins/hf/models.js" - }, - { - "site": "hf", - "name": "paper", - "description": "Hugging Face paper detail by arXiv id (full title / summary / authors / AI keywords)", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "arXiv id (e.g. \"1706.03762\") — same value HF uses to mirror the paper" - } - ], - "columns": [ - "id", - "title", - "authors", - "publishedAt", - "upvotes", - "aiKeywords", - "summary", - "aiSummary", - "url" - ], - "type": "js", - "modulePath": "plugins/hf/paper.js", - "sourceFile": "plugins/hf/paper.js" - }, - { - "site": "hf", - "name": "spaces", - "description": "Top Hugging Face Spaces (likes / created_at / last_modified).", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "sort", - "type": "string", - "default": "likes", - "required": false, - "help": "Sort key: likes, created_at, last_modified" - }, - { - "name": "search", - "type": "string", - "required": false, - "help": "Optional name/owner substring filter (e.g. \"stability\", \"openai/\")" - }, - { - "name": "sdk", - "type": "string", - "required": false, - "help": "Filter by Space SDK: gradio / streamlit / docker / static" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max spaces (max 100; one API page)." - } - ], - "columns": [ - "rank", - "id", - "author", - "sdk", - "likes", - "tags", - "lastModified", - "url" - ], - "type": "js", - "modulePath": "plugins/hf/spaces.js", - "sourceFile": "plugins/hf/spaces.js" - }, - { - "site": "hf", - "name": "top", - "description": "Top upvoted Hugging Face papers", - "access": "read", - "domain": "huggingface.co", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of papers" - }, - { - "name": "all", - "type": "bool", - "default": false, - "required": false, - "help": "Return all papers (ignore limit)" - }, - { - "name": "date", - "type": "str", - "required": false, - "help": "Date (YYYY-MM-DD), defaults to most recent" - }, - { - "name": "period", - "type": "str", - "default": "daily", - "required": false, - "help": "Time period: daily, weekly, or monthly", - "choices": [ - "daily", - "weekly", - "monthly" - ] - } - ], - "columns": [ - "rank", - "id", - "title", - "upvotes", - "authors" - ], - "type": "js", - "modulePath": "plugins/hf/top.js", - "sourceFile": "plugins/hf/top.js" - }, - { - "site": "hf", - "name": "whoami", - "description": "Show the current logged-in hf account", - "access": "read", - "domain": "huggingface.co", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "username", - "fullname", - "type" - ], - "type": "js", - "modulePath": "plugins/hf/auth.js", - "sourceFile": "plugins/hf/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "hft", - "name": "export-postgraduate-courses", - "description": "Export HFT Stuttgart postgraduate programs using the official public programme pages.", - "access": "read", - "example": "webcmd hft export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.hft-stuttgart.de", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" - } - ], - "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" - ], - "type": "js", - "modulePath": "plugins/hft/export-postgraduate-courses.js", - "sourceFile": "plugins/hft/export-postgraduate-courses.js" - }, - { - "site": "homebrew", - "name": "cask", - "description": "Fetch a Homebrew cask's metadata (version, homepage, deprecation, download URL)", - "access": "read", - "domain": "formulae.brew.sh", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "token", - "type": "str", - "required": true, - "positional": true, - "help": "Cask token (e.g. \"firefox\", \"visual-studio-code\", \"google-chrome\")" - } - ], - "columns": [ - "cask", - "tap", - "name", - "version", - "description", - "homepage", - "deprecated", - "disabled", - "download", - "url" - ], - "type": "js", - "modulePath": "plugins/homebrew/cask.js", - "sourceFile": "plugins/homebrew/cask.js" - }, - { - "site": "homebrew", - "name": "formula", - "description": "Fetch a Homebrew formula's metadata (version, license, deps, deprecation, source)", - "access": "read", - "domain": "formulae.brew.sh", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Formula name (e.g. \"wget\", \"gcc@13\", \"imagemagick\")" - } - ], - "columns": [ - "formula", - "tap", - "version", - "license", - "description", - "homepage", - "dependencies", - "deprecated", - "disabled", - "source", - "url" - ], - "type": "js", - "modulePath": "plugins/homebrew/formula.js", - "sourceFile": "plugins/homebrew/formula.js" - }, - { - "site": "homebrew", - "name": "popular", - "description": "List most-installed Homebrew formulae or casks (Homebrew's analytics ranking)", - "access": "read", - "domain": "formulae.brew.sh", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "type", - "type": "str", - "default": "formula", - "required": false, - "help": "Package type (formula / cask)" - }, - { - "name": "window", - "type": "str", - "default": "30d", - "required": false, - "help": "Time window (30d / 90d / 365d)" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max rows (1-500)" - } - ], - "columns": [ - "rank", - "token", - "type", - "installs", - "percent", - "window", - "url" - ], - "type": "js", - "modulePath": "plugins/homebrew/popular.js", - "sourceFile": "plugins/homebrew/popular.js" - }, - { - "site": "iit", - "name": "export-postgraduate-courses", - "description": "Export Illinois Tech postgraduate programs using official public sources.", - "access": "read", - "example": "webcmd iit export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.iit.edu", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" - } - ], - "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" - ], - "type": "js", - "modulePath": "plugins/iit/export-postgraduate-courses.js", - "sourceFile": "plugins/iit/export-postgraduate-courses.js" - }, - { - "site": "imdb", - "name": "person", - "description": "Get actor or director info", - "access": "read", - "domain": "www.imdb.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "IMDb person ID (nm0634240) or URL" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max filmography entries" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "plugins/imdb/person.js", - "sourceFile": "plugins/imdb/person.js" - }, - { - "site": "imdb", - "name": "reviews", - "description": "Get user reviews for a movie or TV show", - "access": "read", - "domain": "www.imdb.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "IMDb title ID (tt1375666) or URL" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of reviews" - } - ], - "columns": [ - "rank", - "title", - "rating", - "author", - "date", - "text" - ], - "type": "js", - "modulePath": "plugins/imdb/reviews.js", - "sourceFile": "plugins/imdb/reviews.js" - }, - { - "site": "imdb", - "name": "search", - "description": "Search IMDb for movies, TV shows, and people", - "access": "read", - "domain": "www.imdb.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "id", - "title", - "year", - "type", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/imdb/search.js", - "sourceFile": "plugins/imdb/search.js" - }, - { - "site": "imdb", - "name": "title", - "description": "Get movie or TV show details", - "access": "read", - "domain": "www.imdb.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "IMDb title ID (tt1375666) or URL" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "plugins/imdb/title.js", - "sourceFile": "plugins/imdb/title.js" - }, - { - "site": "imdb", - "name": "top", - "description": "IMDb Top 250 Movies", - "access": "read", - "domain": "www.imdb.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "rating", - "votes", - "genre", - "url" - ], - "type": "js", - "modulePath": "plugins/imdb/top.js", - "sourceFile": "plugins/imdb/top.js" - }, - { - "site": "imdb", - "name": "trending", - "description": "IMDb Most Popular Movies", - "access": "read", - "domain": "www.imdb.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "rating", - "genre", - "url" - ], - "type": "js", - "modulePath": "plugins/imdb/trending.js", - "sourceFile": "plugins/imdb/trending.js" - }, - { - "site": "indeed", - "name": "job", - "aliases": [ - "detail", - "view" - ], - "description": "Read the full Indeed job posting by jk (job key)", - "access": "read", - "domain": "www.indeed.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Job key (16-char hex from `indeed search`, e.g. \"dccc07ac5a6a3683\")" - } - ], - "columns": [ - "id", - "title", - "company", - "location", - "salary", - "job_type", - "description", - "url" - ], - "type": "js", - "modulePath": "plugins/indeed/job.js", - "sourceFile": "plugins/indeed/job.js", - "navigateBefore": false - }, - { - "site": "indeed", - "name": "search", - "description": "Indeed keyword job search (rendered DOM via browser session, US site)", - "access": "read", - "domain": "www.indeed.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Job keyword (title / skill / company)" - }, - { - "name": "location", - "type": "string", - "default": "", - "required": false, - "help": "Location filter (e.g. \"remote\", \"New York, NY\", \"San Francisco\")" - }, - { - "name": "fromage", - "type": "string", - "default": "", - "required": false, - "help": "Recency filter, days back: 1 / 3 / 7 / 14" - }, - { - "name": "sort", - "type": "string", - "default": "relevance", - "required": false, - "help": "Sort order: relevance | date" - }, - { - "name": "start", - "type": "int", - "default": 0, - "required": false, - "help": "Pagination offset (multiple of 10, 0-based)" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Max rows to return (1-25, capped at one page)" - } - ], - "columns": [ - "rank", - "id", - "title", - "company", - "location", - "salary", - "tags", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/indeed/search.js", - "sourceFile": "plugins/indeed/search.js", - "navigateBefore": false - }, - { - "site": "instagram", - "name": "collection-create", - "description": "Create a new Instagram saved-posts collection (folder)", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Name of the collection to create" - } - ], - "columns": [ - "status", - "collectionId", - "collectionName", - "mediaCount" - ], - "type": "js", - "modulePath": "plugins/instagram/collection-create.js", - "sourceFile": "plugins/instagram/collection-create.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "collection-delete", - "description": "Delete an Instagram saved-posts collection (folder) by name or id", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "target", - "type": "str", - "required": true, - "positional": true, - "help": "Collection name (case-insensitive) or numeric collection_id" - } - ], - "columns": [ - "status", - "collectionId", - "collectionName" - ], - "type": "js", - "modulePath": "plugins/instagram/collection-delete.js", - "sourceFile": "plugins/instagram/collection-delete.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "comment", - "description": "Comment on an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Comment text" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "text" - ], - "type": "js", - "modulePath": "plugins/instagram/comment.js", - "sourceFile": "plugins/instagram/comment.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "download", - "description": "Download images and videos from Instagram posts and reels", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram post / reel / tv URL" - }, - { - "name": "path", - "type": "str", - "default": "~/Downloads/Instagram", - "required": false, - "help": "Download directory" - } - ], - "type": "js", - "modulePath": "plugins/instagram/download.js", - "sourceFile": "plugins/instagram/download.js", - "navigateBefore": false - }, - { - "site": "instagram", - "name": "explore", - "description": "Instagram explore/discover trending posts", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts" - } - ], - "columns": [ - "rank", - "user", - "caption", - "likes", - "comments", - "type" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/instagram/explore.js", - "sourceFile": "plugins/instagram/explore.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "follow", - "description": "Follow an Instagram user", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username to follow" - } - ], - "columns": [ - "status", - "username" - ], - "type": "js", - "modulePath": "plugins/instagram/follow.js", - "sourceFile": "plugins/instagram/follow.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "followers", - "description": "List followers of an Instagram user", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of followers" - } - ], - "columns": [ - "rank", - "username", - "name", - "verified", - "private" - ], - "type": "js", - "modulePath": "plugins/instagram/followers.js", - "sourceFile": "plugins/instagram/followers.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "following", - "description": "List accounts an Instagram user is following", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of accounts" - } - ], - "columns": [ - "rank", - "username", - "name", - "verified", - "private" - ], - "type": "js", - "modulePath": "plugins/instagram/following.js", - "sourceFile": "plugins/instagram/following.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "like", - "description": "Like an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "post" - ], - "type": "js", - "modulePath": "plugins/instagram/like.js", - "sourceFile": "plugins/instagram/like.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "login", - "description": "Open instagram login", - "access": "write", - "domain": "instagram.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "username", - "full_name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/instagram/auth.js", - "sourceFile": "plugins/instagram/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "instagram", - "name": "note", - "description": "Publish a text Instagram note", - "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "content", - "type": "str", - "required": true, - "positional": true, - "help": "Note text (max 60 characters)" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds for the overall command (default: 120)" - } - ], - "columns": [ - "status", - "detail", - "noteId" - ], - "type": "js", - "modulePath": "plugins/instagram/note.js", - "sourceFile": "plugins/instagram/note.js", - "navigateBefore": true - }, - { - "site": "instagram", - "name": "post", - "description": "Post an Instagram feed image or mixed-media carousel", - "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "media", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Comma-separated media paths (images/videos, up to 10)", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "image/jpeg", - "image/png", - "image/webp", - "video/mp4" - ], - "maxBytes": 262144000 - } - }, - { - "name": "content", - "type": "str", - "required": false, - "positional": true, - "help": "Caption text" - }, - { - "name": "timeout", - "type": "int", - "default": 300, - "required": false, - "help": "Max seconds for the overall command (default: 300)" - } - ], - "columns": [ - "status", - "detail", - "url" - ], - "type": "js", - "modulePath": "plugins/instagram/post.js", - "sourceFile": "plugins/instagram/post.js", - "navigateBefore": true - }, - { - "site": "instagram", - "name": "profile", - "description": "Get Instagram user profile info", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" - } - ], - "columns": [ - "username", - "name", - "followers", - "following", - "posts", - "verified", - "bio" - ], - "type": "js", - "modulePath": "plugins/instagram/profile.js", - "sourceFile": "plugins/instagram/profile.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "reel", - "description": "Post an Instagram reel video", - "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "video", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Path to a single .mp4 video file", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "video/mp4" - ], - "maxBytes": 262144000 - } - }, - { - "name": "content", - "type": "str", - "required": false, - "positional": true, - "help": "Caption text" - }, - { - "name": "timeout", - "type": "int", - "default": 600, - "required": false, - "help": "Max seconds for the overall command (default: 600)" - } - ], - "columns": [ - "status", - "detail", - "url" - ], - "type": "js", - "modulePath": "plugins/instagram/reel.js", - "sourceFile": "plugins/instagram/reel.js", - "navigateBefore": true - }, - { - "site": "instagram", - "name": "save", - "description": "Save (bookmark) an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "post" - ], - "type": "js", - "modulePath": "plugins/instagram/save.js", - "sourceFile": "plugins/instagram/save.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "saved", - "description": "Get your saved Instagram posts (optionally from a specific collection)", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of saved posts" - }, - { - "name": "collection", - "type": "str", - "required": false, - "help": "Collection name (case-insensitive). Omit for the default \"All posts\" feed." - } - ], - "columns": [ - "index", - "user", - "caption", - "likes", - "comments", - "type" - ], - "type": "js", - "modulePath": "plugins/instagram/saved.js", - "sourceFile": "plugins/instagram/saved.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "search", - "description": "Search Instagram users", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "username", - "name", - "verified", - "private", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/instagram/search.js", - "sourceFile": "plugins/instagram/search.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "story", - "description": "Post a single Instagram story image or video", - "access": "write", - "domain": "www.instagram.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "media", - "type": "str", - "required": false, - "valueRequired": true, - "help": "Path to a single story image or video file" - }, - { - "name": "timeout", - "type": "int", - "default": 300, - "required": false, - "help": "Max seconds for the overall command (default: 300)" - } - ], - "columns": [ - "status", - "detail", - "url" - ], - "type": "js", - "modulePath": "plugins/instagram/story.js", - "sourceFile": "plugins/instagram/story.js", - "navigateBefore": true - }, - { - "site": "instagram", - "name": "unfollow", - "description": "Unfollow an Instagram user", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username to unfollow" - } - ], - "columns": [ - "status", - "username" - ], - "type": "js", - "modulePath": "plugins/instagram/unfollow.js", - "sourceFile": "plugins/instagram/unfollow.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "unlike", - "description": "Unlike an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "post" - ], - "type": "js", - "modulePath": "plugins/instagram/unlike.js", - "sourceFile": "plugins/instagram/unlike.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "unsave", - "description": "Unsave (remove bookmark) an Instagram post", - "access": "write", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Username of the post author" - }, - { - "name": "index", - "type": "int", - "default": 1, - "required": false, - "help": "Post index (1 = most recent)" - } - ], - "columns": [ - "status", - "user", - "post" - ], - "type": "js", - "modulePath": "plugins/instagram/unsave.js", - "sourceFile": "plugins/instagram/unsave.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "user", - "description": "Get recent posts from an Instagram user", - "access": "read", - "domain": "www.instagram.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Instagram username" - }, - { - "name": "limit", - "type": "int", - "default": 12, - "required": false, - "help": "Number of posts" - } - ], - "columns": [ - "index", - "caption", - "likes", - "comments", - "type", - "date" - ], - "type": "js", - "modulePath": "plugins/instagram/user.js", - "sourceFile": "plugins/instagram/user.js", - "navigateBefore": "https://www.instagram.com" - }, - { - "site": "instagram", - "name": "whoami", - "description": "Show the current logged-in instagram account", - "access": "read", - "domain": "instagram.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "username", - "full_name" - ], - "type": "js", - "modulePath": "plugins/instagram/auth.js", - "sourceFile": "plugins/instagram/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "jhu", - "name": "export-postgraduate-courses", - "description": "Export Johns Hopkins University postgraduate programs using the official Academic Catalogue.", - "access": "read", - "example": "webcmd jhu export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "e-catalogue.jhu.edu", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" - } - ], - "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" - ], - "type": "js", - "modulePath": "plugins/jhu/export-postgraduate-courses.js", - "sourceFile": "plugins/jhu/export-postgraduate-courses.js" - }, - { - "site": "jira", - "name": "attachments", - "description": "Jira issue attachment metadata", - "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Jira issue key, e.g. PROJ-123" - } - ], - "columns": [ - "id", - "filename", - "mimeType", - "size", - "url" - ], - "type": "js", - "modulePath": "plugins/jira/attachments.js", - "sourceFile": "plugins/jira/attachments.js" - }, - { - "site": "jira", - "name": "comments", - "description": "Jira issue comments as Markdown", - "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Jira issue key, e.g. PROJ-123" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max comments to return (1-100)" - } - ], - "columns": [ - "id", - "author", - "created", - "updated", - "markdown" - ], - "type": "js", - "modulePath": "plugins/jira/comments.js", - "sourceFile": "plugins/jira/comments.js" - }, - { - "site": "jira", - "name": "issue", - "description": "Jira issue detail normalized for agents (description, comments, attachments, links)", - "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Jira issue key, e.g. PROJ-123" - }, - { - "name": "comments-limit", - "type": "int", - "default": 100, - "required": false, - "help": "Max comments to include (1-100)" - } - ], - "columns": [ - "key", - "summary", - "issueType", - "status", - "priority", - "assignee", - "updated", - "url" - ], - "type": "js", - "modulePath": "plugins/jira/issue.js", - "sourceFile": "plugins/jira/issue.js" - }, - { - "site": "jira", - "name": "links", - "description": "Jira issue links", - "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Jira issue key, e.g. PROJ-123" - } - ], - "columns": [ - "key", - "type", - "direction" - ], - "type": "js", - "modulePath": "plugins/jira/links.js", - "sourceFile": "plugins/jira/links.js" - }, - { - "site": "jira", - "name": "search", - "description": "Search Jira issues with JQL", - "access": "read", - "domain": "atlassian.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "jql", - "type": "str", - "required": true, - "positional": true, - "help": "JQL query, e.g. \"project = PROJ order by updated desc\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max issues to return (1-100)" - } - ], - "columns": [ - "key", - "summary", - "issueType", - "status", - "priority", - "assignee", - "updated", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/jira/search.js", - "sourceFile": "plugins/jira/search.js" - }, - { - "site": "lesswrong", - "name": "comments", - "description": "Top comments on a post", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "url-or-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post URL or LessWrong post ID" - }, - { - "name": "limit", - "type": "int", - "default": 5, - "required": false, - "help": "Number of comments" - } - ], - "columns": [ - "rank", - "score", - "author", - "text" - ], - "type": "js", - "modulePath": "plugins/lesswrong/comments.js", - "sourceFile": "plugins/lesswrong/comments.js" - }, - { - "site": "lesswrong", - "name": "curated", - "description": "Curated editor's picks", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/lesswrong/curated.js", - "sourceFile": "plugins/lesswrong/curated.js" - }, - { - "site": "lesswrong", - "name": "frontpage", - "description": "Algorithmic frontpage", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/lesswrong/frontpage.js", - "sourceFile": "plugins/lesswrong/frontpage.js" - }, - { - "site": "lesswrong", - "name": "new", - "description": "Latest posts", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/lesswrong/new.js", - "sourceFile": "plugins/lesswrong/new.js" - }, - { - "site": "lesswrong", - "name": "read", - "description": "Read full post by URL or ID", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "url-or-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post URL or LessWrong post ID" - } - ], - "columns": [ - "title", - "author", - "karma", - "comments", - "tags", - "content", - "url" - ], - "type": "js", - "modulePath": "plugins/lesswrong/read.js", - "sourceFile": "plugins/lesswrong/read.js" - }, - { - "site": "lesswrong", - "name": "sequences", - "description": "List post collections", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "author" - ], - "type": "js", - "modulePath": "plugins/lesswrong/sequences.js", - "sourceFile": "plugins/lesswrong/sequences.js" - }, - { - "site": "lesswrong", - "name": "shortform", - "description": "Quick takes / shortform posts", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/lesswrong/shortform.js", - "sourceFile": "plugins/lesswrong/shortform.js" - }, - { - "site": "lesswrong", - "name": "tag", - "description": "Posts by tag", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "tag", - "type": "string", - "required": true, - "positional": true, - "help": "Tag slug or name" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/lesswrong/tag.js", - "sourceFile": "plugins/lesswrong/tag.js" - }, - { - "site": "lesswrong", - "name": "tags", - "description": "List popular tags", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "name", - "posts" - ], - "type": "js", - "modulePath": "plugins/lesswrong/tags.js", - "sourceFile": "plugins/lesswrong/tags.js" - }, - { - "site": "lesswrong", - "name": "top", - "description": "Top all-time", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/lesswrong/top.js", - "sourceFile": "plugins/lesswrong/top.js" - }, - { - "site": "lesswrong", - "name": "top-month", - "description": "Top this month", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/lesswrong/top-month.js", - "sourceFile": "plugins/lesswrong/top-month.js" - }, - { - "site": "lesswrong", - "name": "top-week", - "description": "Top this week", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/lesswrong/top-week.js", - "sourceFile": "plugins/lesswrong/top-week.js" - }, - { - "site": "lesswrong", - "name": "top-year", - "description": "Top this year", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "author", - "karma", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/lesswrong/top-year.js", - "sourceFile": "plugins/lesswrong/top-year.js" - }, - { - "site": "lesswrong", - "name": "user", - "description": "User profile", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "LessWrong username or slug" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "plugins/lesswrong/user.js", - "sourceFile": "plugins/lesswrong/user.js" - }, - { - "site": "lesswrong", - "name": "user-posts", - "description": "List a user's posts", - "access": "read", - "domain": "www.lesswrong.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "LessWrong username or slug" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "karma", - "comments", - "date", - "url" - ], - "type": "js", - "modulePath": "plugins/lesswrong/user-posts.js", - "sourceFile": "plugins/lesswrong/user-posts.js" - }, - { - "site": "lichess", - "name": "top", - "description": "Top-N Lichess leaderboard for a perf type (bullet/blitz/rapid/classical/...)", - "access": "read", - "domain": "lichess.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "perf", - "type": "str", - "required": true, - "positional": true, - "help": "Perf type (bullet, blitz, rapid, classical, ultraBullet, chess960, ...)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Top-N rows (1-200)" - } - ], - "columns": [ - "rank", - "username", - "id", - "title", - "rating", - "progress", - "patron", - "url" - ], - "type": "js", - "modulePath": "plugins/lichess/top.js", - "sourceFile": "plugins/lichess/top.js" - }, - { - "site": "lichess", - "name": "user", - "description": "Fetch a Lichess player profile by username (rating, perfs, counts)", - "access": "read", - "domain": "lichess.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Lichess username (case-insensitive)" - } - ], - "columns": [ - "username", - "id", - "title", - "patron", - "online", - "tosViolation", - "createdAt", - "seenAt", - "gamesAll", - "gamesWin", - "gamesLoss", - "gamesDraw", - "topPerfName", - "topPerfRating", - "topPerfGames", - "fideRating", - "country", - "bio", - "url" - ], - "type": "js", - "modulePath": "plugins/lichess/user.js", - "sourceFile": "plugins/lichess/user.js" - }, - { - "site": "linkedin", - "name": "company", - "description": "Read a LinkedIn company page: industry, size, HQ, founded, website, followers, and about text", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "company", - "type": "string", - "required": true, - "positional": true, - "help": "Company universal name, /company/ path, or full URL" - } - ], - "columns": [ - "name", - "industry", - "size", - "headquarters", - "founded", - "website", - "specialties", - "followers", - "about", - "url" - ], - "type": "js", - "modulePath": "plugins/linkedin/company.js", - "sourceFile": "plugins/linkedin/company.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "connect", - "description": "Fail-closed LinkedIn connection request sender that verifies the exact profile before optionally sending a note", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": true, - "positional": true, - "help": "Exact LinkedIn profile URL to open and verify" - }, - { - "name": "expected-name", - "type": "string", - "required": true, - "help": "Expected visible profile name" - }, - { - "name": "note", - "type": "string", - "default": "", - "required": false, - "help": "Optional connection note, max 300 chars" - }, - { - "name": "send", - "type": "bool", - "default": false, - "required": false, - "help": "Actually click Send. Default is dry-run verification only." - } - ], - "columns": [ - "status", - "recipient", - "reason", - "profile_url", - "note_chars", - "connectable", - "delivery_verified", - "matched_invitation_name", - "matched_invitation_url", - "actualValue", - "blockReason", - "expectedValue", - "observedUrl", - "safety" - ], - "type": "js", - "modulePath": "plugins/linkedin/connect.js", - "sourceFile": "plugins/linkedin/connect.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "connections", - "description": "List your LinkedIn first-degree connections with names, headlines, and profile URLs", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of connections to return (max 500)" - } - ], - "columns": [ - "rank", - "name", - "occupation", - "public_id", - "connected_at", - "url" - ], - "type": "js", - "modulePath": "plugins/linkedin/connections.js", - "sourceFile": "plugins/linkedin/connections.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "inbox", - "description": "List LinkedIn messaging inbox conversations and unread messages", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 40, - "required": false, - "help": "Maximum conversations to return (1-100)" - }, - { - "name": "unread-only", - "type": "bool", - "default": false, - "required": false, - "help": "Return only conversations with unread messages" - } - ], - "columns": [ - "rank", - "thread_url", - "thread_id", - "person_name", - "last_message_preview", - "unread", - "counterparty_type", - "category", - "timestamp" - ], - "type": "js", - "modulePath": "plugins/linkedin/inbox.js", - "sourceFile": "plugins/linkedin/inbox.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "job-detail", - "description": "Read one LinkedIn job page with description, apply URL, workplace type, applicants, and company metadata", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "job-url", - "type": "string", - "required": true, - "positional": true, - "help": "Exact LinkedIn job URL, e.g. https://www.linkedin.com/jobs/view/123/" - } - ], - "columns": [ - "title", - "company", - "location", - "workplace_type", - "job_type", - "applicants", - "listed", - "apply_url", - "company_url", - "url", - "description" - ], - "type": "js", - "modulePath": "plugins/linkedin/job-detail.js", - "sourceFile": "plugins/linkedin/job-detail.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "jobs-preferences", - "description": "Read visible LinkedIn Jobs preferences and alert settings without changing them", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "open_to_work", - "job_titles", - "locations", - "job_alerts", - "preferences_url", - "alerts_url", - "raw_preferences" - ], - "type": "js", - "modulePath": "plugins/linkedin/jobs-preferences.js", - "sourceFile": "plugins/linkedin/jobs-preferences.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "login", - "description": "Open linkedin login", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "public_id", - "plain_id", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/linkedin/auth.js", - "sourceFile": "plugins/linkedin/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "linkedin", - "name": "people-search", - "description": "Search standard LinkedIn (not Sales Navigator) for people by keyword. Each invocation consumes against LinkedIn's monthly Commercial Use Limit on people search; throttle accordingly.", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "keywords", - "type": "string", - "required": true, - "positional": true, - "help": "People search keywords, e.g. \"site reliability engineer berlin\"" - }, - { - "name": "limit", - "type": "int", - "default": 5, - "required": false, - "help": "Maximum people to return (1-10); each query counts toward LinkedIn's monthly CUL" - } - ], - "columns": [ - "rank", - "name", - "headline", - "location", - "profile_url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/linkedin/people-search.js", - "sourceFile": "plugins/linkedin/people-search.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "post-analytics", - "description": "Summarize raw visible LinkedIn post counters without custom scoring or classification", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Maximum posts to summarize (1-100)" - } - ], - "columns": [ - "posts_analyzed", - "total_reactions", - "total_comments", - "total_reposts", - "total_impressions", - "posts_with_media", - "posts_with_urls", - "latest_posted_at", - "latest_reactions", - "latest_comments", - "latest_reposts", - "latest_impressions", - "latest_url" - ], - "type": "js", - "modulePath": "plugins/linkedin/post-analytics.js", - "sourceFile": "plugins/linkedin/post-analytics.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "post-comments", - "description": "List unique commenters and reply authors from one exact LinkedIn post URL", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-url", - "type": "string", - "required": true, - "positional": true, - "help": "Exact LinkedIn post URL" - }, - { - "name": "limit", - "type": "int", - "required": false, - "help": "Maximum unique commenters to return; omit to fetch all" - } - ], - "columns": [ - "rank", - "name", - "headline", - "profile_url", - "comment_count", - "sample_comment", - "commented_at", - "source_post" - ], - "type": "js", - "modulePath": "plugins/linkedin/post-comments.js", - "sourceFile": "plugins/linkedin/post-comments.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "posts", - "description": "Export visible posts from a LinkedIn profile activity page with engagement metrics", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum posts to return (1-100)" - } - ], - "columns": [ - "rank", - "author", - "posted_at", - "body", - "reactions", - "comments", - "reposts", - "impressions", - "media", - "media_urls", - "url", - "raw_text" - ], - "type": "js", - "modulePath": "plugins/linkedin/posts.js", - "sourceFile": "plugins/linkedin/posts.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "profile-analytics", - "description": "Read visible LinkedIn profile dashboard metrics such as profile views, post impressions, and search appearances", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - } - ], - "columns": [ - "profile_url", - "profile_views", - "post_impressions", - "search_appearances", - "followers", - "connections", - "raw_analytics" - ], - "type": "js", - "modulePath": "plugins/linkedin/profile-analytics.js", - "sourceFile": "plugins/linkedin/profile-analytics.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "profile-experience", - "description": "Read visible LinkedIn profile experience entries with titles, dates, locations, skills, media, and URLs", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - } - ], - "columns": [ - "rank", - "total_count", - "title", - "employment_type", - "company", - "date_range", - "start_date", - "end_date", - "location", - "location_type", - "description", - "skills", - "media", - "urls", - "skill_url", - "media_url", - "profile_url", - "raw_text" - ], - "type": "js", - "modulePath": "plugins/linkedin/profile-experience.js", - "sourceFile": "plugins/linkedin/profile-experience.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "profile-projects", - "description": "Read visible LinkedIn profile projects with descriptions, dates, skills, media, and URLs", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - } - ], - "columns": [ - "rank", - "title", - "date_range", - "associated_with", - "description", - "skills", - "media", - "urls", - "profile_url", - "raw_text" - ], - "type": "js", - "modulePath": "plugins/linkedin/profile-projects.js", - "sourceFile": "plugins/linkedin/profile-projects.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "profile-read", - "description": "Read visible LinkedIn profile sections: headline, About, experience, education, services, and featured sections", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - } - ], - "columns": [ - "profile_url", - "name", - "headline", - "location", - "about", - "about_character_count", - "about_skills", - "experience", - "education", - "services", - "featured" - ], - "type": "js", - "modulePath": "plugins/linkedin/profile-read.js", - "sourceFile": "plugins/linkedin/profile-read.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "safe-send", - "description": "Fail-closed LinkedIn message sender that verifies exact thread, recipient, and latest message before filling/sending", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "thread-url", - "type": "str", - "required": true, - "help": "Exact LinkedIn messaging thread URL to open and verify" - }, - { - "name": "expected-name", - "type": "str", - "required": true, - "help": "Expected visible recipient name in the active thread header" - }, - { - "name": "message", - "type": "str", - "required": true, - "help": "Message body to send or dry-run" - }, - { - "name": "expected-last-text", - "type": "str", - "required": false, - "help": "Substring expected in the currently visible latest conversation context" - }, - { - "name": "expected-last-hash", - "type": "str", - "required": false, - "help": "SHA-256 hash of expected latest visible message text" - }, - { - "name": "send", - "type": "bool", - "default": false, - "required": false, - "help": "Actually click Send. Default is dry-run verification only." - }, - { - "name": "screenshot", - "type": "bool", - "default": false, - "required": false, - "help": "Capture a screenshot during verification" - } - ], - "columns": [ - "status", - "recipient", - "reason", - "thread_url", - "message_chars", - "screenshot" - ], - "type": "js", - "modulePath": "plugins/linkedin/safe-send.js", - "sourceFile": "plugins/linkedin/safe-send.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "salesnav-inbox", - "description": "List LinkedIn Sales Navigator message conversations with API pagination", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "limit", - "type": "number", - "default": 40, - "required": false, - "help": "Maximum conversations to return (1-500)" - }, - { - "name": "max-pages", - "type": "number", - "default": 30, - "required": false, - "help": "Maximum Sales Navigator API pages to fetch" - }, - { - "name": "unread-only", - "type": "bool", - "default": false, - "required": false, - "help": "Return only unread conversations" - } - ], - "columns": [ - "rank", - "thread_id", - "thread_url", - "person_name", - "last_message_snippet", - "last_activity_time", - "unread", - "unread_count", - "total_message_count", - "archived", - "participants", - "next_page_starts_at" - ], - "type": "js", - "modulePath": "plugins/linkedin/salesnav-inbox.js", - "sourceFile": "plugins/linkedin/salesnav-inbox.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "salesnav-message", - "description": "Send or dry-run a LinkedIn Sales Navigator InMail to a lead using the Sales Navigator messaging API", - "access": "write", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "recipient", - "type": "string", - "required": true, - "positional": true, - "help": "Sales Navigator lead URL, LinkedIn /in/ URL from salesnav-search, or urn:li:fs_salesProfile:(...)" - }, - { - "name": "subject", - "type": "string", - "required": true, - "help": "InMail subject" - }, - { - "name": "body", - "type": "string", - "required": true, - "help": "InMail body" - }, - { - "name": "send", - "type": "bool", - "default": false, - "required": false, - "help": "Actually send the InMail. Default is dry-run validation only." - }, - { - "name": "copy-to-crm", - "type": "bool", - "default": false, - "required": false, - "help": "Set Sales Navigator copyToCrm on the message request" - } - ], - "columns": [ - "status", - "recipient", - "title", - "company", - "credits_remaining", - "credits_before", - "credits_after", - "sent_in_salesnav", - "message_chars", - "subject_chars", - "recipient_urn", - "degree", - "inmail_restriction", - "open_link" - ], - "type": "js", - "modulePath": "plugins/linkedin/salesnav-message.js", - "sourceFile": "plugins/linkedin/salesnav-message.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "salesnav-search", - "description": "Search LinkedIn Sales Navigator for people leads by keyword", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "keywords", - "type": "string", - "required": true, - "positional": true, - "help": "People search keywords, e.g. \"quality manager food manufacturing\"" - }, - { - "name": "limit", - "type": "number", - "default": 25, - "required": false, - "help": "Maximum leads to return (1-500, fetched 25 per request)" - } - ], - "columns": [ - "rank", - "name", - "title", - "company", - "location", - "degree", - "profile_url", - "lead_url", - "recipient_urn" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/linkedin/salesnav-search.js", - "sourceFile": "plugins/linkedin/salesnav-search.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "salesnav-thread", - "description": "Return full Sales Navigator message history for a thread id, Sales Navigator inbox URL, lead URL, recipient urn, or exact recipient name", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "thread-or-recipient", - "type": "string", - "required": true, - "positional": true, - "help": "Sales Navigator inbox URL/thread id, Sales Navigator lead URL, recipient urn, or exact participant name" - }, - { - "name": "limit", - "type": "number", - "default": 200, - "required": false, - "help": "Maximum messages to return (1-500)" - }, - { - "name": "max-pages", - "type": "number", - "default": 30, - "required": false, - "help": "Maximum inbox pages to scan when resolving a recipient" - } - ], - "columns": [ - "index", - "thread_id", - "thread_url", - "sender", - "text", - "timestamp", - "subject", - "message_id", - "sender_urn", - "delivered_at", - "type", - "total_message_count" - ], - "type": "js", - "modulePath": "plugins/linkedin/salesnav-thread.js", - "sourceFile": "plugins/linkedin/salesnav-thread.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "search", - "description": "Search LinkedIn jobs", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "string", - "required": true, - "positional": true, - "help": "Job search keywords" - }, - { - "name": "location", - "type": "string", - "required": false, - "help": "Location text such as San Francisco Bay Area" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of jobs to return (max 100)" - }, - { - "name": "start", - "type": "int", - "default": 0, - "required": false, - "help": "Result offset for pagination" - }, - { - "name": "details", - "type": "bool", - "default": false, - "required": false, - "help": "Include full job description and apply URL (slower)" - }, - { - "name": "company", - "type": "string", - "required": false, - "help": "Comma-separated company names or LinkedIn company IDs" - }, - { - "name": "experience-level", - "type": "string", - "required": false, - "help": "Comma-separated: internship, entry, associate, mid-senior, director, executive" - }, - { - "name": "job-type", - "type": "string", - "required": false, - "help": "Comma-separated: full-time, part-time, contract, temporary, volunteer, internship, other" - }, - { - "name": "date-posted", - "type": "string", - "required": false, - "help": "One of: any, month, week, 24h" - }, - { - "name": "remote", - "type": "string", - "required": false, - "help": "Comma-separated: on-site, hybrid, remote" - } - ], - "columns": [ - "rank", - "title", - "company", - "location", - "listed", - "salary", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/linkedin/search.js", - "sourceFile": "plugins/linkedin/search.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "sent-invitations", - "description": "List pending LinkedIn sent invitations for CRM reconciliation", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "rank", - "name", - "profile_url", - "invited_date_text" - ], - "type": "js", - "modulePath": "plugins/linkedin/sent-invitations.js", - "sourceFile": "plugins/linkedin/sent-invitations.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "services-read", - "description": "Read LinkedIn Services page details including services, overview, availability, pricing, and media titles/descriptions", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "profile-url", - "type": "string", - "required": false, - "help": "LinkedIn /in// profile URL. Defaults to /in/me/." - }, - { - "name": "services-url", - "type": "string", - "required": false, - "help": "LinkedIn /services/page// URL. If omitted, it is discovered from the profile." - } - ], - "columns": [ - "service_url", - "page_title", - "overview", - "availability", - "work_locations", - "pricing", - "services_provided", - "services_count", - "media", - "media_count", - "messages", - "reviews_visibility" - ], - "type": "js", - "modulePath": "plugins/linkedin/services-read.js", - "sourceFile": "plugins/linkedin/services-read.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "thread-snapshot", - "description": "Load a LinkedIn messaging thread, scroll for available history, and return a full context snapshot", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "thread-url", - "type": "str", - "required": true, - "help": "Exact LinkedIn messaging thread URL to open and snapshot" - }, - { - "name": "max-scrolls", - "type": "number", - "default": 30, - "required": false, - "help": "Maximum upward scroll attempts to load older messages" - }, - { - "name": "json", - "type": "bool", - "default": false, - "required": false, - "help": "Return only JSON snapshot string in the snapshot_json field" - } - ], - "columns": [ - "thread_url", - "recipient", - "message_count", - "latest_text", - "snapshot_json" - ], - "type": "js", - "modulePath": "plugins/linkedin/thread-snapshot.js", - "sourceFile": "plugins/linkedin/thread-snapshot.js", - "navigateBefore": true - }, - { - "site": "linkedin", - "name": "timeline", - "description": "Read LinkedIn home timeline posts", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return (max 100)" - } - ], - "columns": [ - "rank", - "author", - "author_url", - "headline", - "text", - "posted_at", - "reactions", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/linkedin/timeline.js", - "sourceFile": "plugins/linkedin/timeline.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin", - "name": "whoami", - "description": "Show the current logged-in linkedin account", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "public_id", - "plain_id", - "name" - ], - "type": "js", - "modulePath": "plugins/linkedin/auth.js", - "sourceFile": "plugins/linkedin/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "linkedin-learning", - "name": "course", - "description": "Get LinkedIn Learning course detail by slug or course URL", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "slug", - "type": "string", - "required": true, - "positional": true, - "help": "Course slug (e.g. agentic-ai-build-your-first-agentic-ai-system) or full /learning/ URL" - } - ], - "columns": [ - "title", - "slug", - "description", - "difficulty", - "duration_sec", - "videos_count", - "rating", - "rating_count", - "released", - "url" - ], - "type": "js", - "modulePath": "plugins/linkedin-learning/course.js", - "sourceFile": "plugins/linkedin-learning/course.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin-learning", - "name": "login", - "description": "Open linkedin-learning login", - "access": "write", - "domain": "linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "public_id", - "plain_id", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/linkedin-learning/auth.js", - "sourceFile": "plugins/linkedin-learning/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "linkedin-learning", - "name": "search", - "description": "Search LinkedIn Learning courses, videos, and learning paths by keyword", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "keywords", - "type": "string", - "required": true, - "positional": true, - "help": "Search keywords, e.g. \"AI agent\"" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum results to return (1-50)" - } - ], - "columns": [ - "rank", - "type", - "title", - "instructor", - "difficulty", - "duration_sec", - "rating", - "rating_count", - "viewers", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/linkedin-learning/search.js", - "sourceFile": "plugins/linkedin-learning/search.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin-learning", - "name": "trending", - "description": "Browse LinkedIn Learning recommended courses across personalized carousels", - "access": "read", - "domain": "www.linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum results to return (1-50)" - } - ], - "columns": [ - "rank", - "group", - "type", - "title", - "difficulty", - "viewers", - "url" - ], - "type": "js", - "modulePath": "plugins/linkedin-learning/trending.js", - "sourceFile": "plugins/linkedin-learning/trending.js", - "navigateBefore": "https://www.linkedin.com" - }, - { - "site": "linkedin-learning", - "name": "whoami", - "description": "Show the current logged-in linkedin-learning account", - "access": "read", - "domain": "linkedin.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "public_id", - "plain_id", - "name" - ], - "type": "js", - "modulePath": "plugins/linkedin-learning/auth.js", - "sourceFile": "plugins/linkedin-learning/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "lobsters", - "name": "active", - "description": "Lobste.rs most active discussions", - "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "url" - ], - "type": "js", - "modulePath": "plugins/lobsters/active.js", - "sourceFile": "plugins/lobsters/active.js" - }, - { - "site": "lobsters", - "name": "domain", - "description": "Lobste.rs stories submitted from a specific domain", - "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "domain", - "type": "str", - "required": true, - "positional": true, - "help": "Source domain (e.g. github.com, arxiv.org, blog.cloudflare.com)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories (1-25 — single page)" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "submission_url", - "comments_url" - ], - "type": "js", - "modulePath": "plugins/lobsters/domain.js", - "sourceFile": "plugins/lobsters/domain.js" - }, - { - "site": "lobsters", - "name": "hot", - "description": "Lobste.rs hottest stories", - "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "url" - ], - "type": "js", - "modulePath": "plugins/lobsters/hot.js", - "sourceFile": "plugins/lobsters/hot.js" - }, - { - "site": "lobsters", - "name": "newest", - "description": "Lobste.rs newest stories", - "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "url" - ], - "type": "js", - "modulePath": "plugins/lobsters/newest.js", - "sourceFile": "plugins/lobsters/newest.js" - }, - { - "site": "lobsters", - "name": "read", - "description": "Read a Lobste.rs story and its comment tree", - "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Lobste.rs short_id (e.g. 6cmh6h)" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max top-level comments" - }, - { - "name": "depth", - "type": "int", - "default": 2, - "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" - }, - { - "name": "replies", - "type": "int", - "default": 5, - "required": false, - "help": "Max replies shown per comment at each level" - }, - { - "name": "max-length", - "type": "int", - "default": 2000, - "required": false, - "help": "Max characters per comment body (min 100)" - } - ], - "columns": [ - "type", - "author", - "score", - "text" - ], - "type": "js", - "modulePath": "plugins/lobsters/read.js", - "sourceFile": "plugins/lobsters/read.js" - }, - { - "site": "lobsters", - "name": "tag", - "description": "Lobste.rs stories by tag", - "access": "read", - "domain": "lobste.rs", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "tag", - "type": "str", - "required": true, - "positional": true, - "help": "Tag name (e.g. programming, rust, security, ai)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of stories" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "author", - "comments", - "created_at", - "tags", - "url" - ], - "type": "js", - "modulePath": "plugins/lobsters/tag.js", - "sourceFile": "plugins/lobsters/tag.js" - }, - { - "site": "luma", - "name": "create-event", - "description": "Create a free single-session Luma event", - "access": "write", - "domain": "luma.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "start", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "end", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "timezone", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "calendar", - "type": "str", - "required": false, - "help": "" - }, - { - "name": "description", - "type": "str", - "required": false, - "help": "" - }, - { - "name": "location", - "type": "str", - "required": false, - "help": "" - }, - { - "name": "virtual-url", - "type": "str", - "required": false, - "help": "" - }, - { - "name": "visibility", - "type": "str", - "default": "public", - "required": false, - "help": "", - "choices": [ - "public", - "private", - "members-only" - ] - }, - { - "name": "capacity", - "type": "int", - "required": false, - "help": "" - }, - { - "name": "require-approval", - "type": "boolean", - "default": false, - "required": false, - "help": "" - }, - { - "name": "confirm", - "type": "boolean", - "default": false, - "required": false, - "help": "" - } - ], - "columns": [ - "eventId", - "name", - "startsAt", - "endsAt", - "timezone", - "visibility", - "requireApproval", - "capacity", - "eventUrl", - "manageUrl" - ], - "type": "js", - "modulePath": "plugins/luma/create-event.js", - "sourceFile": "plugins/luma/create-event.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true - }, - { - "site": "luma", - "name": "events", - "description": "List upcoming or past Luma events managed by the logged-in account", - "access": "read", - "example": "webcmd luma events --period future --limit 25 -f json", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "period", - "type": "str", - "default": "future", - "required": false, - "help": "List future or past events", - "choices": [ - "future", - "past" - ] - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Maximum number of events to request" - } - ], - "columns": [ - "eventId", - "name", - "startsAt", - "endsAt", - "timezone", - "guestCount", - "requireApproval", - "managerLevel", - "location", - "manageUrl", - "eventUrl" - ], - "type": "js", - "modulePath": "plugins/luma/events.js", - "sourceFile": "plugins/luma/events.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "luma", - "name": "guests", - "description": "List guests and all custom registration answers for a managed Luma event", - "access": "read", - "example": "webcmd luma guests evt-abc --status pending_approval --limit 100 -f json", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "eventId", - "type": "str", - "required": true, - "positional": true, - "help": "Luma event ID returned by webcmd luma events" - }, - { - "name": "status", - "type": "str", - "default": "all", - "required": false, - "help": "Filter by guest approval status", - "choices": [ - "all", - "approved", - "pending_approval", - "declined", - "waitlist", - "invited" - ] - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Maximum matching guests to return" - }, - { - "name": "query", - "type": "str", - "default": "", - "required": false, - "help": "Search text passed to Luma guest search" - } - ], - "columns": [ - "eventId", - "guestId", - "userId", - "name", - "email", - "phone", - "status", - "registeredAt", - "profiles", - "answers" - ], - "type": "js", - "modulePath": "plugins/luma/guests.js", - "sourceFile": "plugins/luma/guests.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "luma", - "name": "login", - "description": "Open Luma sign in", - "access": "write", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "name", - "email", - "url", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/luma/auth.js", - "sourceFile": "plugins/luma/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "luma", - "name": "set-registration-questions", - "description": "Append or replace custom registration questions on a managed Luma event", - "access": "write", - "domain": "luma.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "eventId", - "type": "str", - "required": true, - "positional": true, - "help": "" - }, - { - "name": "questions-file", - "type": "str", - "required": true, - "help": "" - }, - { - "name": "mode", - "type": "str", - "required": true, - "help": "", - "choices": [ - "append", - "replace" - ] - }, - { - "name": "confirm", - "type": "boolean", - "default": false, - "required": false, - "help": "" - } - ], - "columns": [ - "eventId", - "mode", - "previousCount", - "questionCount", - "questions", - "registrationUrl" - ], - "type": "js", - "modulePath": "plugins/luma/set-registration-questions.js", - "sourceFile": "plugins/luma/set-registration-questions.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true - }, - { - "site": "luma", - "name": "update-guest-status", - "description": "Approve or decline a pending Luma guest after explicit confirmation", - "access": "write", - "example": "webcmd luma update-guest-status evt-abc gst-abc --status approved --confirm true -f json", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "eventId", - "type": "str", - "required": true, - "positional": true, - "help": "Luma event ID returned by webcmd luma events" - }, - { - "name": "guestId", - "type": "str", - "required": true, - "positional": true, - "help": "Luma guest ID returned by webcmd luma guests" - }, - { - "name": "status", - "type": "str", - "required": true, - "help": "New guest status", - "choices": [ - "approved", - "declined" - ] - }, - { - "name": "suppress-email", - "type": "boolean", - "default": false, - "required": false, - "help": "Set true to prevent Luma from emailing the guest" - }, - { - "name": "confirm", - "type": "boolean", - "default": false, - "required": false, - "help": "Required. Set --confirm true to change the real guest status" - } - ], - "columns": [ - "eventId", - "guestId", - "name", - "email", - "previousStatus", - "status", - "emailSuppressed" - ], - "type": "js", - "modulePath": "plugins/luma/update-guest-status.js", - "sourceFile": "plugins/luma/update-guest-status.js", - "navigateBefore": false, - "siteSession": "persistent", - "freshPage": true - }, - { - "site": "luma", - "name": "whoami", - "description": "Show the current logged-in Luma account", - "access": "read", - "domain": "luma.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name", - "email", - "url" - ], - "type": "js", - "modulePath": "plugins/luma/auth.js", - "sourceFile": "plugins/luma/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "connectors", - "description": "List available Manus connectors (integrations).", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max connectors to return" - } - ], - "columns": [ - "UID", - "Name", - "Brief" - ], - "type": "js", - "modulePath": "plugins/manus/connectors.js", - "sourceFile": "plugins/manus/connectors.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "credits", - "description": "Show Manus credit balance and refresh details.", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/manus/credits.js", - "sourceFile": "plugins/manus/credits.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "list", - "description": "List Manus sessions (tasks).", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max sessions to return" - }, - { - "name": "archived", - "type": "bool", - "default": false, - "required": false, - "help": "Include archived sessions" - } - ], - "columns": [ - "id", - "Title", - "Status", - "Last Message", - "Last Updated", - "Credits" - ], - "type": "js", - "modulePath": "plugins/manus/list.js", - "sourceFile": "plugins/manus/list.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "login", - "description": "Open manus login", - "access": "write", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/manus/auth.js", - "sourceFile": "plugins/manus/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "read", - "description": "Show details for a specific Manus session.", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "uid", - "type": "str", - "required": true, - "positional": true, - "help": "Session UID" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/manus/read.js", - "sourceFile": "plugins/manus/read.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "skills", - "description": "List Manus skills (user-added and system).", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "ID", - "Name", - "Description", - "Source" - ], - "type": "js", - "modulePath": "plugins/manus/skills.js", - "sourceFile": "plugins/manus/skills.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "status", - "description": "Show current Manus user profile and credit summary.", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/manus/status.js", - "sourceFile": "plugins/manus/status.js", - "navigateBefore": true, - "siteSession": "persistent" - }, - { - "site": "manus", - "name": "whoami", - "description": "Show the current logged-in manus account", - "access": "read", - "domain": "manus.im", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "plugins/manus/auth.js", - "sourceFile": "plugins/manus/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "maven", - "name": "artifact", - "description": "Fetch a Maven Central artifact's version history (groupId:artifactId[:version])", - "access": "read", - "domain": "search.maven.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "coordinate", - "type": "str", - "required": true, - "positional": true, - "help": "Maven coord \"groupId:artifactId\" or \"groupId:artifactId:version\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max versions (1-200, ignored when version is pinned)" - } - ], - "columns": [ - "groupId", - "artifactId", - "version", - "packaging", - "publishedAt", - "tags", - "url" - ], - "type": "js", - "modulePath": "plugins/maven/artifact.js", - "sourceFile": "plugins/maven/artifact.js" - }, - { - "site": "maven", - "name": "search", - "description": "Search Maven Central by keyword (artifact name, groupId, tag)", - "access": "read", - "domain": "search.maven.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"jackson\", \"guava\", \"ai.koog\")" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max artifacts (1-200)" - } - ], - "columns": [ - "rank", - "coordinate", - "groupId", - "artifactId", - "latestVersion", - "packaging", - "versions", - "lastPublished", - "repository", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/maven/search.js", - "sourceFile": "plugins/maven/search.js" - }, - { - "site": "mdn", - "name": "search", - "description": "Search MDN Web Docs by keyword", - "access": "read", - "domain": "developer.mozilla.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"fetch\", \"flexbox\", \"Array.prototype.map\")" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max results (1-50)" - }, - { - "name": "locale", - "type": "str", - "default": "en-US", - "required": false, - "help": "Doc locale (en-US default; de / es / fr / ja / ko / pt-BR / ru / zh-CN / zh-TW)" - } - ], - "columns": [ - "rank", - "title", - "slug", - "locale", - "summary", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/mdn/search.js", - "sourceFile": "plugins/mdn/search.js" - }, - { - "site": "medium", - "name": "feed", - "description": "Medium popular posts Feed", - "access": "read", - "domain": "medium.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "topic", - "type": "str", - "default": "", - "required": false, - "help": "Topic (for example technology, programming, ai)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" - } - ], - "columns": [ - "rank", - "title", - "author", - "date", - "readTime", - "claps" - ], - "type": "js", - "modulePath": "plugins/medium/feed.js", - "sourceFile": "plugins/medium/feed.js", - "navigateBefore": "https://medium.com" - }, - { - "site": "medium", - "name": "search", - "description": "Search Medium posts", - "access": "read", - "domain": "medium.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" - } - ], - "columns": [ - "rank", - "title", - "author", - "date", - "readTime", - "claps", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/medium/search.js", - "sourceFile": "plugins/medium/search.js", - "navigateBefore": "https://medium.com" - }, - { - "site": "medium", - "name": "tag", - "description": "Latest Medium articles tagged with a given keyword (RSS feed)", - "access": "read", - "domain": "medium.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "tag", - "type": "str", - "required": true, - "positional": true, - "help": "Lowercase tag slug (e.g. \"programming\", \"machine-learning\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max articles (1-25 — single RSS page)" - } - ], - "columns": [ - "rank", - "title", - "author", - "description", - "categories", - "published", - "url" - ], - "type": "js", - "modulePath": "plugins/medium/tag.js", - "sourceFile": "plugins/medium/tag.js" - }, - { - "site": "medium", - "name": "user", - "description": "Get Medium user posts", - "access": "read", - "domain": "medium.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "Medium username(for example @username or username)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" - } - ], - "columns": [ - "rank", - "title", - "date", - "readTime", - "claps", - "url" - ], - "type": "js", - "modulePath": "plugins/medium/user.js", - "sourceFile": "plugins/medium/user.js", - "navigateBefore": "https://medium.com" - }, - { - "site": "mercury", - "name": "check-login", - "description": "Open Mercury reimbursements and report whether the active browser profile is logged in", - "access": "read", - "example": "webcmd --profile mercury check-login -f json", - "domain": "app.mercury.com", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "status", - "loggedIn", - "url", - "hasSubmitExpense", - "hasReimbursements", - "title" - ], - "type": "js", - "modulePath": "plugins/mercury/check-login.js", - "sourceFile": "plugins/mercury/check-login.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "mercury", - "name": "reimbursement-draft", - "description": "Create a Mercury reimbursement draft from a local receipt, correct OCR fields, and stop at Review", - "access": "write", - "example": "webcmd --profile mercury reimbursement-draft --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", - "domain": "app.mercury.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "receipt", - "type": "str", - "required": true, - "help": "Local receipt/proof file path", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp", - "application/pdf" - ], - "maxBytes": 26214400 - } - }, - { - "name": "amount", - "type": "str", - "required": true, - "help": "Original-currency amount, e.g. 140.00" - }, - { - "name": "currency", - "type": "str", - "default": "CNY", - "required": false, - "help": "Original currency code" - }, - { - "name": "date", - "type": "str", - "required": true, - "help": "Expense date as YYYY-MM-DD" - }, - { - "name": "merchant", - "type": "str", - "required": true, - "help": "Merchant shown on the reimbursement" - }, - { - "name": "category", - "type": "str", - "default": "Marketing & Advertising", - "required": false, - "help": "Mercury expense category" - }, - { - "name": "notes", - "type": "str", - "required": true, - "help": "Business purpose / reimbursement notes" - }, - { - "name": "ocr-wait-seconds", - "type": "str", - "default": "8", - "required": false, - "help": "Seconds to wait after receipt upload before correcting OCR-overwritten fields" - }, - { - "name": "close-after-review", - "type": "boolean", - "default": false, - "required": false, - "help": "Close the Review dialog after verification; final Submit is still never clicked" - } - ], - "columns": [ - "status", - "url", - "receipt", - "uploaded", - "fieldsTouched", - "reviewReady", - "submitBlocked", - "warnings" - ], - "type": "js", - "modulePath": "plugins/mercury/reimbursement-draft.js", - "sourceFile": "plugins/mercury/reimbursement-draft.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "mercury", - "name": "reimbursement-plan", - "description": "Validate Mercury reimbursement inputs and print the draft plan without opening a browser", - "access": "read", - "example": "webcmd mercury reimbursement-plan --receipt /tmp/receipt.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant \"Example Merchant\" --category \"Marketing & Advertising\" --notes \"Example business purpose.\" -f json", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "receipt", - "type": "str", - "required": true, - "help": "Local receipt/proof file path" - }, - { - "name": "amount", - "type": "str", - "required": true, - "help": "Original-currency amount, e.g. 140.00" - }, - { - "name": "currency", - "type": "str", - "default": "CNY", - "required": false, - "help": "Original currency code" - }, - { - "name": "date", - "type": "str", - "required": true, - "help": "Expense date as YYYY-MM-DD" - }, - { - "name": "merchant", - "type": "str", - "required": true, - "help": "Merchant shown on the reimbursement" - }, - { - "name": "category", - "type": "str", - "default": "Marketing & Advertising", - "required": false, - "help": "Mercury expense category" - }, - { - "name": "notes", - "type": "str", - "required": true, - "help": "Business purpose / reimbursement notes" - }, - { - "name": "ocr-wait-seconds", - "type": "str", - "default": "8", - "required": false, - "help": "Seconds the draft command waits after receipt upload before correcting OCR fields" - }, - { - "name": "close-after-review", - "type": "boolean", - "default": false, - "required": false, - "help": "For draft command: close the Review dialog after verification" - } - ], - "columns": [ - "status", - "receipt", - "amount", - "currency", - "date", - "merchant", - "category", - "notes", - "safety" - ], - "type": "js", - "modulePath": "plugins/mercury/reimbursement-plan.js", - "sourceFile": "plugins/mercury/reimbursement-plan.js" - }, - { - "site": "notebooklm", - "name": "add-source", - "description": "Add a URL, text, or local file source to an existing NotebookLM notebook", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "url", - "type": "str", - "required": false, - "help": "Source URL to add (http/https). Pass exactly one of --url, --content, --file." - }, - { - "name": "content", - "type": "str", - "required": false, - "help": "Raw text content to add as a Text source (max 10 MB)." - }, - { - "name": "file", - "type": "str", - "required": false, - "help": "Local file path to upload as a source (max 52428800 bytes; pdf / txt / md / html / docx / etc.). Uses Google Drive's 3-step resumable upload protocol." - }, - { - "name": "title", - "type": "str", - "required": false, - "help": "Title for the text source (default \"Text Source\"). Ignored for --url and --file." - }, - { - "name": "mime-type", - "type": "str", - "required": false, - "help": "Override the auto-detected MIME type when --file is given." - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually add the remote source to the NotebookLM notebook" - } - ], - "columns": [ - "notebook_id", - "source_id", - "kind", - "identifier", - "notebook_url" - ], - "type": "js", - "modulePath": "plugins/notebooklm/add-source.js", - "sourceFile": "plugins/notebooklm/add-source.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "create", - "description": "Create a new NotebookLM notebook with the given title", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "title", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook title (1-200 chars)" - }, - { - "name": "emoji", - "type": "str", - "required": false, - "help": "Notebook emoji icon (default 📒)" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually create the remote NotebookLM notebook" - } - ], - "columns": [ - "id", - "title", - "emoji", - "url" - ], - "type": "js", - "modulePath": "plugins/notebooklm/create.js", - "sourceFile": "plugins/notebooklm/create.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "current", - "description": "Show metadata for the currently opened NotebookLM notebook tab", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", - "title", - "url", - "source" - ], - "type": "js", - "modulePath": "plugins/notebooklm/current.js", - "sourceFile": "plugins/notebooklm/current.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "generate-audio", - "description": "Trigger an Audio Overview (Deep Dive podcast) generation for a NotebookLM notebook, using all of its sources", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually trigger remote NotebookLM audio generation" - } - ], - "columns": [ - "notebook_id", - "audio_id", - "source_count", - "status", - "notebook_url" - ], - "type": "js", - "modulePath": "plugins/notebooklm/generate-audio.js", - "sourceFile": "plugins/notebooklm/generate-audio.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "generate-slides", - "description": "Trigger a Slide Deck (AI presentation) generation for a NotebookLM notebook, using all of its sources", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "length", - "type": "str", - "required": false, - "help": "Slide deck length: 1=Short, 3=Default (default 3)" - }, - { - "name": "language", - "type": "str", - "required": false, - "help": "Language code (default en)" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually trigger remote NotebookLM slide deck generation" - } - ], - "columns": [ - "notebook_id", - "slides_id", - "source_count", - "status", - "notebook_url" - ], - "type": "js", - "modulePath": "plugins/notebooklm/generate-slides.js", - "sourceFile": "plugins/notebooklm/generate-slides.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "get", - "aliases": [ - "metadata" - ], - "description": "Get rich metadata for the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", - "title", - "emoji", - "source_count", - "created_at", - "updated_at", - "url", - "source" - ], - "type": "js", - "modulePath": "plugins/notebooklm/get.js", - "sourceFile": "plugins/notebooklm/get.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "history", - "description": "List NotebookLM conversation history threads in the current notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "thread_id", - "item_count", - "preview", - "source", - "notebook_id", - "url" - ], - "type": "js", - "modulePath": "plugins/notebooklm/history.js", - "sourceFile": "plugins/notebooklm/history.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "list", - "description": "List NotebookLM notebooks via in-page batchexecute RPC in the current logged-in session", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "id", - "is_owner", - "created_at", - "source", - "url" - ], - "type": "js", - "modulePath": "plugins/notebooklm/list.js", - "sourceFile": "plugins/notebooklm/list.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "login", - "description": "Open notebooklm login", - "access": "write", - "domain": "google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "name", - "authuser", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/notebooklm/auth.js", - "sourceFile": "plugins/notebooklm/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "notebooklm", - "name": "note-list", - "aliases": [ - "notes-list" - ], - "description": "List saved notes from the Studio panel of the current NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "created_at", - "source", - "url" - ], - "type": "js", - "modulePath": "plugins/notebooklm/note-list.js", - "sourceFile": "plugins/notebooklm/note-list.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "notes-get", - "description": "Get one note from the current NotebookLM notebook by title from the visible note editor", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "note", - "type": "str", - "required": true, - "positional": true, - "help": "Note title or id from the current notebook" - } - ], - "columns": [ - "title", - "content", - "source", - "url" - ], - "type": "js", - "modulePath": "plugins/notebooklm/notes-get.js", - "sourceFile": "plugins/notebooklm/notes-get.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "open", - "aliases": [ - "select" - ], - "description": "Open one NotebookLM notebook in the adapter session by id or URL", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from list output, or a full NotebookLM notebook URL" - } - ], - "columns": [ - "id", - "title", - "url", - "source" - ], - "type": "js", - "modulePath": "plugins/notebooklm/open.js", - "sourceFile": "plugins/notebooklm/open.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-fulltext", - "description": "Get the extracted fulltext for one source in the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "source", - "type": "str", - "required": true, - "positional": true, - "help": "Source id or title from the current notebook" - } - ], - "columns": [ - "title", - "kind", - "char_count", - "url", - "source" - ], - "type": "js", - "modulePath": "plugins/notebooklm/source-fulltext.js", - "sourceFile": "plugins/notebooklm/source-fulltext.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-get", - "description": "Get one source from the currently opened NotebookLM notebook by id or title", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "source", - "type": "str", - "required": true, - "positional": true, - "help": "Source id or title from the current notebook" - } - ], - "columns": [ - "title", - "id", - "type", - "size", - "created_at", - "updated_at", - "url", - "source" - ], - "type": "js", - "modulePath": "plugins/notebooklm/source-get.js", - "sourceFile": "plugins/notebooklm/source-get.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-guide", - "description": "Get the guide summary and keywords for one source in the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "source", - "type": "str", - "required": true, - "positional": true, - "help": "Source id or title from the current notebook" - } - ], - "columns": [ - "source_id", - "notebook_id", - "title", - "type", - "summary", - "keywords", - "source" - ], - "type": "js", - "modulePath": "plugins/notebooklm/source-guide.js", - "sourceFile": "plugins/notebooklm/source-guide.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "source-list", - "description": "List sources for the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "id", - "type", - "size", - "created_at", - "updated_at", - "url", - "source" - ], - "type": "js", - "modulePath": "plugins/notebooklm/source-list.js", - "sourceFile": "plugins/notebooklm/source-list.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "status", - "description": "Check NotebookLM page availability and login state in the current Chrome session", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "login", - "page", - "url", - "title", - "notebooks" - ], - "type": "js", - "modulePath": "plugins/notebooklm/status.js", - "sourceFile": "plugins/notebooklm/status.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "summary", - "description": "Get the summary block from the currently opened NotebookLM notebook", - "access": "read", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "title", - "summary", - "source", - "url" - ], - "type": "js", - "modulePath": "plugins/notebooklm/summary.js", - "sourceFile": "plugins/notebooklm/summary.js", - "navigateBefore": false - }, - { - "site": "notebooklm", - "name": "whoami", - "description": "Show the current logged-in notebooklm account", - "access": "read", - "domain": "google.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name", - "authuser" - ], - "type": "js", - "modulePath": "plugins/notebooklm/auth.js", - "sourceFile": "plugins/notebooklm/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "notebooklm", - "name": "write-note", - "description": "Create a Studio note in an existing NotebookLM notebook with the given title and Markdown content", - "access": "write", - "domain": "notebooklm.google.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "notebook", - "type": "str", - "required": true, - "positional": true, - "help": "Notebook id from `notebooklm list` or full notebook URL" - }, - { - "name": "title", - "type": "str", - "required": true, - "help": "Note title (1-200 chars)" - }, - { - "name": "content", - "type": "str", - "required": true, - "help": "Note body as Markdown" - }, - { - "name": "execute", - "type": "boolean", - "required": false, - "help": "Actually create the remote NotebookLM note" - } - ], - "columns": [ - "notebook_id", - "note_id", - "title", - "notebook_url" - ], - "type": "js", - "modulePath": "plugins/notebooklm/write-note.js", - "sourceFile": "plugins/notebooklm/write-note.js", - "navigateBefore": false - }, - { - "site": "npm", - "name": "downloads", - "description": "Daily download counts for an npm package over a window", - "access": "read", - "domain": "api.npmjs.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "npm package name (e.g. \"react\", \"@vercel/og\")" - }, - { - "name": "period", - "type": "str", - "default": "last-week", - "required": false, - "help": "last-day / last-week / last-month / last-year, or YYYY-MM-DD:YYYY-MM-DD" - } - ], - "columns": [ - "rank", - "package", - "day", - "downloads" - ], - "type": "js", - "modulePath": "plugins/npm/downloads.js", - "sourceFile": "plugins/npm/downloads.js" - }, - { - "site": "npm", - "name": "package", - "description": "Single npm package metadata (latest version, license, homepage, repository). Use `npm downloads` for stats.", - "access": "read", - "domain": "registry.npmjs.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "npm package name (e.g. \"react\", \"@vercel/og\")" - } - ], - "columns": [ - "name", - "latestVersion", - "description", - "license", - "homepage", - "repository", - "bugs", - "maintainers", - "keywords", - "created", - "modified", - "url" - ], - "type": "js", - "modulePath": "plugins/npm/package.js", - "sourceFile": "plugins/npm/package.js" - }, - { - "site": "npm", - "name": "search", - "description": "Search the public npm registry by keyword", - "access": "read", - "domain": "registry.npmjs.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"react\", \"graphql client\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-250)" - } - ], - "columns": [ - "rank", - "name", - "version", - "description", - "weeklyDownloads", - "dependents", - "license", - "publisher", - "updated", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/npm/search.js", - "sourceFile": "plugins/npm/search.js" - }, - { - "site": "nuget", - "name": "package", - "description": "Full NuGet package version history (catalogEntry per release)", - "access": "read", - "domain": "api.nuget.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "NuGet package id (e.g. \"Newtonsoft.Json\", case-insensitive)" - } - ], - "columns": [ - "rank", - "id", - "version", - "title", - "authors", - "tags", - "language", - "licenseExpression", - "projectUrl", - "published", - "listed", - "url" - ], - "type": "js", - "modulePath": "plugins/nuget/package.js", - "sourceFile": "plugins/nuget/package.js" - }, - { - "site": "nuget", - "name": "search", - "description": "Search NuGet packages by keyword", - "access": "read", - "domain": "api.nuget.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max packages (1-1000)" - }, - { - "name": "prerelease", - "type": "boolean", - "default": false, - "required": false, - "help": "Include prerelease versions" - } - ], - "columns": [ - "rank", - "id", - "version", - "title", - "description", - "authors", - "tags", - "totalDownloads", - "verified", - "projectUrl", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/nuget/search.js", - "sourceFile": "plugins/nuget/search.js" - }, - { - "site": "nvd", - "name": "cve", - "description": "NIST NVD CVE detail (description, CVSS, CWE, KEV flag)", - "access": "read", - "domain": "services.nvd.nist.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "CVE identifier (e.g. \"CVE-2021-44228\")" - } - ], - "columns": [ - "id", - "published", - "lastModified", - "vulnStatus", - "baseScore", - "severity", - "attackVector", - "cwe", - "kevAdded", - "description", - "url" - ], - "type": "js", - "modulePath": "plugins/nvd/cve.js", - "sourceFile": "plugins/nvd/cve.js" - }, - { - "site": "oeis", - "name": "search", - "description": "Search OEIS sequences by keyword or numeric pattern", - "access": "read", - "domain": "oeis.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword or comma-separated terms (e.g. \"fibonacci\", \"1,1,2,3,5,8\")" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max sequences (1-100)" - } - ], - "columns": [ - "rank", - "id", - "name", - "keywords", - "preview", - "author", - "created", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/oeis/search.js", - "sourceFile": "plugins/oeis/search.js" - }, - { - "site": "oeis", - "name": "sequence", - "description": "Full OEIS sequence detail by A-number (terms, name, keywords, formula counts)", - "access": "read", - "domain": "oeis.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "OEIS sequence id (e.g. \"A000045\" for Fibonacci)" - } - ], - "columns": [ - "id", - "name", - "keywords", - "preview", - "termCount", - "offset", - "author", - "created", - "revision", - "commentCount", - "formulaCount", - "referenceCount", - "xrefCount", - "linkCount", - "url" - ], - "type": "js", - "modulePath": "plugins/oeis/sequence.js", - "sourceFile": "plugins/oeis/sequence.js" - }, - { - "site": "openalex", - "name": "search", - "description": "Search OpenAlex Works (papers, books, preprints) by keyword", - "access": "read", - "domain": "api.openalex.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search text (e.g. \"transformers\", \"open access scholarly\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max works (1-200, single OpenAlex page)" - } - ], - "columns": [ - "rank", - "id", - "title", - "year", - "citations", - "firstAuthor", - "venue", - "openAccess", - "type", - "doi", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/openalex/search.js", - "sourceFile": "plugins/openalex/search.js" - }, - { - "site": "openalex", - "name": "work", - "description": "Fetch a single OpenAlex Work (paper / preprint / book) — metadata + abstract", - "access": "read", - "domain": "api.openalex.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "OpenAlex Work id (\"W2741809807\"), DOI (\"10.7717/peerj.4375\"), or full URL" - } - ], - "columns": [ - "id", - "title", - "type", - "year", - "date", - "language", - "authors", - "venue", - "citations", - "openAccess", - "openAccessUrl", - "referencedCount", - "doi", - "abstract", - "url" - ], - "type": "js", - "modulePath": "plugins/openalex/work.js", - "sourceFile": "plugins/openalex/work.js" - }, - { - "site": "openfda", - "name": "drug-label", - "description": "Search FDA-approved drug labels (brand or generic name)", - "access": "read", - "domain": "fda.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Brand or generic drug name (e.g. \"aspirin\", \"lisinopril\")" - }, - { - "name": "limit", - "type": "int", - "default": 5, - "required": false, - "help": "Max rows (1-25, default 5; openFDA caps anonymous tier at 25/page)" - } - ], - "columns": [ - "rank", - "id", - "brandName", - "genericName", - "manufacturer", - "productType", - "route", - "productNdc", - "pharmClass", - "purpose", - "indications", - "warnings", - "dosage", - "effectiveTime" - ], - "type": "js", - "modulePath": "plugins/openfda/drug-label.js", - "sourceFile": "plugins/openfda/drug-label.js" - }, - { - "site": "openfda", - "name": "food-recall", - "description": "FDA food recall and enforcement actions (most recent first)", - "access": "read", - "domain": "fda.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": false, - "help": "Free-text Lucene query (e.g. \"salmonella\", \"listeria\"); default: all recent recalls" - }, - { - "name": "status", - "type": "str", - "required": false, - "help": "Filter by status: \"Ongoing\", \"Completed\", \"Terminated\"" - }, - { - "name": "classification", - "type": "str", - "required": false, - "help": "Filter by class: \"Class I\" (most serious), \"Class II\", \"Class III\"" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max rows (1-100, default 10; openFDA caps anonymous tier at 100/page)" - } - ], - "columns": [ - "rank", - "recallNumber", - "status", - "classification", - "voluntary", - "recallingFirm", - "city", - "state", - "country", - "productDescription", - "reasonForRecall", - "productQuantity", - "distributionPattern", - "reportDate", - "recallInitiationDate", - "terminationDate" - ], - "type": "js", - "modulePath": "plugins/openfda/food-recall.js", - "sourceFile": "plugins/openfda/food-recall.js" - }, - { - "site": "openreview", - "name": "author", - "description": "List OpenReview submissions by an author profile id (newest first)", - "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "profile", - "type": "str", - "required": true, - "positional": true, - "help": "OpenReview profile id (e.g. \"~Yoshua_Bengio1\"). Find it on the author profile URL on openreview.net." - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max submissions (1-1000)" - } - ], - "columns": [ - "rank", - "id", - "title", - "authors", - "venue", - "pdate", - "url" - ], - "type": "js", - "modulePath": "plugins/openreview/author.js", - "sourceFile": "plugins/openreview/author.js" - }, - { - "site": "openreview", - "name": "paper", - "description": "Show full metadata for a single OpenReview paper", - "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "OpenReview note id (e.g. \"5sRnsubyAK\")" - } - ], - "columns": [ - "id", - "title", - "authors", - "keywords", - "venue", - "venueid", - "primary_area", - "abstract", - "pdate", - "pdf", - "url" - ], - "type": "js", - "modulePath": "plugins/openreview/paper.js", - "sourceFile": "plugins/openreview/paper.js" - }, - { - "site": "openreview", - "name": "reviews", - "description": "Show full review thread (paper + reviews + decisions) for an OpenReview forum", - "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "forum", - "type": "str", - "required": true, - "positional": true, - "help": "OpenReview forum id (same as paper id)" - }, - { - "name": "max-length", - "type": "int", - "default": 4000, - "required": false, - "help": "Per-row text truncation (min 200)" - } - ], - "columns": [ - "type", - "author", - "rating", - "confidence", - "text" - ], - "type": "js", - "modulePath": "plugins/openreview/reviews.js", - "sourceFile": "plugins/openreview/reviews.js" - }, - { - "site": "openreview", - "name": "search", - "description": "Search OpenReview papers by free-text query", - "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"diffusion model\")" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max results (max 50)" - } - ], - "columns": [ - "rank", - "id", - "title", - "authors", - "venue", - "pdate", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/openreview/search.js", - "sourceFile": "plugins/openreview/search.js" - }, - { - "site": "openreview", - "name": "venue", - "description": "List papers at an OpenReview venue (e.g. \"ICLR 2024 oral\" or full invitation id)", - "access": "read", - "domain": "openreview.net", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "venue", - "type": "str", - "required": true, - "positional": true, - "help": "Venue name (\"ICLR 2024 oral\") or invitation (\"ICLR.cc/2025/Conference/-/Submission\")" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max results (max 200)" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Pagination offset" - } - ], - "columns": [ - "rank", - "id", - "title", - "authors", - "keywords", - "primary_area", - "pdate", - "pdf", - "url" - ], - "type": "js", - "modulePath": "plugins/openreview/venue.js", - "sourceFile": "plugins/openreview/venue.js" - }, - { - "site": "osv", - "name": "query", - "description": "OSV.dev vulnerabilities affecting a package (optionally pinned to a version)", - "access": "read", - "domain": "osv.dev", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "package", - "type": "string", - "required": true, - "positional": true, - "help": "Package name (e.g. \"lodash\", \"django\")" - }, - { - "name": "ecosystem", - "type": "string", - "required": true, - "help": "OSV ecosystem (npm / PyPI / Go / Maven / NuGet / RubyGems / crates.io / Packagist / ...)" - }, - { - "name": "version", - "type": "string", - "required": false, - "help": "Pin to a specific version (e.g. \"4.17.20\"); omit for all known vulns" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max rows to return (1-200)" - } - ], - "columns": [ - "rank", - "id", - "summary", - "severity", - "aliases", - "published", - "modified", - "affectedPackages", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/osv/query.js", - "sourceFile": "plugins/osv/query.js" - }, - { - "site": "osv", - "name": "vulnerability", - "description": "Single OSV.dev vulnerability detail (severity, affected packages, CVE/GHSA aliases)", - "access": "read", - "domain": "osv.dev", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "OSV vulnerability id (e.g. \"GHSA-29mw-wpgm-hmr9\", \"CVE-2020-28500\")" - } - ], - "columns": [ - "id", - "summary", - "severity", - "aliases", - "published", - "modified", - "affectedPackages", - "cwes", - "referenceCount", - "url" - ], - "type": "js", - "modulePath": "plugins/osv/vulnerability.js", - "sourceFile": "plugins/osv/vulnerability.js" - }, - { - "site": "packagist", - "name": "package", - "description": "Fetch a Packagist package's metadata (version, downloads, license, repo, GitHub stars)", - "access": "read", - "domain": "packagist.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Composer package \"/\" (e.g. \"symfony/console\", \"monolog/monolog\")" - } - ], - "columns": [ - "package", - "version", - "releasedAt", - "license", - "description", - "repository", - "githubStars", - "favers", - "downloads", - "monthlyDownloads", - "dailyDownloads", - "url" - ], - "type": "js", - "modulePath": "plugins/packagist/package.js", - "sourceFile": "plugins/packagist/package.js" - }, - { - "site": "packagist", - "name": "search", - "description": "Search Packagist (PHP / Composer) packages by keyword", - "access": "read", - "domain": "packagist.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"symfony\", \"laravel http\")" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max packages (1-100, single Packagist page)" - } - ], - "columns": [ - "rank", - "package", - "description", - "downloads", - "favers", - "repository", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/packagist/search.js", - "sourceFile": "plugins/packagist/search.js" - }, - { - "site": "paperreview", - "name": "feedback", - "description": "Submit feedback for a paperreview.ai review token", - "access": "write", - "domain": "paperreview.ai", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "token", - "type": "str", - "required": true, - "positional": true, - "help": "Review token returned by paperreview.ai" - }, - { - "name": "helpfulness", - "type": "int", - "required": true, - "help": "Helpfulness score from 1 to 5" - }, - { - "name": "critical-error", - "type": "str", - "required": true, - "help": "Whether the review contains a critical error", - "choices": [ - "yes", - "no" - ] - }, - { - "name": "actionable-suggestions", - "type": "str", - "required": true, - "help": "Whether the review contains actionable suggestions", - "choices": [ - "yes", - "no" - ] - }, - { - "name": "additional-comments", - "type": "str", - "required": false, - "help": "Optional free-text feedback" - }, - { - "name": "timeout", - "type": "int", - "default": 30, - "required": false, - "help": "Max seconds for the overall command (default: 30)" - } - ], - "columns": [ - "status", - "token", - "helpfulness", - "critical_error", - "actionable_suggestions", - "message" - ], - "type": "js", - "modulePath": "plugins/paperreview/feedback.js", - "sourceFile": "plugins/paperreview/feedback.js" - }, - { - "site": "paperreview", - "name": "review", - "description": "Fetch a paperreview.ai review by token", - "access": "read", - "domain": "paperreview.ai", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "token", - "type": "str", - "required": true, - "positional": true, - "help": "Review token returned by paperreview.ai" - }, - { - "name": "timeout", - "type": "int", - "default": 30, - "required": false, - "help": "Max seconds for the overall command (default: 30)" - } - ], - "columns": [ - "status", - "title", - "venue", - "numerical_score", - "has_feedback", - "review_url" - ], - "type": "js", - "modulePath": "plugins/paperreview/review.js", - "sourceFile": "plugins/paperreview/review.js" - }, - { - "site": "paperreview", - "name": "submit", - "description": "Submit a PDF to paperreview.ai for review", - "access": "write", - "domain": "paperreview.ai", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "pdf", - "type": "str", - "required": true, - "positional": true, - "help": "Path to the paper PDF" - }, - { - "name": "email", - "type": "str", - "required": true, - "help": "Email address for the submission" - }, - { - "name": "venue", - "type": "str", - "required": false, - "help": "Optional target venue such as ICLR or NeurIPS" - }, - { - "name": "dry-run", - "type": "bool", - "default": false, - "required": false, - "help": "Validate the input and stop before remote submission" - }, - { - "name": "prepare-only", - "type": "bool", - "default": false, - "required": false, - "help": "Request an upload slot but stop before uploading the PDF" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds for the overall command (default: 120)" - } - ], - "columns": [ - "status", - "file", - "email", - "venue", - "token", - "review_url", - "message" - ], - "type": "js", - "modulePath": "plugins/paperreview/submit.js", - "sourceFile": "plugins/paperreview/submit.js" - }, - { - "site": "pixiv", - "name": "detail", - "description": "View illustration details (tags, stats, URLs)", - "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Illustration ID" - } - ], - "columns": [ - "illust_id", - "title", - "author", - "type", - "pages", - "bookmarks", - "likes", - "views", - "tags", - "created", - "url" - ], - "type": "js", - "modulePath": "plugins/pixiv/detail.js", - "sourceFile": "plugins/pixiv/detail.js", - "navigateBefore": "https://www.pixiv.net" - }, - { - "site": "pixiv", - "name": "download", - "description": "Download illustration images from Pixiv", - "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "illust-id", - "type": "str", - "required": true, - "positional": true, - "help": "Illustration ID" - }, - { - "name": "output", - "type": "str", - "default": "./pixiv-downloads", - "required": false, - "help": "Output directory" - } - ], - "columns": [ - "index", - "type", - "status", - "size" - ], - "type": "js", - "modulePath": "plugins/pixiv/download.js", - "sourceFile": "plugins/pixiv/download.js", - "navigateBefore": "https://www.pixiv.net" - }, - { - "site": "pixiv", - "name": "illusts", - "description": "List a Pixiv artist's illustrations", - "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "user-id", - "type": "str", - "required": true, - "positional": true, - "help": "Pixiv user ID" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "illust_id", - "pages", - "bookmarks", - "tags", - "created", - "url" - ], - "type": "js", - "modulePath": "plugins/pixiv/illusts.js", - "sourceFile": "plugins/pixiv/illusts.js", - "navigateBefore": "https://www.pixiv.net" - }, - { - "site": "pixiv", - "name": "login", - "description": "Open pixiv login", - "access": "write", - "domain": "pixiv.net", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/pixiv/auth.js", - "sourceFile": "plugins/pixiv/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "pixiv", - "name": "ranking", - "description": "Pixiv illustration rankings (daily/weekly/monthly)", - "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "mode", - "type": "str", - "default": "daily", - "required": false, - "help": "Ranking mode", - "choices": [ - "daily", - "weekly", - "monthly", - "rookie", - "original", - "male", - "female", - "daily_r18", - "weekly_r18" - ] - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "title", - "author", - "user_id", - "illust_id", - "pages", - "bookmarks", - "url" - ], - "type": "js", - "modulePath": "plugins/pixiv/ranking.js", - "sourceFile": "plugins/pixiv/ranking.js", - "navigateBefore": "https://www.pixiv.net" - }, - { - "site": "pixiv", - "name": "search", - "description": "Search Pixiv illustrations by keyword", - "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword or tag" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results" - }, - { - "name": "order", - "type": "str", - "default": "date_d", - "required": false, - "help": "Sort order", - "choices": [ - "date_d", - "date", - "popular_d", - "popular_male_d", - "popular_female_d" - ] - }, - { - "name": "mode", - "type": "str", - "default": "all", - "required": false, - "help": "Search mode", - "choices": [ - "all", - "safe", - "r18" - ] - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number" - } - ], - "columns": [ - "rank", - "title", - "author", - "user_id", - "illust_id", - "pages", - "bookmarks", - "tags", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/pixiv/search.js", - "sourceFile": "plugins/pixiv/search.js", - "navigateBefore": "https://www.pixiv.net" - }, - { - "site": "pixiv", - "name": "user", - "description": "View Pixiv artist profile", - "access": "read", - "domain": "www.pixiv.net", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "uid", - "type": "str", - "required": true, - "positional": true, - "help": "Pixiv user ID" - } - ], - "columns": [ - "user_id", - "name", - "premium", - "following", - "illusts", - "manga", - "novels", - "comment", - "url" - ], - "type": "js", - "modulePath": "plugins/pixiv/user.js", - "sourceFile": "plugins/pixiv/user.js", - "navigateBefore": "https://www.pixiv.net" - }, - { - "site": "pixiv", - "name": "whoami", - "description": "Show the current logged-in pixiv account", - "access": "read", - "domain": "pixiv.net", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "plugins/pixiv/auth.js", - "sourceFile": "plugins/pixiv/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "practo", - "name": "appointment", - "description": "Show logged-in Practo Drive appointment details", - "access": "read", - "domain": "drive.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "appointment_id", - "type": "str", - "required": true, - "positional": true, - "help": "Appointment id from `practo appointments`" - } - ], - "columns": [ - "appointment_id", - "status", - "summary" - ], - "type": "js", - "modulePath": "plugins/practo/appointment.js", - "sourceFile": "plugins/practo/appointment.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "practo", - "name": "appointments", - "description": "List logged-in Practo Drive appointments", - "access": "read", - "domain": "drive.practo.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "appointment_id", - "doctor", - "practice", - "time", - "status" - ], - "type": "js", - "modulePath": "plugins/practo/appointments.js", - "sourceFile": "plugins/practo/appointments.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "practo", - "name": "book-confirm", - "description": "Confirm a Practo clinic visit booking after explicit confirmation", - "access": "write", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "practice_doctor_id", - "type": "str", - "required": true, - "positional": true, - "help": "Practo practice_doctor_id" - }, - { - "name": "time", - "type": "str", - "required": true, - "help": "Slot time YYYY-MM-DD HH:mm:ss" - }, - { - "name": "profile-url", - "type": "str", - "required": false, - "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" - }, - { - "name": "confirm", - "type": "boolean", - "default": false, - "required": false, - "help": "Required. Set --confirm true to create the appointment." - } - ], - "columns": [ - "status", - "practice_doctor_id", - "time", - "url" - ], - "type": "js", - "modulePath": "plugins/practo/book-confirm.js", - "sourceFile": "plugins/practo/book-confirm.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "practo", - "name": "book-preview", - "description": "Preview Practo booking details for a selected slot without confirming", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "practice_doctor_id", - "type": "str", - "required": true, - "positional": true, - "help": "Practo practice_doctor_id" - }, - { - "name": "time", - "type": "str", - "required": true, - "help": "Slot time YYYY-MM-DD HH:mm:ss" - }, - { - "name": "profile-url", - "type": "str", - "required": false, - "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" - } - ], - "columns": [ - "practice_doctor_id", - "time", - "amount", - "prepaid", - "payment_mode", - "requires_payment", - "confirm_button", - "booking_url" - ], - "type": "js", - "modulePath": "plugins/practo/book-preview.js", - "sourceFile": "plugins/practo/book-preview.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "practo", - "name": "booking-link", - "description": "Build a Practo booking URL for a selected slot without confirming it", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "practice_doctor_id", - "type": "str", - "required": true, - "positional": true, - "help": "Practo practice_doctor_id" - }, - { - "name": "time", - "type": "str", - "required": true, - "help": "Slot time YYYY-MM-DD HH:mm:ss" - }, - { - "name": "profile-url", - "type": "str", - "required": false, - "help": "Doctor profile_url from `practo search`, used to build a canonical booking URL" - } - ], - "columns": [ - "practice_doctor_id", - "time", - "booking_url" - ], - "type": "js", - "modulePath": "plugins/practo/booking-link.js", - "sourceFile": "plugins/practo/booking-link.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "cancel", - "description": "Cancel a logged-in Practo Drive appointment after explicit confirmation", - "access": "write", - "domain": "drive.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "appointment_id", - "type": "str", - "required": true, - "positional": true, - "help": "Appointment id from `practo appointments`" - }, - { - "name": "confirm", - "type": "boolean", - "default": false, - "required": false, - "help": "Required. Set --confirm true to cancel the appointment." - } - ], - "columns": [ - "status", - "appointment_id" - ], - "type": "js", - "modulePath": "plugins/practo/cancel.js", - "sourceFile": "plugins/practo/cancel.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "practo", - "name": "contact", - "description": "Get Practo virtual contact number for a practice_doctor_id", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "practice_doctor_id", - "type": "str", - "required": true, - "positional": true, - "help": "Practo practice_doctor_id from search results" - } - ], - "columns": [ - "practice_doctor_id", - "phone", - "raw" - ], - "type": "js", - "modulePath": "plugins/practo/contact.js", - "sourceFile": "plugins/practo/contact.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "login", - "description": "Open practo login", - "access": "write", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/practo/login.js", - "sourceFile": "plugins/practo/login.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "practo", - "name": "profile", - "description": "Read public details from a Practo doctor profile URL", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "Practo doctor profile URL" - } - ], - "columns": [ - "name", - "specialty", - "experience", - "fee", - "profile_url" - ], - "type": "js", - "modulePath": "plugins/practo/profile.js", - "sourceFile": "plugins/practo/profile.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "search", - "description": "Search Practo doctors by specialty, city, and optional locality", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "specialty", - "type": "str", - "required": true, - "positional": true, - "help": "Doctor specialty, e.g. orthopedist or dermatologist" - }, - { - "name": "city", - "type": "str", - "default": "bangalore", - "required": false, - "help": "City, e.g. bangalore" - }, - { - "name": "locality", - "type": "str", - "required": false, - "help": "Optional locality, e.g. indiranagar" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max doctors to return (1-25)" - } - ], - "columns": [ - "rank", - "practice_doctor_id", - "doctor_id", - "practice_id", - "name", - "specialty", - "experience_years", - "locality", - "clinic", - "fee", - "next_available", - "profile_url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/practo/search.js", - "sourceFile": "plugins/practo/search.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "slots", - "description": "List available Practo appointment slots for a practice_doctor_id", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "practice_doctor_id", - "type": "str", - "required": true, - "positional": true, - "help": "Practo practice_doctor_id from search results" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max slots to return (1-25)" - } - ], - "columns": [ - "practice_doctor_id", - "time", - "available", - "amount", - "prepaid", - "appointment_token" - ], - "type": "js", - "modulePath": "plugins/practo/slots.js", - "sourceFile": "plugins/practo/slots.js", - "navigateBefore": false - }, - { - "site": "practo", - "name": "whoami", - "aliases": [ - "auth-status" - ], - "description": "Show the current logged-in practo account", - "access": "read", - "domain": "www.practo.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name" - ], - "type": "js", - "modulePath": "plugins/practo/login.js", - "sourceFile": "plugins/practo/login.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "producthunt", - "name": "browse", - "description": "Best products in a Product Hunt category", - "access": "read", - "domain": "www.producthunt.com", - "strategy": "intercept", - "browser": true, - "args": [ - { - "name": "category", - "type": "string", - "required": true, - "positional": true, - "help": "Category slug, e.g. vibe-coding, ai-agents, developer-tools" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results (max 50)" - } - ], - "columns": [ - "rank", - "name", - "tagline", - "reviews", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/producthunt/browse.js", - "sourceFile": "plugins/producthunt/browse.js", - "navigateBefore": true - }, - { - "site": "producthunt", - "name": "hot", - "description": "Today's top Product Hunt launches with vote counts", - "access": "read", - "domain": "www.producthunt.com", - "strategy": "intercept", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results (max 50)" - } - ], - "columns": [ - "rank", - "name", - "votes", - "url" - ], - "type": "js", - "modulePath": "plugins/producthunt/hot.js", - "sourceFile": "plugins/producthunt/hot.js", - "navigateBefore": true - }, - { - "site": "producthunt", - "name": "posts", - "description": "Latest Product Hunt launches (optional category filter)", - "access": "read", - "domain": "www.producthunt.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results (max 50)" - }, - { - "name": "category", - "type": "string", - "default": "", - "required": false, - "help": "Category filter: ai-agents, ai-coding-agents, ai-code-editors, ai-chatbots, ai-workflow-automation, vibe-coding, developer-tools, productivity, design-creative, marketing-sales, no-code-platforms, llms, finance, social-community, engineering-development" - } - ], - "columns": [ - "rank", - "name", - "tagline", - "author", - "date", - "url" - ], - "type": "js", - "modulePath": "plugins/producthunt/posts.js", - "sourceFile": "plugins/producthunt/posts.js" - }, - { - "site": "producthunt", - "name": "today", - "description": "Today's Product Hunt launches (most recent day in feed)", - "access": "read", - "domain": "www.producthunt.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results" - } - ], - "columns": [ - "rank", - "name", - "tagline", - "author", - "url" - ], - "type": "js", - "modulePath": "plugins/producthunt/today.js", - "sourceFile": "plugins/producthunt/today.js" - }, - { - "site": "pubmed", - "name": "article", - "aliases": [ - "paper", - "read" - ], - "description": "Get detailed information for a PubMed article by PMID", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "pmid", - "type": "str", - "required": true, - "positional": true, - "help": "PubMed ID, e.g. 37780221" - }, - { - "name": "full-abstract", - "type": "boolean", - "default": false, - "required": false, - "help": "Do not truncate the abstract in table output" - } - ], - "columns": [ - "pmid", - "title", - "authors", - "journal", - "year", - "date", - "article_type", - "language", - "doi", - "pmc", - "affiliations", - "grants", - "mesh_terms", - "keywords", - "abstract", - "url" - ], - "defaultFormat": "plain", - "type": "js", - "modulePath": "plugins/pubmed/article.js", - "sourceFile": "plugins/pubmed/article.js" - }, - { - "site": "pubmed", - "name": "author", - "description": "Search PubMed articles by author name and optional affiliation", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Author name, e.g. \"Smith J\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "affiliation", - "type": "str", - "required": false, - "help": "Filter by author affiliation" - }, - { - "name": "position", - "type": "str", - "default": "any", - "required": false, - "help": "Author position: any, first, or last", - "choices": [ - "any", - "first", - "last" - ] - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, - { - "name": "sort", - "type": "str", - "default": "date", - "required": false, - "help": "Sort by date or relevance", - "choices": [ - "date", - "relevance" - ] - } - ], - "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" - ], - "type": "js", - "modulePath": "plugins/pubmed/author.js", - "sourceFile": "plugins/pubmed/author.js" - }, - { - "site": "pubmed", - "name": "citations", - "description": "Get PubMed citation relationships for an article", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "pmid", - "type": "str", - "required": true, - "positional": true, - "help": "PubMed ID, e.g. 37780221" - }, - { - "name": "direction", - "type": "str", - "default": "citedby", - "required": false, - "help": "citedby or references", - "choices": [ - "citedby", - "references" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - } - ], - "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" - ], - "type": "js", - "modulePath": "plugins/pubmed/citations.js", - "sourceFile": "plugins/pubmed/citations.js" - }, - { - "site": "pubmed", - "name": "clinical-trial", - "description": "Search PubMed clinical trials with a trial-study preset", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Clinical topic query, e.g. \"breast cancer\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, - { - "name": "free-full-text", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include free full text articles" - }, - { - "name": "sort", - "type": "str", - "default": "date", - "required": false, - "help": "Sort by date or relevance", - "choices": [ - "date", - "relevance" - ] - } - ], - "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" - ], - "type": "js", - "modulePath": "plugins/pubmed/clinical-trial.js", - "sourceFile": "plugins/pubmed/clinical-trial.js" - }, - { - "site": "pubmed", - "name": "journal", - "description": "Search PubMed articles by journal name", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "journal", - "type": "str", - "required": true, - "positional": true, - "help": "Journal name, e.g. \"Nature\" or \"The Lancet\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, - { - "name": "sort", - "type": "str", - "default": "relevance", - "required": false, - "help": "Sort by relevance or date", - "choices": [ - "relevance", - "date" - ] - } - ], - "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" - ], - "type": "js", - "modulePath": "plugins/pubmed/journal.js", - "sourceFile": "plugins/pubmed/journal.js" - }, - { - "site": "pubmed", - "name": "mesh", - "description": "Search PubMed articles by MeSH term", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "term", - "type": "str", - "required": true, - "positional": true, - "help": "MeSH term, e.g. \"Neoplasms\" or \"Machine Learning\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "major", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include articles where this is a major MeSH topic" - }, - { - "name": "sort", - "type": "str", - "default": "relevance", - "required": false, - "help": "Sort by relevance or date", - "choices": [ - "relevance", - "date" - ] - } - ], - "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" - ], - "type": "js", - "modulePath": "plugins/pubmed/mesh.js", - "sourceFile": "plugins/pubmed/mesh.js" - }, - { - "site": "pubmed", - "name": "related", - "description": "Find articles related to a PubMed article", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "pmid", - "type": "str", - "required": true, - "positional": true, - "help": "PubMed ID, e.g. 37780221" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "score", - "type": "boolean", - "default": false, - "required": false, - "help": "Show similarity scores when available" - } - ], - "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "score", - "doi", - "url" - ], - "type": "js", - "modulePath": "plugins/pubmed/related.js", - "sourceFile": "plugins/pubmed/related.js" - }, - { - "site": "pubmed", - "name": "review", - "description": "Search PubMed review articles with a review preset", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Review topic query, e.g. \"immunotherapy\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, - { - "name": "has-abstract", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include articles with abstracts" - }, - { - "name": "sort", - "type": "str", - "default": "date", - "required": false, - "help": "Sort by date or relevance", - "choices": [ - "date", - "relevance" - ] - } - ], - "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" - ], - "type": "js", - "modulePath": "plugins/pubmed/review.js", - "sourceFile": "plugins/pubmed/review.js" - }, - { - "site": "pubmed", - "name": "search", - "description": "Search PubMed articles with advanced filters", - "access": "read", - "domain": "pubmed.ncbi.nlm.nih.gov", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query, e.g. \"machine learning cancer\"" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-100)" - }, - { - "name": "author", - "type": "str", - "required": false, - "help": "Filter by author name" - }, - { - "name": "journal", - "type": "str", - "required": false, - "help": "Filter by journal name" - }, - { - "name": "year-from", - "type": "int", - "required": false, - "help": "Filter publication year from" - }, - { - "name": "year-to", - "type": "int", - "required": false, - "help": "Filter publication year to" - }, - { - "name": "article-type", - "type": "str", - "required": false, - "help": "Filter by publication type, e.g. Review or Clinical Trial" - }, - { - "name": "has-abstract", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include articles with abstracts" - }, - { - "name": "free-full-text", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include free full text articles" - }, - { - "name": "humans-only", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include human studies" - }, - { - "name": "english-only", - "type": "boolean", - "default": false, - "required": false, - "help": "Only include English articles" - }, - { - "name": "sort", - "type": "str", - "default": "relevance", - "required": false, - "help": "Sort by relevance, date, author, or journal", - "choices": [ - "relevance", - "date", - "author", - "journal" - ] - } - ], - "columns": [ - "rank", - "pmid", - "title", - "authors", - "journal", - "year", - "article_type", - "doi", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/pubmed/search.js", - "sourceFile": "plugins/pubmed/search.js" - }, - { - "site": "pypi", - "name": "downloads", - "description": "PyPI download stats for a package (recent totals or full daily history)", - "access": "read", - "domain": "pypistats.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "PyPI package name (e.g. \"requests\", \"pandas\")" - }, - { - "name": "period", - "type": "str", - "default": "recent", - "required": false, - "help": "recent (default — 1 row, last day/week/month) or overall (1 row per day)" - } - ], - "columns": [ - "rank", - "package", - "period", - "date", - "downloads" - ], - "type": "js", - "modulePath": "plugins/pypi/downloads.js", - "sourceFile": "plugins/pypi/downloads.js" - }, - { - "site": "pypi", - "name": "package", - "description": "Single PyPI package metadata (latest version, license, homepage, classifiers)", - "access": "read", - "domain": "pypi.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "PyPI package name (e.g. \"requests\", \"pandas\")" - } - ], - "columns": [ - "name", - "latestVersion", - "summary", - "author", - "license", - "homepage", - "repository", - "requiresPython", - "keywords", - "releases", - "firstReleased", - "lastReleased", - "url" - ], - "type": "js", - "modulePath": "plugins/pypi/package.js", - "sourceFile": "plugins/pypi/package.js" - }, - { - "site": "pypi", - "name": "releases", - "description": "List recent public PyPI package releases", - "access": "read", - "domain": "pypi.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "string", - "required": true, - "positional": true, - "help": "Python package name, for example django" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum releases to return (1-50)" - } - ], - "columns": [ - "version", - "uploadedAt", - "fileCount", - "pythonVersions", - "yanked", - "url" - ], - "type": "js", - "modulePath": "plugins/pypi/releases.js", - "sourceFile": "plugins/pypi/releases.js" - }, - { - "site": "qoder", - "name": "account", - "description": "Click the account button (username) in the Qoder sidebar and return the visible account dropdown items.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": false, - "help": "Username text shown in the sidebar (default: tries common short labels)" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/qoder/ui.js", - "sourceFile": "plugins/qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "add-workspace", - "description": "Click \"Add Workspace\" — opens the folder picker. Note: this opens a system file-picker dialog that Qoder controls; the actual folder selection must be done in the UI by the user.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/qoder/ui.js", - "sourceFile": "plugins/qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "ask", - "description": "Send a prompt to Qoder and wait up to --timeout seconds for the reply (best-effort: polls for the chat turn count to grow + stabilize).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Prompt text" - }, - { - "name": "timeout", - "type": "int", - "default": 120, - "required": false, - "help": "Max seconds to wait" - } - ], - "columns": [ - "Role", - "Text", - "WaitedSeconds" - ], - "type": "js", - "modulePath": "plugins/qoder/quest.js", - "sourceFile": "plugins/qoder/quest.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "credits", - "description": "Click \"Credits Usage\" and return the credits-usage display text.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/qoder/ui.js", - "sourceFile": "plugins/qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "history", - "description": "List Quests visible in the Qoder sidebar. Returns title + visible metadata.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Title" - ], - "type": "js", - "modulePath": "plugins/qoder/history.js", - "sourceFile": "plugins/qoder/history.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "knowledge", - "description": "Open the Knowledge view (Qoder's personal/team knowledge base).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/qoder/ui.js", - "sourceFile": "plugins/qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "marketplace", - "description": "Open the Qoder Marketplace.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/qoder/ui.js", - "sourceFile": "plugins/qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "more-actions", - "description": "Click the \"More Actions\" button and list its menu items.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Item" - ], - "type": "js", - "modulePath": "plugins/qoder/ui.js", - "sourceFile": "plugins/qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "new", - "description": "Start a new Qoder Quest (conversation). Clicks the \"New Quest\" button in the sidebar (or its ⌘N variant).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/qoder/quest.js", - "sourceFile": "plugins/qoder/quest.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "open-editor", - "description": "Click \"Open Editor\" — opens the current draft in a full editor pane.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/qoder/composer.js", - "sourceFile": "plugins/qoder/composer.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "open-panel", - "description": "Open / close the Qoder bottom panel (Output / Terminal / Debug Console). ⌥⌘B equivalent.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/qoder/ui.js", - "sourceFile": "plugins/qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "prompt-enhance", - "description": "Click \"Prompt Enhance\" — Qoder rewrites the current composer draft for better LLM consumption.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/qoder/composer.js", - "sourceFile": "plugins/qoder/composer.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "read", - "description": "Read messages in the current Qoder Quest. Returns role + text for each visible turn.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Role", - "Text" - ], - "type": "js", - "modulePath": "plugins/qoder/read.js", - "sourceFile": "plugins/qoder/read.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "search", - "description": "Open Qoder Search palette (⌘P), type a query, return matched options.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search text" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Item" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/qoder/ui.js", - "sourceFile": "plugins/qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "send", - "description": "Type text into the Qoder composer and click \"Send message\" (fire-and-forget).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Text to send" - } - ], - "columns": [ - "Status", - "Length" - ], - "type": "js", - "modulePath": "plugins/qoder/quest.js", - "sourceFile": "plugins/qoder/quest.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "settings", - "description": "Click the Settings button in the Qoder sidebar.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/qoder/ui.js", - "sourceFile": "plugins/qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "sidebar-toggle", - "description": "Collapse / Expand the Qoder Quest List sidebar (⌘B).", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/qoder/ui.js", - "sourceFile": "plugins/qoder/ui.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "status", - "description": "Check Qoder CDP connection and report the current renderer URL + title.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" - ], - "type": "js", - "modulePath": "plugins/qoder/status.js", - "sourceFile": "plugins/qoder/status.js", - "navigateBefore": true - }, - { - "site": "qoder", - "name": "view-all", - "description": "Click \"View all\" to show all Quests.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status" - ], - "type": "js", - "modulePath": "plugins/qoder/ui.js", - "sourceFile": "plugins/qoder/ui.js", - "navigateBefore": true - }, - { - "site": "reddit", - "name": "comment", - "description": "Post a comment on a Reddit post", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" - }, - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "Comment text" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/reddit/comment.js", - "sourceFile": "plugins/reddit/comment.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "frontpage", - "description": "Reddit Frontpage / r/all", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "author", - "upvotes", - "comments", - "url", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "plugins/reddit/frontpage.js", - "sourceFile": "plugins/reddit/frontpage.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "home", - "description": "Reddit personalized home feed (Best, requires login)", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Number of posts (1–100)" - } - ], - "columns": [ - "rank", - "title", - "subreddit", - "score", - "comments", - "postId", - "author", - "url", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "plugins/reddit/home.js", - "sourceFile": "plugins/reddit/home.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "hot", - "description": "Reddit hot posts", - "access": "read", - "domain": "www.reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "subreddit", - "type": "str", - "default": "", - "required": false, - "help": "Subreddit name (e.g. programming). Empty for frontpage" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts" - } - ], - "columns": [ - "rank", - "title", - "subreddit", - "score", - "comments", - "postId", - "author", - "url", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "plugins/reddit/hot.js", - "sourceFile": "plugins/reddit/hot.js", - "navigateBefore": "https://www.reddit.com" - }, - { - "site": "reddit", - "name": "login", - "description": "Open reddit login", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "username", - "id", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/reddit/auth.js", - "sourceFile": "plugins/reddit/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "reddit", - "name": "popular", - "description": "Reddit Popular posts (/r/popular)", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "" - } - ], - "columns": [ - "rank", - "id", - "title", - "subreddit", - "score", - "comments", - "author", - "url", - "created_utc", - "selftext", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "plugins/reddit/popular.js", - "sourceFile": "plugins/reddit/popular.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "read", - "description": "Read a Reddit post and its comments", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "str", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or full URL" - }, - { - "name": "sort", - "type": "str", - "default": "best", - "required": false, - "help": "Comment sort: best, top, new, controversial, old, qa" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Number of top-level comments" - }, - { - "name": "depth", - "type": "int", - "default": 2, - "required": false, - "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)" - }, - { - "name": "replies", - "type": "int", - "default": 5, - "required": false, - "help": "Max replies shown per comment at each level (sorted by score)" - }, - { - "name": "max-length", - "type": "int", - "default": 2000, - "required": false, - "help": "Max characters per comment body (min 100)" - }, - { - "name": "expand-more", - "type": "bool", - "default": false, - "required": false, - "help": "Follow Reddit \"more comments\" stubs by calling /api/morechildren.json" - }, - { - "name": "expand-rounds", - "type": "int", - "default": 2, - "required": false, - "help": "Max expansion passes when --expand-more is on (1–5; each round can fan out new \"more\" stubs)" - } - ], - "columns": [ - "type", - "author", - "score", - "text", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "plugins/reddit/read.js", - "sourceFile": "plugins/reddit/read.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "reply", - "description": "Reply to a Reddit comment", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "comment-id", - "type": "string", - "required": true, - "positional": true, - "help": "Comment ID (e.g. okf3s7u) or fullname (t1_xxx)" - }, - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "Reply text" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/reddit/reply.js", - "sourceFile": "plugins/reddit/reply.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "save", - "description": "Save or unsave a Reddit post", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" - }, - { - "name": "undo", - "type": "boolean", - "default": false, - "required": false, - "help": "Unsave instead of save" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/reddit/save.js", - "sourceFile": "plugins/reddit/save.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "saved", - "description": "Browse your saved Reddit posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "score", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/reddit/saved.js", - "sourceFile": "plugins/reddit/saved.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "search", - "description": "Search Reddit Posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit search query" - }, - { - "name": "subreddit", - "type": "string", - "default": "", - "required": false, - "help": "Search within a specific subreddit" - }, - { - "name": "sort", - "type": "string", - "default": "relevance", - "required": false, - "help": "Sort order: relevance, hot, top, new, comments" - }, - { - "name": "time", - "type": "string", - "default": "all", - "required": false, - "help": "Time filter: hour, day, week, month, year, all" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "id", - "title", - "subreddit", - "author", - "score", - "comments", - "url", - "created_utc", - "selftext", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/reddit/search.js", - "sourceFile": "plugins/reddit/search.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "subreddit", - "description": "Get posts from a specific Subreddit", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "name", - "type": "string", - "required": true, - "positional": true, - "help": "Subreddit name (no `r/` prefix; e.g. `python`)" - }, - { - "name": "sort", - "type": "string", - "default": "hot", - "required": false, - "help": "Sorting method: hot, new, top, rising, controversial" - }, - { - "name": "time", - "type": "string", - "default": "all", - "required": false, - "help": "Time filter for top/controversial: hour, day, week, month, year, all" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "id", - "title", - "subreddit", - "author", - "upvotes", - "comments", - "url", - "created_utc", - "selftext", - "post_hint", - "url_overridden_by_dest", - "preview_image_url", - "gallery_urls" - ], - "type": "js", - "modulePath": "plugins/reddit/subreddit.js", - "sourceFile": "plugins/reddit/subreddit.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "subreddit-info", - "description": "Show metadata for a Reddit subreddit (subscribers, description, created date, NSFW)", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "name", - "type": "string", - "required": true, - "positional": true, - "help": "Subreddit name (no `r/` prefix needed)" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "plugins/reddit/subreddit-info.js", - "sourceFile": "plugins/reddit/subreddit-info.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "subscribe", - "description": "Subscribe or unsubscribe to a subreddit", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "subreddit", - "type": "string", - "required": true, - "positional": true, - "help": "Subreddit name (e.g. python)" - }, - { - "name": "undo", - "type": "boolean", - "default": false, - "required": false, - "help": "Unsubscribe instead of subscribe" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/reddit/subscribe.js", - "sourceFile": "plugins/reddit/subscribe.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "subscribed", - "description": "List subreddits you are subscribed to", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Max subreddits to return (1-1000, auto-paginates)" - } - ], - "columns": [ - "id", - "subreddit", - "title", - "subscribers", - "description", - "url" - ], - "type": "js", - "modulePath": "plugins/reddit/subscribed.js", - "sourceFile": "plugins/reddit/subscribed.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "upvote", - "description": "Upvote or downvote a Reddit post", - "access": "write", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "post-id", - "type": "string", - "required": true, - "positional": true, - "help": "Post ID (e.g. 1abc123) or fullname (t3_xxx)" - }, - { - "name": "direction", - "type": "string", - "default": "up", - "required": false, - "help": "Vote direction: up, down, none" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/reddit/upvote.js", - "sourceFile": "plugins/reddit/upvote.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "upvoted", - "description": "Browse your upvoted Reddit posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "score", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/reddit/upvoted.js", - "sourceFile": "plugins/reddit/upvoted.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "user", - "description": "View a Reddit user profile", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit username (no `u/` prefix needed)" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "plugins/reddit/user.js", - "sourceFile": "plugins/reddit/user.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "user-comments", - "description": "View a Reddit user's comment history", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit username (no `u/` prefix needed)" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "subreddit", - "score", - "body", - "url" - ], - "type": "js", - "modulePath": "plugins/reddit/user-comments.js", - "sourceFile": "plugins/reddit/user-comments.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "user-posts", - "description": "View a Reddit user's submitted posts", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Reddit username (no `u/` prefix needed)" - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "" - } - ], - "columns": [ - "title", - "subreddit", - "score", - "comments", - "url" - ], - "type": "js", - "modulePath": "plugins/reddit/user-posts.js", - "sourceFile": "plugins/reddit/user-posts.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "reddit", - "name": "whoami", - "description": "Show the currently logged-in Reddit user", - "access": "read", - "domain": "reddit.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "plugins/reddit/whoami.js", - "sourceFile": "plugins/reddit/whoami.js", - "navigateBefore": "https://reddit.com" - }, - { - "site": "rest-countries", - "name": "country", - "description": "Look up countries by name (common / official, substring match)", - "access": "read", - "domain": "restcountries.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Country name (e.g. \"japan\", \"united kingdom\")" - }, - { - "name": "limit", - "type": "int", - "default": 25, - "required": false, - "help": "Max rows (1-250)" - } - ], - "columns": [ - "rank", - "commonName", - "officialName", - "cca2", - "cca3", - "ccn3", - "capital", - "region", - "subregion", - "population", - "area", - "languages", - "currencies", - "latitude", - "longitude", - "timezones", - "independent", - "unMember", - "landlocked", - "flag", - "url" - ], - "type": "js", - "modulePath": "plugins/rest-countries/country.js", - "sourceFile": "plugins/rest-countries/country.js" - }, - { - "site": "rest-countries", - "name": "region", - "description": "List countries in a region (africa / americas / asia / europe / oceania / antarctic)", - "access": "read", - "domain": "restcountries.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "region", - "type": "str", - "required": true, - "positional": true, - "help": "Region name (case-insensitive)" - }, - { - "name": "limit", - "type": "int", - "default": 250, - "required": false, - "help": "Max rows (1-250)" - } - ], - "columns": [ - "rank", - "commonName", - "officialName", - "cca2", - "cca3", - "ccn3", - "capital", - "region", - "subregion", - "population", - "area", - "languages", - "currencies", - "latitude", - "longitude", - "timezones", - "independent", - "unMember", - "landlocked", - "flag", - "url" - ], - "type": "js", - "modulePath": "plugins/rest-countries/region.js", - "sourceFile": "plugins/rest-countries/region.js" - }, - { - "site": "reuters", - "name": "article-detail", - "description": "Reuters Reuters article detail:title/author/body text", - "access": "read", - "domain": "www.reuters.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "Reuters article URL (must be on reuters.com)" - } - ], - "columns": [ - "title", - "date", - "section", - "section_path", - "authors", - "description", - "word_count", - "url", - "body" - ], - "type": "js", - "modulePath": "plugins/reuters/article-detail.js", - "sourceFile": "plugins/reuters/article-detail.js", - "navigateBefore": "https://www.reuters.com" - }, - { - "site": "reuters", - "name": "login", - "description": "Open reuters login", - "access": "write", - "domain": "reuters.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "subscribed", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/reuters/auth.js", - "sourceFile": "plugins/reuters/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "reuters", - "name": "search", - "description": "Reuters Reuters news search", - "access": "read", - "domain": "www.reuters.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results (1-40)" - } - ], - "columns": [ - "rank", - "title", - "date", - "section", - "section_path", - "authors", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/reuters/search.js", - "sourceFile": "plugins/reuters/search.js", - "navigateBefore": "https://www.reuters.com" - }, - { - "site": "reuters", - "name": "whoami", - "description": "Show the current logged-in reuters account", - "access": "read", - "domain": "reuters.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "subscribed" - ], - "type": "js", - "modulePath": "plugins/reuters/auth.js", - "sourceFile": "plugins/reuters/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "rfc", - "name": "rfc", - "description": "Single IETF RFC metadata (title, abstract, working group, authors, std level)", - "access": "read", - "domain": "datatracker.ietf.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "number", - "type": "int", - "required": true, - "positional": true, - "help": "RFC number (e.g. 9000, 791, 2616)" - } - ], - "columns": [ - "rfc", - "title", - "state", - "stdLevel", - "group", - "groupType", - "pages", - "published", - "authors", - "abstract", - "rfcEditorUrl", - "url" - ], - "type": "js", - "modulePath": "plugins/rfc/rfc.js", - "sourceFile": "plugins/rfc/rfc.js" - }, - { - "site": "rubygems", - "name": "gem", - "description": "Fetch a RubyGems.org gem's metadata (version, downloads, license, links)", - "access": "read", - "domain": "rubygems.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Gem name (e.g. \"rails\", \"sidekiq\")" - } - ], - "columns": [ - "gem", - "version", - "releasedAt", - "downloads", - "versionDownloads", - "license", - "authors", - "homepage", - "source", - "bugs", - "info", - "url" - ], - "type": "js", - "modulePath": "plugins/rubygems/gem.js", - "sourceFile": "plugins/rubygems/gem.js" - }, - { - "site": "rubygems", - "name": "search", - "description": "Search RubyGems.org gems by keyword", - "access": "read", - "domain": "rubygems.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"rails\", \"redis\")" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max gems (1-100, single RubyGems page)" - } - ], - "columns": [ - "rank", - "gem", - "version", - "downloads", - "license", - "authors", - "info", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/rubygems/search.js", - "sourceFile": "plugins/rubygems/search.js" - }, - { - "site": "semanticscholar", - "name": "citations", - "description": "List papers that cite a Semantic Scholar paper (paginated)", - "access": "read", - "domain": "api.semanticscholar.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max citing papers (1-1000, single Semantic Scholar page)" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Page offset (0-based)" - } - ], - "columns": [ - "rank", - "paperId", - "doi", - "title", - "year", - "firstAuthor", - "citationCount", - "url" - ], - "type": "js", - "modulePath": "plugins/semanticscholar/citations.js", - "sourceFile": "plugins/semanticscholar/citations.js" - }, - { - "site": "semanticscholar", - "name": "paper", - "description": "Semantic Scholar paper detail (citation graph + AI tldr) by paperId, DOI, or arXiv id", - "access": "read", - "domain": "api.semanticscholar.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id (e.g. \"ARXIV:1706.03762\", \"PMID:12345\")" - } - ], - "columns": [ - "paperId", - "doi", - "title", - "year", - "firstAuthor", - "citationCount", - "influentialCitationCount", - "referenceCount", - "tldr", - "url" - ], - "type": "js", - "modulePath": "plugins/semanticscholar/paper.js", - "sourceFile": "plugins/semanticscholar/paper.js" - }, - { - "site": "semanticscholar", - "name": "recommendations", - "description": "Semantic Scholar AI-curated related papers for a paperId, DOI, or arXiv id", - "access": "read", - "domain": "api.semanticscholar.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "paperId (40-char hex), DOI, arXiv id, or prefixed id" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max recommendations (1-500)" - } - ], - "columns": [ - "rank", - "paperId", - "doi", - "title", - "year", - "firstAuthor", - "citationCount", - "url" - ], - "type": "js", - "modulePath": "plugins/semanticscholar/recommendations.js", - "sourceFile": "plugins/semanticscholar/recommendations.js" - }, - { - "site": "semanticscholar", - "name": "search", - "description": "Search Semantic Scholar papers by free text", - "access": "read", - "domain": "api.semanticscholar.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search text (e.g. \"attention is all you need\", \"diffusion model\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max papers (1-100, single Semantic Scholar page)" - } - ], - "columns": [ - "rank", - "paperId", - "doi", - "title", - "year", - "firstAuthor", - "citationCount", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/semanticscholar/search.js", - "sourceFile": "plugins/semanticscholar/search.js" - }, - { - "site": "skyscanner", - "name": "flights", - "description": "Skyscanner visible round-trip flight results from a warmed browser session", - "access": "read", - "domain": "www.skyscanner.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "origin", - "type": "str", - "required": true, - "positional": true, - "help": "Skyscanner origin route code, for example nyca" - }, - { - "name": "destination", - "type": "str", - "required": true, - "positional": true, - "help": "Skyscanner destination route code, for example lond" - }, - { - "name": "depart-date", - "type": "str", - "required": true, - "help": "Outbound date as YYYY-MM-DD" - }, - { - "name": "return-date", - "type": "str", - "required": true, - "help": "Return date as YYYY-MM-DD" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum flight rows to return (1-30)" - } - ], - "columns": [ - "rank", - "priceText", - "airlines", - "outboundTime", - "outboundRoute", - "outboundDuration", - "outboundStops", - "returnTime", - "returnRoute", - "returnDuration", - "returnStops", - "url" - ], - "type": "js", - "modulePath": "plugins/skyscanner/flights.js", - "sourceFile": "plugins/skyscanner/flights.js", - "navigateBefore": false - }, - { - "site": "slock", - "name": "attachment-download", - "description": "Download an attachment to a local file. Resolves a signed CDN URL in the page, then fetches bytes node-side (no CORS).", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "attachmentId", - "type": "str", - "required": true, - "positional": true, - "help": "Attachment UUID" - }, - { - "name": "out", - "type": "str", - "required": false, - "help": "Local path to write to. Defaults to ./.bin" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server slug" - } - ], - "columns": [ - "attachmentId", - "out", - "sizeBytes" - ], - "type": "js", - "modulePath": "plugins/slock/attachment-download.js", - "sourceFile": "plugins/slock/attachment-download.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "attachment-upload", - "description": "Upload a local file to Slock attachments. Prints the attachmentId for use with `message-send --attach`.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "file", - "type": "str", - "required": true, - "positional": true, - "help": "Local file path to upload (single file; max 50 MB)" - }, - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name — server requires the attachment be scoped to a channel" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server slug" - } - ], - "columns": [ - "attachmentId", - "filename", - "mimeType", - "sizeBytes" - ], - "type": "js", - "modulePath": "plugins/slock/attachment-upload.js", - "sourceFile": "plugins/slock/attachment-upload.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "attachment-url", - "description": "Get a short-lived signed CDN URL for an attachment (does not download bytes).", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "attachmentId", - "type": "str", - "required": true, - "positional": true, - "help": "Attachment UUID" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server slug" - } - ], - "columns": [ - "attachmentId", - "url", - "expiresAt" - ], - "type": "js", - "modulePath": "plugins/slock/attachment-url.js", - "sourceFile": "plugins/slock/attachment-url.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "bookmark-add", - "description": "Bookmark a message (POST /channels/saved). Requires full messageId UUID.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full messageId UUID (short ids rejected)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "messageId", - "saved" - ], - "type": "js", - "modulePath": "plugins/slock/bookmark-add.js", - "sourceFile": "plugins/slock/bookmark-add.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "bookmark-list", - "description": "List bookmarks (saved messages) in the active server", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max results" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Offset" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "messageId", - "content", - "savedAt" - ], - "type": "js", - "modulePath": "plugins/slock/bookmark-list.js", - "sourceFile": "plugins/slock/bookmark-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "bookmark-remove", - "description": "Remove a bookmark (DELETE /channels/saved/:messageId). 404 is treated as already-removed.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full messageId UUID" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "messageId", - "removed", - "note" - ], - "type": "js", - "modulePath": "plugins/slock/bookmark-remove.js", - "sourceFile": "plugins/slock/bookmark-remove.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-archive", - "description": "Archive a channel — admin only (POST /channels/:id/archive)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "id", - "archivedAt", - "result" - ], - "type": "js", - "modulePath": "plugins/slock/channel-archive.js", - "sourceFile": "plugins/slock/channel-archive.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-create", - "description": "Create a channel — admin only (POST /channels/). Public unless --private.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Channel name" - }, - { - "name": "description", - "type": "str", - "required": false, - "help": "Channel description / topic (≤500 chars)" - }, - { - "name": "private", - "type": "bool", - "default": false, - "required": false, - "help": "Create a private channel instead of public" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "name", - "type", - "result" - ], - "type": "js", - "modulePath": "plugins/slock/channel-create.js", - "sourceFile": "plugins/slock/channel-create.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-files", - "description": "List files shared in a channel (GET /channels/:id/files)", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max files" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "filename", - "mimeType", - "sizeBytes", - "messageId", - "createdAt" - ], - "type": "js", - "modulePath": "plugins/slock/channel-files.js", - "sourceFile": "plugins/slock/channel-files.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-info", - "description": "Show one channel's details (GET /channels/:id)", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "name", - "type", - "topic", - "joined", - "archivedAt" - ], - "type": "js", - "modulePath": "plugins/slock/channel-info.js", - "sourceFile": "plugins/slock/channel-info.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-join", - "description": "Join a public channel (POST /channels/:id/join)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "id", - "archivedAt", - "result" - ], - "type": "js", - "modulePath": "plugins/slock/channel-join.js", - "sourceFile": "plugins/slock/channel-join.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-leave", - "description": "Leave a channel (POST /channels/:id/leave)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "id", - "archivedAt", - "result" - ], - "type": "js", - "modulePath": "plugins/slock/channel-leave.js", - "sourceFile": "plugins/slock/channel-leave.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-list", - "description": "List channels in the active slock server", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id) for this call" - } - ], - "columns": [ - "id", - "name", - "topic" - ], - "type": "js", - "modulePath": "plugins/slock/channel-list.js", - "sourceFile": "plugins/slock/channel-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-mark", - "description": "Mark a channel read (default), read up to --seq, or --unread.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "seq", - "type": "int", - "required": false, - "help": "Mark read up to this seq (omit for read-all)" - }, - { - "name": "unread", - "type": "bool", - "default": false, - "required": false, - "help": "Mark the channel unread instead of read" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "action", - "result" - ], - "type": "js", - "modulePath": "plugins/slock/channel-mark.js", - "sourceFile": "plugins/slock/channel-mark.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-members", - "description": "List members of a channel", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id)" - } - ], - "columns": [ - "userId", - "name", - "kind", - "role" - ], - "type": "js", - "modulePath": "plugins/slock/channel-members.js", - "sourceFile": "plugins/slock/channel-members.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "channel-unarchive", - "description": "Unarchive a channel — admin only (POST /channels/:id/unarchive). #name lookups exclude archived channels; pass the channelId UUID for archived ones.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "id", - "archivedAt", - "result" - ], - "type": "js", - "modulePath": "plugins/slock/channel-unarchive.js", - "sourceFile": "plugins/slock/channel-unarchive.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "dm-list", - "description": "List DM channels in the active server (GET /channels/dm)", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id)" - } - ], - "columns": [ - "channelId", - "peerName", - "peerId", - "createdAt" - ], - "type": "js", - "modulePath": "plugins/slock/dm-list.js", - "sourceFile": "plugins/slock/dm-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "inbox", - "description": "List unified inbox items (channels, DMs, followed threads) that need attention.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "filter", - "type": "str", - "default": "all", - "required": false, - "help": "all | unread | mentions" - }, - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max items (server caps at 100)" - }, - { - "name": "offset", - "type": "int", - "default": 0, - "required": false, - "help": "Pagination offset" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "kind", - "id", - "name", - "unreadCount", - "hasMention", - "lastActivityAt", - "preview" - ], - "type": "js", - "modulePath": "plugins/slock/inbox.js", - "sourceFile": "plugins/slock/inbox.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "inbox-done", - "description": "Mark one chat as done / clear it from the inbox (POST /channels/inbox/done)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "channel", - "result" - ], - "type": "js", - "modulePath": "plugins/slock/inbox-done.js", - "sourceFile": "plugins/slock/inbox-done.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "inbox-read-all", - "description": "Mark the entire inbox as read (POST /channels/inbox/read-all)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "result", - "markedCount" - ], - "type": "js", - "modulePath": "plugins/slock/inbox-read-all.js", - "sourceFile": "plugins/slock/inbox-read-all.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "login", - "description": "Open slock login", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "id", - "name", - "email", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/slock/whoami.js", - "sourceFile": "plugins/slock/whoami.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "message-read", - "description": "Read messages in a channel or thread. Thread form: \"#channel:msgIdOrShort\". Use --after seq|UUID for cursor.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID, \"#name\", or \"#channel:msgIdOrShort\"" - }, - { - "name": "after", - "type": "str", - "required": false, - "help": "Cursor: seq number or messageId UUID (exclusive)" - }, - { - "name": "before", - "type": "str", - "required": false, - "help": "seq to page before" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max messages" - }, - { - "name": "no-threads", - "type": "bool", - "default": false, - "required": false, - "help": "Skip /threads enrichment" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "seq", - "createdAt", - "senderName", - "content", - "threadChannelId", - "replyCount", - "unreadCount", - "lastReplyAt" - ], - "type": "js", - "modulePath": "plugins/slock/message-read.js", - "sourceFile": "plugins/slock/message-read.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "message-search", - "description": "Search messages", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "channel", - "type": "str", - "required": false, - "help": "Restrict to a channel (UUID or #name)" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max results" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "channelId", - "createdAt", - "senderName", - "content" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/slock/message-search.js", - "sourceFile": "plugins/slock/message-search.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "message-send", - "description": "Send a message to a channel, DM, or thread (content sent verbatim)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "target", - "type": "str", - "required": true, - "positional": true, - "help": "\"#channel\", \"#channel:msgIdOrShort\", \"dm:@name\", \"dm:\", or channel UUID" - }, - { - "name": "content", - "type": "str", - "required": true, - "positional": true, - "help": "Message body (sent verbatim, no marker)" - }, - { - "name": "dry-run", - "type": "bool", - "default": false, - "required": false, - "help": "Print the planned payload without sending" - }, - { - "name": "as-task", - "type": "bool", - "default": false, - "required": false, - "help": "Create the message as a task (asTask)" - }, - { - "name": "attach", - "type": "str", - "required": false, - "help": "Comma-separated attachmentId UUIDs (upload separately first)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server (slug or id)" - } - ], - "columns": [ - "target", - "channelId", - "content", - "result", - "messageId" - ], - "type": "js", - "modulePath": "plugins/slock/message-send.js", - "sourceFile": "plugins/slock/message-send.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "reaction-add", - "description": "Add an emoji reaction to a message (POST /messages/:id/reactions). Idempotent server-side.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full messageId UUID (short ids rejected)" - }, - { - "name": "emoji", - "type": "str", - "required": true, - "positional": true, - "help": "A single unicode emoji, e.g. 👍" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "messageId", - "emoji", - "result" - ], - "type": "js", - "modulePath": "plugins/slock/reaction-add.js", - "sourceFile": "plugins/slock/reaction-add.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "reaction-remove", - "description": "Remove your emoji reaction from a message (DELETE /messages/:id/reactions).", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full messageId UUID (short ids rejected)" - }, - { - "name": "emoji", - "type": "str", - "required": true, - "positional": true, - "help": "The unicode emoji to remove, e.g. 👍" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "messageId", - "emoji", - "result" - ], - "type": "js", - "modulePath": "plugins/slock/reaction-remove.js", - "sourceFile": "plugins/slock/reaction-remove.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "server-list", - "description": "List slock servers you belong to; marks active per localStorage slug", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", - "slug", - "name", - "active" - ], - "type": "js", - "modulePath": "plugins/slock/server-list.js", - "sourceFile": "plugins/slock/server-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "server-use", - "description": "Set the active slock server (writes localStorage.slock_last_server_slug)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "server slug, \"#slug\", or UUID id" - } - ], - "columns": [ - "id", - "slug", - "name", - "written" - ], - "type": "js", - "modulePath": "plugins/slock/server-use.js", - "sourceFile": "plugins/slock/server-use.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-claim", - "description": "Claim a chat task (PATCH /tasks/:id/claim). Requires full task UUID (= message id).", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "taskId", - "type": "str", - "required": true, - "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "taskId", - "taskStatus", - "assigneeId", - "taskNumber" - ], - "type": "js", - "modulePath": "plugins/slock/task-claim.js", - "sourceFile": "plugins/slock/task-claim.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-convert", - "description": "Convert a message into a chat task (POST /tasks/convert-message). Accepts a message UUID or \"#channel:shortId\".", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "messageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full message UUID, or \"#channel:shortId\" (short id expanded via /messages/context)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "channelId" - ], - "type": "js", - "modulePath": "plugins/slock/task-convert.js", - "sourceFile": "plugins/slock/task-convert.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-create", - "description": "Create a task in a channel (single title; batch 1-50 is server-supported but client surface is single — see backlog R4).", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "title", - "type": "str", - "required": true, - "positional": true, - "help": "Task title (single; batch TODO via R4)" - }, - { - "name": "desc", - "type": "str", - "required": false, - "help": "Optional description body for the task" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "channelId" - ], - "type": "js", - "modulePath": "plugins/slock/task-create.js", - "sourceFile": "plugins/slock/task-create.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-delete", - "description": "Delete a chat task (DELETE /tasks/:taskId). Requires --confirm — destructive, irreversible.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "taskId", - "type": "str", - "required": true, - "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" - }, - { - "name": "confirm", - "type": "bool", - "default": false, - "required": false, - "help": "Required acknowledgement: deletion is irreversible" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "taskId", - "deleted" - ], - "type": "js", - "modulePath": "plugins/slock/task-delete.js", - "sourceFile": "plugins/slock/task-delete.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-get", - "description": "Fetch a task by channel + taskNumber (GET /tasks/channel/:channelId/number/:taskNumber).", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "number", - "type": "str", - "required": true, - "positional": true, - "help": "taskNumber (per-channel integer, as shown in \"task #N\")" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "assigneeId" - ], - "type": "js", - "modulePath": "plugins/slock/task-get.js", - "sourceFile": "plugins/slock/task-get.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-list", - "description": "List tasks (chat tasks = messages with task fields) attached to a channel. Optional --status filter.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "channelId UUID or #name" - }, - { - "name": "status", - "type": "str", - "required": false, - "help": "Filter by status: todo|in_progress|in_review|done|closed" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "assigneeId" - ], - "type": "js", - "modulePath": "plugins/slock/task-list.js", - "sourceFile": "plugins/slock/task-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-list-server", - "description": "List tasks across all channels in the active server (GET /tasks/server). Optional --status filter.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "status", - "type": "str", - "required": false, - "help": "Filter by status: todo|in_progress|in_review|done|closed" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "id", - "taskNumber", - "title", - "taskStatus", - "channelId", - "assigneeId" - ], - "type": "js", - "modulePath": "plugins/slock/task-list-server.js", - "sourceFile": "plugins/slock/task-list-server.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-status", - "description": "Set a task's status (PATCH /tasks/:taskId/status, body {status}). One of todo|in_progress|in_review|done|closed.", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "taskId", - "type": "str", - "required": true, - "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" - }, - { - "name": "status", - "type": "str", - "required": true, - "positional": true, - "help": "One of: todo|in_progress|in_review|done|closed" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "taskId", - "taskStatus", - "assigneeId", - "taskNumber" - ], - "type": "js", - "modulePath": "plugins/slock/task-status.js", - "sourceFile": "plugins/slock/task-status.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "task-unclaim", - "description": "Release ownership of a chat task (PATCH /tasks/:id/unclaim).", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "taskId", - "type": "str", - "required": true, - "positional": true, - "help": "Full task UUID (= message id; short ids rejected)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "taskId", - "taskStatus", - "assigneeId", - "taskNumber" - ], - "type": "js", - "modulePath": "plugins/slock/task-unclaim.js", - "sourceFile": "plugins/slock/task-unclaim.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "thread-done", - "description": "Mark a thread as done / hide it from the active list (POST /channels/threads/done)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "threadChannelId", - "type": "str", - "required": true, - "positional": true, - "help": "Thread channel UUID (from thread-list / message-read)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "threadChannelId", - "result" - ], - "type": "js", - "modulePath": "plugins/slock/thread-done.js", - "sourceFile": "plugins/slock/thread-done.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "thread-follow", - "description": "Follow the thread on a parent message (POST /channels/threads/follow)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "parentMessageId", - "type": "str", - "required": true, - "positional": true, - "help": "Full parent messageId UUID (short ids rejected)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "parentMessageId", - "threadChannelId", - "result" - ], - "type": "js", - "modulePath": "plugins/slock/thread-follow.js", - "sourceFile": "plugins/slock/thread-follow.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "thread-list", - "description": "List followed threads in the active server (GET /channels/threads/followed)", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "threadChannelId", - "parentMessageId", - "parentChannelName", - "unreadCount", - "replyCount", - "lastReplyAt" - ], - "type": "js", - "modulePath": "plugins/slock/thread-list.js", - "sourceFile": "plugins/slock/thread-list.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "thread-undone", - "description": "Restore a done thread to the active list (POST /channels/threads/undone)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "threadChannelId", - "type": "str", - "required": true, - "positional": true, - "help": "Thread channel UUID (from thread-list / message-read)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "threadChannelId", - "result" - ], - "type": "js", - "modulePath": "plugins/slock/thread-undone.js", - "sourceFile": "plugins/slock/thread-undone.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "thread-unfollow", - "description": "Stop following a thread (POST /channels/threads/unfollow)", - "access": "write", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "threadChannelId", - "type": "str", - "required": true, - "positional": true, - "help": "Thread channel UUID (from thread-list / message-read)" - }, - { - "name": "server", - "type": "str", - "required": false, - "help": "Override active server" - } - ], - "columns": [ - "threadChannelId", - "result" - ], - "type": "js", - "modulePath": "plugins/slock/thread-unfollow.js", - "sourceFile": "plugins/slock/thread-unfollow.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "unread-summary", - "description": "Global unread counts across every server you belong to.", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "serverId", - "slug", - "name", - "unreadCount" - ], - "type": "js", - "modulePath": "plugins/slock/unread-summary.js", - "sourceFile": "plugins/slock/unread-summary.js", - "navigateBefore": "https://app.slock.ai", - "siteSession": "persistent" - }, - { - "site": "slock", - "name": "whoami", - "description": "Show the current logged-in slock account", - "access": "read", - "domain": "app.slock.ai", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "id", - "name", - "email" - ], - "type": "js", - "modulePath": "plugins/slock/whoami.js", - "sourceFile": "plugins/slock/whoami.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "spotify", - "name": "auth", - "description": "Authenticate with Spotify (OAuth — run once)", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "spotify", - "name": "next", - "description": "Skip to next track", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "spotify", - "name": "pause", - "description": "Pause playback", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "spotify", - "name": "play", - "description": "Resume playback or search and play a track/artist", - "access": "write", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "default": "", - "required": false, - "positional": true, - "help": "Track or artist to play (optional)" - } - ], - "columns": [ - "track", - "artist", - "status" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "spotify", - "name": "prev", - "description": "Skip to previous track", - "access": "write", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "status" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "spotify", - "name": "queue", - "description": "Add a track to the playback queue", - "access": "write", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Track to add to queue" - } - ], - "columns": [ - "track", - "artist", - "status" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "spotify", - "name": "repeat", - "description": "Set repeat mode (off / track / context)", - "access": "write", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "mode", - "type": "str", - "default": "context", - "required": false, - "positional": true, - "help": "off / track / context", - "choices": [ - "off", - "track", - "context" - ] - } - ], - "columns": [ - "repeat" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "spotify", - "name": "search", - "description": "Search for tracks", - "access": "read", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results (default: 10)" - } - ], - "columns": [ - "track", - "artist", - "album", - "uri" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "spotify", - "name": "shuffle", - "description": "Toggle shuffle on/off", - "access": "write", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "state", - "type": "str", - "default": "on", - "required": false, - "positional": true, - "help": "on or off", - "choices": [ - "on", - "off" - ] - } - ], - "columns": [ - "shuffle" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "spotify", - "name": "status", - "description": "Show current playback status", - "access": "read", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "track", - "artist", - "album", - "status", - "progress" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "spotify", - "name": "volume", - "description": "Set playback volume (0-100)", - "access": "write", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "level", - "type": "int", - "default": 50, - "required": true, - "positional": true, - "help": "Volume 0–100" - } - ], - "columns": [ - "volume" - ], - "type": "js", - "modulePath": "plugins/spotify/spotify.js", - "sourceFile": "plugins/spotify/spotify.js" - }, - { - "site": "stackoverflow", - "name": "bounties", - "description": "Active bounties on Stack Overflow", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max number of results" - } - ], - "columns": [ - "rank", - "id", - "bounty", - "title", - "score", - "answers", - "views", - "is_answered", - "tags", - "author", - "creation_date", - "url" - ], - "type": "js", - "modulePath": "plugins/stackoverflow/bounties.js", - "sourceFile": "plugins/stackoverflow/bounties.js" - }, - { - "site": "stackoverflow", - "name": "hot", - "description": "Hot Stack Overflow questions", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max number of results" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "is_answered", - "tags", - "author", - "creation_date", - "url" - ], - "type": "js", - "modulePath": "plugins/stackoverflow/hot.js", - "sourceFile": "plugins/stackoverflow/hot.js" - }, - { - "site": "stackoverflow", - "name": "read", - "description": "Read a Stack Overflow question with answers and comments", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Stack Overflow question id (numeric, e.g. 79935770)" - }, - { - "name": "answers-limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max answers to include (1-100; accepted answer always included first)" - }, - { - "name": "comments-limit", - "type": "int", - "default": 5, - "required": false, - "help": "Max comments per question/answer (1-100)" - }, - { - "name": "max-length", - "type": "int", - "default": 4000, - "required": false, - "help": "Max characters per body / answer / comment (min 100)" - } - ], - "columns": [ - "type", - "author", - "score", - "accepted", - "text" - ], - "type": "js", - "modulePath": "plugins/stackoverflow/read.js", - "sourceFile": "plugins/stackoverflow/read.js" - }, - { - "site": "stackoverflow", - "name": "related", - "description": "List Stack Overflow questions related to a given question id.", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "string", - "required": true, - "positional": true, - "help": "Stack Overflow question id (numeric, e.g. 79935770)." - }, - { - "name": "sort", - "type": "string", - "default": "rank", - "required": false, - "help": "Sort key: rank, activity, votes, creation (rank = SO relevance default)." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max related questions (1-100)." - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "isAnswered", - "tags", - "author", - "createdAt", - "lastActivityAt", - "url" - ], - "type": "js", - "modulePath": "plugins/stackoverflow/related.js", - "sourceFile": "plugins/stackoverflow/related.js" - }, - { - "site": "stackoverflow", - "name": "search", - "description": "Search Stack Overflow questions", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "string", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max number of results" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "is_answered", - "tags", - "author", - "creation_date", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/stackoverflow/search.js", - "sourceFile": "plugins/stackoverflow/search.js" - }, - { - "site": "stackoverflow", - "name": "tag", - "description": "List Stack Overflow questions tagged with a given tag (most active first).", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "tag", - "type": "string", - "required": true, - "positional": true, - "help": "Tag slug (e.g. python, rust, typescript)." - }, - { - "name": "sort", - "type": "string", - "default": "activity", - "required": false, - "help": "Sort key: activity, votes, creation, hot, week, month" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max questions to return (max 100)." - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "isAnswered", - "tags", - "author", - "createdAt", - "lastActivityAt", - "url" - ], - "type": "js", - "modulePath": "plugins/stackoverflow/tag.js", - "sourceFile": "plugins/stackoverflow/tag.js" - }, - { - "site": "stackoverflow", - "name": "unanswered", - "description": "Top voted unanswered questions on Stack Overflow", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max number of results" - } - ], - "columns": [ - "rank", - "id", - "title", - "score", - "answers", - "views", - "tags", - "author", - "creation_date", - "url" - ], - "type": "js", - "modulePath": "plugins/stackoverflow/unanswered.js", - "sourceFile": "plugins/stackoverflow/unanswered.js" - }, - { - "site": "stackoverflow", - "name": "user", - "description": "Find Stack Overflow users by display name (highest reputation first).", - "access": "read", - "domain": "stackoverflow.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "name", - "type": "string", - "required": true, - "positional": true, - "help": "Display name (or substring) to search." - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max users to return (max 100)." - } - ], - "columns": [ - "userId", - "displayName", - "reputation", - "goldBadges", - "silverBadges", - "bronzeBadges", - "location", - "createdAt", - "lastAccessAt", - "url" - ], - "type": "js", - "modulePath": "plugins/stackoverflow/user.js", - "sourceFile": "plugins/stackoverflow/user.js" - }, - { - "site": "steam", - "name": "app", - "description": "Steam storefront detail for a single app id", - "access": "read", - "domain": "store.steampowered.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Numeric Steam app id (e.g. \"620\" for Portal 2)" - }, - { - "name": "currency", - "type": "str", - "default": "us", - "required": false, - "help": "Storefront country code (e.g. us / cn / jp / de)" - } - ], - "columns": [ - "id", - "name", - "type", - "isFree", - "releaseDate", - "developers", - "publishers", - "price", - "currency", - "metacritic", - "recommendations", - "genres", - "categories", - "shortDescription", - "website", - "url" - ], - "type": "js", - "modulePath": "plugins/steam/app.js", - "sourceFile": "plugins/steam/app.js" - }, - { - "site": "steam", - "name": "search", - "description": "Search the Steam storefront by name keyword", - "access": "read", - "domain": "store.steampowered.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (e.g. \"portal\", \"stardew\")" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (1-50)" - }, - { - "name": "currency", - "type": "str", - "default": "us", - "required": false, - "help": "Storefront country code (e.g. us / cn / jp / de)" - } - ], - "columns": [ - "rank", - "id", - "name", - "price", - "currency", - "metascore", - "platforms", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/steam/search.js", - "sourceFile": "plugins/steam/search.js" - }, - { - "site": "steam", - "name": "top-sellers", - "description": "Steam top selling games", - "access": "read", - "domain": "store.steampowered.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of games" - } - ], - "columns": [ - "rank", - "name", - "price", - "discount", - "url" - ], - "type": "js", - "modulePath": "plugins/steam/top-sellers.js", - "sourceFile": "plugins/steam/top-sellers.js" - }, - { - "site": "substack", - "name": "feed", - "description": "Substack popular posts Feed", - "access": "read", - "domain": "substack.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "category", - "type": "str", - "default": "all", - "required": false, - "help": "Post category: all, tech, business, culture, politics, science, health" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" - } - ], - "columns": [ - "rank", - "title", - "author", - "date", - "readTime", - "url" - ], - "type": "js", - "modulePath": "plugins/substack/feed.js", - "sourceFile": "plugins/substack/feed.js", - "navigateBefore": "https://substack.com" - }, - { - "site": "substack", - "name": "publication", - "description": "Get a specific Substack Newsletter latest posts", - "access": "read", - "domain": "substack.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "Newsletter URL(for example https://example.substack.com)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of posts to return" - } - ], - "columns": [ - "rank", - "title", - "date", - "description", - "url" - ], - "type": "js", - "modulePath": "plugins/substack/publication.js", - "sourceFile": "plugins/substack/publication.js", - "navigateBefore": "https://substack.com" - }, - { - "site": "substack", - "name": "search", - "description": "Search Substack posts and newsletters", - "access": "read", - "domain": "substack.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "type", - "type": "str", - "default": "posts", - "required": false, - "help": "Search type(posts=posts, publications=Newsletter)", - "choices": [ - "posts", - "publications" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results to return" - } - ], - "columns": [ - "rank", - "title", - "author", - "date", - "description", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/substack/search.js", - "sourceFile": "plugins/substack/search.js" - }, - { - "site": "suno", - "name": "download", - "description": "Download an existing Suno clip (MP3 + optional WAV/M4A/video) by id", - "access": "write", - "domain": "suno.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "clip", - "type": "str", - "required": true, - "positional": true, - "help": "Clip UUID or https://suno.com/song/ URL" - }, - { - "name": "formats", - "type": "str", - "required": false, - "help": "Comma-separated formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" - }, - { - "name": "op", - "type": "str", - "required": false, - "help": "Output directory (default: ~/Music/suno)" - }, - { - "name": "confirm-paid", - "type": "boolean", - "default": false, - "required": false, - "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." - } - ], - "columns": [ - "status", - "clip", - "title", - "files", - "link" - ], - "defaultFormat": "plain", - "type": "js", - "modulePath": "plugins/suno/download.js", - "sourceFile": "plugins/suno/download.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "suno", - "name": "generate", - "description": "Generate music with Suno (V5.5 chirp-fenix by default) and download clips locally", - "access": "write", - "domain": "suno.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": false, - "positional": true, - "help": "Simple-mode description (ignored when --lyrics is provided)" - }, - { - "name": "lyrics", - "type": "str", - "required": false, - "help": "Custom-mode lyrics (with [Verse]/[Chorus] metatags). Triggers Custom mode." - }, - { - "name": "tags", - "type": "str", - "required": false, - "help": "Custom-mode style tags (genre, BPM, instruments...). Used with --lyrics." - }, - { - "name": "negative-tags", - "type": "str", - "required": false, - "help": "Custom-mode style exclusions (e.g. \"no vocals, no autotune\"). Used with --lyrics." - }, - { - "name": "title", - "type": "str", - "required": false, - "help": "Song title (default: auto-derived from prompt)" - }, - { - "name": "instrumental", - "type": "boolean", - "default": false, - "required": false, - "help": "No vocals" - }, - { - "name": "model", - "type": "str", - "required": false, - "help": "Model id: chirp-fenix, chirp-bluejay, chirp-v4, chirp-v3-5. Default: chirp-fenix" - }, - { - "name": "weirdness", - "type": "str", - "required": false, - "help": "Creative weirdness slider (0..1). Default: 0.5" - }, - { - "name": "style-weight", - "type": "str", - "required": false, - "help": "Style adherence slider (0..1). Default: 0.5" - }, - { - "name": "formats", - "type": "str", - "required": false, - "help": "Comma-separated download formats: mp3, m4a, wav, video, cover, metadata. Default: mp3,metadata" - }, - { - "name": "op", - "type": "str", - "required": false, - "help": "Output directory (default: ~/Music/suno)" - }, - { - "name": "timeout", - "type": "int", - "default": 300, - "required": false, - "help": "Max seconds to wait for clips to finish (default: 300)" - }, - { - "name": "sd", - "type": "boolean", - "default": false, - "required": false, - "help": "Skip download; only print clip ids and Suno URLs" - }, - { - "name": "confirm-paid", - "type": "boolean", - "default": false, - "required": false, - "help": "Required to allow paid downloads (wav). Without it, paid formats are skipped with a warning." - } - ], - "columns": [ - "status", - "clip", - "title", - "files", - "link" - ], - "defaultFormat": "plain", - "type": "js", - "modulePath": "plugins/suno/generate.js", - "sourceFile": "plugins/suno/generate.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "suno", - "name": "list", - "description": "List recent Suno clips in your library (id, title, status, created_at, link)", - "access": "read", - "domain": "suno.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max clips to list (default: 20)" - }, - { - "name": "page", - "type": "int", - "default": 0, - "required": false, - "help": "Pagination offset, 0-based (default: 0)" - } - ], - "columns": [ - "rank", - "clip", - "title", - "status", - "created", - "link" - ], - "type": "js", - "modulePath": "plugins/suno/list.js", - "sourceFile": "plugins/suno/list.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "suno", - "name": "login", - "description": "Open suno login", - "access": "write", - "domain": "suno.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/suno/auth.js", - "sourceFile": "plugins/suno/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "suno", - "name": "status", - "description": "Check Suno login, plan, credit balance, and captcha readiness", - "access": "read", - "domain": "suno.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "Status", - "Plan", - "Credits", - "Monthly", - "Captcha" - ], - "type": "js", - "modulePath": "plugins/suno/status.js", - "sourceFile": "plugins/suno/status.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "suno", - "name": "whoami", - "description": "Show the current logged-in suno account", - "access": "read", - "domain": "suno.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "name" - ], - "type": "js", - "modulePath": "plugins/suno/auth.js", - "sourceFile": "plugins/suno/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "techcrunch", - "name": "article", - "description": "Read a TechCrunch article from its URL", - "access": "read", - "domain": "techcrunch.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "TechCrunch article URL" - } - ], - "columns": [ - "title", - "author", - "publishedAt", - "categories", - "description", - "content", - "url" - ], - "type": "js", - "modulePath": "plugins/techcrunch/article.js", - "sourceFile": "plugins/techcrunch/article.js" - }, - { - "site": "techcrunch", - "name": "search", - "description": "Search TechCrunch stories or list the latest stories", - "access": "read", - "domain": "techcrunch.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "string", - "required": false, - "positional": true, - "help": "Words to search for" - }, - { - "name": "latest", - "type": "boolean", - "default": false, - "required": false, - "help": "List the latest stories instead of searching" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum stories to return (1-50)" - } - ], - "columns": [ - "rank", - "title", - "author", - "publishedAt", - "description", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/techcrunch/search.js", - "sourceFile": "plugins/techcrunch/search.js" - }, - { - "site": "tiktok", - "name": "comment", - "description": "Post a comment on a TikTok video", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok video URL (https://www.tiktok.com/@user/video/)" - }, - { - "name": "text", - "type": "str", - "required": true, - "positional": true, - "help": "Comment text (≤150 chars)" - } - ], - "columns": [ - "url", - "text", - "result" - ], - "type": "js", - "modulePath": "plugins/tiktok/comment.js", - "sourceFile": "plugins/tiktok/comment.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "creator-videos", - "description": "TikTok Studio creator content list (views/likes/comments/saves/shares)", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of creator videos to return (max 250)" - }, - { - "name": "cursor", - "type": "string", - "default": "0", - "required": false, - "help": "Non-negative TikTok Studio pagination cursor" - } - ], - "columns": [ - "video_id", - "title", - "date", - "views", - "likes", - "comments", - "saves", - "shares", - "url" - ], - "type": "js", - "modulePath": "plugins/tiktok/creator-videos.js", - "sourceFile": "plugins/tiktok/creator-videos.js", - "navigateBefore": "https://www.tiktok.com/tiktokstudio/content" - }, - { - "site": "tiktok", - "name": "explore", - "description": "Get trending TikTok videos from the recommend feed via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of videos to return (max 120)" - } - ], - "columns": [ - "index", - "id", - "author", - "url", - "cover", - "title", - "desc", - "plays", - "likes", - "comments", - "shares", - "createTime" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/tiktok/explore.js", - "sourceFile": "plugins/tiktok/explore.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "follow", - "description": "Follow a TikTok user by username", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok username (without @)" - } - ], - "columns": [ - "username", - "url", - "result" - ], - "type": "js", - "modulePath": "plugins/tiktok/follow.js", - "sourceFile": "plugins/tiktok/follow.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "following", - "description": "List accounts the logged-in user follows on TikTok via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of accounts (max 200)" - } - ], - "columns": [ - "index", - "username", - "name", - "secUid", - "verified", - "followers", - "following", - "url" - ], - "type": "js", - "modulePath": "plugins/tiktok/following.js", - "sourceFile": "plugins/tiktok/following.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "friends", - "description": "Get TikTok friend / who-to-follow suggestions via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of suggestions (max 100)" - } - ], - "columns": [ - "index", - "username", - "name", - "secUid", - "verified", - "followers", - "following", - "url" - ], - "type": "js", - "modulePath": "plugins/tiktok/friends.js", - "sourceFile": "plugins/tiktok/friends.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "like", - "description": "Like a TikTok video", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok video URL" - } - ], - "columns": [ - "status", - "likes", - "url" - ], - "type": "js", - "modulePath": "plugins/tiktok/like.js", - "sourceFile": "plugins/tiktok/like.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "live", - "description": "Browse TikTok live streams via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of streams (max 60)" - } - ], - "columns": [ - "index", - "streamer", - "name", - "title", - "viewers", - "likes", - "secUid", - "url" - ], - "type": "js", - "modulePath": "plugins/tiktok/live.js", - "sourceFile": "plugins/tiktok/live.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "login", - "description": "Open tiktok login", - "access": "write", - "domain": "tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "sec_uid", - "username", - "nickname", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/tiktok/auth.js", - "sourceFile": "plugins/tiktok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "tiktok", - "name": "notifications", - "description": "Read TikTok inbox notifications (likes, comments, mentions, followers) via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Number of notifications (max 100)" - }, - { - "name": "type", - "type": "str", - "default": "all", - "required": false, - "help": "Notification type", - "choices": [ - "all", - "likes", - "comments", - "mentions", - "followers" - ] - } - ], - "columns": [ - "index", - "id", - "from", - "text", - "createTime" - ], - "type": "js", - "modulePath": "plugins/tiktok/notifications.js", - "sourceFile": "plugins/tiktok/notifications.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "profile", - "description": "Get TikTok user profile info", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok username (without @)" - } - ], - "columns": [ - "username", - "name", - "followers", - "following", - "likes", - "videos", - "verified", - "bio" - ], - "type": "js", - "modulePath": "plugins/tiktok/profile.js", - "sourceFile": "plugins/tiktok/profile.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "save", - "description": "Add a TikTok video to Favorites", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok video URL" - } - ], - "columns": [ - "status", - "url" - ], - "type": "js", - "modulePath": "plugins/tiktok/save.js", - "sourceFile": "plugins/tiktok/save.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "search", - "description": "Search TikTok videos", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Number of results" - } - ], - "columns": [ - "rank", - "desc", - "author", - "url", - "plays", - "likes", - "comments", - "shares" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/tiktok/search.js", - "sourceFile": "plugins/tiktok/search.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "unfollow", - "description": "Unfollow a TikTok user by username", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok username (without @)" - } - ], - "columns": [ - "username", - "url", - "result" - ], - "type": "js", - "modulePath": "plugins/tiktok/unfollow.js", - "sourceFile": "plugins/tiktok/unfollow.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "unlike", - "description": "Unlike a TikTok video", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok video URL" - } - ], - "columns": [ - "status", - "likes", - "url" - ], - "type": "js", - "modulePath": "plugins/tiktok/unlike.js", - "sourceFile": "plugins/tiktok/unlike.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "unsave", - "description": "Remove a TikTok video from Favorites", - "access": "write", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok video URL" - } - ], - "columns": [ - "status", - "url" - ], - "type": "js", - "modulePath": "plugins/tiktok/unsave.js", - "sourceFile": "plugins/tiktok/unsave.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "user", - "description": "Get recent videos from a TikTok user via page-context APIs", - "access": "read", - "domain": "www.tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": true, - "positional": true, - "help": "TikTok username (without @)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of videos to return (max 120)" - } - ], - "columns": [ - "index", - "id", - "source", - "author", - "url", - "cover", - "title", - "desc", - "plays", - "likes", - "comments", - "shares", - "createTime" - ], - "type": "js", - "modulePath": "plugins/tiktok/user.js", - "sourceFile": "plugins/tiktok/user.js", - "navigateBefore": "https://www.tiktok.com" - }, - { - "site": "tiktok", - "name": "whoami", - "description": "Show the current logged-in tiktok account", - "access": "read", - "domain": "tiktok.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "sec_uid", - "username", - "nickname" - ], - "type": "js", - "modulePath": "plugins/tiktok/auth.js", - "sourceFile": "plugins/tiktok/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "trae-solo", - "name": "automation-list", - "description": "List Trae SOLO Automation tab content. Default tab is \"Configured\"; pass --tab to switch.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "tab", - "type": "str", - "default": "configured", - "required": false, - "help": "Tab to view: configured / run-history / task-template" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Title", - "Summary" - ], - "type": "js", - "modulePath": "plugins/trae-solo/automation.js", - "sourceFile": "plugins/trae-solo/automation.js", - "navigateBefore": true - }, - { - "site": "trae-solo", - "name": "cookies", - "description": "List cookies on the Trae SOLO renderer (JS-visible via document.cookie; httpOnly cookies not shown).", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version" - ], - "type": "js", - "modulePath": "plugins/trae-solo/renderer-storage.js", - "sourceFile": "plugins/trae-solo/renderer-storage.js", - "navigateBefore": true - }, - { - "site": "trae-solo", - "name": "extensions-list", - "description": "List VSCode extensions installed in Trae SOLO (~/.trae/extensions/extensions.json). Works while Trae is closed.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "Index", - "Workspace Id", - "Kind", - "Target", - "Modified", - "Id", - "Version", - "Source", - "Installed" - ], - "type": "js", - "modulePath": "plugins/trae-solo/workspaces-fs.js", - "sourceFile": "plugins/trae-solo/workspaces-fs.js" - }, - { - "site": "trae-solo", - "name": "history", - "description": "List Trae SOLO projects and the tasks within each (from the project-list view sidebar).", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "project", - "type": "str", - "required": false, - "help": "Filter by project name (substring, case-insensitive)" - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Max tasks per project" - } - ], - "columns": [ - "Project", - "Task Index", - "Task" - ], - "type": "js", - "modulePath": "plugins/trae-solo/history.js", - "sourceFile": "plugins/trae-solo/history.js", - "navigateBefore": true - }, - { - "site": "trae-solo", - "name": "idb-list", - "description": "List IndexedDB databases on the Trae SOLO renderer. Trae ships an @byted/ve-rtc DB used by the Volcengine RTC voice/video infrastructure.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version" - ], - "type": "js", - "modulePath": "plugins/trae-solo/renderer-storage.js", - "sourceFile": "plugins/trae-solo/renderer-storage.js", - "navigateBefore": true - }, - { - "site": "trae-solo", - "name": "mode", - "description": "Read or switch TRAE SOLO between Code mode and Work mode.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "target", - "type": "str", - "required": false, - "positional": true, - "help": "Target mode: code or work. Omit to read current." - } - ], - "columns": [ - "Status", - "Mode" - ], - "type": "js", - "modulePath": "plugins/trae-solo/mode.js", - "sourceFile": "plugins/trae-solo/mode.js", - "navigateBefore": true - }, - { - "site": "trae-solo", - "name": "model", - "description": "Read or switch the current AI model in TRAE SOLO. Without arguments, reports the current model. With argument (substring, case-insensitive), switches to a matching model. Pass --list to enumerate available models.", - "access": "write", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": false, - "positional": true, - "help": "Target model name (substring match, case-insensitive). Omit to read current." - }, - { - "name": "list", - "type": "boolean", - "default": false, - "required": false, - "help": "List all available models (does not switch)" - } - ], - "columns": [ - "Status", - "Model" - ], - "type": "js", - "modulePath": "plugins/trae-solo/model.js", - "sourceFile": "plugins/trae-solo/model.js", - "navigateBefore": true - }, - { - "site": "trae-solo", - "name": "recent-workspaces", - "description": "Show Trae SOLO's recently-opened workspaces (the File → Open Recent menu, stored under key \"history.recentlyOpenedPathsList\" in state.vscdb).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Key", - "Kind", - "Path" - ], - "type": "js", - "modulePath": "plugins/trae-solo/state-fs.js", - "sourceFile": "plugins/trae-solo/state-fs.js" - }, - { - "site": "trae-solo", - "name": "settings-read", - "description": "Parse and pretty-print Trae SOLO user settings.json (~/Library/Application Support/TRAE SOLO/User/settings.json). Handles VSCode JSONC syntax (line comments + trailing commas).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/trae-solo/settings.js", - "sourceFile": "plugins/trae-solo/settings.js" - }, - { - "site": "trae-solo", - "name": "skill-category", - "description": "Filter Skills Marketplace by category. Pass --list to see categories.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "name", - "type": "str", - "required": false, - "positional": true, - "help": "Category name (substring; case-insensitive). Common: All / Developer Tools / Data Analysis / UI Design / Content Creation / Productivity" - }, - { - "name": "list", - "type": "boolean", - "default": false, - "required": false, - "help": "List available categories" - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Name", - "Description" - ], - "type": "js", - "modulePath": "plugins/trae-solo/skill.js", - "sourceFile": "plugins/trae-solo/skill.js", - "navigateBefore": true - }, - { - "site": "trae-solo", - "name": "skill-fs-installed", - "description": "List INSTALLED Trae SOLO skills (managedSkills entry in ~/.trae/skill-config.json).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "Index", - "Name", - "Description", - "Source" - ], - "type": "js", - "modulePath": "plugins/trae-solo/skill-fs.js", - "sourceFile": "plugins/trae-solo/skill-fs.js" - }, - { - "site": "trae-solo", - "name": "skill-fs-list", - "description": "List all Trae SOLO skills present on disk under ~/.trae/skills/. Reads SKILL.md front-matter for descriptions. Works while Trae is closed.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 200, - "required": false, - "help": "Max rows" - } - ], - "columns": [ - "Index", - "Name", - "Description", - "Source" - ], - "type": "js", - "modulePath": "plugins/trae-solo/skill-fs.js", - "sourceFile": "plugins/trae-solo/skill-fs.js" - }, - { - "site": "trae-solo", - "name": "skill-fs-show", - "description": "Print a skill's SKILL.md content + on-disk path.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "name", - "type": "str", - "required": true, - "positional": true, - "help": "Skill name (folder under ~/.trae/skills/)" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/trae-solo/skill-fs.js", - "sourceFile": "plugins/trae-solo/skill-fs.js" - }, - { - "site": "trae-solo", - "name": "skill-list", - "description": "List Trae SOLO Skills — by default the Marketplace; pass --installed to list installed ones.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "installed", - "type": "boolean", - "default": false, - "required": false, - "help": "List installed skills instead of the marketplace" - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Max rows to return" - } - ], - "columns": [ - "Index", - "Name", - "Description" - ], - "type": "js", - "modulePath": "plugins/trae-solo/skill.js", - "sourceFile": "plugins/trae-solo/skill.js", - "navigateBefore": true - }, - { - "site": "trae-solo", - "name": "skill-search", - "description": "Filter Skills Marketplace by keyword.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (substring)" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max rows" - } - ], - "columns": [ - "Index", - "Name", - "Description" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/trae-solo/skill.js", - "sourceFile": "plugins/trae-solo/skill.js", - "navigateBefore": true - }, - { - "site": "trae-solo", - "name": "state-get", - "description": "Read a single key from Trae SOLO's globalStorage state.vscdb. Pass --workspace to query a per-workspace DB instead. Returns parsed JSON if the value is JSON.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "State key (use state-keys to discover)" - }, - { - "name": "workspace", - "type": "str", - "required": false, - "help": "Workspace id (from workspaces-list) to query a per-workspace DB" - }, - { - "name": "max-bytes", - "type": "int", - "default": 8000, - "required": false, - "help": "Truncate value to this many bytes" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/trae-solo/state-fs.js", - "sourceFile": "plugins/trae-solo/state-fs.js" - }, - { - "site": "trae-solo", - "name": "state-keys", - "description": "List all keys present in Trae SOLO's globalStorage state.vscdb (VSCode-style UI/agent state). Pass --workspace to query a per-workspace DB instead. Use state-get to read a specific value. (See renderer storage-keys for browser-side LS/SS.)", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "filter", - "type": "str", - "required": false, - "help": "Case-insensitive substring filter over keys" - }, - { - "name": "workspace", - "type": "str", - "required": false, - "help": "Workspace id (from workspaces-list) to query a per-workspace DB" - }, - { - "name": "limit", - "type": "int", - "default": 200, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Key", - "Kind", - "Path" - ], - "type": "js", - "modulePath": "plugins/trae-solo/state-fs.js", - "sourceFile": "plugins/trae-solo/state-fs.js" - }, - { - "site": "trae-solo", - "name": "status", - "description": "Check active CDP connection to Trae SOLO Desktop", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [], - "columns": [ - "Status", - "Url", - "Title" - ], - "type": "js", - "modulePath": "plugins/trae-solo/status.js", - "sourceFile": "plugins/trae-solo/status.js", - "navigateBefore": true - }, - { - "site": "trae-solo", - "name": "storage-get", - "description": "Read a single localStorage / sessionStorage value on the Trae SOLO renderer.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "key", - "type": "str", - "required": true, - "positional": true, - "help": "Storage key (use storage-keys to discover)" - }, - { - "name": "storage", - "type": "str", - "default": "local", - "required": false, - "help": "\"local\" or \"session\"" - }, - { - "name": "max-bytes", - "type": "int", - "default": 4000, - "required": false, - "help": "Truncate value to this many chars" - } - ], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/trae-solo/renderer-storage.js", - "sourceFile": "plugins/trae-solo/renderer-storage.js", - "navigateBefore": true - }, - { - "site": "trae-solo", - "name": "storage-keys", - "description": "List localStorage / sessionStorage keys on the Trae SOLO renderer (CDP). For the on-disk VSCode state.vscdb, see state-keys.", - "access": "read", - "domain": "localhost", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "storage", - "type": "str", - "default": "local", - "required": false, - "help": "\"local\" or \"session\"" - }, - { - "name": "filter", - "type": "str", - "required": false, - "help": "Case-insensitive substring filter" - }, - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "Max rows to return" - } - ], - "columns": [ - "Index", - "Key", - "Bytes", - "Name", - "Preview", - "Database", - "Version" - ], - "type": "js", - "modulePath": "plugins/trae-solo/renderer-storage.js", - "sourceFile": "plugins/trae-solo/renderer-storage.js", - "navigateBefore": true - }, - { - "site": "trae-solo", - "name": "task-fs-list", - "description": "List Trae SOLO task ids from disk (snapshot/ + agentconfig/.json). Works while Trae is closed.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Task Id", - "Has Snapshot", - "Has Config", - "Modified", - "Phase", - "Turn Id", - "Commit" - ], - "type": "js", - "modulePath": "plugins/trae-solo/task-fs.js", - "sourceFile": "plugins/trae-solo/task-fs.js" - }, - { - "site": "trae-solo", - "name": "task-fs-show", - "description": "Show the workspace tree at a given chat-turn ref (via git ls-tree). Pass --turn to pick a turn; otherwise the latest after-chat-turn ref.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "task-id", - "type": "str", - "required": true, - "positional": true, - "help": "Task UUID" - }, - { - "name": "turn", - "type": "str", - "required": false, - "help": "Specific turn id (omit for latest after-chat-turn)" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "" - } - ], - "columns": [ - "Mode", - "Path", - "Size" - ], - "type": "js", - "modulePath": "plugins/trae-solo/task-fs.js", - "sourceFile": "plugins/trae-solo/task-fs.js" - }, - { - "site": "trae-solo", - "name": "task-fs-turns", - "description": "Show the chat-turn timeline for a Trae SOLO task as git tags (before-chat-turn-* / after-chat-turn-*).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "task-id", - "type": "str", - "required": true, - "positional": true, - "help": "Task UUID (folder name under snapshot/)" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Task Id", - "Has Snapshot", - "Has Config", - "Modified", - "Phase", - "Turn Id", - "Commit" - ], - "type": "js", - "modulePath": "plugins/trae-solo/task-fs.js", - "sourceFile": "plugins/trae-solo/task-fs.js" - }, - { - "site": "trae-solo", - "name": "user-rules", - "description": "Print Trae SOLO user rules (~/.trae/user_rules.md).", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [], - "columns": [ - "Field", - "Value" - ], - "type": "js", - "modulePath": "plugins/trae-solo/user-rules.js", - "sourceFile": "plugins/trae-solo/user-rules.js" - }, - { - "site": "trae-solo", - "name": "workspaces-list", - "description": "List Trae SOLO workspaceStorage entries (~/Library/.../TRAE SOLO/User/workspaceStorage//), resolving each workspace.json to its single-folder path or multi-folder workspace target. Works while Trae is closed.", - "access": "read", - "domain": "localhost", - "strategy": "local", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 100, - "required": false, - "help": "" - } - ], - "columns": [ - "Index", - "Workspace Id", - "Kind", - "Target", - "Modified", - "Id", - "Version", - "Source", - "Installed" - ], - "type": "js", - "modulePath": "plugins/trae-solo/workspaces-fs.js", - "sourceFile": "plugins/trae-solo/workspaces-fs.js" - }, - { - "site": "trip", - "name": "attraction", - "description": "Search Trip.com attractions and experiences by destination keyword", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Destination or attraction keyword (e.g. Tokyo / Paris / Louvre)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of results (1-50)" - } - ], - "columns": [ - "rank", - "name", - "rating", - "reviews", - "booked", - "price", - "currency", - "url" - ], - "type": "js", - "modulePath": "plugins/trip/attraction.js", - "sourceFile": "plugins/trip/attraction.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "car", - "description": "List Trip.com car-rental vehicles for a city (category, model, seats, daily price)", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "city", - "type": "str", - "required": true, - "positional": true, - "help": "Numeric Trip.com carhire city id (discover via the carhire search box)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of vehicles (1-50)" - } - ], - "columns": [ - "rank", - "category", - "vehicle", - "seats", - "price", - "currency", - "url" - ], - "type": "js", - "modulePath": "plugins/trip/car.js", - "sourceFile": "plugins/trip/car.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "deals", - "description": "List Trip.com live promotions from the Top Deals hub: campaign title, offer, discount, and link", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of deals (1-50)" - } - ], - "columns": [ - "rank", - "title", - "offer", - "discount", - "url" - ], - "type": "js", - "modulePath": "plugins/trip/deals.js", - "sourceFile": "plugins/trip/deals.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "flight", - "description": "Search Trip.com one-way flights by IATA route + departure date", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "from", - "type": "str", - "required": true, - "positional": true, - "help": "Departure IATA code (e.g. LON / LHR)" - }, - { - "name": "to", - "type": "str", - "required": true, - "positional": true, - "help": "Arrival IATA code (e.g. NYC / JFK)" - }, - { - "name": "date", - "type": "str", - "required": true, - "help": "Departure date (YYYY-MM-DD)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of flights (1-50)" - } - ], - "columns": [ - "rank", - "airline", - "departureTime", - "departureAirport", - "arrivalTime", - "arrivalAirport", - "duration", - "stops", - "price", - "currency", - "url" - ], - "type": "js", - "modulePath": "plugins/trip/flight.js", - "sourceFile": "plugins/trip/flight.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "flight-round", - "description": "Search Trip.com round-trip flights by IATA route + depart/return dates", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "from", - "type": "str", - "required": true, - "positional": true, - "help": "Departure IATA code (e.g. LON / LHR)" - }, - { - "name": "to", - "type": "str", - "required": true, - "positional": true, - "help": "Arrival IATA code (e.g. NYC / JFK)" - }, - { - "name": "depart", - "type": "str", - "required": true, - "help": "Outbound date (YYYY-MM-DD)" - }, - { - "name": "return", - "type": "str", - "required": true, - "help": "Return date (YYYY-MM-DD)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of flights (1-50)" - } - ], - "columns": [ - "rank", - "airline", - "departureTime", - "departureAirport", - "arrivalTime", - "arrivalAirport", - "duration", - "stops", - "price", - "currency", - "url" - ], - "type": "js", - "modulePath": "plugins/trip/flight-round.js", - "sourceFile": "plugins/trip/flight-round.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "hotel", - "description": "Show a Trip.com hotel detail by id (rating breakdown, amenities, check-in/out policy)", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Numeric Trip.com hotel id (discover via the hotels list; e.g. 715233)" - } - ], - "columns": [ - "hotelId", - "name", - "enName", - "star", - "score", - "scoreLabel", - "reviewCount", - "ratingBreakdown", - "facilities", - "checkInOut", - "cityName", - "address", - "lat", - "lon", - "url" - ], - "type": "js", - "modulePath": "plugins/trip/hotel.js", - "sourceFile": "plugins/trip/hotel.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "hotel-search", - "description": "List Trip.com hotels for a city id + check-in/out date range", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "city", - "type": "str", - "required": true, - "positional": true, - "help": "Numeric Trip.com city id (discover via the hotels search box; e.g. 338 for London)" - }, - { - "name": "checkin", - "type": "str", - "required": true, - "help": "Check-in date (YYYY-MM-DD)" - }, - { - "name": "checkout", - "type": "str", - "required": true, - "help": "Check-out date (YYYY-MM-DD)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of hotels (1-50)" - } - ], - "columns": [ - "rank", - "name", - "score", - "reviewLabel", - "reviews", - "location", - "room", - "price", - "currency", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/trip/hotel-search.js", - "sourceFile": "plugins/trip/hotel-search.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "package", - "description": "Search Trip.com flight+hotel packages by route + dates; lists the package flight options priced at the bundle rate", - "access": "read", - "domain": "trip.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "from", - "type": "str", - "required": true, - "positional": true, - "help": "Origin city keyword (e.g. Seoul / London / Bangkok)" - }, - { - "name": "to", - "type": "str", - "required": true, - "positional": true, - "help": "Destination city keyword (e.g. Tokyo / Paris / Singapore)" - }, - { - "name": "depart", - "type": "str", - "required": true, - "help": "Outbound date (YYYY-MM-DD)" - }, - { - "name": "return", - "type": "str", - "required": true, - "help": "Return date (YYYY-MM-DD)" - }, - { - "name": "adults", - "type": "int", - "default": 2, - "required": false, - "help": "Number of adults (1-9, default 2)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of packages (1-50)" - } - ], - "columns": [ - "rank", - "airline", - "flightNo", - "from", - "to", - "departure", - "arrival", - "stops", - "price", - "currency" - ], - "type": "js", - "modulePath": "plugins/trip/package.js", - "sourceFile": "plugins/trip/package.js" - }, - { - "site": "trip", - "name": "search", - "description": "Suggest Trip.com destinations (cities, airports) for a keyword; resolves the ids the other commands take", - "access": "read", - "domain": "trip.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Destination keyword (e.g. Tokyo / Bali / London)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of suggestions (1-50)" - } - ], - "columns": [ - "rank", - "name", - "type", - "cityId", - "airportCode", - "province", - "country" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/trip/search.js", - "sourceFile": "plugins/trip/search.js" - }, - { - "site": "trip", - "name": "tour", - "description": "Search Trip.com tour packages by destination keyword (private or group tours)", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Destination or tour keyword (e.g. Tokyo / Kyoto / Bali)" - }, - { - "name": "type", - "type": "str", - "default": "private", - "required": false, - "help": "Tour line: private or group (default private)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of tours (1-50)" - } - ], - "columns": [ - "rank", - "name", - "type", - "rating", - "reviews", - "price", - "currency", - "url" - ], - "type": "js", - "modulePath": "plugins/trip/tour.js", - "sourceFile": "plugins/trip/tour.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "train", - "description": "Show a Trip.com train route timetable (departure/arrival times, duration, changes)", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "from", - "type": "str", - "required": true, - "positional": true, - "help": "Departure city (e.g. London / Paris / Shanghai)" - }, - { - "name": "to", - "type": "str", - "required": true, - "positional": true, - "help": "Arrival city (e.g. Manchester / Lyon / Beijing)" - }, - { - "name": "country", - "type": "str", - "required": true, - "help": "Route country slug (e.g. uk / france / italy / spain / germany / china)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of journeys (1-50)" - } - ], - "columns": [ - "rank", - "departureTime", - "fromStation", - "arrivalTime", - "toStation", - "duration", - "changes", - "url" - ], - "type": "js", - "modulePath": "plugins/trip/train.js", - "sourceFile": "plugins/trip/train.js", - "navigateBefore": false - }, - { - "site": "trip", - "name": "transfer", - "description": "List Trip.com airport-transfer vehicles for a city + airport (type, seats, from-price)", - "access": "read", - "domain": "trip.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "city", - "type": "str", - "required": true, - "positional": true, - "help": "Airport city (e.g. Bangkok / Beijing / Da Nang)" - }, - { - "name": "airport", - "type": "str", - "required": true, - "positional": true, - "help": "3-letter airport IATA code (e.g. DMK / PKX / DAD)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of vehicles (1-50)" - } - ], - "columns": [ - "rank", - "type", - "passengers", - "luggage", - "price", - "currency", - "url" - ], - "type": "js", - "modulePath": "plugins/trip/transfer.js", - "sourceFile": "plugins/trip/transfer.js", - "navigateBefore": false - }, - { - "site": "tvmaze", - "name": "search", - "description": "TVmaze TV show search by title (returns id, name, network, premiered/ended, rating)", - "access": "read", - "domain": "tvmaze.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "string", - "required": true, - "positional": true, - "help": "TV show title or fragment to search for" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max rows to return (1-50)" - } - ], - "columns": [ - "rank", - "id", - "name", - "type", - "language", - "genres", - "status", - "premiered", - "ended", - "network", - "rating", - "matchScore", - "summary", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/tvmaze/search.js", - "sourceFile": "plugins/tvmaze/search.js" - }, - { - "site": "tvmaze", - "name": "show", - "description": "Single TVmaze TV show detail by id (network, schedule, rating, IMDB/TheTVDB cross-refs)", - "access": "read", - "domain": "tvmaze.com", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "int", - "required": true, - "positional": true, - "help": "TVmaze show id (positive integer)" - } - ], - "columns": [ - "id", - "name", - "type", - "language", - "genres", - "status", - "premiered", - "ended", - "runtime", - "averageRuntime", - "network", - "country", - "schedule", - "rating", - "imdb", - "thetvdb", - "officialSite", - "summary", - "url" - ], - "type": "js", - "modulePath": "plugins/tvmaze/show.js", - "sourceFile": "plugins/tvmaze/show.js" - }, - { - "site": "twitter", - "name": "accept", - "description": "Auto-accept DM requests containing specific keywords", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "query", - "type": "string", - "required": true, - "positional": true, - "help": "Keywords to match (comma-separated for OR, e.g. \"invoice,urgent\")" - }, - { - "name": "max", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of requests to accept (default: 20)" - }, - { - "name": "timeout", - "type": "int", - "default": 600, - "required": false, - "help": "Max seconds for the overall command (default: 600 — batch op)" - } - ], - "columns": [ - "index", - "status", - "user", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/accept.js", - "sourceFile": "plugins/twitter/accept.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "article", - "description": "Fetch a Twitter Article (long-form content) and export as Markdown", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "tweet-id", - "type": "string", - "required": true, - "positional": true, - "help": "Tweet ID or URL containing the article" - } - ], - "columns": [ - "title", - "author", - "content", - "url" - ], - "type": "js", - "modulePath": "plugins/twitter/article.js", - "sourceFile": "plugins/twitter/article.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "block", - "description": "Block a Twitter user", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Twitter screen name (without @)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/block.js", - "sourceFile": "plugins/twitter/block.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "bookmark", - "description": "Bookmark a tweet", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "Tweet URL to bookmark" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/bookmark.js", - "sourceFile": "plugins/twitter/bookmark.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "bookmark-folder", - "description": "Read the tweets inside a single Twitter/X bookmark folder. Get the folder id from `webcmd twitter bookmark-folders`.", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "folder-id", - "type": "string", - "required": true, - "positional": true, - "help": "Folder id from `webcmd twitter bookmark-folders`." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of bookmarks to return (default 20)." - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the folder by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (saved-time) ordering." - } - ], - "columns": [ - "id", - "author", - "text", - "likes", - "retweets", - "bookmarks", - "created_at", - "url", - "has_media", - "media_urls", - "media_posters" - ], - "type": "js", - "modulePath": "plugins/twitter/bookmark-folder.js", - "sourceFile": "plugins/twitter/bookmark-folder.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "bookmark-folders", - "description": "List your Twitter/X bookmark folders (the user-created collections under Bookmarks). Returns folder id, name, item count, and created_at.", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "id", - "name", - "items", - "created_at" - ], - "type": "js", - "modulePath": "plugins/twitter/bookmark-folders.js", - "sourceFile": "plugins/twitter/bookmark-folders.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "bookmarks", - "description": "Fetch your Twitter/X bookmarks (the logged-in user's saved tweets, newest first)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of bookmarks to return (default 20)." - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the bookmarks by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (saved-time) ordering." - } - ], - "columns": [ - "id", - "author", - "text", - "likes", - "retweets", - "bookmarks", - "created_at", - "url", - "has_media", - "media_urls", - "media_posters" - ], - "type": "js", - "modulePath": "plugins/twitter/bookmarks.js", - "sourceFile": "plugins/twitter/bookmarks.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "delete", - "description": "Delete a specific tweet by URL", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to delete" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/delete.js", - "sourceFile": "plugins/twitter/delete.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "device-follow", - "description": "Read the /i/timeline device-follow notification stream (tweets aggregated under a bell-icon \"new posts from @userA and N others\" notification)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of tweets to return (1-200, default 20)" - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank by weighted engagement and return the top N. Default 0 keeps upstream ordering." - } - ], - "columns": [ - "id", - "author", - "text", - "likes", - "retweets", - "replies", - "views", - "created_at", - "url" - ], - "type": "js", - "modulePath": "plugins/twitter/device-follow.js", - "sourceFile": "plugins/twitter/device-follow.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "download", - "description": "Download Twitter/X media (images and videos). Provide either to fetch every media item from their profile via the GraphQL UserMedia endpoint with cursor pagination, or --tweet-url to download a single tweet.", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "str", - "required": false, - "positional": true, - "help": "Twitter username (with or without @) to scan their profile media. Either or --tweet-url is required." - }, - { - "name": "tweet-url", - "type": "str", - "required": false, - "help": "Single tweet URL to download. Use this OR , not both required at once." - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum number of media items to download when scanning a profile (default 10). Ignored when --tweet-url is used." - }, - { - "name": "output", - "type": "str", - "default": "./twitter-downloads", - "required": false, - "help": "Output directory (default ./twitter-downloads). A per-source subdir is created inside.", - "file": { - "direction": "output", - "pathKind": "directory", - "multiple": false - } - } - ], - "columns": [ - "index", - "tweet_id", - "url", - "type", - "status", - "size" - ], - "type": "js", - "modulePath": "plugins/twitter/download.js", - "sourceFile": "plugins/twitter/download.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "follow", - "description": "Follow a Twitter user", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Twitter screen name (without @)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/follow.js", - "sourceFile": "plugins/twitter/follow.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "follow-batch", - "description": "Follow multiple Twitter/X users from a comma-separated username list", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "usernames", - "type": "string", - "required": true, - "positional": true, - "help": "Comma-separated Twitter/X screen names, with or without @" - }, - { - "name": "delay-ms", - "type": "int", - "default": 3000, - "required": false, - "help": "Delay between follow attempts in milliseconds" - } - ], - "columns": [ - "username", - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/follow-batch.js", - "sourceFile": "plugins/twitter/follow-batch.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "followers", - "description": "Get accounts following a Twitter/X user (defaults to the logged-in user when no user is given)", - "access": "read", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "user", - "type": "string", - "required": false, - "positional": true, - "help": "Twitter/X handle (with or without @). Omit to fetch followers of the currently logged-in account." - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Maximum number of follower rows to return (default 50). Must be a positive integer." - } - ], - "columns": [ - "screen_name", - "name", - "bio" - ], - "type": "js", - "modulePath": "plugins/twitter/followers.js", - "sourceFile": "plugins/twitter/followers.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "following", - "description": "Get accounts a Twitter/X user is following (defaults to the logged-in user when no user is given)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "user", - "type": "string", - "required": false, - "positional": true, - "help": "Twitter/X handle (with or without @). Omit to fetch the accounts the currently logged-in user follows." - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Maximum number of following rows to return (default 50). Must be a positive integer." - } - ], - "columns": [ - "screen_name", - "name", - "bio", - "followers" - ], - "type": "js", - "modulePath": "plugins/twitter/following.js", - "sourceFile": "plugins/twitter/following.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "hide-reply", - "description": "Hide a reply on your tweet (useful for hiding bot/spam replies)", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the reply tweet to hide" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/hide-reply.js", - "sourceFile": "plugins/twitter/hide-reply.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "like", - "description": "Like a specific tweet", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to like" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/like.js", - "sourceFile": "plugins/twitter/like.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "likes", - "description": "Fetch liked tweets of a Twitter user (defaults to the logged-in user when no username is given)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": false, - "positional": true, - "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of liked tweets to return (default 20)." - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the liked tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (recency) ordering." - } - ], - "columns": [ - "id", - "author", - "name", - "text", - "likes", - "retweets", - "created_at", - "url", - "has_media", - "media_urls", - "media_posters" - ], - "type": "js", - "modulePath": "plugins/twitter/likes.js", - "sourceFile": "plugins/twitter/likes.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "list-add", - "description": "Add a user to a Twitter/X list you own (no-op if already a member)", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" - }, - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Twitter/X handle to add (with or without @)" - } - ], - "columns": [ - "listId", - "username", - "userId", - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/list-add.js", - "sourceFile": "plugins/twitter/list-add.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "list-add-batch", - "description": "Add multiple users to a Twitter/X list you own from a comma-separated username list", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" - }, - { - "name": "usernames", - "type": "string", - "required": true, - "positional": true, - "help": "Comma-separated Twitter/X handles to add (with or without @)" - }, - { - "name": "interval", - "type": "int", - "default": 5, - "required": false, - "help": "Seconds to wait between account additions (default: 5)" - }, - { - "name": "timeout", - "type": "int", - "default": 600, - "required": false, - "help": "Max seconds for the overall batch command (default: 600)" - } - ], - "columns": [ - "listId", - "username", - "userId", - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/list-add-batch.js", - "sourceFile": "plugins/twitter/list-add-batch.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "list-create", - "description": "Create a new Twitter/X list (returns the new list id)", - "access": "write", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "name", - "type": "string", - "required": true, - "positional": true, - "help": "List name (max 25 chars)" - }, - { - "name": "description", - "type": "string", - "default": "", - "required": false, - "help": "Optional list description (max 100 chars)" - }, - { - "name": "mode", - "type": "string", - "default": "public", - "required": false, - "help": "public | private" - } - ], - "columns": [ - "id", - "name", - "description", - "mode", - "status" - ], - "type": "js", - "modulePath": "plugins/twitter/list-create.js", - "sourceFile": "plugins/twitter/list-create.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "list-delete", - "description": "Delete a Twitter/X list you own after explicit confirmation", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" - }, - { - "name": "confirm", - "type": "boolean", - "default": false, - "required": false, - "help": "Required. Set --confirm true to delete the list." - }, - { - "name": "timeout", - "type": "int", - "default": 300, - "required": false, - "help": "Max seconds for the overall delete command (default: 300)" - } - ], - "columns": [ - "listId", - "name", - "members", - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/list-delete.js", - "sourceFile": "plugins/twitter/list-delete.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "list-remove", - "description": "Remove a user from a Twitter/X list you own (toggles via UI; no-op if not currently a member)", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" - }, - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Twitter/X handle to remove (with or without @)" - } - ], - "columns": [ - "listId", - "username", - "userId", - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/list-remove.js", - "sourceFile": "plugins/twitter/list-remove.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "list-remove-batch", - "description": "Remove multiple users from a Twitter/X list you own from a comma-separated username list", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of the list you own (e.g. from `webcmd twitter lists`)" - }, - { - "name": "usernames", - "type": "string", - "required": true, - "positional": true, - "help": "Comma-separated Twitter/X handles to remove (with or without @)" - }, - { - "name": "interval", - "type": "int", - "default": 5, - "required": false, - "help": "Seconds to wait between account removals (default: 5)" - }, - { - "name": "timeout", - "type": "int", - "default": 600, - "required": false, - "help": "Max seconds for the overall batch command (default: 600)" - } - ], - "columns": [ - "listId", - "username", - "userId", - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/list-remove-batch.js", - "sourceFile": "plugins/twitter/list-remove-batch.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "list-tweets", - "description": "Fetch tweets from a Twitter/X list timeline", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "listId", - "type": "string", - "required": true, - "positional": true, - "help": "Numeric ID of a Twitter/X list (e.g. from `webcmd twitter lists`)" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "" - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the list timeline by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the list's native (recency) ordering." - } - ], - "columns": [ - "id", - "author", - "bio", - "text", - "likes", - "retweets", - "replies", - "created_at", - "url", - "has_media", - "media_urls", - "media_posters", - "card", - "quoted_tweet" - ], - "type": "js", - "modulePath": "plugins/twitter/list-tweets.js", - "sourceFile": "plugins/twitter/list-tweets.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "lists", - "description": "Get Twitter/X lists for the logged-in user (owned + subscribed)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Maximum number of lists to return (default 50)." - } - ], - "columns": [ - "id", - "name", - "members", - "followers", - "mode" - ], - "type": "js", - "modulePath": "plugins/twitter/lists.js", - "sourceFile": "plugins/twitter/lists.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "login", - "description": "Open twitter login", - "access": "write", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "username", - "url", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/twitter/auth.js", - "sourceFile": "plugins/twitter/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "twitter", - "name": "notifications", - "description": "Get your Twitter/X notifications (the logged-in user's likes/replies/follows feed, newest first)", - "access": "read", - "domain": "x.com", - "strategy": "intercept", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of notifications to return (default 20)." - } - ], - "columns": [ - "id", - "action", - "author", - "text", - "url" - ], - "type": "js", - "modulePath": "plugins/twitter/notifications.js", - "sourceFile": "plugins/twitter/notifications.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "post", - "description": "Post a new tweet/thread", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "The text content of the tweet" - }, - { - "name": "images", - "type": "string", - "required": false, - "help": "Image paths, comma-separated, max 4 (jpg/png/gif/webp)", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": true, - "separator": ",", - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - } - ], - "columns": [ - "status", - "message", - "text", - "id", - "url" - ], - "type": "js", - "modulePath": "plugins/twitter/post.js", - "sourceFile": "plugins/twitter/post.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "profile", - "description": "Fetch a Twitter user profile — bio, stats, etc. (defaults to the logged-in user when no username is given)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": false, - "positional": true, - "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." - } - ], - "columns": [ - "screen_name", - "name", - "bio", - "location", - "url", - "followers", - "following", - "tweets", - "likes", - "verified", - "created_at" - ], - "type": "js", - "modulePath": "plugins/twitter/profile.js", - "sourceFile": "plugins/twitter/profile.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "quote", - "description": "Quote-tweet a specific tweet with your own text, optionally with a local or remote image", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to quote" - }, - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "The text content of your quote" - }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Optional local image path to attach to the quote tweet", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - }, - { - "name": "image-url", - "type": "str", - "required": false, - "help": "Optional remote image URL to download and attach to the quote tweet" - } - ], - "columns": [ - "status", - "message", - "text" - ], - "type": "js", - "modulePath": "plugins/twitter/quote.js", - "sourceFile": "plugins/twitter/quote.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "reply", - "description": "Reply to a specific tweet, optionally with a local or remote image", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to reply to" - }, - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "The text content of your reply" - }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Optional local image path to attach to the reply", - "file": { - "direction": "input", - "pathKind": "file", - "multiple": false, - "contentTypes": [ - "image/jpeg", - "image/png", - "image/gif", - "image/webp" - ], - "maxBytes": 26214400 - } - }, - { - "name": "image-url", - "type": "str", - "required": false, - "help": "Optional remote image URL to download and attach to the reply" - } - ], - "columns": [ - "status", - "message", - "text", - "url" - ], - "type": "js", - "modulePath": "plugins/twitter/reply.js", - "sourceFile": "plugins/twitter/reply.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "reply-dm", - "description": "Send a message to recent DM conversations", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "text", - "type": "string", - "required": true, - "positional": true, - "help": "Message text to send (e.g. \"my messaging handle wxkabi\")" - }, - { - "name": "max", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of conversations to reply to (default: 20)" - }, - { - "name": "skip-replied", - "type": "boolean", - "default": true, - "required": false, - "help": "Skip conversations where you already sent the same text (default: true)" - }, - { - "name": "timeout", - "type": "int", - "default": 600, - "required": false, - "help": "Max seconds for the overall command (default: 600 — batch op)" - } - ], - "columns": [ - "index", - "status", - "user", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/reply-dm.js", - "sourceFile": "plugins/twitter/reply-dm.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "retweet", - "description": "Retweet a specific tweet", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to retweet" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/retweet.js", - "sourceFile": "plugins/twitter/retweet.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "search", - "description": "Search Twitter/X for tweets, with optional --from / --has / --exclude / --product filters mapped to X's search operators", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "string", - "required": true, - "positional": true, - "help": "Search query. Raw X operators (e.g. \"exact phrase\", #tag, OR, lang:en, since:YYYY-MM-DD, from:, since:) are passed through unchanged." - }, - { - "name": "filter", - "type": "string", - "default": "top", - "required": false, - "help": "Legacy alias for --product. Kept for backwards compatibility; if --product is set it wins.", - "choices": [ - "top", - "live" - ] - }, - { - "name": "product", - "type": "string", - "required": false, - "help": "Which X search tab to read: top (default), live (Latest), photos, videos. Maps to the f= URL param.", - "choices": [ - "top", - "live", - "photos", - "videos" - ] - }, - { - "name": "from", - "type": "string", - "required": false, - "help": "Restrict to tweets authored by . Leading @ is stripped. Equivalent to appending `from:` to the query." - }, - { - "name": "has", - "type": "string", - "required": false, - "help": "Restrict to tweets that have media|images|videos|links|replies. Maps to X's `filter:` operator.", - "choices": [ - "media", - "images", - "videos", - "links", - "replies" - ] - }, - { - "name": "exclude", - "type": "string", - "required": false, - "help": "Exclude tweets matching : replies|retweets|media|links. Maps to X's `-filter:` operator (retweets → -filter:nativeretweets).", - "choices": [ - "replies", - "retweets", - "media", - "links" - ] - }, - { - "name": "limit", - "type": "int", - "default": 15, - "required": false, - "help": "Maximum number of tweets to return (default 15). Result count after server-side filtering." - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the results by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X's native ordering." - } - ], - "columns": [ - "id", - "author", - "bio", - "text", - "created_at", - "likes", - "views", - "url", - "has_media", - "media_urls", - "media_posters", - "card", - "quoted_tweet" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/twitter/search.js", - "sourceFile": "plugins/twitter/search.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "thread", - "description": "Get a tweet thread (original + all replies)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "tweet-id", - "type": "string", - "required": true, - "positional": true, - "help": "Tweet numeric ID (e.g. 1234567890) or full status URL" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "" - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the thread by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the conversation's structural ordering." - } - ], - "columns": [ - "id", - "author", - "bio", - "text", - "likes", - "retweets", - "url", - "has_media", - "media_urls", - "media_posters", - "card", - "quoted_tweet" - ], - "type": "js", - "modulePath": "plugins/twitter/thread.js", - "sourceFile": "plugins/twitter/thread.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "timeline", - "description": "Fetch the logged-in user's home timeline (for-you algorithmic feed by default; pass --type following for the chronological feed of accounts you follow)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "type", - "type": "str", - "default": "for-you", - "required": false, - "help": "Which home-timeline feed to read. Default for-you (algorithmic). Use following for the chronological feed of accounts you follow.", - "choices": [ - "for-you", - "following" - ] - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum number of tweets to return (default 20)." - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the timeline by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X's native ordering." - } - ], - "columns": [ - "id", - "author", - "bio", - "text", - "likes", - "retweets", - "replies", - "quotes", - "bookmarks", - "views", - "created_at", - "url", - "has_media", - "media_urls", - "media_posters", - "card", - "quoted_tweet" - ], - "type": "js", - "modulePath": "plugins/twitter/timeline.js", - "sourceFile": "plugins/twitter/timeline.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "trending", - "description": "Twitter/X trending topics", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Number of trends to show" - } - ], - "columns": [ - "rank", - "topic", - "category" - ], - "type": "js", - "modulePath": "plugins/twitter/trending.js", - "sourceFile": "plugins/twitter/trending.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "tweets", - "description": "Fetch a Twitter user's most recent tweets (chronological, excludes pinned; defaults to the logged-in user when no username is given)", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": false, - "positional": true, - "help": "Twitter screen name (with or without @). Defaults to the logged-in user when omitted." - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max tweets to return (1-10000; fetched across cursor pages)" - }, - { - "name": "page-delay", - "type": "int", - "default": 2, - "required": false, - "help": "Seconds to wait between paginated timeline requests to reduce rate-limit risk. Use 0 to disable." - }, - { - "name": "top-by-engagement", - "type": "int", - "default": 0, - "required": false, - "help": "When set to N>0, re-rank the tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the chronological ordering." - } - ], - "columns": [ - "id", - "author", - "created_at", - "is_retweet", - "text", - "likes", - "retweets", - "replies", - "views", - "url", - "has_media", - "media_urls", - "media_posters", - "quoted_tweet" - ], - "type": "js", - "modulePath": "plugins/twitter/tweets.js", - "sourceFile": "plugins/twitter/tweets.js", - "navigateBefore": "https://x.com" - }, - { - "site": "twitter", - "name": "unblock", - "description": "Unblock a Twitter user", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Twitter screen name (without @)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/unblock.js", - "sourceFile": "plugins/twitter/unblock.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "unbookmark", - "description": "Remove a tweet from bookmarks", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "Tweet URL to unbookmark" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/unbookmark.js", - "sourceFile": "plugins/twitter/unbookmark.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "unfollow", - "description": "Unfollow a Twitter user", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "username", - "type": "string", - "required": true, - "positional": true, - "help": "Twitter screen name (without @)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/unfollow.js", - "sourceFile": "plugins/twitter/unfollow.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "unlike", - "description": "Remove a like from a specific tweet", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to unlike" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/unlike.js", - "sourceFile": "plugins/twitter/unlike.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "unretweet", - "description": "Undo a retweet on a specific tweet", - "access": "write", - "domain": "x.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "url", - "type": "string", - "required": true, - "positional": true, - "help": "The URL of the tweet to unretweet" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/twitter/unretweet.js", - "sourceFile": "plugins/twitter/unretweet.js", - "navigateBefore": true - }, - { - "site": "twitter", - "name": "whoami", - "description": "Show the current logged-in twitter account", - "access": "read", - "domain": "x.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "username", - "url" - ], - "type": "js", - "modulePath": "plugins/twitter/auth.js", - "sourceFile": "plugins/twitter/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "ualberta", - "name": "export-postgraduate-courses", - "description": "Export University of Alberta postgraduate programs from the official graduate-program catalogue.", - "access": "read", - "example": "webcmd ualberta export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "www.ualberta.ca", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" - } - ], - "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" - ], - "type": "js", - "modulePath": "plugins/ualberta/export-postgraduate-courses.js", - "sourceFile": "plugins/ualberta/export-postgraduate-courses.js", - "navigateBefore": false - }, - { - "site": "uiverse", - "name": "code", - "description": "Export Uiverse component code (HTML, CSS, React, or Vue)", - "access": "read", - "domain": "uiverse.io", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "Uiverse URL or author/slug identifier" - }, - { - "name": "target", - "type": "str", - "required": true, - "help": "Code target to export", - "choices": [ - "html", - "css", - "react", - "vue" - ] - } - ], - "columns": [ - "target", - "username", - "slug", - "language", - "length" - ], - "type": "js", - "modulePath": "plugins/uiverse/code.js", - "sourceFile": "plugins/uiverse/code.js", - "navigateBefore": "https://uiverse.io" - }, - { - "site": "uiverse", - "name": "preview", - "description": "Capture a screenshot of the Uiverse preview element", - "access": "read", - "domain": "uiverse.io", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "input", - "type": "str", - "required": true, - "positional": true, - "help": "Uiverse URL or author/slug identifier" - }, - { - "name": "output", - "type": "str", - "required": false, - "help": "Output image path (defaults to a temp file)" - }, - { - "name": "padding", - "type": "int", - "default": 8, - "required": false, - "help": "Extra padding around the captured preview in pixels" - } - ], - "columns": [ - "username", - "slug", - "width", - "height", - "output" - ], - "type": "js", - "modulePath": "plugins/uiverse/preview.js", - "sourceFile": "plugins/uiverse/preview.js", - "navigateBefore": "https://uiverse.io" - }, - { - "site": "upwork", - "name": "detail", - "aliases": [ - "job", - "view" - ], - "description": "Read the full Upwork job posting by ciphertext id (e.g. ~022054964136512093518)", - "access": "read", - "domain": "www.upwork.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Job ciphertext id (~01… / ~02…) or full /jobs/~02… URL" - } - ], - "columns": [ - "id", - "title", - "type", - "budget", - "experienceLevel", - "workload", - "category", - "skills", - "description", - "clientCountry", - "clientSpent", - "clientHires", - "clientRating", - "proposalsCount", - "publishedOn", - "url" - ], - "type": "js", - "modulePath": "plugins/upwork/detail.js", - "sourceFile": "plugins/upwork/detail.js", - "navigateBefore": false - }, - { - "site": "upwork", - "name": "feed", - "aliases": [ - "best-matches" - ], - "description": "Upwork personalized jobs feed (best-matches | most-recent) — requires login", - "access": "read", - "domain": "www.upwork.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "tab", - "type": "str", - "default": "best-matches", - "required": false, - "positional": true, - "help": "Feed tab: best-matches | most-recent" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max rows to return (1-50, capped at one page)" - } - ], - "columns": [ - "rank", - "id", - "title", - "type", - "budget", - "experienceLevel", - "proposalsTier", - "skills", - "clientCountry", - "clientRating", - "publishedOn", - "url" - ], - "type": "js", - "modulePath": "plugins/upwork/feed.js", - "sourceFile": "plugins/upwork/feed.js", - "navigateBefore": false - }, - { - "site": "upwork", - "name": "login", - "description": "Open upwork login", - "access": "write", - "domain": "upwork.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "user_id", - "ciphertext", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/upwork/auth.js", - "sourceFile": "plugins/upwork/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "upwork", - "name": "search", - "description": "Upwork keyword job search (logged-in browser session, US site)", - "access": "read", - "domain": "www.upwork.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Job keyword (skill / title / company)" - }, - { - "name": "location", - "type": "string", - "default": "", - "required": false, - "help": "Country/city filter (e.g. \"United States\", \"Remote\")" - }, - { - "name": "category", - "type": "string", - "default": "", - "required": false, - "help": "Category uid filter (advanced; from job detail `category` slug)" - }, - { - "name": "sort", - "type": "string", - "default": "recency", - "required": false, - "help": "Sort: recency | relevance | client_total_charge | client_total_reviews" - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number (1-based)" - }, - { - "name": "per_page", - "type": "int", - "default": 10, - "required": false, - "help": "Rows per page (10-50, capped at one page)" - } - ], - "columns": [ - "rank", - "id", - "title", - "type", - "budget", - "experienceLevel", - "proposalsTier", - "skills", - "clientCountry", - "clientRating", - "publishedOn", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/upwork/search.js", - "sourceFile": "plugins/upwork/search.js", - "navigateBefore": false - }, - { - "site": "upwork", - "name": "whoami", - "description": "Show the current logged-in upwork account", - "access": "read", - "domain": "upwork.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "user_id", - "ciphertext" - ], - "type": "js", - "modulePath": "plugins/upwork/auth.js", - "sourceFile": "plugins/upwork/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "web", - "name": "fetch-browser", - "description": "Fetch any web page and export as Markdown", - "access": "read", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "help": "Any web page URL" - }, - { - "name": "output", - "type": "str", - "default": "./web-articles", - "required": false, - "help": "Output directory" - }, - { - "name": "download-images", - "type": "boolean", - "default": true, - "required": false, - "help": "Download images locally" - }, - { - "name": "wait", - "type": "int", - "default": 3, - "required": false, - "help": "Seconds to wait after page load" - }, - { - "name": "wait-for", - "type": "str", - "required": false, - "valueRequired": true, - "help": "CSS selector to wait for in the main document or same-origin iframes" - }, - { - "name": "wait-until", - "type": "str", - "default": "domstable", - "required": false, - "help": "Readiness policy after navigation: domstable or networkidle", - "choices": [ - "domstable", - "networkidle" - ] - }, - { - "name": "frames", - "type": "str", - "default": "same-origin", - "required": false, - "help": "Iframe handling mode: relevant same-origin, all-same-origin, or none", - "choices": [ - "same-origin", - "all-same-origin", - "none" - ] - }, - { - "name": "diagnose", - "type": "boolean", - "default": false, - "required": false, - "help": "Print render diagnostics (frames, empty containers, XHR/API-like requests) to stderr" - }, - { - "name": "stdout", - "type": "boolean", - "default": false, - "required": false, - "help": "Print markdown to stdout instead of saving to a file" - } - ], - "columns": [ - "title", - "author", - "publish_time", - "status", - "size", - "saved" - ], - "type": "js", - "modulePath": "plugins/web/fetch-browser.js", - "sourceFile": "plugins/web/fetch-browser.js", - "navigateBefore": false - }, - { - "site": "wikidata", - "name": "entity", - "description": "Fetch a Wikidata entity by Q/P/L id (label, description, aliases, claim summary)", - "access": "read", - "domain": "www.wikidata.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Entity id (e.g. Q937 = Albert Einstein, P31 = instance of)" - }, - { - "name": "language", - "type": "str", - "default": "en", - "required": false, - "help": "Display language (ISO 639, falls back to English when missing)" - } - ], - "columns": [ - "qid", - "type", - "label", - "description", - "aliases", - "claimPropertyCount", - "sitelinkCount", - "enwikiTitle", - "modified", - "url" - ], - "type": "js", - "modulePath": "plugins/wikidata/entity.js", - "sourceFile": "plugins/wikidata/entity.js" - }, - { - "site": "wikidata", - "name": "search", - "description": "Search Wikidata items by keyword (returns Q-IDs)", - "access": "read", - "domain": "www.wikidata.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (label / alias)" - }, - { - "name": "language", - "type": "str", - "default": "en", - "required": false, - "help": "Search & display language (ISO 639, e.g. en, fr, zh)" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max items (1-50)" - } - ], - "columns": [ - "rank", - "qid", - "label", - "description", - "matchType", - "matchText", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/wikidata/search.js", - "sourceFile": "plugins/wikidata/search.js" - }, - { - "site": "wikipedia", - "name": "page", - "description": "Full plain-text extract of a Wikipedia article (optional paragraph cap).", - "access": "read", - "domain": "wikipedia.org", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "title", - "type": "string", - "required": true, - "positional": true, - "help": "Article title (e.g. \"Transformer (machine learning model)\")" - }, - { - "name": "lang", - "type": "string", - "default": "en", - "required": false, - "help": "Language code (en, zh, ja, de, ...)." - }, - { - "name": "paragraphs", - "type": "int", - "default": 0, - "required": false, - "help": "Cap to first N paragraphs (0 = full article)." - } - ], - "columns": [ - "title", - "description", - "pageId", - "paragraphs", - "extract", - "url" - ], - "type": "js", - "modulePath": "plugins/wikipedia/page.js", - "sourceFile": "plugins/wikipedia/page.js" - }, - { - "site": "wikipedia", - "name": "random", - "description": "Get a random Wikipedia article", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language code (e.g. en, zh, ja)" - } - ], - "columns": [ - "title", - "description", - "extract", - "url" - ], - "type": "js", - "modulePath": "plugins/wikipedia/random.js", - "sourceFile": "plugins/wikipedia/random.js" - }, - { - "site": "wikipedia", - "name": "search", - "description": "Search Wikipedia articles", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max results" - }, - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language code (e.g. en, zh, ja)" - } - ], - "columns": [ - "title", - "snippet", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/wikipedia/search.js", - "sourceFile": "plugins/wikipedia/search.js" - }, - { - "site": "wikipedia", - "name": "summary", - "description": "Get Wikipedia article summary", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "title", - "type": "str", - "required": true, - "positional": true, - "help": "Article title (e.g. \"Transformer (machine learning model)\")" - }, - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language code (e.g. en, zh, ja)" - } - ], - "columns": [ - "title", - "description", - "extract", - "url" - ], - "type": "js", - "modulePath": "plugins/wikipedia/summary.js", - "sourceFile": "plugins/wikipedia/summary.js" - }, - { - "site": "wikipedia", - "name": "trending", - "description": "Most-read Wikipedia articles (yesterday)", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max results" - }, - { - "name": "lang", - "type": "str", - "default": "en", - "required": false, - "help": "Language code (e.g. en, zh, ja)" - } - ], - "columns": [ - "rank", - "title", - "description", - "views" - ], - "type": "js", - "modulePath": "plugins/wikipedia/trending.js", - "sourceFile": "plugins/wikipedia/trending.js" - }, - { - "site": "wttr", - "name": "current", - "description": "Current weather conditions for a location (city, lat,lon, or airport code)", - "access": "read", - "domain": "wttr.in", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "location", - "type": "str", - "required": true, - "positional": true, - "help": "City name, \"lat,lon\", airport ICAO code, or \"@domain\"" - } - ], - "columns": [ - "location", - "region", - "country", - "latitude", - "longitude", - "observedAt", - "tempC", - "tempF", - "feelsLikeC", - "feelsLikeF", - "description", - "humidity", - "cloudCover", - "pressure", - "precipMm", - "visibilityKm", - "uvIndex", - "windKmph", - "windDirection", - "windDirectionDegree" - ], - "type": "js", - "modulePath": "plugins/wttr/current.js", - "sourceFile": "plugins/wttr/current.js" - }, - { - "site": "wttr", - "name": "forecast", - "description": "Multi-day weather forecast (up to 3 days, wttr.in free tier max)", - "access": "read", - "domain": "wttr.in", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "location", - "type": "str", - "required": true, - "positional": true, - "help": "City name, \"lat,lon\", airport ICAO code, or \"@domain\"" - }, - { - "name": "days", - "type": "int", - "default": 3, - "required": false, - "help": "Max forecast days (1-3, wttr.in caps the response at 3 days)" - } - ], - "columns": [ - "rank", - "date", - "minTempC", - "maxTempC", - "avgTempC", - "minTempF", - "maxTempF", - "avgTempF", - "sunHour", - "totalSnowCm", - "uvIndex", - "description", - "sunrise", - "sunset" - ], - "type": "js", - "modulePath": "plugins/wttr/forecast.js", - "sourceFile": "plugins/wttr/forecast.js" - }, - { - "site": "yahoo", - "name": "search", - "description": "Search Yahoo (powered by Bing)", - "access": "read", - "domain": "search.yahoo.com", - "strategy": "public", - "browser": true, - "args": [ - { - "name": "keyword", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 7, - "required": false, - "help": "Number of results per page (max 7)" - }, - { - "name": "page", - "type": "int", - "default": 1, - "required": false, - "help": "Page number (1, 2, 3...). Yahoo returns ~7 results per page" - } - ], - "columns": [ - "rank", - "title", - "url", - "snippet" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/yahoo/search.js", - "sourceFile": "plugins/yahoo/search.js" - }, - { - "site": "yahoo-finance", - "name": "quote", - "description": "Yahoo Finance stock quote", - "access": "read", - "domain": "finance.yahoo.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "symbol", - "type": "str", - "required": true, - "positional": true, - "help": "Stock ticker (e.g. AAPL, MSFT, TSLA)" - } - ], - "columns": [ - "symbol", - "name", - "price", - "change", - "changePercent", - "open", - "high", - "low", - "volume", - "marketCap" - ], - "type": "js", - "modulePath": "plugins/yahoo-finance/quote.js", - "sourceFile": "plugins/yahoo-finance/quote.js", - "navigateBefore": "https://finance.yahoo.com" - }, - { - "site": "yale", - "name": "export-postgraduate-courses", - "description": "Export Yale University postgraduate and professional programs from official Yale sources.", - "access": "read", - "example": "webcmd yale export-postgraduate-courses --degree-level masters --count 10 -f csv", - "domain": "yale.edu", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "degree-level", - "type": "string", - "default": "all", - "required": false, - "help": "all, masters, certificate, diploma, professional, or doctorate" - }, - { - "name": "count", - "type": "int", - "required": false, - "help": "Positive maximum number of programs after filtering and deduplication" - } - ], - "columns": [ - "Course Name", - "Course URL", - "University \nname", - "Intake Month", - "Substream/\nSpecialisation", - "App fees", - "Degree Level", - "Study Level", - "Duration\n(in months)", - "Study option", - "Program Type", - "Partner", - "Tution fees \n(per year)", - "Total Tution \nFees", - "IELTS \n(Overall & Subscores)", - "ielts_reading_score", - "ielts_writing_score", - "ielts_listening_score", - "ielts_speaking_score", - "TOEFL\n(Overall & Subscores)", - "toefl_reading_score", - "toefl_writing_score", - "toefl_listening_score", - "toefl_speaking_score", - "PTE\n(Overall & Subscores)", - "pte_reading_score", - "pte_writing_score", - "pte_listening_score", - "pte_speaking_score", - "Duolingo\n(Overall & Subscores)", - "duolingo_comprehension_score", - "duolingo_literacy_score", - "duolingo_conversation_score", - "duolingo_production_score", - "Is Waiver \nProvided?", - "Waiver Info", - "Is MOI \naccepted?", - "Share list, if any", - "GRE Required", - "GMAT Required", - "GRE/GMAT Scores", - "12th scores", - "Min UG score", - "15 years of\nEducation Allowed?", - "Gap Years", - "Backlogs", - "Work \nExperience \nRequired?", - "Main Entry \nRequirements", - "Status", - "Intake status(open/close)\n(eg: Fall (september)-Open\nSpring(January)- Closed)", - "Remarks (if any)", - "Reference Links (if any)" - ], - "type": "js", - "modulePath": "plugins/yale/export-postgraduate-courses.js", - "sourceFile": "plugins/yale/export-postgraduate-courses.js" - }, - { - "site": "ycombinator", - "name": "companies", - "description": "Search the public Y Combinator startup directory", - "access": "read", - "domain": "www.ycombinator.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": false, - "positional": true, - "help": "Company name, product, or keyword such as AI" - }, - { - "name": "batch", - "type": "str", - "required": false, - "help": "Exact YC batch, for example Spring 2026" - }, - { - "name": "industry", - "type": "str", - "required": false, - "help": "Exact YC industry, for example B2B" - }, - { - "name": "recent", - "type": "boolean", - "default": false, - "required": false, - "help": "Sort matches by launch date, newest first" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Maximum companies to return (1-40)" - } - ], - "columns": [ - "rank", - "name", - "batch", - "location", - "description", - "industries", - "url" - ], - "type": "js", - "modulePath": "plugins/ycombinator/companies.js", - "sourceFile": "plugins/ycombinator/companies.js", - "navigateBefore": false - }, - { - "site": "ycombinator", - "name": "company", - "description": "Read a public Y Combinator company profile", - "access": "read", - "domain": "www.ycombinator.com", - "strategy": "ui", - "browser": true, - "args": [ - { - "name": "company", - "type": "str", - "required": true, - "positional": true, - "help": "YC company slug or full company URL" - } - ], - "columns": [ - "name", - "description", - "batch", - "status", - "location", - "founded", - "teamSize", - "website", - "founders", - "jobCount", - "url" - ], - "type": "js", - "modulePath": "plugins/ycombinator/company.js", - "sourceFile": "plugins/ycombinator/company.js", - "navigateBefore": false - }, - { - "site": "yollomi", - "name": "background", - "description": "Generate AI background for a product/object image (5 credits)", - "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "image", - "type": "str", - "required": true, - "positional": true, - "help": "Image URL (upload via \"webcmd yollomi upload\" first)" - }, - { - "name": "prompt", - "type": "str", - "default": "", - "required": false, - "help": "Background description (optional)" - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL" - } - ], - "columns": [ - "status", - "file", - "size", - "url" - ], - "type": "js", - "modulePath": "plugins/yollomi/background.js", - "sourceFile": "plugins/yollomi/background.js", - "navigateBefore": "https://yollomi.com" - }, - { - "site": "yollomi", - "name": "edit", - "description": "Edit images with AI text prompts (Qwen image edit)", - "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "image", - "type": "str", - "required": true, - "positional": true, - "help": "Input image URL (upload via \"webcmd yollomi upload\" first)" - }, - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Editing instruction (e.g. \"Make it look vintage\")" - }, - { - "name": "model", - "type": "str", - "default": "qwen-image-edit", - "required": false, - "help": "Edit model", - "choices": [ - "qwen-image-edit", - "qwen-image-edit-plus" - ] - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL" - } - ], - "columns": [ - "status", - "file", - "size", - "credits", - "url" - ], - "type": "js", - "modulePath": "plugins/yollomi/edit.js", - "sourceFile": "plugins/yollomi/edit.js", - "navigateBefore": "https://yollomi.com" - }, - { - "site": "yollomi", - "name": "face-swap", - "description": "Swap faces between two photos (3 credits)", - "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "source", - "type": "str", - "required": true, - "help": "Source face image URL" - }, - { - "name": "target", - "type": "str", - "required": true, - "help": "Target photo URL" - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL" - } - ], - "columns": [ - "status", - "file", - "size", - "url" - ], - "type": "js", - "modulePath": "plugins/yollomi/face-swap.js", - "sourceFile": "plugins/yollomi/face-swap.js", - "navigateBefore": "https://yollomi.com" - }, - { - "site": "yollomi", - "name": "generate", - "description": "Generate images with AI (text-to-image or image-to-image)", - "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Text prompt describing the image" - }, - { - "name": "model", - "type": "str", - "default": "z-image-turbo", - "required": false, - "help": "Model ID (z-image-turbo, flux-schnell, nano-banana, flux-2-pro, ...)" - }, - { - "name": "ratio", - "type": "str", - "default": "1:1", - "required": false, - "help": "Aspect ratio", - "choices": [ - "1:1", - "16:9", - "9:16", - "4:3", - "3:4" - ] - }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Input image URL for image-to-image (upload via \"webcmd yollomi upload\" first)" - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URLs, skip download" - } - ], - "columns": [ - "index", - "status", - "file", - "size", - "url" - ], - "type": "js", - "modulePath": "plugins/yollomi/generate.js", - "sourceFile": "plugins/yollomi/generate.js", - "navigateBefore": "https://yollomi.com" - }, - { - "site": "yollomi", - "name": "models", - "description": "List available Yollomi AI models (image, video, tools)", - "access": "read", - "strategy": "public", - "browser": false, - "args": [ - { - "name": "type", - "type": "str", - "default": "all", - "required": false, - "help": "Filter by model type", - "choices": [ - "all", - "image", - "video", - "tool" - ] - } - ], - "columns": [ - "type", - "model", - "credits", - "description" - ], - "type": "js", - "modulePath": "plugins/yollomi/models.js", - "sourceFile": "plugins/yollomi/models.js" - }, - { - "site": "yollomi", - "name": "object-remover", - "description": "Remove unwanted objects from images (3 credits)", - "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "image", - "type": "str", - "required": true, - "positional": true, - "help": "Image URL" - }, - { - "name": "mask", - "type": "str", - "required": true, - "positional": true, - "help": "Mask image URL (white = area to remove)" - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL" - } - ], - "columns": [ - "status", - "file", - "size", - "url" - ], - "type": "js", - "modulePath": "plugins/yollomi/object-remover.js", - "sourceFile": "plugins/yollomi/object-remover.js", - "navigateBefore": "https://yollomi.com" - }, - { - "site": "yollomi", - "name": "remove-bg", - "description": "Remove image background with AI (free)", - "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "image", - "type": "str", - "required": true, - "positional": true, - "help": "Image URL to remove background from" - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL" - } - ], - "columns": [ - "status", - "file", - "size", - "url" - ], - "type": "js", - "modulePath": "plugins/yollomi/remove-bg.js", - "sourceFile": "plugins/yollomi/remove-bg.js", - "navigateBefore": "https://yollomi.com" - }, - { - "site": "yollomi", - "name": "restore", - "description": "Restore old or damaged photos with AI (4 credits)", - "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "image", - "type": "str", - "required": true, - "positional": true, - "help": "Image URL to restore" - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL" - } - ], - "columns": [ - "status", - "file", - "size", - "url" - ], - "type": "js", - "modulePath": "plugins/yollomi/restore.js", - "sourceFile": "plugins/yollomi/restore.js", - "navigateBefore": "https://yollomi.com" - }, - { - "site": "yollomi", - "name": "try-on", - "description": "Virtual try-on — see how clothes look on a person (3 credits)", - "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "person", - "type": "str", - "required": true, - "help": "Person photo URL (upload via \"webcmd yollomi upload\" first)" - }, - { - "name": "cloth", - "type": "str", - "required": true, - "help": "Clothing image URL" - }, - { - "name": "cloth-type", - "type": "str", - "default": "upper", - "required": false, - "help": "Clothing type", - "choices": [ - "upper", - "lower", - "overall" - ] - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL" - } - ], - "columns": [ - "status", - "file", - "size", - "url" - ], - "type": "js", - "modulePath": "plugins/yollomi/try-on.js", - "sourceFile": "plugins/yollomi/try-on.js", - "navigateBefore": "https://yollomi.com" - }, - { - "site": "yollomi", - "name": "upload", - "description": "Upload an image or video to Yollomi (returns URL for other commands)", - "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "file", - "type": "str", - "required": true, - "positional": true, - "help": "Local file path to upload" - } - ], - "columns": [ - "status", - "file", - "size", - "url" - ], - "type": "js", - "modulePath": "plugins/yollomi/upload.js", - "sourceFile": "plugins/yollomi/upload.js", - "navigateBefore": "https://yollomi.com" - }, - { - "site": "yollomi", - "name": "upscale", - "description": "Upscale image resolution with AI (1 credit)", - "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "image", - "type": "str", - "required": true, - "positional": true, - "help": "Image URL to upscale" - }, - { - "name": "scale", - "type": "str", - "default": "2", - "required": false, - "help": "Upscale factor (2 or 4)", - "choices": [ - "2", - "4" - ] - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL" - } - ], - "columns": [ - "status", - "file", - "size", - "scale", - "url" - ], - "type": "js", - "modulePath": "plugins/yollomi/upscale.js", - "sourceFile": "plugins/yollomi/upscale.js", - "navigateBefore": "https://yollomi.com" - }, - { - "site": "yollomi", - "name": "video", - "description": "Generate videos with AI (text-to-video or image-to-video)", - "access": "write", - "domain": "yollomi.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "prompt", - "type": "str", - "required": true, - "positional": true, - "help": "Text prompt describing the video" - }, - { - "name": "model", - "type": "str", - "default": "kling-2-1", - "required": false, - "help": "Model (kling-2-1, openai-sora-2, google-veo-3-1, wan-2-5-t2v, ...)" - }, - { - "name": "image", - "type": "str", - "required": false, - "help": "Input image URL for image-to-video" - }, - { - "name": "ratio", - "type": "str", - "default": "16:9", - "required": false, - "help": "Aspect ratio", - "choices": [ - "1:1", - "16:9", - "9:16", - "4:3", - "3:4" - ] - }, - { - "name": "output", - "type": "str", - "default": "./yollomi-output", - "required": false, - "help": "Output directory" - }, - { - "name": "no-download", - "type": "boolean", - "default": false, - "required": false, - "help": "Only show URL, skip download" - } - ], - "columns": [ - "status", - "file", - "size", - "credits", - "url" - ], - "type": "js", - "modulePath": "plugins/yollomi/video.js", - "sourceFile": "plugins/yollomi/video.js", - "navigateBefore": "https://yollomi.com" - }, - { - "site": "youtube", - "name": "channel", - "description": "Get YouTube channel info and recent videos", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Channel ID (UCxxxx) or handle (@name)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max recent videos (max 30)" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "plugins/youtube/channel.js", - "sourceFile": "plugins/youtube/channel.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "comments", - "description": "Get YouTube video comments", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "YouTube video URL or video ID" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max comments (max 100)" - } - ], - "columns": [ - "rank", - "author", - "text", - "likes", - "replies", - "time" - ], - "type": "js", - "modulePath": "plugins/youtube/comments.js", - "sourceFile": "plugins/youtube/comments.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "feed", - "description": "Get YouTube homepage recommended videos", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max videos to return (default 20, max 100)" - } - ], - "columns": [ - "rank", - "title", - "channel", - "video_id", - "views", - "duration", - "published", - "url" - ], - "type": "js", - "modulePath": "plugins/youtube/feed.js", - "sourceFile": "plugins/youtube/feed.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "history", - "description": "Get YouTube watch history", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 30, - "required": false, - "help": "Max videos to return (default 30, max 200)" - } - ], - "columns": [ - "rank", - "title", - "channel", - "views", - "duration", - "url" - ], - "type": "js", - "modulePath": "plugins/youtube/history.js", - "sourceFile": "plugins/youtube/history.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "like", - "description": "Like a YouTube video", - "access": "write", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "YouTube video URL or video ID" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/youtube/like.js", - "sourceFile": "plugins/youtube/like.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "login", - "description": "Open youtube login", - "access": "write", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "name", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/youtube/auth.js", - "sourceFile": "plugins/youtube/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "youtube", - "name": "playlist", - "description": "Get YouTube playlist info and video list", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "id", - "type": "str", - "required": true, - "positional": true, - "help": "Playlist URL or playlist ID (PLxxxxxx)" - }, - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max videos to return (default 50, max 200)" - } - ], - "columns": [ - "rank", - "title", - "channel", - "duration", - "views", - "published", - "url" - ], - "type": "js", - "modulePath": "plugins/youtube/playlist.js", - "sourceFile": "plugins/youtube/playlist.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "search", - "description": "Search YouTube videos", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Max results (max 50)" - }, - { - "name": "type", - "type": "str", - "default": "", - "required": false, - "help": "Filter type: shorts, video, channel, playlist" - }, - { - "name": "upload", - "type": "str", - "default": "", - "required": false, - "help": "Upload date: hour, today, week, month, year" - }, - { - "name": "sort", - "type": "str", - "default": "", - "required": false, - "help": "Sort by: relevance, date, views, rating" - } - ], - "columns": [ - "rank", - "title", - "channel", - "views", - "duration", - "published", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/youtube/search.js", - "sourceFile": "plugins/youtube/search.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "subscribe", - "description": "Subscribe to a YouTube channel", - "access": "write", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "Channel ID (UCxxxx) or handle (@name)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/youtube/subscribe.js", - "sourceFile": "plugins/youtube/subscribe.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "subscriptions", - "description": "List subscribed YouTube channels", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max channels to return (default 50)" - } - ], - "columns": [ - "rank", - "name", - "handle", - "subscribers", - "url" - ], - "type": "js", - "modulePath": "plugins/youtube/subscriptions.js", - "sourceFile": "plugins/youtube/subscriptions.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "transcript", - "description": "Get YouTube video transcript/subtitles", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "YouTube video URL or video ID" - }, - { - "name": "lang", - "type": "str", - "required": false, - "help": "Language code (e.g. en, zh-Hans). Omit to auto-select" - }, - { - "name": "mode", - "type": "str", - "default": "grouped", - "required": false, - "help": "Output mode: grouped (readable paragraphs) or raw (every segment)" - } - ], - "type": "js", - "modulePath": "plugins/youtube/transcript.js", - "sourceFile": "plugins/youtube/transcript.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "unlike", - "description": "Remove like from a YouTube video", - "access": "write", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "YouTube video URL or video ID" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/youtube/unlike.js", - "sourceFile": "plugins/youtube/unlike.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "unsubscribe", - "description": "Unsubscribe from a YouTube channel", - "access": "write", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "channel", - "type": "str", - "required": true, - "positional": true, - "help": "Channel ID (UCxxxx) or handle (@name)" - } - ], - "columns": [ - "status", - "message" - ], - "type": "js", - "modulePath": "plugins/youtube/unsubscribe.js", - "sourceFile": "plugins/youtube/unsubscribe.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "video", - "description": "Get YouTube video metadata (title, views, description, etc.)", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "YouTube video URL or video ID" - } - ], - "columns": [ - "field", - "value" - ], - "type": "js", - "modulePath": "plugins/youtube/video.js", - "sourceFile": "plugins/youtube/video.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "watch-later", - "description": "Get your YouTube Watch Later queue", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "limit", - "type": "int", - "default": 50, - "required": false, - "help": "Max videos to return (default 50, max 200)" - } - ], - "columns": [ - "rank", - "title", - "channel", - "duration", - "views", - "published", - "url" - ], - "type": "js", - "modulePath": "plugins/youtube/watch-later.js", - "sourceFile": "plugins/youtube/watch-later.js", - "navigateBefore": "https://www.youtube.com" - }, - { - "site": "youtube", - "name": "whoami", - "description": "Show the current logged-in youtube account", - "access": "read", - "domain": "www.youtube.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site", - "name" - ], - "type": "js", - "modulePath": "plugins/youtube/auth.js", - "sourceFile": "plugins/youtube/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "zepto", - "name": "add-to-cart", - "description": "Add a Zepto product to cart", - "access": "write", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "product", - "type": "str", - "required": true, - "positional": true, - "help": "Product URL from Zepto search results" - }, - { - "name": "quantity", - "type": "int", - "default": 1, - "required": false, - "help": "Quantity to add (max 12)" - } - ], - "columns": [ - "ok", - "product_id", - "quantity", - "item_count", - "message" - ], - "type": "js", - "modulePath": "plugins/zepto/add-to-cart.js", - "sourceFile": "plugins/zepto/add-to-cart.js", - "navigateBefore": false - }, - { - "site": "zepto", - "name": "cart", - "description": "Read Zepto cart line items", - "access": "read", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "rank", - "product_id", - "title", - "pack_size", - "quantity", - "price", - "mrp", - "availability" - ], - "type": "js", - "modulePath": "plugins/zepto/cart.js", - "sourceFile": "plugins/zepto/cart.js", - "navigateBefore": false - }, - { - "site": "zepto", - "name": "checkout", - "description": "Open Zepto checkout review without placing an order", - "access": "write", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "ok", - "stage", - "item_count", - "next_action", - "url" - ], - "type": "js", - "modulePath": "plugins/zepto/checkout.js", - "sourceFile": "plugins/zepto/checkout.js", - "navigateBefore": false - }, - { - "site": "zepto", - "name": "location", - "description": "Show the selected Zepto delivery location", - "access": "read", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "selected", - "label", - "area", - "city", - "pincode", - "hasCoordinates", - "source" - ], - "type": "js", - "modulePath": "plugins/zepto/location.js", - "sourceFile": "plugins/zepto/location.js", - "navigateBefore": false - }, - { - "site": "zepto", - "name": "login", - "description": "Open zepto login", - "access": "write", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "status", - "logged_in", - "site", - "action", - "verify_command" - ], - "type": "js", - "modulePath": "plugins/zepto/auth.js", - "sourceFile": "plugins/zepto/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "zepto", - "name": "place-order", - "description": "Submit a real Zepto order only when --confirm true is passed", - "access": "write", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "confirm", - "type": "boolean", - "default": false, - "required": false, - "help": "Required. Set true to submit a real Zepto order/payment action." - } - ], - "columns": [ - "status", - "confirmed", - "message" - ], - "type": "js", - "modulePath": "plugins/zepto/place-order.js", - "sourceFile": "plugins/zepto/place-order.js", - "navigateBefore": false - }, - { - "site": "zepto", - "name": "product", - "description": "Read Zepto product details", - "access": "read", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "product", - "type": "str", - "required": true, - "positional": true, - "help": "Product URL from Zepto search results" - } - ], - "columns": [ - "product_id", - "title", - "brand", - "pack_size", - "price", - "mrp", - "availability", - "url" - ], - "type": "js", - "modulePath": "plugins/zepto/product.js", - "sourceFile": "plugins/zepto/product.js", - "navigateBefore": false - }, - { - "site": "zepto", - "name": "search", - "description": "Search Zepto products", - "access": "read", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search query" - }, - { - "name": "limit", - "type": "int", - "default": 20, - "required": false, - "help": "Maximum products to return (max 50)" - } - ], - "columns": [ - "rank", - "product_id", - "title", - "brand", - "pack_size", - "price", - "mrp", - "availability", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/zepto/search.js", - "sourceFile": "plugins/zepto/search.js", - "navigateBefore": false - }, - { - "site": "zepto", - "name": "whoami", - "description": "Show the current logged-in zepto account", - "access": "read", - "domain": "www.zepto.com", - "strategy": "cookie", - "browser": true, - "args": [], - "columns": [ - "logged_in", - "site" - ], - "type": "js", - "modulePath": "plugins/zepto/auth.js", - "sourceFile": "plugins/zepto/auth.js", - "navigateBefore": false, - "siteSession": "persistent" - }, - { - "site": "zlibrary", - "name": "info", - "description": "Get book details and available download formats from a Z-Library book page", - "access": "read", - "domain": "z-library.im", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "url", - "type": "str", - "required": true, - "positional": true, - "help": "Z-Library book page URL (e.g. https://z-library.im/book/...)" - } - ], - "columns": [ - "title", - "pdf", - "epub", - "url" - ], - "type": "js", - "modulePath": "plugins/zlibrary/info.js", - "sourceFile": "plugins/zlibrary/info.js", - "navigateBefore": false - }, - { - "site": "zlibrary", - "name": "search", - "description": "Search Z-Library for books by title, author, ISBN, or keyword", - "access": "read", - "domain": "z-library.im", - "strategy": "cookie", - "browser": true, - "args": [ - { - "name": "query", - "type": "str", - "required": true, - "positional": true, - "help": "Search keyword (title, author, ISBN, etc.)" - }, - { - "name": "limit", - "type": "int", - "default": 10, - "required": false, - "help": "Max results (1–25)" - } - ], - "columns": [ - "rank", - "title", - "author", - "url" - ], - "tags": [ - "search" - ], - "type": "js", - "modulePath": "plugins/zlibrary/search.js", - "sourceFile": "plugins/zlibrary/search.js", - "navigateBefore": false - } -] From c9cefc35ebfd3aa374d3782def73449266adb37f Mon Sep 17 00:00:00 2001 From: Ankit Ranjan Date: Thu, 6 Aug 2026 16:22:28 +0530 Subject: [PATCH 29/39] fix: generate plugin-command-manifest.json as part of npm run build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gitignoring it in 4aec6c5d broke the unit-test CI job: that job runs on a separate runner with a fresh checkout and only does `npm ci` + vitest, so the file no longer existed for the three test files that read it from the package root (hosted/availability, hosted/file-contract, build-manifest) — 5 tests failed with ENOENT. hosted-contract.json is safely gitignored only because `npm run build` always regenerates it. Give the plugin manifest the same guarantee instead of leaving it as a build artifact nothing in the install path produces. Costs ~0.9s per build. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 984eb745..dfc32f1d 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "scripts": { "dev": "tsx src/main.ts", "dev:bun": "bun src/main.ts", - "build": "npm run clean-dist && npm run copy-yaml && npm run compile && npm run build-manifest", + "build": "npm run clean-dist && npm run copy-yaml && npm run compile && npm run build-manifest && npm run build-plugin-manifest", "compile": "tsc --build && node -e \"require('fs').chmodSync('dist/src/main.js', 0o755)\"", "build-manifest": "tsx src/build-manifest.ts", "build-plugin-manifest": "tsx src/build-plugin-command-manifest.ts", From ef8d96eb186b66cbf6e92838877e4a7a7082c188 Mon Sep 17 00:00:00 2001 From: beubax Date: Fri, 7 Aug 2026 03:29:43 +0530 Subject: [PATCH 30/39] feat: export deriveHostedAvailability for hosted consumers --- package.json | 3 ++- src/hosted/availability.test.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index edc62a85..6bada5c1 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ "./download/progress": "./dist/src/download/progress.js", "./fetch/command": "./dist/src/fetch/command.js", "./pipeline": "./dist/src/pipeline/index.js", - "./plugin-runtime": "./dist/src/plugin-runtime.js" + "./plugin-runtime": "./dist/src/plugin-runtime.js", + "./hosted/availability": "./dist/src/hosted/availability.js" }, "files": [ "dist/src/", diff --git a/src/hosted/availability.test.ts b/src/hosted/availability.test.ts index cb34e0f5..df4ce587 100644 --- a/src/hosted/availability.test.ts +++ b/src/hosted/availability.test.ts @@ -202,3 +202,29 @@ describe('hosted availability', () => { expect(desktopApps).toHaveLength(111); }); }); + +describe('deriveHostedAvailability classification table', () => { + it('treats a dotted domain as hosted', () => { + expect(deriveHostedAvailability({ strategy: 'PUBLIC', domain: 'news.ycombinator.com' })) + .toEqual({ mode: 'hosted' }); + }); + + it('treats a dotless domain as a desktop app', () => { + expect(deriveHostedAvailability({ strategy: 'UI', domain: 'chatgpt-app' })) + .toEqual({ mode: 'local-only', reason: 'desktop-app' }); + }); + + it('treats a local IP domain as a desktop app', () => { + expect(deriveHostedAvailability({ strategy: 'UI', domain: '127.0.0.1:3000' })) + .toEqual({ mode: 'local-only', reason: 'desktop-app' }); + }); + + it('treats an absent domain as hosted', () => { + expect(deriveHostedAvailability({ strategy: 'PUBLIC' })).toEqual({ mode: 'hosted' }); + }); + + it('treats LOCAL strategy as a local tool regardless of domain', () => { + expect(deriveHostedAvailability({ strategy: 'local', domain: 'example.com' })) + .toEqual({ mode: 'local-only', reason: 'local-tool' }); + }); +}); From 1238d4d40b67ba68bc554abaeba608f5a3a39005 Mon Sep 17 00:00:00 2001 From: beubax Date: Fri, 7 Aug 2026 03:38:12 +0530 Subject: [PATCH 31/39] fix: handle ports in local IPv4 address detection for deriveHostedAvailability The isLocalIpDomain function now strips port numbers from domain strings (e.g. '127.0.0.1:3000') before validating the IPv4 format. This ensures desktop-app adapters running on local IP addresses with ports are correctly classified as 'app' rather than 'site'. Includes regression tests to prevent port-handling regressions. --- src/command-presentation.ts | 3 ++- src/help.test.ts | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/command-presentation.ts b/src/command-presentation.ts index f4c33599..144ff13c 100644 --- a/src/command-presentation.ts +++ b/src/command-presentation.ts @@ -282,7 +282,8 @@ export type AdapterKind = 'site' | 'app'; function isLocalIpDomain(domain: string): boolean { if (domain === '::1' || domain === '[::1]') return true; - const parts = domain.split('.'); + const ipPart = domain.split(':')[0]; + const parts = ipPart.split('.'); if (parts.length !== 4) return false; return parts.every((part) => /^\d+$/.test(part) && Number(part) >= 0 && Number(part) <= 255) && Number(parts[0]) === 127; diff --git a/src/help.test.ts b/src/help.test.ts index 40c2da95..f4258c64 100644 --- a/src/help.test.ts +++ b/src/help.test.ts @@ -48,6 +48,17 @@ describe('classifyAdapter', () => { it('defaults missing domain to site (most adapters without explicit domain are public web scrapers)', () => { expect(classifyAdapter(undefined)).toBe('site'); }); + + it('classifies local IPv4 addresses (127.x.x.x) as app, with or without port', () => { + expect(classifyAdapter('127.0.0.1')).toBe('app'); + expect(classifyAdapter('127.0.0.1:3000')).toBe('app'); + expect(classifyAdapter('127.0.0.1:8080')).toBe('app'); + }); + + it('classifies dotted domains as site even if they resemble IPs when not 127.x.x.x', () => { + expect(classifyAdapter('news.ycombinator.com')).toBe('site'); + expect(classifyAdapter('192.168.1.1')).toBe('site'); + }); }); describe('formatRootAdapterHelpText', () => { From 4ea047918d23fa678bfca0654126072cc84a8764 Mon Sep 17 00:00:00 2001 From: beubax Date: Fri, 7 Aug 2026 04:59:30 +0530 Subject: [PATCH 32/39] feat: support plugin list, uninstall, update, and create in hosted mode --- src/builtin-command-surface.ts | 22 +++++ src/hosted/client.test.ts | 79 ++++++++++++++++++ src/hosted/client.ts | 56 +++++++++++++ src/hosted/runner.test.ts | 141 ++++++++++++++++++++++++++++++++- src/hosted/runner.ts | 127 +++++++++++++++++++++++++++-- src/hosted/types.ts | 9 +++ 6 files changed, 427 insertions(+), 7 deletions(-) diff --git a/src/builtin-command-surface.ts b/src/builtin-command-surface.ts index dde75938..86dd49f7 100644 --- a/src/builtin-command-surface.ts +++ b/src/builtin-command-surface.ts @@ -34,3 +34,25 @@ export function configurePluginInstallSurface(command: Command): Command { .description('Install a plugin from a git repository') .argument('', 'Plugin source (e.g. github:user/repo)'); } + +/** Configure installed-plugin listing grammar shared by local and hosted runtimes. */ +export function configurePluginListSurface(command: Command): Command { + return command + .description('List installed plugins') + .option('-f, --format ', 'Output format: table, json', 'table'); +} + +/** Configure plugin uninstall grammar shared by local and hosted runtimes. */ +export function configurePluginUninstallSurface(command: Command): Command { + return command + .description('Uninstall a plugin') + .argument('', 'Installed plugin name'); +} + +/** Configure plugin update grammar shared by local and hosted runtimes. */ +export function configurePluginUpdateSurface(command: Command): Command { + return command + .description('Update a plugin (or all plugins) to the latest version') + .argument('[name]', 'Installed plugin name') + .option('--all', 'Update all installed plugins'); +} diff --git a/src/hosted/client.test.ts b/src/hosted/client.test.ts index b7296807..59e6e4c0 100644 --- a/src/hosted/client.test.ts +++ b/src/hosted/client.test.ts @@ -144,6 +144,85 @@ describe('HostedClient', () => { }]); }); + it('lists marketplace installations through the authenticated API', async () => { + const client = new HostedClient({ + apiBaseUrl: 'https://api.example.com', + apiKey: 'key', + fetchImpl: async () => new Response(JSON.stringify({ + ok: true, + result: { + installations: [{ + name: 'alpha', version: '0.1.0', installSource: 'github:agentrhq/webcmd/alpha', + sourceCommit: null, installedAt: '2026-08-07T00:00:00.000Z', updateAvailable: false, + }], + }, + })), + }); + + await expect(client.listMarketplaceInstallations()).resolves.toEqual([{ + name: 'alpha', version: '0.1.0', installSource: 'github:agentrhq/webcmd/alpha', + sourceCommit: null, installedAt: '2026-08-07T00:00:00.000Z', updateAvailable: false, + }]); + }); + + it('uninstalls a marketplace plugin through the authenticated API', async () => { + const requests: Array<{ url: string; method: string }> = []; + const client = new HostedClient({ + apiBaseUrl: 'https://api.example.com', + apiKey: 'key', + fetchImpl: async (url, init) => { + requests.push({ url: String(url), method: init?.method ?? 'GET' }); + return new Response(JSON.stringify({ ok: true, result: { uninstalled: true } })); + }, + }); + + await expect(client.uninstallMarketplacePlugin('alpha')).resolves.toEqual({ uninstalled: true }); + expect(requests).toEqual([{ url: 'https://api.example.com/v1/marketplace/installations/alpha', method: 'DELETE' }]); + }); + + it('accepts the ordinary 3-key update response', async () => { + const client = new HostedClient({ + apiBaseUrl: 'https://api.example.com', + apiKey: 'key', + fetchImpl: async () => new Response(JSON.stringify({ + ok: true, + result: { updated: true, name: 'alpha', version: '0.2.0' }, + })), + }); + + await expect(client.updateMarketplacePlugin('alpha')).resolves.toEqual({ + updated: true, name: 'alpha', version: '0.2.0', + }); + }); + + it('accepts the 4-key delisted update response', async () => { + const client = new HostedClient({ + apiBaseUrl: 'https://api.example.com', + apiKey: 'key', + fetchImpl: async () => new Response(JSON.stringify({ + ok: true, + result: { updated: false, name: 'alpha', version: '0.1.0', delisted: true }, + })), + }); + + await expect(client.updateMarketplacePlugin('alpha')).resolves.toEqual({ + updated: false, name: 'alpha', version: '0.1.0', delisted: true, + }); + }); + + it('rejects an update response with delisted: false as protocol-invalid', async () => { + const client = new HostedClient({ + apiBaseUrl: 'https://api.example.com', + apiKey: 'key', + fetchImpl: async () => new Response(JSON.stringify({ + ok: true, + result: { updated: false, name: 'alpha', version: '0.1.0', delisted: false }, + })), + }); + + await expect(client.updateMarketplacePlugin('alpha')).rejects.toThrow(HostedClientError); + }); + it('sends bearer auth and parses hosted manifest', async () => { const requests: Array<{ url: string; authorization: string | null }> = []; const client = new HostedClient({ diff --git a/src/hosted/client.ts b/src/hosted/client.ts index fd165a8a..14ef5aee 100644 --- a/src/hosted/client.ts +++ b/src/hosted/client.ts @@ -18,6 +18,7 @@ import type { HostedUploadArtifactResponse, HostedManifest, HostedMarketplaceInstallation, + HostedMarketplaceInstallationRow, HostedMarketplaceSearchResult, HostedTraceReceipt, } from './types.js'; @@ -124,6 +125,34 @@ export class HostedClient { return body.result; } + async listMarketplaceInstallations(): Promise { + const body = await this.request('/v1/marketplace/installations'); + if (!hasExactKeys(body, ['ok', 'result']) || body.ok !== true + || !isRecord(body.result) || !Array.isArray(body.result.installations) + || !body.result.installations.every(isHostedMarketplaceInstallationRow)) { + throw protocolError('Webcmd Cloud returned an invalid marketplace installation list.'); + } + return body.result.installations; + } + + async uninstallMarketplacePlugin(name: string): Promise<{ uninstalled: true }> { + const body = await this.request(`/v1/marketplace/installations/${encodeURIComponent(name)}`, { method: 'DELETE' }); + if (!hasExactKeys(body, ['ok', 'result']) || body.ok !== true + || !isRecord(body.result) || body.result.uninstalled !== true) { + throw protocolError('Webcmd Cloud returned an invalid marketplace uninstall response.'); + } + return { uninstalled: true }; + } + + async updateMarketplacePlugin(name: string): Promise { + const body = await this.request(`/v1/marketplace/installations/${encodeURIComponent(name)}/update`, { method: 'POST' }); + if (!hasExactKeys(body, ['ok', 'result']) || body.ok !== true + || !isHostedMarketplaceUpdateResult(body.result)) { + throw protocolError('Webcmd Cloud returned an invalid marketplace update response.'); + } + return body.result; + } + async execute(input: { command: string; args: Record; @@ -459,6 +488,33 @@ function isHostedMarketplaceInstallation(value: unknown): value is HostedMarketp && typeof value.installSource === 'string'; } +function isHostedMarketplaceInstallationRow(value: unknown): value is HostedMarketplaceInstallationRow { + return hasExactKeys(value, ['name', 'version', 'installSource', 'sourceCommit', 'installedAt', 'updateAvailable']) + && typeof value.name === 'string' + && typeof value.version === 'string' + && typeof value.installSource === 'string' + && (value.sourceCommit === null || typeof value.sourceCommit === 'string') + && typeof value.installedAt === 'string' + && typeof value.updateAvailable === 'boolean'; +} + +// `delisted` is optional and appears ONLY when true (installed plugin whose catalog +// entry was delisted — nothing to update to, a normal outcome not an error). hasExactKeys +// would reject either the 3-key or 4-key shape depending on which list you pass it, so +// check the base 3 keys with hasOnlyKeys (permits the optional 4th) and validate `delisted` +// separately when present. +export type HostedMarketplaceUpdateResult = + | { updated: boolean; name: string; version: string } + | { updated: boolean; name: string; version: string; delisted: true }; + +function isHostedMarketplaceUpdateResult(value: unknown): value is HostedMarketplaceUpdateResult { + return hasOnlyKeys(value, ['updated', 'name', 'version', 'delisted']) + && typeof value.updated === 'boolean' + && typeof value.name === 'string' + && typeof value.version === 'string' + && (value.delisted === undefined || value.delisted === true); +} + function isHostedPublicProfile(value: unknown): boolean { return hasExactKeys(value, [ 'id', 'name', 'workspace', 'default', 'status', diff --git a/src/hosted/runner.test.ts b/src/hosted/runner.test.ts index e7afa47c..f5823b90 100644 --- a/src/hosted/runner.test.ts +++ b/src/hosted/runner.test.ts @@ -335,7 +335,7 @@ describe('runHostedCli', () => { expect(requests).toEqual(['https://api.example.com/v1/marketplace/installations']); }); - it.each(['catalog', 'create', 'update', 'list', 'uninstall'])('rejects unsupported hosted plugin %s without an API call', async (subcommand) => { + it.each(['catalog'])('rejects unsupported hosted plugin %s without an API call', async (subcommand) => { const stderr = sink(); const fetchImpl = vi.fn(); const result = await runHostedCli(['plugin', subcommand], { @@ -349,6 +349,145 @@ describe('runHostedCli', () => { expect(fetchImpl).not.toHaveBeenCalled(); }); + it('lists hosted installations as a table', async () => { + const stdout = sink(); + const stderr = sink(); + const result = await runHostedCli(['plugin', 'list'], { + config: makeHostedConfig({ apiBaseUrl: 'https://api.example.com', apiKey: 'key' }), + stdout: stdout.stream, + stderr: stderr.stream, + fetchImpl: async () => new Response(JSON.stringify({ + ok: true, + result: { + installations: [{ + name: 'alpha', version: '0.1.0', installSource: 'github:agentrhq/webcmd/alpha', + sourceCommit: 'a'.repeat(40), installedAt: '2026-08-07T00:00:00.000Z', updateAvailable: true, + }], + }, + })), + }); + + expect(result).toEqual({ handled: true, exitCode: 0 }); + expect(stderr.text()).toBe(''); + expect(stdout.text()).toContain('alpha'); + expect(stdout.text()).toContain('0.1.0'); + }); + + it('uninstalls a hosted plugin', async () => { + const requests: Array<{ url: string; method: string }> = []; + const stdout = sink(); + const stderr = sink(); + const result = await runHostedCli(['plugin', 'uninstall', 'alpha'], { + config: makeHostedConfig({ apiBaseUrl: 'https://api.example.com', apiKey: 'key' }), + stdout: stdout.stream, + stderr: stderr.stream, + fetchImpl: async (url, init) => { + requests.push({ url: String(url), method: init?.method ?? 'GET' }); + return new Response(JSON.stringify({ ok: true, result: { uninstalled: true } })); + }, + }); + + expect(result).toEqual({ handled: true, exitCode: 0 }); + expect(stderr.text()).toBe(''); + expect(stdout.text()).toContain('alpha'); + expect(requests).toEqual([{ url: 'https://api.example.com/v1/marketplace/installations/alpha', method: 'DELETE' }]); + }); + + it('reports when update finds nothing newer', async () => { + const stdout = sink(); + const stderr = sink(); + const result = await runHostedCli(['plugin', 'update', 'alpha'], { + config: makeHostedConfig({ apiBaseUrl: 'https://api.example.com', apiKey: 'key' }), + stdout: stdout.stream, + stderr: stderr.stream, + fetchImpl: async () => new Response(JSON.stringify({ + ok: true, + result: { updated: false, name: 'alpha', version: '0.1.0' }, + })), + }); + + expect(result).toEqual({ handled: true, exitCode: 0 }); + expect(stderr.text()).toBe(''); + expect(stdout.text()).toMatch(/already|up to date/i); + }); + + it('reports a delisted plugin distinctly from an ordinary no-op update', async () => { + const stdout = sink(); + const stderr = sink(); + const result = await runHostedCli(['plugin', 'update', 'alpha'], { + config: makeHostedConfig({ apiBaseUrl: 'https://api.example.com', apiKey: 'key' }), + stdout: stdout.stream, + stderr: stderr.stream, + fetchImpl: async () => new Response(JSON.stringify({ + ok: true, + result: { updated: false, name: 'alpha', version: '0.1.0', delisted: true }, + })), + }); + + expect(result).toEqual({ handled: true, exitCode: 0 }); + expect(stderr.text()).toBe(''); + expect(stdout.text()).toMatch(/delisted/i); + expect(stdout.text()).not.toMatch(/already|up to date/i); + }); + + it('updates all installed plugins with --all and keeps going after one failure', async () => { + const stdout = sink(); + const stderr = sink(); + let calls = 0; + const result = await runHostedCli(['plugin', 'update', '--all'], { + config: makeHostedConfig({ apiBaseUrl: 'https://api.example.com', apiKey: 'key' }), + stdout: stdout.stream, + stderr: stderr.stream, + fetchImpl: async (url) => { + if (String(url).endsWith('/installations')) { + return new Response(JSON.stringify({ + ok: true, + result: { + installations: [ + { name: 'alpha', version: '0.1.0', installSource: 'a', sourceCommit: null, installedAt: 'x', updateAvailable: true }, + { name: 'beta', version: '0.1.0', installSource: 'b', sourceCommit: null, installedAt: 'x', updateAvailable: true }, + ], + }, + })); + } + calls += 1; + if (String(url).includes('/alpha/update')) { + return new Response(JSON.stringify({ ok: false, error: { code: 'NOT_FOUND', message: 'gone' } }), { status: 404 }); + } + return new Response(JSON.stringify({ ok: true, result: { updated: true, name: 'beta', version: '0.2.0' } })); + }, + }); + + expect(calls).toBe(2); + expect(stdout.text()).toContain('beta'); + expect(stderr.text()).toContain('alpha'); + expect(result.exitCode).not.toBe(0); + }); + + it('scaffolds in hosted mode and prints contribute guidance instead of a local install', async () => { + const stdout = sink(); + const stderr = sink(); + const fetchImpl = vi.fn(); + const tempDir = await mkdtemp(path.join(tmpdir(), 'webcmd-hosted-plugin-create-')); + try { + const result = await runHostedCli(['plugin', 'create', 'acme', '--dir', tempDir, + '--author-name', 'A', '--author-handle', 'a'], { + config: makeHostedConfig({ apiBaseUrl: 'https://api.example.com', apiKey: 'key' }), + stdout: stdout.stream, + stderr: stderr.stream, + fetchImpl, + }); + + expect(result).toEqual({ handled: true, exitCode: 0 }); + expect(stdout.text()).toContain('Plugin scaffold created'); + expect(stdout.text()).not.toContain('plugin install file://'); + expect(stdout.text()).toMatch(/pull request|contribute/i); + expect(fetchImpl).not.toHaveBeenCalled(); + } finally { + await rm(tempDir, { recursive: true, force: true }); + } + }); + it('shows hosted plugin search and install help without an API call', async () => { const stdout = sink(); const stderr = sink(); diff --git a/src/hosted/runner.ts b/src/hosted/runner.ts index 20ec8341..e4205332 100644 --- a/src/hosted/runner.ts +++ b/src/hosted/runner.ts @@ -2,7 +2,15 @@ import { readFileSync, writeFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { Command, CommanderError } from 'commander'; -import { configureCompletionCommandSurface, configureListCommandSurface, configurePluginInstallSurface, configurePluginSearchSurface } from '../builtin-command-surface.js'; +import { + configureCompletionCommandSurface, + configureListCommandSurface, + configurePluginInstallSurface, + configurePluginListSurface, + configurePluginSearchSurface, + configurePluginUninstallSurface, + configurePluginUpdateSurface, +} from '../builtin-command-surface.js'; import { BrowserSessionArgvError, rewriteBrowserArgv } from '../cli-argv-preprocess.js'; import { CommanderStructuralError, MissingRequiredPositionalError } from '../command-surface.js'; import { filterCommandsByTag, formatRootHelp, getCommandCompletionCandidates } from '../command-presentation.js'; @@ -216,10 +224,11 @@ async function dispatchHosted( if (args[0] === 'plugin') { const subcommand = args[1]; - if (subcommand !== 'search' && subcommand !== 'install' && subcommand !== '--help' && subcommand !== '-h') { + const allowed = new Set(['search', 'install', 'list', 'uninstall', 'update', 'create', '--help', '-h']); + if (!allowed.has(subcommand ?? '')) { throw new ConfigError( `webcmd plugin ${subcommand ?? ''}`.trimEnd() + ' is not available in hosted mode.', - 'Hosted mode supports: webcmd plugin search and webcmd plugin install.', + 'Hosted mode supports: webcmd plugin search, install, list, uninstall, update, and create.', ); } const parsed = parseHostedPluginSurface(args.slice(1), normalized.literal); @@ -243,8 +252,75 @@ async function dispatchHosted( } return; } - const installed = await client.installMarketplacePlugin(parsed.source); - await writeToStream(stdout, `✅ Plugin "${installed.name}" installed successfully. Commands are ready to use.\n`); + if (parsed.command === 'install') { + const installed = await client.installMarketplacePlugin(parsed.source); + await writeToStream(stdout, `✅ Plugin "${installed.name}" installed successfully. Commands are ready to use.\n`); + return; + } + if (parsed.command === 'list') { + const installations = await client.listMarketplaceInstallations(); + await renderOutput(installations, { + fmt: parsed.format, + columns: ['name', 'version', 'installSource', 'installedAt', 'updateAvailable'], + title: `${CLI_COMMAND}/plugins`, + source: `${CLI_COMMAND} plugin list`, + stdout, + }); + return; + } + if (parsed.command === 'uninstall') { + await client.uninstallMarketplacePlugin(parsed.name); + await writeToStream(stdout, `✅ Plugin "${parsed.name}" uninstalled.\n`); + return; + } + if (parsed.command === 'update') { + if (!parsed.name && !parsed.all) { + throw new ConfigError( + 'Specify a plugin name or use --all.', + 'Example: webcmd plugin update alpha', + ); + } + if (parsed.name && parsed.all) { + throw new ConfigError('Cannot specify both a plugin name and --all.'); + } + const targets = parsed.all + ? (await client.listMarketplaceInstallations()).map((row) => row.name) + : [parsed.name!]; + let hasErrors = false; + for (const target of targets) { + try { + const outcome = await client.updateMarketplacePlugin(target); + if ('delisted' in outcome && outcome.delisted) { + await writeToStream(stdout, `⚠ "${target}" is installed but its catalog entry was delisted; nothing to update to.\n`); + } else if (outcome.updated) { + await writeToStream(stdout, `✅ Updated "${target}" to ${outcome.version}.\n`); + } else { + await writeToStream(stdout, `✔ "${target}" is already up to date.\n`); + } + } catch (err) { + hasErrors = true; + const message = err instanceof Error ? err.message : String(err); + await writeToStream(stderr, `✗ "${target}" — ${message}\n`); + } + } + if (hasErrors) throw new ConfigError('Some plugins failed to update.'); + return; + } + // parsed.command === 'create' + const { createPluginScaffold } = await import('../plugin-scaffold.js'); + const result = createPluginScaffold(parsed.name, { + ...(parsed.dir !== undefined ? { dir: parsed.dir } : {}), + ...(parsed.description !== undefined ? { description: parsed.description } : {}), + author: { name: parsed.authorName ?? '', handle: parsed.authorHandle ?? '' }, + }); + await writeToStream(stdout, `✅ Plugin scaffold created at ${result.dir}\n\n`); + await writeToStream(stdout, ' Next steps (hosted mode):\n'); + await writeToStream(stdout, ' 1. Author and verify the adapter in the cloud:\n'); + await writeToStream(stdout, ` ${CLI_COMMAND} browser init /\n`); + await writeToStream(stdout, ` ${CLI_COMMAND} browser verify /\n`); + await writeToStream(stdout, ' 2. Copy the verified command files into this scaffold.\n'); + await writeToStream(stdout, ' 3. Open a pull request against agentrhq/webcmd to publish it.\n'); + await writeToStream(stdout, ' See docs/publish-community-plugin.mdx\n'); return; } @@ -860,7 +936,11 @@ async function dispatchHostedProfile( type ParsedHostedPluginSurface = | { kind: 'help'; output: string } | { kind: 'run'; command: 'search'; query?: string; format: string } - | { kind: 'run'; command: 'install'; source: string }; + | { kind: 'run'; command: 'install'; source: string } + | { kind: 'run'; command: 'list'; format: string } + | { kind: 'run'; command: 'uninstall'; name: string } + | { kind: 'run'; command: 'update'; name?: string; all: boolean } + | { kind: 'run'; command: 'create'; name: string; dir?: string; description?: string; authorName?: string; authorHandle?: string }; function parseHostedPluginSurface( argv: readonly string[], @@ -886,6 +966,41 @@ function parseHostedPluginSurface( install.exitOverride().configureOutput(output).action((source: string) => { parsed = { kind: 'run', command: 'install', source }; }); + const list = configurePluginListSurface(plugin.command('list')); + list.exitOverride().configureOutput(output).action((options: { format: string }) => { + parsed = { kind: 'run', command: 'list', format: options.format }; + }); + const uninstall = configurePluginUninstallSurface(plugin.command('uninstall')); + uninstall.exitOverride().configureOutput(output).action((name: string) => { + parsed = { kind: 'run', command: 'uninstall', name }; + }); + const update = configurePluginUpdateSurface(plugin.command('update')); + update.exitOverride().configureOutput(output).action((name: string | undefined, options: { all?: boolean }) => { + parsed = { kind: 'run', command: 'update', ...(name !== undefined ? { name } : {}), all: options.all === true }; + }); + const create = plugin.command('create') + .description('Create a new plugin scaffold') + .argument('', 'Plugin name (lowercase, hyphens allowed)') + .option('-d, --dir ', 'Output directory (default: ./)') + .option('--description ', 'Plugin description') + .option('--author-name ', 'Author display name') + .option('--author-handle ', 'Author GitHub handle'); + create.exitOverride().configureOutput(output).action((name: string, options: { + dir?: string; + description?: string; + authorName?: string; + authorHandle?: string; + }) => { + parsed = { + kind: 'run', + command: 'create', + name, + ...(options.dir !== undefined ? { dir: options.dir } : {}), + ...(options.description !== undefined ? { description: options.description } : {}), + ...(options.authorName !== undefined ? { authorName: options.authorName } : {}), + ...(options.authorHandle !== undefined ? { authorHandle: options.authorHandle } : {}), + }; + }); try { root.parse(literal ? ['--', 'plugin', ...argv] : ['plugin', ...argv], { from: 'user' }); diff --git a/src/hosted/types.ts b/src/hosted/types.ts index 4a4dc695..03ffa852 100644 --- a/src/hosted/types.ts +++ b/src/hosted/types.ts @@ -89,6 +89,15 @@ export interface HostedMarketplaceInstallation { installSource: string; } +export interface HostedMarketplaceInstallationRow { + name: string; + version: string; + installSource: string; + sourceCommit: string | null; + installedAt: string; + updateAvailable: boolean; +} + export interface HostedExecution { id: string; command: string; From fd840292638d116dde2ed160c89ffb95889a0a84 Mon Sep 17 00:00:00 2001 From: beubax Date: Fri, 7 Aug 2026 05:08:15 +0530 Subject: [PATCH 33/39] fix: refuse to update a plugin with uncommitted changes unless forced updatePlugin/updateAllPlugins now check for tracked-file modifications in both the standalone plugin directory and the shared monorepo clone before beginReplaceDir wipes them, with --force to opt out. Local (symlinked) installs are unaffected since they never go through beginReplaceDir. --- src/cli.test.ts | 2 +- src/cli.ts | 7 +- src/plugin.test.ts | 237 +++++++++++++++++++++++++++++++++++++++++++++ src/plugin.ts | 41 +++++++- 4 files changed, 279 insertions(+), 8 deletions(-) diff --git a/src/cli.test.ts b/src/cli.test.ts index 4d263d3a..734ed20e 100644 --- a/src/cli.test.ts +++ b/src/cli.test.ts @@ -813,7 +813,7 @@ name: 'search', usage: 'webcmd plugin update [name] [options]', positionals: [{ name: 'name' }], }); - expect(update.command_options.map((option: any) => option.name)).toEqual(['all']); + expect(update.command_options.map((option: any) => option.name)).toEqual(['all', 'force']); } finally { process.argv = argv; } diff --git a/src/cli.ts b/src/cli.ts index 76c46e62..5d0828f6 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1160,7 +1160,8 @@ cli({ .description('Update a plugin (or all plugins) to the latest version') .argument('[name]', 'Plugin name (required unless --all is passed)') .option('--all', 'Update all installed plugins') - .action(async (name: string | undefined, opts: { all?: boolean }) => { + .option('--force', 'Discard uncommitted changes in the plugin directory') + .action(async (name: string | undefined, opts: { all?: boolean; force?: boolean }) => { if (!name && !opts.all) { console.error('Error: Please specify a plugin name or use the --all flag.'); process.exitCode = EXIT_CODES.USAGE_ERROR; @@ -1175,7 +1176,7 @@ cli({ const { updatePlugin, updateAllPlugins } = await import('./plugin.js'); const { discoverPlugins } = await import('./discovery.js'); if (opts.all) { - const results = updateAllPlugins(); + const results = updateAllPlugins({ force: opts.force === true }); if (results.length > 0) { await discoverPlugins(); } @@ -1207,7 +1208,7 @@ cli({ } try { - updatePlugin(name!); + updatePlugin(name!, { force: opts.force === true }); await discoverPlugins(); console.log(`✅ Plugin "${name}" updated successfully.`); } catch (err) { diff --git a/src/plugin.test.ts b/src/plugin.test.ts index cfd69a8d..858ac4a0 100644 --- a/src/plugin.test.ts +++ b/src/plugin.test.ts @@ -1522,3 +1522,240 @@ describe('updatePlugin transactional staging', () => { }); }); }); + +describe('updatePlugin dirty-checkout guard', () => { + const standaloneName = '__test-dirty-standalone__'; + const standaloneDir = path.join(PLUGINS_DIR, standaloneName); + const monorepoName = '__test-dirty-mono__'; + const monorepoRepoDir = path.join(_getMonoreposDir(), monorepoName); + const monorepoPluginName = 'alpha-dirty'; + const monorepoLink = path.join(PLUGINS_DIR, monorepoPluginName); + + beforeEach(() => { + mockExecFileSync.mockClear(); + mockExecSync.mockClear(); + }); + + afterEach(() => { + try { fs.unlinkSync(monorepoLink); } catch {} + try { fs.rmSync(monorepoLink, { recursive: true, force: true }); } catch {} + try { fs.rmSync(monorepoRepoDir, { recursive: true, force: true }); } catch {} + try { fs.rmSync(standaloneDir, { recursive: true, force: true }); } catch {} + const lock = _readLockFile(); + delete lock[standaloneName]; + delete lock[monorepoPluginName]; + _writeLockFile(lock); + vi.clearAllMocks(); + }); + + function mockStandaloneUpdate(dirtyStatus: string) { + mockExecFileSync.mockImplementation((cmd, args, opts) => { + if (cmd === 'git' && Array.isArray(args) && args[0] === 'status') { + return opts?.cwd === standaloneDir ? dirtyStatus : ''; + } + if (cmd === 'git' && Array.isArray(args) && args[0] === 'clone') { + const cloneDir = String(args[4]); + fs.mkdirSync(cloneDir, { recursive: true }); + fs.writeFileSync(path.join(cloneDir, 'hello.js'), 'cli({ site: "test", name: "hello", access: "read" })'); + fs.writeFileSync(path.join(cloneDir, 'package.json'), JSON.stringify({ name: standaloneName })); + return ''; + } + if (cmd === 'git' && Array.isArray(args) && args[0] === 'rev-parse' && args[1] === 'HEAD') { + return '1234567890abcdef1234567890abcdef12345678\n'; + } + return ''; + }); + } + + it('refuses to update a standalone plugin with uncommitted changes', () => { + fs.mkdirSync(standaloneDir, { recursive: true }); + fs.writeFileSync(path.join(standaloneDir, 'old.js'), 'cli({ site: "old", name: "old", access: "read" })'); + const lock = _readLockFile(); + lock[standaloneName] = { + source: { kind: 'git', url: 'https://github.com/user/webcmd-plugin-__test-dirty-standalone__.git' }, + commitHash: 'oldhasholdhasholdhasholdhasholdhasholdh', + installedAt: '2025-01-01T00:00:00.000Z', + }; + _writeLockFile(lock); + + mockStandaloneUpdate(' M old.js\n'); + + expect(() => updatePlugin(standaloneName)).toThrow(/uncommitted/i); + expect(mockExecFileSync.mock.calls.some(([cmd, args]) => cmd === 'git' && Array.isArray(args) && args[0] === 'clone')).toBe(false); + }); + + it('updates a standalone plugin with uncommitted changes when forced', () => { + fs.mkdirSync(standaloneDir, { recursive: true }); + fs.writeFileSync(path.join(standaloneDir, 'old.js'), 'cli({ site: "old", name: "old", access: "read" })'); + const lock = _readLockFile(); + lock[standaloneName] = { + source: { kind: 'git', url: 'https://github.com/user/webcmd-plugin-__test-dirty-standalone__.git' }, + commitHash: 'oldhasholdhasholdhasholdhasholdhasholdh', + installedAt: '2025-01-01T00:00:00.000Z', + }; + _writeLockFile(lock); + + mockStandaloneUpdate(' M old.js\n'); + + expect(() => updatePlugin(standaloneName, { force: true })).not.toThrow(); + }); + + it('updates a clean standalone plugin without force', () => { + fs.mkdirSync(standaloneDir, { recursive: true }); + fs.writeFileSync(path.join(standaloneDir, 'old.js'), 'cli({ site: "old", name: "old", access: "read" })'); + const lock = _readLockFile(); + lock[standaloneName] = { + source: { kind: 'git', url: 'https://github.com/user/webcmd-plugin-__test-dirty-standalone__.git' }, + commitHash: 'oldhasholdhasholdhasholdhasholdhasholdh', + installedAt: '2025-01-01T00:00:00.000Z', + }; + _writeLockFile(lock); + + mockStandaloneUpdate(''); + + expect(() => updatePlugin(standaloneName)).not.toThrow(); + }); + + it('refuses to update a monorepo plugin when the shared clone has uncommitted changes, before touching the clone', () => { + const subDir = path.join(monorepoRepoDir, 'packages', monorepoPluginName); + fs.mkdirSync(subDir, { recursive: true }); + fs.writeFileSync(path.join(subDir, 'old.js'), 'cli({ site: "old", name: "old", access: "read" })'); + fs.mkdirSync(PLUGINS_DIR, { recursive: true }); + fs.symlinkSync(subDir, monorepoLink, 'dir'); + + const lock = _readLockFile(); + lock[monorepoPluginName] = { + source: { + kind: 'monorepo', + url: 'https://github.com/user/webcmd-plugins-__test-dirty-mono__.git', + repoName: monorepoName, + subPath: `packages/${monorepoPluginName}`, + }, + commitHash: 'oldmonooldmonooldmonooldmonooldmonoold', + installedAt: '2025-01-01T00:00:00.000Z', + }; + _writeLockFile(lock); + + mockExecFileSync.mockImplementation((cmd, args, opts) => { + if (cmd === 'git' && Array.isArray(args) && args[0] === 'status') { + return opts?.cwd === monorepoRepoDir ? ' M packages/alpha-dirty/old.js\n' : ''; + } + return ''; + }); + + expect(() => updatePlugin(monorepoPluginName)).toThrow(/uncommitted/i); + expect(mockExecFileSync.mock.calls.some(([cmd, args]) => cmd === 'git' && Array.isArray(args) && args[0] === 'clone')).toBe(false); + expect(fs.readFileSync(path.join(subDir, 'old.js'), 'utf-8')).toContain('site: "old"'); + }); + + it('updates a monorepo plugin with uncommitted changes in the shared clone when forced', () => { + const subDir = path.join(monorepoRepoDir, 'packages', monorepoPluginName); + fs.mkdirSync(subDir, { recursive: true }); + fs.writeFileSync(path.join(subDir, 'old.js'), 'cli({ site: "old", name: "old", access: "read" })'); + fs.mkdirSync(PLUGINS_DIR, { recursive: true }); + fs.symlinkSync(subDir, monorepoLink, 'dir'); + + const lock = _readLockFile(); + lock[monorepoPluginName] = { + source: { + kind: 'monorepo', + url: 'https://github.com/user/webcmd-plugins-__test-dirty-mono__.git', + repoName: monorepoName, + subPath: `packages/${monorepoPluginName}`, + }, + commitHash: 'oldmonooldmonooldmonooldmonooldmonoold', + installedAt: '2025-01-01T00:00:00.000Z', + }; + _writeLockFile(lock); + + mockExecFileSync.mockImplementation((cmd, args, opts) => { + if (cmd === 'git' && Array.isArray(args) && args[0] === 'status') { + return opts?.cwd === monorepoRepoDir ? ' M packages/alpha-dirty/old.js\n' : ''; + } + if (cmd === 'git' && Array.isArray(args) && args[0] === 'clone') { + const cloneDir = String(args[4]); + const alphaDir = path.join(cloneDir, 'packages', monorepoPluginName); + fs.mkdirSync(alphaDir, { recursive: true }); + fs.writeFileSync(path.join(cloneDir, 'package.json'), JSON.stringify({ + name: 'webcmd-plugins-__test-dirty-mono__', + private: true, + })); + fs.writeFileSync(path.join(cloneDir, 'webcmd-plugin.json'), JSON.stringify({ + plugins: { + [monorepoPluginName]: { path: `packages/${monorepoPluginName}` }, + }, + })); + fs.writeFileSync(path.join(alphaDir, 'hello.js'), 'cli({ site: "test", name: "hello", access: "read" })'); + return ''; + } + if (cmd === 'git' && Array.isArray(args) && args[0] === 'rev-parse' && args[1] === 'HEAD') { + return '1234567890abcdef1234567890abcdef12345678\n'; + } + return ''; + }); + + expect(() => updatePlugin(monorepoPluginName, { force: true })).not.toThrow(); + }); + + it('local (symlinked) plugin updates are not blocked by the dirty-checkout guard', () => { + const localTarget = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-local-dirty-')); + const linkPath = path.join(PLUGINS_DIR, '__test-local-dirty__'); + + fs.mkdirSync(PLUGINS_DIR, { recursive: true }); + fs.writeFileSync(path.join(localTarget, 'hello.js'), 'cli({ site: "test", name: "hello", access: "read" })'); + fs.symlinkSync(localTarget, linkPath, 'dir'); + + const lock = _readLockFile(); + lock['__test-local-dirty__'] = { + source: { kind: 'local', path: localTarget }, + commitHash: 'local', + installedAt: '2025-01-01T00:00:00.000Z', + }; + _writeLockFile(lock); + + // Even if `git status` would report the checkout as dirty, local installs + // are symlinked to the user's own dev checkout and never go through + // beginReplaceDir, so the guard must not fire for them. + mockExecFileSync.mockImplementation((cmd, args) => { + if (cmd === 'git' && Array.isArray(args) && args[0] === 'status') { + return ' M hello.js\n'; + } + return ''; + }); + + expect(() => updatePlugin('__test-local-dirty__')).not.toThrow(); + + try { fs.unlinkSync(linkPath); } catch {} + try { fs.rmSync(localTarget, { recursive: true, force: true }); } catch {} + const finalLock = _readLockFile(); + delete finalLock['__test-local-dirty__']; + _writeLockFile(finalLock); + }); +}); + +describe('getDirtyFiles', () => { + beforeEach(() => { + mockExecFileSync.mockClear(); + }); + + it('returns an empty array when git invocation fails (non-git directory or missing git binary)', () => { + mockExecFileSync.mockImplementation(() => { + throw new Error('not a git repository'); + }); + expect(pluginModule.getDirtyFiles('/nonexistent/dir')).toEqual([]); + }); + + it('parses tracked-file modifications from porcelain output', () => { + mockExecFileSync.mockImplementation(() => ' M foo.js\n?? untracked.js\n'); + expect(pluginModule.getDirtyFiles('/some/dir')).toEqual(['M foo.js', '?? untracked.js']); + }); + + it('passes --untracked-files=no so new untracked files never block an update', () => { + mockExecFileSync.mockImplementation((cmd, args) => { + expect(args).toContain('--untracked-files=no'); + return ''; + }); + pluginModule.getDirtyFiles('/some/dir'); + expect(mockExecFileSync).toHaveBeenCalled(); + }); +}); diff --git a/src/plugin.ts b/src/plugin.ts index b2ec2036..4f2c246b 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -509,6 +509,35 @@ export function getCommitHash(dir: string): string | undefined { } } +/** + * Report tracked-file modifications in a git checkout. + * + * Returns an empty array for a non-git directory: a plugin installed without + * git history has no baseline to compare against, so there is nothing to protect. + */ +export function getDirtyFiles(dir: string): string[] { + try { + const out = execFileSync('git', ['status', '--porcelain', '--untracked-files=no'], { + cwd: dir, + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }); + return out.split('\n').map((line) => line.trim()).filter(Boolean); + } catch { + return []; + } +} + +function assertPluginNotDirty(name: string, dir: string, force: boolean): void { + if (force) return; + const dirty = getDirtyFiles(dir); + if (dirty.length === 0) return; + throw new PluginError( + `Plugin "${name}" has uncommitted changes that updating would destroy:\n ${dirty.slice(0, 10).join('\n ')}`, + 'Commit or stash them, re-run with --force to discard them, or develop against a symlinked checkout with "webcmd plugin install file:///path".', + ); +} + /** * Validate that a downloaded plugin directory is a structurally valid plugin. * Checks for at least one command file (.ts, .js) and a valid @@ -1097,17 +1126,18 @@ function isSymlinkSync(p: string): boolean { * For monorepo sub-plugins: pulls the monorepo root and re-runs lifecycle * for all sub-plugins from the same monorepo. */ -export function updatePlugin(name: string): void { +export function updatePlugin(name: string, options: { force?: boolean } = {}): void { const targetDir = path.join(PLUGINS_DIR, name); if (!fs.existsSync(targetDir)) { throw new Error(`Plugin "${name}" is not installed.`); } - const lock = readLockFile(); const lockEntry = lock[name]; const source = resolvePluginSource(lockEntry, targetDir); if (source?.kind === 'local') { + // Local installs are symlinked to the user's own checkout, not replaced + // wholesale, so dirty edits there are the intended workflow, not a hazard. updateLocalPlugin(name, targetDir, lock, lockEntry); return; } @@ -1116,6 +1146,7 @@ export function updatePlugin(name: string): void { const monoDir = path.join(getMonoreposDir(), source.repoName); const monoName = source.repoName; const cloneUrl = source.url; + assertPluginNotDirty(monoName, monoDir, options.force === true); withTempClone(cloneUrl, (tmpCloneDir) => { const manifest = readPluginManifest(tmpCloneDir); if (!manifest || !isMonorepo(manifest)) { @@ -1157,6 +1188,8 @@ export function updatePlugin(name: string): void { return; } + assertPluginNotDirty(name, targetDir, options.force === true); + const cloneUrl = resolveRemotePluginSource(lockEntry, targetDir); withTempClone(cloneUrl, (tmpCloneDir) => { const manifest = readPluginManifest(tmpCloneDir); @@ -1190,10 +1223,10 @@ export interface UpdateResult { * Update all installed plugins. * Continues even if individual plugin updates fail. */ -export function updateAllPlugins(): UpdateResult[] { +export function updateAllPlugins(options: { force?: boolean } = {}): UpdateResult[] { return listPlugins().map((plugin): UpdateResult => { try { - updatePlugin(plugin.name); + updatePlugin(plugin.name, options); return { name: plugin.name, success: true }; } catch (err) { return { From 6f83a750042592ca84ef00b2e403fcb8b9596d95 Mon Sep 17 00:00:00 2001 From: beubax Date: Fri, 7 Aug 2026 10:28:37 +0530 Subject: [PATCH 34/39] fix: count untracked files as dirty in the plugin update guard git status --porcelain already omits gitignored paths (node_modules/dist never show up), so --untracked-files=no was only hiding the case the guard exists to catch: a new, unstaged command file. Drop the flag, label refusal entries as new-unstaged vs modified for clarity. --- src/plugin.test.ts | 59 ++++++++++++++++++++++++++++++++++++++++++++-- src/plugin.ts | 19 ++++++++++++--- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/src/plugin.test.ts b/src/plugin.test.ts index 858ac4a0..386e3690 100644 --- a/src/plugin.test.ts +++ b/src/plugin.test.ts @@ -1616,6 +1616,61 @@ describe('updatePlugin dirty-checkout guard', () => { expect(() => updatePlugin(standaloneName)).not.toThrow(); }); + it('refuses to update a standalone plugin that has only an untracked new file', () => { + fs.mkdirSync(standaloneDir, { recursive: true }); + fs.writeFileSync(path.join(standaloneDir, 'old.js'), 'cli({ site: "old", name: "old", access: "read" })'); + const lock = _readLockFile(); + lock[standaloneName] = { + source: { kind: 'git', url: 'https://github.com/user/webcmd-plugin-__test-dirty-standalone__.git' }, + commitHash: 'oldhasholdhasholdhasholdhasholdhasholdh', + installedAt: '2025-01-01T00:00:00.000Z', + }; + _writeLockFile(lock); + + // Simulates a command file the user is mid-writing that hasn't been `git add`ed yet. + mockStandaloneUpdate('?? new-command.js\n'); + + expect(() => updatePlugin(standaloneName)).toThrow(/uncommitted/i); + expect(mockExecFileSync.mock.calls.some(([cmd, args]) => cmd === 'git' && Array.isArray(args) && args[0] === 'clone')).toBe(false); + }); + + it('updates a standalone plugin with only an untracked new file when forced', () => { + fs.mkdirSync(standaloneDir, { recursive: true }); + fs.writeFileSync(path.join(standaloneDir, 'old.js'), 'cli({ site: "old", name: "old", access: "read" })'); + const lock = _readLockFile(); + lock[standaloneName] = { + source: { kind: 'git', url: 'https://github.com/user/webcmd-plugin-__test-dirty-standalone__.git' }, + commitHash: 'oldhasholdhasholdhasholdhasholdhasholdh', + installedAt: '2025-01-01T00:00:00.000Z', + }; + _writeLockFile(lock); + + mockStandaloneUpdate('?? new-command.js\n'); + + expect(() => updatePlugin(standaloneName, { force: true })).not.toThrow(); + }); + + it('updates cleanly when the only untracked paths are gitignored, since git status omits them entirely', () => { + fs.mkdirSync(standaloneDir, { recursive: true }); + fs.writeFileSync(path.join(standaloneDir, 'old.js'), 'cli({ site: "old", name: "old", access: "read" })'); + fs.writeFileSync(path.join(standaloneDir, '.gitignore'), 'node_modules/\ndist/\n'); + fs.mkdirSync(path.join(standaloneDir, 'node_modules'), { recursive: true }); + fs.writeFileSync(path.join(standaloneDir, 'node_modules', 'junk.js'), '// build artifact\n'); + const lock = _readLockFile(); + lock[standaloneName] = { + source: { kind: 'git', url: 'https://github.com/user/webcmd-plugin-__test-dirty-standalone__.git' }, + commitHash: 'oldhasholdhasholdhasholdhasholdhasholdh', + installedAt: '2025-01-01T00:00:00.000Z', + }; + _writeLockFile(lock); + + // Real `git status --porcelain` omits gitignored paths entirely (no `!!` + // entry either, unless --ignored is passed) — mock that exact behavior. + mockStandaloneUpdate(''); + + expect(() => updatePlugin(standaloneName)).not.toThrow(); + }); + it('refuses to update a monorepo plugin when the shared clone has uncommitted changes, before touching the clone', () => { const subDir = path.join(monorepoRepoDir, 'packages', monorepoPluginName); fs.mkdirSync(subDir, { recursive: true }); @@ -1750,9 +1805,9 @@ describe('getDirtyFiles', () => { expect(pluginModule.getDirtyFiles('/some/dir')).toEqual(['M foo.js', '?? untracked.js']); }); - it('passes --untracked-files=no so new untracked files never block an update', () => { + it('does not pass --untracked-files=no, so untracked files are reported (git already omits gitignored paths)', () => { mockExecFileSync.mockImplementation((cmd, args) => { - expect(args).toContain('--untracked-files=no'); + expect(args).not.toContain('--untracked-files=no'); return ''; }); pluginModule.getDirtyFiles('/some/dir'); diff --git a/src/plugin.ts b/src/plugin.ts index 4f2c246b..eba5dff7 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -510,14 +510,20 @@ export function getCommitHash(dir: string): string | undefined { } /** - * Report tracked-file modifications in a git checkout. + * Report tracked-file modifications and untracked files in a git checkout. + * + * Untracked files are included on purpose: `git status` already excludes + * gitignored paths (build output like node_modules/dist never shows up), so + * anything untracked that does show up is real, unsaved user work — e.g. a + * new command file that hasn't been `git add`ed yet — which updating would + * destroy just as surely as an uncommitted edit to a tracked file. * * Returns an empty array for a non-git directory: a plugin installed without * git history has no baseline to compare against, so there is nothing to protect. */ export function getDirtyFiles(dir: string): string[] { try { - const out = execFileSync('git', ['status', '--porcelain', '--untracked-files=no'], { + const out = execFileSync('git', ['status', '--porcelain'], { cwd: dir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], @@ -528,12 +534,19 @@ export function getDirtyFiles(dir: string): string[] { } } +function describeDirtyEntry(entry: string): string { + const isUntracked = entry.startsWith('??'); + const file = entry.replace(/^\?\?\s*/, '').replace(/^[MADRCU! ]+\s*/, ''); + return isUntracked ? `${file} (new, unstaged)` : `${file} (modified)`; +} + function assertPluginNotDirty(name: string, dir: string, force: boolean): void { if (force) return; const dirty = getDirtyFiles(dir); if (dirty.length === 0) return; + const described = dirty.slice(0, 10).map(describeDirtyEntry); throw new PluginError( - `Plugin "${name}" has uncommitted changes that updating would destroy:\n ${dirty.slice(0, 10).join('\n ')}`, + `Plugin "${name}" has uncommitted changes that updating would destroy:\n ${described.join('\n ')}`, 'Commit or stash them, re-run with --force to discard them, or develop against a symlinked checkout with "webcmd plugin install file:///path".', ); } From 5816d504278c143a5923b5e915a80fa13441a976 Mon Sep 17 00:00:00 2001 From: beubax Date: Fri, 7 Aug 2026 10:33:21 +0530 Subject: [PATCH 35/39] ci: reconcile the hosted plugin catalog on merge to main Co-Authored-By: Claude Opus 5 --- .../workflows/reconcile-hosted-plugins.yml | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/reconcile-hosted-plugins.yml diff --git a/.github/workflows/reconcile-hosted-plugins.yml b/.github/workflows/reconcile-hosted-plugins.yml new file mode 100644 index 00000000..51db745f --- /dev/null +++ b/.github/workflows/reconcile-hosted-plugins.yml @@ -0,0 +1,45 @@ +# Required repository secrets: +# GCP_WORKLOAD_IDENTITY_PROVIDER — workload identity provider resource name +# GCP_RECONCILE_SERVICE_ACCOUNT — service account with run.jobs.run on the job +# GCP_REGION — region hosting webcmd-reconcile-marketplace +# +# The Cloud Run Job webcmd-reconcile-marketplace must exist and run +# `npm run job:reconcile-marketplace` with DATABASE_URL, WEBCMD_ARTIFACT_ROOT, +# and GITHUB_TOKEN configured. + +name: Reconcile hosted plugin catalog + +on: + push: + branches: [main] + paths: + - 'plugins/**' + - 'webcmd-plugin.json' + workflow_dispatch: + +concurrency: + group: reconcile-hosted-plugins + cancel-in-progress: false + +jobs: + reconcile: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - name: Authenticate to Google Cloud + uses: google-github-actions/auth@v2 + with: + workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ secrets.GCP_RECONCILE_SERVICE_ACCOUNT }} + + - name: Set up gcloud CLI + uses: google-github-actions/setup-gcloud@v2 + + - name: Execute Cloud Run Job + run: | + gcloud run jobs execute webcmd-reconcile-marketplace \ + --region "${{ secrets.GCP_REGION }}" \ + --update-env-vars "WEBCMD_PLUGINS_COMMIT=${{ github.sha }}" \ + --wait From 5b158ffc4aa7220ac2f67ac131608060d1e343b1 Mon Sep 17 00:00:00 2001 From: beubax Date: Fri, 7 Aug 2026 10:37:56 +0530 Subject: [PATCH 36/39] ci: clarify --update-env-vars semantics and workflow_dispatch usage Add inline comments explaining that --update-env-vars on 'execute' is a per-execution override (not a job-spec mutation) and that workflow_dispatch runs should target main to avoid delisting plugins. Co-Authored-By: Claude Opus 5 --- .github/workflows/reconcile-hosted-plugins.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/reconcile-hosted-plugins.yml b/.github/workflows/reconcile-hosted-plugins.yml index 51db745f..dbbc79b4 100644 --- a/.github/workflows/reconcile-hosted-plugins.yml +++ b/.github/workflows/reconcile-hosted-plugins.yml @@ -16,6 +16,9 @@ on: - 'plugins/**' - 'webcmd-plugin.json' workflow_dispatch: + # Note: manual runs should be triggered against main to ensure github.sha + # points to the current tip. Reconciling an older ref would delist plugins + # added since that commit. concurrency: group: reconcile-hosted-plugins @@ -39,6 +42,9 @@ jobs: - name: Execute Cloud Run Job run: | + # Note: --update-env-vars on 'execute' (not 'update') is a per-execution + # override that does not modify the job's stored configuration. See: + # "environment variables overrides for an execution of a job" gcloud run jobs execute webcmd-reconcile-marketplace \ --region "${{ secrets.GCP_REGION }}" \ --update-env-vars "WEBCMD_PLUGINS_COMMIT=${{ github.sha }}" \ From 33429734a482651e5295f8a838388a999e57a47c Mon Sep 17 00:00:00 2001 From: beubax Date: Fri, 7 Aug 2026 10:54:40 +0530 Subject: [PATCH 37/39] fix: fail closed on unexpected git errors in the plugin dirty-checkout guard getDirtyFiles previously treated any git failure (missing binary, "detected dubious ownership in repository", permission errors) as "clean", letting beginReplaceDir silently destroy uncommitted work. It also ran `git status --porcelain` with no pathspec, which reports the whole enclosing repository rather than just the plugin directory, causing spurious refusals for plugins inside a larger repo (e.g. a dotfiles-tracked home directory). Now getDirtyFiles probes with `git rev-parse --git-dir` first: a genuine non-repository still proceeds, but any other failure refuses the update with a message pointing at --force. `git status` is scoped with `-- .` so only the plugin directory is considered. Co-Authored-By: Claude Opus 5 --- src/plugin.test.ts | 87 ++++++++++++++++++++++++++++++++++++++++++++-- src/plugin.ts | 54 ++++++++++++++++++++++++---- 2 files changed, 132 insertions(+), 9 deletions(-) diff --git a/src/plugin.test.ts b/src/plugin.test.ts index 386e3690..b6cd4e6e 100644 --- a/src/plugin.test.ts +++ b/src/plugin.test.ts @@ -1600,6 +1600,47 @@ describe('updatePlugin dirty-checkout guard', () => { expect(() => updatePlugin(standaloneName, { force: true })).not.toThrow(); }); + it('refuses to update when git status fails for a reason other than "not a repository" (e.g. dubious ownership), and proceeds with --force', () => { + fs.mkdirSync(standaloneDir, { recursive: true }); + fs.writeFileSync(path.join(standaloneDir, 'old.js'), 'cli({ site: "old", name: "old", access: "read" })'); + const lock = _readLockFile(); + lock[standaloneName] = { + source: { kind: 'git', url: 'https://github.com/user/webcmd-plugin-__test-dirty-standalone__.git' }, + commitHash: 'oldhasholdhasholdhasholdhasholdhasholdh', + installedAt: '2025-01-01T00:00:00.000Z', + }; + _writeLockFile(lock); + + mockExecFileSync.mockImplementation((cmd, args, opts) => { + if (cmd === 'git' && Array.isArray(args) && args[0] === 'rev-parse' && args[1] === '--git-dir') { + if (opts?.cwd === standaloneDir) return '.git\n'; + return '.git\n'; + } + if (cmd === 'git' && Array.isArray(args) && args[0] === 'status') { + if (opts?.cwd === standaloneDir) { + const err: NodeJS.ErrnoException & { stderr?: string } = new Error('git exited with code 128'); + err.stderr = `fatal: detected dubious ownership in repository at '${standaloneDir}'`; + throw err; + } + return ''; + } + if (cmd === 'git' && Array.isArray(args) && args[0] === 'clone') { + const cloneDir = String(args[4]); + fs.mkdirSync(cloneDir, { recursive: true }); + fs.writeFileSync(path.join(cloneDir, 'hello.js'), 'cli({ site: "test", name: "hello", access: "read" })'); + fs.writeFileSync(path.join(cloneDir, 'package.json'), JSON.stringify({ name: standaloneName })); + return ''; + } + if (cmd === 'git' && Array.isArray(args) && args[0] === 'rev-parse' && args[1] === 'HEAD') { + return '1234567890abcdef1234567890abcdef12345678\n'; + } + return ''; + }); + + expect(() => updatePlugin(standaloneName)).toThrow(/could not determine|dubious ownership|--force/i); + expect(() => updatePlugin(standaloneName, { force: true })).not.toThrow(); + }); + it('updates a clean standalone plugin without force', () => { fs.mkdirSync(standaloneDir, { recursive: true }); fs.writeFileSync(path.join(standaloneDir, 'old.js'), 'cli({ site: "old", name: "old", access: "read" })'); @@ -1793,15 +1834,41 @@ describe('getDirtyFiles', () => { mockExecFileSync.mockClear(); }); - it('returns an empty array when git invocation fails (non-git directory or missing git binary)', () => { + it('returns an empty array for a genuine non-git directory', () => { mockExecFileSync.mockImplementation(() => { - throw new Error('not a git repository'); + const err: NodeJS.ErrnoException & { stderr?: string } = new Error( + "fatal: not a git repository (or any of the parent directories): .git", + ); + err.stderr = err.message; + throw err; }); expect(pluginModule.getDirtyFiles('/nonexistent/dir')).toEqual([]); }); + it('refuses (fails closed) when git status fails for a reason other than "not a repository", e.g. dubious ownership', () => { + mockExecFileSync.mockImplementation((cmd, args) => { + if (Array.isArray(args) && args[0] === 'rev-parse') return '.git\n'; + const err: NodeJS.ErrnoException & { stderr?: string } = new Error('git exited with code 128'); + err.stderr = 'fatal: detected dubious ownership in repository at \'/some/dir\''; + throw err; + }); + expect(() => pluginModule.getDirtyFiles('/some/dir')).toThrow(/could not determine|dubious ownership/i); + }); + + it('refuses (fails closed) when the git binary itself is missing', () => { + mockExecFileSync.mockImplementation(() => { + const err: NodeJS.ErrnoException = new Error('spawnSync git ENOENT'); + err.code = 'ENOENT'; + throw err; + }); + expect(() => pluginModule.getDirtyFiles('/some/dir')).toThrow(/could not determine/i); + }); + it('parses tracked-file modifications from porcelain output', () => { - mockExecFileSync.mockImplementation(() => ' M foo.js\n?? untracked.js\n'); + mockExecFileSync.mockImplementation((cmd, args) => { + if (Array.isArray(args) && args[0] === 'rev-parse') return '.git\n'; + return ' M foo.js\n?? untracked.js\n'; + }); expect(pluginModule.getDirtyFiles('/some/dir')).toEqual(['M foo.js', '?? untracked.js']); }); @@ -1813,4 +1880,18 @@ describe('getDirtyFiles', () => { pluginModule.getDirtyFiles('/some/dir'); expect(mockExecFileSync).toHaveBeenCalled(); }); + + it('scopes git status to the plugin directory with "-- .", not the whole enclosing repo', () => { + mockExecFileSync.mockImplementation((cmd, args) => { + if (Array.isArray(args) && args[0] === 'rev-parse') return '.git\n'; + // Simulate real git: only the pathspec-scoped call excludes files + // outside the plugin directory. This is the regression test for the + // bug where `git status --porcelain` (no pathspec) reported dirty + // files from anywhere in the enclosing repository (e.g. repo/top.txt) + // when cwd was repo/sub/plug. + const scoped = Array.isArray(args) && args.includes('--') && args[args.length - 1] === '.'; + return scoped ? '?? sub/plug/new.txt\n' : '?? top.txt\n?? sub/plug/new.txt\n'; + }); + expect(pluginModule.getDirtyFiles('/repo/sub/plug')).toEqual(['?? sub/plug/new.txt']); + }); }); diff --git a/src/plugin.ts b/src/plugin.ts index eba5dff7..4ec1ffb7 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -509,8 +509,24 @@ export function getCommitHash(dir: string): string | undefined { } } +/** True only for git's "this directory has no repository at all" failure. */ +function isNotAGitRepositoryError(error: unknown): boolean { + const stderr = typeof (error as { stderr?: unknown })?.stderr === 'string' + ? (error as { stderr: string }).stderr + : (error as { stderr?: Buffer })?.stderr?.toString('utf-8') ?? ''; + const message = (error as Error)?.message ?? ''; + return /not a git repository/i.test(stderr) || /not a git repository/i.test(message); +} + +function describeGitError(error: unknown): string { + const stderr = typeof (error as { stderr?: unknown })?.stderr === 'string' + ? (error as { stderr: string }).stderr + : (error as { stderr?: Buffer })?.stderr?.toString('utf-8') ?? ''; + return stderr.trim() || (error as Error)?.message || String(error); +} + /** - * Report tracked-file modifications and untracked files in a git checkout. + * Report tracked-file modifications and untracked files within `dir` in a git checkout. * * Untracked files are included on purpose: `git status` already excludes * gitignored paths (build output like node_modules/dist never shows up), so @@ -518,19 +534,45 @@ export function getCommitHash(dir: string): string | undefined { * new command file that hasn't been `git add`ed yet — which updating would * destroy just as surely as an uncommitted edit to a tracked file. * - * Returns an empty array for a non-git directory: a plugin installed without - * git history has no baseline to compare against, so there is nothing to protect. + * The `-- .` pathspec on `git status` restricts the report to `dir` itself. + * Without it, git reports the *entire enclosing repository* — e.g. a plugin + * living inside a dotfiles repo, or any plugin directory that isn't itself a + * repo root, would surface unrelated dirty files from elsewhere in the repo. + * + * Returns an empty array only when `dir` is genuinely not inside a git + * repository: a plugin installed without git history has no baseline to + * compare against, so there is nothing to protect. Any other failure (git + * missing, "detected dubious ownership in repository", permission errors, + * ...) is a failure to determine dirtiness, not evidence of cleanliness, and + * must fail closed — this guard exists to prevent silent data loss, so an + * inconclusive check must refuse the update rather than proceed as if clean. */ export function getDirtyFiles(dir: string): string[] { try { - const out = execFileSync('git', ['status', '--porcelain'], { + execFileSync('git', ['rev-parse', '--git-dir'], { + cwd: dir, + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }); + } catch (error) { + if (isNotAGitRepositoryError(error)) return []; + throw new PluginError( + `Could not determine whether "${dir}" has uncommitted changes: git failed with: ${describeGitError(error)}`, + 'This can happen when git is not installed, or refuses to run here (e.g. "detected dubious ownership in repository"). Re-run with --force to update anyway — this accepts the risk of discarding uncommitted work, which is why it is not the default.', + ); + } + try { + const out = execFileSync('git', ['status', '--porcelain', '--', '.'], { cwd: dir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], }); return out.split('\n').map((line) => line.trim()).filter(Boolean); - } catch { - return []; + } catch (error) { + throw new PluginError( + `Could not determine whether "${dir}" has uncommitted changes: git failed with: ${describeGitError(error)}`, + 'This can happen when git refuses to run here (e.g. "detected dubious ownership in repository"). Re-run with --force to update anyway — this accepts the risk of discarding uncommitted work, which is why it is not the default.', + ); } } From 77e51ddf29c75459d1e2baccca78c35d407b1f58 Mon Sep 17 00:00:00 2001 From: beubax Date: Fri, 7 Aug 2026 12:56:29 +0530 Subject: [PATCH 38/39] fix: ensure parent directories exist before writing plugin output files --- src/plugin-runtime.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/plugin-runtime.ts b/src/plugin-runtime.ts index 32198321..27f0edd3 100644 --- a/src/plugin-runtime.ts +++ b/src/plugin-runtime.ts @@ -1,4 +1,5 @@ import * as fs from 'node:fs'; +import * as path from 'node:path'; import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from './errors.js'; import { cli, Strategy, type CommandArgs, type CliOptions } from './registry-api.js'; import type { IPage } from './types.js'; @@ -108,6 +109,8 @@ export function makeScreenshotCommand(site: string, displayName?: string, extra: const html = await page.evaluate('document.documentElement.outerHTML'); const htmlPath = String(outputPath).replace(/\.\w+$/, '') + '-dom.html'; const snapPath = String(outputPath).replace(/\.\w+$/, '') + '-a11y.txt'; + fs.mkdirSync(path.dirname(htmlPath), { recursive: true }); + fs.mkdirSync(path.dirname(snapPath), { recursive: true }); fs.writeFileSync(htmlPath, html); fs.writeFileSync(snapPath, typeof snap === 'string' ? snap : JSON.stringify(snap, null, 2)); return [{ Status: 'Success', File: htmlPath }, { Status: 'Success', File: snapPath }]; @@ -169,9 +172,13 @@ export function makeDumpCommand(site: string) { args: [], columns: ['action', 'files'], func: async (page: IPage) => { - fs.writeFileSync(`/tmp/${site}-dom.html`, await page.evaluate('document.body.innerHTML')); - fs.writeFileSync(`/tmp/${site}-snapshot.json`, JSON.stringify(await page.snapshot({ interactive: false }), null, 2)); - return [{ action: 'Dom extraction finished', files: `/tmp/${site}-dom.html, /tmp/${site}-snapshot.json` }]; + const domPath = `/tmp/${site}-dom.html`; + const snapPath = `/tmp/${site}-snapshot.json`; + fs.mkdirSync(path.dirname(domPath), { recursive: true }); + fs.mkdirSync(path.dirname(snapPath), { recursive: true }); + fs.writeFileSync(domPath, await page.evaluate('document.body.innerHTML')); + fs.writeFileSync(snapPath, JSON.stringify(await page.snapshot({ interactive: false }), null, 2)); + return [{ action: 'Dom extraction finished', files: `${domPath}, ${snapPath}` }]; }, }); } From 56b0cebbe59fed042042c2721ac5096c6b8ceea4 Mon Sep 17 00:00:00 2001 From: beubax Date: Fri, 7 Aug 2026 13:03:54 +0530 Subject: [PATCH 39/39] chore: update lockfile dependencies for package-lock.json --- package-lock.json | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index eb9b2ffa..233643ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -198,6 +198,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" }, @@ -244,6 +245,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" } @@ -275,6 +277,7 @@ "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -2123,9 +2126,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "funding": [ { "type": "github", @@ -2675,6 +2678,7 @@ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -2687,6 +2691,7 @@ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", "license": "Apache-2.0", + "peer": true, "bin": { "playwright-core": "cli.js" }, @@ -3044,6 +3049,7 @@ "integrity": "sha512-6w9FwtT8WQqRAyTNR+Z+86kghRqpmOLjXUrBlBT6T+CQGDuIMm0VmAqaFUFBIeKDTGobE6/YSigZYLeomzBaRg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "~0.28.0" }, @@ -3087,9 +3093,9 @@ } }, "node_modules/undici": { - "version": "6.27.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz", - "integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.0.tgz", + "integrity": "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==", "license": "MIT", "engines": { "node": ">=18.17" @@ -3108,6 +3114,7 @@ "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.5",
    fixedfixed