-
Notifications
You must be signed in to change notification settings - Fork 49
docs: add agent skill (SKILL.md) for the Apify CLI #1233
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
4801f38
e5cd6d1
55f9f44
a7bfa5e
805ea33
16b3bdf
54af37e
004a6c2
0e67e30
4ee7c47
92eb99c
3166388
078a167
c78e550
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,10 +21,14 @@ DESCRIPTION | |
| Prints out help about a command, or all available commands. | ||
|
|
||
| USAGE | ||
| $ apify help [commandString] | ||
| $ apify help [commandString] [--skills] | ||
|
|
||
| ARGUMENTS | ||
| commandString The command to get help for. | ||
|
|
||
| FLAGS | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: the What about
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or, to be at least in sync with mcpc, it should be singular |
||
| --skills Print the Apify CLI agent skill (guidance for driving | ||
| `apify` from agents). | ||
| ``` | ||
|
|
||
| ##### `apify upgrade` | ||
|
|
@@ -782,7 +786,8 @@ FLAGS | |
| DESCRIPTION | ||
| Executes Actor remotely using your authenticated account. | ||
| Reads input from local key-value store by default. | ||
| Inspect the input schema first with "apify actors info <actor> --input". | ||
| To inspect the input schema before creating a JSON input, use "apify actors | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Super nit: is this really better? 🤔
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That was my suggestion, so I can answer: jumping from "executes" and "reads" to an imperative creates some dissonance. We still have human readers :D |
||
| info <actor> --input". | ||
|
|
||
| USAGE | ||
| $ apify actors call [actorId] [-b <value>] | ||
|
|
@@ -799,8 +804,8 @@ FLAGS | |
| -b, --build=<value> Tag or number of the build to | ||
| run (e.g. "latest" or "1.2.34"). | ||
| -i, --input=<value> Optional inline JSON object | ||
| input for the Actor. Wrap the JSON in quotes to avoid | ||
| shell parsing issues. For JSON files, use --input-file. | ||
| input for the Actor. To avoid shell parsing issues, wrap | ||
| the JSON in quotes. For JSON files, use --input-file. | ||
| -f, --input-file=<value> Optional path to a file with | ||
| JSON input to be given to the Actor. The file must be a | ||
| valid JSON file. You can also specify `-` to read from | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,7 +27,8 @@ | |
| "prepare": "husky" | ||
| }, | ||
| "files": [ | ||
| "dist" | ||
| "dist", | ||
| "skills" | ||
| ], | ||
| "bin": { | ||
| "actor": "./dist/actor.js", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| --- | ||
| name: apify-cli | ||
| description: Patterns for invoking the Apify CLI (`apify`) from agents. Covers authentication, creating/running/pushing Actors, calling Actors in the cloud, and reading results from datasets and key-value stores. | ||
| --- | ||
|
|
||
| ## Start here | ||
|
|
||
| Run `apify -h` first to see the available commands and global options, then `apify <command> -h` (e.g. `apify call -h`) for the args and flags of a specific command. This is the source of truth — prefer it over assumptions. | ||
|
|
||
| ## Non-interactive use | ||
|
|
||
| Many commands prompt when run interactively. To run without prompts, pass every required argument and flag explicitly: | ||
|
|
||
| - `apify create <name> --template <template>` — skip the create wizard. | ||
| - `apify init <name>` — skip the init prompt. | ||
| - `-y` / `--yes` on destructive commands (`apify actors rm`, etc.) — auto-confirm. | ||
| - `apify login --token <token>` — log in without the interactive token prompt. | ||
|
|
||
| If a command's help shows an "interactive note", it lists exactly which flags make it non-interactive. | ||
|
|
||
| ## Auth | ||
|
|
||
| - Preferred for automation: set `APIFY_TOKEN` in the environment (no `apify login` needed). Get a token at https://console.apify.com/settings/integrations. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: this might be potentially dangerous. If user, for any reason, has the Overall, we should, instead, refer to Additional instructions on authentication bellow are fine, but also not ideal as we are going to have 2 sources of truth. The auth.md should have a CLI section instead (working on one currently). |
||
| - Or persist a session once: `apify login --token <token>`. | ||
| - Verify auth: `apify info` (prints the logged-in user; non-zero exit / error if not authenticated). | ||
| - Print the stored token: `apify auth token`. | ||
|
|
||
| ## Structured output | ||
|
|
||
| - `--json` is supported on most list/info commands (`apify actors ls --json`, `apify actors info <id> --json`, `apify datasets info <id> --json`, `apify runs ls --json`, etc.). Use it and parse with `jq`; don't scrape the human table. | ||
| - List commands paginate — control with `--limit` / `--offset` (and `--desc`). | ||
| - Dataset items: `apify datasets get-items <datasetId> --format json`. Use `--limit` / `--offset`. | ||
|
|
||
| ## Core workflows | ||
|
|
||
| **Discover Actors in the Apify Store** | ||
|
|
||
| Before assuming an Actor name or scripting a raw Store query, search for an existing Actor: | ||
|
|
||
| ```sh | ||
| apify actors search "jobs scraper" --json # no auth required | ||
| apify actors search "ai" --pricing-model FREE --sort-by popularity --limit 5 | ||
| ``` | ||
|
|
||
| Run `apify actors search -h` to see the available filters (pricing model, category, username, sort order, pagination) and their accepted values. | ||
|
|
||
| Pricing matters: prefer a `FREE` Actor when one covers the task. Check an Actor's pricing before running it with `apify actors info <actor> --json` (look at `currentPricingInfo`). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Might lead to agents picking FREE over well supported, popular and well rated Actors. We might need more clarification that it is a combination of all above that matters. |
||
|
|
||
| **Develop and deploy a local Actor** | ||
|
|
||
| ```sh | ||
| apify create my-actor --template <template> # or run `apify create` and pick interactively | ||
| # template names: https://raw.githubusercontent.com/apify/actor-templates/master/templates/manifest.json | ||
| cd my-actor | ||
| apify run # run locally; --input / --input-file - for input | ||
| apify push # build & deploy to the platform | ||
| ``` | ||
|
|
||
| **Run an Actor in the cloud and get results** | ||
|
|
||
| ```sh | ||
| apify call apify/website-content-crawler -i '{"startUrls":[{"url":"https://example.com"}]}' --json | ||
| # or non-blocking: apify actors start <actor> --json (returns run details immediately) | ||
| # inspect input schema first: apify actors info <actor> --input | ||
| ``` | ||
|
|
||
| Non-obvious `apify call` flags: `-f -` reads input from stdin; `-o`/`--output-dataset` prints the result dataset. Run `apify call -h` for the full list. | ||
|
|
||
| **Wait for and inspect runs/builds** | ||
|
|
||
| ```sh | ||
| apify runs ls --json | ||
| apify runs info <runId> --json | ||
| apify runs wait <runId> # block until finished | ||
| apify runs log <runId> | ||
| apify builds wait <buildId> | ||
| ``` | ||
|
|
||
| **Storage** | ||
|
|
||
| ```sh | ||
| apify datasets get-items <datasetId> --format json | ||
| apify key-value-stores get-value <storeId> <key> | ||
| apify key-value-stores set-value <storeId> <key> <value> | ||
| apify key-value-stores keys <storeId> --json | ||
| ``` | ||
|
|
||
| ## Scheduling and recurring runs | ||
|
|
||
| For anything recurring or unattended (e.g. "run every 15 minutes"), use the Apify platform — **not** local `cron`, a `while` loop, or GitHub Actions. Apify Schedules run in the cloud, so they keep firing after your laptop, terminal, or agent session is shut down. | ||
|
|
||
| - Save a reusable input config as a **task**, then run it: `apify task run <taskId>`. | ||
| - There is no dedicated `apify schedules` command yet — manage schedules via `apify api` against the `schedules` endpoint, or in the Console (https://console.apify.com/schedules): | ||
|
|
||
| ```sh | ||
| apify api GET schedules | ||
| apify api POST schedules -d '{"name":"jobs-every-15m","cronExpression":"*/15 * * * *","isEnabled":true,"actions":[{"type":"RUN_ACTOR","actorId":"<actorId>","runInput":{"body":"<json>","contentType":"application/json"}}]}' | ||
| ``` | ||
|
|
||
| ## Escape hatch: `apify api` | ||
|
|
||
| Any platform capability without a dedicated command is reachable via the authenticated API wrapper (use this instead of hand-rolling `curl` against `api.apify.com` — it injects auth for you): | ||
|
|
||
| ```sh | ||
| apify api --list-endpoints # discover endpoints (filter with -s "<tokens>") | ||
| apify api --describe "actor-runs/{runId}" # methods, summary, path params for an endpoint | ||
| apify api GET /v2/users/me # GET is the default method | ||
| apify api POST acts -d '<json>' -p '{"limit":1}' # -d body (use - for stdin), -p query params | ||
| ``` | ||
|
|
||
| The `v2/` prefix and leading slash are optional. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,43 @@ | ||
| import { existsSync, readFileSync } from 'node:fs'; | ||
| import { dirname, join } from 'node:path'; | ||
| import { fileURLToPath } from 'node:url'; | ||
|
|
||
| import chalk from 'chalk'; | ||
|
|
||
| import { ApifyCommand, commandRegistry } from '../lib/command-framework/apify-command.js'; | ||
| import { Args } from '../lib/command-framework/args.js'; | ||
| import { Flags } from '../lib/command-framework/flags.js'; | ||
| import { renderHelpForCommand, renderMainHelpMenu } from '../lib/command-framework/help.js'; | ||
| import { useCommandSuggestions } from '../lib/hooks/useCommandSuggestions.js'; | ||
| import { error } from '../lib/outputs.js'; | ||
| import { error, simpleLog } from '../lib/outputs.js'; | ||
|
|
||
| declare const __APIFY_CLI_SKILL__: string | undefined; | ||
|
|
||
| const SKILL_RELATIVE_PATH = join('skills', 'apify', 'SKILL.md'); | ||
|
|
||
| function readApifySkill(): string | null { | ||
| if (typeof __APIFY_CLI_SKILL__ === 'string' && __APIFY_CLI_SKILL__) { | ||
| return __APIFY_CLI_SKILL__; | ||
| } | ||
|
|
||
| let dir = dirname(fileURLToPath(import.meta.url)); | ||
|
|
||
| while (true) { | ||
| const candidate = join(dir, SKILL_RELATIVE_PATH); | ||
|
|
||
| if (existsSync(candidate)) { | ||
| return readFileSync(candidate, 'utf-8'); | ||
| } | ||
|
|
||
| const parent = dirname(dir); | ||
|
|
||
| if (parent === dir) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: worth adding a comment here that that dirname of the root ( |
||
| return null; | ||
| } | ||
|
|
||
| dir = parent; | ||
| } | ||
| } | ||
|
|
||
| export class HelpCommand extends ApifyCommand<typeof HelpCommand> { | ||
| static override name = 'help' as const; | ||
|
|
@@ -21,9 +54,30 @@ export class HelpCommand extends ApifyCommand<typeof HelpCommand> { | |
| }), | ||
| }; | ||
|
|
||
| static override flags = { | ||
| skills: Flags.boolean({ | ||
| description: 'Print the Apify CLI agent skill (guidance for driving `apify` from agents).', | ||
| default: false, | ||
| }), | ||
| }; | ||
|
|
||
| async run() { | ||
| const { commandString } = this.args; | ||
|
|
||
| if (this.flags.skills) { | ||
| const skill = readApifySkill(); | ||
|
|
||
| if (!skill) { | ||
| error({ message: 'Could not find the Apify CLI skill file.' }); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| simpleLog({ stdout: true, message: skill.trimEnd() }); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| if (!commandString || commandString.toLowerCase().startsWith('help')) { | ||
| const helpMenu = renderMainHelpMenu(this.entrypoint); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: the skill should be named more specific, e.g.:
~/.claude/skills/apify-cliand~/.agents/skills/apify-clior evenapify-cli-agentic-guideorapify-cli-agentic-help.