antiphon 0.1.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/LICENSE +21 -0
- package/README.md +113 -0
- package/bin/antiphon.mjs +19 -0
- package/lib/antiphon.py +1178 -0
- package/lib/channel.mjs +143 -0
- package/package.json +18 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Serkan Can Gökalp
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Antiphon
|
|
2
|
+
|
|
3
|
+
**Two terminals, two separate agents, an open-identity bridge.** While Claude Code and Codex CLI work in the same project, each sees the other's context and can wake the other when it needs to, without ever faking who the message is from.
|
|
4
|
+
|
|
5
|
+
Antiphon doesn't dispatch work. It only carries messages between the two sides while preserving whether they came from the human user, from Claude, or from Codex.
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
No shared log is kept. Both CLIs already write their own transcripts; Antiphon reads and derives from them, marking in `.antiphon/cursor.json` which messages each side has already seen.
|
|
10
|
+
|
|
11
|
+
### Pull — context, no wake
|
|
12
|
+
|
|
13
|
+
| Direction | Mechanism |
|
|
14
|
+
|---|---|
|
|
15
|
+
| Codex → Claude | Claude `UserPromptSubmit` hook |
|
|
16
|
+
| Claude → Codex | Codex `UserPromptSubmit` hook |
|
|
17
|
+
|
|
18
|
+
The other side's recent messages enter your turn's context when you type
|
|
19
|
+
something. Nobody is woken up.
|
|
20
|
+
|
|
21
|
+
### Push — live wake
|
|
22
|
+
|
|
23
|
+
| Direction | Mechanism |
|
|
24
|
+
|---|---|
|
|
25
|
+
| Claude → Codex | Claude `Stop` hook + `codex queue` |
|
|
26
|
+
| Codex → Claude | Codex `Stop` hook + MCP Channel |
|
|
27
|
+
|
|
28
|
+
A line starting with `@codex` or `@claude` in a reply reaches the other
|
|
29
|
+
agent immediately, even if nobody is typing.
|
|
30
|
+
|
|
31
|
+
### How identity is preserved
|
|
32
|
+
|
|
33
|
+
A Claude → Codex message reaches Codex tagged either `[Antiphon bridge] Claude:` (pushed from Claude's Stop hook) or `[Antiphon channel] Claude:` (a direct reply sent through the channel, via the `reply_to_codex` tool) — either way, Codex sees these as Claude's words, not the human user's.
|
|
34
|
+
|
|
35
|
+
A Codex → Claude message never pastes text into the terminal and never impersonates user input. The local MCP server sends Claude Code a `notifications/claude/channel` event. Its metadata looks like:
|
|
36
|
+
|
|
37
|
+
```xml
|
|
38
|
+
<channel source="antiphon" sender="codex" sender_kind="agent" message_id="...">
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Claude Code's interface shows this as an incoming channel event, and Claude treats the message as the words of the Codex agent, not of the human user. It sends its reply back with the `reply_to_codex` MCP tool.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
Requires Node 18+ and Python 3. The Claude Code channel is a research
|
|
46
|
+
preview and needs Claude Code 2.1.80 or newer.
|
|
47
|
+
|
|
48
|
+
Install the command, either straight from the repository:
|
|
49
|
+
|
|
50
|
+
npm i -g github:serkancangokalp/antiphon
|
|
51
|
+
|
|
52
|
+
or from npm:
|
|
53
|
+
|
|
54
|
+
npm i -g @serkancangokalp/antiphon
|
|
55
|
+
|
|
56
|
+
Either way the command is `antiphon` — the package name only decides
|
|
57
|
+
where it comes from. Then, in the project the two agents share:
|
|
58
|
+
|
|
59
|
+
cd /your/project
|
|
60
|
+
antiphon setup
|
|
61
|
+
claude --dangerously-load-development-channels server:antiphon
|
|
62
|
+
|
|
63
|
+
`setup` writes `.claude/settings.json`, `.codex/hooks.json`,
|
|
64
|
+
`.codex/config.toml`, `.mcp.json`, `.claude/settings.local.json`,
|
|
65
|
+
`AGENTS.md` and `CLAUDE.md` for that project. The hook commands resolve
|
|
66
|
+
`antiphon` through your `PATH` rather than hardcoding an install path, so
|
|
67
|
+
they keep working after a reinstall or a move; `.mcp.json` and
|
|
68
|
+
`.codex/config.toml` still record this project's own absolute directory,
|
|
69
|
+
so each side can find the right channel socket. None of the seven files
|
|
70
|
+
live in this repository — they are generated per project. Approve the
|
|
71
|
+
Codex hooks once when Codex first shows them.
|
|
72
|
+
|
|
73
|
+
## Update
|
|
74
|
+
|
|
75
|
+
npm i -g github:serkancangokalp/antiphon # from the repository
|
|
76
|
+
npm i -g @serkancangokalp/antiphon@latest # from npm
|
|
77
|
+
|
|
78
|
+
cd /your/project && antiphon setup
|
|
79
|
+
|
|
80
|
+
Re-running `setup` migrates hooks and instruction blocks written by older
|
|
81
|
+
versions in place; it never creates duplicates.
|
|
82
|
+
|
|
83
|
+
## Commands
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
antiphon status # transcript, cursor and channel status
|
|
87
|
+
antiphon summary [side] # show the context that would be injected
|
|
88
|
+
antiphon setup # (re)install the project setup
|
|
89
|
+
npm test # Python unit tests + real MCP protocol test
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`setup` registers the `antiphon_read` MCP tool for Codex in this project's
|
|
93
|
+
`.codex/config.toml`, so there is nothing to add by hand. Note the entry
|
|
94
|
+
names `args = ["mcp"]`: the `channel` server is Claude's side and hands out
|
|
95
|
+
`reply_to_codex`. Aiming Codex at it would let Codex publish messages
|
|
96
|
+
labelled as Claude's — exactly what this bridge exists to prevent — so
|
|
97
|
+
`setup` rewrites that table whenever it is wrong, leaving the rest of the
|
|
98
|
+
file alone.
|
|
99
|
+
|
|
100
|
+
The bridge works without this entry; it only lets Codex query the bridge by hand when it suspects the pull hook has gone quiet.
|
|
101
|
+
|
|
102
|
+
## Limits
|
|
103
|
+
|
|
104
|
+
- A live push needs an open session on the target side. If the target is closed, the message still shows up on the next pull.
|
|
105
|
+
- The Claude MCP Channel is only live while Claude Code is started with the right development-channel flag.
|
|
106
|
+
- Channels is currently a research preview; it requires a claude.ai login or a Console API key. It isn't supported on the Bedrock, Vertex, or Foundry providers. A Team/Enterprise admin may need to enable the feature.
|
|
107
|
+
- The Codex hook asks for re-approval the first time it's used and whenever the hook file changes.
|
|
108
|
+
- Matching is done on the same project's absolute directory.
|
|
109
|
+
- Once a message has been seen, the cursor advances and the same content is never injected twice.
|
|
110
|
+
- Context transfer has a budget of roughly 2600 characters; the newest messages are kept.
|
|
111
|
+
- Unix sockets only — there is no Windows support.
|
|
112
|
+
|
|
113
|
+
MIT.
|
package/bin/antiphon.mjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const [subcommand, ...rest] = process.argv.slice(2);
|
|
8
|
+
|
|
9
|
+
// `channel` is a long-lived stdio MCP server; it runs directly, never through Python.
|
|
10
|
+
const target = subcommand === "channel"
|
|
11
|
+
? { cmd: process.execPath, args: [join(here, "..", "lib", "channel.mjs")] }
|
|
12
|
+
: { cmd: "python3", args: [join(here, "..", "lib", "antiphon.py"), ...(subcommand ? [subcommand] : []), ...rest] };
|
|
13
|
+
|
|
14
|
+
const child = spawn(target.cmd, target.args, { stdio: "inherit" });
|
|
15
|
+
child.on("exit", (code, signal) => process.exit(signal ? 1 : code ?? 0));
|
|
16
|
+
child.on("error", (error) => {
|
|
17
|
+
process.stderr.write(`antiphon: ${error.message}\n`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
});
|