@liquidmetal-ai/raindrop-code 0.0.1-alpha20
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 +158 -0
- package/install.js +230 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Raindrop Code
|
|
2
|
+
|
|
3
|
+
> AI-powered terminal coding assistant with deep code understanding
|
|
4
|
+
|
|
5
|
+
Raindrop Code is an intelligent coding assistant that runs in your terminal, combining the power of large language models (Claude, GPT, and compatible APIs) with deep codebase understanding, file operations, git integration, and an extensible tool ecosystem.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- š§ **Deep Code Understanding** - Automatically indexes your codebase using tree-sitter for intelligent search and context-aware assistance
|
|
10
|
+
- š ļø **Rich Tool Ecosystem** - File operations, git, bash, web search, and more - all accessible to the AI
|
|
11
|
+
- š¤ **Specialized Agents** - Spawn sub-agents for code review, refactoring, and custom tasks
|
|
12
|
+
- ā” **Fast & Responsive** - Incremental indexing with real-time file watching
|
|
13
|
+
- šØ **Modern Terminal UI** - Clean interface with syntax highlighting and markdown rendering
|
|
14
|
+
- š **MCP Support** - Integrates with Model Context Protocol servers
|
|
15
|
+
- š **Multi-Provider** - Works with Anthropic (Claude), OpenAI (GPT), and OpenAI-compatible APIs
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g @liquidmetal-ai/raindrop-code
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
### 1. Set up API keys
|
|
26
|
+
|
|
27
|
+
Set at least one API key as an environment variable:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Anthropic Claude (recommended)
|
|
31
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
32
|
+
|
|
33
|
+
# OpenAI GPT
|
|
34
|
+
export OPENAI_API_KEY=sk-...
|
|
35
|
+
|
|
36
|
+
# Optional: Enable web search
|
|
37
|
+
export BRAVE_SEARCH_API_KEY=...
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. Run in your project directory
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cd my-project
|
|
44
|
+
raindrop-code
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 3. Start coding!
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
> Explain how authentication works in this codebase
|
|
51
|
+
> Find all TODO comments
|
|
52
|
+
> Create a new API endpoint for user registration
|
|
53
|
+
> Review src/handlers/user.go for potential bugs
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Configuration
|
|
57
|
+
|
|
58
|
+
On first run, Raindrop Code creates `~/.raindrop-code/` with configuration files. Edit `~/.raindrop-code/config.yaml` to customize behavior.
|
|
59
|
+
|
|
60
|
+
### Example Configuration
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
# Which provider to use by default
|
|
64
|
+
default_provider: anthropic
|
|
65
|
+
|
|
66
|
+
providers:
|
|
67
|
+
anthropic:
|
|
68
|
+
connection:
|
|
69
|
+
api_key: env:ANTHROPIC_API_KEY
|
|
70
|
+
base_url: "https://api.anthropic.com/v1"
|
|
71
|
+
model:
|
|
72
|
+
default: claude-sonnet-4-5-20250929
|
|
73
|
+
configs:
|
|
74
|
+
claude-sonnet-4-5-20250929:
|
|
75
|
+
context_window: 200000
|
|
76
|
+
params:
|
|
77
|
+
max_tokens: 8192
|
|
78
|
+
temperature: 1.0
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Available Tools
|
|
82
|
+
|
|
83
|
+
The AI has access to these tools:
|
|
84
|
+
|
|
85
|
+
| Tool | Description |
|
|
86
|
+
|------|-------------|
|
|
87
|
+
| `read_file` | Read any file in your project |
|
|
88
|
+
| `write_file` | Create new files |
|
|
89
|
+
| `edit_file` | Make precise edits to existing files |
|
|
90
|
+
| `bash` | Execute shell commands |
|
|
91
|
+
| `git` | Run git operations (status, diff, commit, etc.) |
|
|
92
|
+
| `search` | Intelligent code search with symbol understanding |
|
|
93
|
+
| `explore` | Browse directories, see file structure and symbols |
|
|
94
|
+
| `web_search` | Search the web (requires BRAVE_SEARCH_API_KEY) |
|
|
95
|
+
| `web_fetch` | Fetch content from URLs |
|
|
96
|
+
| `agent_tool` | Spawn specialized sub-agents |
|
|
97
|
+
| `workflow` | Execute multi-step workflows |
|
|
98
|
+
|
|
99
|
+
## Commands
|
|
100
|
+
|
|
101
|
+
| Command | Description |
|
|
102
|
+
|---------|-------------|
|
|
103
|
+
| `/clear` | Clear conversation history |
|
|
104
|
+
| `/context` | Toggle context panel (shows indexing status) |
|
|
105
|
+
| `/help` | Show available commands |
|
|
106
|
+
| `/reindex` | Manually trigger workspace reindexing |
|
|
107
|
+
| `/readme` | Display README |
|
|
108
|
+
|
|
109
|
+
## Custom Commands & Agents
|
|
110
|
+
|
|
111
|
+
Create custom commands in `~/.raindrop-code/commands/`:
|
|
112
|
+
|
|
113
|
+
```markdown
|
|
114
|
+
---
|
|
115
|
+
description: Run tests and fix any failures
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
Please run the test suite and fix any failures you find.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Create custom agents in `~/.raindrop-code/agents/`:
|
|
122
|
+
|
|
123
|
+
```markdown
|
|
124
|
+
---
|
|
125
|
+
name: security-auditor
|
|
126
|
+
description: Security audit specialist
|
|
127
|
+
tools: [read_file, search, bash]
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
You are a security expert. Review code for vulnerabilities...
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Platform Support
|
|
134
|
+
|
|
135
|
+
- **macOS** (Apple Silicon & Intel)
|
|
136
|
+
- **Linux** (x64 & ARM64)
|
|
137
|
+
- **Windows** (x64)
|
|
138
|
+
|
|
139
|
+
## Documentation
|
|
140
|
+
|
|
141
|
+
- [Full Documentation](https://github.com/ianschenck/raindrop-code)
|
|
142
|
+
- [Configuration Guide](https://github.com/ianschenck/raindrop-code#configuration)
|
|
143
|
+
- [Custom Agents](https://github.com/ianschenck/raindrop-code#agents)
|
|
144
|
+
- [API Reference](https://github.com/ianschenck/raindrop-code/wiki)
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT License - see LICENSE file for details
|
|
149
|
+
|
|
150
|
+
## Links
|
|
151
|
+
|
|
152
|
+
- [GitHub Repository](https://github.com/ianschenck/raindrop-code)
|
|
153
|
+
- [Report Issues](https://github.com/ianschenck/raindrop-code/issues)
|
|
154
|
+
- [LiquidMetal.AI](https://liquidmetal.ai)
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
Built with ā¤ļø by the Raindrop team
|
package/install.js
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Postinstall script for @liquidmetal-ai/raindrop-code
|
|
5
|
+
*
|
|
6
|
+
* This script ensures the correct platform-specific binary is available.
|
|
7
|
+
* It follows a two-tier approach:
|
|
8
|
+
* 1. First, check if a platform-specific optional dependency was installed
|
|
9
|
+
* 2. If not, download the binary from npm as a fallback
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const https = require('https');
|
|
15
|
+
const { execSync } = require('child_process');
|
|
16
|
+
|
|
17
|
+
// Get package version dynamically from package.json
|
|
18
|
+
const packageJson = require('./package.json');
|
|
19
|
+
const VERSION = packageJson.version;
|
|
20
|
+
|
|
21
|
+
// Lookup table for platform-specific packages
|
|
22
|
+
const BINARY_DISTRIBUTION_PACKAGES = {
|
|
23
|
+
'darwin-x64': '@liquidmetal-ai/raindrop-code-darwin-universal',
|
|
24
|
+
'darwin-arm64': '@liquidmetal-ai/raindrop-code-darwin-universal',
|
|
25
|
+
'linux-x64': '@liquidmetal-ai/raindrop-code-linux-x64',
|
|
26
|
+
'linux-arm64': '@liquidmetal-ai/raindrop-code-linux-arm64',
|
|
27
|
+
'win32-x64': '@liquidmetal-ai/raindrop-code-win32-x64',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Determine binary name based on platform
|
|
31
|
+
const binaryName = process.platform === 'win32' ? 'raindrop-code.exe' : 'raindrop-code';
|
|
32
|
+
|
|
33
|
+
// Paths
|
|
34
|
+
const binDir = path.join(__dirname, 'bin');
|
|
35
|
+
const fallbackBinaryPath = path.join(binDir, binaryName);
|
|
36
|
+
|
|
37
|
+
// Determine the platform-specific package name
|
|
38
|
+
const platformKey = `${process.platform}-${process.arch}`;
|
|
39
|
+
const platformSpecificPackageName = BINARY_DISTRIBUTION_PACKAGES[platformKey];
|
|
40
|
+
|
|
41
|
+
if (!platformSpecificPackageName) {
|
|
42
|
+
console.error(`ERROR: Unsupported platform: ${platformKey}`);
|
|
43
|
+
console.error(`Supported platforms: ${Object.keys(BINARY_DISTRIBUTION_PACKAGES).join(', ')}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Check if the optional dependency was installed successfully
|
|
49
|
+
*/
|
|
50
|
+
function checkOptionalDependency() {
|
|
51
|
+
try {
|
|
52
|
+
const optionalDepPath = path.join(__dirname, 'node_modules', platformSpecificPackageName);
|
|
53
|
+
const binaryPath = path.join(optionalDepPath, 'bin', binaryName);
|
|
54
|
+
|
|
55
|
+
if (fs.existsSync(binaryPath)) {
|
|
56
|
+
console.log(`ā Platform-specific package installed: ${platformSpecificPackageName}`);
|
|
57
|
+
|
|
58
|
+
// Create bin directory if it doesn't exist
|
|
59
|
+
if (!fs.existsSync(binDir)) {
|
|
60
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Create a symlink or copy to the expected location
|
|
64
|
+
if (process.platform === 'win32') {
|
|
65
|
+
// Windows doesn't handle symlinks well, so copy
|
|
66
|
+
fs.copyFileSync(binaryPath, fallbackBinaryPath);
|
|
67
|
+
} else {
|
|
68
|
+
// Unix: create symlink
|
|
69
|
+
const relativePath = path.relative(binDir, binaryPath);
|
|
70
|
+
if (fs.existsSync(fallbackBinaryPath)) {
|
|
71
|
+
fs.unlinkSync(fallbackBinaryPath);
|
|
72
|
+
}
|
|
73
|
+
fs.symlinkSync(relativePath, fallbackBinaryPath);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Ensure executable
|
|
77
|
+
if (process.platform !== 'win32') {
|
|
78
|
+
fs.chmodSync(fallbackBinaryPath, 0o755);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
// Ignore errors, fall through to download
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Make an HTTPS request following redirects
|
|
92
|
+
*/
|
|
93
|
+
function makeRequest(url) {
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
https.get(url, (response) => {
|
|
96
|
+
if (response.statusCode >= 200 && response.statusCode < 300) {
|
|
97
|
+
const chunks = [];
|
|
98
|
+
response.on('data', (chunk) => chunks.push(chunk));
|
|
99
|
+
response.on('end', () => resolve(Buffer.concat(chunks)));
|
|
100
|
+
} else if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
101
|
+
// Follow redirects
|
|
102
|
+
makeRequest(response.headers.location).then(resolve, reject);
|
|
103
|
+
} else {
|
|
104
|
+
reject(new Error(`Request failed with status ${response.statusCode}`));
|
|
105
|
+
}
|
|
106
|
+
}).on('error', reject);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Extract a specific file from a tarball buffer
|
|
112
|
+
*/
|
|
113
|
+
function extractFileFromTarball(tarballBuffer, filepath) {
|
|
114
|
+
let offset = 0;
|
|
115
|
+
|
|
116
|
+
while (offset < tarballBuffer.length) {
|
|
117
|
+
const header = tarballBuffer.subarray(offset, offset + 512);
|
|
118
|
+
offset += 512;
|
|
119
|
+
|
|
120
|
+
const fileName = header.toString('utf-8', 0, 100).replace(/\0.*/g, '');
|
|
121
|
+
const fileSize = parseInt(header.toString('utf-8', 124, 136).replace(/\0.*/g, ''), 8);
|
|
122
|
+
|
|
123
|
+
if (fileName === filepath) {
|
|
124
|
+
return tarballBuffer.subarray(offset, offset + fileSize);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Move to next header (data is padded to 512 byte blocks)
|
|
128
|
+
offset += Math.ceil(fileSize / 512) * 512;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
throw new Error(`File ${filepath} not found in tarball`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Download the platform-specific binary from npm
|
|
136
|
+
*/
|
|
137
|
+
async function downloadBinary() {
|
|
138
|
+
console.log(`Downloading binary for ${platformKey}...`);
|
|
139
|
+
console.log(`Package: ${platformSpecificPackageName}@${VERSION}`);
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
// Construct npm registry URL
|
|
143
|
+
const tarballUrl = `https://registry.npmjs.org/${platformSpecificPackageName}/-/${platformSpecificPackageName.split('/').pop()}-${VERSION}.tgz`;
|
|
144
|
+
|
|
145
|
+
console.log(`Fetching: ${tarballUrl}`);
|
|
146
|
+
|
|
147
|
+
// Download the tarball
|
|
148
|
+
const tarballBuffer = await makeRequest(tarballUrl);
|
|
149
|
+
console.log(`ā Downloaded ${(tarballBuffer.length / 1024 / 1024).toFixed(2)} MB`);
|
|
150
|
+
|
|
151
|
+
// Decompress gzip
|
|
152
|
+
const zlib = require('zlib');
|
|
153
|
+
const tarBuffer = zlib.gunzipSync(tarballBuffer);
|
|
154
|
+
|
|
155
|
+
// Extract binary from tar (npm packages have a 'package/' prefix)
|
|
156
|
+
const binaryPathInTar = `package/bin/${binaryName}`;
|
|
157
|
+
const binaryBuffer = extractFileFromTarball(tarBuffer, binaryPathInTar);
|
|
158
|
+
|
|
159
|
+
// Write binary to disk
|
|
160
|
+
if (!fs.existsSync(binDir)) {
|
|
161
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
fs.writeFileSync(fallbackBinaryPath, binaryBuffer);
|
|
165
|
+
|
|
166
|
+
// Make executable on Unix
|
|
167
|
+
if (process.platform !== 'win32') {
|
|
168
|
+
fs.chmodSync(fallbackBinaryPath, 0o755);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
console.log(`ā Binary installed to ${fallbackBinaryPath}`);
|
|
172
|
+
return true;
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error(`ā Failed to download binary: ${error.message}`);
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Verify the binary works
|
|
181
|
+
*/
|
|
182
|
+
function verifyBinary() {
|
|
183
|
+
try {
|
|
184
|
+
const output = execSync(`"${fallbackBinaryPath}" --version`, {
|
|
185
|
+
encoding: 'utf-8',
|
|
186
|
+
stdio: ['ignore', 'pipe', 'ignore']
|
|
187
|
+
});
|
|
188
|
+
console.log(`ā Binary verified: ${output.trim()}`);
|
|
189
|
+
return true;
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error(`ā Binary verification failed`);
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Main installation logic
|
|
198
|
+
*/
|
|
199
|
+
async function main() {
|
|
200
|
+
console.log('raindrop-code postinstall');
|
|
201
|
+
console.log('========================');
|
|
202
|
+
|
|
203
|
+
// Strategy 1: Check if optional dependency was installed
|
|
204
|
+
if (checkOptionalDependency()) {
|
|
205
|
+
console.log('ā Installation complete (via optional dependency)');
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
console.log('Optional dependency not found, downloading binary...');
|
|
210
|
+
|
|
211
|
+
// Strategy 2: Download binary from npm
|
|
212
|
+
const downloadSuccess = await downloadBinary();
|
|
213
|
+
|
|
214
|
+
if (!downloadSuccess) {
|
|
215
|
+
console.error('\nā Installation incomplete');
|
|
216
|
+
console.error('Please report this issue: https://github.com/ianschenck/raindrop-code/issues');
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Verify the binary
|
|
221
|
+
verifyBinary();
|
|
222
|
+
|
|
223
|
+
console.log('ā Installation complete (via download)');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Run installation
|
|
227
|
+
main().catch((error) => {
|
|
228
|
+
console.error('Installation failed:', error);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@liquidmetal-ai/raindrop-code",
|
|
3
|
+
"version": "0.0.1-alpha20",
|
|
4
|
+
"description": "AI-powered terminal coding assistant with deep code understanding, file operations, and extensible tools",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"coding-assistant",
|
|
8
|
+
"code-assistant",
|
|
9
|
+
"cli",
|
|
10
|
+
"terminal",
|
|
11
|
+
"developer-tools",
|
|
12
|
+
"llm",
|
|
13
|
+
"claude",
|
|
14
|
+
"gpt",
|
|
15
|
+
"code-analysis",
|
|
16
|
+
"refactoring",
|
|
17
|
+
"code-review"
|
|
18
|
+
],
|
|
19
|
+
"homepage": "https://github.com/ianschenck/raindrop-code#readme",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/ianschenck/raindrop-code.git"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/ianschenck/raindrop-code/issues"
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"author": "LiquidMetal.AI",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"postinstall": "node install.js"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"bin": {
|
|
36
|
+
"raindrop-code": "bin/raindrop-code"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"install.js",
|
|
40
|
+
"README.md",
|
|
41
|
+
"bin/"
|
|
42
|
+
],
|
|
43
|
+
"optionalDependencies": {
|
|
44
|
+
"@liquidmetal-ai/raindrop-code-darwin-universal": "0.0.1-alpha20",
|
|
45
|
+
"@liquidmetal-ai/raindrop-code-linux-x64": "0.0.1-alpha20",
|
|
46
|
+
"@liquidmetal-ai/raindrop-code-linux-arm64": "0.0.1-alpha20",
|
|
47
|
+
"@liquidmetal-ai/raindrop-code-win32-x64": "0.0.1-alpha20"
|
|
48
|
+
}
|
|
49
|
+
}
|