@ajvikram/relay 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.
Files changed (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +538 -0
  3. package/dist/cli/commands/branch.d.ts +9 -0
  4. package/dist/cli/commands/branch.d.ts.map +1 -0
  5. package/dist/cli/commands/branch.js +69 -0
  6. package/dist/cli/commands/branch.js.map +1 -0
  7. package/dist/cli/commands/diff.d.ts +8 -0
  8. package/dist/cli/commands/diff.d.ts.map +1 -0
  9. package/dist/cli/commands/diff.js +61 -0
  10. package/dist/cli/commands/diff.js.map +1 -0
  11. package/dist/cli/commands/history.d.ts +8 -0
  12. package/dist/cli/commands/history.d.ts.map +1 -0
  13. package/dist/cli/commands/history.js +32 -0
  14. package/dist/cli/commands/history.js.map +1 -0
  15. package/dist/cli/commands/hook.d.ts +6 -0
  16. package/dist/cli/commands/hook.d.ts.map +1 -0
  17. package/dist/cli/commands/hook.js +118 -0
  18. package/dist/cli/commands/hook.js.map +1 -0
  19. package/dist/cli/commands/init.d.ts +4 -0
  20. package/dist/cli/commands/init.d.ts.map +1 -0
  21. package/dist/cli/commands/init.js +41 -0
  22. package/dist/cli/commands/init.js.map +1 -0
  23. package/dist/cli/commands/install.d.ts +18 -0
  24. package/dist/cli/commands/install.d.ts.map +1 -0
  25. package/dist/cli/commands/install.js +203 -0
  26. package/dist/cli/commands/install.js.map +1 -0
  27. package/dist/cli/commands/load.d.ts +9 -0
  28. package/dist/cli/commands/load.d.ts.map +1 -0
  29. package/dist/cli/commands/load.js +69 -0
  30. package/dist/cli/commands/load.js.map +1 -0
  31. package/dist/cli/commands/repos.d.ts +7 -0
  32. package/dist/cli/commands/repos.d.ts.map +1 -0
  33. package/dist/cli/commands/repos.js +53 -0
  34. package/dist/cli/commands/repos.js.map +1 -0
  35. package/dist/cli/commands/save.d.ts +9 -0
  36. package/dist/cli/commands/save.d.ts.map +1 -0
  37. package/dist/cli/commands/save.js +267 -0
  38. package/dist/cli/commands/save.js.map +1 -0
  39. package/dist/cli/commands/status.d.ts +23 -0
  40. package/dist/cli/commands/status.d.ts.map +1 -0
  41. package/dist/cli/commands/status.js +134 -0
  42. package/dist/cli/commands/status.js.map +1 -0
  43. package/dist/cli/commands/ui.d.ts +9 -0
  44. package/dist/cli/commands/ui.d.ts.map +1 -0
  45. package/dist/cli/commands/ui.js +54 -0
  46. package/dist/cli/commands/ui.js.map +1 -0
  47. package/dist/cli/commands/vscode.d.ts +6 -0
  48. package/dist/cli/commands/vscode.d.ts.map +1 -0
  49. package/dist/cli/commands/vscode.js +86 -0
  50. package/dist/cli/commands/vscode.js.map +1 -0
  51. package/dist/cli/index.d.ts +3 -0
  52. package/dist/cli/index.d.ts.map +1 -0
  53. package/dist/cli/index.js +143 -0
  54. package/dist/cli/index.js.map +1 -0
  55. package/dist/format/renderer.d.ts +5 -0
  56. package/dist/format/renderer.d.ts.map +1 -0
  57. package/dist/format/renderer.js +207 -0
  58. package/dist/format/renderer.js.map +1 -0
  59. package/dist/format/schema.d.ts +516 -0
  60. package/dist/format/schema.d.ts.map +1 -0
  61. package/dist/format/schema.js +90 -0
  62. package/dist/format/schema.js.map +1 -0
  63. package/dist/format/template.d.ts +44 -0
  64. package/dist/format/template.d.ts.map +1 -0
  65. package/dist/format/template.js +131 -0
  66. package/dist/format/template.js.map +1 -0
  67. package/dist/git/context.d.ts +23 -0
  68. package/dist/git/context.d.ts.map +1 -0
  69. package/dist/git/context.js +121 -0
  70. package/dist/git/context.js.map +1 -0
  71. package/dist/index.d.ts +8 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +8 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/mcp/server.d.ts +2 -0
  76. package/dist/mcp/server.d.ts.map +1 -0
  77. package/dist/mcp/server.js +164 -0
  78. package/dist/mcp/server.js.map +1 -0
  79. package/dist/server/index.d.ts +12 -0
  80. package/dist/server/index.d.ts.map +1 -0
  81. package/dist/server/index.js +143 -0
  82. package/dist/server/index.js.map +1 -0
  83. package/dist/server/ui.d.ts +2 -0
  84. package/dist/server/ui.d.ts.map +1 -0
  85. package/dist/server/ui.js +797 -0
  86. package/dist/server/ui.js.map +1 -0
  87. package/dist/storage/global.d.ts +40 -0
  88. package/dist/storage/global.d.ts.map +1 -0
  89. package/dist/storage/global.js +126 -0
  90. package/dist/storage/global.js.map +1 -0
  91. package/dist/storage/index.d.ts +59 -0
  92. package/dist/storage/index.d.ts.map +1 -0
  93. package/dist/storage/index.js +253 -0
  94. package/dist/storage/index.js.map +1 -0
  95. package/package.json +54 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Relay Contributors
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,538 @@
1
+ # Relay 🔄
2
+
3
+ > **Seamless AI context handoff across platforms.**
4
+ > Continue exactly where you left off — on Claude, Cursor, Copilot, or Antigravity — without losing a single decision.
5
+
6
+ [![npm version](https://img.shields.io/npm/v/@relay-ai/relay)](https://www.npmjs.com/package/@relay-ai/relay)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ [![Node.js >=18](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
9
+ [![Tests](https://img.shields.io/badge/tests-11%20passing-brightgreen)](#testing)
10
+
11
+ ---
12
+
13
+ ## The Problem
14
+
15
+ When you exhaust tokens on one AI coding assistant and switch to another, you lose everything:
16
+
17
+ | Approach | What goes wrong |
18
+ |----------|----------------|
19
+ | Paste raw chat history | New agent gets overwhelmed — hallucinates, loses the original goal |
20
+ | Start fresh | Repeats the same failed approaches you just spent an hour debugging |
21
+ | Manual summary | Strips the evidence, constraints, and rationale the next agent actually needs |
22
+
23
+ ## The Solution
24
+
25
+ **Relay transfers work state, not conversation history.**
26
+
27
+ It captures what actually matters — the decisions made, the hard constraints, the approaches that failed, and the precise next steps — and hands them off to any AI agent in a structured format it can immediately act on.
28
+
29
+ ```bash
30
+ # About to hit your token limit?
31
+ relay save
32
+
33
+ # Switched to a new platform?
34
+ relay load --prompt --clipboard
35
+ # Paste into the new agent — it picks up exactly where you left off
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Installation
41
+
42
+ ### Option 1: Via npm & GitHub (Recommended)
43
+ ```bash
44
+ # Direct install from GitHub
45
+ npm install -g github:ajvikram/relay
46
+ ```
47
+
48
+ ### Option 2: Standalone Binaries (No Node.js runtime required)
49
+ Download pre-built standalone executables from [GitHub Releases](https://github.com/ajvikram/relay/releases/latest):
50
+
51
+ * **macOS Apple Silicon (M1/M2/M3/M4):** `relay-darwin-arm64`
52
+ * **macOS Intel (x64):** `relay-darwin-amd64`
53
+ * **Linux (x64 / ARM64):** `relay-linux-amd64` / `relay-linux-arm64`
54
+ * **Windows (x64):** `relay-windows-amd64.exe`
55
+
56
+ ```bash
57
+ # One-liner for macOS (Apple Silicon):
58
+ curl -fsSL https://github.com/ajvikram/relay/releases/latest/download/relay-darwin-arm64 -o /usr/local/bin/relay && chmod +x /usr/local/bin/relay
59
+ ```
60
+
61
+ **Requirements:** Git (and Node.js ≥ 18 if installing via npm)
62
+
63
+ ---
64
+
65
+ ## Quick Start
66
+
67
+ ```bash
68
+ # 1. Initialize Relay in your project
69
+ cd my-project
70
+ relay init
71
+
72
+ # 2. Install bootstrap rules (agents auto-read the handoff on session start)
73
+ relay install all
74
+
75
+ # 3. When you hit a token limit — save your context
76
+ relay save
77
+
78
+ # 4. On the new platform — load the context
79
+ relay load --prompt --clipboard
80
+ # Paste into the new agent — done
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Commands
86
+
87
+ ### `relay init`
88
+ Initialize Relay in the current git repository. Creates the `.relay/` directory and updates `.gitignore`.
89
+
90
+ ```bash
91
+ relay init
92
+ relay init --dir /path/to/repo
93
+ ```
94
+
95
+ ---
96
+
97
+ ### `relay save`
98
+ Save the current session context as a structured handoff document.
99
+
100
+ ```bash
101
+ relay save # Interactive guided mode — prompts for each section
102
+ relay save --agent # Agent-assisted mode — generates a prompt to paste into your current AI
103
+ relay save --platform claude # Override source platform
104
+ ```
105
+
106
+ **Interactive mode** walks you through:
107
+ - Session goal and status
108
+ - Decisions made (with rationale and evidence)
109
+ - Non-negotiable constraints
110
+ - Failed approaches (so the next agent doesn't repeat them)
111
+ - Next steps (ordered, with file hints)
112
+
113
+ **Agent-assisted mode** (`--agent`) outputs a prompt you paste into your current AI agent. The agent writes the structured JSON, you paste it back, and Relay saves it. Best used when you're close to the token limit and want the agent to document its own work.
114
+
115
+ ---
116
+
117
+ ### `relay load`
118
+ Load and display the current handoff context.
119
+
120
+ ```bash
121
+ relay load # Display full handoff document
122
+ relay load --prompt # Compact agent-ready prompt (fewer tokens, paste into new session)
123
+ relay load --clipboard # Copy output to clipboard
124
+ relay load --json # Raw JSON (for scripting)
125
+ relay load --prompt --clipboard # Most common: copy compact prompt to clipboard
126
+ ```
127
+
128
+ ---
129
+
130
+ ### `relay status` / `relay check`
131
+ Inspect workspace alignment and detect code drift against the active handoff.
132
+
133
+ ```bash
134
+ relay status # Human-readable drift & alignment report
135
+ relay check # Alias for relay status
136
+ relay status --json # Raw JSON drift report
137
+ ```
138
+
139
+ Identifies:
140
+ - Branch alignment vs. handoff branch
141
+ - Commit drift (number of commits made since handoff was recorded)
142
+ - Uncommitted modified/added/reverted files
143
+ - Immediate pending tasks
144
+
145
+ ---
146
+
147
+ ### `relay diff`
148
+ Compare the active handoff against previous history archives.
149
+
150
+ ```bash
151
+ relay diff # Compare active handoff against previous session
152
+ relay diff --from abc1234 # Compare a specific archive against active handoff
153
+ relay diff --from a1 --to b2 # Compare two historical handoffs
154
+ ```
155
+
156
+ ---
157
+
158
+ ### `relay ui` / `relay dashboard`
159
+ Launch the local web dashboard to visually explore decisions, constraints, history timeline, and generate custom prompts.
160
+
161
+ ```bash
162
+ relay ui # Launches dashboard and opens browser (http://localhost:4242)
163
+ relay dashboard # Alias for relay ui
164
+ relay ui --port 8080 # Use custom port
165
+ relay ui --no-open # Start server without auto-opening browser
166
+ ```
167
+
168
+ Features:
169
+ - **Active Goal & Status Banner:** Instant visibility on token limits and original goal.
170
+ - **Action Checklist:** Filterable next steps by priority (`Immediate`, `Soon`, `Later`).
171
+ - **Decisions & Constraints Wall:** Categorized, searchable card matrix.
172
+ - **Failed Approaches Graveyard:** Clear warnings to prevent agent hallucinations & repeated errors.
173
+ - **1-Click Prompt Builder:** Customize prompt components and copy with one click.
174
+ - **Live Git Diff & File Tree:** Visual representation of changes made.
175
+ - **Session History Timeline:** Browse chronological archive of past handoffs.
176
+
177
+ ---
178
+
179
+ ### `relay branch`
180
+ Manage branch-scoped handoffs across git branches.
181
+
182
+ ```bash
183
+ relay branch list # List all saved branch handoffs
184
+ relay branch show <branch> # View handoff for a specific branch
185
+ relay branch apply <branch> # Apply branch handoff as the active handoff.md
186
+ ```
187
+
188
+ ---
189
+
190
+ ### `relay hook`
191
+ Install git hooks to automatically stage commit messages and file diffs into handoff drafts.
192
+
193
+ ```bash
194
+ relay hook install # Install post-commit auto-drafting hook
195
+ relay hook uninstall # Remove hook cleanly
196
+ ```
197
+
198
+ ---
199
+
200
+ ### `relay vscode`
201
+ Generate VS Code / Cursor IDE tasks for 1-click execution.
202
+
203
+ ```bash
204
+ relay vscode # Configures .vscode/tasks.json with Relay shortcuts
205
+ ```
206
+
207
+ ---
208
+
209
+ ### `relay repos` / `relay list`
210
+ List and manage registered Relay repositories across your machine.
211
+
212
+ ```bash
213
+ relay repos # List all registered repositories and their status
214
+ relay list # Alias for relay repos
215
+ relay repos --json # Output machine registry as JSON
216
+ relay repos --remove <path> # Remove a repository from the global registry
217
+ ```
218
+
219
+ ---
220
+
221
+ ### `relay history`
222
+ Browse past handoffs.
223
+
224
+ ```bash
225
+ relay history # List all past handoffs
226
+ relay history --limit 5 # Show last 5
227
+ relay history --show abc123 # Show a specific handoff by ID
228
+ ```
229
+
230
+ ---
231
+
232
+ ### `relay install`
233
+ Install bootstrap rules so AI platforms automatically read `.relay/handoff.md`. Supports both **repository-level** and **machine-wide global** installations.
234
+
235
+ ```bash
236
+ relay install claude # Installs to CLAUDE.md in current repo
237
+ relay install cursor # Installs to .cursor/rules/relay.mdc
238
+ relay install antigravity # Installs to AGENTS.md
239
+ relay install copilot # Installs to .github/copilot-instructions.md
240
+ relay install all # Installs to all platforms in current repo
241
+
242
+ # Global / System-Wide Installation:
243
+ relay install all --global # Installs global rules into ~/.claude, ~/.cursor, etc.
244
+ relay install all -g --dry-run # Preview global installation without touching files
245
+ ```
246
+
247
+ When installed globally, **every project on your computer** will automatically check for and respect `.relay/handoff.md` without having to configure rules repository-by-repository!
248
+
249
+ ---
250
+
251
+ ### `relay ui` / `relay dashboard`
252
+ Launch the local web dashboard to visually explore decisions, constraints, history timeline, and generate custom prompts.
253
+
254
+ ```bash
255
+ relay ui # Launches dashboard and opens browser (http://localhost:4242)
256
+ relay dashboard # Alias for relay ui
257
+ relay ui --global # Launch global multi-repo dashboard with project switcher
258
+ relay ui --port 8080 # Use custom port
259
+ relay ui --no-open # Start server without auto-opening browser
260
+ ```
261
+
262
+ ---
263
+
264
+ ### `relay serve`
265
+ Start the Relay MCP server for programmatic access from MCP-compatible platforms.
266
+
267
+ ```bash
268
+ relay serve
269
+ ```
270
+
271
+ Add to your platform's MCP config:
272
+
273
+ ```json
274
+ {
275
+ "mcpServers": {
276
+ "relay": {
277
+ "command": "relay",
278
+ "args": ["serve"]
279
+ }
280
+ }
281
+ }
282
+ ```
283
+
284
+ ---
285
+
286
+ ## Platform Support
287
+
288
+ | Platform | Auto-reads handoff | MCP tools | Bootstrap file |
289
+ |----------|--------------------|-----------|----------------|
290
+ | Claude / Claude Code | ✅ via `CLAUDE.md` rule | ✅ 5 tools | `CLAUDE.md` |
291
+ | Cursor | ✅ via `.cursor/rules/` | ✅ 5 tools | `.cursor/rules/relay.mdc` |
292
+ | Antigravity | ✅ via `AGENTS.md` rule | ✅ 5 tools | `AGENTS.md` |
293
+ | GitHub Copilot | ✅ via instructions file | ❌ no MCP | `.github/copilot-instructions.md` |
294
+
295
+ ---
296
+
297
+ ## What Gets Saved
298
+
299
+ A Relay handoff captures everything the next agent needs:
300
+
301
+ ```markdown
302
+ # 🔄 Relay Handoff
303
+
304
+ > Generated: 8/28/2026, 2:00:00 PM | Source: claude | Status: in_progress
305
+ > ⚠️ Session ended due to token limit
306
+
307
+ ## 🎯 Goal
308
+ Implement JWT refresh token rotation in the auth service
309
+
310
+ ## 📁 Repository State
311
+ | Field | Value |
312
+ |---------|--------------------|
313
+ | Branch | `feature/auth` |
314
+ | HEAD | `abc1234` |
315
+
316
+ ## 📝 Files Modified This Session
317
+ - 🟡 `src/auth/tokens.ts` — Added stub for token rotation
318
+ - 🟡 `src/auth/routes.ts` — Added POST /auth/refresh route
319
+
320
+ ## 🏛️ Decisions Made
321
+ ### Using Redis for refresh token storage
322
+ **Why:** O(1) revocation required. DB queries were 40ms+ under load.
323
+ **Evidence:** src/auth/benchmarks.ts
324
+
325
+ ## 🚫 Non-Negotiable Constraints
326
+ - ❌ No new npm dependencies — bundle size at ceiling
327
+ - ❌ Must maintain backward compatibility with v2 API
328
+
329
+ ## ⚠️ Failed Approaches — Do NOT Repeat
330
+ ### ~~Stateless JWT blocklist~~
331
+ **Why it failed:** Requires Redis anyway — doubles overhead with no benefit
332
+
333
+ ## ✅ Next Steps
334
+ 1. **Complete rotateRefreshToken() in src/auth/tokens.ts**
335
+ - Context: Stub exists at tokens.ts:45, needs Redis atomic swap
336
+ - Start at: `src/auth/tokens.ts`
337
+ ```
338
+
339
+ ---
340
+
341
+ ## MCP Tools
342
+
343
+ When running `relay serve`, these tools are available to MCP-compatible agents:
344
+
345
+ | Tool | Description |
346
+ |------|-------------|
347
+ | `read_handoff` | Get current context (`prompt` / `markdown` / `json` format) |
348
+ | `write_handoff` | Save context programmatically (agent calls this itself) |
349
+ | `get_git_context` | Current branch, modified files, raw diff |
350
+ | `list_history` | Past handoffs with timestamps and goals |
351
+ | `get_history_entry` | Full content of a specific past handoff |
352
+
353
+ **Resource:** `relay://handoff/current` — the current handoff as a readable MCP resource.
354
+
355
+ ---
356
+
357
+ ## Directory Structure
358
+
359
+ Relay creates a `.relay/` directory in your repository:
360
+
361
+ ```
362
+ .relay/
363
+ ├── handoff.md ← Current handoff, human-readable (commit this to git)
364
+ ├── handoff.json ← Current handoff, machine-readable (for MCP tools)
365
+ ├── config.yaml ← Local config (git-ignored by default)
366
+ └── history/ ← Archived past handoffs (git-ignored by default)
367
+ ├── history-index.json
368
+ ├── abc12345.md
369
+ └── def67890.md
370
+ ```
371
+
372
+ > **Tip:** Commit `.relay/handoff.md` to git so teammates can also pick up context. History files stay local.
373
+
374
+ ---
375
+
376
+ ## Configuration
377
+
378
+ Edit `.relay/config.yaml` to customize behaviour:
379
+
380
+ ```yaml
381
+ # Include raw git diff in handoff files (can be large for big changesets)
382
+ include_diff: true
383
+
384
+ # Maximum diff size in bytes before truncation (default: 50KB)
385
+ max_diff_size: 51200
386
+
387
+ # Keep history files in git (default: false — local only)
388
+ git_track_history: false
389
+
390
+ # Track the current handoff.md in git (default: true)
391
+ git_track_handoff: true
392
+ ```
393
+
394
+ ---
395
+
396
+ ## How the Handoff Protocol Works
397
+
398
+ ```
399
+ ┌─────────────────────────────────────────────────────────┐
400
+ │ Platform A (e.g. Claude) │
401
+ │ │
402
+ │ Token limit approaching... │
403
+ │ Developer runs: relay save │
404
+ │ └─ Captures git state automatically │
405
+ │ └─ Prompts for decisions / constraints / next steps │
406
+ │ └─ Writes .relay/handoff.md + handoff.json │
407
+ └─────────────────────────────────────────────────────────┘
408
+
409
+ │ (switch platform)
410
+
411
+ ┌─────────────────────────────────────────────────────────┐
412
+ │ Platform B (e.g. Cursor) │
413
+ │ │
414
+ │ Agent reads CLAUDE.md / AGENTS.md / .cursor/rules │
415
+ │ └─ Rule: "Read .relay/handoff.md first" │
416
+ │ └─ Agent reads the handoff and orients itself │
417
+ │ └─ Acknowledges goal, constraints, next steps │
418
+ │ Work continues seamlessly │
419
+ └─────────────────────────────────────────────────────────┘
420
+ ```
421
+
422
+ **Two tiers, both work independently:**
423
+
424
+ | Tier | Mechanism | Works with | Requires |
425
+ |------|-----------|-----------|----------|
426
+ | **Tier 1** | `.relay/handoff.md` file | Any platform (any agent can read a file) | Nothing |
427
+ | **Tier 2** | MCP server tools | Claude, Cursor, Antigravity | `relay serve` running |
428
+
429
+ ---
430
+
431
+ ## Development
432
+
433
+ ```bash
434
+ # Clone and install
435
+ git clone https://github.com/relay-ai/relay
436
+ cd relay
437
+ npm install
438
+
439
+ # Build
440
+ npm run build
441
+
442
+ # Watch mode
443
+ npm run dev
444
+
445
+ # Run tests
446
+ npm test
447
+ npm run test:watch
448
+
449
+ # Type check only (no emit)
450
+ npm run typecheck
451
+ ```
452
+
453
+ ### Project Structure
454
+
455
+ ```
456
+ src/
457
+ ├── cli/
458
+ │ ├── index.ts ← CLI entry point (Commander.js)
459
+ │ └── commands/
460
+ │ ├── init.ts ← relay init
461
+ │ ├── save.ts ← relay save (interactive + agent-assisted)
462
+ │ ├── load.ts ← relay load
463
+ │ ├── history.ts ← relay history
464
+ │ └── install.ts ← relay install
465
+ ├── format/
466
+ │ ├── schema.ts ← Zod schema for the handoff document
467
+ │ ├── renderer.ts ← Markdown + agent prompt renderers
468
+ │ └── template.ts ← Interactive prompt choices + agent-save prompt
469
+ ├── git/
470
+ │ └── context.ts ← Git state capture (branch, HEAD, diff, files)
471
+ ├── storage/
472
+ │ └── index.ts ← .relay/ directory I/O, history management
473
+ ├── mcp/
474
+ │ └── server.ts ← MCP server (5 tools + 1 resource)
475
+ └── index.ts ← Public API exports
476
+ tests/
477
+ └── schema-renderer.test.ts ← Unit tests (11 tests, all passing)
478
+ ```
479
+
480
+ ---
481
+
482
+ ## Testing
483
+
484
+ ```bash
485
+ npm test
486
+ ```
487
+
488
+ ```
489
+ ✓ tests/schema-renderer.test.ts (11 tests)
490
+ ✓ HandoffSchema — parses a valid handoff
491
+ ✓ HandoffSchema — applies defaults for missing optional arrays
492
+ ✓ HandoffSchema — rejects invalid status
493
+ ✓ HandoffSchema — rejects invalid platform
494
+ ✓ renderMarkdown — generates valid markdown with all sections
495
+ ✓ renderMarkdown — includes token exhausted warning when flag is set
496
+ ✓ renderMarkdown — includes relay metadata comment
497
+ ✓ renderMarkdown — does not include empty sections
498
+ ✓ renderAgentPrompt — generates compact agent prompt
499
+ ✓ renderAgentPrompt — prompt is shorter than markdown output
500
+ ✓ renderAgentPrompt — uses at most one top-level heading
501
+ ```
502
+
503
+ ---
504
+
505
+ ## Contributing
506
+
507
+ 1. Fork the repository
508
+ 2. Create a feature branch: `git checkout -b feat/my-feature`
509
+ 3. Make your changes and add tests
510
+ 4. Run `npm run typecheck && npm test` — both must pass
511
+ 5. Submit a pull request
512
+
513
+ ### Adding a new platform
514
+
515
+ 1. Add the platform to the `Platform` enum in [`src/format/schema.ts`](src/format/schema.ts)
516
+ 2. Add a bootstrap rule string and installer target in [`src/cli/commands/install.ts`](src/cli/commands/install.ts)
517
+ 3. Update the platform support table in this README
518
+
519
+ ---
520
+
521
+ ## Roadmap
522
+
523
+ - [ ] `relay save --quick` — minimal prompts for fast saves
524
+ - [ ] TUI history browser (`relay history` with arrow key navigation)
525
+ - [ ] VSCode extension — save/load handoff from the command palette
526
+ - [ ] Team handoffs — share context via a central server
527
+ - [ ] Token budget warning integration — auto-prompt save when approaching limits
528
+ - [ ] `relay diff` — compare two handoffs to see how context evolved
529
+
530
+ ---
531
+
532
+ ## License
533
+
534
+ [MIT](LICENSE) — free for personal and commercial use.
535
+
536
+ ---
537
+
538
+ *Built to solve the real pain of working across AI coding platforms. If it helps you, ⭐ the repo.*
@@ -0,0 +1,9 @@
1
+ interface BranchOptions {
2
+ dir?: string;
3
+ list?: boolean;
4
+ show?: string;
5
+ apply?: string;
6
+ }
7
+ export declare function branchCommand(subcommand: string | undefined, branchName: string | undefined, options: BranchOptions): Promise<void>;
8
+ export {};
9
+ //# sourceMappingURL=branch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/branch.ts"],"names":[],"mappings":"AAKA,UAAU,aAAa;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA8EzI"}
@@ -0,0 +1,69 @@
1
+ import chalk from 'chalk';
2
+ import { getRepoRoot, captureGitContext } from '../../git/context.js';
3
+ import { resolveCwd, listSavedBranches, readHandoff, saveHandoff } from '../../storage/index.js';
4
+ import { renderMarkdown } from '../../format/renderer.js';
5
+ export async function branchCommand(subcommand, branchName, options) {
6
+ const cwd = resolveCwd(options.dir);
7
+ const repoRoot = await getRepoRoot(cwd);
8
+ if (!repoRoot) {
9
+ console.error(chalk.red('✗ Not a git repository.'));
10
+ process.exit(1);
11
+ }
12
+ const action = subcommand || (options.list ? 'list' : options.show ? 'show' : options.apply ? 'apply' : 'list');
13
+ if (action === 'list') {
14
+ const branches = listSavedBranches(repoRoot);
15
+ const gitCtx = await captureGitContext(repoRoot);
16
+ const currentBranch = gitCtx.repository.branch;
17
+ console.log(chalk.bold('\n🌿 Saved Branch Handoffs'));
18
+ console.log(chalk.dim('─'.repeat(60)));
19
+ if (branches.length === 0) {
20
+ console.log(chalk.yellow('No branch handoffs found.'));
21
+ console.log(chalk.dim('Run `relay save` on any branch to create a branch-scoped handoff.\n'));
22
+ return;
23
+ }
24
+ for (const b of branches) {
25
+ const isCurrent = b.branch === currentBranch;
26
+ const prefix = isCurrent ? chalk.green('● ') : chalk.dim('○ ');
27
+ const branchLabel = isCurrent ? chalk.bold.green(b.branch) + chalk.dim(' (active)') : chalk.cyan(b.branch);
28
+ console.log(`${prefix}${branchLabel} [${b.id}]`);
29
+ console.log(` Goal: ${b.goal}`);
30
+ console.log(` Updated: ${new Date(b.timestamp).toLocaleString()} | Status: ${b.status}`);
31
+ console.log('');
32
+ }
33
+ console.log(chalk.dim('Tip: Use `relay branch show <branch>` to view a specific branch handoff.'));
34
+ console.log('');
35
+ return;
36
+ }
37
+ if (action === 'show') {
38
+ const target = branchName || options.show;
39
+ if (!target) {
40
+ console.error(chalk.red('✗ Please specify a branch name: relay branch show <branch>'));
41
+ process.exit(1);
42
+ }
43
+ const handoff = readHandoff(repoRoot, target);
44
+ if (!handoff) {
45
+ console.error(chalk.red(`✗ No handoff found for branch: ${target}`));
46
+ process.exit(1);
47
+ }
48
+ console.log(renderMarkdown(handoff));
49
+ return;
50
+ }
51
+ if (action === 'apply' || action === 'switch') {
52
+ const target = branchName || options.apply;
53
+ if (!target) {
54
+ console.error(chalk.red('✗ Please specify a branch name: relay branch apply <branch>'));
55
+ process.exit(1);
56
+ }
57
+ const handoff = readHandoff(repoRoot, target);
58
+ if (!handoff) {
59
+ console.error(chalk.red(`✗ No handoff found for branch: ${target}`));
60
+ process.exit(1);
61
+ }
62
+ saveHandoff(repoRoot, handoff);
63
+ console.log(chalk.green(`✓ Switched active .relay/handoff.md to branch: ${target}`));
64
+ return;
65
+ }
66
+ console.log(chalk.yellow(`Unknown branch command: ${action}`));
67
+ console.log(chalk.dim('Available: relay branch list, relay branch show <name>, relay branch apply <name>'));
68
+ }
69
+ //# sourceMappingURL=branch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch.js","sourceRoot":"","sources":["../../../src/cli/commands/branch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface HandoffDiffOptions {
2
+ dir?: string;
3
+ from?: string;
4
+ to?: string;
5
+ json?: boolean;
6
+ }
7
+ export declare function diffCommand(options: HandoffDiffOptions): Promise<void>;
8
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/diff.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+D5E"}