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.
- package/README.md +134 -46
- package/index.js +210 -20
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,46 +1,52 @@
|
|
|
1
|
-
#
|
|
1
|
+
# DATA Tools
|
|
2
2
|
|
|
3
|
-
**
|
|
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
|
|
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
|
-
##
|
|
7
|
+
## Quick Start
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
13
|
+
That's it. The installer will:
|
|
17
14
|
|
|
18
|
-
|
|
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
|
-
|
|
21
|
-
npx replit-claude-persist
|
|
22
|
-
```
|
|
21
|
+
## What Gets Installed
|
|
23
22
|
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
|
|
30
|
+
## What Gets Persisted
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
##
|
|
41
|
+
## The Session Picker
|
|
38
42
|
|
|
39
|
-
After installation
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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-
|
|
90
|
-
| `claude-
|
|
91
|
-
| `claude-
|
|
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
|
|
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/ #
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
|
20
|
+
console.log('│ Replit Tools - Claude & Codex Persistence │');
|
|
19
21
|
console.log('╰─────────────────────────────────────────────────────────╯');
|
|
20
22
|
console.log('');
|
|
21
23
|
|
|
22
|
-
//
|
|
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('
|
|
126
|
-
console.log('');
|
|
127
|
-
console.log('
|
|
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('
|
|
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
|
-
|
|
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.
|
|
4
|
-
"description": "
|
|
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"
|