omp-mode-switch 1.0.0
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 +84 -0
- package/README.zh-CN.md +84 -0
- package/mode-switch.ts +148 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
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
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# omp-mode-switch
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/omp-mode-switch)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
An OMP plugin that lets you switch between three permission modes at runtime. Press **Tab** to cycle, or use `/mode <ask|auto|plan>`.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
omp plugin install omp-mode-switch
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Restart OMP (or start a new session) to activate.
|
|
17
|
+
|
|
18
|
+
### Manual
|
|
19
|
+
|
|
20
|
+
Copy `mode-switch.ts` to `~/.omp/agent/extensions/` or your project's `.omp/extensions/`.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Modes
|
|
25
|
+
|
|
26
|
+
| Mode | Behavior |
|
|
27
|
+
|--------|----------|
|
|
28
|
+
| `ask` | Every tool call prompts for your confirmation (default) |
|
|
29
|
+
| `auto` | All tool calls are approved automatically |
|
|
30
|
+
| `plan` | Read-only tools only + `.md` file writing. Dangerous `bash` commands are blocked |
|
|
31
|
+
|
|
32
|
+
### Switching
|
|
33
|
+
|
|
34
|
+
| Action | Effect |
|
|
35
|
+
|--------------|--------|
|
|
36
|
+
| **Tab** | Cycle: `ask → auto → plan → ask` |
|
|
37
|
+
| `/mode ask` | Switch to ask mode |
|
|
38
|
+
| `/mode auto` | Switch to auto mode |
|
|
39
|
+
| `/mode plan` | Switch to plan mode |
|
|
40
|
+
|
|
41
|
+
### Status indicator
|
|
42
|
+
|
|
43
|
+
The current mode is shown as a colored widget at the bottom of the editor area:
|
|
44
|
+
|
|
45
|
+
- **ASK** (yellow)
|
|
46
|
+
- **AUTO** (green)
|
|
47
|
+
- **PLAN** (blue-cyan)
|
|
48
|
+
|
|
49
|
+
## Example workflow
|
|
50
|
+
|
|
51
|
+
1. **Explore** — Use `plan` mode to read through the codebase safely
|
|
52
|
+
2. **Plan** — Write `.md` plans in `plan` mode
|
|
53
|
+
3. **Execute** — Switch to `auto` to run changes
|
|
54
|
+
4. **Review** — Switch to `ask` for step-by-step confirmation on critical operations
|
|
55
|
+
|
|
56
|
+
The mode resets to `ask` on each new session.
|
|
57
|
+
|
|
58
|
+
## How it works
|
|
59
|
+
|
|
60
|
+
- Registers a **Tab** shortcut via `pi.registerShortcut()`
|
|
61
|
+
- Registers a `/mode` command via `pi.registerCommand()`
|
|
62
|
+
- Intercepts `tool_call` events to block destructive tools in `plan` mode
|
|
63
|
+
- Restricts tools via `pi.setActiveTools()` in plan mode
|
|
64
|
+
- Shows status via `ctx.ui.setWidget()` with colored text
|
|
65
|
+
|
|
66
|
+
## Development
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/your-username/omp-mode-switch.git
|
|
70
|
+
cd omp-mode-switch
|
|
71
|
+
# Make changes to mode-switch.ts
|
|
72
|
+
# Copy to ~/.omp/agent/extensions/ and restart OMP to test
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Publishing
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npm login
|
|
79
|
+
npm publish
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
MIT
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# omp-mode-switch
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/omp-mode-switch)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
一个 OMP 插件,让你在运行中切换三种权限模式。按 **Tab** 键循环切换,或使用 `/mode <ask|auto|plan>` 命令。
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 安装
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
omp plugin install omp-mode-switch
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
重启 OMP(或新建会话)即可生效。
|
|
17
|
+
|
|
18
|
+
### 手动安装
|
|
19
|
+
|
|
20
|
+
将 `mode-switch.ts` 复制到 `~/.omp/agent/extensions/` 或项目的 `.omp/extensions/`。
|
|
21
|
+
|
|
22
|
+
## 使用方法
|
|
23
|
+
|
|
24
|
+
### 模式
|
|
25
|
+
|
|
26
|
+
| 模式 | 行为 |
|
|
27
|
+
|--------|------|
|
|
28
|
+
| `ask` | 每个工具调用都需确认(默认) |
|
|
29
|
+
| `auto` | 自动批准所有工具调用 |
|
|
30
|
+
| `plan` | 仅允许只读工具和 `.md` 文件写入,危险 bash 命令被拦截 |
|
|
31
|
+
|
|
32
|
+
### 切换
|
|
33
|
+
|
|
34
|
+
| 操作 | 效果 |
|
|
35
|
+
|---------------|------|
|
|
36
|
+
| **Tab** | 循环:`ask → auto → plan → ask` |
|
|
37
|
+
| `/mode ask` | 切换到确认模式 |
|
|
38
|
+
| `/mode auto` | 切换到自动模式 |
|
|
39
|
+
| `/mode plan` | 切换到计划模式 |
|
|
40
|
+
|
|
41
|
+
### 状态指示
|
|
42
|
+
|
|
43
|
+
当前模式以彩色指示器显示在编辑器底部:
|
|
44
|
+
|
|
45
|
+
- **ASK**(黄色)
|
|
46
|
+
- **AUTO**(绿色)
|
|
47
|
+
- **PLAN**(蓝青色)
|
|
48
|
+
|
|
49
|
+
## 工作流程示例
|
|
50
|
+
|
|
51
|
+
1. **探索** — 用 `plan` 模式安全阅读代码
|
|
52
|
+
2. **规划** — 在 `plan` 模式下编写 `.md` 规划文档
|
|
53
|
+
3. **执行** — 切换到 `auto` 执行修改
|
|
54
|
+
4. **审查** — 切换到 `ask` 对关键操作逐次确认
|
|
55
|
+
|
|
56
|
+
每次新会话自动重置为 `ask` 模式。
|
|
57
|
+
|
|
58
|
+
## 工作原理
|
|
59
|
+
|
|
60
|
+
- 通过 `pi.registerShortcut()` 注册 Tab 快捷键
|
|
61
|
+
- 通过 `pi.registerCommand()` 注册 `/mode` 命令
|
|
62
|
+
- 拦截 `tool_call` 事件,在 `plan` 模式下阻止破坏性工具
|
|
63
|
+
- 在 `plan` 模式下通过 `pi.setActiveTools()` 限制可用工具
|
|
64
|
+
- 通过 `ctx.ui.setWidget()` 显示彩色状态指示
|
|
65
|
+
|
|
66
|
+
## 开发
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/your-username/omp-mode-switch.git
|
|
70
|
+
cd omp-mode-switch
|
|
71
|
+
# 修改 mode-switch.ts
|
|
72
|
+
# 复制到 ~/.omp/agent/extensions/ 后重启 OMP 测试
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 发布
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npm login
|
|
79
|
+
npm publish
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 许可证
|
|
83
|
+
|
|
84
|
+
MIT
|
package/mode-switch.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mode Switch - OMP permission mode switching plugin
|
|
3
|
+
*
|
|
4
|
+
* Three modes:
|
|
5
|
+
* ask Prompt for every tool call (default)
|
|
6
|
+
* auto Auto-approve all tool calls
|
|
7
|
+
* plan Read-only mode: only read/search tools + .md writing allowed
|
|
8
|
+
*
|
|
9
|
+
* Shortcut: Tab cycles ask → auto → plan → ask
|
|
10
|
+
* Command: /mode <ask|auto|plan>
|
|
11
|
+
* Display: Colored widget at editor bottom, e.g. [ASK] [AUTO] [PLAN]
|
|
12
|
+
*
|
|
13
|
+
* Install:
|
|
14
|
+
* omp plugin install omp-mode-switch
|
|
15
|
+
* (Restart OMP to activate)
|
|
16
|
+
*
|
|
17
|
+
* Manual install: copy to ~/.omp/agent/extensions/mode-switch.ts
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { ExtensionAPI, ExtensionContext } from "@oh-my-pi/pi-coding-agent";
|
|
21
|
+
|
|
22
|
+
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
type Mode = "ask" | "auto" | "plan";
|
|
25
|
+
|
|
26
|
+
// ─── Read-only tools allowed in plan mode ───────────────────────────────────
|
|
27
|
+
|
|
28
|
+
const READ_ONLY_TOOLS = [
|
|
29
|
+
"read", "grep", "glob", "search", "ast_grep",
|
|
30
|
+
"codegraph_search", "codegraph_explore", "codegraph_files",
|
|
31
|
+
"codegraph_status", "codegraph_node", "codegraph_callers",
|
|
32
|
+
"codegraph_callees", "codegraph_impact",
|
|
33
|
+
"ls", "find", "head", "tail", "cat", "less",
|
|
34
|
+
] as const;
|
|
35
|
+
|
|
36
|
+
const roSet = new Set<string>(READ_ONLY_TOOLS);
|
|
37
|
+
|
|
38
|
+
// ─── Bash safety checks ─────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
const SAFE_BASH_COMMANDS = new Set([
|
|
41
|
+
"cat", "head", "tail", "less", "more", "ls", "find", "grep", "rg",
|
|
42
|
+
"git", "echo", "printf", "pwd", "which", "dirname", "basename",
|
|
43
|
+
"wc", "sort", "uniq", "cut", "date", "uptime", "whoami", "id", "env",
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
const DANGEROUS_PATTERNS = [
|
|
47
|
+
" rm ", " rmdir ", " mv ", " cp ", " dd ", " chmod ",
|
|
48
|
+
" chown ", " sudo ", " su ", " &&", " ||", " >", " >>", " |",
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
function isSafeBash(cmd: string): boolean {
|
|
52
|
+
const t = cmd.trim();
|
|
53
|
+
if (!t) return false;
|
|
54
|
+
for (const p of DANGEROUS_PATTERNS) {
|
|
55
|
+
if (t.includes(p)) return false;
|
|
56
|
+
}
|
|
57
|
+
return SAFE_BASH_COMMANDS.has(t.split(/\s+/)[0]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
function isMdWrite(name: string, input: Record<string, unknown>): boolean {
|
|
63
|
+
if (name !== "write") return false;
|
|
64
|
+
return typeof input.path === "string" && (input.path as string).endsWith(".md");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function coloredMode(ctx: ExtensionContext, m: Mode): string {
|
|
68
|
+
const t = ctx.ui.theme;
|
|
69
|
+
if (m === "ask") return t.fg("warning", "ASK");
|
|
70
|
+
if (m === "auto") return t.fg("success", "AUTO");
|
|
71
|
+
return t.fg("accent", "PLAN");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ─── Plugin entry ───────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
export default function modeSwitch(pi: ExtensionAPI) {
|
|
77
|
+
let mode: Mode = "ask";
|
|
78
|
+
const CYCLE: Mode[] = ["ask", "auto", "plan"];
|
|
79
|
+
|
|
80
|
+
function showWidget(ctx: ExtensionContext | undefined) {
|
|
81
|
+
if (!ctx) return;
|
|
82
|
+
ctx.ui.setWidget("mode-switch", [coloredMode(ctx, mode)], {
|
|
83
|
+
placement: "belowEditor",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function switchTo(next: Mode, ctx: ExtensionContext | undefined) {
|
|
88
|
+
if (mode === next || !ctx) return;
|
|
89
|
+
mode = next;
|
|
90
|
+
showWidget(ctx);
|
|
91
|
+
|
|
92
|
+
if (mode === "plan") {
|
|
93
|
+
pi.setActiveTools([...READ_ONLY_TOOLS, "bash", "write", "edit"]);
|
|
94
|
+
} else {
|
|
95
|
+
pi.setActiveTools(pi.getAllTools());
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Tab key to cycle modes
|
|
100
|
+
pi.registerShortcut("Tab", {
|
|
101
|
+
description: "Cycle mode: ask → auto → plan → ask",
|
|
102
|
+
handler: (ctx) => {
|
|
103
|
+
const i = CYCLE.indexOf(mode);
|
|
104
|
+
switchTo(CYCLE[(i + 1) % CYCLE.length], ctx);
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// /mode command
|
|
109
|
+
pi.registerCommand("mode", {
|
|
110
|
+
description: "Switch permission mode: /mode <ask|auto|plan>",
|
|
111
|
+
getArgumentCompletions: () => ["ask", "auto", "plan"],
|
|
112
|
+
handler: async (args, ctx) => {
|
|
113
|
+
const target = args[0]?.toLowerCase() as Mode | undefined;
|
|
114
|
+
if (target && CYCLE.includes(target)) {
|
|
115
|
+
switchTo(target, ctx);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Plan mode: block non-readonly tools
|
|
121
|
+
pi.on("tool_call", async (event) => {
|
|
122
|
+
if (mode !== "plan") return;
|
|
123
|
+
|
|
124
|
+
const { toolName } = event;
|
|
125
|
+
if (roSet.has(toolName)) return;
|
|
126
|
+
|
|
127
|
+
if (toolName === "bash") {
|
|
128
|
+
const input = (event as Record<string, unknown>).input as Record<string, unknown> | undefined;
|
|
129
|
+
const cmd = input?.command as string | undefined;
|
|
130
|
+
if (cmd && isSafeBash(cmd)) return;
|
|
131
|
+
return { block: true, reason: "Blocked by plan mode: dangerous command" };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if ((toolName === "write" || toolName === "edit") && (event as any).input) {
|
|
135
|
+
const input = (event as any).input as Record<string, unknown>;
|
|
136
|
+
if (isMdWrite(toolName, input)) return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return { block: true, reason: "Blocked by plan mode: read-only tools only" };
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Reset to ask on new session
|
|
143
|
+
pi.on("session_start", (_event, ctx) => {
|
|
144
|
+
mode = "ask";
|
|
145
|
+
showWidget(ctx);
|
|
146
|
+
pi.setActiveTools(pi.getAllTools());
|
|
147
|
+
});
|
|
148
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "omp-mode-switch",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "OMP plugin: switch between ask / auto / plan permission modes with Tab key",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "mode-switch.ts",
|
|
7
|
+
"scripts": {},
|
|
8
|
+
"files": [
|
|
9
|
+
"mode-switch.ts",
|
|
10
|
+
"README.md",
|
|
11
|
+
"LICENSE"
|
|
12
|
+
],
|
|
13
|
+
"omp": {
|
|
14
|
+
"name": "Mode Switch",
|
|
15
|
+
"description": "Switch between ask / auto / plan permission modes with Tab key",
|
|
16
|
+
"version": "1.0.0",
|
|
17
|
+
"author": "OMP Community",
|
|
18
|
+
"extensions": {
|
|
19
|
+
"mode-switch": {
|
|
20
|
+
"description": "Mode Switch extension - permission mode toggle",
|
|
21
|
+
"main": "mode-switch.ts"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"omp",
|
|
27
|
+
"plugin",
|
|
28
|
+
"mode",
|
|
29
|
+
"permission",
|
|
30
|
+
"security",
|
|
31
|
+
"ask",
|
|
32
|
+
"auto",
|
|
33
|
+
"plan",
|
|
34
|
+
"oh-my-pi"
|
|
35
|
+
],
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/your-username/omp-mode-switch.git"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/your-username/omp-mode-switch/issues"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/your-username/omp-mode-switch#readme",
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18"
|
|
47
|
+
}
|
|
48
|
+
}
|