omnish 1.6.6 → 2.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/CHANGELOG.md +32 -0
- package/README.md +26 -18
- package/config.example.json +1 -0
- package/dist/downloads/omnish-claude/install.sh +174 -0
- package/dist/downloads/omnish-claude/uninstall.sh +114 -0
- package/dist/downloads/omnish-claude.tar.gz +0 -0
- package/dist/downloads/omnish-cursor/install.sh +162 -0
- package/dist/downloads/omnish-cursor/uninstall.sh +107 -0
- package/dist/downloads/omnish-cursor.tar.gz +0 -0
- package/dist/index.js +424 -392
- package/dist/omnish-claude/README.md +80 -0
- package/dist/omnish-claude/hooks/omnish-notify.py +213 -0
- package/dist/omnish-claude/hooks/omnish-notify.sh +13 -0
- package/dist/omnish-claude/install.sh +174 -0
- package/dist/omnish-claude/omnish-notify.json.example +8 -0
- package/dist/omnish-claude/scripts/doctor.sh +99 -0
- package/dist/omnish-claude/skill/SKILL.md +39 -0
- package/dist/omnish-claude/skill/files-and-sharing.md +94 -0
- package/dist/omnish-claude/skill/notifications.md +113 -0
- package/dist/omnish-claude/skill/setup-paths.md +81 -0
- package/dist/omnish-claude/uninstall.sh +114 -0
- package/dist/omnish-cursor/README.md +176 -0
- package/dist/omnish-cursor/hooks/__pycache__/omnish-notify.cpython-313.pyc +0 -0
- package/dist/omnish-cursor/hooks/omnish-notify.py +563 -0
- package/dist/omnish-cursor/hooks/omnish-notify.sh +13 -0
- package/dist/omnish-cursor/hooks/omnish-session-start.sh +48 -0
- package/dist/omnish-cursor/install.sh +162 -0
- package/dist/omnish-cursor/omnish-notify.json.example +13 -0
- package/dist/omnish-cursor/rules/omnish-notify.mdc +45 -0
- package/dist/omnish-cursor/scripts/doctor.sh +126 -0
- package/dist/omnish-cursor/skill/SKILL.md +129 -0
- package/dist/omnish-cursor/skill/files-and-sharing.md +94 -0
- package/dist/omnish-cursor/skill/notifications.md +155 -0
- package/dist/omnish-cursor/skill/setup-paths.md +81 -0
- package/dist/omnish-cursor/uninstall.sh +107 -0
- package/dist/ui/assets/{index-aUJGrxrr.js → index-BwG51a2I.js} +10 -10
- package/dist/ui/index.html +16 -1
- package/package.json +12 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.0.0] - 2026-06-18
|
|
11
|
+
|
|
12
|
+
### Breaking
|
|
13
|
+
|
|
14
|
+
- **Platform routing:** unprefixed chat commands route to the **default device only**; use `>label cmd` for other hosts. Dashboard peer bindings no longer affect inbound dispatch ([docs/adr/0013-platform-device-prefix-routing.md](docs/adr/0013-platform-device-prefix-routing.md)).
|
|
15
|
+
- **Cluster CLI:** `omnish cluster here` removed — use chat `/c here` or `omnish cluster use <sender> <label>`.
|
|
16
|
+
- **Cluster chat:** `/c step-down` removed; per-sender binding via `/c unuse`.
|
|
17
|
+
- **`clusterRole`:** informational only; no longer gates traffic.
|
|
18
|
+
- **Telemetry default-on:** gateways send anonymous heartbeats unless `OMNISH_TELEMETRY=0` or `telemetryEnabled: false` ([docs/adr/0017-default-on-instance-telemetry.md](docs/adr/0017-default-on-instance-telemetry.md)).
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Connectors:** Signal integration; OAuth link flows for Slack, Discord, and Twitch; Slack DM gateway assets.
|
|
23
|
+
- **Platform:** user auth (signup/login, email verification, password reset, SMTP templates); default device management; `/devices` routing commands; admin telemetry APIs and AdminView improvements.
|
|
24
|
+
- **Integrations:** Omnish Cursor and Claude Code install/notify paths; enhanced `omnish-notify` config.
|
|
25
|
+
- **Gateway:** lifecycle broadcast to allowlisted peers on startup/shutdown (with WhatsApp JID fix).
|
|
26
|
+
- **Cluster:** WebSocket keep-alive; attached auto-binding on inbound; enhanced cluster state handling.
|
|
27
|
+
- **CLI/UI:** `/apps start` flags; platform dashboard mobile nav; analytics hooks; `.nvmrc`.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- Connector management and user allowlist handling.
|
|
32
|
+
- Dependencies for expanded channel support.
|
|
33
|
+
- Postinstall / native module checks.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- Signal QR display issue.
|
|
38
|
+
- AdminView fetch error handling.
|
|
39
|
+
|
|
40
|
+
[2.0.0]: https://github.com/labKnowledge/omnish/compare/v1.6.6...v2.0.0
|
|
41
|
+
|
|
10
42
|
## [1.6.6] - 2026-05-30
|
|
11
43
|
|
|
12
44
|
### Fixed
|
package/README.md
CHANGED
|
@@ -79,6 +79,12 @@ Full steps for systemd, launchd, and Task Scheduler — **[docs/guides/backgroun
|
|
|
79
79
|
|
|
80
80
|
Run `omnish status` to see the resolved data directory and auth path.
|
|
81
81
|
|
|
82
|
+
## Telemetry (default-on, opt-out)
|
|
83
|
+
|
|
84
|
+
While **`omnish run`** is active, omnish sends anonymous instance health metadata to the platform telemetry endpoint (default: `https://tunnel.omnish.dev`): install id (`~/.omnish/node-id`), omnish version, OS/arch, Node version, gateway mode, and enabled channel count. No message content, allowlists, or file paths are included.
|
|
85
|
+
|
|
86
|
+
**Opt out:** `OMNISH_TELEMETRY=0` or `omnish config set telemetryEnabled false`. Override the endpoint with `telemetryUrl` in config.
|
|
87
|
+
|
|
82
88
|
While **`omnish run`** is active, a localhost control channel may write **`gateway-control.json`** (and a random high port on `127.0.0.1`) so **`omnish i`** can request outbound file sends.
|
|
83
89
|
|
|
84
90
|
Per-chat shell cwd is in `sessions.json`; **user shortcuts** are stored in `shortcuts.json` (same data directory).
|
|
@@ -157,24 +163,24 @@ Output is **debounced** (`appsFlushMs`, default 300 ms), **throttled** (`appsMin
|
|
|
157
163
|
|
|
158
164
|
### `/apps` cheat sheet
|
|
159
165
|
|
|
160
|
-
| Command
|
|
161
|
-
|
|
|
162
|
-
| `/apps start <name> <cmd…>`
|
|
163
|
-
| `/apps attach <name>`
|
|
164
|
-
| `/apps detach`
|
|
165
|
-
| `/apps list`
|
|
166
|
-
| `/apps info <name>` / `/apps get <name>`
|
|
167
|
-
| `/apps send <name> <text>`
|
|
168
|
-
| `>name text`
|
|
169
|
-
| `/apps key <name> KEY[,KEY…]`
|
|
170
|
-
| `/apps tail <name> [lines]`
|
|
171
|
-
| `/apps since <name>`
|
|
172
|
-
| `/apps mute <name>` / `/apps unmute <name>`
|
|
173
|
-
| `/apps raw <name> on\|off`
|
|
174
|
-
| `/apps resize <name> <cols> <rows>`
|
|
175
|
-
| `/apps stop <name>`
|
|
176
|
-
| `/apps kill <name>`
|
|
177
|
-
| `/apps rm <name>`
|
|
166
|
+
| Command | Action |
|
|
167
|
+
| --------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
168
|
+
| `/apps start <name> [--mute\|-mute\|--detach\|-d] <cmd…>` | Spawn in session cwd; **auto-attaches** unless `--detach`; `--mute` skips chat streaming. |
|
|
169
|
+
| `/apps attach <name>` | Focus plain DMs on that running session. |
|
|
170
|
+
| `/apps detach` | Clear focus. |
|
|
171
|
+
| `/apps list` | Sessions for this chat; `*` marks focus. Shows `attached: …` or `(no focus)`. |
|
|
172
|
+
| `/apps info <name>` / `/apps get <name>` | Cmd, cwd, env key count, terminal size, ring bytes, log path/size, mute/raw. |
|
|
173
|
+
| `/apps send <name> <text>` | Write `text` + newline (no attach). |
|
|
174
|
+
| `>name text` | Same as `/apps send`. |
|
|
175
|
+
| `/apps key <name> KEY[,KEY…]` | Special keys: `Enter`, `Tab`, `Esc`, arrows, `^C`, `ctrl+d`, `\x1b`, … |
|
|
176
|
+
| `/apps tail <name> [lines]` | Last _lines_ of log (default `appsLogTailLines`). |
|
|
177
|
+
| `/apps since <name>` | New log bytes since your last `/apps since` for that name. |
|
|
178
|
+
| `/apps mute <name>` / `/apps unmute <name>` | Pause / resume streaming to the chat (log still grows). |
|
|
179
|
+
| `/apps raw <name> on\|off` | Keep ANSI in streamed messages when `on`. |
|
|
180
|
+
| `/apps resize <name> <cols> <rows>` | Resize terminal. |
|
|
181
|
+
| `/apps stop <name>` | SIGTERM; SIGKILL after 5 s if still alive. |
|
|
182
|
+
| `/apps kill <name>` | SIGKILL immediately. |
|
|
183
|
+
| `/apps rm <name>` | Only after the process has **exited**; removes metadata + log file. |
|
|
178
184
|
|
|
179
185
|
To send a literal `!`, `/`, or `>` to the attached program, use **`/apps send <name> …`** or **`>name …`**.
|
|
180
186
|
|
|
@@ -266,6 +272,8 @@ Index and curated paths: **[docs/README.md](docs/README.md)**.
|
|
|
266
272
|
| Documentation search (`/s`, `omnish search`) | [docs/features/docs-search-from-chat.md](docs/features/docs-search-from-chat.md) |
|
|
267
273
|
| System agents + `/run` | [docs/guides/system-agents-and-run.md](docs/guides/system-agents-and-run.md) |
|
|
268
274
|
| MCP IDE spike (experimental) | [contrib/mcp-spike/README.md](contrib/mcp-spike/README.md) |
|
|
275
|
+
| Cursor integration (hooks + skill) | [contrib/omnish-cursor/README.md](contrib/omnish-cursor/README.md) |
|
|
276
|
+
| Claude Code integration (hooks + skill) | [contrib/omnish-claude/README.md](contrib/omnish-claude/README.md) |
|
|
269
277
|
| Telegram | [docs/telegram-integration-notes.md](docs/telegram-integration-notes.md) |
|
|
270
278
|
| Send/receive files (`/send` selectors, `/sendto` destinations) | [docs/files-send-receive.md](docs/files-send-receive.md) |
|
|
271
279
|
| Troubleshooting | [docs/advanced/troubleshooting.md](docs/advanced/troubleshooting.md) |
|
package/config.example.json
CHANGED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install Omnish Claude Code bundle: Stop hook + skill into ~/.claude
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
OMNISH_CLAUDE_BUNDLE_URL="${OMNISH_CLAUDE_BUNDLE_URL:-https://omnish.dev/downloads/omnish-claude.tar.gz}"
|
|
6
|
+
_TMP_BUNDLE_DIR=""
|
|
7
|
+
|
|
8
|
+
need() {
|
|
9
|
+
command -v "$1" >/dev/null 2>&1 || {
|
|
10
|
+
echo "error: required command not found: $1" >&2
|
|
11
|
+
exit 1
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
cleanup() {
|
|
16
|
+
if [[ -n "$_TMP_BUNDLE_DIR" && -d "$_TMP_BUNDLE_DIR" ]]; then
|
|
17
|
+
rm -rf "$_TMP_BUNDLE_DIR"
|
|
18
|
+
fi
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
_SCRIPT="${BASH_SOURCE[0]:-}"
|
|
22
|
+
if [[ -n "$_SCRIPT" && -f "$_SCRIPT" ]]; then
|
|
23
|
+
ROOT="$(cd "$(dirname "$_SCRIPT")" && pwd)"
|
|
24
|
+
else
|
|
25
|
+
need curl
|
|
26
|
+
need tar
|
|
27
|
+
_TMP_BUNDLE_DIR="$(mktemp -d "${TMPDIR:-/tmp}/omnish-claude.XXXXXX")"
|
|
28
|
+
trap cleanup EXIT
|
|
29
|
+
echo "Downloading Omnish Claude Code bundle..." >&2
|
|
30
|
+
curl -fsSL "$OMNISH_CLAUDE_BUNDLE_URL" | tar -xzf - -C "$_TMP_BUNDLE_DIR"
|
|
31
|
+
if [[ ! -d "$_TMP_BUNDLE_DIR/omnish-claude" ]]; then
|
|
32
|
+
echo "error: bundle missing omnish-claude/ top-level directory" >&2
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
ROOT="$_TMP_BUNDLE_DIR/omnish-claude"
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
CLAUDE_DIR="${CLAUDE_DIR:-$HOME/.claude}"
|
|
39
|
+
HOOKS_DIR="$CLAUDE_DIR/hooks"
|
|
40
|
+
SKILL_DIR="$CLAUDE_DIR/skills/omnish"
|
|
41
|
+
SETTINGS_JSON="$CLAUDE_DIR/settings.json"
|
|
42
|
+
HOOK_CMD="$HOOKS_DIR/omnish-notify.sh"
|
|
43
|
+
|
|
44
|
+
INSTALL_HOOKS=1
|
|
45
|
+
INSTALL_SKILL=1
|
|
46
|
+
|
|
47
|
+
usage() {
|
|
48
|
+
cat <<EOF
|
|
49
|
+
Usage: $(basename "$0") [options]
|
|
50
|
+
|
|
51
|
+
Install Omnish Claude Code integration (Stop hook, skill, notify config).
|
|
52
|
+
|
|
53
|
+
Options:
|
|
54
|
+
--hooks-only Install only Stop hook + notify config
|
|
55
|
+
--skill-only Install only the omnish Claude skill (+ doctor script)
|
|
56
|
+
-h, --help Show this help
|
|
57
|
+
EOF
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
while [[ $# -gt 0 ]]; do
|
|
61
|
+
case "$1" in
|
|
62
|
+
--hooks-only)
|
|
63
|
+
INSTALL_HOOKS=1
|
|
64
|
+
INSTALL_SKILL=0
|
|
65
|
+
shift
|
|
66
|
+
;;
|
|
67
|
+
--skill-only)
|
|
68
|
+
INSTALL_HOOKS=0
|
|
69
|
+
INSTALL_SKILL=1
|
|
70
|
+
shift
|
|
71
|
+
;;
|
|
72
|
+
-h | --help)
|
|
73
|
+
usage
|
|
74
|
+
exit 0
|
|
75
|
+
;;
|
|
76
|
+
*)
|
|
77
|
+
echo "error: unknown option: $1" >&2
|
|
78
|
+
usage >&2
|
|
79
|
+
exit 1
|
|
80
|
+
;;
|
|
81
|
+
esac
|
|
82
|
+
done
|
|
83
|
+
|
|
84
|
+
if [[ "$INSTALL_HOOKS" -eq 1 ]]; then
|
|
85
|
+
need python3
|
|
86
|
+
need omnish
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
if [[ "$INSTALL_HOOKS" -eq 1 ]]; then
|
|
90
|
+
mkdir -p "$HOOKS_DIR"
|
|
91
|
+
|
|
92
|
+
install -m 755 "$ROOT/hooks/omnish-notify.sh" "$HOOKS_DIR/omnish-notify.sh"
|
|
93
|
+
install -m 644 "$ROOT/hooks/omnish-notify.py" "$HOOKS_DIR/omnish-notify.py"
|
|
94
|
+
|
|
95
|
+
if [[ ! -f "$CLAUDE_DIR/omnish-notify.json" ]]; then
|
|
96
|
+
install -m 644 "$ROOT/omnish-notify.json.example" "$CLAUDE_DIR/omnish-notify.json"
|
|
97
|
+
echo "created $CLAUDE_DIR/omnish-notify.json"
|
|
98
|
+
else
|
|
99
|
+
echo "kept existing $CLAUDE_DIR/omnish-notify.json"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
python3 - "$SETTINGS_JSON" "$HOOK_CMD" <<'PY'
|
|
103
|
+
import json
|
|
104
|
+
import sys
|
|
105
|
+
from pathlib import Path
|
|
106
|
+
|
|
107
|
+
settings_path = Path(sys.argv[1])
|
|
108
|
+
hook_cmd = sys.argv[2]
|
|
109
|
+
entry = {
|
|
110
|
+
"hooks": [
|
|
111
|
+
{
|
|
112
|
+
"type": "command",
|
|
113
|
+
"command": hook_cmd,
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if settings_path.exists():
|
|
119
|
+
try:
|
|
120
|
+
data = json.loads(settings_path.read_text(encoding="utf-8"))
|
|
121
|
+
except json.JSONDecodeError:
|
|
122
|
+
data = {}
|
|
123
|
+
else:
|
|
124
|
+
data = {}
|
|
125
|
+
|
|
126
|
+
hooks = data.setdefault("hooks", {})
|
|
127
|
+
existing = hooks.get("Stop") or []
|
|
128
|
+
if not isinstance(existing, list):
|
|
129
|
+
existing = []
|
|
130
|
+
|
|
131
|
+
found = False
|
|
132
|
+
for group in existing:
|
|
133
|
+
if not isinstance(group, dict):
|
|
134
|
+
continue
|
|
135
|
+
inner = group.get("hooks") or []
|
|
136
|
+
if any(isinstance(h, dict) and h.get("command") == hook_cmd for h in inner):
|
|
137
|
+
found = True
|
|
138
|
+
break
|
|
139
|
+
|
|
140
|
+
if not found:
|
|
141
|
+
existing.append(entry)
|
|
142
|
+
hooks["Stop"] = existing
|
|
143
|
+
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
144
|
+
settings_path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
|
|
145
|
+
print(f"updated {settings_path} (added Stop hook)")
|
|
146
|
+
else:
|
|
147
|
+
print(f"Stop hook already registered in {settings_path}")
|
|
148
|
+
PY
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
if [[ "$INSTALL_SKILL" -eq 1 ]]; then
|
|
152
|
+
mkdir -p "$SKILL_DIR/scripts"
|
|
153
|
+
install -m 644 "$ROOT/skill/SKILL.md" "$SKILL_DIR/SKILL.md"
|
|
154
|
+
install -m 644 "$ROOT/skill/setup-paths.md" "$SKILL_DIR/setup-paths.md"
|
|
155
|
+
install -m 644 "$ROOT/skill/files-and-sharing.md" "$SKILL_DIR/files-and-sharing.md"
|
|
156
|
+
install -m 644 "$ROOT/skill/notifications.md" "$SKILL_DIR/notifications.md"
|
|
157
|
+
install -m 755 "$ROOT/scripts/doctor.sh" "$SKILL_DIR/scripts/doctor.sh"
|
|
158
|
+
echo "installed skill to $SKILL_DIR"
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
cat <<EOF
|
|
162
|
+
|
|
163
|
+
Installed Omnish Claude Code bundle.
|
|
164
|
+
|
|
165
|
+
Next steps:
|
|
166
|
+
1. Ensure omnish gateway is running: omnish run
|
|
167
|
+
2. Platform attached: omnish config show platform
|
|
168
|
+
Local mode: omnish link && omnish allow +E164 (or tg:id)
|
|
169
|
+
3. Restart Claude Code (hooks reload from settings.json)
|
|
170
|
+
4. Run: bash $SKILL_DIR/scripts/doctor.sh
|
|
171
|
+
|
|
172
|
+
Config: $CLAUDE_DIR/omnish-notify.json
|
|
173
|
+
Default peer: * (all allowlisted contacts)
|
|
174
|
+
EOF
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Remove Omnish Claude Code bundle entries from ~/.claude (keeps unrelated hooks).
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
CLAUDE_DIR="${CLAUDE_DIR:-$HOME/.claude}"
|
|
6
|
+
SETTINGS_JSON="$CLAUDE_DIR/settings.json"
|
|
7
|
+
HOOK_CMD="$CLAUDE_DIR/hooks/omnish-notify.sh"
|
|
8
|
+
|
|
9
|
+
REMOVE_HOOKS=1
|
|
10
|
+
REMOVE_SKILL=1
|
|
11
|
+
REMOVE_CONFIG=0
|
|
12
|
+
|
|
13
|
+
usage() {
|
|
14
|
+
cat <<EOF
|
|
15
|
+
Usage: $(basename "$0") [options]
|
|
16
|
+
|
|
17
|
+
Remove Omnish Claude Code integration installed by install.sh.
|
|
18
|
+
|
|
19
|
+
Options:
|
|
20
|
+
--hooks-only Remove Stop hook + hook scripts only
|
|
21
|
+
--skill-only Remove ~/.claude/skills/omnish only
|
|
22
|
+
--with-config Also delete ~/.claude/omnish-notify.json
|
|
23
|
+
-h, --help Show this help
|
|
24
|
+
EOF
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
while [[ $# -gt 0 ]]; do
|
|
28
|
+
case "$1" in
|
|
29
|
+
--hooks-only)
|
|
30
|
+
REMOVE_HOOKS=1
|
|
31
|
+
REMOVE_SKILL=0
|
|
32
|
+
shift
|
|
33
|
+
;;
|
|
34
|
+
--skill-only)
|
|
35
|
+
REMOVE_HOOKS=0
|
|
36
|
+
REMOVE_SKILL=1
|
|
37
|
+
shift
|
|
38
|
+
;;
|
|
39
|
+
--with-config)
|
|
40
|
+
REMOVE_CONFIG=1
|
|
41
|
+
shift
|
|
42
|
+
;;
|
|
43
|
+
-h | --help)
|
|
44
|
+
usage
|
|
45
|
+
exit 0
|
|
46
|
+
;;
|
|
47
|
+
*)
|
|
48
|
+
echo "error: unknown option: $1" >&2
|
|
49
|
+
usage >&2
|
|
50
|
+
exit 1
|
|
51
|
+
;;
|
|
52
|
+
esac
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
if [[ "$REMOVE_HOOKS" -eq 1 && -f "$SETTINGS_JSON" ]]; then
|
|
56
|
+
python3 - "$SETTINGS_JSON" "$HOOK_CMD" <<'PY'
|
|
57
|
+
import json
|
|
58
|
+
import sys
|
|
59
|
+
from pathlib import Path
|
|
60
|
+
|
|
61
|
+
settings_path = Path(sys.argv[1])
|
|
62
|
+
hook_cmd = sys.argv[2]
|
|
63
|
+
if not settings_path.is_file():
|
|
64
|
+
raise SystemExit(0)
|
|
65
|
+
data = json.loads(settings_path.read_text(encoding="utf-8"))
|
|
66
|
+
hooks = data.get("hooks") or {}
|
|
67
|
+
existing = hooks.get("Stop") or []
|
|
68
|
+
if not isinstance(existing, list):
|
|
69
|
+
raise SystemExit(0)
|
|
70
|
+
filtered = []
|
|
71
|
+
changed = False
|
|
72
|
+
for group in existing:
|
|
73
|
+
if not isinstance(group, dict):
|
|
74
|
+
filtered.append(group)
|
|
75
|
+
continue
|
|
76
|
+
inner = group.get("hooks") or []
|
|
77
|
+
new_inner = [
|
|
78
|
+
h for h in inner
|
|
79
|
+
if not (isinstance(h, dict) and h.get("command") == hook_cmd)
|
|
80
|
+
]
|
|
81
|
+
if len(new_inner) != len(inner):
|
|
82
|
+
changed = True
|
|
83
|
+
if new_inner:
|
|
84
|
+
group = dict(group)
|
|
85
|
+
group["hooks"] = new_inner
|
|
86
|
+
filtered.append(group)
|
|
87
|
+
if changed:
|
|
88
|
+
if filtered:
|
|
89
|
+
hooks["Stop"] = filtered
|
|
90
|
+
else:
|
|
91
|
+
hooks.pop("Stop", None)
|
|
92
|
+
settings_path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
|
|
93
|
+
print(f"updated {settings_path} (removed omnish Stop hook)")
|
|
94
|
+
else:
|
|
95
|
+
print(f"no omnish Stop hook in {settings_path}")
|
|
96
|
+
PY
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
if [[ "$REMOVE_HOOKS" -eq 1 ]]; then
|
|
100
|
+
rm -f "$CLAUDE_DIR/hooks/omnish-notify.sh" "$CLAUDE_DIR/hooks/omnish-notify.py"
|
|
101
|
+
echo "removed hook scripts"
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
if [[ "$REMOVE_SKILL" -eq 1 ]]; then
|
|
105
|
+
rm -rf "$CLAUDE_DIR/skills/omnish"
|
|
106
|
+
echo "removed $CLAUDE_DIR/skills/omnish"
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
if [[ "$REMOVE_CONFIG" -eq 1 ]]; then
|
|
110
|
+
rm -f "$CLAUDE_DIR/omnish-notify.json"
|
|
111
|
+
echo "removed $CLAUDE_DIR/omnish-notify.json"
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
echo "Done. Restart Claude Code if hooks were removed."
|
|
Binary file
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install Omnish Cursor bundle: hooks + skill + rule into ~/.cursor
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
OMNISH_CURSOR_BUNDLE_URL="${OMNISH_CURSOR_BUNDLE_URL:-https://omnish.dev/downloads/omnish-cursor.tar.gz}"
|
|
6
|
+
_TMP_BUNDLE_DIR=""
|
|
7
|
+
|
|
8
|
+
need() {
|
|
9
|
+
command -v "$1" >/dev/null 2>&1 || {
|
|
10
|
+
echo "error: required command not found: $1" >&2
|
|
11
|
+
exit 1
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
cleanup() {
|
|
16
|
+
if [[ -n "$_TMP_BUNDLE_DIR" && -d "$_TMP_BUNDLE_DIR" ]]; then
|
|
17
|
+
rm -rf "$_TMP_BUNDLE_DIR"
|
|
18
|
+
fi
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
_SCRIPT="${BASH_SOURCE[0]:-}"
|
|
22
|
+
if [[ -n "$_SCRIPT" && -f "$_SCRIPT" ]]; then
|
|
23
|
+
ROOT="$(cd "$(dirname "$_SCRIPT")" && pwd)"
|
|
24
|
+
else
|
|
25
|
+
need curl
|
|
26
|
+
need tar
|
|
27
|
+
_TMP_BUNDLE_DIR="$(mktemp -d "${TMPDIR:-/tmp}/omnish-cursor.XXXXXX")"
|
|
28
|
+
trap cleanup EXIT
|
|
29
|
+
echo "Downloading Omnish Cursor bundle..." >&2
|
|
30
|
+
curl -fsSL "$OMNISH_CURSOR_BUNDLE_URL" | tar -xzf - -C "$_TMP_BUNDLE_DIR"
|
|
31
|
+
if [[ ! -d "$_TMP_BUNDLE_DIR/omnish-cursor" ]]; then
|
|
32
|
+
echo "error: bundle missing omnish-cursor/ top-level directory" >&2
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
ROOT="$_TMP_BUNDLE_DIR/omnish-cursor"
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
CURSOR_DIR="${CURSOR_DIR:-$HOME/.cursor}"
|
|
39
|
+
HOOKS_DIR="$CURSOR_DIR/hooks"
|
|
40
|
+
RULES_DIR="$CURSOR_DIR/rules"
|
|
41
|
+
SKILL_DIR="$CURSOR_DIR/skills/omnish"
|
|
42
|
+
|
|
43
|
+
INSTALL_HOOKS=1
|
|
44
|
+
INSTALL_SKILL=1
|
|
45
|
+
|
|
46
|
+
usage() {
|
|
47
|
+
cat <<EOF
|
|
48
|
+
Usage: $(basename "$0") [options]
|
|
49
|
+
|
|
50
|
+
Install Omnish Cursor integration (hooks, skill, rule, config).
|
|
51
|
+
|
|
52
|
+
Options:
|
|
53
|
+
--hooks-only Install only Cursor hooks + rule + notify config
|
|
54
|
+
--skill-only Install only the omnish Cursor skill (+ doctor script)
|
|
55
|
+
-h, --help Show this help
|
|
56
|
+
EOF
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
while [[ $# -gt 0 ]]; do
|
|
60
|
+
case "$1" in
|
|
61
|
+
--hooks-only)
|
|
62
|
+
INSTALL_HOOKS=1
|
|
63
|
+
INSTALL_SKILL=0
|
|
64
|
+
shift
|
|
65
|
+
;;
|
|
66
|
+
--skill-only)
|
|
67
|
+
INSTALL_HOOKS=0
|
|
68
|
+
INSTALL_SKILL=1
|
|
69
|
+
shift
|
|
70
|
+
;;
|
|
71
|
+
-h | --help)
|
|
72
|
+
usage
|
|
73
|
+
exit 0
|
|
74
|
+
;;
|
|
75
|
+
*)
|
|
76
|
+
echo "error: unknown option: $1" >&2
|
|
77
|
+
usage >&2
|
|
78
|
+
exit 1
|
|
79
|
+
;;
|
|
80
|
+
esac
|
|
81
|
+
done
|
|
82
|
+
|
|
83
|
+
if [[ "$INSTALL_HOOKS" -eq 1 ]]; then
|
|
84
|
+
need python3
|
|
85
|
+
need omnish
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
if [[ "$INSTALL_HOOKS" -eq 1 ]]; then
|
|
89
|
+
mkdir -p "$HOOKS_DIR" "$RULES_DIR"
|
|
90
|
+
|
|
91
|
+
install -m 755 "$ROOT/hooks/omnish-notify.sh" "$HOOKS_DIR/omnish-notify.sh"
|
|
92
|
+
install -m 755 "$ROOT/hooks/omnish-session-start.sh" "$HOOKS_DIR/omnish-session-start.sh"
|
|
93
|
+
install -m 644 "$ROOT/hooks/omnish-notify.py" "$HOOKS_DIR/omnish-notify.py"
|
|
94
|
+
install -m 644 "$ROOT/rules/omnish-notify.mdc" "$RULES_DIR/omnish-notify.mdc"
|
|
95
|
+
|
|
96
|
+
if [[ ! -f "$CURSOR_DIR/omnish-notify.json" ]]; then
|
|
97
|
+
install -m 644 "$ROOT/omnish-notify.json.example" "$CURSOR_DIR/omnish-notify.json"
|
|
98
|
+
echo "created $CURSOR_DIR/omnish-notify.json"
|
|
99
|
+
else
|
|
100
|
+
echo "kept existing $CURSOR_DIR/omnish-notify.json"
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
python3 - "$CURSOR_DIR/hooks.json" <<'PY'
|
|
104
|
+
import json
|
|
105
|
+
import sys
|
|
106
|
+
from pathlib import Path
|
|
107
|
+
|
|
108
|
+
hooks_path = Path(sys.argv[1])
|
|
109
|
+
desired = {
|
|
110
|
+
"sessionStart": [{"command": "./hooks/omnish-session-start.sh"}],
|
|
111
|
+
"stop": [{"command": "./hooks/omnish-notify.sh"}],
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if hooks_path.exists():
|
|
115
|
+
try:
|
|
116
|
+
data = json.loads(hooks_path.read_text(encoding="utf-8"))
|
|
117
|
+
except json.JSONDecodeError:
|
|
118
|
+
data = {"version": 1, "hooks": {}}
|
|
119
|
+
else:
|
|
120
|
+
data = {"version": 1, "hooks": {}}
|
|
121
|
+
|
|
122
|
+
data.setdefault("version", 1)
|
|
123
|
+
hooks = data.setdefault("hooks", {})
|
|
124
|
+
|
|
125
|
+
for event, entries in desired.items():
|
|
126
|
+
existing = hooks.get(event) or []
|
|
127
|
+
commands = {entry.get("command") for entry in existing if isinstance(entry, dict)}
|
|
128
|
+
merged = list(existing)
|
|
129
|
+
for entry in entries:
|
|
130
|
+
if entry["command"] not in commands:
|
|
131
|
+
merged.append(entry)
|
|
132
|
+
hooks[event] = merged
|
|
133
|
+
|
|
134
|
+
hooks_path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
|
|
135
|
+
print(f"updated {hooks_path}")
|
|
136
|
+
PY
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
if [[ "$INSTALL_SKILL" -eq 1 ]]; then
|
|
140
|
+
mkdir -p "$SKILL_DIR/scripts"
|
|
141
|
+
install -m 644 "$ROOT/skill/SKILL.md" "$SKILL_DIR/SKILL.md"
|
|
142
|
+
install -m 644 "$ROOT/skill/setup-paths.md" "$SKILL_DIR/setup-paths.md"
|
|
143
|
+
install -m 644 "$ROOT/skill/files-and-sharing.md" "$SKILL_DIR/files-and-sharing.md"
|
|
144
|
+
install -m 644 "$ROOT/skill/notifications.md" "$SKILL_DIR/notifications.md"
|
|
145
|
+
install -m 755 "$ROOT/scripts/doctor.sh" "$SKILL_DIR/scripts/doctor.sh"
|
|
146
|
+
echo "installed skill to $SKILL_DIR"
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
cat <<EOF
|
|
150
|
+
|
|
151
|
+
Installed Omnish Cursor bundle.
|
|
152
|
+
|
|
153
|
+
Next steps:
|
|
154
|
+
1. Ensure omnish gateway is running: omnish run
|
|
155
|
+
2. Platform attached: omnish config show platform
|
|
156
|
+
Local mode: omnish link && omnish allow +E164 (or tg:id)
|
|
157
|
+
3. Restart Cursor (or reload Hooks in Settings)
|
|
158
|
+
4. Run: bash $SKILL_DIR/scripts/doctor.sh
|
|
159
|
+
|
|
160
|
+
Config: $CURSOR_DIR/omnish-notify.json
|
|
161
|
+
Default peer: * (all allowlisted contacts)
|
|
162
|
+
EOF
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Remove Omnish Cursor bundle entries from ~/.cursor (keeps unrelated hooks).
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
CURSOR_DIR="${CURSOR_DIR:-$HOME/.cursor}"
|
|
6
|
+
HOOKS_JSON="$CURSOR_DIR/hooks.json"
|
|
7
|
+
|
|
8
|
+
REMOVE_HOOKS=1
|
|
9
|
+
REMOVE_SKILL=1
|
|
10
|
+
REMOVE_CONFIG=0
|
|
11
|
+
|
|
12
|
+
usage() {
|
|
13
|
+
cat <<EOF
|
|
14
|
+
Usage: $(basename "$0") [options]
|
|
15
|
+
|
|
16
|
+
Remove Omnish Cursor integration installed by install.sh.
|
|
17
|
+
|
|
18
|
+
Options:
|
|
19
|
+
--hooks-only Remove hooks + rule + hooks.json entries only
|
|
20
|
+
--skill-only Remove ~/.cursor/skills/omnish only
|
|
21
|
+
--with-config Also delete ~/.cursor/omnish-notify.json
|
|
22
|
+
-h, --help Show this help
|
|
23
|
+
EOF
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
while [[ $# -gt 0 ]]; do
|
|
27
|
+
case "$1" in
|
|
28
|
+
--hooks-only)
|
|
29
|
+
REMOVE_HOOKS=1
|
|
30
|
+
REMOVE_SKILL=0
|
|
31
|
+
shift
|
|
32
|
+
;;
|
|
33
|
+
--skill-only)
|
|
34
|
+
REMOVE_HOOKS=0
|
|
35
|
+
REMOVE_SKILL=1
|
|
36
|
+
shift
|
|
37
|
+
;;
|
|
38
|
+
--with-config)
|
|
39
|
+
REMOVE_CONFIG=1
|
|
40
|
+
shift
|
|
41
|
+
;;
|
|
42
|
+
-h | --help)
|
|
43
|
+
usage
|
|
44
|
+
exit 0
|
|
45
|
+
;;
|
|
46
|
+
*)
|
|
47
|
+
echo "error: unknown option: $1" >&2
|
|
48
|
+
usage >&2
|
|
49
|
+
exit 1
|
|
50
|
+
;;
|
|
51
|
+
esac
|
|
52
|
+
done
|
|
53
|
+
|
|
54
|
+
OMNISH_COMMANDS=(
|
|
55
|
+
"./hooks/omnish-session-start.sh"
|
|
56
|
+
"./hooks/omnish-notify.sh"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if [[ "$REMOVE_HOOKS" -eq 1 && -f "$HOOKS_JSON" ]]; then
|
|
60
|
+
python3 - "$HOOKS_JSON" "${OMNISH_COMMANDS[@]}" <<'PY'
|
|
61
|
+
import json
|
|
62
|
+
import sys
|
|
63
|
+
from pathlib import Path
|
|
64
|
+
|
|
65
|
+
hooks_path = Path(sys.argv[1])
|
|
66
|
+
remove_cmds = set(sys.argv[2:])
|
|
67
|
+
data = json.loads(hooks_path.read_text(encoding="utf-8"))
|
|
68
|
+
hooks = data.get("hooks") or {}
|
|
69
|
+
changed = False
|
|
70
|
+
for event, entries in list(hooks.items()):
|
|
71
|
+
if not isinstance(entries, list):
|
|
72
|
+
continue
|
|
73
|
+
filtered = [
|
|
74
|
+
e for e in entries
|
|
75
|
+
if not (isinstance(e, dict) and e.get("command") in remove_cmds)
|
|
76
|
+
]
|
|
77
|
+
if len(filtered) != len(entries):
|
|
78
|
+
changed = True
|
|
79
|
+
hooks[event] = filtered
|
|
80
|
+
if changed:
|
|
81
|
+
hooks_path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
|
|
82
|
+
print(f"updated {hooks_path} (removed omnish hook entries)")
|
|
83
|
+
else
|
|
84
|
+
print(f"no omnish hook entries in {hooks_path}")
|
|
85
|
+
PY
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
if [[ "$REMOVE_HOOKS" -eq 1 ]]; then
|
|
89
|
+
rm -f \
|
|
90
|
+
"$CURSOR_DIR/hooks/omnish-notify.sh" \
|
|
91
|
+
"$CURSOR_DIR/hooks/omnish-notify.py" \
|
|
92
|
+
"$CURSOR_DIR/hooks/omnish-session-start.sh" \
|
|
93
|
+
"$CURSOR_DIR/rules/omnish-notify.mdc"
|
|
94
|
+
echo "removed hook scripts and omnish-notify rule"
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
if [[ "$REMOVE_SKILL" -eq 1 ]]; then
|
|
98
|
+
rm -rf "$CURSOR_DIR/skills/omnish"
|
|
99
|
+
echo "removed $CURSOR_DIR/skills/omnish"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
if [[ "$REMOVE_CONFIG" -eq 1 ]]; then
|
|
103
|
+
rm -f "$CURSOR_DIR/omnish-notify.json"
|
|
104
|
+
echo "removed $CURSOR_DIR/omnish-notify.json"
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
echo "Done. Restart Cursor if hooks were removed."
|
|
Binary file
|