apex-fast 0.0.2-next.1 → 0.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/Morph-Rules.md +167 -0
- package/README.md +192 -17
- package/dist/index.js +27 -1
- package/dist/mcp-server.js +29378 -855
- package/package.json +3 -3
package/Morph-Rules.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Morph Fast Apply - AI Agent Instructions
|
|
2
|
+
|
|
3
|
+
> **What is morph_edit / fastApply?** A tool that allows you to edit files using partial code snippets tagged with `// ... existing code ...` markers. Morph's AI merges your changes into the full file at 10,500+ tokens/sec with 98% accuracy.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Choosing the Right Tool
|
|
8
|
+
|
|
9
|
+
| Situation | Tool | Reason |
|
|
10
|
+
| ----------------------------------- | ----------- | -------------------------------------------------------------------- |
|
|
11
|
+
| **Small, exact string replacement** | `edit` | **FASTEST.** No API call, immediate execution. |
|
|
12
|
+
| **Simple variable/function rename** | `edit` | Precise, no AI needed. |
|
|
13
|
+
| **Large file (300+ lines)** | `fastApply` | 10x faster, handles partial snippets without reading the whole file. |
|
|
14
|
+
| **Multiple scattered changes** | `fastApply` | Batch changes efficiently in one pass. |
|
|
15
|
+
| **Complex refactoring** | `fastApply` | AI understands context better than strict string matching. |
|
|
16
|
+
| **Whitespace-sensitive edits** | `fastApply` | Forgiving with formatting differences. |
|
|
17
|
+
|
|
18
|
+
### ❌ Anti-Patterns (When NOT to use fastApply)
|
|
19
|
+
|
|
20
|
+
- **Do NOT** use for single-line changes (e.g., changing a port number). Use `edit`.
|
|
21
|
+
- **Do NOT** use for simple typo fixes. Use `edit`.
|
|
22
|
+
- **Do NOT** use for creating brand new files. Use `write`/`create`.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Quick Reference
|
|
27
|
+
|
|
28
|
+
**IMPORTANT:** Use `fastApply` over `str_replace_editor` or full file writes. It works with partial code snippets—no need for full file content.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## CRITICAL: Omitting Markers Causes Deletions
|
|
33
|
+
|
|
34
|
+
**If you omit the `// ... existing code ...` markers, Morph will DELETE that code.**
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
// BAD - will DELETE everything before and after the function
|
|
38
|
+
function newFeature() {
|
|
39
|
+
return 'hello';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// GOOD - preserves existing code
|
|
43
|
+
// ... existing code ...
|
|
44
|
+
function newFeature() {
|
|
45
|
+
return 'hello';
|
|
46
|
+
}
|
|
47
|
+
// ... existing code ...
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Always wrap your changes with markers at the start AND end** unless you intend to replace the entire file.
|
|
51
|
+
|
|
52
|
+
### 🛡️ Pre-flight Validation
|
|
53
|
+
|
|
54
|
+
If you attempt a `fastApply` on a large file (>10 lines) and provide no `// ... existing code ...` markers, the plugin will **automatically reject the edit**. This failsafe prevents the AI from unintentionally wiping out entire files. Ensure your `codeEdit` param correctly utilizes markers.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Instructions Parameter
|
|
59
|
+
|
|
60
|
+
**This is critical for accuracy.** Write a first-person description of your changes.
|
|
61
|
+
|
|
62
|
+
**Good:** "I am adding error handling for null users and removing the deprecated auth check"
|
|
63
|
+
|
|
64
|
+
**Bad:** "Update code" / "Fix bug" / "Add stuff"
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Examples
|
|
69
|
+
|
|
70
|
+
### Adding a new function
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
// ... existing code ...
|
|
74
|
+
import { newDep } from './newDep';
|
|
75
|
+
// ... existing code ...
|
|
76
|
+
|
|
77
|
+
function newFeature() {
|
|
78
|
+
return newDep.process();
|
|
79
|
+
}
|
|
80
|
+
// ... existing code ...
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Modifying existing code
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
// ... existing code ...
|
|
87
|
+
function existingFunc(param) {
|
|
88
|
+
// Updated implementation
|
|
89
|
+
const result = param * 2; // Changed from * 1
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
// ... existing code ...
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Adding a timeout to fetch (From Morph docs)
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
// ... existing code ...
|
|
99
|
+
export async function fetchData(endpoint: string) {
|
|
100
|
+
// ... existing code ...
|
|
101
|
+
const response = await fetch(endpoint, {
|
|
102
|
+
headers,
|
|
103
|
+
timeout: 5000 // added timeout
|
|
104
|
+
});
|
|
105
|
+
// ... existing code ...
|
|
106
|
+
}
|
|
107
|
+
// ... existing code ...
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Deleting code (Show what remains)
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
// ... existing code ...
|
|
114
|
+
function keepThis() {
|
|
115
|
+
return 'stays';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// The function between these two was removed
|
|
119
|
+
|
|
120
|
+
function alsoKeepThis() {
|
|
121
|
+
return 'also stays';
|
|
122
|
+
}
|
|
123
|
+
// ... existing code ...
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Providing Context for Disambiguation
|
|
129
|
+
|
|
130
|
+
When a file has similar code patterns, include enough unique context:
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
// BAD - "return result" could match many places
|
|
134
|
+
// ... existing code ...
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
// ... existing code ...
|
|
138
|
+
|
|
139
|
+
// GOOD - unique function signature anchors the location accurately
|
|
140
|
+
// ... existing code ...
|
|
141
|
+
function processUserData(userId) {
|
|
142
|
+
const result = await fetchUser(userId);
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
// ... existing code ...
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Common Mistakes
|
|
151
|
+
|
|
152
|
+
| Mistake | Result | Fix |
|
|
153
|
+
| ----------------------- | ------------------------- | ------------------------------------------------------------ |
|
|
154
|
+
| No markers at start/end | Deletes code before/after | Always start and end snippet with `// ... existing code ...` |
|
|
155
|
+
| Too little context | Wrong location chosen | Add 1-2 unique lines around your change |
|
|
156
|
+
| Vague instructions | Ambiguous merge | Be specific: what, where, why |
|
|
157
|
+
| Using for tiny changes | Slower than `edit` | Use native `edit` for 1-2 line exact replacements |
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Fallback Behavior
|
|
162
|
+
|
|
163
|
+
If the Morph API fails (timeout, rate limit, etc.):
|
|
164
|
+
|
|
165
|
+
1. An error message containing the specifics is returned
|
|
166
|
+
2. Use the native `edit` tool as a fallback
|
|
167
|
+
3. Note that the native `edit` tool requires exact string matching
|
package/README.md
CHANGED
|
@@ -1,33 +1,208 @@
|
|
|
1
1
|
# apex-fast
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**apex-fast** is a plugin for [OpenCode AI](https://opencode.ai) that integrates [Morph Fast Apply](https://morphllm.com) — an AI model specialized for code editing boasting 10,500+ tokens/second speeds and 98% accuracy. This plugin overrides the built-in edit tool with `morph_edit`, which is significantly faster for large files and complex changes.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
---
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## ✨ Features
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- ⚡ **10x Faster** — Uses partial snippets, no need to read the entire file
|
|
10
|
+
- 🧠 **Smart Routing** — Automatically selects `morph-v3-fast` or `morph-v3-large` models based on instruction complexity
|
|
11
|
+
- 🔒 **Overrides `edit` Tool** — Blocks the built-in `edit` tool and directs the AI to use `fastApply` instead
|
|
12
|
+
- 🛡️ **Pre-flight Validation & Readonly Agents Protection** — Prevents catastrophic accidental deletions and disables edits in `plan` or `explore` modes.
|
|
13
|
+
- 🛠️ **MCP Server** — Can be run as a standalone MCP server (local via stdio or remote via SSE)
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
---
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
## 🚀 Installation
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
### Step 1: Get Morph API Key
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
- `mise run test` - Run tests
|
|
19
|
-
- `mise run lint` - Lint code
|
|
20
|
-
- `mise run lint:fix` - Fix linting issues
|
|
21
|
-
- `mise run format` - Format code with Prettier
|
|
21
|
+
Sign up and get your API key at **[morphllm.com](https://morphllm.com)**.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
---
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
### Step 2: Configure Plugin in OpenCode
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Edit your `~/.config/opencode/opencode.json` file and add the plugin:
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"plugin": ["apex-fast@latest"],
|
|
32
|
+
"instructions": ["Follow Instructions in `~/.config/opencode/Morph-Rules.md`"]
|
|
33
|
+
}
|
|
34
|
+
```
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
> **Note:** The `"instructions"` field tells the AI to read `Morph-Rules.md` as its guide on when to use `fastApply` vs other tools.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
### Step 3: Install `Morph-Rules.md` _(REQUIRED)_
|
|
41
|
+
|
|
42
|
+
The `Morph-Rules.md` file acts as the primary behavioral guideline for the AI. You **must** copy it into your OpenCode config directory. Without it, the AI won't know how to use `morph_edit` properly.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Download Morph-Rules.md into your OpenCode config directory
|
|
46
|
+
curl -o ~/.config/opencode/Morph-Rules.md \
|
|
47
|
+
https://raw.githubusercontent.com/yunaamelia/apex-fast/main/Morph-Rules.md
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Alternatively, manually copy it from this repository.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
### Step 4: Set Environment Variable
|
|
55
|
+
|
|
56
|
+
The plugin requires the `MORPH_API_KEY` to be available in your environment.
|
|
57
|
+
|
|
58
|
+
**Permanent approach (recommended):**
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Add to ~/.bashrc or ~/.zshrc
|
|
62
|
+
echo 'export MORPH_API_KEY="sk-your-key-here"' >> ~/.bashrc
|
|
63
|
+
source ~/.bashrc
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Or** add it directly to a `.env` file in your working directory:
|
|
67
|
+
|
|
68
|
+
```env
|
|
69
|
+
MORPH_API_KEY=sk-your-key-here
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 🛠️ Using the `fastApply` Tool
|
|
75
|
+
|
|
76
|
+
Once the plugin is active, your AI will have access to the `fastApply` tool. Use the syntax `// ... existing code ...` as a marker for unchanged code.
|
|
77
|
+
|
|
78
|
+
### Parameters
|
|
79
|
+
|
|
80
|
+
| Parameter | Type | Required | Description |
|
|
81
|
+
| -------------- | -------- | -------- | ------------------------------------------------------------- |
|
|
82
|
+
| `filePath` | `string` | ✅ | Relative path to the file you want to edit |
|
|
83
|
+
| `instructions` | `string` | ✅ | Specific instructions detailing what to change |
|
|
84
|
+
| `codeEdit` | `string` | ❌ | Partial code snippet using `// ... existing code ...` markers |
|
|
85
|
+
|
|
86
|
+
### Usage Examples
|
|
87
|
+
|
|
88
|
+
**Adding a new function:**
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
// ... existing code ...
|
|
92
|
+
import { newDep } from './newDep';
|
|
93
|
+
// ... existing code ...
|
|
94
|
+
|
|
95
|
+
function newFeature() {
|
|
96
|
+
return newDep.process();
|
|
97
|
+
}
|
|
98
|
+
// ... existing code ...
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Modifying existing code:**
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
// ... existing code ...
|
|
105
|
+
function existingFunc(param) {
|
|
106
|
+
// Updated implementation
|
|
107
|
+
const result = param * 2; // Changed from * 1
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
// ... existing code ...
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
> ⚠️ **IMPORTANT:** Always include `// ... existing code ...` at the beginning and end of your snippet. Otherwise, Morph will **delete** the code outside of the snippet.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 📋 Tool Selection Guide
|
|
118
|
+
|
|
119
|
+
| Situation | Tool | Reason |
|
|
120
|
+
| -------------------------------- | ----------- | ----------------------------------------- |
|
|
121
|
+
| Small & exact string replacement | `edit` | Fastest, no API call |
|
|
122
|
+
| Simple variable/function rename | `edit` | Precise, no AI needed |
|
|
123
|
+
| **Large files (300+ lines)** | `fastApply` | 10x faster, partial snippets |
|
|
124
|
+
| **Multiple scattered changes** | `fastApply` | Batch changes in one pass |
|
|
125
|
+
| **Complex refactoring** | `fastApply` | AI parses context better |
|
|
126
|
+
| **Whitespace-sensitive edits** | `fastApply` | High tolerance for formatting differences |
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 🖥️ Running as an MCP Server (Optional)
|
|
131
|
+
|
|
132
|
+
This plugin can also run as a **standalone MCP Server** for use with other MCP clients (like Claude Desktop).
|
|
133
|
+
|
|
134
|
+
### Stdio Mode (Default)
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
MORPH_API_KEY=sk-your-key npx apex-fast-mcp
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### SSE Mode (Remote/HTTP)
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
MORPH_API_KEY=sk-your-key npx apex-fast-mcp sse 3000
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The server will run on:
|
|
147
|
+
|
|
148
|
+
- **SSE Endpoint:** `http://localhost:3000/sse`
|
|
149
|
+
- **Message Endpoint:** `http://localhost:3000/message`
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## ⚠️ Troubleshooting
|
|
154
|
+
|
|
155
|
+
### Error: `[ERROR] Missing MORPH_API_KEY`
|
|
156
|
+
|
|
157
|
+
The plugin cannot find the API key. Ensure:
|
|
158
|
+
|
|
159
|
+
- The `MORPH_API_KEY` environment variable is set
|
|
160
|
+
- If using `.env`, the file exists in the current working directory
|
|
161
|
+
|
|
162
|
+
### Error: `[ERROR] Failed to read file`
|
|
163
|
+
|
|
164
|
+
The specific `filePath` cannot be read. Ensure:
|
|
165
|
+
|
|
166
|
+
- The path provided is **relative** to the current working directory
|
|
167
|
+
- The file actually exists
|
|
168
|
+
|
|
169
|
+
### Pre-flight Validation Error
|
|
170
|
+
|
|
171
|
+
If you attempt to edit a large file (>10 lines) and omit the `// ... existing code ...` markers, the plugin will block the tool call to prevent catastrophic code loss. The AI should simply rewrite the `codeEdit` parameter correctly wrapped inside markers.
|
|
172
|
+
|
|
173
|
+
### Fallback Behavior for Morph API Failure
|
|
174
|
+
|
|
175
|
+
If Morph API times out or rate limits:
|
|
176
|
+
|
|
177
|
+
1. The plugin will return an error message containing the specifics
|
|
178
|
+
2. The AI can fall back to using the built-in `edit` tool
|
|
179
|
+
3. The built-in `edit` tool requires exact string matching
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 🔧 Development
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
mise run build # Build the project
|
|
187
|
+
mise run test # Run the test suite
|
|
188
|
+
mise run lint # Lint the code
|
|
189
|
+
mise run lint:fix # Automatically fix linting issues
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 📦 Release
|
|
195
|
+
|
|
196
|
+
See the [RELEASE.md](RELEASE.md) file for instructions on how to release a new version.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## 🤝 Contributing
|
|
201
|
+
|
|
202
|
+
Contributions are very welcome! Please open an issue or submit a pull request on the GitHub repository.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 📄 License
|
|
32
207
|
|
|
33
208
|
See the [LICENSE](LICENSE) file for details.
|
package/dist/index.js
CHANGED
|
@@ -94,7 +94,7 @@ function isTextualFile(filePath, maxBytes = 2000000) {
|
|
|
94
94
|
var init_chunk_YPKNMYD4 = () => {};
|
|
95
95
|
|
|
96
96
|
// node_modules/@vscode/ripgrep/lib/index.js
|
|
97
|
-
var __dirname = "/home/
|
|
97
|
+
var __dirname = "/home/racoondev/my-module/node_modules/@vscode/ripgrep/lib", path2, $rgPath;
|
|
98
98
|
var init_lib = __esm(() => {
|
|
99
99
|
path2 = __require("path");
|
|
100
100
|
$rgPath = path2.join(__dirname, `../bin/rg${process.platform === "win32" ? ".exe" : ""}`);
|
|
@@ -64705,6 +64705,11 @@ var FastApplyPlugin = async (ctx) => {
|
|
|
64705
64705
|
codeEdit: tool.schema.string().optional().describe("The specific code snippet to edit (optional)")
|
|
64706
64706
|
},
|
|
64707
64707
|
execute: async (args, context) => {
|
|
64708
|
+
console.log(`[DEBUG] fastApply called for file: ${args.filePath}`);
|
|
64709
|
+
const READONLY_AGENTS = ["plan", "explore"];
|
|
64710
|
+
if (context && context.agent && READONLY_AGENTS.includes(context.agent)) {
|
|
64711
|
+
throw new Error(`[ERROR] fastApply is not available in ${context.agent} mode. Please switch to a build/code mode.`);
|
|
64712
|
+
}
|
|
64708
64713
|
const directory = ctx?.directory || context?.directory || process.cwd();
|
|
64709
64714
|
const absolutePath = path6.join(directory, args.filePath);
|
|
64710
64715
|
let originalCode;
|
|
@@ -64718,6 +64723,15 @@ var FastApplyPlugin = async (ctx) => {
|
|
|
64718
64723
|
}
|
|
64719
64724
|
let result;
|
|
64720
64725
|
try {
|
|
64726
|
+
if (args.codeEdit) {
|
|
64727
|
+
const lines = originalCode.split(`
|
|
64728
|
+
`);
|
|
64729
|
+
const isLargeFile = lines.length > 10;
|
|
64730
|
+
const hasMarkers = args.codeEdit.includes("... existing code ...");
|
|
64731
|
+
if (isLargeFile && !hasMarkers) {
|
|
64732
|
+
throw new Error("[ERROR] codeEdit missing '// ... existing code ...' markers for file >10 lines. This prevents catastrophic deletion. Please wrap partial edits with markers.");
|
|
64733
|
+
}
|
|
64734
|
+
}
|
|
64721
64735
|
const { difficulty } = await morph.routers.raw.classify({
|
|
64722
64736
|
input: args.instructions
|
|
64723
64737
|
});
|
|
@@ -64749,6 +64763,18 @@ var FastApplyPlugin = async (ctx) => {
|
|
|
64749
64763
|
}
|
|
64750
64764
|
}
|
|
64751
64765
|
})
|
|
64766
|
+
},
|
|
64767
|
+
"tool.execute.after": async (input, output) => {
|
|
64768
|
+
if (input.tool === "fastApply") {
|
|
64769
|
+
const fileMatch = output.output.match(/Successfully applied changes to (.+)/);
|
|
64770
|
+
if (fileMatch) {
|
|
64771
|
+
output.title = `Morph: applied changes to ${fileMatch[1]}`;
|
|
64772
|
+
}
|
|
64773
|
+
output.metadata = {
|
|
64774
|
+
...output.metadata,
|
|
64775
|
+
provider: "morph"
|
|
64776
|
+
};
|
|
64777
|
+
}
|
|
64752
64778
|
}
|
|
64753
64779
|
};
|
|
64754
64780
|
};
|