opencode-replay 1.0.2 → 1.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/README.md +81 -3
- package/dist/assets/gist-preview.js +126 -0
- package/dist/index.js +1307 -215
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -39,6 +39,47 @@ opencode-replay
|
|
|
39
39
|
opencode-replay --open
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
## Output Formats
|
|
43
|
+
|
|
44
|
+
### HTML (default)
|
|
45
|
+
|
|
46
|
+
Generate static HTML transcripts viewable in any browser:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
opencode-replay # Current project's sessions
|
|
50
|
+
opencode-replay --all # All projects
|
|
51
|
+
opencode-replay -o ./my-transcripts # Custom output directory
|
|
52
|
+
opencode-replay -a # Auto-name output (e.g., ./my-project-replay)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Markdown
|
|
56
|
+
|
|
57
|
+
Generate markdown for sharing or piping:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# To stdout (for piping)
|
|
61
|
+
opencode-replay -f md -s ses_xxx
|
|
62
|
+
opencode-replay -f md -s ses_xxx | gh gist create --filename transcript.md -
|
|
63
|
+
opencode-replay -f md -s ses_xxx | pbcopy
|
|
64
|
+
|
|
65
|
+
# To file
|
|
66
|
+
opencode-replay -f md -s ses_xxx -o transcript.md
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### GitHub Gist
|
|
70
|
+
|
|
71
|
+
Upload HTML directly to GitHub Gist with a shareable preview URL:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
opencode-replay --gist # Secret gist (default)
|
|
75
|
+
opencode-replay --gist --gist-public # Public gist
|
|
76
|
+
opencode-replay -s ses_xxx --gist # Upload specific session
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Requires [GitHub CLI](https://cli.github.com/) to be installed and authenticated (`gh auth login`).
|
|
80
|
+
|
|
81
|
+
The generated gist is viewable via [gisthost.github.io](https://gisthost.github.io/) which renders HTML files directly.
|
|
82
|
+
|
|
42
83
|
## Usage
|
|
43
84
|
|
|
44
85
|
### Basic Commands
|
|
@@ -85,19 +126,40 @@ The built-in server includes:
|
|
|
85
126
|
- Path traversal protection
|
|
86
127
|
- Auto-opens browser on start
|
|
87
128
|
|
|
129
|
+
### GitHub Integration
|
|
130
|
+
|
|
131
|
+
Add clickable links to GitHub commits in the rendered output:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
opencode-replay --repo owner/repo-name
|
|
135
|
+
|
|
136
|
+
# Example
|
|
137
|
+
opencode-replay --repo sst/opencode
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
This adds links to commit hashes found in tool outputs, making it easy to navigate to the exact commits referenced during a session.
|
|
141
|
+
|
|
88
142
|
### All Options
|
|
89
143
|
|
|
90
144
|
| Option | Short | Description |
|
|
91
145
|
|--------|-------|-------------|
|
|
92
|
-
| `--all` |
|
|
93
|
-
| `--
|
|
146
|
+
| `--all` | | Generate for all projects (default: current project only) |
|
|
147
|
+
| `--auto` | `-a` | Auto-name output directory from project/session name |
|
|
148
|
+
| `--output <path>` | `-o` | Output directory (HTML) or file (markdown) |
|
|
94
149
|
| `--session <id>` | `-s` | Generate for a specific session only |
|
|
150
|
+
| `--format <type>` | `-f` | Output format: `html` (default), `md` |
|
|
151
|
+
| `--stdout` | | Output to stdout (markdown only, requires `--session`) |
|
|
152
|
+
| `--gist` | | Upload HTML to GitHub Gist after generation |
|
|
153
|
+
| `--gist-public` | | Make gist public (default: secret) |
|
|
95
154
|
| `--json` | | Include raw JSON export alongside HTML |
|
|
96
155
|
| `--open` | | Open in browser after generation |
|
|
97
156
|
| `--storage <path>` | | Custom storage path (default: `~/.local/share/opencode/storage`) |
|
|
98
157
|
| `--serve` | | Start HTTP server after generation |
|
|
99
158
|
| `--port <number>` | | Server port (default: `3000`) |
|
|
100
159
|
| `--no-generate` | | Skip generation, only serve existing output |
|
|
160
|
+
| `--repo <owner/name>` | | GitHub repo for commit links (e.g., `sst/opencode`) |
|
|
161
|
+
| `--quiet` | `-q` | Suppress non-essential output |
|
|
162
|
+
| `--verbose` | | Show detailed debug output |
|
|
101
163
|
| `--help` | `-h` | Show help message |
|
|
102
164
|
| `--version` | `-v` | Show version |
|
|
103
165
|
|
|
@@ -114,6 +176,9 @@ opencode-replay --all --open
|
|
|
114
176
|
# Export a specific session with JSON data
|
|
115
177
|
opencode-replay --session ses_abc123 --json -o ./session-export
|
|
116
178
|
|
|
179
|
+
# Auto-name output from project name
|
|
180
|
+
opencode-replay -a # Creates ./my-project-replay
|
|
181
|
+
|
|
117
182
|
# Use custom storage location
|
|
118
183
|
opencode-replay --storage /custom/path/to/storage
|
|
119
184
|
|
|
@@ -122,6 +187,18 @@ opencode-replay --serve --port 8080
|
|
|
122
187
|
|
|
123
188
|
# Quick preview of existing transcripts
|
|
124
189
|
opencode-replay --serve --no-generate -o ./my-transcripts
|
|
190
|
+
|
|
191
|
+
# Markdown to stdout for piping
|
|
192
|
+
opencode-replay -f md -s ses_xxx
|
|
193
|
+
|
|
194
|
+
# Create a GitHub Gist from a session
|
|
195
|
+
opencode-replay -s ses_xxx --gist
|
|
196
|
+
|
|
197
|
+
# Upload all projects to a public gist
|
|
198
|
+
opencode-replay --all --gist --gist-public
|
|
199
|
+
|
|
200
|
+
# Add GitHub commit links
|
|
201
|
+
opencode-replay --repo sst/opencode
|
|
125
202
|
```
|
|
126
203
|
|
|
127
204
|
## Output Structure
|
|
@@ -131,7 +208,7 @@ opencode-replay-output/
|
|
|
131
208
|
├── index.html # Master index (all sessions)
|
|
132
209
|
├── assets/
|
|
133
210
|
│ ├── styles.css # Stylesheet
|
|
134
|
-
│ └── search.js # Client-side search
|
|
211
|
+
│ └── search.js # Client-side search
|
|
135
212
|
├── projects/ # Only in --all mode
|
|
136
213
|
│ └── {project-name}/
|
|
137
214
|
│ └── index.html # Project-level session list
|
|
@@ -164,6 +241,7 @@ Each tool type has specialized rendering:
|
|
|
164
241
|
|
|
165
242
|
- [Bun](https://bun.sh) runtime (recommended) or Node.js 18+
|
|
166
243
|
- OpenCode sessions in standard storage location
|
|
244
|
+
- [GitHub CLI](https://cli.github.com/) (optional, for `--gist` feature)
|
|
167
245
|
|
|
168
246
|
## Development
|
|
169
247
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode Replay - Gist Preview Link Rewriter
|
|
3
|
+
* Rewrites relative links for gisthost.github.io preview
|
|
4
|
+
*
|
|
5
|
+
* When HTML files are uploaded to GitHub Gist and viewed via gisthost.github.io,
|
|
6
|
+
* relative links need to be rewritten to include the gist ID in the URL format:
|
|
7
|
+
* ?GIST_ID/path/to/file.html
|
|
8
|
+
*
|
|
9
|
+
* This script runs only on gisthost.github.io and automatically rewrites all
|
|
10
|
+
* relative links in the document.
|
|
11
|
+
*/
|
|
12
|
+
(function initGistPreview() {
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
// Only run on gisthost.github.io
|
|
16
|
+
if (!window.location.host.includes('gisthost.github.io')) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Extract gist ID from URL
|
|
21
|
+
// URL format: https://gisthost.github.io/?GIST_ID/filename.html
|
|
22
|
+
var search = window.location.search;
|
|
23
|
+
if (!search || search.length < 2) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Remove leading '?' and extract gist ID (everything before first '/')
|
|
28
|
+
var path = search.slice(1);
|
|
29
|
+
var slashIndex = path.indexOf('/');
|
|
30
|
+
if (slashIndex === -1) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
var gistId = path.slice(0, slashIndex);
|
|
35
|
+
if (!gistId) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Get current directory for resolving relative paths
|
|
40
|
+
var currentPath = path.slice(slashIndex + 1);
|
|
41
|
+
var currentDir = currentPath.substring(0, currentPath.lastIndexOf('/') + 1);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Resolve a relative path against the current directory
|
|
45
|
+
* Handles './' and '../' prefixes
|
|
46
|
+
*/
|
|
47
|
+
function resolvePath(href) {
|
|
48
|
+
// Already absolute or anchor-only
|
|
49
|
+
if (href.startsWith('http') || href.startsWith('#') || href.startsWith('?')) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
var resolved = currentDir + href;
|
|
54
|
+
|
|
55
|
+
// Normalize path (handle ../)
|
|
56
|
+
var parts = resolved.split('/');
|
|
57
|
+
var normalized = [];
|
|
58
|
+
for (var i = 0; i < parts.length; i++) {
|
|
59
|
+
var part = parts[i];
|
|
60
|
+
if (part === '..') {
|
|
61
|
+
normalized.pop();
|
|
62
|
+
} else if (part !== '.' && part !== '') {
|
|
63
|
+
normalized.push(part);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return normalized.join('/');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Rewrite a single link element
|
|
72
|
+
*/
|
|
73
|
+
function rewriteLink(link) {
|
|
74
|
+
var href = link.getAttribute('href');
|
|
75
|
+
if (!href) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
var resolved = resolvePath(href);
|
|
80
|
+
if (resolved !== null) {
|
|
81
|
+
link.setAttribute('href', '?' + gistId + '/' + resolved);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Rewrite all links in the document
|
|
87
|
+
*/
|
|
88
|
+
function rewriteAllLinks() {
|
|
89
|
+
var links = document.querySelectorAll('a[href]');
|
|
90
|
+
for (var i = 0; i < links.length; i++) {
|
|
91
|
+
rewriteLink(links[i]);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Run on DOMContentLoaded or immediately if DOM is ready
|
|
96
|
+
if (document.readyState === 'loading') {
|
|
97
|
+
document.addEventListener('DOMContentLoaded', rewriteAllLinks);
|
|
98
|
+
} else {
|
|
99
|
+
rewriteAllLinks();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Also observe for dynamically added links (optional but useful)
|
|
103
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
104
|
+
var observer = new MutationObserver(function(mutations) {
|
|
105
|
+
mutations.forEach(function(mutation) {
|
|
106
|
+
mutation.addedNodes.forEach(function(node) {
|
|
107
|
+
if (node.nodeType === 1) { // Element node
|
|
108
|
+
if (node.tagName === 'A' && node.hasAttribute('href')) {
|
|
109
|
+
rewriteLink(node);
|
|
110
|
+
}
|
|
111
|
+
// Also check children
|
|
112
|
+
var childLinks = node.querySelectorAll ? node.querySelectorAll('a[href]') : [];
|
|
113
|
+
for (var i = 0; i < childLinks.length; i++) {
|
|
114
|
+
rewriteLink(childLinks[i]);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
observer.observe(document.body || document.documentElement, {
|
|
122
|
+
childList: true,
|
|
123
|
+
subtree: true
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
})();
|