replit-tools 1.0.1 → 1.0.3

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 (3) hide show
  1. package/README.md +134 -46
  2. package/index.js +210 -20
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,46 +1,52 @@
1
- # replit-claude-persist
1
+ # DATA Tools
2
2
 
3
- **Persist Claude Code sessions, authentication, and history across Replit container restarts.**
3
+ **One command to set up Claude Code and Codex CLI on Replit with full persistence.**
4
4
 
5
- When Replit containers restart, everything outside `/home/runner/workspace/` is wiped - including your Claude conversations, auth tokens, and installed binaries. This tool fixes that.
5
+ When Replit containers restart, everything outside `/home/runner/workspace/` is wiped - including installed CLIs, conversations, auth tokens, and command history. DATA Tools fixes all of that.
6
6
 
7
- ## Features
7
+ ## Quick Start
8
8
 
9
- - **Session Persistence** - Conversations survive container restarts
10
- - **Interactive Session Picker** - Choose which session to resume on shell start
11
- - **Multi-Terminal Support** - Each terminal tracks its own session
12
- - **Auth Persistence** - Keep your Claude authentication working
13
- - **Binary Caching** - Claude binary persists (faster startup)
14
- - **Bash History** - Command history survives restarts too
9
+ ```bash
10
+ npx replit-tools
11
+ ```
15
12
 
16
- ## Installation
13
+ That's it. The installer will:
17
14
 
18
- ### Option 1: npx (easiest)
15
+ 1. **Install Claude Code** (if not already installed)
16
+ 2. **Install OpenAI Codex CLI** (if not already installed)
17
+ 3. **Detect existing config** and preserve your data
18
+ 4. **Set up persistence** so everything survives restarts
19
+ 5. **Launch the session picker** so you can start working immediately
19
20
 
20
- ```bash
21
- npx replit-claude-persist
22
- ```
21
+ ## What Gets Installed
23
22
 
24
- ### Option 2: curl
23
+ | Tool | Source | Purpose |
24
+ |------|--------|---------|
25
+ | **Claude Code** | `curl https://claude.ai/install.sh` | Anthropic's CLI for Claude |
26
+ | **Codex CLI** | `npm i -g @openai/codex` | OpenAI's coding assistant |
25
27
 
26
- ```bash
27
- curl -fsSL https://raw.githubusercontent.com/stevemoraco/DATAtools/main/install.sh | bash
28
- ```
28
+ Both are installed only if not already present. Existing installations are preserved.
29
29
 
30
- ### Option 3: Manual
30
+ ## What Gets Persisted
31
31
 
32
- ```bash
33
- npm install -g replit-claude-persist
34
- replit-claude-persist
35
- ```
32
+ | Data | Location | Survives Restart? |
33
+ |------|----------|-------------------|
34
+ | Claude conversations | `.claude-persistent/` | Yes |
35
+ | Claude credentials | `.claude-persistent/` | Yes |
36
+ | Claude binary | `.local/share/claude/versions/` | Yes |
37
+ | Codex data | `.codex-persistent/` | Yes |
38
+ | Bash history | `.persistent-home/` | Yes |
39
+ | Per-terminal sessions | `.claude-sessions/` | Yes |
36
40
 
37
- ## What You'll See
41
+ ## The Session Picker
38
42
 
39
- After installation, opening a new shell shows:
43
+ After installation (and on every new shell), you'll see:
40
44
 
41
45
  ```
46
+ ✅ Claude Code already installed (1.0.17)
47
+ ✅ Codex CLI already installed
48
+
42
49
  ✅ Claude authentication: valid (23h remaining)
43
- ✅ Claude Code ready: 2.0.71 (Claude Code)
44
50
 
45
51
  ╭─────────────────────────────────────────────────────────╮
46
52
  │ Claude Session Manager │
@@ -56,7 +62,9 @@ After installation, opening a new shell shows:
56
62
  Choice [c/r/n/s]: _
57
63
  ```
58
64
 
59
- Press `r` to see detailed session info:
65
+ ### Session Details
66
+
67
+ Press `r` to see full session metadata:
60
68
 
61
69
  ```
62
70
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -69,30 +77,85 @@ Press `r` to see detailed session info:
69
77
  Latest: "Thanks, that worked!"
70
78
  ```
71
79
 
80
+ Each session shows:
81
+ - Full UUID
82
+ - Message count and file size
83
+ - Time since last activity
84
+ - Start and last activity timestamps
85
+ - First and latest prompts (truncated)
86
+
87
+ ## Multi-Terminal Support
88
+
89
+ Each terminal tracks its own session independently:
90
+
91
+ ```
92
+ Terminal pts/0 → Session abc123... (your migration work)
93
+ Terminal pts/1 → Session def456... (your docs work)
94
+ Terminal pts/2 → Session ghi789... (your debugging)
95
+ ```
96
+
97
+ Press `c` to continue YOUR terminal's last session. Other terminals are unaffected.
98
+
72
99
  ## How It Works
73
100
 
74
- The tool creates symlinks from ephemeral locations to persistent workspace storage:
101
+ The installer creates symlinks from ephemeral locations to persistent workspace storage:
102
+
103
+ ```
104
+ ~/.claude → /workspace/.claude-persistent/
105
+ ~/.codex → /workspace/.codex-persistent/
106
+ ~/.local/share/claude → /workspace/.local/share/claude/
107
+ ~/.local/bin/claude → /workspace/.local/share/claude/versions/X.X.X
108
+ ```
109
+
110
+ Three layers ensure setup runs on every restart:
111
+ 1. `.replit` onBoot hook (runs at container boot)
112
+ 2. `.config/bashrc` (runs on every shell start)
113
+ 3. Scripts in `workspace/scripts/` (called by above)
114
+
115
+ ## Smart Detection
116
+
117
+ The installer checks for:
118
+
119
+ - **Existing persistent config** - Uses your existing `.claude-persistent/` if present
120
+ - **Replit Secrets** - Detects `ANTHROPIC_API_KEY` and `OPENAI_API_KEY`
121
+ - **Existing installations** - Won't reinstall Claude or Codex if already present
122
+ - **Existing data in ~/.claude** - Moves it to persistent storage instead of overwriting
75
123
 
124
+ ## Installation Options
125
+
126
+ ### Option 1: npx (recommended)
127
+
128
+ ```bash
129
+ npx replit-tools
76
130
  ```
77
- ~/.claude → /workspace/.claude-persistent/
78
- ~/.codex → /workspace/.codex-persistent/
79
- ~/.local/bin/ → /workspace/.local/share/claude/versions/
131
+
132
+ ### Option 2: curl
133
+
134
+ ```bash
135
+ curl -fsSL https://raw.githubusercontent.com/stevemoraco/DATAtools/main/install.sh | bash
80
136
  ```
81
137
 
82
- On every shell start, `.config/bashrc` ensures symlinks exist and shows the session picker.
138
+ ### Option 3: Global install
139
+
140
+ ```bash
141
+ npm install -g replit-tools
142
+ replit-tools
143
+ ```
83
144
 
84
145
  ## Commands
85
146
 
147
+ After installation, these aliases are available:
148
+
86
149
  | Command | Description |
87
150
  |---------|-------------|
88
- | `cr` | Continue last session |
89
- | `claude-menu` | Show session picker again |
90
- | `claude-new` | Start fresh session |
91
- | `claude-pick` | Claude's built-in picker |
151
+ | `cr` | Continue last Claude session |
152
+ | `claude-resume` | Same as `cr` |
153
+ | `claude-pick` | Claude's built-in session picker |
154
+ | `claude-menu` | Show the session manager menu again |
92
155
 
93
156
  ## Configuration
94
157
 
95
- ### Disable the menu
158
+ ### Disable the session picker menu
96
159
 
97
160
  ```bash
98
161
  export CLAUDE_NO_PROMPT=true
@@ -100,32 +163,39 @@ export CLAUDE_NO_PROMPT=true
100
163
 
101
164
  Add to `.config/bashrc` to make permanent.
102
165
 
103
- ### Fix auth permanently
166
+ ### Fix authentication permanently
104
167
 
105
168
  ```bash
106
169
  claude setup-token
107
170
  ```
108
171
 
109
- Creates a long-lived token that doesn't expire.
172
+ Creates a long-lived API token that doesn't expire (OAuth tokens expire every ~24h).
110
173
 
111
174
  ## Files Created
112
175
 
113
176
  ```
114
177
  workspace/
115
- ├── .claude-persistent/ # Conversations, credentials
178
+ ├── .claude-persistent/ # Claude conversations & credentials
116
179
  ├── .codex-persistent/ # Codex CLI data
117
180
  ├── .claude-sessions/ # Per-terminal session tracking
118
181
  ├── .local/share/claude/ # Claude binary versions
119
182
  ├── .persistent-home/ # Bash history
120
183
  ├── .config/bashrc # Shell startup config
121
- └── scripts/
122
- ├── setup-claude-code.sh
123
- └── claude-session-manager.sh
184
+ ├── scripts/
185
+ ├── setup-claude-code.sh
186
+ └── claude-session-manager.sh
187
+ └── .gitignore # Updated to ignore credential dirs
124
188
  ```
125
189
 
126
190
  ## Troubleshooting
127
191
 
128
- ### Menu not appearing
192
+ ### Claude or Codex not found after restart
193
+
194
+ ```bash
195
+ source /home/runner/workspace/scripts/setup-claude-code.sh
196
+ ```
197
+
198
+ ### Session picker not appearing
129
199
 
130
200
  ```bash
131
201
  source /home/runner/workspace/.config/bashrc
@@ -142,9 +212,27 @@ claude setup-token
142
212
  ### Symlinks broken
143
213
 
144
214
  ```bash
145
- source /home/runner/workspace/scripts/setup-claude-code.sh
215
+ npx replit-tools
146
216
  ```
147
217
 
218
+ Running the installer again is safe - it preserves existing data.
219
+
220
+ ## Security
221
+
222
+ The installer adds these to `.gitignore`:
223
+ - `.claude-persistent/` (contains credentials)
224
+ - `.codex-persistent/` (contains credentials)
225
+
226
+ Your API keys and conversation history won't be committed to git.
227
+
228
+ ## Why "DATA Tools"?
229
+
230
+ **D**eveloper **A**ssistant **T**ool **A**utomation - everything you need to run AI coding assistants on Replit, persisted and ready to go.
231
+
232
+ ## Repository
233
+
234
+ GitHub: [stevemoraco/DATAtools](https://github.com/stevemoraco/DATAtools)
235
+
148
236
  ## License
149
237
 
150
238
  MIT
package/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { execSync, spawn } = require('child_process');
3
+ const { execSync, spawn, spawnSync } = require('child_process');
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const os = require('os');
6
7
 
7
8
  const WORKSPACE = '/home/runner/workspace';
9
+ const HOME = os.homedir();
8
10
 
9
11
  // Check if we're on Replit
10
12
  if (!fs.existsSync(WORKSPACE)) {
@@ -15,11 +17,45 @@ if (!fs.existsSync(WORKSPACE)) {
15
17
 
16
18
  console.log('');
17
19
  console.log('╭─────────────────────────────────────────────────────────╮');
18
- console.log('│ Replit Claude Persistence Installer │');
20
+ console.log('│ Replit Tools - Claude & Codex Persistence │');
19
21
  console.log('╰─────────────────────────────────────────────────────────╯');
20
22
  console.log('');
21
23
 
22
- // Create directories
24
+ // Helper to check if command exists
25
+ function commandExists(cmd) {
26
+ try {
27
+ execSync(`which ${cmd}`, { stdio: 'pipe' });
28
+ return true;
29
+ } catch {
30
+ return false;
31
+ }
32
+ }
33
+
34
+ // Helper to check for Replit secret
35
+ function hasReplitSecret(name) {
36
+ return process.env[name] !== undefined;
37
+ }
38
+
39
+ // Check for existing persistent config
40
+ const existingClaudeConfig = fs.existsSync(path.join(WORKSPACE, '.claude-persistent'));
41
+ const existingCodexConfig = fs.existsSync(path.join(WORKSPACE, '.codex-persistent'));
42
+
43
+ if (existingClaudeConfig) {
44
+ console.log('✅ Found existing Claude config in workspace');
45
+ }
46
+ if (existingCodexConfig) {
47
+ console.log('✅ Found existing Codex config in workspace');
48
+ }
49
+
50
+ // Check for Replit secrets
51
+ if (hasReplitSecret('ANTHROPIC_API_KEY')) {
52
+ console.log('✅ Found ANTHROPIC_API_KEY in Replit secrets');
53
+ }
54
+ if (hasReplitSecret('OPENAI_API_KEY')) {
55
+ console.log('✅ Found OPENAI_API_KEY in Replit secrets');
56
+ }
57
+
58
+ // Create directories (preserving existing data)
23
59
  const dirs = [
24
60
  '.claude-persistent',
25
61
  '.codex-persistent',
@@ -31,6 +67,7 @@ const dirs = [
31
67
  'logs'
32
68
  ];
33
69
 
70
+ console.log('');
34
71
  console.log('📁 Creating directories...');
35
72
  dirs.forEach(dir => {
36
73
  const fullPath = path.join(WORKSPACE, dir);
@@ -39,13 +76,146 @@ dirs.forEach(dir => {
39
76
  }
40
77
  });
41
78
 
79
+ // Check and install Claude Code if needed
80
+ console.log('');
81
+ const claudeInstalled = commandExists('claude') ||
82
+ fs.existsSync(path.join(HOME, '.local/bin/claude')) ||
83
+ fs.existsSync(path.join(WORKSPACE, '.local/share/claude/versions'));
84
+
85
+ let claudeVersions = [];
86
+ try {
87
+ claudeVersions = fs.readdirSync(path.join(WORKSPACE, '.local/share/claude/versions'));
88
+ } catch {}
89
+
90
+ if (!claudeInstalled || claudeVersions.length === 0) {
91
+ console.log('📦 Installing Claude Code...');
92
+ try {
93
+ execSync('curl -fsSL https://claude.ai/install.sh | bash', {
94
+ stdio: 'inherit',
95
+ shell: '/bin/bash'
96
+ });
97
+ console.log('✅ Claude Code installed');
98
+ } catch (err) {
99
+ console.log('⚠️ Claude Code installation failed (you can install manually later)');
100
+ }
101
+ } else {
102
+ const version = claudeVersions.sort().pop() || 'unknown';
103
+ console.log(`✅ Claude Code already installed (${version})`);
104
+ }
105
+
106
+ // Check and install Codex if needed
107
+ const codexInstalled = commandExists('codex');
108
+
109
+ if (!codexInstalled) {
110
+ console.log('📦 Installing OpenAI Codex CLI...');
111
+ try {
112
+ execSync('npm i -g @openai/codex', {
113
+ stdio: 'inherit',
114
+ shell: '/bin/bash'
115
+ });
116
+ console.log('✅ Codex CLI installed');
117
+ } catch (err) {
118
+ console.log('⚠️ Codex installation failed (you can install manually later)');
119
+ }
120
+ } else {
121
+ console.log('✅ Codex CLI already installed');
122
+ }
123
+
124
+ // Set up symlinks
125
+ console.log('');
126
+ console.log('🔗 Setting up symlinks...');
127
+
128
+ // Claude symlink
129
+ const claudeTarget = path.join(WORKSPACE, '.claude-persistent');
130
+ const claudeLink = path.join(HOME, '.claude');
131
+ try {
132
+ const stat = fs.lstatSync(claudeLink);
133
+ if (stat.isSymbolicLink()) {
134
+ const current = fs.readlinkSync(claudeLink);
135
+ if (current !== claudeTarget) {
136
+ fs.unlinkSync(claudeLink);
137
+ fs.symlinkSync(claudeTarget, claudeLink);
138
+ }
139
+ } else if (stat.isDirectory()) {
140
+ // Move existing data to persistent location
141
+ console.log(' Moving existing ~/.claude data to persistent storage...');
142
+ execSync(`cp -rn ${claudeLink}/* ${claudeTarget}/ 2>/dev/null || true`, { shell: '/bin/bash' });
143
+ execSync(`rm -rf ${claudeLink}`, { shell: '/bin/bash' });
144
+ fs.symlinkSync(claudeTarget, claudeLink);
145
+ }
146
+ } catch {
147
+ // Doesn't exist, create it
148
+ fs.symlinkSync(claudeTarget, claudeLink);
149
+ }
150
+ console.log(' ~/.claude → .claude-persistent/');
151
+
152
+ // Codex symlink
153
+ const codexTarget = path.join(WORKSPACE, '.codex-persistent');
154
+ const codexLink = path.join(HOME, '.codex');
155
+ try {
156
+ const stat = fs.lstatSync(codexLink);
157
+ if (stat.isSymbolicLink()) {
158
+ const current = fs.readlinkSync(codexLink);
159
+ if (current !== codexTarget) {
160
+ fs.unlinkSync(codexLink);
161
+ fs.symlinkSync(codexTarget, codexLink);
162
+ }
163
+ } else if (stat.isDirectory()) {
164
+ console.log(' Moving existing ~/.codex data to persistent storage...');
165
+ execSync(`cp -rn ${codexLink}/* ${codexTarget}/ 2>/dev/null || true`, { shell: '/bin/bash' });
166
+ execSync(`rm -rf ${codexLink}`, { shell: '/bin/bash' });
167
+ fs.symlinkSync(codexTarget, codexLink);
168
+ }
169
+ } catch {
170
+ fs.symlinkSync(codexTarget, codexLink);
171
+ }
172
+ console.log(' ~/.codex → .codex-persistent/');
173
+
174
+ // Claude binary symlinks
175
+ const localBin = path.join(HOME, '.local/bin');
176
+ const localShare = path.join(HOME, '.local/share');
177
+ const claudeShareTarget = path.join(WORKSPACE, '.local/share/claude');
178
+
179
+ try { fs.mkdirSync(localBin, { recursive: true }); } catch {}
180
+ try { fs.mkdirSync(localShare, { recursive: true }); } catch {}
181
+
182
+ // Link .local/share/claude
183
+ try {
184
+ const stat = fs.lstatSync(path.join(localShare, 'claude'));
185
+ if (stat.isSymbolicLink()) {
186
+ const current = fs.readlinkSync(path.join(localShare, 'claude'));
187
+ if (current !== claudeShareTarget) {
188
+ fs.unlinkSync(path.join(localShare, 'claude'));
189
+ fs.symlinkSync(claudeShareTarget, path.join(localShare, 'claude'));
190
+ }
191
+ }
192
+ } catch {
193
+ try {
194
+ fs.symlinkSync(claudeShareTarget, path.join(localShare, 'claude'));
195
+ } catch {}
196
+ }
197
+ console.log(' ~/.local/share/claude → .local/share/claude/');
198
+
199
+ // Link binary to latest version
200
+ try {
201
+ const versions = fs.readdirSync(path.join(WORKSPACE, '.local/share/claude/versions')).sort();
202
+ if (versions.length > 0) {
203
+ const latest = versions[versions.length - 1];
204
+ const binaryPath = path.join(WORKSPACE, '.local/share/claude/versions', latest);
205
+ const binLink = path.join(localBin, 'claude');
206
+ try { fs.unlinkSync(binLink); } catch {}
207
+ fs.symlinkSync(binaryPath, binLink);
208
+ console.log(` ~/.local/bin/claude → versions/${latest}`);
209
+ }
210
+ } catch {}
211
+
42
212
  // Copy scripts from the package
43
213
  const scriptsDir = path.join(__dirname, 'scripts');
44
214
  const targetScriptsDir = path.join(WORKSPACE, 'scripts');
45
215
 
216
+ console.log('');
46
217
  console.log('📝 Installing scripts...');
47
218
 
48
- // Read and write each script
49
219
  const scripts = ['setup-claude-code.sh', 'claude-session-manager.sh'];
50
220
  scripts.forEach(script => {
51
221
  const srcPath = path.join(scriptsDir, script);
@@ -54,10 +224,12 @@ scripts.forEach(script => {
54
224
  if (fs.existsSync(srcPath)) {
55
225
  fs.copyFileSync(srcPath, destPath);
56
226
  fs.chmodSync(destPath, '755');
227
+ console.log(` ${script}`);
57
228
  }
58
229
  });
59
230
 
60
231
  // Create/update .config/bashrc
232
+ console.log('');
61
233
  console.log('📝 Creating .config/bashrc...');
62
234
  const bashrcContent = `#!/bin/bash
63
235
  # Replit Claude Persistence - Auto-generated bashrc
@@ -121,23 +293,41 @@ if (fs.existsSync(gitignorePath)) {
121
293
  fs.writeFileSync(gitignorePath, gitignoreEntries.trim() + '\n');
122
294
  }
123
295
 
296
+ // Add PATH to current process for session manager
297
+ process.env.PATH = `${localBin}:${process.env.PATH}`;
298
+
124
299
  console.log('');
125
- console.log('✅ Installation complete!');
126
- console.log('');
127
- console.log('What happens now:');
128
- console.log(' • New shells will show the Claude session picker');
129
- console.log(' • Your conversations persist across container restarts');
130
- console.log(' • Claude binary is cached (faster startup)');
131
- console.log(' • Bash history is preserved');
132
- console.log('');
133
- console.log('To test, open a new shell or run:');
134
- console.log(' source ~/.config/bashrc');
300
+ console.log('╭─────────────────────────────────────────────────────────╮');
301
+ console.log('│ ✅ Installation complete! │');
302
+ console.log('╰─────────────────────────────────────────────────────────╯');
135
303
  console.log('');
136
- console.log('Options:');
137
- console.log(" Press 'c' - Continue last session");
138
- console.log(" Press 'r' - Pick from session list");
139
- console.log(" Press 'n' - New session");
140
- console.log(" Press 's' - Skip (just a shell)");
304
+ console.log('Your conversations and credentials now persist across restarts.');
141
305
  console.log('');
142
- console.log('To disable the menu: export CLAUDE_NO_PROMPT=true');
306
+
307
+ // Check if Claude needs login
308
+ let needsLogin = true;
309
+ try {
310
+ const authCheck = execSync('claude auth status 2>&1 || true', { encoding: 'utf8', shell: '/bin/bash' });
311
+ if (authCheck.includes('Logged in') || authCheck.includes('valid')) {
312
+ needsLogin = false;
313
+ }
314
+ } catch {}
315
+
316
+ if (needsLogin && !hasReplitSecret('ANTHROPIC_API_KEY')) {
317
+ console.log('⚠️ Claude needs authentication. Run: claude login');
318
+ console.log('');
319
+ }
320
+
321
+ // Launch the session picker
322
+ console.log('Launching session manager...');
143
323
  console.log('');
324
+
325
+ // Use spawn to run bash interactively with our session manager
326
+ const sessionManager = spawn('bash', ['--rcfile', path.join(WORKSPACE, '.config/bashrc'), '-i'], {
327
+ stdio: 'inherit',
328
+ cwd: WORKSPACE
329
+ });
330
+
331
+ sessionManager.on('exit', (code) => {
332
+ process.exit(code || 0);
333
+ });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "replit-tools",
3
- "version": "1.0.1",
4
- "description": "Persist Claude Code sessions, auth, and history across Replit container restarts",
3
+ "version": "1.0.3",
4
+ "description": "DATA Tools - One command to set up Claude Code and Codex CLI on Replit with full persistence",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "replit-tools": "./index.js"