Skip to content

feat(create): machine-readable --json output and hidden --origin flag - #1280

Draft
l2ysho wants to merge 7 commits into
1236-guided-apify-create-wizard-use-case-language-best-match-templatefrom
1238-apify-create-machine-readable-json-origin
Draft

feat(create): machine-readable --json output and hidden --origin flag#1280
l2ysho wants to merge 7 commits into
1236-guided-apify-create-wizard-use-case-language-best-match-templatefrom
1238-apify-create-machine-readable-json-origin

Conversation

@l2ysho

@l2ysho l2ysho commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Closes #1238. Stacked on #1278 (base = the 1236 wizard branch); rebase onto master after that merges.

Makes apify create machine-readable for agents and the Console "Clone locally" handoff.

What changed

  • --json on apify create prints a contract on stdout:

    { "dir", "actorJsonPath", "template", "source": "apify", "nextSteps": [],
      "postCreate": null, "gitRepositoryInitialized": false }

    postCreate is non-null when the template needs extra setup before apify run works (e.g. python-playwright needs playwright install --with-deps) — 9 of 43 templates set it, and dropping it left agents with a broken Actor. gitRepositoryInitialized reports whether git init actually succeeded, since the human-only warning isn't machine-readable.

    All progress/info/warning output stays on stderr. On error: non-zero exit, message on stderr, no stdout.

  • Hidden --origin console|cli flag (default cli) → recorded in create telemetry for the funnel.

  • stdout hygiene — two things were writing to stdout ahead of the payload, so apify create --json | jq failed on any invocation that didn't pass both --skip-git-init and --skip-dependency-install (i.e. the bare call agents and Console actually make):

    • Child processes were spawned with stdout inherited, putting git init and the whole dependency-installer transcript on our stdout. keepStdoutClean() now routes child stdout to stderr, latched by the command framework whenever --json is set — so it covers every command with enableJsonFlag, not just this call site.
    • @inquirer/core pipes to process.stdout unless handed a context, so any prompt wrote escape sequences and the answer line to stdout. All five prompt wrappers now render on stderr (separate commit — prompts are UI, not command output, and this is correct independent of --json).
  • Small: buildNextSteps() extracted (shared by --json and the success message); templates ls shows JavaScript instead of raw javascript; useSelectFromList no longer tells users to pass --confirm/--no-confirm, flags that don't exist on the commands that use it.

Not in scope (deferred, per the epic)

  • --source/--git-repo and the git-connect flow → separate sub-issue. So the contract omits remote/gitConnectUrl and hardcodes source: "apify" (the only behavior today).
  • --json without --template in a non-TTY still prompts, then fails. useStdin sets hasData = true for any non-TTY stdin, so the intended "pass the flags" guard effectively only fires under isCI. With the prompts moved off stdout this is now a loud failure rather than a corrupted payload, but making --json strictly non-interactive is still a follow-up. Agents/Console always pass --template.
  • template is null on the hidden --template-archive-url path (internal, for templates not yet in the manifest). null is the honest value there; the documented consumers always pass --template.

Tests

14 create tests + a new exec test covering the stdout redirect (mutation-checked: it fails if keepStdoutClean() is neutered). The --json tests previously searched for the JSON among other stdout lines, which tolerated the pollution; they now require stdout to be exactly the payload. --origin is asserted through telemetry rather than a duplicated payload check, and the cli default is covered too.

Verified end to end against the built CLI: bare create --template project_cheerio_crawler_js --json yields pure parseable JSON on stdout with the full npm transcript and git init on stderr.

pnpm run test:local → 405 passed. The 2 [python] fixture failures are pre-existing and environmental (they need Python ≥ 3.11; confirmed identical on the untouched tree). build / lint / format clean; docs regenerated.

🤖 Generated with Claude Code

l2ysho and others added 2 commits July 16, 2026 14:09
- Enable --json on `apify create`: prints { dir, actorJsonPath, template,
  source, nextSteps }. Progress/logs stay on stderr.
- Add hidden --origin console|cli flag for funnel telemetry (default cli).
- Extract buildNextSteps(), shared by the --json output and success message.
- templates ls: render the language label ("JavaScript") not the raw category.

Refs #1238

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…use-case-language-best-match-template' into pr1280-conflicts

# Conflicts:
#	docs/reference.md
@l2ysho l2ysho self-assigned this Jul 28, 2026
l2ysho and others added 5 commits July 30, 2026 15:15
…match-template' into 1238-apify-create-machine-readable-json-origin
Child process output was inherited onto the CLI's own stdout, so `git init`
and the dependency installer transcript landed ahead of the JSON payload.
`apify create --json | jq` failed on every invocation that did not pass both
--skip-git-init and --skip-dependency-install, including the bare --json call
that agents and the Console "Clone locally" handoff actually make.

Add keepStdoutClean() to route child stdout to stderr, latched by the command
framework whenever --json is set, so it covers every command with
enableJsonFlag rather than just this one call site.

Also include postCreate in the payload: 9 templates set it and two
(python-playwright, python-selenium) document hard prerequisites, so an agent
following nextSteps alone ended up with a broken Actor. Add
gitRepositoryInitialized so a failed git init is visible to machine callers.

The tests searched for the JSON among other stdout lines, which tolerated the
pollution; they now require stdout to be exactly the payload. --origin is
asserted through telemetry instead of a duplicated payload check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@inquirer/core pipes to process.stdout unless handed a context, so every
prompt wrote its UI escape sequences and the persisted answer line to stdout.
That corrupts the payload of any command invoked with --json — reachable from
`apify create` whenever the Actor name or template is omitted, and
unconditionally when the target directory already exists.

Prompts are UI, not command output, so route all five wrappers to stderr.

Also give useSelectFromList its own errorMessageForStdin: it inherited the
confirmation default and told users to pass --confirm/--no-confirm, flags that
do not exist on any of the commands that use it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…match-template' into 1238-apify-create-machine-readable-json-origin
`--template` matched only the manifest `name`, but the new `--json` payload
reports the template `id` — and 19 of 43 templates have `id !== name`, so the
value the machine-readable output emits was rejected on round-trip. The
`--help` example `--template js-crawlee-cheerio` was broken for the same
reason. Resolve against either field.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants