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 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` | `-a` | Generate for all projects (default: current project only) |
93
- | `--output <dir>` | `-o` | Output directory (default: `./opencode-replay-output`) |
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 (coming soon)
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
+ })();