claws-code 0.8.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/.claude/commands/claws-auto.md +90 -0
- package/.claude/commands/claws-bin.md +28 -0
- package/.claude/commands/claws-cleanup.md +28 -0
- package/.claude/commands/claws-do.md +82 -0
- package/.claude/commands/claws-fix.md +40 -0
- package/.claude/commands/claws-goal.md +111 -0
- package/.claude/commands/claws-help.md +54 -0
- package/.claude/commands/claws-plan.md +103 -0
- package/.claude/commands/claws-report.md +29 -0
- package/.claude/commands/claws-status.md +37 -0
- package/.claude/commands/claws-update.md +32 -0
- package/.claude/commands/claws.md +64 -0
- package/.claude/rules/claws-default-behavior.md +76 -0
- package/.claude/settings.json +112 -0
- package/.claude/settings.local.json +19 -0
- package/.claude/skills/claws-auto-engine/SKILL.md +97 -0
- package/.claude/skills/claws-goal-tracker/SKILL.md +106 -0
- package/.claude/skills/claws-prompt-templates/SKILL.md +203 -0
- package/.claude/skills/claws-wave-lead/SKILL.md +126 -0
- package/.claude/skills/claws-wave-subworker/SKILL.md +60 -0
- package/CHANGELOG.md +1949 -0
- package/LICENSE +21 -0
- package/README.md +420 -0
- package/bin/cli.js +84 -0
- package/cli.js +223 -0
- package/docs/ARCHITECTURE.md +511 -0
- package/docs/event-protocol.md +588 -0
- package/docs/features.md +562 -0
- package/docs/guide.md +891 -0
- package/docs/index.html +716 -0
- package/docs/protocol.md +323 -0
- package/extension/.vscodeignore +15 -0
- package/extension/CHANGELOG.md +1906 -0
- package/extension/LICENSE +21 -0
- package/extension/README.md +137 -0
- package/extension/docs/features.md +424 -0
- package/extension/docs/protocol.md +197 -0
- package/extension/esbuild.mjs +25 -0
- package/extension/icon.png +0 -0
- package/extension/native/.metadata.json +10 -0
- package/extension/native/node-pty/LICENSE +69 -0
- package/extension/native/node-pty/README.md +165 -0
- package/extension/native/node-pty/lib/conpty_console_list_agent.js +16 -0
- package/extension/native/node-pty/lib/conpty_console_list_agent.js.map +1 -0
- package/extension/native/node-pty/lib/eventEmitter2.js +47 -0
- package/extension/native/node-pty/lib/eventEmitter2.js.map +1 -0
- package/extension/native/node-pty/lib/index.js +52 -0
- package/extension/native/node-pty/lib/index.js.map +1 -0
- package/extension/native/node-pty/lib/interfaces.js +7 -0
- package/extension/native/node-pty/lib/interfaces.js.map +1 -0
- package/extension/native/node-pty/lib/shared/conout.js +11 -0
- package/extension/native/node-pty/lib/shared/conout.js.map +1 -0
- package/extension/native/node-pty/lib/terminal.js +190 -0
- package/extension/native/node-pty/lib/terminal.js.map +1 -0
- package/extension/native/node-pty/lib/types.js +7 -0
- package/extension/native/node-pty/lib/types.js.map +1 -0
- package/extension/native/node-pty/lib/unixTerminal.js +346 -0
- package/extension/native/node-pty/lib/unixTerminal.js.map +1 -0
- package/extension/native/node-pty/lib/utils.js +39 -0
- package/extension/native/node-pty/lib/utils.js.map +1 -0
- package/extension/native/node-pty/lib/windowsConoutConnection.js +125 -0
- package/extension/native/node-pty/lib/windowsConoutConnection.js.map +1 -0
- package/extension/native/node-pty/lib/windowsPtyAgent.js +320 -0
- package/extension/native/node-pty/lib/windowsPtyAgent.js.map +1 -0
- package/extension/native/node-pty/lib/windowsTerminal.js +199 -0
- package/extension/native/node-pty/lib/windowsTerminal.js.map +1 -0
- package/extension/native/node-pty/lib/worker/conoutSocketWorker.js +22 -0
- package/extension/native/node-pty/lib/worker/conoutSocketWorker.js.map +1 -0
- package/extension/native/node-pty/package.json +64 -0
- package/extension/native/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
- package/extension/native/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
- package/extension/native/node-pty/prebuilds/darwin-x64/pty.node +0 -0
- package/extension/native/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
- package/extension/native/node-pty/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
- package/extension/native/node-pty/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
- package/extension/native/node-pty/prebuilds/win32-arm64/conpty.node +0 -0
- package/extension/native/node-pty/prebuilds/win32-arm64/conpty_console_list.node +0 -0
- package/extension/native/node-pty/prebuilds/win32-arm64/pty.node +0 -0
- package/extension/native/node-pty/prebuilds/win32-arm64/winpty-agent.exe +0 -0
- package/extension/native/node-pty/prebuilds/win32-arm64/winpty.dll +0 -0
- package/extension/native/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
- package/extension/native/node-pty/prebuilds/win32-x64/conpty/conpty.dll +0 -0
- package/extension/native/node-pty/prebuilds/win32-x64/conpty.node +0 -0
- package/extension/native/node-pty/prebuilds/win32-x64/conpty_console_list.node +0 -0
- package/extension/native/node-pty/prebuilds/win32-x64/pty.node +0 -0
- package/extension/native/node-pty/prebuilds/win32-x64/winpty-agent.exe +0 -0
- package/extension/native/node-pty/prebuilds/win32-x64/winpty.dll +0 -0
- package/extension/package-lock.json +605 -0
- package/extension/package.json +343 -0
- package/extension/scripts/bundle-native.mjs +104 -0
- package/extension/scripts/deploy-dev.mjs +60 -0
- package/extension/src/ansi-strip.ts +52 -0
- package/extension/src/backends/vscode/claws-pty.ts +483 -0
- package/extension/src/backends/vscode/status-bar.ts +99 -0
- package/extension/src/backends/vscode/vscode-backend.ts +282 -0
- package/extension/src/capture-store.ts +125 -0
- package/extension/src/event-log.ts +629 -0
- package/extension/src/event-schemas.ts +478 -0
- package/extension/src/extension.js +492 -0
- package/extension/src/extension.ts +873 -0
- package/extension/src/lifecycle-engine.ts +60 -0
- package/extension/src/lifecycle-rules.ts +171 -0
- package/extension/src/lifecycle-store.ts +506 -0
- package/extension/src/peer-registry.ts +176 -0
- package/extension/src/pipeline-registry.ts +82 -0
- package/extension/src/platform.ts +64 -0
- package/extension/src/protocol.ts +532 -0
- package/extension/src/server-config.ts +98 -0
- package/extension/src/server.ts +2210 -0
- package/extension/src/task-registry.ts +51 -0
- package/extension/src/terminal-backend.ts +211 -0
- package/extension/src/terminal-manager.ts +395 -0
- package/extension/src/topic-registry.ts +70 -0
- package/extension/src/topic-utils.ts +46 -0
- package/extension/src/transport.ts +45 -0
- package/extension/src/uninstall-cleanup.ts +232 -0
- package/extension/src/wave-registry.ts +314 -0
- package/extension/src/websocket-transport.ts +153 -0
- package/extension/tsconfig.json +23 -0
- package/lib/capabilities.js +145 -0
- package/lib/dry-run.js +43 -0
- package/lib/install.js +1018 -0
- package/lib/mcp-setup.js +92 -0
- package/lib/platform.js +240 -0
- package/lib/preflight.js +152 -0
- package/lib/shell-hook.js +343 -0
- package/lib/uninstall.js +162 -0
- package/lib/verify.js +166 -0
- package/mcp_server.js +3529 -0
- package/package.json +48 -0
- package/rules/claws-default-behavior.md +72 -0
- package/scripts/_helpers/atomic-file.mjs +137 -0
- package/scripts/_helpers/fix-repair.js +64 -0
- package/scripts/_helpers/json-safe.mjs +218 -0
- package/scripts/bump-version.sh +84 -0
- package/scripts/codegen/gen-docs.mjs +61 -0
- package/scripts/codegen/gen-json-schema.mjs +62 -0
- package/scripts/codegen/gen-mcp-tools.mjs +358 -0
- package/scripts/codegen/gen-types.mjs +172 -0
- package/scripts/codegen/index.mjs +42 -0
- package/scripts/dev-hooks/check-extension-dirs.js +77 -0
- package/scripts/dev-hooks/check-open-claws-terminals.js +70 -0
- package/scripts/dev-hooks/check-stale-main.js +55 -0
- package/scripts/dev-hooks/check-tag-pushed.js +51 -0
- package/scripts/dev-hooks/check-tag-vs-main.js +56 -0
- package/scripts/dev-vsix-install.sh +60 -0
- package/scripts/fix.sh +702 -0
- package/scripts/gen-client-types.mjs +81 -0
- package/scripts/git-hooks/pre-commit +31 -0
- package/scripts/hooks/lifecycle-state.js +61 -0
- package/scripts/hooks/package.json +4 -0
- package/scripts/hooks/post-tool-use-claws.js +292 -0
- package/scripts/hooks/pre-bash-no-verify-block.js +72 -0
- package/scripts/hooks/pre-tool-use-claws.js +206 -0
- package/scripts/hooks/session-start-claws.js +97 -0
- package/scripts/hooks/stop-claws.js +88 -0
- package/scripts/inject-claude-md.js +205 -0
- package/scripts/inject-dev-hooks.js +96 -0
- package/scripts/inject-global-claude-md.js +140 -0
- package/scripts/inject-settings-hooks.js +370 -0
- package/scripts/install.ps1 +146 -0
- package/scripts/install.sh +1729 -0
- package/scripts/monitor-arm-watch.js +155 -0
- package/scripts/rebuild-node-pty.sh +245 -0
- package/scripts/report.sh +232 -0
- package/scripts/shell-hook.fish +164 -0
- package/scripts/shell-hook.ps1 +33 -0
- package/scripts/shell-hook.sh +232 -0
- package/scripts/stream-events.js +399 -0
- package/scripts/terminal-wrapper.sh +36 -0
- package/scripts/test-enforcement.sh +132 -0
- package/scripts/test-install.sh +174 -0
- package/scripts/test-installer-parity.sh +135 -0
- package/scripts/test-template-enforcement.sh +76 -0
- package/scripts/uninstall.sh +143 -0
- package/scripts/update.sh +337 -0
- package/scripts/verify-release.sh +323 -0
- package/scripts/verify-wrapped.sh +194 -0
- package/templates/CLAUDE.global.md +135 -0
- package/templates/CLAUDE.project.md +37 -0
package/scripts/fix.sh
ADDED
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Claws — auto-diagnosis and repair
|
|
3
|
+
# Checks every piece of the install chain and repairs what it can. Run this
|
|
4
|
+
# when claws_* tools aren't showing up or something feels broken.
|
|
5
|
+
#
|
|
6
|
+
# Usage: bash ~/.claws-src/scripts/fix.sh [project-root]
|
|
7
|
+
#
|
|
8
|
+
# The slash command /claws-fix is a thin dispatcher that calls this script.
|
|
9
|
+
# Add new checks/repairs here — they'll be picked up on the next git pull
|
|
10
|
+
# without any change to the slash-command markdown.
|
|
11
|
+
|
|
12
|
+
set -eo pipefail
|
|
13
|
+
|
|
14
|
+
INSTALL_DIR="${CLAWS_DIR:-$HOME/.claws-src}"
|
|
15
|
+
PROJECT_ROOT="${1:-$(pwd)}"
|
|
16
|
+
|
|
17
|
+
# ─── Colors ────────────────────────────────────────────────────────────────
|
|
18
|
+
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
|
|
19
|
+
C_RESET='\033[0m'; C_BOLD='\033[1m'
|
|
20
|
+
C_GREEN='\033[0;32m'; C_YELLOW='\033[0;33m'; C_RED='\033[0;31m'; C_DIM='\033[2m'
|
|
21
|
+
else
|
|
22
|
+
C_RESET=''; C_BOLD=''; C_GREEN=''; C_YELLOW=''; C_RED=''; C_DIM=''
|
|
23
|
+
fi
|
|
24
|
+
check() { printf "${C_BOLD}[check]${C_RESET} %s\n" "$*"; }
|
|
25
|
+
ok() { printf " ${C_GREEN}✓${C_RESET} %s\n" "$*"; }
|
|
26
|
+
fix() { printf " ${C_YELLOW}→${C_RESET} %s\n" "$*"; }
|
|
27
|
+
fail() { printf " ${C_RED}✗${C_RESET} %s\n" "$*"; }
|
|
28
|
+
|
|
29
|
+
FIXED=0
|
|
30
|
+
ISSUES=0
|
|
31
|
+
|
|
32
|
+
# ─── 0. System dependencies ────────────────────────────────────────────────
|
|
33
|
+
# Fast precheck so users see a clear answer when something upstream broke
|
|
34
|
+
# (e.g. they nvm'd to an old Node, uninstalled Xcode CLT, etc).
|
|
35
|
+
PLATFORM="$(uname -s)"
|
|
36
|
+
check "System dependencies"
|
|
37
|
+
if command -v git &>/dev/null; then ok "git ($(git --version | awk '{print $3}'))"
|
|
38
|
+
else fail "git not found — install: xcode-select --install (macOS) or sudo apt install git"; ISSUES=$((ISSUES+1)); fi
|
|
39
|
+
|
|
40
|
+
if command -v node &>/dev/null; then
|
|
41
|
+
NODE_MAJOR=$(node -e "console.log(process.versions.node.split('.')[0])" 2>/dev/null || echo "0")
|
|
42
|
+
if [ "$NODE_MAJOR" -lt 18 ] 2>/dev/null; then
|
|
43
|
+
fail "node $(node --version) too old — Claws requires Node 18+"; ISSUES=$((ISSUES+1))
|
|
44
|
+
else
|
|
45
|
+
ok "node ($(node --version))"
|
|
46
|
+
fi
|
|
47
|
+
else
|
|
48
|
+
fail "node not found — required for MCP server"; ISSUES=$((ISSUES+1))
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
if command -v npm &>/dev/null; then ok "npm ($(npm --version))"
|
|
52
|
+
else fail "npm not found — required for extension build"; ISSUES=$((ISSUES+1)); fi
|
|
53
|
+
|
|
54
|
+
if command -v python3 &>/dev/null; then ok "python3 ($(python3 --version 2>&1 | awk '{print $2}'))"
|
|
55
|
+
else
|
|
56
|
+
# python3 is only actually needed when node-pty needs to compile from
|
|
57
|
+
# source, which only happens if no prebuild matches the current Node
|
|
58
|
+
# version. Inform but don't count as a failure.
|
|
59
|
+
printf " ${C_YELLOW}!${C_RESET} python3 not found — only needed if node-pty needs source compile\n"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
case "$PLATFORM" in
|
|
63
|
+
Darwin)
|
|
64
|
+
if xcode-select -p &>/dev/null; then ok "Xcode Command Line Tools"
|
|
65
|
+
else
|
|
66
|
+
printf " ${C_YELLOW}!${C_RESET} Xcode CLT missing — needed only if node-pty must compile (run: xcode-select --install)\n"
|
|
67
|
+
fi
|
|
68
|
+
;;
|
|
69
|
+
Linux)
|
|
70
|
+
if command -v g++ &>/dev/null; then ok "g++ ($(g++ -dumpversion 2>/dev/null))"
|
|
71
|
+
else printf " ${C_YELLOW}!${C_RESET} g++ missing — needed only for node-pty source compile (run: sudo apt install build-essential)\n"
|
|
72
|
+
fi
|
|
73
|
+
;;
|
|
74
|
+
esac
|
|
75
|
+
|
|
76
|
+
# ─── 1. Source clone ───────────────────────────────────────────────────────
|
|
77
|
+
check "Claws source clone at $INSTALL_DIR"
|
|
78
|
+
if [ -d "$INSTALL_DIR/.git" ]; then
|
|
79
|
+
ok "clone exists ($(cd "$INSTALL_DIR" && git log --oneline -1 2>/dev/null))"
|
|
80
|
+
else
|
|
81
|
+
fail "no clone at $INSTALL_DIR"
|
|
82
|
+
fix "run the installer: bash <(curl -fsSL https://raw.githubusercontent.com/neunaha/claws/main/scripts/install.sh)"
|
|
83
|
+
ISSUES=$((ISSUES+1))
|
|
84
|
+
exit 1
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# ─── 1b. node-pty native binary (ABI-correct for VS Code's Electron) ──────
|
|
88
|
+
# node-pty MUST be compiled against VS Code's Electron-embedded Node, not
|
|
89
|
+
# system Node. A binary built with plain `node-gyp rebuild` against system
|
|
90
|
+
# Node 24 silently fails to load in Electron 39 (Node 22) and the extension
|
|
91
|
+
# falls back to pipe-mode. Use @electron/rebuild to target the correct ABI.
|
|
92
|
+
check "node-pty native binary (for glitch-free wrapped terminals)"
|
|
93
|
+
NPTY_BIN="$INSTALL_DIR/extension/node_modules/node-pty/build/Release/pty.node"
|
|
94
|
+
ELECTRON_ABI_FILE="$INSTALL_DIR/extension/dist/.electron-abi"
|
|
95
|
+
ELECTRON_VERSION=""
|
|
96
|
+
if [ "$(uname)" = "Darwin" ]; then
|
|
97
|
+
# M-32: prefer $TERM_PROGRAM-matching editor; M-33-compat: CURSOR_CHANNEL overrides vscode.
|
|
98
|
+
_fix_tp="${TERM_PROGRAM:-}"
|
|
99
|
+
[ "$_fix_tp" = "vscode" ] && [ -n "${CURSOR_CHANNEL:-}" ] && _fix_tp="cursor"
|
|
100
|
+
_fix_tp=$(echo "$_fix_tp" | tr '[:upper:]' '[:lower:]')
|
|
101
|
+
case "$_fix_tp" in
|
|
102
|
+
cursor) _fix_darwin_apps=('/Applications/Cursor.app' '/Applications/Visual Studio Code.app' '/Applications/Visual Studio Code - Insiders.app' '/Applications/Windsurf.app') ;;
|
|
103
|
+
windsurf) _fix_darwin_apps=('/Applications/Windsurf.app' '/Applications/Visual Studio Code.app' '/Applications/Visual Studio Code - Insiders.app' '/Applications/Cursor.app') ;;
|
|
104
|
+
*) _fix_darwin_apps=('/Applications/Visual Studio Code.app' '/Applications/Visual Studio Code - Insiders.app' '/Applications/Cursor.app' '/Applications/Windsurf.app') ;;
|
|
105
|
+
esac
|
|
106
|
+
for _fix_app in "${_fix_darwin_apps[@]}"; do
|
|
107
|
+
plist="$_fix_app/Contents/Frameworks/Electron Framework.framework/Resources/Info.plist"
|
|
108
|
+
if [ -f "$plist" ]; then
|
|
109
|
+
v=$(plutil -extract CFBundleVersion raw "$plist" 2>/dev/null || true)
|
|
110
|
+
[ -n "$v" ] && ELECTRON_VERSION="$v" && break
|
|
111
|
+
fi
|
|
112
|
+
done
|
|
113
|
+
elif [ "$(uname)" = "Linux" ]; then
|
|
114
|
+
# M-33: Linux Cursor/Windsurf paths + TERM_PROGRAM ordering.
|
|
115
|
+
_fix_tp="${TERM_PROGRAM:-}"
|
|
116
|
+
[ "$_fix_tp" = "vscode" ] && [ -n "${CURSOR_CHANNEL:-}" ] && _fix_tp="cursor"
|
|
117
|
+
_fix_tp=$(echo "$_fix_tp" | tr '[:upper:]' '[:lower:]')
|
|
118
|
+
_fix_linux_vscode=('/usr/share/code/electron' '/usr/lib/code/electron' '/opt/visual-studio-code/electron' '/snap/code/current/electron')
|
|
119
|
+
_fix_linux_cursor=('/usr/share/cursor/electron' '/opt/cursor/electron' '/snap/cursor/current/usr/share/cursor/electron')
|
|
120
|
+
_fix_linux_windsurf=('/usr/share/windsurf/electron' '/opt/windsurf/electron')
|
|
121
|
+
_fix_linux_candidates=()
|
|
122
|
+
case "$_fix_tp" in
|
|
123
|
+
cursor) _fix_linux_candidates=("${_fix_linux_cursor[@]}" "${_fix_linux_vscode[@]}" "${_fix_linux_windsurf[@]}") ;;
|
|
124
|
+
windsurf) _fix_linux_candidates=("${_fix_linux_windsurf[@]}" "${_fix_linux_vscode[@]}" "${_fix_linux_cursor[@]}") ;;
|
|
125
|
+
*) _fix_linux_candidates=("${_fix_linux_vscode[@]}" "${_fix_linux_cursor[@]}" "${_fix_linux_windsurf[@]}") ;;
|
|
126
|
+
esac
|
|
127
|
+
for _fix_ep in "${_fix_linux_candidates[@]}"; do
|
|
128
|
+
if [ -x "$_fix_ep" ]; then
|
|
129
|
+
v=$("$_fix_ep" --version 2>/dev/null | sed 's/^v//' || true)
|
|
130
|
+
if echo "$v" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then ELECTRON_VERSION="$v" && break; fi
|
|
131
|
+
fi
|
|
132
|
+
done
|
|
133
|
+
fi
|
|
134
|
+
[ -z "$ELECTRON_VERSION" ] && ELECTRON_VERSION="39.8.5"
|
|
135
|
+
LAST_ABI=$(cat "$ELECTRON_ABI_FILE" 2>/dev/null || echo "")
|
|
136
|
+
|
|
137
|
+
if [ -f "$NPTY_BIN" ] && [ "$LAST_ABI" = "$ELECTRON_VERSION" ]; then
|
|
138
|
+
ok "node-pty binary OK for Electron $ELECTRON_VERSION ($(wc -c < "$NPTY_BIN" | tr -d ' ') bytes)"
|
|
139
|
+
elif [ -d "$INSTALL_DIR/extension/node_modules/node-pty" ]; then
|
|
140
|
+
if [ -f "$NPTY_BIN" ] && [ "$LAST_ABI" != "$ELECTRON_VERSION" ]; then
|
|
141
|
+
fix "node-pty built for Electron '$LAST_ABI', need '$ELECTRON_VERSION' — rebuilding"
|
|
142
|
+
else
|
|
143
|
+
fix "node-pty binary missing — rebuilding for Electron $ELECTRON_VERSION"
|
|
144
|
+
fi
|
|
145
|
+
if [ "$(uname)" = "Darwin" ] && ! xcode-select -p &>/dev/null; then
|
|
146
|
+
fail "Xcode Command Line Tools required — run: xcode-select --install"
|
|
147
|
+
ISSUES=$((ISSUES+1))
|
|
148
|
+
else
|
|
149
|
+
# M-31: 5-minute timeout ceiling — prevents indefinite hang on slow Electron header fetch.
|
|
150
|
+
_fix_timeout_cmd=""
|
|
151
|
+
if command -v timeout >/dev/null 2>&1; then _fix_timeout_cmd="timeout 300"
|
|
152
|
+
elif command -v gtimeout >/dev/null 2>&1; then _fix_timeout_cmd="gtimeout 300"
|
|
153
|
+
fi
|
|
154
|
+
if ( cd "$INSTALL_DIR/extension" && $_fix_timeout_cmd npx --yes @electron/rebuild --version="$ELECTRON_VERSION" --only=node-pty --force >/dev/null 2>&1 ); then
|
|
155
|
+
_fix_rebuild_rc=0
|
|
156
|
+
else
|
|
157
|
+
_fix_rebuild_rc=$?
|
|
158
|
+
fi
|
|
159
|
+
if [ "$_fix_rebuild_rc" = "124" ]; then
|
|
160
|
+
fail "@electron/rebuild timed out after 5 min — likely a slow Electron headers download. Check network / proxy settings."
|
|
161
|
+
ISSUES=$((ISSUES+1))
|
|
162
|
+
elif [ "$_fix_rebuild_rc" = "0" ] && [ -f "$NPTY_BIN" ]; then
|
|
163
|
+
echo "$ELECTRON_VERSION" > "$ELECTRON_ABI_FILE" 2>/dev/null || true
|
|
164
|
+
ok "node-pty rebuilt for Electron $ELECTRON_VERSION in source clone"
|
|
165
|
+
|
|
166
|
+
# Propagate the rebuilt binary into the source's bundled native/ slot
|
|
167
|
+
# AND into every installed extension directory. Without this, VS Code
|
|
168
|
+
# keeps loading the OLD pty.node from ~/.vscode/extensions/neunaha.claws-X/
|
|
169
|
+
# and the rebuild has no visible effect after reload. (Audit gap #3.)
|
|
170
|
+
SOURCE_NATIVE_DEST="$INSTALL_DIR/extension/native/node-pty/build/Release/pty.node"
|
|
171
|
+
if [ -f "$NPTY_BIN" ] && [ -d "$INSTALL_DIR/extension/native/node-pty/build/Release" ]; then
|
|
172
|
+
cp -f "$NPTY_BIN" "$SOURCE_NATIVE_DEST" 2>/dev/null && ok "propagated to source native/ bundle"
|
|
173
|
+
# Update the metadata.json electronVersion to match
|
|
174
|
+
node --no-deprecation -e "
|
|
175
|
+
const fs=require('fs');
|
|
176
|
+
const p='$INSTALL_DIR/extension/native/.metadata.json';
|
|
177
|
+
try { const m=JSON.parse(fs.readFileSync(p,'utf8')); m.electronVersion='$ELECTRON_VERSION'; m.bundledAt=new Date().toISOString(); fs.writeFileSync(p,JSON.stringify(m,null,2)+'\n'); } catch(e){}
|
|
178
|
+
" 2>/dev/null || true
|
|
179
|
+
fi
|
|
180
|
+
PROPAGATED=0
|
|
181
|
+
for ext_root in "$HOME/.vscode/extensions" "$HOME/.vscode-insiders/extensions" "$HOME/.cursor/extensions" "$HOME/.windsurf/extensions"; do
|
|
182
|
+
[ -d "$ext_root" ] || continue
|
|
183
|
+
for inst in "$ext_root"/neunaha.claws-*; do
|
|
184
|
+
[ -d "$inst" ] || continue
|
|
185
|
+
# Skip if this is a symlink pointing back at the source clone (already updated via source propagation above)
|
|
186
|
+
if [ -L "$inst" ]; then
|
|
187
|
+
ok "skipped $(basename "$inst") — symlink to source"
|
|
188
|
+
PROPAGATED=$((PROPAGATED+1))
|
|
189
|
+
continue
|
|
190
|
+
fi
|
|
191
|
+
target_dir="$inst/native/node-pty/build/Release"
|
|
192
|
+
target="$target_dir/pty.node"
|
|
193
|
+
if [ -d "$target_dir" ]; then
|
|
194
|
+
mkdir -p "$target_dir" 2>/dev/null || true
|
|
195
|
+
if cp -f "$NPTY_BIN" "$target" 2>/dev/null; then
|
|
196
|
+
ok "propagated to $(basename "$inst")"
|
|
197
|
+
PROPAGATED=$((PROPAGATED+1))
|
|
198
|
+
else
|
|
199
|
+
fail "could not write to $target (permissions?)"
|
|
200
|
+
ISSUES=$((ISSUES+1))
|
|
201
|
+
fi
|
|
202
|
+
fi
|
|
203
|
+
done
|
|
204
|
+
done
|
|
205
|
+
if [ "$PROPAGATED" -eq 0 ]; then
|
|
206
|
+
printf " ${C_YELLOW}!${C_RESET} no installed extension dirs found to propagate to — reload VS Code or run install.sh\n"
|
|
207
|
+
fi
|
|
208
|
+
fix "reload VS Code now: Cmd+Shift+P → Developer: Reload Window"
|
|
209
|
+
FIXED=$((FIXED+1))
|
|
210
|
+
else
|
|
211
|
+
fail "@electron/rebuild failed — wrapped terminals will use pipe-mode"
|
|
212
|
+
ISSUES=$((ISSUES+1))
|
|
213
|
+
fi
|
|
214
|
+
fi
|
|
215
|
+
else
|
|
216
|
+
info "node-pty not installed — extension bundle may be missing too (see check 2)"
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
# ─── 2. Extension bundle ───────────────────────────────────────────────────
|
|
220
|
+
check "Extension bundle"
|
|
221
|
+
if [ -f "$INSTALL_DIR/extension/dist/extension.js" ]; then
|
|
222
|
+
ok "built ($(wc -c < "$INSTALL_DIR/extension/dist/extension.js" | tr -d ' ') bytes)"
|
|
223
|
+
elif [ -f "$INSTALL_DIR/extension/src/extension.js" ]; then
|
|
224
|
+
fix "bundle missing — repointing main to legacy src/extension.js"
|
|
225
|
+
node --no-deprecation -e "const fs=require('fs'),p='$INSTALL_DIR/extension/package.json';const j=JSON.parse(fs.readFileSync(p,'utf8'));j.main='./src/extension.js';fs.writeFileSync(p,JSON.stringify(j,null,2));" 2>/dev/null || true
|
|
226
|
+
FIXED=$((FIXED+1))
|
|
227
|
+
# Try to rebuild
|
|
228
|
+
if command -v npm &>/dev/null; then
|
|
229
|
+
fix "attempting to rebuild with npm"
|
|
230
|
+
( cd "$INSTALL_DIR/extension" && npm install --no-audit --no-fund --loglevel=error --silent && npm run build --silent ) 2>&1 | tail -3 || true
|
|
231
|
+
[ -f "$INSTALL_DIR/extension/dist/extension.js" ] && ok "rebuilt" && FIXED=$((FIXED+1))
|
|
232
|
+
fi
|
|
233
|
+
else
|
|
234
|
+
fail "no bundle and no legacy JS fallback"
|
|
235
|
+
ISSUES=$((ISSUES+1))
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
# ─── 3. Extension installed in editors ─────────────────────────────────────
|
|
239
|
+
check "Claws extension installed in VS Code / Cursor / etc"
|
|
240
|
+
EXT_VERSION=$(node --no-deprecation -e "try{console.log(require('$INSTALL_DIR/extension/package.json').version)}catch(e){console.log('0.7.13')}" 2>/dev/null || echo "0.7.13")
|
|
241
|
+
|
|
242
|
+
FOUND_INSTALLS=()
|
|
243
|
+
for dir in "$HOME/.vscode/extensions" "$HOME/.vscode-insiders/extensions" "$HOME/.cursor/extensions" "$HOME/.windsurf/extensions"; do
|
|
244
|
+
[ -d "$dir" ] || continue
|
|
245
|
+
for inst in "$dir"/neunaha.claws-*; do
|
|
246
|
+
[ -d "$inst" ] || [ -L "$inst" ] || continue
|
|
247
|
+
FOUND_INSTALLS+=("$(basename "$dir"): $(basename "$inst")")
|
|
248
|
+
done
|
|
249
|
+
done
|
|
250
|
+
|
|
251
|
+
# Warn about DUPLICATE extensions in the same editor dir (FINDING-C-4)
|
|
252
|
+
declare -A _EDITOR_COUNT
|
|
253
|
+
for entry in "${FOUND_INSTALLS[@]}"; do
|
|
254
|
+
editor="${entry%%:*}"
|
|
255
|
+
_EDITOR_COUNT["$editor"]=$(( ${_EDITOR_COUNT["$editor"]:-0} + 1 ))
|
|
256
|
+
done
|
|
257
|
+
for editor in "${!_EDITOR_COUNT[@]}"; do
|
|
258
|
+
if [ "${_EDITOR_COUNT[$editor]}" -gt 1 ]; then
|
|
259
|
+
fail "DUPLICATE extensions in $editor (${_EDITOR_COUNT[$editor]} copies) — remove old versions to avoid load-order conflict"
|
|
260
|
+
ISSUES=$((ISSUES+1))
|
|
261
|
+
fi
|
|
262
|
+
done
|
|
263
|
+
unset _EDITOR_COUNT
|
|
264
|
+
|
|
265
|
+
if [ "${#FOUND_INSTALLS[@]}" -gt 0 ]; then
|
|
266
|
+
for entry in "${FOUND_INSTALLS[@]}"; do
|
|
267
|
+
ok "installed → $entry"
|
|
268
|
+
done
|
|
269
|
+
else
|
|
270
|
+
fix "no Claws extension found in any editor — packaging VSIX and installing"
|
|
271
|
+
VSIX_PATH="/tmp/claws-$EXT_VERSION.vsix"
|
|
272
|
+
if command -v npx &>/dev/null && ( cd "$INSTALL_DIR/extension" && npx --yes @vscode/vsce package --skip-license --no-git-tag-version --no-update-package-json --out "$VSIX_PATH" >/dev/null 2>&1 ); then
|
|
273
|
+
# Try each editor CLI
|
|
274
|
+
INSTALLED=0
|
|
275
|
+
for pair in \
|
|
276
|
+
"code:/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code" \
|
|
277
|
+
"code-insiders:/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code-insiders" \
|
|
278
|
+
"cursor:/Applications/Cursor.app/Contents/Resources/app/bin/cursor" \
|
|
279
|
+
"windsurf:/Applications/Windsurf.app/Contents/Resources/app/bin/windsurf"; do
|
|
280
|
+
label="${pair%%:*}"
|
|
281
|
+
bundled="${pair#*:}"
|
|
282
|
+
cli=""
|
|
283
|
+
if command -v "$label" &>/dev/null; then cli="$(command -v "$label")"
|
|
284
|
+
elif [ -x "$bundled" ]; then cli="$bundled"
|
|
285
|
+
else continue
|
|
286
|
+
fi
|
|
287
|
+
if "$cli" --install-extension "$VSIX_PATH" --force >/dev/null 2>&1; then
|
|
288
|
+
ok "installed into $label"
|
|
289
|
+
INSTALLED=$((INSTALLED+1))
|
|
290
|
+
fi
|
|
291
|
+
done
|
|
292
|
+
if [ "$INSTALLED" -gt 0 ]; then
|
|
293
|
+
FIXED=$((FIXED+1))
|
|
294
|
+
else
|
|
295
|
+
# Fall back to symlink
|
|
296
|
+
TARGET_DIR=""
|
|
297
|
+
for dir in "$HOME/.vscode/extensions" "$HOME/.vscode-insiders/extensions" "$HOME/.cursor/extensions" "$HOME/.windsurf/extensions"; do
|
|
298
|
+
[ -d "$dir" ] && TARGET_DIR="$dir" && break
|
|
299
|
+
done
|
|
300
|
+
[ -z "$TARGET_DIR" ] && TARGET_DIR="$HOME/.vscode/extensions" && mkdir -p "$TARGET_DIR"
|
|
301
|
+
rm -f "$TARGET_DIR"/neunaha.claws-* 2>/dev/null || true
|
|
302
|
+
if ln -sf "$INSTALL_DIR/extension" "$TARGET_DIR/neunaha.claws-$EXT_VERSION" 2>/dev/null \
|
|
303
|
+
|| sudo ln -sf "$INSTALL_DIR/extension" "$TARGET_DIR/neunaha.claws-$EXT_VERSION" 2>/dev/null; then
|
|
304
|
+
ok "fallback symlink created at $TARGET_DIR/neunaha.claws-$EXT_VERSION"
|
|
305
|
+
FIXED=$((FIXED+1))
|
|
306
|
+
else
|
|
307
|
+
fail "could not install or symlink extension"
|
|
308
|
+
ISSUES=$((ISSUES+1))
|
|
309
|
+
fi
|
|
310
|
+
fi
|
|
311
|
+
else
|
|
312
|
+
fail "npx/vsce unavailable and no existing install found"
|
|
313
|
+
ISSUES=$((ISSUES+1))
|
|
314
|
+
fi
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
# ─── 4. Project-local MCP registration ─────────────────────────────────────
|
|
318
|
+
check "Project .mcp.json at $PROJECT_ROOT"
|
|
319
|
+
PROJECT_MCP="$PROJECT_ROOT/.mcp.json"
|
|
320
|
+
if [ -f "$PROJECT_MCP" ] && grep -q '"claws"' "$PROJECT_MCP" 2>/dev/null; then
|
|
321
|
+
ok "claws registered in project"
|
|
322
|
+
else
|
|
323
|
+
fix "not registered — adding claws to $PROJECT_MCP"
|
|
324
|
+
# FINDING-B-2 (fix.sh mirror): guard against dangling symlinks before mkdir -p
|
|
325
|
+
if [ -L "$PROJECT_ROOT/.claws-bin" ]; then
|
|
326
|
+
warn ".claws-bin is a symlink — removing before mkdir"
|
|
327
|
+
rm -f "$PROJECT_ROOT/.claws-bin"
|
|
328
|
+
fi
|
|
329
|
+
mkdir -p "$PROJECT_ROOT/.claws-bin"
|
|
330
|
+
cp "$INSTALL_DIR/mcp_server.js" "$PROJECT_ROOT/.claws-bin/mcp_server.js"
|
|
331
|
+
chmod +x "$PROJECT_ROOT/.claws-bin/mcp_server.js"
|
|
332
|
+
# M-45: use fix-repair.js (json-safe.mjs: abort-on-malformed + atomic write).
|
|
333
|
+
# Path passed via env var — no string-interpolation into JS source (M-20).
|
|
334
|
+
if CLAWS_REPAIR_TARGET="$PROJECT_MCP" node --no-deprecation "$INSTALL_DIR/scripts/_helpers/fix-repair.js" mcp 2>&1 | sed 's/^/ /'; then
|
|
335
|
+
ok "wrote $PROJECT_MCP"
|
|
336
|
+
FIXED=$((FIXED+1))
|
|
337
|
+
else
|
|
338
|
+
fail "could not write $PROJECT_MCP — malformed JSON? Check backup above."
|
|
339
|
+
ISSUES=$((ISSUES+1))
|
|
340
|
+
fi
|
|
341
|
+
fi
|
|
342
|
+
|
|
343
|
+
# ─── 4b. Project .vscode/extensions.json recommends claws ─────────────────
|
|
344
|
+
check "Project .vscode/extensions.json recommends neunaha.claws"
|
|
345
|
+
VSCODE_EXT_JSON="$PROJECT_ROOT/.vscode/extensions.json"
|
|
346
|
+
if [ -f "$VSCODE_EXT_JSON" ] && grep -q "neunaha.claws" "$VSCODE_EXT_JSON" 2>/dev/null; then
|
|
347
|
+
ok "already recommended"
|
|
348
|
+
else
|
|
349
|
+
fix "adding neunaha.claws to workspace recommendations"
|
|
350
|
+
mkdir -p "$PROJECT_ROOT/.vscode"
|
|
351
|
+
# M-46: use fix-repair.js (json-safe.mjs: abort-on-malformed + atomic write + JSONC-tolerant).
|
|
352
|
+
# Path passed via env var — no string-interpolation into JS source (M-20).
|
|
353
|
+
if CLAWS_REPAIR_TARGET="$VSCODE_EXT_JSON" node --no-deprecation "$INSTALL_DIR/scripts/_helpers/fix-repair.js" extensions 2>&1 | sed 's/^/ /'; then
|
|
354
|
+
ok "wrote $VSCODE_EXT_JSON"
|
|
355
|
+
FIXED=$((FIXED+1))
|
|
356
|
+
else
|
|
357
|
+
fail "could not write $VSCODE_EXT_JSON — malformed JSON? Check backup above."
|
|
358
|
+
ISSUES=$((ISSUES+1))
|
|
359
|
+
fi
|
|
360
|
+
fi
|
|
361
|
+
|
|
362
|
+
# ─── 4c. .claws-bin integrity (unconditional, independent of .mcp.json) ──────
|
|
363
|
+
check ".claws-bin integrity"
|
|
364
|
+
CLAWS_BIN="$PROJECT_ROOT/.claws-bin"
|
|
365
|
+
CLAWS_BIN_SERVER="$CLAWS_BIN/mcp_server.js"
|
|
366
|
+
BIN_OK=1
|
|
367
|
+
if [ ! -d "$CLAWS_BIN" ]; then
|
|
368
|
+
fix ".claws-bin directory missing — creating and deploying mcp_server.js"
|
|
369
|
+
mkdir -p "$CLAWS_BIN"
|
|
370
|
+
BIN_OK=0
|
|
371
|
+
elif [ -L "$CLAWS_BIN_SERVER" ] && [ ! -e "$CLAWS_BIN_SERVER" ]; then
|
|
372
|
+
fix ".claws-bin/mcp_server.js is a dangling symlink — removing"
|
|
373
|
+
rm -f "$CLAWS_BIN_SERVER"
|
|
374
|
+
BIN_OK=0
|
|
375
|
+
elif [ ! -f "$CLAWS_BIN_SERVER" ]; then
|
|
376
|
+
fix ".claws-bin/mcp_server.js missing"
|
|
377
|
+
BIN_OK=0
|
|
378
|
+
fi
|
|
379
|
+
if [ "$BIN_OK" -eq 0 ]; then
|
|
380
|
+
if cp "$INSTALL_DIR/mcp_server.js" "$CLAWS_BIN_SERVER" 2>/dev/null && chmod +x "$CLAWS_BIN_SERVER"; then
|
|
381
|
+
ok ".claws-bin/mcp_server.js restored from $INSTALL_DIR"
|
|
382
|
+
FIXED=$((FIXED+1))
|
|
383
|
+
else
|
|
384
|
+
fail "could not copy mcp_server.js to $CLAWS_BIN — check permissions"
|
|
385
|
+
ISSUES=$((ISSUES+1))
|
|
386
|
+
fi
|
|
387
|
+
else
|
|
388
|
+
ok ".claws-bin/mcp_server.js present"
|
|
389
|
+
fi
|
|
390
|
+
|
|
391
|
+
# Fix #1 (v0.7.12): ensure .claws-bin/package.json exists with {"type":"commonjs"}.
|
|
392
|
+
# Auto-restores so existing installs without it get fixed by running /claws-fix.
|
|
393
|
+
if [ -d "$CLAWS_BIN" ] && [ ! -f "$CLAWS_BIN/package.json" ]; then
|
|
394
|
+
fix ".claws-bin/package.json missing — writing ESM compat shim"
|
|
395
|
+
printf '{\n "type": "commonjs",\n "_comment": "Forces CommonJS for .js files in .claws-bin/. Required when the parent project has type:module in its package.json (Next.js, Vite, etc.)"\n}\n' > "$CLAWS_BIN/package.json"
|
|
396
|
+
ok "wrote .claws-bin/package.json"
|
|
397
|
+
FIXED=$((FIXED+1))
|
|
398
|
+
else
|
|
399
|
+
ok ".claws-bin/package.json present (ESM compat shim)"
|
|
400
|
+
fi
|
|
401
|
+
|
|
402
|
+
# ─── 5. MCP server handshake ───────────────────────────────────────────────
|
|
403
|
+
check "MCP server handshake"
|
|
404
|
+
MCP_PATH="$INSTALL_DIR/mcp_server.js"
|
|
405
|
+
[ -f "$PROJECT_ROOT/.claws-bin/mcp_server.js" ] && MCP_PATH="$PROJECT_ROOT/.claws-bin/mcp_server.js"
|
|
406
|
+
if command -v node &>/dev/null && [ -f "$MCP_PATH" ]; then
|
|
407
|
+
# M-44: mcp_server.js uses newline-delimited JSON, not Content-Length framing.
|
|
408
|
+
# Full protocolVersion + clientInfo required per MCP 2024-11-05 spec.
|
|
409
|
+
HANDSHAKE=$(node --no-deprecation -e '
|
|
410
|
+
const { spawn } = require("child_process");
|
|
411
|
+
const mcp = spawn("node", [process.argv[1]], { stdio: ["pipe", "pipe", "ignore"] });
|
|
412
|
+
const req = JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "claws-fix", version: "1" } } });
|
|
413
|
+
let buf = "";
|
|
414
|
+
const done = (c, o) => { try { mcp.kill(); } catch {} process.stdout.write(o); process.exit(c); };
|
|
415
|
+
const timer = setTimeout(() => done(1, "TIMEOUT"), 4000);
|
|
416
|
+
mcp.stdout.on("data", d => { buf += d.toString("utf8"); if (buf.includes("claws")) { clearTimeout(timer); done(0, buf.slice(0, 200)); } });
|
|
417
|
+
mcp.on("error", e => { clearTimeout(timer); done(1, "SPAWN_ERROR: " + e.message); });
|
|
418
|
+
mcp.stdin.write(req + "\n");
|
|
419
|
+
' "$MCP_PATH" 2>&1 || echo "FAILED")
|
|
420
|
+
if echo "$HANDSHAKE" | grep -q "claws"; then
|
|
421
|
+
ok "MCP server responds (initialize OK)"
|
|
422
|
+
else
|
|
423
|
+
fail "MCP server failed to respond: ${HANDSHAKE:0:200}"
|
|
424
|
+
fix "refreshing mcp_server.js from $INSTALL_DIR and re-probing"
|
|
425
|
+
if cp "$INSTALL_DIR/mcp_server.js" "$PROJECT_ROOT/.claws-bin/mcp_server.js" 2>/dev/null; then
|
|
426
|
+
chmod +x "$PROJECT_ROOT/.claws-bin/mcp_server.js" 2>/dev/null || true
|
|
427
|
+
HANDSHAKE2=$(node --no-deprecation -e '
|
|
428
|
+
const { spawn } = require("child_process");
|
|
429
|
+
const mcp = spawn("node", [process.argv[1]], { stdio: ["pipe", "pipe", "ignore"] });
|
|
430
|
+
const req = JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "claws-fix", version: "1" } } });
|
|
431
|
+
let buf = "";
|
|
432
|
+
const done = (c, o) => { try { mcp.kill(); } catch {} process.stdout.write(o); process.exit(c); };
|
|
433
|
+
const timer = setTimeout(() => done(1, "TIMEOUT"), 4000);
|
|
434
|
+
mcp.stdout.on("data", d => { buf += d.toString("utf8"); if (buf.includes("claws")) { clearTimeout(timer); done(0, buf.slice(0, 200)); } });
|
|
435
|
+
mcp.on("error", e => { clearTimeout(timer); done(1, "SPAWN_ERROR: " + e.message); });
|
|
436
|
+
mcp.stdin.write(req + "\n");
|
|
437
|
+
' "$PROJECT_ROOT/.claws-bin/mcp_server.js" 2>&1 || echo "FAILED")
|
|
438
|
+
if echo "$HANDSHAKE2" | grep -q "claws"; then
|
|
439
|
+
ok "MCP server responds after refresh"
|
|
440
|
+
FIXED=$((FIXED+1))
|
|
441
|
+
else
|
|
442
|
+
fail "MCP server still unresponsive after refresh: ${HANDSHAKE2:0:200}"
|
|
443
|
+
ISSUES=$((ISSUES+1))
|
|
444
|
+
fi
|
|
445
|
+
else
|
|
446
|
+
fail "could not copy mcp_server.js from $INSTALL_DIR — check permissions"
|
|
447
|
+
ISSUES=$((ISSUES+1))
|
|
448
|
+
fi
|
|
449
|
+
fi
|
|
450
|
+
else
|
|
451
|
+
fail "node or $MCP_PATH missing"
|
|
452
|
+
ISSUES=$((ISSUES+1))
|
|
453
|
+
fi
|
|
454
|
+
|
|
455
|
+
# ─── 6. Extension socket liveness ──────────────────────────────────────────
|
|
456
|
+
check "Extension socket .claws/claws.sock"
|
|
457
|
+
SOCK="$PROJECT_ROOT/.claws/claws.sock"
|
|
458
|
+
if [ -S "$SOCK" ]; then
|
|
459
|
+
if command -v nc &>/dev/null && echo '{"id":1,"cmd":"list"}' | nc -U "$SOCK" 2>/dev/null | head -c 200 | grep -q '"ok"'; then
|
|
460
|
+
ok "socket is LIVE — extension listening"
|
|
461
|
+
# FINDING-C-13: probe wrapped terminals for missing logPath (pipe-mode / script(1) failure)
|
|
462
|
+
LIST_OUT=$(echo '{"id":2,"cmd":"list"}' | nc -U "$SOCK" 2>/dev/null | head -c 4096)
|
|
463
|
+
if echo "$LIST_OUT" | node --no-deprecation -e "
|
|
464
|
+
const lines = require('fs').readFileSync('/dev/stdin','utf8').split('\n');
|
|
465
|
+
let broken = 0;
|
|
466
|
+
for (const l of lines) {
|
|
467
|
+
try {
|
|
468
|
+
const j = JSON.parse(l);
|
|
469
|
+
if (j.ok && Array.isArray(j.terminals)) {
|
|
470
|
+
for (const t of j.terminals) {
|
|
471
|
+
if (t.wrapped && !t.logPath) {
|
|
472
|
+
process.stderr.write(' WRAPPED_NO_LOG: terminal ' + t.id + ' (' + (t.name||'?') + ')\n');
|
|
473
|
+
broken++;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
} catch {}
|
|
478
|
+
}
|
|
479
|
+
if (broken > 0) { process.stdout.write(broken + ''); process.exit(1); }
|
|
480
|
+
process.exit(0);
|
|
481
|
+
" 2>/dev/null; then
|
|
482
|
+
:
|
|
483
|
+
else
|
|
484
|
+
BAD_COUNT=$(echo "$LIST_OUT" | node --no-deprecation -e "
|
|
485
|
+
const ls=require('fs').readFileSync('/dev/stdin','utf8').split('\n');
|
|
486
|
+
let n=0;
|
|
487
|
+
for(const l of ls){try{const j=JSON.parse(l);if(j.ok&&Array.isArray(j.terminals)){for(const t of j.terminals){if(t.wrapped&&!t.logPath)n++;}}}catch{}}
|
|
488
|
+
process.stdout.write(n+'');
|
|
489
|
+
" 2>/dev/null || echo "?")
|
|
490
|
+
fail "$BAD_COUNT wrapped terminal(s) have wrapped=true but no logPath — script(1) may have failed"
|
|
491
|
+
# script(1) availability check (Linux only; macOS always has it)
|
|
492
|
+
if [ "$(uname -s)" = "Linux" ] && ! command -v script &>/dev/null; then
|
|
493
|
+
fix "script(1) not found on Linux — install bsdutils or util-linux: sudo apt-get install bsdutils"
|
|
494
|
+
fi
|
|
495
|
+
ISSUES=$((ISSUES+1))
|
|
496
|
+
fi
|
|
497
|
+
else
|
|
498
|
+
fix "socket is stale — VS Code needs to reload"
|
|
499
|
+
rm -f "$SOCK" 2>/dev/null || true
|
|
500
|
+
fail "after reload the extension will re-create the socket"
|
|
501
|
+
ISSUES=$((ISSUES+1))
|
|
502
|
+
fi
|
|
503
|
+
else
|
|
504
|
+
fail "no socket at $SOCK"
|
|
505
|
+
fix "this is normal if VS Code isn't running this project — reload a VS Code window on this folder"
|
|
506
|
+
ISSUES=$((ISSUES+1))
|
|
507
|
+
fi
|
|
508
|
+
|
|
509
|
+
# ─── 6b. Shell hook sourcing in rc files (FINDING-C-11) ─────────────────────
|
|
510
|
+
check "Shell hook sourcing in rc files (.zshrc / .bashrc)"
|
|
511
|
+
HOOK_SCRIPT="$INSTALL_DIR/scripts/shell-hook.sh"
|
|
512
|
+
RC_SOURCED=0
|
|
513
|
+
RC_CHECKED=0
|
|
514
|
+
for RC in "$HOME/.zshrc" "$HOME/.bashrc"; do
|
|
515
|
+
[ -f "$RC" ] || continue
|
|
516
|
+
RC_CHECKED=$((RC_CHECKED+1))
|
|
517
|
+
if grep -q 'shell-hook.sh' "$RC" 2>/dev/null; then
|
|
518
|
+
ok "shell-hook.sh sourced in $(basename "$RC")"
|
|
519
|
+
RC_SOURCED=$((RC_SOURCED+1))
|
|
520
|
+
fi
|
|
521
|
+
done
|
|
522
|
+
if [ "$RC_SOURCED" -eq 0 ] && [ -f "$HOOK_SCRIPT" ]; then
|
|
523
|
+
TARGET_RC="$HOME/.zshrc"
|
|
524
|
+
[ -f "$HOME/.bashrc" ] && TARGET_RC="$HOME/.bashrc"
|
|
525
|
+
[ -f "$HOME/.zshrc" ] && TARGET_RC="$HOME/.zshrc"
|
|
526
|
+
fix "shell-hook.sh not sourced in any rc file — appending to $(basename "$TARGET_RC")"
|
|
527
|
+
printf '\n# Claws shell functions (claws-ls, claws-new, claws-run, claws-log)\n[ -f "%s" ] && source "%s"\n' \
|
|
528
|
+
"$HOOK_SCRIPT" "$HOOK_SCRIPT" >> "$TARGET_RC"
|
|
529
|
+
ok "appended source line to $(basename "$TARGET_RC") — open a new terminal to activate"
|
|
530
|
+
FIXED=$((FIXED+1))
|
|
531
|
+
elif [ "$RC_CHECKED" -eq 0 ]; then
|
|
532
|
+
fix "no .zshrc or .bashrc found — skipping shell-hook sourcing check"
|
|
533
|
+
fi
|
|
534
|
+
|
|
535
|
+
# ─── 7. Stale global MCP registration in ~/.claude/settings.json ───────────
|
|
536
|
+
check "Global ~/.claude/settings.json (informational)"
|
|
537
|
+
if [ -f "$HOME/.claude/settings.json" ] && grep -q '"claws"' "$HOME/.claude/settings.json" 2>/dev/null; then
|
|
538
|
+
if [ -f "$PROJECT_MCP" ]; then
|
|
539
|
+
fix "global claws entry exists alongside project .mcp.json — project takes precedence (safe to remove global if you want)"
|
|
540
|
+
else
|
|
541
|
+
ok "global claws registration active"
|
|
542
|
+
fi
|
|
543
|
+
fi
|
|
544
|
+
|
|
545
|
+
# ─── 7b. ~/.claude/settings.json JSON validity (FINDING-C-7, P0) ─────────────
|
|
546
|
+
# A malformed settings.json causes EVERY Claude Code hook (SessionStart,
|
|
547
|
+
# PreToolUse, Stop) to fail silently on every tool call. Detect and repair
|
|
548
|
+
# before the stale-paths check below, so check 8 can assume valid JSON.
|
|
549
|
+
check "~/.claude/settings.json is valid JSON"
|
|
550
|
+
if [ -f "$HOME/.claude/settings.json" ]; then
|
|
551
|
+
if ! node --no-deprecation -e "
|
|
552
|
+
const fs=require('fs');
|
|
553
|
+
JSON.parse(fs.readFileSync(process.argv[1],'utf8'));
|
|
554
|
+
" "$HOME/.claude/settings.json" 2>/dev/null; then
|
|
555
|
+
fail "settings.json is malformed JSON — ALL claude hooks will fail silently"
|
|
556
|
+
fix "backing up and re-injecting Claws hooks via inject-settings-hooks.js"
|
|
557
|
+
cp "$HOME/.claude/settings.json" "$HOME/.claude/settings.json.bak.$(date +%s)" 2>/dev/null || true
|
|
558
|
+
if node --no-deprecation "$INSTALL_DIR/scripts/inject-settings-hooks.js" --remove >/dev/null 2>&1 \
|
|
559
|
+
&& node --no-deprecation "$INSTALL_DIR/scripts/inject-settings-hooks.js" "$INSTALL_DIR/scripts" >/dev/null 2>&1; then
|
|
560
|
+
ok "hooks re-written into repaired settings.json"
|
|
561
|
+
FIXED=$((FIXED+1))
|
|
562
|
+
else
|
|
563
|
+
fail "automatic repair failed — manually edit $HOME/.claude/settings.json (backup saved)"
|
|
564
|
+
ISSUES=$((ISSUES+1))
|
|
565
|
+
fi
|
|
566
|
+
else
|
|
567
|
+
ok "settings.json is valid JSON"
|
|
568
|
+
fi
|
|
569
|
+
fi
|
|
570
|
+
|
|
571
|
+
# ─── 8. Stale Claws hook script paths in ~/.claude/settings.json (v0.7.3) ──
|
|
572
|
+
# Detects the "SessionStart:startup hook error / non-blocking status code"
|
|
573
|
+
# class of failure: registered hook commands point at a path that no longer
|
|
574
|
+
# exists (install dir moved, sandbox path leaked into settings, prior
|
|
575
|
+
# install was deleted). Re-registers from the current INSTALL_DIR so
|
|
576
|
+
# Claude Code stops reporting hook errors on every tool call.
|
|
577
|
+
check "Hook script paths in ~/.claude/settings.json"
|
|
578
|
+
if [ -f "$HOME/.claude/settings.json" ] && grep -q '_source.*claws' "$HOME/.claude/settings.json" 2>/dev/null; then
|
|
579
|
+
STALE_HOOKS=$(node --no-deprecation -e "
|
|
580
|
+
const fs=require('fs');
|
|
581
|
+
const j=JSON.parse(fs.readFileSync('$HOME/.claude/settings.json','utf8'));
|
|
582
|
+
const stale=[];
|
|
583
|
+
for(const ev of Object.keys(j.hooks||{})){
|
|
584
|
+
for(const e of (j.hooks[ev]||[])){
|
|
585
|
+
if(e._source!=='claws')continue;
|
|
586
|
+
if(!e.hooks||!e.hooks[0])continue;
|
|
587
|
+
const cmd=e.hooks[0].command||'';
|
|
588
|
+
// Extract the .js path from either: plain 'node \"<path>\"' OR
|
|
589
|
+
// wrapped 'sh -c \"...\" \"<path>\"' (path is the LAST quoted token).
|
|
590
|
+
const matches=[...cmd.matchAll(/\"([^\"]+\\.js)\"/g)];
|
|
591
|
+
if(!matches.length)continue;
|
|
592
|
+
const scriptPath=matches[matches.length-1][1];
|
|
593
|
+
if(!fs.existsSync(scriptPath)) stale.push({event:ev,path:scriptPath});
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
if(stale.length===0){console.log('OK')}
|
|
597
|
+
else for(const s of stale)console.log(s.event+'\t'+s.path);
|
|
598
|
+
" 2>/dev/null)
|
|
599
|
+
if [ "$STALE_HOOKS" = "OK" ]; then
|
|
600
|
+
ok "all registered Claws hook paths resolve"
|
|
601
|
+
else
|
|
602
|
+
fail "stale hook path(s) detected:"
|
|
603
|
+
echo "$STALE_HOOKS" | sed 's/^/ /'
|
|
604
|
+
fix "re-registering hooks against $INSTALL_DIR/scripts/hooks/"
|
|
605
|
+
if node --no-deprecation "$INSTALL_DIR/scripts/inject-settings-hooks.js" --remove >/dev/null 2>&1 \
|
|
606
|
+
&& node --no-deprecation "$INSTALL_DIR/scripts/inject-settings-hooks.js" "$INSTALL_DIR/scripts" >/dev/null 2>&1; then
|
|
607
|
+
ok "hooks re-registered from $INSTALL_DIR/scripts/hooks/"
|
|
608
|
+
FIXED=$((FIXED+1))
|
|
609
|
+
else
|
|
610
|
+
fail "could not re-register hooks — check $INSTALL_DIR/scripts/inject-settings-hooks.js exists"
|
|
611
|
+
ISSUES=$((ISSUES+1))
|
|
612
|
+
fi
|
|
613
|
+
fi
|
|
614
|
+
fi
|
|
615
|
+
|
|
616
|
+
# ─── 8b. PostToolUse spawn-class hooks registered (Wave C) ──────────────────
|
|
617
|
+
# Detect the case where settings.json has valid Claws hooks but no PostToolUse
|
|
618
|
+
# entries — typical after upgrading from pre-Wave C install. Check 8 only detects
|
|
619
|
+
# stale paths; it cannot detect entirely absent entries.
|
|
620
|
+
check "PostToolUse spawn-class hooks registered (Wave C monitor race-close)"
|
|
621
|
+
if [ -f "$HOME/.claude/settings.json" ]; then
|
|
622
|
+
if CLAWS_SETTINGS_CHECK="$HOME/.claude/settings.json" node --no-deprecation -e "
|
|
623
|
+
const s = JSON.parse(require('fs').readFileSync(process.env.CLAWS_SETTINGS_CHECK, 'utf8'));
|
|
624
|
+
const h = (s.hooks && s.hooks.PostToolUse) || [];
|
|
625
|
+
process.exit(h.some(e => e.matcher && e.matcher.includes('mcp__claws__claws_worker')) ? 0 : 1);
|
|
626
|
+
" 2>/dev/null; then
|
|
627
|
+
ok "PostToolUse spawn-class hooks registered"
|
|
628
|
+
else
|
|
629
|
+
fail "PostToolUse spawn-class hooks missing — Wave C monitor race-close inactive"
|
|
630
|
+
fix "re-registering all Claws hooks via inject-settings-hooks.js --update"
|
|
631
|
+
if node --no-deprecation "$INSTALL_DIR/scripts/inject-settings-hooks.js" "$INSTALL_DIR/scripts" --update 2>/dev/null; then
|
|
632
|
+
ok "hooks re-registered (PostToolUse entries added)"
|
|
633
|
+
FIXED=$((FIXED+1))
|
|
634
|
+
else
|
|
635
|
+
fail "could not re-register hooks — check $INSTALL_DIR/scripts/inject-settings-hooks.js"
|
|
636
|
+
ISSUES=$((ISSUES+1))
|
|
637
|
+
fi
|
|
638
|
+
fi
|
|
639
|
+
fi
|
|
640
|
+
|
|
641
|
+
# ─── 9. Hook script execution probe (v0.7.3) ──────────────────────────────
|
|
642
|
+
# Defense in depth: even if the path resolves, the script might crash on
|
|
643
|
+
# load (ESM vs CJS, missing deps, etc). Invoke each hook with synthetic
|
|
644
|
+
# stdin and confirm exit 0. Hooks were hardened in v0.7.3 to never crash,
|
|
645
|
+
# but if a user is running pre-v0.7.3 hook scripts, we surface the gap.
|
|
646
|
+
check "Hook scripts execute cleanly"
|
|
647
|
+
HOOK_DIR="$INSTALL_DIR/scripts/hooks"
|
|
648
|
+
HOOKS_PRESENT=0
|
|
649
|
+
HOOKS_OK=0
|
|
650
|
+
HOOKS_FAIL=0
|
|
651
|
+
HOOKS_FAILED_NAMES=""
|
|
652
|
+
for hook in session-start-claws.js pre-tool-use-claws.js post-tool-use-claws.js stop-claws.js; do
|
|
653
|
+
[ -f "$HOOK_DIR/$hook" ] || continue
|
|
654
|
+
HOOKS_PRESENT=$((HOOKS_PRESENT+1))
|
|
655
|
+
# Use a Node-based 5s ceiling instead of `timeout` (not on macOS by default).
|
|
656
|
+
# The hook should exit in <100ms anyway; this just guards against pathological
|
|
657
|
+
# hangs in pre-v0.7.3 hook scripts.
|
|
658
|
+
if echo '{"cwd":"/tmp","tool_name":"Bash","tool_input":{"command":"ls"}}' \
|
|
659
|
+
| node --no-deprecation -e "
|
|
660
|
+
const { spawn } = require('child_process');
|
|
661
|
+
let buf = '';
|
|
662
|
+
process.stdin.on('data', d => buf += d);
|
|
663
|
+
process.stdin.on('end', () => {
|
|
664
|
+
const ch = spawn('node', ['$HOOK_DIR/$hook'], { stdio: ['pipe','pipe','pipe'] });
|
|
665
|
+
const t = setTimeout(() => { try { ch.kill('SIGKILL'); } catch {} process.exit(124); }, 5000);
|
|
666
|
+
ch.stdin.write(buf); ch.stdin.end();
|
|
667
|
+
ch.on('exit', c => { clearTimeout(t); process.exit(c||0); });
|
|
668
|
+
ch.on('error', () => { clearTimeout(t); process.exit(127); });
|
|
669
|
+
});
|
|
670
|
+
" >/dev/null 2>&1; then
|
|
671
|
+
HOOKS_OK=$((HOOKS_OK+1))
|
|
672
|
+
else
|
|
673
|
+
HOOKS_FAIL=$((HOOKS_FAIL+1))
|
|
674
|
+
HOOKS_FAILED_NAMES="$HOOKS_FAILED_NAMES $hook"
|
|
675
|
+
fi
|
|
676
|
+
done
|
|
677
|
+
if [ "$HOOKS_PRESENT" -eq 0 ]; then
|
|
678
|
+
fail "no hook scripts found at $HOOK_DIR — run install.sh"
|
|
679
|
+
ISSUES=$((ISSUES+1))
|
|
680
|
+
elif [ "$HOOKS_FAIL" -eq 0 ]; then
|
|
681
|
+
ok "all $HOOKS_OK hook script(s) probe clean"
|
|
682
|
+
else
|
|
683
|
+
fail "$HOOKS_FAIL of $HOOKS_PRESENT hook(s) exited non-zero on probe:$HOOKS_FAILED_NAMES"
|
|
684
|
+
fix "if you have an older Claws version, run install.sh to refresh hooks (v0.7.3+ hardening)"
|
|
685
|
+
ISSUES=$((ISSUES+1))
|
|
686
|
+
fi
|
|
687
|
+
|
|
688
|
+
# ─── Summary ──────────────────────────────────────────────────────────────
|
|
689
|
+
echo ""
|
|
690
|
+
if [ "$ISSUES" -eq 0 ]; then
|
|
691
|
+
printf "${C_GREEN}${C_BOLD}All checks passed${C_RESET} — ${FIXED} auto-repairs applied.\n"
|
|
692
|
+
else
|
|
693
|
+
printf "${C_YELLOW}${C_BOLD}${FIXED} fixed, ${ISSUES} still open.${C_RESET}\n"
|
|
694
|
+
fi
|
|
695
|
+
echo ""
|
|
696
|
+
printf "${C_BOLD}Activate changes:${C_RESET}\n"
|
|
697
|
+
echo " 1. Reload VS Code: Cmd+Shift+P → Developer: Reload Window"
|
|
698
|
+
echo " 2. Restart Claude Code: exit this session and re-open 'claude' in this project"
|
|
699
|
+
echo ""
|
|
700
|
+
printf "${C_BOLD}Still broken after that?${C_RESET}\n"
|
|
701
|
+
echo " Run /claws-report to bundle logs + state for a support request."
|
|
702
|
+
echo ""
|