opencode-add-dir 1.0.4 → 1.2.1
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/LICENSE +21 -0
- package/README.md +86 -95
- package/bin/setup.mjs +86 -0
- package/dist/context.d.ts +2 -0
- package/dist/index.d.ts +2 -4
- package/dist/index.js +234 -220
- package/dist/permissions.d.ts +6 -0
- package/dist/plugin.d.ts +2 -0
- package/dist/state.d.ts +8 -0
- package/dist/validate.d.ts +8 -0
- package/package.json +47 -26
- package/command/add-dir.md +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/scripts/install.js +0 -58
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Cristian Fonseca
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,142 +1,133 @@
|
|
|
1
|
-
#
|
|
1
|
+
# opencode-add-dir
|
|
2
2
|
|
|
3
|
-
Add
|
|
3
|
+
Add working directories to your [OpenCode](https://opencode.ai) session — inspired by Claude Code's [`/add-dir`](https://docs.anthropic.com/en/docs/claude-code/cli-usage#add-dir) command.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
1. Install the plugin:
|
|
5
|
+
When you need an agent to read, edit, or search files outside the current project, this plugin grants access without permission popups.
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
cd ~/.config/opencode
|
|
11
|
-
npm install opencode-add-dir
|
|
12
|
-
```
|
|
7
|
+
## Quick Start
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
Add to your `opencode.json`:
|
|
15
10
|
|
|
16
|
-
```
|
|
11
|
+
```json
|
|
17
12
|
{
|
|
18
|
-
"plugin": [
|
|
19
|
-
"your-other-plugins",
|
|
20
|
-
"opencode-add-dir"
|
|
21
|
-
]
|
|
13
|
+
"plugin": ["opencode-add-dir"]
|
|
22
14
|
}
|
|
23
15
|
```
|
|
24
16
|
|
|
25
|
-
|
|
17
|
+
Restart OpenCode. Done.
|
|
26
18
|
|
|
27
|
-
|
|
19
|
+
<details>
|
|
20
|
+
<summary>Alternative: setup CLI</summary>
|
|
28
21
|
|
|
29
22
|
```bash
|
|
30
|
-
|
|
23
|
+
bunx opencode-add-dir-setup
|
|
31
24
|
```
|
|
32
25
|
|
|
33
|
-
|
|
26
|
+
Automatically adds the plugin to your global `opencode.json`.
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
- **Auto Permission**: No permission prompts for directories you add
|
|
37
|
-
- **Smart Filtering**: Automatically skips node_modules, .git, binary files, etc.
|
|
38
|
-
- **Recursive Scanning**: Reads entire directory structures
|
|
39
|
-
- **File Limits**: 100KB per file, 500 files max (to prevent overwhelm)
|
|
40
|
-
- **Full Access**: Read and write to added directories
|
|
28
|
+
</details>
|
|
41
29
|
|
|
42
|
-
|
|
30
|
+
<details>
|
|
31
|
+
<summary>Alternative: local file</summary>
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Clone and build
|
|
35
|
+
git clone https://github.com/kuzeofficial/add-dir-opencode.git
|
|
36
|
+
cd add-dir-opencode
|
|
37
|
+
bun install && bun run deploy
|
|
38
|
+
```
|
|
43
39
|
|
|
44
|
-
|
|
45
|
-
2. Directory path is automatically registered for future access
|
|
46
|
-
3. Any operations on that directory skip permission prompts
|
|
47
|
-
4. Works recursively - all subdirectories are auto-approved
|
|
40
|
+
Bundles to `~/.config/opencode/plugins/add-dir.js`.
|
|
48
41
|
|
|
49
|
-
|
|
42
|
+
</details>
|
|
50
43
|
|
|
51
|
-
|
|
52
|
-
# Add a project directory
|
|
53
|
-
/add-dir ~/projects/my-app
|
|
44
|
+
## Usage
|
|
54
45
|
|
|
55
|
-
|
|
56
|
-
/add-dir /path/to/external/code
|
|
46
|
+
### Slash Command
|
|
57
47
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
48
|
+
```
|
|
49
|
+
/add-dir /path/to/directory # Session only
|
|
50
|
+
/add-dir /path/to/directory --remember # Persist across sessions
|
|
51
|
+
/add-dir list # Show added directories
|
|
52
|
+
/add-dir remove /path/to/directory # Remove a directory
|
|
61
53
|
```
|
|
62
54
|
|
|
63
|
-
|
|
55
|
+
### LLM Tools
|
|
64
56
|
|
|
65
|
-
|
|
66
|
-
- `node_modules`
|
|
67
|
-
- `.git`
|
|
68
|
-
- `dist`, `build`
|
|
69
|
-
- `.next`
|
|
70
|
-
- `__pycache__`
|
|
71
|
-
- Virtual environments (`.venv`, `venv`, `env`)
|
|
72
|
-
- `.env`, `.env.*`
|
|
73
|
-
- `coverage`
|
|
74
|
-
- `.nuxt`, `.output`
|
|
75
|
-
- `tmp`, `temp`, `.turbo`
|
|
57
|
+
The agent can also call these tools directly:
|
|
76
58
|
|
|
77
|
-
|
|
78
|
-
|
|
59
|
+
| Tool | Description |
|
|
60
|
+
|------|-------------|
|
|
61
|
+
| `add_dir` | Add a directory (with optional `remember` flag) |
|
|
62
|
+
| `list_dirs` | List all added directories |
|
|
63
|
+
| `remove_dir` | Remove a directory |
|
|
79
64
|
|
|
80
|
-
##
|
|
65
|
+
## How It Works
|
|
81
66
|
|
|
82
|
-
The plugin
|
|
83
|
-
1. Finds your OpenCode config directory (`~/.config/opencode/`)
|
|
84
|
-
2. Creates the `/add-dir` command file
|
|
85
|
-
3. Installs it to `~/.config/opencode/command/add-dir.md`
|
|
67
|
+
The plugin uses a layered approach to handle permissions across all sessions, including subagents:
|
|
86
68
|
|
|
87
|
-
|
|
69
|
+
| Layer | When | Scope |
|
|
70
|
+
|-------|------|-------|
|
|
71
|
+
| **Config hook** | Startup | Injects `external_directory: "allow"` rules for persisted dirs into all agents |
|
|
72
|
+
| **Session permission** | `/add-dir` | Sets `external_directory: true` on the current session via `tools` field |
|
|
73
|
+
| **tool.execute.before** | Every file tool | Detects subagent sessions accessing added dirs, grants permission before execution |
|
|
74
|
+
| **Event auto-approve** | Permission popup | Catches any remaining `external_directory` requests and auto-approves via SDK |
|
|
88
75
|
|
|
89
|
-
|
|
76
|
+
### AGENTS.md Injection
|
|
90
77
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
78
|
+
If an added directory contains `AGENTS.md`, `CLAUDE.md`, or `.agents/AGENTS.md`, the content is automatically injected into the system prompt via `experimental.chat.system.transform`.
|
|
79
|
+
|
|
80
|
+
## Persistence
|
|
81
|
+
|
|
82
|
+
Directories added with `--remember` are stored in:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
~/.local/share/opencode/add-dir/directories.json
|
|
94
86
|
```
|
|
95
87
|
|
|
96
|
-
|
|
88
|
+
These are loaded at startup and injected into agent permission rules via the config hook.
|
|
89
|
+
|
|
90
|
+
## Development
|
|
91
|
+
|
|
97
92
|
```bash
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
Add the directory at path $ARGUMENTS to this session's context.
|
|
104
|
-
Use the add_dir tool to read all files from the specified directory.
|
|
105
|
-
EOF
|
|
93
|
+
bun install
|
|
94
|
+
bun test # 17 tests
|
|
95
|
+
bun run typecheck # Type check
|
|
96
|
+
bun run build # Build npm package
|
|
97
|
+
bun run deploy # Bundle to ~/.config/opencode/plugins/
|
|
106
98
|
```
|
|
107
99
|
|
|
108
|
-
|
|
100
|
+
### Project Structure
|
|
109
101
|
|
|
110
|
-
|
|
102
|
+
```
|
|
103
|
+
src/
|
|
104
|
+
├── index.ts # Entry point (default export)
|
|
105
|
+
├── plugin.ts # Hooks + tools
|
|
106
|
+
├── state.ts # Persistence
|
|
107
|
+
├── validate.ts # Directory validation
|
|
108
|
+
├── permissions.ts # Session grants + auto-approve
|
|
109
|
+
└── context.ts # AGENTS.md injection
|
|
110
|
+
```
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
# Clone the repository
|
|
114
|
-
git clone https://github.com/kuzeofficial/add-dir-opencode.git
|
|
115
|
-
cd opencode-add-dir
|
|
112
|
+
## Debugging
|
|
116
113
|
|
|
117
|
-
|
|
118
|
-
bun install
|
|
114
|
+
Run OpenCode with logs:
|
|
119
115
|
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
```bash
|
|
117
|
+
opencode --print-logs 2>debug.log
|
|
118
|
+
```
|
|
122
119
|
|
|
123
|
-
|
|
124
|
-
npm link
|
|
125
|
-
cd ~/.config/opencode
|
|
126
|
-
npm link opencode-add-dir
|
|
120
|
+
Filter plugin logs:
|
|
127
121
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
npm publish
|
|
122
|
+
```bash
|
|
123
|
+
grep "\[add-dir\]" debug.log
|
|
131
124
|
```
|
|
132
125
|
|
|
133
|
-
##
|
|
126
|
+
## Limitations
|
|
134
127
|
|
|
135
|
-
- `
|
|
136
|
-
- `
|
|
137
|
-
- `scripts/install.js` - Post-install script (auto-runs)
|
|
138
|
-
- `package.json` - NPM package configuration
|
|
128
|
+
- Directories added mid-session (without `--remember`) rely on session-level permissions and the event hook auto-approve. The first access by a subagent may briefly show a permission popup before auto-dismissing.
|
|
129
|
+
- The `permission.ask` plugin hook is defined in the OpenCode SDK but [not invoked](https://github.com/sst/opencode/blob/main/packages/opencode/src/permission/index.ts) in the source — this plugin works around it using `tool.execute.before` and event-based auto-approval.
|
|
139
130
|
|
|
140
131
|
## License
|
|
141
132
|
|
|
142
|
-
MIT
|
|
133
|
+
[MIT](LICENSE)
|
package/bin/setup.mjs
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs"
|
|
3
|
+
import { join } from "path"
|
|
4
|
+
import { homedir } from "os"
|
|
5
|
+
|
|
6
|
+
const PKG = "opencode-add-dir"
|
|
7
|
+
const args = process.argv.slice(2)
|
|
8
|
+
const isRemove = args.includes("--remove")
|
|
9
|
+
|
|
10
|
+
function configDir() {
|
|
11
|
+
if (process.env.XDG_CONFIG_HOME) return join(process.env.XDG_CONFIG_HOME, "opencode")
|
|
12
|
+
if (process.platform === "darwin") return join(homedir(), ".config", "opencode")
|
|
13
|
+
return join(homedir(), ".config", "opencode")
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function findConfig() {
|
|
17
|
+
const dir = configDir()
|
|
18
|
+
for (const name of ["opencode.jsonc", "opencode.json"]) {
|
|
19
|
+
const p = join(dir, name)
|
|
20
|
+
if (existsSync(p)) return p
|
|
21
|
+
}
|
|
22
|
+
return join(dir, "opencode.json")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function stripJsonComments(text) {
|
|
26
|
+
let result = ""
|
|
27
|
+
let inString = false
|
|
28
|
+
let escape = false
|
|
29
|
+
for (let i = 0; i < text.length; i++) {
|
|
30
|
+
const ch = text[i]
|
|
31
|
+
if (escape) { result += ch; escape = false; continue }
|
|
32
|
+
if (ch === "\\" && inString) { result += ch; escape = true; continue }
|
|
33
|
+
if (ch === '"') { inString = !inString; result += ch; continue }
|
|
34
|
+
if (inString) { result += ch; continue }
|
|
35
|
+
if (ch === "/" && text[i + 1] === "/") { while (i < text.length && text[i] !== "\n") i++; continue }
|
|
36
|
+
if (ch === "/" && text[i + 1] === "*") { i += 2; while (i < text.length && !(text[i] === "*" && text[i + 1] === "/")) i++; i++; continue }
|
|
37
|
+
result += ch
|
|
38
|
+
}
|
|
39
|
+
return result
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function run() {
|
|
43
|
+
const configPath = findConfig()
|
|
44
|
+
const dir = configDir()
|
|
45
|
+
|
|
46
|
+
let config = {}
|
|
47
|
+
if (existsSync(configPath)) {
|
|
48
|
+
const raw = readFileSync(configPath, "utf-8")
|
|
49
|
+
config = JSON.parse(stripJsonComments(raw))
|
|
50
|
+
} else {
|
|
51
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
config.plugin = config.plugin || []
|
|
55
|
+
const has = config.plugin.some((p) => {
|
|
56
|
+
const name = Array.isArray(p) ? p[0] : p
|
|
57
|
+
return name === PKG || name.startsWith(PKG + "@")
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
if (isRemove) {
|
|
61
|
+
if (!has) {
|
|
62
|
+
console.log(`${PKG} is not in ${configPath}`)
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
config.plugin = config.plugin.filter((p) => {
|
|
66
|
+
const name = Array.isArray(p) ? p[0] : p
|
|
67
|
+
return name !== PKG && !name.startsWith(PKG + "@")
|
|
68
|
+
})
|
|
69
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n")
|
|
70
|
+
console.log(`Removed ${PKG} from ${configPath}`)
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (has) {
|
|
75
|
+
console.log(`${PKG} is already in ${configPath}`)
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
config.plugin.push(PKG)
|
|
80
|
+
if (!config.$schema) config.$schema = "https://opencode.ai/config.json"
|
|
81
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n")
|
|
82
|
+
console.log(`Added ${PKG} to ${configPath}`)
|
|
83
|
+
console.log("Restart OpenCode to activate the plugin.")
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
run()
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,237 +1,251 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
'.next',
|
|
10
|
-
'__pycache__',
|
|
11
|
-
'.venv',
|
|
12
|
-
'venv',
|
|
13
|
-
'env',
|
|
14
|
-
'.env',
|
|
15
|
-
'coverage',
|
|
16
|
-
'.nuxt',
|
|
17
|
-
'.output',
|
|
18
|
-
'tmp',
|
|
19
|
-
'temp',
|
|
20
|
-
'.turbo'
|
|
21
|
-
]);
|
|
22
|
-
const BINARY_EXTENSIONS = new Set([
|
|
23
|
-
'.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.svg', '.webp',
|
|
24
|
-
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
|
25
|
-
'.zip', '.tar', '.gz', '.rar', '.7z',
|
|
26
|
-
'.mp3', '.mp4', '.avi', '.mov', '.wav',
|
|
27
|
-
'.exe', '.dll', '.so', '.dylib', '.bin',
|
|
28
|
-
'.class', '.jar', '.war',
|
|
29
|
-
'.pyc', '.pyo',
|
|
30
|
-
'.db', '.sqlite', '.sqlite3',
|
|
31
|
-
'.lock', '.log'
|
|
32
|
-
]);
|
|
33
|
-
const MAX_FILE_SIZE_BYTES = 100 * 1024;
|
|
34
|
-
const MAX_FILES = 500;
|
|
35
|
-
const ADDED_DIRS_FILE = path.join(__dirname, '.added-dirs.json');
|
|
36
|
-
function getAddedDirectories() {
|
|
37
|
-
try {
|
|
38
|
-
if (fs.existsSync(ADDED_DIRS_FILE)) {
|
|
39
|
-
const content = fs.readFileSync(ADDED_DIRS_FILE, 'utf-8');
|
|
40
|
-
const data = JSON.parse(content);
|
|
41
|
-
return new Set(data.directories);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
console.error('Failed to read added directories:', error);
|
|
46
|
-
}
|
|
47
|
-
return new Set();
|
|
1
|
+
// src/plugin.ts
|
|
2
|
+
import { tool } from "@opencode-ai/plugin";
|
|
3
|
+
|
|
4
|
+
// src/state.ts
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
function stateDir() {
|
|
8
|
+
return join(process.env["XDG_DATA_HOME"] || join(process.env["HOME"] || "~", ".local", "share"), "opencode", "add-dir");
|
|
48
9
|
}
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
10
|
+
function loadDirs() {
|
|
11
|
+
const dirs = new Map;
|
|
12
|
+
const file = join(stateDir(), "directories.json");
|
|
13
|
+
if (!existsSync(file))
|
|
14
|
+
return dirs;
|
|
15
|
+
try {
|
|
16
|
+
for (const p of JSON.parse(readFileSync(file, "utf-8")))
|
|
17
|
+
dirs.set(p, { path: p, persist: true });
|
|
18
|
+
} catch {}
|
|
19
|
+
return dirs;
|
|
57
20
|
}
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return false;
|
|
21
|
+
function saveDirs(dirs) {
|
|
22
|
+
const list = [...dirs.values()].filter((d) => d.persist).map((d) => d.path);
|
|
23
|
+
const dir = stateDir();
|
|
24
|
+
if (!existsSync(dir))
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
writeFileSync(join(dir, "directories.json"), JSON.stringify(list, null, 2));
|
|
68
27
|
}
|
|
69
|
-
function
|
|
70
|
-
|
|
71
|
-
return BINARY_EXTENSIONS.has(ext);
|
|
28
|
+
function isChildOf(parent, child) {
|
|
29
|
+
return child === parent || child.startsWith(parent + "/");
|
|
72
30
|
}
|
|
73
|
-
function
|
|
74
|
-
|
|
31
|
+
function matchesDirs(dirs, filepath) {
|
|
32
|
+
for (const entry of dirs.values()) {
|
|
33
|
+
if (isChildOf(entry.path, filepath))
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
75
37
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return 1;
|
|
83
|
-
}
|
|
84
|
-
return a.name.localeCompare(b.name);
|
|
85
|
-
});
|
|
38
|
+
|
|
39
|
+
// src/validate.ts
|
|
40
|
+
import { statSync } from "fs";
|
|
41
|
+
import { resolve } from "path";
|
|
42
|
+
function expandHome(p) {
|
|
43
|
+
return p.startsWith("~/") ? (process.env["HOME"] || "~") + p.slice(1) : p;
|
|
86
44
|
}
|
|
87
|
-
function
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return
|
|
45
|
+
function validateDir(input, worktree, existing) {
|
|
46
|
+
const trimmed = input.trim();
|
|
47
|
+
if (!trimmed)
|
|
48
|
+
return { ok: false, reason: "No directory path provided." };
|
|
49
|
+
const abs = resolve(expandHome(trimmed));
|
|
50
|
+
try {
|
|
51
|
+
if (!statSync(abs).isDirectory())
|
|
52
|
+
return { ok: false, reason: `${abs} is not a directory.` };
|
|
53
|
+
} catch (e) {
|
|
54
|
+
if ("ENOENT ENOTDIR EACCES EPERM".includes(e.code))
|
|
55
|
+
return { ok: false, reason: `Path ${abs} was not found.` };
|
|
56
|
+
throw e;
|
|
57
|
+
}
|
|
58
|
+
if (isChildOf(worktree, abs))
|
|
59
|
+
return { ok: false, reason: `${abs} is already within the project directory ${worktree}.` };
|
|
60
|
+
for (const dir of existing)
|
|
61
|
+
if (isChildOf(dir, abs))
|
|
62
|
+
return { ok: false, reason: `${abs} is already accessible within ${dir}.` };
|
|
63
|
+
return { ok: true, absolutePath: abs };
|
|
91
64
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
return {
|
|
102
|
-
content,
|
|
103
|
-
isTruncated: false
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
catch (error) {
|
|
107
|
-
return {
|
|
108
|
-
content: undefined,
|
|
109
|
-
isTruncated: false
|
|
110
|
-
};
|
|
111
|
-
}
|
|
65
|
+
|
|
66
|
+
// src/permissions.ts
|
|
67
|
+
import { join as join2, resolve as resolve2 } from "path";
|
|
68
|
+
var FILE_TOOLS = new Set(["read", "write", "edit", "apply_patch", "multiedit", "glob", "grep", "list", "bash"]);
|
|
69
|
+
var grantedSessions = new Set;
|
|
70
|
+
function expandHome2(p) {
|
|
71
|
+
return p.startsWith("~/") ? (process.env["HOME"] || "~") + p.slice(1) : p;
|
|
112
72
|
}
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
return
|
|
73
|
+
function extractPath(tool, args) {
|
|
74
|
+
if (!args)
|
|
75
|
+
return "";
|
|
76
|
+
if (tool === "bash")
|
|
77
|
+
return args.workdir || args.command || "";
|
|
78
|
+
return args.filePath || args.path || args.pattern || "";
|
|
116
79
|
}
|
|
117
|
-
function
|
|
118
|
-
|
|
119
|
-
result.tree.push(createTreeEntry(entry.name, true, depth));
|
|
120
|
-
const nestedResult = scanDirectory(fullPath, baseDir, result.files, result.tree, depth + 1, result.fileCount);
|
|
121
|
-
result.files = nestedResult.files;
|
|
122
|
-
result.tree = nestedResult.tree;
|
|
123
|
-
result.fileCount = nestedResult.fileCount;
|
|
124
|
-
result.skipped += nestedResult.skipped;
|
|
80
|
+
function permissionGlob(dirPath) {
|
|
81
|
+
return join2(dirPath, "*");
|
|
125
82
|
}
|
|
126
|
-
function
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
size: stats.size,
|
|
135
|
-
content: contentResult.content,
|
|
136
|
-
isTruncated: contentResult.isTruncated
|
|
137
|
-
});
|
|
138
|
-
result.fileCount++;
|
|
83
|
+
async function grantSession(sdk, sessionID, text) {
|
|
84
|
+
if (grantedSessions.has(sessionID))
|
|
85
|
+
return;
|
|
86
|
+
grantedSessions.add(sessionID);
|
|
87
|
+
await sdk.session.prompt({
|
|
88
|
+
path: { id: sessionID },
|
|
89
|
+
body: { noReply: true, tools: { external_directory: true }, parts: [{ type: "text", text }] }
|
|
90
|
+
}).catch(() => {});
|
|
139
91
|
}
|
|
140
|
-
function
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
92
|
+
function grantSessionAsync(sdk, sessionID, text) {
|
|
93
|
+
setTimeout(() => {
|
|
94
|
+
sdk.session.promptAsync({
|
|
95
|
+
path: { id: sessionID },
|
|
96
|
+
body: { noReply: true, tools: { external_directory: true }, parts: [{ type: "text", text }] }
|
|
97
|
+
})?.then?.(() => grantedSessions.add(sessionID))?.catch?.(() => {});
|
|
98
|
+
}, 150);
|
|
99
|
+
}
|
|
100
|
+
function shouldGrantBeforeTool(dirs, tool, args) {
|
|
101
|
+
if (!dirs.size || !FILE_TOOLS.has(tool))
|
|
102
|
+
return false;
|
|
103
|
+
const p = extractPath(tool, args);
|
|
104
|
+
return !!p && matchesDirs(dirs, resolve2(expandHome2(p)));
|
|
105
|
+
}
|
|
106
|
+
async function autoApprovePermission(sdk, props, dirs) {
|
|
107
|
+
if (props.permission !== "external_directory")
|
|
108
|
+
return;
|
|
109
|
+
const meta = props.metadata ?? {};
|
|
110
|
+
const filepath = meta.filepath ?? "";
|
|
111
|
+
const parentDir = meta.parentDir ?? "";
|
|
112
|
+
const patterns = props.patterns ?? [];
|
|
113
|
+
const matches = matchesDirs(dirs, filepath) || matchesDirs(dirs, parentDir) || patterns.some((p) => matchesDirs(dirs, p.replace(/\/?\*$/, "")));
|
|
114
|
+
if (!matches || !props.id || !props.sessionID)
|
|
115
|
+
return;
|
|
116
|
+
await sdk.postSessionIdPermissionsPermissionId({
|
|
117
|
+
path: { id: props.sessionID, permissionID: props.id },
|
|
118
|
+
body: { response: "always" }
|
|
119
|
+
}).catch(() => {});
|
|
166
120
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
121
|
+
|
|
122
|
+
// src/context.ts
|
|
123
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
124
|
+
import { join as join3 } from "path";
|
|
125
|
+
var CONTEXT_FILES = ["AGENTS.md", "CLAUDE.md", ".agents/AGENTS.md"];
|
|
126
|
+
function collectAgentContext(dirs) {
|
|
127
|
+
const sections = [];
|
|
128
|
+
for (const entry of dirs.values()) {
|
|
129
|
+
for (const name of CONTEXT_FILES) {
|
|
130
|
+
const fp = join3(entry.path, name);
|
|
131
|
+
if (!existsSync2(fp))
|
|
132
|
+
continue;
|
|
133
|
+
try {
|
|
134
|
+
const content = readFileSync2(fp, "utf-8").trim();
|
|
135
|
+
if (content)
|
|
136
|
+
sections.push(`# Context from ${fp}
|
|
137
|
+
|
|
138
|
+
${content}`);
|
|
139
|
+
} catch {}
|
|
178
140
|
}
|
|
179
|
-
|
|
141
|
+
}
|
|
142
|
+
return sections;
|
|
180
143
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
144
|
+
|
|
145
|
+
// src/plugin.ts
|
|
146
|
+
var SENTINEL = "__ADD_DIR_HANDLED__";
|
|
147
|
+
var AddDirPlugin = async ({ client, worktree, directory }) => {
|
|
148
|
+
const root = worktree || directory;
|
|
149
|
+
const dirs = loadDirs();
|
|
150
|
+
const sdk = client;
|
|
151
|
+
function add(dirPath, persist, sessionID) {
|
|
152
|
+
const result = validateDir(dirPath, root, [...dirs.values()].map((d) => d.path));
|
|
153
|
+
if (!result.ok)
|
|
154
|
+
return result.reason;
|
|
155
|
+
dirs.set(result.absolutePath, { path: result.absolutePath, persist });
|
|
156
|
+
if (persist)
|
|
157
|
+
saveDirs(dirs);
|
|
158
|
+
const label = persist ? "persistent" : "session";
|
|
159
|
+
const msg = `Added ${result.absolutePath} as a working directory (${label}).`;
|
|
160
|
+
grantSessionAsync(sdk, sessionID, msg);
|
|
161
|
+
return msg;
|
|
162
|
+
}
|
|
163
|
+
function remove(path) {
|
|
164
|
+
if (!dirs.has(path))
|
|
165
|
+
return `${path} is not in the directory list.`;
|
|
166
|
+
dirs.delete(path);
|
|
167
|
+
saveDirs(dirs);
|
|
168
|
+
return `Removed ${path} from working directories.`;
|
|
169
|
+
}
|
|
170
|
+
function list() {
|
|
171
|
+
if (!dirs.size)
|
|
172
|
+
return "No additional directories added.";
|
|
173
|
+
return [...dirs.values()].map((d) => `${d.path} (${d.persist ? "persistent" : "session"})`).join(`
|
|
174
|
+
`);
|
|
175
|
+
}
|
|
176
|
+
function handleCommand(args, sessionID) {
|
|
177
|
+
const tokens = args.trim().split(/\s+/);
|
|
178
|
+
const flags = new Set(tokens.filter((t) => t.startsWith("--")));
|
|
179
|
+
const pos = tokens.filter((t) => !t.startsWith("--"));
|
|
180
|
+
if (pos[0] === "list")
|
|
181
|
+
return list();
|
|
182
|
+
if (pos[0] === "remove" && pos[1])
|
|
183
|
+
return remove(pos[1]);
|
|
184
|
+
if (!pos[0])
|
|
185
|
+
return `Usage: /add-dir <path> [--remember]
|
|
186
|
+
/add-dir list
|
|
187
|
+
/add-dir remove <path>`;
|
|
188
|
+
return add(pos[0], flags.has("--remember"), sessionID);
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
config: async (cfg) => {
|
|
192
|
+
cfg.command ??= {};
|
|
193
|
+
cfg.command["add-dir"] = { template: "/add-dir", description: "Add a working directory for this session" };
|
|
194
|
+
if (!dirs.size)
|
|
195
|
+
return;
|
|
196
|
+
cfg.permission ??= {};
|
|
197
|
+
cfg.permission.external_directory ??= {};
|
|
198
|
+
for (const entry of dirs.values())
|
|
199
|
+
cfg.permission.external_directory[permissionGlob(entry.path)] = "allow";
|
|
200
|
+
},
|
|
201
|
+
"command.execute.before": async (input) => {
|
|
202
|
+
if (input.command !== "add-dir")
|
|
203
|
+
return;
|
|
204
|
+
handleCommand(input.arguments || "", input.sessionID);
|
|
205
|
+
throw new Error(SENTINEL);
|
|
206
|
+
},
|
|
207
|
+
"tool.execute.before": async (input, output) => {
|
|
208
|
+
if (shouldGrantBeforeTool(dirs, input.tool, output.args))
|
|
209
|
+
await grantSession(sdk, input.sessionID, "Directory access granted by add-dir plugin.");
|
|
210
|
+
},
|
|
211
|
+
event: async ({ event }) => {
|
|
212
|
+
if (event.type === "permission.asked" && event.properties)
|
|
213
|
+
await autoApprovePermission(sdk, event.properties, dirs);
|
|
214
|
+
},
|
|
215
|
+
"experimental.chat.system.transform": async (_, output) => {
|
|
216
|
+
output.system.push(...collectAgentContext(dirs));
|
|
217
|
+
},
|
|
218
|
+
tool: {
|
|
219
|
+
add_dir: tool({
|
|
220
|
+
description: "Add an external directory as a working directory. Files in added directories can be read and edited without permission prompts.",
|
|
221
|
+
args: {
|
|
222
|
+
path: tool.schema.string().describe("Absolute or relative path to directory"),
|
|
223
|
+
remember: tool.schema.boolean().optional().describe("Persist across sessions")
|
|
222
224
|
},
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const patterns = Array.isArray(input.pattern) ? input.pattern : [input.pattern];
|
|
226
|
-
for (const pattern of patterns) {
|
|
227
|
-
if (typeof pattern === 'string' && isPathInAddedDirectories(pattern)) {
|
|
228
|
-
output.status = 'allow';
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
225
|
+
async execute(args, ctx) {
|
|
226
|
+
return add(args.path, args.remember ?? false, ctx.sessionID);
|
|
233
227
|
}
|
|
234
|
-
|
|
228
|
+
}),
|
|
229
|
+
list_dirs: tool({
|
|
230
|
+
description: "List all added working directories.",
|
|
231
|
+
args: {},
|
|
232
|
+
async execute() {
|
|
233
|
+
return list();
|
|
234
|
+
}
|
|
235
|
+
}),
|
|
236
|
+
remove_dir: tool({
|
|
237
|
+
description: "Remove a previously added working directory.",
|
|
238
|
+
args: { path: tool.schema.string().describe("Path of directory to remove") },
|
|
239
|
+
async execute(args) {
|
|
240
|
+
return remove(args.path);
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// src/index.ts
|
|
248
|
+
var src_default = AddDirPlugin;
|
|
249
|
+
export {
|
|
250
|
+
src_default as default
|
|
235
251
|
};
|
|
236
|
-
export default addDirPlugin;
|
|
237
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DirEntry } from "./state.js";
|
|
2
|
+
export declare function permissionGlob(dirPath: string): string;
|
|
3
|
+
export declare function grantSession(sdk: any, sessionID: string, text: string): Promise<void>;
|
|
4
|
+
export declare function grantSessionAsync(sdk: any, sessionID: string, text: string): void;
|
|
5
|
+
export declare function shouldGrantBeforeTool(dirs: Map<string, DirEntry>, tool: string, args: any): boolean;
|
|
6
|
+
export declare function autoApprovePermission(sdk: any, props: any, dirs: Map<string, DirEntry>): Promise<void>;
|
package/dist/plugin.d.ts
ADDED
package/dist/state.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface DirEntry {
|
|
2
|
+
path: string;
|
|
3
|
+
persist: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare function loadDirs(): Map<string, DirEntry>;
|
|
6
|
+
export declare function saveDirs(dirs: Map<string, DirEntry>): void;
|
|
7
|
+
export declare function isChildOf(parent: string, child: string): boolean;
|
|
8
|
+
export declare function matchesDirs(dirs: Map<string, DirEntry>, filepath: string): boolean;
|
package/package.json
CHANGED
|
@@ -1,37 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-add-dir",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "Add working directories to your OpenCode session with auto-approved permissions",
|
|
5
|
+
"author": "Cristian Fonseca <cfonsecacomas@gmail.com>",
|
|
5
6
|
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"bin": {
|
|
16
|
+
"opencode-add-dir-setup": "./bin/setup.mjs"
|
|
17
|
+
},
|
|
8
18
|
"files": [
|
|
9
|
-
"dist",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
19
|
+
"dist/",
|
|
20
|
+
"bin/",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
13
23
|
],
|
|
14
24
|
"scripts": {
|
|
15
|
-
"build": "tsc",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"@opencode-ai/plugin": "^1.0.0"
|
|
21
|
-
},
|
|
22
|
-
"devDependencies": {
|
|
23
|
-
"@types/node": "^20.0.0",
|
|
24
|
-
"typescript": "^5.0.0"
|
|
25
|
-
},
|
|
26
|
-
"peerDependencies": {
|
|
27
|
-
"@opencode-ai/plugin": "^1.0.0"
|
|
25
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target node --format esm --external @opencode-ai/plugin && bun x tsc --emitDeclarationOnly",
|
|
26
|
+
"deploy": "bun build ./src/plugin.ts --outfile ~/.config/opencode/plugins/add-dir.js --target node --format esm --external @opencode-ai/plugin",
|
|
27
|
+
"test": "bun test",
|
|
28
|
+
"typecheck": "bun x tsc --noEmit",
|
|
29
|
+
"prepublishOnly": "bun run typecheck && bun test && bun run build"
|
|
28
30
|
},
|
|
29
31
|
"keywords": [
|
|
30
32
|
"opencode",
|
|
31
|
-
"plugin",
|
|
32
|
-
"add-
|
|
33
|
-
"
|
|
33
|
+
"opencode-plugin",
|
|
34
|
+
"add-dir",
|
|
35
|
+
"working-directory",
|
|
36
|
+
"permissions"
|
|
34
37
|
],
|
|
35
|
-
"
|
|
36
|
-
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/kuzeofficial/add-dir-opencode.git"
|
|
41
|
+
},
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@opencode-ai/plugin": ">=1.0.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@opencode-ai/plugin": "^1.1.14",
|
|
48
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
49
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
50
|
+
"@semantic-release/git": "^10.0.1",
|
|
51
|
+
"@semantic-release/github": "^12.0.6",
|
|
52
|
+
"@semantic-release/npm": "^13.1.5",
|
|
53
|
+
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
54
|
+
"@types/bun": "latest",
|
|
55
|
+
"semantic-release": "^25.0.3",
|
|
56
|
+
"typescript": "^5.8.3"
|
|
57
|
+
}
|
|
37
58
|
}
|
package/command/add-dir.md
DELETED
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAsRlD,QAAA,MAAM,YAAY,EAAE,MA6DnB,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/scripts/install.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
import os from 'os';
|
|
5
|
-
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
-
|
|
9
|
-
const COMMAND_SOURCE = path.join(__dirname, '..', 'command', 'add-dir.md');
|
|
10
|
-
|
|
11
|
-
function findOpencodeConfigDir(startDir = process.cwd()) {
|
|
12
|
-
const configFiles = ['opencode.jsonc', 'opencode.json'];
|
|
13
|
-
let currentDir = startDir;
|
|
14
|
-
|
|
15
|
-
while (currentDir !== path.parse(currentDir).root) {
|
|
16
|
-
for (const configFile of configFiles) {
|
|
17
|
-
const configPath = path.join(currentDir, configFile);
|
|
18
|
-
if (fs.existsSync(configPath)) {
|
|
19
|
-
return currentDir;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
currentDir = path.dirname(currentDir);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const defaultConfigDir = path.join(os.homedir(), '.config', 'opencode');
|
|
26
|
-
if (fs.existsSync(defaultConfigDir)) {
|
|
27
|
-
return defaultConfigDir;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
const opencodeDir = findOpencodeConfigDir();
|
|
35
|
-
|
|
36
|
-
if (!opencodeDir) {
|
|
37
|
-
console.log('ℹ opencode-add-dir: Could not find opencode config directory, skipping command file installation');
|
|
38
|
-
process.exit(0);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const commandDir = path.join(opencodeDir, 'command');
|
|
42
|
-
const commandDest = path.join(commandDir, 'add-dir.md');
|
|
43
|
-
|
|
44
|
-
if (!fs.existsSync(commandDir)) {
|
|
45
|
-
fs.mkdirSync(commandDir, { recursive: true });
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (fs.existsSync(COMMAND_SOURCE)) {
|
|
49
|
-
fs.copyFileSync(COMMAND_SOURCE, commandDest);
|
|
50
|
-
console.log('✓ opencode-add-dir: Command file installed to', commandDest);
|
|
51
|
-
} else {
|
|
52
|
-
console.error('✗ opencode-add-dir: Command source file not found at', COMMAND_SOURCE);
|
|
53
|
-
process.exit(1);
|
|
54
|
-
}
|
|
55
|
-
} catch (error) {
|
|
56
|
-
console.error('✗ opencode-add-dir: Failed to install command file:', error.message);
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|