spiracha 1.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/AGENTS.md ADDED
@@ -0,0 +1,128 @@
1
+ # AGENTS.md
2
+
3
+ ## Purpose
4
+
5
+ This repo exports local Codex chats and Claude Code transcripts to Markdown or plain text.
6
+
7
+ Main entrypoints:
8
+ - `bun start ...` for Codex chat export
9
+ - `bun start` for interactive export mode
10
+ - `bun run export:claude -- ...` for Claude transcript export
11
+ - `bun run mcp` for the MCP server used by the local Codex plugin
12
+ - published package goal:
13
+ - `bunx codex-chats`
14
+ - `bunx codex-chats ...`
15
+ - `bunx codex-chats-claude ...`
16
+
17
+ ## Conventions and Rules
18
+
19
+ - Always use `bun` and `bunx`, HARD BAN on: `npm` unless absolutely necessary.
20
+ - Prefer `Bun.file()` instead of `fs` whenever possible.
21
+ - Kill any browser instance you start or stray `bun`, `node` processes after you are done. Never leave it running.
22
+ - Prefer arrow functions to classical functions, and `type` over `interface` in TS.
23
+ - Fixes are made using TDD approach.
24
+ - `bun format` and `bun typecheck` should always be ran at the end of your completion, if you introduced any warnings or errors clean them up yourself before you complete.
25
+ - NEVER disable a biome rule or TS rule without explicit permission from the user.
26
+ - If you believe the code you are changing requires some clarification for future AI agents to understand why the change was made, add a brief comment.
27
+ - Do NOT use decorative section headers made of repeated characters (e.g. `// -----`, `// =====`, etc.).
28
+ - Use `it('should...')` style tests.
29
+ - Unit-tests always live in the same folder as their implementation, never in the `test` folder.
30
+
31
+ ## Working Rules
32
+
33
+ - Use `rtk` as the default wrapper for shell commands that produce meaningful stdout or stderr.
34
+ - Prefer `bun test` for verification. Run it after non-trivial changes.
35
+ - When changing package/distribution behavior, also run a local packed-tarball smoke test.
36
+ - When changing the interactive flow, validate it in a real TTY session; piped stdin is not a reliable substitute.
37
+ - Use `apply_patch` for manual edits.
38
+ - Keep output compatibility stable. The exported transcript format is user-facing.
39
+ - Preserve the behavior of existing flags unless the task explicitly changes CLI semantics.
40
+
41
+ ## Architecture
42
+
43
+ Codex exporter modules:
44
+ - `src/lib/codex-exporter.ts`
45
+ - top-level orchestration and public barrel exports
46
+ - `src/lib/codex-exporter-cli.ts`
47
+ - CLI parsing, help text, deeplink parsing, default output-dir resolution
48
+ - `src/lib/codex-exporter-db.ts`
49
+ - SQLite queries, fallback session discovery, filter matching, export target construction
50
+ - `src/lib/codex-exporter-transcript.ts`
51
+ - JSONL transcript parsing, metadata extraction, message/tool rendering
52
+ - `src/lib/codex-exporter-types.ts`
53
+ - shared Codex exporter types and default path constants
54
+
55
+ Other important files:
56
+ - `src/lib/claude-exporter.ts`
57
+ - Claude transcript export pipeline
58
+ - `src/mcp-server.ts`
59
+ - MCP server exposing `export_codex_chats` and `export_claude_transcript`
60
+ - `plugins/codex-chats-export/`
61
+ - local Codex plugin manifest, skill, and MCP wiring
62
+
63
+ ## Test Strategy
64
+
65
+ Current tests cover:
66
+ - exporter end-to-end behavior for Codex and Claude
67
+ - Codex CLI parsing helpers
68
+ - interactive-mode inference helpers
69
+ - transcript formatting helpers
70
+ - MCP stdio protocol round-trips using the real server process
71
+ - type-checking via `bun run typecheck`
72
+
73
+ When changing risky areas:
74
+ - CLI changes: update/add tests in `src/lib/codex-exporter-cli.test.ts`
75
+ - transcript parsing/rendering: update/add tests in `src/lib/codex-exporter-transcript.test.ts`
76
+ - DB/filter/target logic: prefer focused unit tests against `src/lib/codex-exporter-db.ts`
77
+ - MCP contract changes: update `src/mcp-server.test.ts`
78
+
79
+ ## Common Commands
80
+
81
+ ```bash
82
+ rtk bun test
83
+ rtk bun run typecheck
84
+ rtk bun run build
85
+ rtk bun run test:perf
86
+ rtk bun start
87
+ rtk bun start -- --help
88
+ rtk bun start --interactive
89
+ rtk bun run export:claude -- --help
90
+ rtk bun run mcp
91
+ ```
92
+
93
+ Packed tarball smoke test:
94
+
95
+ ```bash
96
+ rtk bun pm pack
97
+ package_tgz="$PWD/codex-chats-<version>.tgz"
98
+ tmp_dir=$(mktemp -d)
99
+ cd "$tmp_dir"
100
+ printf '{"name":"codex-chats-smoke","private":true}\n' > package.json
101
+ rtk bun add "$package_tgz"
102
+ rtk bunx codex-chats --help
103
+ rtk bunx codex-chats-claude --help
104
+ ```
105
+
106
+ Example Codex export:
107
+
108
+ ```bash
109
+ rtk bunx codex-chats
110
+ rtk bunx codex-chats codex://threads/<thread-id> --optimized
111
+ rtk bun start --tools --project summer
112
+ rtk bun start codex://threads/<thread-id> --output-format txt
113
+ ```
114
+
115
+ Example Claude export:
116
+
117
+ ```bash
118
+ rtk bunx codex-chats-claude /path/to/transcript --output-format txt
119
+ rtk bun run export:claude -- /path/to/export-dir --output-format txt
120
+ ```
121
+
122
+ ## Notes
123
+
124
+ - `--project` matches the final `cwd` path segment for both POSIX and Windows-style paths, not the full path.
125
+ - Running `codex-chats` or `bun start` with no args enters interactive mode.
126
+ - Codex MCP exports must be scoped by at least one of `deeplinks`, `project`, or `cwd`.
127
+ - `txt` output is intentionally real plain text, not Markdown with a `.txt` extension.
128
+ - The published package is Bun-first. `bin` entrypoints target Bun shebang execution.
package/LICENSE.md ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2026 Ragaeeb Haq
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,182 @@
1
+ # spiracha
2
+
3
+ [![npm version](https://img.shields.io/npm/v/spiracha?label=npm)](https://www.npmjs.com/package/spiracha)
4
+ [![downloads](https://img.shields.io/npm/dm/spiracha?label=downloads)](https://www.npmjs.com/package/spiracha)
5
+ [![license](https://img.shields.io/npm/l/spiracha)](LICENSE.md)
6
+ [![runtime](https://img.shields.io/badge/runtime-Bun-000000?logo=bun)](https://bun.sh)
7
+
8
+ Export local Codex chats and Claude Code transcripts to Markdown or plain text.
9
+
10
+ ## Quick Start
11
+
12
+ For repo-local development:
13
+
14
+ ```bash
15
+ bun start
16
+ ```
17
+
18
+ Published package usage, once the package is available on npm:
19
+
20
+ ```bash
21
+ bunx spiracha
22
+ bunx spiracha claude /path/to/session-export.jsonl --output-format txt
23
+ ```
24
+
25
+ ## Features
26
+
27
+ - Export Codex session transcripts from local `.codex` history
28
+ - Filter Codex exports by:
29
+ - exact `cwd`
30
+ - project basename via `--project`
31
+ - specific thread deeplinks like `codex://threads/<id>`
32
+ - Include command logs with `--tools`
33
+ - Write Markdown or real plain-text output with `--output-format md|txt`
34
+ - Export Claude Code transcript `.jsonl` files or export directories
35
+ - Run the same export flows through an MCP server and a local Codex plugin
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ bun install
41
+ ```
42
+
43
+ For package use after publish, no local install is required:
44
+
45
+ ```bash
46
+ bunx spiracha --help
47
+ ```
48
+
49
+ ## Usage
50
+
51
+ ### Codex exports
52
+
53
+ Package entrypoint:
54
+
55
+ ```bash
56
+ bunx spiracha [options] [codex://threads/<id> ...]
57
+ bunx spiracha codex [options] [codex://threads/<id> ...]
58
+ ```
59
+
60
+ With no arguments, `spiracha` starts in interactive mode and asks what you want to export.
61
+
62
+ Examples:
63
+
64
+ ```bash
65
+ bunx spiracha
66
+ bunx spiracha --interactive
67
+ bunx spiracha --project summer
68
+ bunx spiracha --tools --project summer
69
+ bunx spiracha codex://threads/019da28f-ee5b-7881-afe0-68b3d3bd2c77
70
+ bunx spiracha codex://threads/019da28f-ee5b-7881-afe0-68b3d3bd2c77 --output-format txt
71
+ bunx spiracha --cwd ~/workspace/reversed/summer --flat
72
+ ```
73
+
74
+ Important flags:
75
+ - no args: start interactive mode
76
+ - `--interactive`: force the interactive prompt flow
77
+ - `--project <name>`: matches the final `cwd` path segment for both POSIX and Windows-style paths
78
+ - `--cwd <path>`: exact cwd match
79
+ - `--tools`: include `exec_command` call logs and summaries
80
+ - `--optimized`: compact transcript output
81
+ - `--flat`: write files into a single output folder
82
+ - `--output-format md|txt`: output as Markdown or plain text
83
+
84
+ ### Claude exports
85
+
86
+ ```bash
87
+ bunx spiracha claude <input-path> [options]
88
+ ```
89
+
90
+ Examples:
91
+
92
+ ```bash
93
+ bunx spiracha claude /path/to/session.jsonl
94
+ bunx spiracha claude /path/to/export-dir --tools
95
+ bunx spiracha claude /path/to/export-dir --output-format txt
96
+ ```
97
+
98
+ Repo-local equivalents remain available during development:
99
+
100
+ ```bash
101
+ bun start
102
+ bun start --interactive
103
+ bun start ...
104
+ bun run export:claude -- ...
105
+ ```
106
+
107
+ Legacy aliases remain available for compatibility:
108
+
109
+ ```bash
110
+ bunx codex-chats
111
+ bunx codex-chats-claude
112
+ ```
113
+
114
+ ## MCP server
115
+
116
+ Run the MCP server with:
117
+
118
+ ```bash
119
+ bun run mcp
120
+ ```
121
+
122
+ Exposed tools:
123
+ - `export_codex_chats`
124
+ - `export_claude_transcript`
125
+
126
+ The local plugin lives in [plugins/codex-chats-export](~/workspace/codex-chats/plugins/codex-chats-export) and is registered through [plugins/codex-chats-export/.mcp.json](~/workspace/codex-chats/plugins/codex-chats-export/.mcp.json).
127
+
128
+ ## Development
129
+
130
+ Useful commands:
131
+
132
+ ```bash
133
+ bun test
134
+ bun run typecheck
135
+ bun run build
136
+ bun run test:perf
137
+ bun start
138
+ bun start --interactive
139
+ bun start -- --help
140
+ bun run export:claude -- --help
141
+ bun run mcp
142
+ ```
143
+
144
+ Packed-tarball smoke test before publishing:
145
+
146
+ ```bash
147
+ bun pm pack
148
+ package_tgz="$PWD/codex-chats-<version>.tgz"
149
+ tmp_dir=$(mktemp -d)
150
+ cd "$tmp_dir"
151
+ printf '{"name":"codex-chats-smoke","private":true}\n' > package.json
152
+ bun add "$package_tgz"
153
+ bunx spiracha --help
154
+ bunx spiracha claude --help
155
+ bunx codex-chats --help
156
+ bunx codex-chats-claude --help
157
+ ```
158
+
159
+ ## Project Layout
160
+
161
+ - `src/export-chats.ts`: Codex CLI wrapper
162
+ - `src/export-claude.ts`: Claude CLI wrapper
163
+ - `src/mcp-server.ts`: MCP server entrypoint
164
+ - `src/lib/codex-exporter-*.ts`: Codex exporter modules
165
+ - `src/lib/claude-exporter.ts`: Claude exporter implementation
166
+ - `plugins/codex-chats-export/`: local Codex plugin bundle
167
+
168
+ ## Testing
169
+
170
+ The test suite includes:
171
+ - Codex exporter end-to-end coverage
172
+ - Claude exporter end-to-end coverage
173
+ - Codex CLI helper tests
174
+ - transcript rendering helper tests
175
+ - MCP stdio protocol round-trip tests
176
+ - local packaging should be smoke-tested with a packed tarball before publishing
177
+
178
+ Run:
179
+
180
+ ```bash
181
+ bun test
182
+ ```
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "author": {
3
+ "name": "Ragaeeb Haq",
4
+ "url": "https://github.com/ragaeeb"
5
+ },
6
+ "bin": {
7
+ "codex-chats": "./src/export-chats.ts",
8
+ "codex-chats-claude": "./src/export-claude.ts",
9
+ "spiracha": "./src/spiracha.ts"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/ragaeeb/spiracha/issues"
13
+ },
14
+ "dependencies": {
15
+ "@inquirer/prompts": "^8.4.2",
16
+ "@modelcontextprotocol/sdk": "^1.29.0",
17
+ "zod": "^4.4.3"
18
+ },
19
+ "description": "Export local Codex chats and Claude Code transcripts to Markdown or plain text.",
20
+ "devDependencies": {
21
+ "@types/bun": "^1.3.13",
22
+ "@types/node": "^25.6.2",
23
+ "typescript": "^6.0.3"
24
+ },
25
+ "engines": {
26
+ "bun": ">=1.3.13"
27
+ },
28
+ "files": [
29
+ "src/export-chats.ts",
30
+ "src/export-claude.ts",
31
+ "src/mcp-server.ts",
32
+ "src/spiracha.ts",
33
+ "src/lib/claude-exporter.ts",
34
+ "src/lib/codex-exporter-cli.ts",
35
+ "src/lib/codex-exporter-db.ts",
36
+ "src/lib/codex-exporter-transcript.ts",
37
+ "src/lib/codex-exporter-types.ts",
38
+ "src/lib/codex-exporter.ts",
39
+ "src/lib/interactive-cli.ts",
40
+ "src/lib/shared.ts",
41
+ "README.md",
42
+ "AGENTS.md"
43
+ ],
44
+ "homepage": "https://github.com/ragaeeb/spiracha#readme",
45
+ "keywords": [
46
+ "codex",
47
+ "claude",
48
+ "transcripts",
49
+ "export",
50
+ "markdown",
51
+ "bun"
52
+ ],
53
+ "license": "MIT",
54
+ "name": "spiracha",
55
+ "packageManager": "bun@1.3.13",
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/ragaeeb/spiracha.git"
59
+ },
60
+ "scripts": {
61
+ "build": "bun run typecheck",
62
+ "export:claude": "bun run ./src/export-claude.ts",
63
+ "format": "biome check . --write && biome lint . --write",
64
+ "mcp": "bun run ./src/mcp-server.ts",
65
+ "start": "bun run ./src/export-chats.ts",
66
+ "typecheck": "bunx tsc --noEmit"
67
+ },
68
+ "type": "module",
69
+ "version": "1.0.0"
70
+ }
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { lstat } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { stdin as input, stdout as output } from 'node:process';
6
+ import { createInterface } from 'node:readline/promises';
7
+ import { getCodexHelpText, parseCodexCliArgs, runCodexExport } from './lib/codex-exporter';
8
+ import { runInteractiveExport } from './lib/interactive-cli';
9
+ import { CliUsageError } from './lib/shared';
10
+
11
+ export const runExportChatsCli = async (argv = process.argv.slice(2)): Promise<void> => {
12
+ if (argv.includes('--help') || argv.includes('-h')) {
13
+ console.log(getCodexHelpText());
14
+ return;
15
+ }
16
+
17
+ try {
18
+ if (shouldRunInteractive(argv)) {
19
+ await runInteractiveCliFlow();
20
+ return;
21
+ }
22
+
23
+ await runCodexCliFlow(argv);
24
+ } catch (error) {
25
+ if (error instanceof CliUsageError) {
26
+ console.error(error.message);
27
+ console.error('');
28
+ console.error(getCodexHelpText());
29
+ process.exit(1);
30
+ }
31
+
32
+ if (error instanceof Error) {
33
+ console.error(error.message);
34
+ process.exit(1);
35
+ }
36
+
37
+ throw error;
38
+ }
39
+ };
40
+
41
+ const shouldRunInteractive = (argv: string[]): boolean => {
42
+ return argv.length === 0 || argv.includes('--interactive');
43
+ };
44
+
45
+ const runInteractiveCliFlow = async (): Promise<void> => {
46
+ const result = await runInteractiveExport();
47
+ const targetFolder = await printInteractiveExportResult(result);
48
+ await maybeOpenExportFolder(targetFolder);
49
+ };
50
+
51
+ const runCodexCliFlow = async (argv: string[]): Promise<void> => {
52
+ const options = parseCodexCliArgs(argv);
53
+ const result = await runCodexExport(options);
54
+ printCodexExportResult(result);
55
+ };
56
+
57
+ const printInteractiveExportResult = async (
58
+ result: Awaited<ReturnType<typeof runInteractiveExport>>,
59
+ ): Promise<string> => {
60
+ if (result.mode === 'claude') {
61
+ console.log(`Exported ${result.sourcePath} -> ${result.outputPath}`);
62
+ return resolveExportFolder(result.outputPath);
63
+ }
64
+
65
+ printCodexExportFiles(result.files);
66
+ printMissingThreadWarnings(result.missingThreadIds);
67
+ console.log(`Done. Exported ${result.exportedCount} chat(s) to ${result.outputDir}`);
68
+ return result.outputDir;
69
+ };
70
+
71
+ const printCodexExportResult = (result: Awaited<ReturnType<typeof runCodexExport>>): void => {
72
+ printCodexExportFiles(result.files);
73
+ printMissingThreadWarnings(result.missingThreadIds);
74
+ console.log(`Done. Exported ${result.exportedCount} chat(s) to ${result.outputDir}`);
75
+ };
76
+
77
+ const printCodexExportFiles = (files: Awaited<ReturnType<typeof runCodexExport>>['files']): void => {
78
+ for (const file of files) {
79
+ console.log(`Exported ${file.sourcePath} -> ${file.outputPath}`);
80
+ }
81
+ };
82
+
83
+ const printMissingThreadWarnings = (missingThreadIds: string[]): void => {
84
+ if (missingThreadIds.length > 0) {
85
+ console.warn(
86
+ `Warning: ${missingThreadIds.length} requested thread(s) were not found: ${missingThreadIds.join(', ')}`,
87
+ );
88
+ }
89
+ };
90
+
91
+ const maybeOpenExportFolder = async (targetFolder: string): Promise<void> => {
92
+ const rl = createInterface({ input, output });
93
+ try {
94
+ while (true) {
95
+ const answer = (await rl.question('Open the exported folder now? [y/N]: ')).trim().toLowerCase();
96
+ if (!answer || answer === 'n' || answer === 'no') {
97
+ return;
98
+ }
99
+ if (answer === 'y' || answer === 'yes') {
100
+ await openPathNatively(targetFolder);
101
+ return;
102
+ }
103
+ console.log('Please answer y or n.');
104
+ }
105
+ } finally {
106
+ rl.close();
107
+ }
108
+ };
109
+
110
+ const resolveExportFolder = async (targetPath: string): Promise<string> => {
111
+ const stats = await lstat(targetPath).catch(() => null);
112
+ if (stats?.isDirectory()) {
113
+ return targetPath;
114
+ }
115
+ return path.dirname(targetPath);
116
+ };
117
+
118
+ const openPathNatively = async (targetPath: string): Promise<void> => {
119
+ const command = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'explorer' : 'xdg-open';
120
+
121
+ const proc = Bun.spawn([command, targetPath], {
122
+ stderr: 'pipe',
123
+ stdout: 'ignore',
124
+ });
125
+ const exitCode = await proc.exited;
126
+ if (exitCode !== 0) {
127
+ const errorText = await new Response(proc.stderr).text();
128
+ throw new Error(`Failed to open ${targetPath} with ${command}: ${errorText.trim() || `exit code ${exitCode}`}`);
129
+ }
130
+ };
131
+
132
+ if (import.meta.main) {
133
+ await runExportChatsCli();
134
+ }
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { getClaudeHelpText, parseClaudeCliArgs, runClaudeExport } from './lib/claude-exporter';
4
+ import { CliUsageError } from './lib/shared';
5
+
6
+ export const runExportClaudeCli = async (argv = process.argv.slice(2)): Promise<void> => {
7
+ if (argv.includes('--help') || argv.includes('-h')) {
8
+ console.log(getClaudeHelpText());
9
+ return;
10
+ }
11
+
12
+ try {
13
+ const options = parseClaudeCliArgs(argv);
14
+ const result = await runClaudeExport(options);
15
+
16
+ console.log(`Exported ${result.sourcePath} -> ${result.outputPath}`);
17
+ } catch (error) {
18
+ if (error instanceof CliUsageError) {
19
+ console.error(error.message);
20
+ console.error('');
21
+ console.error(getClaudeHelpText());
22
+ process.exit(1);
23
+ }
24
+
25
+ if (error instanceof Error) {
26
+ console.error(error.message);
27
+ process.exit(1);
28
+ }
29
+
30
+ throw error;
31
+ }
32
+ };
33
+
34
+ if (import.meta.main) {
35
+ await runExportClaudeCli();
36
+ }