diff --git a/scripts/sync-to-cursor.py b/scripts/sync-to-cursor.py index 46ea0f2..fe4e01d 100755 --- a/scripts/sync-to-cursor.py +++ b/scripts/sync-to-cursor.py @@ -7,10 +7,11 @@ - Cursor skills replace the trailing "## Setup" section (which has full install commands inline) with a brief "## If `parallel-cli` is not found" stanza that delegates to the cursor /parallel-setup command -- Cursor has 4 skills (search/extract/research/enrichment); setup/status/ - result are slash commands in cursor, not skills, so they are skipped - by this sync. Skills new to agent-skills (findall, monitor) get both a - synced SKILL.md AND a freshly-generated command wrapper +- Cursor has six CLI skills; setup/status/result are slash commands, + not skills, so they are skipped. Search/extract descriptions use the CLI + as the default because this package does not bundle the source's MCP. +- Missing command wrappers are generated; existing wrappers and version + remain owned by the Cursor repository. Usage: python3 scripts/sync-to-cursor.py [--dry-run] [--cursor-repo PATH] @@ -41,18 +42,34 @@ # Frontmatter fields that only make sense in Claude Code agent-skills. CC_ONLY_FIELDS = {"user-invocable", "argument-hint", "context", "agent"} +CURSOR_DESCRIPTIONS = { + "parallel-web-search": ( + "CLI web search, the default for lookups, current information and research queries. " + "Save retrieved sources as JSON and cite them. Only use parallel-deep-research " + "when the user explicitly requests deep or exhaustive research." + ), + "parallel-web-extract": ( + "CLI content extraction from one or more URLs, including webpages, articles and PDFs. " + "Save JSON, preserve successful content and report per-URL failures." + ), +} + # Replacement for the agent-skills "## Setup" trailing section. # Heading is intentionally specific to "binary not installed" so it doesn't # get conflated with the in-body "errors with `no such command`" guidance, -# which covers a separate failure mode (stale CLI) and routes to -# `parallel-cli update`, not `/parallel-setup`. +# which covers a separate failure mode (stale CLI). Setup also explains +# installation-method-specific upgrades and authentication recovery. CURSOR_SETUP_SECTION = """## If the `parallel-cli` binary is not installed -If the shell reports `command not found: parallel-cli` (i.e. the binary itself is missing — distinct from a `No such command` error from a stale CLI, which the in-body guidance above covers), **stop immediately**. Do NOT search the web yourself, do NOT use any built-in search tools, and do NOT try to answer the query from your own knowledge. Instead, tell the user: +If the shell reports `command not found: parallel-cli`, stop and tell the user to run `/parallel-setup`, then retry their request. Do not substitute built-in search, another provider or an answer from memory. + +### Command and authentication failures -1. `parallel-cli` is not installed -2. Run `/parallel-setup` to install it -3. Then retry their request +`No such command`, `No such option` or `unrecognized arguments` from an installed CLI indicate a stale or mismatched interface. Check its version and upgrade through its installation method using `/parallel-setup`; `parallel-cli update` is for standalone installs only. Verify the required command in the same Cursor terminal before retrying. + +For authentication errors, run `parallel-cli auth --json` and inspect `authenticated`; exit zero alone does not prove authentication. Use `/parallel-setup` for terminal login or environment-key guidance, without requesting credentials in chat. A `403` can be an authorization or billing error: report the actual error and do not assume insufficient balance or add funds automatically. + +For other API/input errors, report the error without calling it a version problem. Reuse saved run IDs to resume asynchronous work. After an ambiguous creation failure, resolve whether a job exists before retrying creation. """ # Skill → cursor command wrapper config. Heading-label is the noun the @@ -98,7 +115,9 @@ def transform_frontmatter(fm: str) -> str: - """Drop CC-only fields. fm is the inner frontmatter, no leading/trailing ---.""" + """Adapt platform fields and routing; retain all other source metadata.""" + name_match = re.search(r"(?m)^name:\s*([^\s]+)", fm) + description = CURSOR_DESCRIPTIONS.get(name_match.group(1)) if name_match else None out: list[str] = [] skip_continuation = False for line in fm.splitlines(): @@ -107,6 +126,10 @@ def transform_frontmatter(fm: str) -> str: skip_continuation = m.group(1) in CC_ONLY_FIELDS if skip_continuation: continue + if m.group(1) == "description" and description is not None: + out.append(f"description: {json.dumps(description)}") + skip_continuation = True + continue elif skip_continuation and (line.startswith((" ", "\t")) or line.strip() == ""): # multi-line value continuation of a skipped field continue diff --git a/skills/parallel-data-enrichment/SKILL.md b/skills/parallel-data-enrichment/SKILL.md index c53a766..35664c2 100644 --- a/skills/parallel-data-enrichment/SKILL.md +++ b/skills/parallel-data-enrichment/SKILL.md @@ -25,9 +25,9 @@ If the user gave a vague intent ("enrich these companies with useful info") and parallel-cli enrich suggest "Find CEO and recent funding info" --json ``` -The response is an envelope: `{title, processor, enriched_columns, warnings}`. Extract just the **`enriched_columns` array** (not the whole envelope) and pass it as the value of `--enriched-columns` on `enrich run`, **in place of `--intent`** — the two flags are alternative ways to specify what to enrich, not combined. If `suggest` returned a `processor`, pass it through explicitly via `--processor` on the `run` call (it's a tuned recommendation for the schema). Skip this whole section if the user already specified the fields they want. +The response is an envelope: `{title, processor, enriched_columns, warnings}`. Extract just the **`enriched_columns` array** (not the whole envelope) and pass it as the value of `--enriched-columns` on `enrich run`, **in place of `--intent`**. These flags are alternative ways to specify what to enrich. If `suggest` returned a `processor`, pass it explicitly via `--processor` on the `run` call. Skip this section if the user already specified the fields they want. -> `enrich suggest` requires `parallel-cli` ≥ 0.3.0. If it errors with anything resembling `no such command` / `No such command` / `unknown command`, **do not bail** — skip the suggestion step, fall through to step 1 with `--intent`, complete the run, and mention `parallel-cli update` (or `pipx upgrade parallel-web-tools`) in the final response so the user picks up the feature next time. +> `enrich suggest` requires `parallel-cli` ≥ 0.3.0. If only that command is missing, skip the optional suggestion step and use `--intent` in step 1. Suggest an installation-specific upgrade from Setup. Do not classify authentication, API or invalid-input failures as an older CLI. An intent-based run itself requests a suggestion; explicit columns default to `core-fast`, while intent can select another processor unless `--processor` overrides it. ## Step 1: Start the enrichment @@ -51,36 +51,44 @@ If this is a **follow-up** to a previous research task and you have its `interac parallel-cli enrich run --data '...' --intent "..." --target "output.csv" --no-wait --json --previous-interaction-id "$INTERACTION_ID" ``` -The enrichment will run with the full context of that prior research — so you can enrich entities discovered earlier without restating what was already found. Note: enrichment does **not** itself produce a new `interaction_id`, so you cannot chain a further follow-up off of an enrichment. +This reuses the prior Task's context. Context chaining is unavailable for Zero Data Retention (ZDR) accounts, so omit the flag there and include the needed context explicitly. Enrichment does **not** return a new `interaction_id`; retain the prior Task ID for later follow-ups. A `taskgroup_id` or Search/Extract `session_id` is not a Task interaction ID. **IMPORTANT:** Always include `--no-wait` so the command returns immediately instead of blocking. -Parse the `--json` output to extract `taskgroup_id` and `url`. The output is `{taskgroup_id, url, num_runs}` — there is no `interaction_id` field, do not look for one. Immediately tell the user: +Save the `--json` output's `taskgroup_id`, `url` and `num_runs` immediately. There is no `interaction_id` field. If creation is interrupted or its response is lost, inspect whether the group was created before submitting another run. Immediately tell the user: - Enrichment has been kicked off - The monitoring URL where they can track progress -Tell them they can background the polling step to continue working while it runs. +The group runs server-side; polling can resume later using its saved ID. ## Step 2: Poll for results -Pick a concrete output path (e.g., `/tmp/enrichment-acme.json`). Note: the file is JSON regardless of the extension you choose — it's an array of `{input, output}` objects, not a CSV. Name it `.json` to avoid confusing yourself or the user. +Pick a persistent, run-specific output path (e.g., `enrichment-acme-tgrp-.json`). Polling overwrites its output file, so inspect any existing file and use a new path unless replacement is intended. The output is JSON regardless of extension: an array of rows with `input` and either `output` or `error`. Async polling does not include basis or per-row interaction IDs; do not invent citations or context IDs. ```bash -parallel-cli enrich poll "$TASKGROUP_ID" --timeout 540 --output "/tmp/enrichment-.json" +parallel-cli enrich poll "$TASKGROUP_ID" --timeout 60 --output "enrichment--.json" ``` Important: -- Use `--timeout 540` (9 minutes) to stay within tool execution limits -- The `--target` from step 1 is unused in `--no-wait` mode — only `--output` here determines where results are saved, and the file is always JSON +- Keep polls bounded; `--timeout 60` allows progress updates between waits. +- The `--target` from step 1 is unused in `--no-wait` mode. Only `--output` here determines where results are saved, and the file is always JSON. +- A completed group can include failed rows. Count rows containing `output` separately from rows containing `error` and compare their total with `num_runs`; an empty or incomplete file is not successful enrichment of the entire input. -### If the poll times out +### If polling times out or is interrupted -Enrichment of large datasets can take longer than 9 minutes. If the poll exits without completing: +Timeout exit 5 or interruption ends the local wait. Check group state before saying it is still running: -1. Tell the user the enrichment is still running server-side -2. Re-run the same `parallel-cli enrich poll` command to continue waiting +```bash +parallel-cli enrich status "$TASKGROUP_ID" --json +``` + +Inspect `is_active`, `status_counts` and `num_runs`. Resume the same poll for an active group, or retrieve results for an inactive group and report failures or unresolved rows. Do not recreate the group on a timeout or automatically rerun failed rows. A local file-write failure can be retried with the same group ID and a writable output path. + +### If the user requested CSV + +Convert the saved JSON locally into a separate CSV. Preserve every original input column and row, including duplicate and failed rows; keep enrichment fields separate from conflicting input names and include an error column for failures. Do not assume streamed rows match original input order or guess a join when row identity is ambiguous. Validate the row count and leave the input CSV untouched. This is local conversion, not a CSV produced by async polling; report both JSON and CSV paths. ## Response format @@ -88,11 +96,11 @@ Enrichment of large datasets can take longer than 9 minutes. If the poll exits w **After step 2:** -1. Report number of rows enriched -2. Preview first few rows from the output file (it's a JSON array of `{input, output}` objects) +1. Report successful, failed and total row counts, with any missing results called out. +2. Preview a few successful rows and a representative failure if present, without claiming all rows succeeded. 3. Tell the user the full path to the output file -Do NOT re-share the monitoring URL after completion — the results are in the output file. +After completion, link the saved output rather than repeating the monitoring URL. ## Setup @@ -102,4 +110,6 @@ If `parallel-cli` is not found, install and authenticate: /parallel:parallel-cli-setup ``` -If any `parallel-cli enrich` command returns `403`, tell the user balance is likely required. Offer to run `parallel-cli balance get`, and if needed ask for explicit confirmation before running `parallel-cli balance add `. Then retry the original enrichment command. +If a documented option or command is missing, identify the install method and upgrade through that method: standalone `parallel-cli update`; pipx `pipx upgrade parallel-web-tools`; uv `uv tool upgrade parallel-web-tools`; Homebrew `brew upgrade parallel-web/tap/parallel-cli`; npm `npm update -g parallel-web-cli`. Recheck version and help in the agent's terminal before retrying. + +For authentication or API errors, inspect the returned message. A `403` can indicate permissions, account policy or billing; it does not prove low balance. Check `parallel-cli auth --json` and its `authenticated` boolean when relevant without exposing credentials. Only a billing-specific error warrants a balance check, and adding funds needs explicit confirmation. Reuse saved group IDs; do not automatically retry an ambiguous creation. diff --git a/skills/parallel-deep-research/SKILL.md b/skills/parallel-deep-research/SKILL.md index 46dd30a..44922a8 100644 --- a/skills/parallel-deep-research/SKILL.md +++ b/skills/parallel-deep-research/SKILL.md @@ -1,6 +1,6 @@ --- name: parallel-deep-research -description: "ONLY use when user explicitly says 'deep research', 'exhaustive', 'comprehensive report', or 'thorough investigation'. Slower and more expensive than parallel-web-search. For normal research/lookup requests, use parallel-web-search instead. Supports multi-turn: pass --previous-interaction-id from a prior research or enrichment to continue with context." +description: "ONLY use when user explicitly says 'deep research', 'exhaustive', 'comprehensive report', or 'thorough investigation'. Slower and more expensive than parallel-web-search. For normal research/lookup requests, use parallel-web-search instead. Supports follow-ups with a known prior Task interaction ID." user-invocable: true argument-hint: compatibility: Requires parallel-cli >= 0.3.0 and internet access. @@ -13,15 +13,15 @@ metadata: Research topic: $ARGUMENTS -> Requires `parallel-cli` ≥ 0.3.0. If any command below errors with `no such option`, `no such command`, or `unrecognized arguments`, the user is on an older CLI. Tell them to run `parallel-cli update` (or `pipx upgrade parallel-web-tools` if installed via pipx), then retry. +> Requires `parallel-cli` ≥ 0.3.0 for text output and context chaining. If a documented command or option is missing, check `parallel-cli --version` and that command's `--help`, then follow the installation-specific upgrade guidance in Setup. API, authentication and input errors are not evidence of an older CLI. ## When to use (vs parallel-web-search) -ONLY use this skill when the user explicitly requests deep/exhaustive research. Deep research is 10-100x slower and more expensive than parallel-web-search. For normal "research X" requests, quick lookups, or fact-checking, use **parallel-web-search** instead. +ONLY use this skill when the user explicitly requests deep/exhaustive research. It can take several minutes and costs more than a quick search, depending on the processor and task. For normal "research X" requests, quick lookups, or fact-checking, use **parallel-web-search** instead. ## Step 1: Start the research -Choose a descriptive filename based on the topic (e.g., `ai-chip-market-2026`, `react-vs-vue-comparison`). Use lowercase with hyphens, no spaces. Reuse this base name in step 2 as `-o "$FILENAME"`. +Choose a descriptive output base in a persistent directory (e.g., `reports/ai-chip-market-2026`). Include the returned run ID to make it unique, then use this base in step 2 as `-o "$FILENAME"`. Check for existing `.json` and `.md` files before saving. ```bash parallel-cli research run "$ARGUMENTS" --processor pro-fast --text --no-wait --json @@ -31,79 +31,73 @@ The `--text` flag tells the API to return a markdown report (with inline citatio Optional with `--text`: pass `--text-description "Keep under 1500 words, focus on M&A activity"` to steer length, format, or focus. -If this is a **follow-up** to a previous research or enrichment task where you know the `interaction_id`, add context chaining: +If this is a **follow-up** and you have a prior Task's returned `interaction_id`, add context chaining. An enrichment `taskgroup_id` and a Search/Extract `session_id` are not Task interaction IDs. Async enrichment does not return a new interaction ID; retain the prior Task ID instead. Context chaining is unavailable for Zero Data Retention (ZDR) accounts, so omit it there and provide the needed context explicitly. ```bash parallel-cli research run "$ARGUMENTS" --processor lite-fast --text --no-wait --json --previous-interaction-id "$INTERACTION_ID" ``` -By chaining `interaction_id` values across requests, each follow-up question automatically has the full context of prior turns — so you can drill deeper without restating what was already researched. Use a lighter processor (`lite-fast` or `base-fast`) for follow-ups since the heavy lifting was done in the initial turn. +This reuses the prior Task's context. A lighter processor (`lite-fast` or `base-fast`) can suit a focused follow-up; choose based on the new question's depth rather than assuming all follow-ups are simple. -This returns instantly. Do NOT omit `--no-wait` — without it the command blocks for minutes and will time out. +Always use `--no-wait` to separate creation from bounded polling. Save the returned IDs immediately. If creation is interrupted or its response is lost, do not submit a replacement until you have checked whether the first task was created. -Processor options (choose based on user request): +Use `pro-fast` by default for exploratory research. Run `parallel-cli research processors` for the installed CLI's processor list and latency estimates; these are not deadlines. Choose `ultra` tiers only when explicitly requested and within the user's approved budget. Check [current pricing](https://parallel.ai/pricing) rather than quoting fixed cost multipliers. -| Processor | Expected latency | Use when | -|-----------|-----------------|----------| -| `lite-fast` | 10–60s | Quick lookups, follow-ups | -| `base-fast` | 15–100s | Simple questions | -| `core-fast` | 1–5 min | Moderate research | -| `pro-fast` | 2–10 min | **Default** — exploratory research, good depth/speed balance | -| `ultra-fast` | 5–25 min | Multi-source deep research (~2× cost) | -| `ultra2x-fast` / `ultra4x-fast` / `ultra8x-fast` | up to 2 hr | Hardest questions, only when explicitly requested | +Fast variants prioritize speed and may use less fresh indexed data. Standard variants may suit freshness-sensitive work, but neither choice guarantees that every source was fetched live. State the relevant date or freshness requirement in the research prompt and check the returned evidence. -Notes on the `-fast` suffix: `-fast` tiers use cached web data and are quicker. The non-fast variants (`pro`, `ultra`, etc.) re-fetch fresher data — slower but better for very recent events. Default to `-fast` unless the user specifically asks about news from the last day or two. - -Run `parallel-cli research processors` to see the full list with latencies. - -Parse the JSON output to extract the `run_id`, `interaction_id`, and monitoring URL. Immediately tell the user: +Parse the JSON output to save `run_id`, `interaction_id`, and `result_url`. Immediately tell the user: - Deep research has been kicked off -- The expected latency for the processor tier chosen (from the table above) +- The estimated latency for the selected processor, if available - The monitoring URL where they can track progress -Tell them they can background the polling step to continue working while it runs. +The task runs server-side; polling can resume later using the saved `run_id`. ## Step 2: Poll for results ```bash -parallel-cli research poll "$RUN_ID" -o "$FILENAME" --timeout 540 +parallel-cli research poll "$RUN_ID" -o "$FILENAME" --timeout 60 ``` Important: -- Use `--timeout 540` (9 minutes) to stay within tool execution limits -- Do NOT pass `--json` — the full output is large and will flood context. The `-o` flag writes results to files instead. +- Keep each poll bounded; `--timeout 60` allows progress updates between waits. +- Avoid `--json` when polling a large report. The `-o` flag saves the full result to files. - With `-o "$FILENAME"`: - `$FILENAME.json` is always written (metadata + basis) - - `$FILENAME.md` is written **only if step 1 used `--text`** (markdown report) -- The poll command prints an **executive summary** to stdout when the research completes. Share this executive summary with the user — it gives them a quick overview without having to open the files. -- Pass `--force` if re-polling and you want to overwrite existing files + - `$FILENAME.md` is written only for returned text output, normally requested with `--text`; auto-schema results can remain JSON-only. + - For text, JSON references `output.content_file` relative to the saved JSON file instead of duplicating the report body. +- Share the executive summary if one was printed. Some successful outputs have no summary; do not invent one or treat its absence as failure. +- Existing output files are refused unless `--force` is explicit. Prefer a new base; use `--force` only when overwriting those files is intended. +- Read the actual printed paths. On a write error, the CLI may fall back to the system temp directory, and writes may be partial. Inspect both locations before retrying. Copy a final report from temporary storage to the intended persistent location before presenting it as saved durably. + +### If polling times out or is interrupted -### If the poll times out +Timeout exit 5 or interruption ends the local wait, not necessarily the server task. Check the saved task: -Higher processor tiers can take longer than 9 minutes. If the poll exits without completing: +```bash +parallel-cli research status "$RUN_ID" --json +``` -1. Tell the user the research is still running server-side -2. Re-run the same `parallel-cli research poll` command to continue waiting +Resume the same poll only for a pending/running task; retrieve completed output and report failed/cancelled or `action_required` states accurately. The CLI's polling loop may not recognize `action_required`, so do not poll that state indefinitely. Never recreate the task merely because a local wait ended. ## Response format -**After step 1:** Share the monitoring URL (for tracking progress only — it is not the final report). +**After step 1:** Share the monitoring URL for tracking progress. **After step 2:** -1. Share the **executive summary** that the poll command printed to stdout +1. Share the executive summary if printed; otherwise say the result is saved and provide a brief summary only from inspected output when needed. 2. Tell the user the generated file paths: - - `$FILENAME.md` — formatted markdown report (if `--text` was used) - - `$FILENAME.json` — metadata and basis + - Actual `.md` path, if a text report exists + - Actual `.json` path with metadata and basis (and structured content for JSON output) 3. Share the `interaction_id` and tell the user they can ask follow-up questions that build on this research (e.g., "drill deeper into X" or "compare that to Y") -Do NOT re-share the monitoring URL after completion — the results are in the files, not at that link. +After completion, link the saved files rather than repeating the monitoring URL. -Ask the user if they would like to read through the files for more detail. Do NOT read the file contents into context unless the user asks. +Avoid loading the whole report into context. Read only the relevant sections when answering a requested summary or follow-up, and cite the returned sources. -**Remember the `interaction_id`** — if the user asks a follow-up question that relates to this research, use it as `--previous-interaction-id` in the next research or enrichment command. +**Remember the `interaction_id`:** use it for a related research or enrichment follow-up when context chaining is supported by the account. ## Setup @@ -113,4 +107,6 @@ If `parallel-cli` is not found, install and authenticate: /parallel:parallel-cli-setup ``` -If any `parallel-cli research` command returns `403`, tell the user balance is likely required. Offer to run `parallel-cli balance get`, and if needed ask for explicit confirmation before running `parallel-cli balance add `. Then retry the original research command. +If a documented option or command is missing, identify the install method and upgrade through that method: standalone `parallel-cli update`; pipx `pipx upgrade parallel-web-tools`; uv `uv tool upgrade parallel-web-tools`; Homebrew `brew upgrade parallel-web/tap/parallel-cli`; npm `npm update -g parallel-web-cli`. Recheck version and help in the agent's terminal before retrying. + +For authentication or API errors, inspect the returned message. A `403` can indicate permissions, account policy or billing; it does not prove low balance. Check `parallel-cli auth --json` and its `authenticated` boolean when relevant without exposing credentials. Only a billing-specific error warrants a balance check, and adding funds needs explicit confirmation. Reuse saved task IDs; do not automatically retry an ambiguous creation. diff --git a/skills/parallel-findall/SKILL.md b/skills/parallel-findall/SKILL.md index 6a6dc3d..5638189 100644 --- a/skills/parallel-findall/SKILL.md +++ b/skills/parallel-findall/SKILL.md @@ -13,134 +13,101 @@ metadata: Find: $ARGUMENTS -> Requires `parallel-cli` ≥ 0.6.0 (the `findall entity-search` command was added in 0.6.0; the broader `findall` command was added in 0.3.0). If either errors with `no such command` or similar, tell the user to run `parallel-cli update` (or `pipx upgrade parallel-web-tools` if installed via pipx), then retry. +> Full FindAll requires `parallel-cli` ≥ 0.3.0; the optional `entity-search` path requires ≥ 0.6.0. If a documented command or option is missing, update through the installation method used for this CLI, then retry. See . ## When to use this skill -Use FindAll when the user wants a **structured list of entities** matching a description, not webpages or a narrative answer. +Use FindAll for a structured list of entities matching a description. Use parallel-web-search for webpages or quick answers, parallel-deep-research for narrative analysis, and parallel-data-enrichment to add fields to a list the user already has. -| User asks for… | Use | -|---|---| -| "Find all X that…" / "List every Y…" | **parallel-findall** (this skill) | -| Webpage results / quick answers / current info | parallel-web-search | -| Narrative report / analysis / "research X" | parallel-deep-research | -| Add fields to a list you already have | parallel-data-enrichment | +Default to the comprehensive, asynchronous `findall run`. It supports match conditions, exclusions, enrichment, evidence, and entity types beyond companies and people. “Find all” does not guarantee exhaustive internet coverage. -If the user already has a list and just wants to add fields, this is the wrong skill — use parallel-data-enrichment. +Use the synchronous `entity-search` path only when the user explicitly wants a quick or rough list of companies or people and accepts results without individual verification. Do not choose it just because the entity type is supported. It has no exclusions, generator selection, enrichment, or FindAll condition/enrichment citations. -FindAll has two paths: the comprehensive, asynchronous `findall run` (Steps 1–2) and the fast, synchronous `entity-search` (final section). +## Step 1: Start and retain the run -- **`entity-search`** — very fast (few seconds), only supports people or company search. Supports a more limited set of query arguments. Optimized for recall over precision; results are not individually verified. -- **`findall run`** — Provides comprehensive coverage, complex, match conditions, exclusions, enrichment, citations, or a type other than people/companies. - -If it's ambiguous, ask the user which they'd prefer and offer a default. Remember entity search limits: companies/people only, no exclusions/generator/enrichment, and `entity_set_id` can't be used with `enrich`/`extend` (re-run via `findall run` if needed). - -Switch to `entity-search` **only when the user explicitly signals they want a fast, throwaway list**. `entity-search` is also strictly more limited: it only supports `companies` or `people` entity types, no exclusions, no generator choice, no enrichment, and the returned `entity_set_id` is **not** usable with `findall enrich`/`extend`. If you start there and the user later asks to enrich or extend, you'll have to re-run via `findall run`. - -## Step 1: Start the run +Choose an unused, descriptive, run-specific `$FILENAME` for the saved JSON files. Pass the user's objective as one quoted argument, without shell evaluation. ```bash -parallel-cli findall run "$ARGUMENTS" --no-wait --json +parallel-cli findall run "$ARGUMENTS" --no-wait --json -o "/tmp/$FILENAME-create.json" ``` -Defaults: generator `core`, match limit `10`. Stick with `core` unless the user has a reason to escalate: - -- `-g pro` — most thorough generator (slower, costlier). Use when the user asks for "comprehensive" coverage or matches are sparse on `core` -- `-g base` — fastest, but **markedly lower quality**. Often returns query-echo entities (e.g., directory pages, the literal query string), entries with no URL, or category placeholders. Only use if the user explicitly asks for a quick scan and accepts noise; otherwise prefer `core` -- `-n 50` — return up to 50 matched entities (5–1000 allowed) +Defaults are generator `core` and match limit `10`. Use `-n 50` for up to 50 matched entities; the allowed limit is 5–1000. Stay with `core` unless the user requests a different tradeoff. `pro` searches a larger pool and is slower/costlier; `base` is a faster, lower-quality option for an explicitly requested rough scan. Spot-check specific claims such as batch, year, and geography against available evidence, especially for `base`. -If the user wants to exclude known entities (e.g., "find competitors but not Google or OpenAI"): +For requested exclusions: ```bash parallel-cli findall run "$ARGUMENTS" --no-wait --json \ - --exclude '[{"name":"Google","url":"google.com"},{"name":"OpenAI","url":"openai.com"}]' + --exclude '[{"name":"Google","url":"google.com"},{"name":"OpenAI","url":"openai.com"}]' \ + -o "/tmp/$FILENAME-create.json" ``` -Tip — preview the schema first if the objective is ambiguous: `parallel-cli findall ingest "$ARGUMENTS" --json` shows the entity type and match conditions the API inferred, so you can refine wording before paying for a run. +If the objective needs clarification, `parallel-cli findall ingest "$ARGUMENTS" --json` previews the inferred entity type, conditions, and suggested enrichments. This calls the API; it is not an offline or free test. Refine the objective before creating the run if the inferred conditions differ from the user's intent. -Parse the JSON output to extract the `findall_id` and any monitoring URL. Tell the user: +Capture the returned `findall_id` immediately, along with the objective, generator, match limit and exclusions. Report that the run started and give a monitoring URL only if one was actually returned. Do not infer a URL or a guaranteed completion time. If the creation response is lost, resolve the existing job before submitting again. -- A FindAll run has been started -- Approximate cadence (minutes for `core`, longer for `pro`) -- They can keep working while it runs +## Step 2: Add requested fields explicitly -## Step 2: Poll for results - -Choose a descriptive filename (e.g., `series-a-ai-2026`, `charlotte-roofers`). Use lowercase with hyphens, no spaces. +`--no-wait` ingests and creates the run but does **not** apply suggested enrichments. Requested output fields such as CEO name or employee count need a separate enrichment request; mentioning them in the objective is insufficient. ```bash -parallel-cli findall poll "$FINDALL_ID" -o "/tmp/$FILENAME.json" --timeout 540 +parallel-cli findall enrich "$FINDALL_ID" \ + '{"type":"object","properties":{"ceo":{"type":"string","description":"CEO name"},"employee_count":{"type":"number","description":"Number of employees"}}}' \ + -p core --json ``` -Important: - -- Use `--timeout 540` (9 minutes) to stay within tool execution limits -- Do NOT pass `--json` for large result sets — it will flood context. `-o` saves the full results to disk - -### If the poll times out +Use a JSON Schema object describing the user's fields, not the complete ingest envelope. Retain the exact submitted schema and processor locally with the run ID, including multiple requests if used. Do not rely on schema summaries to reconstruct them later. Enrichment adds non-boolean output data; it does not change match conditions. -Re-run the same `parallel-cli findall poll` command to continue waiting. Server-side the run continues regardless. +Enrichment can be added while the run is active or after completion. A terminal run can requeue to process the fields. Creation, enrichment acceptance, and populated results are separate outcomes. Do not claim the fields are ready from the enrichment response or a completed poll alone. -## Response format +## Step 3: Check status and retrieve results -Before presenting matches, **filter the results** for obvious noise: - -- Drop entries with empty/missing `url` -- Drop entries whose `name` echoes the user's query (e.g., literal "YC W25 batch companies in developer tools") — those are search-result placeholders, not real entities -- Drop entries whose `url` is a third-party directory or profile page rather than the entity's own domain. The URL should be something the entity itself owns (its product site, docs, or marketing site) +```bash +parallel-cli findall status "$FINDALL_ID" --json +parallel-cli findall poll "$FINDALL_ID" -o "/tmp/$FILENAME.json" --timeout 60 +parallel-cli findall result "$FINDALL_ID" -o "/tmp/$FILENAME-snapshot.json" +``` -If filtering removes a meaningful share of matches, mention this to the user and suggest re-running with `-g pro` or a higher `-n`. +Use bounded waits. A timeout (exit 5) or interrupt is local wait exhaustion, not cancellation. Check status and resume the same ID while it is active, within the user's waiting window; do not submit another run. The shared poller does not recognize the compatibility status `action_required`. If that status, `failed`, `cancelled`, or an inactive unfinished state appears, stop automatic waiting and report the state and saved ID as needing attention. -**Sanity-check `-g base` results.** The base generator can hallucinate categorical attributes (e.g., return a YC S22 company as a YC W25 match). The filter rules above only catch URL/name shape, not factual correctness. If the user's query has a falsifiable attribute (a specific batch, year, geography, etc.), spot-check the kept entries against the source URL and flag any that don't fit. Recommend re-running with `-g core` (or higher) if **either** multiple kept entries fail the spot-check **or** noise filtering dropped a meaningful share of the matched set (say, ≥40%) — both indicate `base` isn't producing reliable results for this query. +`result` returns a snapshot and does not prove completion. Read `status` and `is_active` together. After enrichment, inspect each matched candidate's `output` for every requested field. If fields are missing, take further result snapshots within a bounded waiting window, even if the first poll said completed. Report missing, null, or failed values rather than inventing them; if the window expires, return partial results and the ID for resumption. An empty matched set is not proof of successful enrichment. -Present the remaining (real) entities as a markdown table or list. Lead with the count, then list each entity with its name, URL, and a one-line description if available. Cite each entity with its source URL. +Avoid `--json` for large result sets; `-o` retains the complete JSON. These commands can overwrite their selected files, so use paths belonging to this run. Preserve the raw candidate list and status. `/tmp` is temporary; copy requested deliverables to a persistent user location when needed. -Tell the user: +## Present matches and evidence -- How many entities were matched (and how many were filtered as noise, if any) -- The full results path (`/tmp/$FILENAME.json`) -- That they can: - - Add fields to these results, e.g.: +Present only candidates with `match_status: "matched"` as matches. Preserve generated, unmatched, and discarded candidates in the raw file. Review obvious query-echo placeholders and unsupported entries rather than treating every candidate as an entity. - ```bash - parallel-cli findall enrich $FINDALL_ID '{"properties":{"ceo":{"type":"string"},"employee_count":{"type":"number"}}}' - ``` +Review URLs in the context of the entity. LinkedIn profiles can legitimately identify people, and YC or Crunchbase profiles can identify companies. Do not discard these solely because the entity does not own the domain. Flag missing or unverifiable URLs and use available evidence to resolve uncertainty. - The schema is a JSON Schema-style object with `properties` mapping field names → `{type, description?}`. - - Get more matches: `parallel-cli findall extend $FINDALL_ID 50` +Use condition and enrichment basis for factual claims, with its source URLs. The entity's primary URL and a supporting citation may differ. Do not label a primary/profile URL as evidence for an attribute unless it supports the claim. -## Fast entity search +Lead with the number of matched entities presented, note exclusions or unresolved entries, and use a table or list with names, URLs, and requested fields. Include the saved raw-results path, run ID, current state, and any incomplete fields. Sparse or noisy results can warrant suggesting a revised objective or generator; do not automatically create a replacement paid run. -**Use this path only when the user explicitly signals they want a quick/rough/preview list** — do not pick it just because the entity type happens to be `companies` or `people`. +## Get more matches -Synchronous call. No polling, no `findall_id`. Pick a descriptive `$FILENAME` (lowercase, hyphens, no spaces), as in Step 2. +Extend only when the user requests additional matches: ```bash -parallel-cli findall entity-search "$ARGUMENTS" -t companies -n 100 -o "/tmp/$FILENAME.json" +parallel-cli findall schema "$FINDALL_ID" --json +parallel-cli findall extend "$FINDALL_ID" 50 --json ``` -Flags: +`50` is an increment, not the new total. Check the known creation limit or current schema, including prior extensions, so the resulting total stays at or below 1000. Preview runs cannot be extended. A completed run is eligible only if its termination reason was `match_limit_met`; status/result in the CLI omit that reason and cannot prove eligibility. For an explicitly requested extension within the limit, let the API validate eligibility and surface any rejection without creating a new run automatically. -- `-t companies|people` — entity type (required). The endpoint only supports these two; for anything else, use `findall run` -- `-n 5..1000` — match limit (default `10`). When possible, request more than the user needs (e.g. `-n 100`) and select after filtering — results are ranked but not individually verified, and a low limit can omit relevant entities -- Do NOT pass `--json` for large result sets — it will flood context. `-o` saves the full results to disk +Retain the updated limit and poll the same ID for new results. Recheck requested enrichment fields; if the existing enrichment must be reapplied, use the original retained request payload and processor within the user's authorized scope. -Avoid highly restrictive objectives on this path: the API fills toward the limit, so relevance declines toward the tail. Keep the core criterion in the objective and filter the rest downstream, or use `findall run`. +## Fast entity search -Response shape: +Use only for explicit speed/rough-list intent and entity type `companies` or `people`. It is synchronous and returns `entity_set_id` plus ranked `entities`, not `findall_id` or verified candidates. -```json -{ "entity_set_id": "entity_set_…", "entities": [ {"name": "...", "url": "...", "description": "..."}, -… ] } +```bash +parallel-cli findall entity-search "$ARGUMENTS" -t companies -n 10 -o "/tmp/$FILENAME.json" ``` -Unlike the full path, the `url` returned by `entity-search` is usually a directory/profile link — expected, not noise. Don't drop them; only filter out entries with an empty `url` or a `name` that echoes the query. - -Present the kept entities as a markdown table or list, lead with the count, and cite each with its source URL. Tell the user: +The `-n` limit is 5–1000, default 10. Choose a limit proportional to the user's request. Avoid highly restrictive criteria on this path: relevance can decline toward the tail. Use full FindAll when individual condition checks or enrichment are required. -- How many entities came back (and how many were filtered as noise) -- The full results path (`/tmp/$FILENAME.json`) if `-o` was used +Keep legitimate directory/profile links and review empty URLs or query-echo names. Present these as unverified leads, cite their links as links to the entities, and avoid attributing absent FindAll basis or verification to them. Report the saved path and returned count. Never pass an `entity_set_id` to FindAll poll/status/result/enrich/extend. If the user later requests those capabilities, explain that a separate full run is needed and retain the original quick results. ## Setup -Requires `parallel-cli` (installed and authenticated). If `parallel-cli --version` fails, or if a later command fails with an authentication error, tell the user to see and stop. +Requires an installed and authenticated `parallel-cli`. Check `parallel-cli --version` and `parallel-cli auth --json`; auth can exit successfully while `authenticated` is false. Missing binary, unsupported command/option, and authentication failure need different remedies: installation, upgrade through the existing installation method, or terminal login respectively. See . Stop the affected request on auth failure, do not ask for secrets in chat, and do not change account policy to work around blocked setup. diff --git a/skills/parallel-monitor/SKILL.md b/skills/parallel-monitor/SKILL.md index 787f050..2d0ac65 100644 --- a/skills/parallel-monitor/SKILL.md +++ b/skills/parallel-monitor/SKILL.md @@ -17,7 +17,9 @@ Action: $ARGUMENTS ## What this skill does -Monitors are long-running, server-side jobs that re-check the web on a cadence and emit events when something changes. Unlike search/research/findall (one-shot lookups), monitors persist until cancelled and can optionally deliver detected events through a webhook. +Monitors are long-running, server-side jobs that re-check the web on a cadence and emit events when something changes. Unlike search/research/findall (one-shot lookups), monitors persist until cancelled and can optionally deliver detected events through a webhook. Creation does not establish an ongoing agent notification service: explain how to read server-side events or use the configured webhook, without promising chat or email alerts. + +The default type is `event_stream`, with frequency `1d` and server processor `lite`. A `snapshot` monitor requires an existing Task run ID. Create or modify only the resource requested by the user; do not create a monitor just to demonstrate the skill. ## Decide the action @@ -32,7 +34,7 @@ Parse the user's request and pick one: | "Change cadence / webhook for X" | **update** | | "Check monitor X now" / "Run it now" | **trigger** | | "Show me the full payload for event group X" | **events** with `--event-group-id` | -| "Stop / delete monitor X" | **cancel** (always confirm before cancelling) | +| "Stop / delete monitor X" | **cancel** (permanent; verify authority and exact ID) | ## Create a monitor @@ -40,7 +42,7 @@ Parse the user's request and pick one: parallel-cli monitor create "" --frequency 1d --json ``` -Frequency accepts `` with `h`, `d`, or `w` (for example `1h`, `1d`, or `1w`). The aliases `hourly`, `daily`, `weekly`, and `every_two_weeks` are also accepted. Match cadence to how often the source actually changes — hourly for prices/news, weekly for filings/staffing. +Frequency accepts `` with `h`, `d`, or `w` (for example `1h`, `1d`, or `1w`), within the supported range of 1 hour to 30 days. The aliases `hourly`, `daily`, `weekly`, and `every_two_weeks` are also accepted. Match cadence to the user's request and how often the source actually changes. Optional flags: @@ -48,12 +50,14 @@ Optional flags: - `--metadata '{"team":"competitive-intel"}'` — attach JSON metadata for your own bookkeeping - `--output-schema ''` — structure the event payload (advanced) -Parse the JSON to extract the `monitor_id`. Tell the user: +Capture the returned `monitor_id` immediately with the query, frequency and requested settings. Verify creation with `get` using that ID. Tell the user: - The monitor has been created with its ID - The frequency (so they know how often the monitor checks) - That recent events are available server-side — they can run `parallel-cli monitor events $MONITOR_ID` later to see what changed +If creation or another mutation times out or the response is lost, resolve the existing monitor/action before retrying. Resume with the saved ID, `get` and `events`; do not automatically recreate it. Recreating can duplicate persistent monitoring and billing. + ## List monitors ```bash @@ -72,29 +76,40 @@ parallel-cli monitor events "$MONITOR_ID" --json Events are returned newest-first. If the response contains `next_cursor`, pass it with `--cursor` to retrieve another page. +An empty event list does not prove that a check completed without changes. To inspect completion history as well as detected events: + +```bash +parallel-cli monitor events "$MONITOR_ID" --include-completions --limit 10 --json +``` + +Distinguish typed detected events, no-change `completion` events and `error` events. Use their actual timestamps and report failures. An empty completion history is not execution proof. Retain `event_id` and `event_group_id` when present; these identify events and executions, not monitor IDs. + For deeper detail on a specific event group: ```bash parallel-cli monitor events "$MONITOR_ID" --event-group-id "$EVENT_GROUP_ID" --json ``` -Summarize for the user: count of events, then a bulleted list of what changed with dates or timestamps. Cite source URLs from the event payload. +Event-group detail ignores pagination arguments. Summarize detected changes separately from completions/errors, with dates or timestamps. Read typed `output` or `changed_output` and available `basis`; cite its source URLs for factual claims. Do not invent provenance when the payload lacks basis. Surface response warnings and continue pagination only as needed for the requested period. ## Get / update / trigger / cancel ```bash parallel-cli monitor get "$MONITOR_ID" --json parallel-cli monitor update "$MONITOR_ID" --frequency 1w --json +parallel-cli monitor update "$MONITOR_ID" --webhook https://example.com/hook --json parallel-cli monitor trigger "$MONITOR_ID" --json parallel-cli monitor cancel "$MONITOR_ID" --json ``` -The current CLI does not expose query updates; create a new monitor to change the query. +Only supply fields the user requested to update. A webhook-only update leaves frequency unchanged; update has no default frequency. Metadata and advanced event-stream settings can also be updated through the CLI, but query and Task run identity are immutable. A different query needs a new monitor with separate authority and a deliberate decision about the old monitor; do not silently recreate or cancel it. + +`trigger` enqueues a real billed off-schedule execution without changing the regular schedule. It is not a synthetic webhook test and must not substitute for a request to test notification delivery. A successful trigger response confirms enqueueing, not completion. It emits a detected event only if material change is found; inspect completion history for no-change execution. Cancelled monitors cannot be triggered. -`trigger` enqueues a real off-schedule run without changing the regular schedule. It is not a synthetic webhook test, and it emits an event only if the run detects a material change. +Cancellation is irreversible, not deletion or a temporary pause. Explain this and obtain confirmation for the exact monitor ID unless the user has already authorized that permanent cancellation or cleanup of the specific disposable monitor. After cancelling, verify its state with `get`. Never recreate it automatically to resume monitoring. -**Always confirm before cancelling** — cancellation is permanent. +On authentication or API errors, report the actual error and retain IDs for recovery. Do not classify every error as an outdated CLI or every permission error as insufficient credit. Failed reads are not evidence that a monitor stopped; reading events does not cancel it. ## Setup -Requires `parallel-cli` (installed and authenticated). If `parallel-cli --version` fails, or if a later command fails with an authentication error, tell the user to see and stop. +Requires an installed and authenticated `parallel-cli`. Check `parallel-cli --version` and `parallel-cli auth --json`; auth can exit successfully while `authenticated` is false. Missing binary, unsupported command/option, and authentication failure need different remedies: installation, upgrade through the existing installation method, or terminal login respectively. See . Stop the affected request on auth failure, do not ask for secrets in chat, and do not change account policy to work around blocked setup. diff --git a/skills/parallel-web-extract/SKILL.md b/skills/parallel-web-extract/SKILL.md index 035a517..7333fa4 100644 --- a/skills/parallel-web-extract/SKILL.md +++ b/skills/parallel-web-extract/SKILL.md @@ -19,17 +19,13 @@ Extract content from: $ARGUMENTS Choose a short, descriptive filename based on the URL or content (e.g., `vespa-docs`, `react-hooks-api`). Use lowercase with hyphens, no spaces. Substitute it into the command **inline** — `$FILENAME` is a placeholder, not a shell variable. -```bash -parallel-cli extract "$ARGUMENTS" --json -o "/tmp/$FILENAME.json" -``` - -Concrete example: +Pass each requested URL as a separate quoted positional argument, up to 20 per call. Do not collapse multiple URLs into one quoted `$ARGUMENTS` string or use `eval` to split them. Construct arguments directly from the requested URLs. For example: ```bash -parallel-cli extract "https://docs.parallel.ai" --json -o "/tmp/parallel-docs.json" +parallel-cli extract "https://docs.parallel.ai/integrations/cli" "https://docs.parallel.ai/integrations/cursor-marketplace" --json -o "/tmp/parallel-docs.json" ``` -Note: `-o` always saves JSON. The extension must be `.json`. +`-o` saves JSON. Use a `.json` extension and inspect an existing path before use because Extract overwrites it. Read the saved file as authoritative; stdout may truncate and human-readable output previews only part of the content. Do not treat a stale file as a successful response after a failed call. Options if needed: @@ -38,13 +34,14 @@ Options if needed: - `--full-content` to include the complete page body (for long articles, PDFs, or when excerpts may not capture what you need) - `--full-content-max-chars N` to cap full-content size per result - `--no-excerpts` to strip excerpts when you only want full content +- `--session-id ""` to group related Search/Extract calls. A session ID is not a Task interaction ID or run ID; never use it with research status/poll or `--previous-interaction-id` ## Handling failed extractions -If the response has an `errors` field, an empty `results` array, or a 404/timeout for the URL, do NOT fabricate content. Tell the user the extraction failed, surface the upstream status, and suggest: +Inspect the exit status, API error, `results`, per-URL `errors` and any warnings. `errors: []` is normal success. Nonempty errors can coexist with successful results: retain and present successful content, then name each failed URL and its returned reason. Empty results or missing content are not a successful extraction. Do not fabricate content. For affected URLs, suggest: - Verifying the URL (the page may have moved) -- Retrying with `--full-content` if excerpts came back empty but the page exists +- Requesting `--full-content` if excerpts are empty but the returned metadata supports that the page was fetched - Using `parallel-cli search` to locate the current URL if the page was renamed ## Response format @@ -53,15 +50,17 @@ Return content as: **[Page Title](URL)** -Then the extracted content verbatim, with these rules: +Use returned `full_content` for full-page requests; excerpts alone are selected passages and must be labelled as such. Even full content may be capped by `--full-content-max-chars` or upstream limits; do not promise completeness when capped. Preserve retrieved content verbatim, with these rules: - Keep content verbatim - do not paraphrase or summarize -- Parse lists exhaustively - extract EVERY numbered/bulleted item +- Preserve every numbered/bulleted item in the retrieved content; do not claim an excerpt contains the whole page - Strip only obvious noise: nav menus, footers, ads - Preserve all facts, names, numbers, dates, quotes After the response, mention the output file path (`/tmp/$FILENAME.json`) so the user knows it's available for follow-up questions. +For large content, keep the full verbatim text in the saved file and provide a brief labelled preview plus its path. Never silently truncate content while claiming it is the complete extraction. + ## Setup If `parallel-cli` is not found, install and authenticate: @@ -70,4 +69,6 @@ If `parallel-cli` is not found, install and authenticate: /parallel:parallel-cli-setup ``` -If `parallel-cli extract` returns `403`, tell the user balance is likely required. Offer to run `parallel-cli balance get`, and if needed ask for explicit confirmation before running `parallel-cli balance add `. Then retry the original extract command. +If a documented command or option is missing, check the installed version and upgrade through its installation method: standalone `parallel-cli update`, pipx `pipx upgrade parallel-web-tools`, uv `uv tool upgrade parallel-web-tools`, Homebrew `brew upgrade parallel-web/tap/parallel-cli`, or npm `npm update -g parallel-web-cli`. Verify help in the same terminal before retrying. + +For authentication errors, inspect `parallel-cli auth --json` and its `authenticated` boolean; exit zero alone does not prove authentication. A `403` can indicate permissions, policy or billing. Report the actual error; check balance only for a billing-specific failure and never add funds without explicit confirmation. diff --git a/skills/parallel-web-search/SKILL.md b/skills/parallel-web-search/SKILL.md index c35cf12..86480c4 100644 --- a/skills/parallel-web-search/SKILL.md +++ b/skills/parallel-web-search/SKILL.md @@ -36,20 +36,21 @@ Options if needed: - `--after-date YYYY-MM-DD` for time-sensitive queries - `--include-domains domain1.com,domain2.com` to limit to specific sources - `--exclude-domains domain.com` to filter out noisy sources -- `--mode turbo` for simple fact lookups where speed and cost matter most (p50 ~200ms, lowest cost). English and Japanese queries only -- `--mode fast` for high-quality search within a ~1s latency budget -- `--mode advanced` for harder questions (multi-step, agentic search). Default `basic` is right for almost everything; escalate to `advanced` only when basic results are insufficient, and drop to `turbo` for high-volume simple lookups +- `--mode turbo` for simple fact lookups where speed matters most; supports English and Japanese queries +- `--mode fast` for high-quality search within an approximately one-second latency budget; requires CLI ≥ 0.9.2, and latency is not guaranteed +- `--mode advanced` for harder questions (multi-step, agentic search). Keep the default `basic` unless the request needs another mode - `--location us` (ISO 3166-1 alpha-2) for geo-targeted results +- `--session-id ""` to group related Search/Extract calls when a prior response returned one. A `session_id` or `search_id` is not a Task interaction ID or research run ID; never send it to research status/poll or `--previous-interaction-id` ## Parsing results -Do not set `max_output_tokens` on the command execution — the output is already bounded by `--max-results` and `--excerpt-max-chars-total`. Capping output tokens will truncate the JSON and break parsing. +**Read the saved `-o` JSON file as the authoritative payload.** Result and excerpt limits bound requested content, but stdout can still exceed the tool's output limit. Truncated stdout is not parseable JSON and is not proof of incomplete saved results. Inspect an existing output path before using it because Search overwrites that file. For each result, extract: -**Prefer reading from the saved `-o` file**, not stdout. Even bounded output regularly exceeds harness stdout limits and gets truncated. Read `/tmp/$FILENAME.json` for the authoritative payload. For each result, extract: - -- title, url, publish_date +- title, url, and publish_date if provided; omit unknown dates - Useful content from excerpts (skip navigation noise like menus, footers, "Skip to content") +Check the exit status, returned API error and `warnings` before presenting results. On an error or empty `results`, report what happened and do not fabricate an answer. An old output file is not evidence that a failed request succeeded. For sparse results, state the coverage limits; refine the objective or queries only when useful for the user's request. + ## Response format **CRITICAL: Every claim must have an inline citation.** Use markdown links like [Title](URL) pulling only from the JSON output. Never invent or guess URLs. @@ -71,6 +72,8 @@ Sources: This Sources section is mandatory. Do not omit it. +Only include source dates that were returned or verified in the retrieved content. Leave the date out when unknown. + After the Sources section, mention the output file path (`/tmp/$FILENAME.json`) so the user knows it's available for follow-up questions. ## Setup @@ -81,4 +84,6 @@ If `parallel-cli` is not found, install and authenticate: /parallel:parallel-cli-setup ``` -If `parallel-cli search` returns `403`, tell the user balance is likely required. Offer to run `parallel-cli balance get`, and if needed ask for explicit confirmation before running `parallel-cli balance add `. Then retry the original search command. +If a documented command or option is missing, check the installed version and upgrade through its installation method: standalone `parallel-cli update`, pipx `pipx upgrade parallel-web-tools`, uv `uv tool upgrade parallel-web-tools`, Homebrew `brew upgrade parallel-web/tap/parallel-cli`, or npm `npm update -g parallel-web-cli`. Verify help in the same terminal before retrying. + +For authentication errors, inspect `parallel-cli auth --json` and its `authenticated` boolean; exit zero alone does not prove authentication. A `403` can indicate permissions, policy or billing. Report the actual error; check balance only for a billing-specific failure and never add funds without explicit confirmation. diff --git a/tests/test_cursor_sync.py b/tests/test_cursor_sync.py new file mode 100644 index 0000000..e8adf1d --- /dev/null +++ b/tests/test_cursor_sync.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import contextlib +import importlib.util +import io +import json +import tempfile +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[1] +SPEC = importlib.util.spec_from_file_location("cursor_sync", REPO_ROOT / "scripts/sync-to-cursor.py") +assert SPEC is not None and SPEC.loader is not None +SYNC = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(SYNC) + + +class CursorSyncTestCase(unittest.TestCase): + def test_cursor_routing_does_not_assume_a_bundled_mcp(self): + for name in ("parallel-web-search", "parallel-web-extract"): + source = (REPO_ROOT / "skills" / name / "SKILL.md").read_text() + generated = SYNC.transform_skill(source) + description = next(line for line in generated.splitlines() if line.startswith("description:")) + with self.subTest(skill=name): + self.assertNotIn("MCP", description) + self.assertIn("CLI", description) + if name == "parallel-web-search": + self.assertIn("default", description.lower()) + self.assertIn("explicitly", description) + self.assertIn("bundled Parallel Search MCP", source) + + def test_setup_replacement_retains_distinct_failure_paths(self): + source = "---\nname: example\ndescription: Example\n---\n\n# Example\n\nKeep workflow.\n\n## Setup\n\nSource-client install instructions.\n" + generated = SYNC.transform_skill(source) + self.assertIn("Keep workflow.", generated) + self.assertNotIn("Source-client install instructions", generated) + self.assertIn("command not found", generated) + self.assertIn("/parallel-setup", generated) + self.assertIn("No such command", generated) + self.assertIn("installation method", generated) + self.assertIn("authentication", generated.lower()) + self.assertIn("authenticated", generated) + self.assertIn("403", generated) + + def test_platform_fields_are_stripped_without_dropping_other_metadata(self): + source = "---\nname: example\ndescription: Example\ncontext:\n mode: fork\nagent: parallel:parallel-subagent\nargument-hint: \nuser-invocable: true\ncompatibility: Requires CLI\nallowed-tools: Bash(parallel-cli:*)\nmetadata:\n author: parallel\n---\n\n# Example\n" + generated = SYNC.transform_skill(source) + self.assertNotIn("mode: fork", generated) + for key in SYNC.CC_ONLY_FIELDS: + self.assertNotIn(f"\n{key}:", generated) + self.assertIn("compatibility: Requires CLI", generated) + self.assertIn("allowed-tools: Bash(parallel-cli:*)", generated) + self.assertIn("metadata:\n author: parallel", generated) + + def test_sync_is_repeatable_and_preserves_existing_wrappers_and_manifest(self): + with tempfile.TemporaryDirectory() as directory: + target = Path(directory) + (target / ".cursor-plugin").mkdir() + (target / "commands").mkdir() + wrapper = target / "commands/parallel-search.md" + wrapper.write_text("Keep the existing reviewed command.\n") + manifest = {"name": "parallel", "version": "0.2.0", "skills": "./skills/", "rules": "./rules/", "commands": ["commands/parallel-setup.md", "commands/parallel-search.md"]} + manifest_path = target / ".cursor-plugin/plugin.json" + manifest_path.write_text(json.dumps(manifest)) + with contextlib.redirect_stdout(io.StringIO()): + self.assertEqual(0, SYNC.sync(target, dry_run=True)) + self.assertFalse((target / "skills").exists()) + with contextlib.redirect_stdout(io.StringIO()): + self.assertEqual(0, SYNC.sync(target, dry_run=False)) + files_before = {p.relative_to(target): p.read_bytes() for p in target.rglob("*") if p.is_file()} + output = io.StringIO() + with contextlib.redirect_stdout(output): + self.assertEqual(0, SYNC.sync(target, dry_run=False)) + self.assertIn("No changes", output.getvalue()) + self.assertEqual(files_before, {p.relative_to(target): p.read_bytes() for p in target.rglob("*") if p.is_file()}) + self.assertEqual("Keep the existing reviewed command.\n", wrapper.read_text()) + synced = json.loads(manifest_path.read_text()) + self.assertEqual("0.2.0", synced["version"]) + self.assertEqual(set(SYNC.SKILLS_TO_SYNC), {p.name for p in (target / "skills").iterdir()}) + self.assertNotIn("mcpServers", synced) + self.assertEqual(set(manifest["commands"] + [f"commands/{cfg['command']}.md" for cfg in SYNC.COMMAND_WRAPPERS.values()]), set(synced["commands"])) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_parallel_monitor_cli_contract.py b/tests/test_parallel_monitor_cli_contract.py index 2afd837..d07ad96 100644 --- a/tests/test_parallel_monitor_cli_contract.py +++ b/tests/test_parallel_monitor_cli_contract.py @@ -1,5 +1,6 @@ from __future__ import annotations +import json import os import re import shlex @@ -7,6 +8,7 @@ import subprocess import unittest from pathlib import Path +from unittest.mock import patch REPO_ROOT = Path(__file__).resolve().parents[1] @@ -18,7 +20,7 @@ MONITOR_CONTRACT: dict[str, tuple[str, ...]] = { "create": ("--frequency", "--webhook", "--metadata", "--output-schema", "--json"), "list": ("-n", "--status", "--json"), - "events": ("--cursor", "--event-group-id", "--json"), + "events": ("--cursor", "--event-group-id", "--include-completions", "--limit", "--json"), "get": ("--json",), "update": ("--frequency", "--webhook", "--json"), "trigger": ("--json",), @@ -203,6 +205,34 @@ def test_obsolete_monitor_surface_is_not_documented(self): with self.subTest(flag=flag): self.assertNotIn(flag, self.skill_text) + def test_documented_no_change_history_invocation_requests_completions(self): + from click.testing import CliRunner + from parallel_web_tools.cli import commands + + examples = [ + invocation + for command, flags, invocation in documented_monitor_invocations(self.skill_text) + if command == "events" and "--include-completions" in flags + ] + self.assertTrue(examples, "the no-change history example must request completion events") + completion = {"event_type": "completion", "timestamp": "2026-09-24T12:00:00Z"} + for example in examples: + tokens = shlex.split(example.replace("$MONITOR_ID", "mon_fixture")) + with self.subTest(example=example), patch.object( + commands, "list_monitor_events", return_value={"events": [completion]} + ) as events: + result = CliRunner().invoke(commands.main, tokens[1:]) + self.assertEqual(0, result.exit_code, result.output) + self.assertEqual("mon_fixture", events.call_args.args[0]) + self.assertTrue(events.call_args.kwargs["include_completions"]) + self.assertEqual(10, events.call_args.kwargs["limit"]) + self.assertEqual([completion], json.loads(result.output)["events"]) + + with patch.object(commands, "list_monitor_events", return_value={"events": []}) as events: + result = CliRunner().invoke(commands.main, ["monitor", "events", "mon_fixture", "--json"]) + self.assertEqual(0, result.exit_code, result.output) + self.assertFalse(events.call_args.kwargs["include_completions"]) + if __name__ == "__main__": unittest.main()