feat: add claude_args passthrough + structured_output output (#1) #2
Reference in New Issue
Block a user
Delete Branch "feat/claude-args-structured-output"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #1.
Brings the upstream v1
claude_argspassthrough input andstructured_outputoutput into this Gitea fork, so downstream workflows can run the agent with arbitrary Claude CLI flags — notably--json-schema— and read the schema-validated verdict back as an action output instead of an agent-written file, while keeping the Gitea adaptations this fork exists for.Approach — issue Option B, ported (not bumped)
The production Run Claude Code step previously delegated to the external
anthropics/claude-code-base-action@v0.0.63, which has neither input. Upstream base-actionmainhas them, but has since migrated to the Claude Agent SDK — a wholesale architectural divergence that would change the v0.0.63 prompt-file/mcp-config/stream-json behavior every downstream step (review/docs/conflict/triage/@claude) relies on. So instead of bumping the pin to an untaggedmainSHA, this:base-action/directly viabun run ${{ github.action_path }}/base-action/src/index.ts— the samegithub.action_pathpatternprepare.ts/update-comment-link.tsalready use in production (reliable on Gitea; a relativeuses: ./base-actionis not, as Gitea resolves it against$GITHUB_WORKSPACE). This also removes the old double-CLI-install ambiguity.Changes
base-action/src/run-claude.ts— tokenizeclaude_argswithshell-quote(full-comment-line stripping; quoted / file-path schemas survive intact) and append the tokens after the unchangedBASE_ARGS, so emptyclaude_argsyields an identical arg list. Extractstructured_outputfrom the stream-jsonresultevent when--json-schemais present; fail loudly (non-zero exit,conclusion=failure) if the schema was requested but none came back.base-action/src/index.ts— forwardINPUT_CLAUDE_ARGS.base-action/action.yml— addclaude_argsinput +structured_outputoutput.action.yml— addclaude_argsinput +structured_outputoutput; replace the external base-action delegation with the directbun run(fullINPUT_*/provider env contract derived from the vendoredvalidate-env/index); bump the default Claude CLI install1.0.117 → 2.1.160(--json-schemaneeds a newer CLI).shell-quoteadded to bothpackage.jsonfiles. The rootbun.lockis reconciled as a side effect: it was already stale vspackage.json(locked@octokit/rest@21while^22is declared), so the action's non-frozenbun installalready resolved the newer tree at runtime — this just makes the committed lockfile honest.shell-quotemust live in the root because production resolves it from the rootnode_modulesvia walk-up.parseClaudeArgs(incl. a$refschema round-trip),hasJsonSchemadetection,extractStructuredOutput, and a byte-identical-when-empty guard. Full base-action suite green; root suite green (the 3updateCommentBodyfailures are pre-existing order-dependent flakes, present ongiteatoo).examples/gitea-structured-output.yml.Verification
Beyond unit tests, the rewired entrypoint was driven end-to-end with
base-action/node_modulesremoved (production layout) and a stubbedclaude:claude_args→execution_file+conclusion=success, nostructured_output(behave-unchanged);--json-schema→structured_outputpopulated,conclusion=success;--json-schemawith no structured result →conclusion=failure, exit 1.This confirms
shell-quote/@actions/coreresolve from the rootnode_modules, the named-pipe orchestration runs, and the outputs wire through.Follow-up (cannot be done from this repo — acceptance criterion #4)
Consumers using a pre-baked runner image that sets
path_to_claude_code_executableskip the action's install step, so the baked CLI must be bumped independently: rebuild that image with a--json-schema-capableCLAUDE_CLI_VERSIONand restart the runner. Documented under README → Version pins.Accuracy notes
@1.0.61plus the baked one) and PATH order decided the winner; the new flow deterministically uses the runner's pinnedpath_to_claude_code_executable.structured_outputfield name is taken from the SDK's result message and is expected to match CLI2.1.160's stream-jsonresultevent; a real--json-schemarun on the rebuilt runner should confirm it (the fail-loud guard degrades safely if it differs).🤖 Generated with Claude Code
Bring the upstream v1 `claude_args` input and `structured_output` output into this Gitea fork so downstream workflows can run the agent with arbitrary Claude CLI flags (notably `--json-schema`) and read the schema-validated verdict back as an action output, without an agent-written file. Approach (issue Option B, ported rather than bumped): the production "Run Claude Code" step previously delegated to the external anthropics/claude-code-base-action@v0.0.63, which has neither input. Upstream base-action main has them but has migrated to the Claude Agent SDK — a wholesale divergence that would change v0.0.63 behavior every downstream step relies on. Instead this invokes the vendored base-action directly via `bun run ${github.action_path}/base-action/src/index.ts` (the same github.action_path pattern prepare.ts already uses, reliable on Gitea), and ports only the two features onto the v0.0.63 process-spawn engine. - base-action/src/run-claude.ts: tokenize claude_args with shell-quote (comment-line stripping; quoted/file-path schemas survive intact) and append after the byte-identical BASE_ARGS, so empty claude_args yields an unchanged arg list; extract structured_output from the stream-json result event when --json-schema is present, failing loudly if absent. - base-action/src/index.ts: forward INPUT_CLAUDE_ARGS. - base-action/action.yml: add claude_args input + structured_output output. - action.yml: add claude_args input + structured_output output; replace the external base-action delegation with a direct bun-run of the vendored copy (full INPUT_*/provider env contract derived from the vendored validate-env/index); bump the default Claude CLI install to 2.1.160 (--json-schema requires a newer CLI than 1.0.117). - shell-quote added to both package.json files; root bun.lock reconciled (it was stale vs package.json — non-frozen installs already resolved the newer tree). - Tests for tokenizer, hasJsonSchema, and structured-output extraction. - README: inputs/outputs tables, "Structured output" + "Version pins". - examples/gitea-structured-output.yml. Follow-up (cannot be done from this repo): consumers using a pre-baked runner image (path_to_claude_code_executable) must rebuild that image with a --json-schema-capable Claude CLI and restart the runner. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>