pytxo 1.3.6 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,71 @@
1
- # pytxo (npm)
1
+ # pytxo
2
2
 
3
- npm wrapper for the [Pytxo](https://pytxo.com) CLI. The first `pytxo` command downloads the same-version prebuilt binary from the public [pytxo-releases](https://github.com/Pytxo-dev/pytxo-releases) repository and checks it against the release's `SHA256SUMS.txt`. Package managers that run install scripts do this during `postinstall` instead; npm 12 and pnpm 10 skip it by default, and npm's warning about it is safe to ignore.
3
+ **Land all your agents' branches in one command.**
4
4
 
5
- Community: [Discord](https://discord.gg/AUFRPFjSYv)
5
+ Pytxo merges your Claude Code, Codex and Copilot branches in a throwaway
6
+ worktree, runs your tests on the combined result, and lands them only if they
7
+ pass. If two branches break each other, nothing lands and you see why. Free,
8
+ open source, runs on your machine or in CI.
6
9
 
7
10
  ```bash
8
- npm i -g pytxo
9
- pytxo doctor
11
+ npx pytxo demo # watch it catch two branches that pass alone and fail together
12
+ npx pytxo # check the agent branches in your repository
13
+ npx pytxo --apply # and land them if they pass
10
14
  ```
11
15
 
12
- Set `PYTXO_SKIP_DOWNLOAD=1` and `PYTXO_BINARY=/path/to/pytxo` for local development.
16
+ `npx pytxo` finds the agent branches (`claude/*`, `codex/*`, `copilot/*`,
17
+ `cursor/*`, ... and every branch in a linked worktree), finds your test command
18
+ (`npm test`, `pytest`, `cargo test`, `go test`, ...), and prints a verdict:
19
+
20
+ ```
21
+ BLOCKED claude/rename-price and codex/add-discount pass npm test alone but fail together.
22
+ why claude/rename-price renamed price to unitPrice; codex/add-discount still uses price (src/discount.js:1).
23
+ next rebase codex/add-discount onto claude/rename-price and use unitPrice, then run npx pytxo again.
24
+ ```
25
+
26
+ Nothing in your repository changes unless you add `--apply`. Exit codes: `0` ready (or nothing to
27
+ check), `2` blocked, `3` stale (the target moved), `1` error, `130` stopped by
28
+ Ctrl+C.
29
+
30
+ In CI, one line in a workflow checks each agent pull request together with
31
+ the other open ones into its base branch. Add
32
+ `.github/workflows/pytxo.yml`:
33
+
34
+ ```yaml
35
+ name: Pytxo
36
+ on: pull_request
37
+ permissions:
38
+ contents: read
39
+ pull-requests: write
40
+ concurrency:
41
+ group: pytxo-${{ github.event.pull_request.number }}
42
+ cancel-in-progress: true
43
+ jobs:
44
+ check:
45
+ runs-on: ubuntu-latest
46
+ steps:
47
+ - uses: Pytxo-dev/pytxo/actions/land@v2
48
+ ```
49
+
50
+ Your tests run in the job that holds this token, so give the workflow no other
51
+ permissions or secrets. If Copilot's coding agent opens pull requests here, add
52
+ `actions: read` so the check can include them once someone lets their run
53
+ start; with it, code in the checked branches can also read every workflow
54
+ run's logs and artifacts. See
55
+ [the GitHub Action guide](https://pytxo.com/docs/getting-started/land-in-ci).
56
+
57
+ Docs: [pytxo.com/docs](https://pytxo.com/docs) · Source:
58
+ [github.com/Pytxo-dev/pytxo](https://github.com/Pytxo-dev/pytxo) · Community:
59
+ [Discord](https://discord.gg/AUFRPFjSYv)
60
+
61
+ ## How the package works
62
+
63
+ The first run downloads the same-version prebuilt binary from the public
64
+ [pytxo-releases](https://github.com/Pytxo-dev/pytxo-releases) repository and
65
+ checks it against the release's `SHA256SUMS.txt`. Package managers that run
66
+ install scripts do this during `postinstall` instead; npm 12 and pnpm 10 skip
67
+ it by default, and npm's warning about it is safe to ignore. Pytxo sends
68
+ nothing anywhere.
69
+
70
+ Set `PYTXO_SKIP_DOWNLOAD=1` and `PYTXO_BINARY=/path/to/pytxo` for local
71
+ development.
package/bin/pytxo.js CHANGED
@@ -3,16 +3,28 @@
3
3
 
4
4
  const { spawnSync } = require("node:child_process");
5
5
  const fs = require("node:fs");
6
+ const os = require("node:os");
6
7
 
7
8
  const { dest, install } = require("../postinstall");
8
9
 
10
+ // Lets the CLI say "run npx pytxo again" when it was started that way.
11
+ const runAs = process.env.PYTXO_RUN_AS || (process.env.npm_command === "exec" ? "npx pytxo" : "pytxo");
12
+
9
13
  function run(bin) {
10
- const result = spawnSync(bin, process.argv.slice(2), { stdio: "inherit" });
14
+ // The binary handles Ctrl+C itself: it removes its scratch checkout, a
15
+ // second Ctrl+C quits it at once, and it exits 130. Waiting for it keeps the
16
+ // terminal from coming back while it cleans up.
17
+ for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) process.on(signal, () => {});
18
+ const result = spawnSync(bin, process.argv.slice(2), {
19
+ stdio: "inherit",
20
+ env: { ...process.env, PYTXO_RUN_AS: runAs },
21
+ });
11
22
  if (result.error) {
12
23
  console.error(result.error.message);
13
24
  process.exit(1);
14
25
  }
15
- process.exit(result.status ?? 1);
26
+ // Killed by a signal: exit as a shell reports it, 128 plus its number.
27
+ process.exit(result.status ?? (result.signal ? 128 + os.constants.signals[result.signal] : 1));
16
28
  }
17
29
 
18
30
  async function main() {
package/lib/platform.js CHANGED
@@ -3,6 +3,8 @@
3
3
  function getAssetName(platform = process.platform, arch = process.arch) {
4
4
  const assets = {
5
5
  "win32:x64": "pytxo-windows-x64.exe",
6
+ // Windows on Arm runs x64 binaries under emulation.
7
+ "win32:arm64": "pytxo-windows-x64.exe",
6
8
  "darwin:arm64": "pytxo-darwin-arm64",
7
9
  "darwin:x64": "pytxo-darwin-x64",
8
10
  "linux:arm64": "pytxo-linux-arm64",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pytxo",
3
- "version": "1.3.6",
4
- "description": "Pytxo agent hypervisor CLI — local-first PTY orchestration",
3
+ "version": "2.0.0",
4
+ "description": "Land all your agents' branches in one command. Pytxo merges your Claude Code, Codex and Copilot branches in a throwaway worktree, runs your tests on the combined result, and lands them only if they pass.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -11,12 +11,17 @@
11
11
  "bugs": "https://github.com/Pytxo-dev/pytxo-releases/issues",
12
12
  "keywords": [
13
13
  "pytxo",
14
- "cli",
14
+ "claude-code",
15
+ "codex",
16
+ "copilot",
17
+ "cursor",
18
+ "coding-agents",
15
19
  "ai-agents",
16
- "mcp",
17
- "orchestration",
18
- "terminal",
19
- "rust"
20
+ "git",
21
+ "worktree",
22
+ "merge",
23
+ "ci",
24
+ "tests"
20
25
  ],
21
26
  "engines": {
22
27
  "node": ">=18"
package/postinstall.js CHANGED
@@ -125,7 +125,10 @@ async function install() {
125
125
  const checksumUrl = `${releaseBase}/SHA256SUMS.txt`;
126
126
  const url = `${releaseBase}/${asset}`;
127
127
  console.error(`pytxo: downloading ${asset} ${TAG} and checking it against SHA256SUMS.txt…`);
128
- const checksumText = (await downloadBuffer(checksumUrl)).toString("utf8");
128
+ const fetched = (promise) => promise.catch((error) => {
129
+ throw Object.assign(error, { downloadFailed: true });
130
+ });
131
+ const checksumText = (await fetched(downloadBuffer(checksumUrl))).toString("utf8");
129
132
  const checksum = expectedChecksum(checksumText, asset);
130
133
 
131
134
  if (fs.existsSync(dest)) {
@@ -136,7 +139,7 @@ async function install() {
136
139
  }
137
140
  }
138
141
 
139
- const bytes = await downloadBuffer(url);
142
+ const bytes = await fetched(downloadBuffer(url));
140
143
  assertChecksum(bytes, checksum, asset);
141
144
  const suffix = process.platform === "win32" ? ".exe" : "";
142
145
  const temp = path.join(
@@ -167,7 +170,11 @@ async function main() {
167
170
  if (require.main === module) {
168
171
  main().catch((error) => {
169
172
  console.error(`pytxo: install failed: ${error.message}`);
170
- process.exitCode = 1;
173
+ // A download that did not happen (offline, a proxy) must not fail
174
+ // `npm install` or `npx`: the launcher tries again on first run. A binary
175
+ // that fails verification fails the install.
176
+ if (error.downloadFailed) console.error("pytxo: the first run of pytxo will try again.");
177
+ else process.exitCode = 1;
171
178
  });
172
179
  }
173
180