# Add an Output to a Custom GitHub Action from a Linear Issue

Yes. An agent reads your Linear issue, adds a duration-ms output to your TypeScript GitHub Action with a test, and opens a pull request.

[Run this on Rush](https://cloud.getrush.ai/run/add-action-output/github-actions/), in a Project that has your repository.

## What the agent does

The run starts from the issue text and the repository named in resources. In an action bootstrapped from actions/typescript-action, the agent opens action.yml first: it declares one input, milliseconds, with a default of '1000', one output, time, and a runs block with using: node24 pointing at dist/index.js. It reads src/main.ts to see how that output is produced, core.getInput('milliseconds'), a debug log, await wait(parseInt(ms, 10)), then core.setOutput('time', new Date().toTimeString()). src/index.ts is only the entrypoint that calls run(), and src/wait.ts holds the wait helper that throws 'milliseconds is not a number' when the input is not a number.

Adding duration-ms means measuring the run and reporting it the same way as time. The agent records a start timestamp before the wait and calls core.setOutput('duration-ms', ...) after it, adds a duration-ms entry under outputs in action.yml with a description, and extends the README next to the Usage example that echoes steps.test-action.outputs.time with a matching line for the new output. For the test, __tests__/main.test.ts mocks @actions/core through __fixtures__/core.js with jest.unstable_mockModule and asserts on the core.setOutput calls; the agent adds an assertion that duration-ms is set with a number, following the pattern the file already uses for the time output. jest.config.js runs ts-jest with useESM and testMatch '**/*.test.ts', and both test scripts in package.json run jest with NODE_OPTIONS=--experimental-vm-modules.

Then it runs npm run all, the script the repository's CI uses: prettier --write, eslint ., jest, the coverage badge written to badges/coverage.svg, and npm run package, which rebuilds dist/index.js with rollup. That last step matters because .github/workflows/check-dist.yml rebuilds dist with npm run bundle on every pull request and fails if the checked-in bundle does not match the sources, so the rebuilt dist/index.js has to be part of the commit, not only the TypeScript change.

It opens the pull request on its own branch with the Linear issue linked in the description, then reads the checks the pull request triggers. ci.yml runs format:check, lint, ci-test, and an end-to-end run of the action with milliseconds: 2000 that echoes steps.test-action.outputs.time; a green run shows the action still works with the new output. If a check fails, the agent reads the failing step's log, fixes the cause and pushes again.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "A Linear issue labeled agent says: \"Add an Output to a Custom GitHub Action. Add a duration-ms output that reports how long the action ran, declare it in action.yml, document it in the README and test it.\" Do the work in this repository, add tests for it, run npm run all, and open a pull request that links back to the issue.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a pull request on a new branch. In a repository shaped like actions/typescript-action it touches action.yml (the new duration-ms output), src/main.ts (the start timestamp and the setOutput call), dist/index.js (the rebuilt bundle), README.md (the new output in the usage example), __tests__/main.test.ts (the new assertion) and badges/coverage.svg (regenerated by the coverage script). The description states what the issue asked for, which files changed and why, and links the Linear issue.

The pull request's checks are the proof: ci.yml runs the format, lint and Jest steps, and check-dist.yml verifies that dist/index.js is exactly what npm run bundle produces from the changed sources. The session's trajectory, at GET /o/{org}/sessions/{id}/messages, keeps every command the agent ran with its output, including each npm run all and any pushes it made after reading a failed check.

Before merging, a reviewer checks that duration-ms measures what the issue described, that the dist diff matches the src change without unrelated churn, that the README example references the new output with the same step id, and that the test asserts the value, not only that setOutput was called.

## Run it on every Linear issue labeled

Add an automation or a small endpoint that receives Linear's issue-labeled webhook for the label you triage with, agent, and POSTs this same request, naming the issue as a resource at https://linear.app/<workspace>/issue/<ID> so its text lands in the prompt, read through your Linear connection. Each labeled issue gets its own run in its own sandbox and its own pull request, so an afternoon of triage becomes a queue of reviewable branches. Because resources names a repository, the Rush GitHub App has to be installed on that repository.

Set a webhook URL on the API key and Rush POSTs run.completed or run.failed when each run finishes, with the session URL, so your server can post the pull request link back to the issue or the team channel. Send an Idempotency-Key header on each POST so a retry cannot start a duplicate run. A key runs at most max_concurrent sessions at once, 4 by default, and answers 429 past that, so a labeled batch should queue its requests.

## Why run it on Rush?

On a laptop this job is a slow loop: npm ci, edit three files, remember that dist/index.js is the file that actually runs, run npm run all, find the bundle drifted, rebuild, commit, push, and wait for check-dist.yml. On Rush the agent runs that loop in its own sandbox: it edits the sources, rebuilds the bundle, runs npm run all there, reads the pull request's checks, and pushes fixes itself. Your working tree is not involved, and the run keeps going if you close the laptop.

The result is checkable rather than claimed. The checks that prove the change run on the pull request itself, and the trajectory keeps every command with its output, so a reviewer can see the jest run and the dist rebuild behind the branch. Because each run is independent, the same request handles several labeled issues at once, one sandbox and one pull request each, and nothing reaches your default branch until a person merges it.

## Frequently asked questions

### Does the pull request include the rebuilt dist/index.js?

Yes, and it has to. An action with runs.using: node24 executes the checked-in bundle, and .github/workflows/check-dist.yml rebuilds dist with npm run bundle and fails the pull request if the bundle does not match the sources. The agent runs npm run package in its sandbox and commits the result, so the diff shows both the TypeScript change and the bundle it produces.

### What does npm run all cover?

It chains the repository's own checks: prettier --write, eslint ., the Jest suite with NODE_OPTIONS=--experimental-vm-modules, the coverage badge written to badges/coverage.svg, and npm run package, the rollup build of dist/index.js. A failing step stops the chain, so a green npm run all in the sandbox means the pull request's ci.yml steps pass too.

### How does the agent test the new output?

__tests__/main.test.ts mocks @actions/core through __fixtures__/core.js with jest.unstable_mockModule, so the test asserts on the setOutput calls the action makes. The agent adds a case that runs the action's run() function and checks that duration-ms was set with a number, next to the existing assertion on the time output.

### Will it touch the existing time output or the wait helper?

No. The change adds duration-ms alongside time and leaves inputs.milliseconds and src/wait.ts alone; the diff shows only the new measurement, the declaration in action.yml, the README line and the test. If the issue text conflicts with how the repository already measures time, the agent says so in the pull request instead of guessing.

### Do I need anything besides the repository?

The repository has to be reachable by the Rush GitHub App, installed once in the console under Settings, Integrations, GitHub. The issue text rides in the prompt, and naming the issue as a resource also works, read through your Linear connection. Runs without a repository in resources need no GitHub at all.
