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
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Claws — update runner
|
|
3
|
+
#
|
|
4
|
+
# Self-contained. Works two ways:
|
|
5
|
+
#
|
|
6
|
+
# # 1. Via curl URL (no prior clone needed if ~/.claws-src is already there):
|
|
7
|
+
# bash <(curl -fsSL https://raw.githubusercontent.com/neunaha/claws/main/scripts/update.sh)
|
|
8
|
+
#
|
|
9
|
+
# # 2. From the local clone:
|
|
10
|
+
# bash ~/.claws-src/scripts/update.sh
|
|
11
|
+
#
|
|
12
|
+
# The /claws-update slash command is a thin wrapper around option 2.
|
|
13
|
+
#
|
|
14
|
+
# New update steps get added to this script and users pick them up on their
|
|
15
|
+
# next update — no per-project re-install of the slash-command markdown.
|
|
16
|
+
#
|
|
17
|
+
# Usage: bash update.sh [project-root] (defaults to current pwd)
|
|
18
|
+
|
|
19
|
+
set -eo pipefail
|
|
20
|
+
|
|
21
|
+
# FINDING-B-5: --dry-run flag — show what would change without applying it.
|
|
22
|
+
DRY_RUN=0
|
|
23
|
+
_update_args=()
|
|
24
|
+
for _arg in "$@"; do
|
|
25
|
+
case "$_arg" in
|
|
26
|
+
--dry-run) DRY_RUN=1 ;;
|
|
27
|
+
*) _update_args+=("$_arg") ;;
|
|
28
|
+
esac
|
|
29
|
+
done
|
|
30
|
+
set -- "${_update_args[@]+"${_update_args[@]}"}"
|
|
31
|
+
unset _update_args _arg
|
|
32
|
+
|
|
33
|
+
INSTALL_DIR="${CLAWS_DIR:-$HOME/.claws-src}"
|
|
34
|
+
# PROJECT_PWD is set by the slash command; fall back to $1, then $PWD.
|
|
35
|
+
PROJECT_ROOT="${1:-${PROJECT_PWD:-$(pwd)}}"
|
|
36
|
+
|
|
37
|
+
# ─── Colors ────────────────────────────────────────────────────────────────
|
|
38
|
+
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
|
|
39
|
+
C_RESET='\033[0m'; C_BOLD='\033[1m'
|
|
40
|
+
C_GREEN='\033[0;32m'; C_YELLOW='\033[0;33m'; C_DIM='\033[2m'
|
|
41
|
+
else
|
|
42
|
+
C_RESET=''; C_BOLD=''; C_GREEN=''; C_YELLOW=''; C_DIM=''
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
header() { printf "\n${C_BOLD}═════ %s ═════${C_RESET}\n" "$*"; }
|
|
46
|
+
note() { printf " ${C_DIM}%s${C_RESET}\n" "$*"; }
|
|
47
|
+
|
|
48
|
+
# M-19: Anchor CLAWS_LOG BEFORE install.sh runs so Step 6's warning
|
|
49
|
+
# "see install log: $CLAWS_LOG" references the real log written by install.sh.
|
|
50
|
+
# install.sh uses ${CLAWS_LOG:-...} so it inherits this value when exported.
|
|
51
|
+
CLAWS_LOG="${CLAWS_LOG:-/tmp/claws-install-$(date +%Y%m%d-%H%M%S)-$$.log}"
|
|
52
|
+
export CLAWS_LOG
|
|
53
|
+
|
|
54
|
+
# ─── Step 0: Ensure Claws source + pull latest ─────────────────────────────
|
|
55
|
+
# Runs whether update.sh was invoked via curl URL or from the local clone —
|
|
56
|
+
# this is the one action that can't be delegated to install.sh because
|
|
57
|
+
# install.sh's logic assumes INSTALL_DIR is already up-to-date.
|
|
58
|
+
if [ ! -d "$INSTALL_DIR/.git" ]; then
|
|
59
|
+
if [ -d "$INSTALL_DIR" ]; then
|
|
60
|
+
echo "Claws source at $INSTALL_DIR exists but is not a git clone." >&2
|
|
61
|
+
echo "Remove it or set CLAWS_DIR to a different path, then re-run." >&2
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
echo "Claws not yet installed — running the installer first."
|
|
65
|
+
bash <(curl -fsSL https://raw.githubusercontent.com/neunaha/claws/main/scripts/install.sh)
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
header "Pulling latest Claws source"
|
|
70
|
+
# M-21: track whether git pull succeeded so install.sh can skip stale-source
|
|
71
|
+
# CLAUDE.md re-injection. GIT_PULL_OK=0 exported on failure → install.sh gates on it.
|
|
72
|
+
GIT_PULL_OK=1
|
|
73
|
+
_claws_prev_sha=$(cd "$INSTALL_DIR" && git rev-parse HEAD 2>/dev/null || echo "unknown")
|
|
74
|
+
if ( cd "$INSTALL_DIR" && git pull --ff-only --quiet origin main 2>/tmp/claws-pull-err.$$ ); then
|
|
75
|
+
_claws_new_sha=$(cd "$INSTALL_DIR" && git rev-parse HEAD 2>/dev/null || echo "unknown")
|
|
76
|
+
if [ "$_claws_prev_sha" = "$_claws_new_sha" ]; then
|
|
77
|
+
note "already up-to-date ($(cd "$INSTALL_DIR" && git log --oneline -1))"
|
|
78
|
+
else
|
|
79
|
+
note "git pull OK (${_claws_prev_sha:0:7} → ${_claws_new_sha:0:7})"
|
|
80
|
+
fi
|
|
81
|
+
rm -f /tmp/claws-pull-err.$$
|
|
82
|
+
else
|
|
83
|
+
GIT_PULL_OK=0
|
|
84
|
+
printf " ${C_YELLOW}!${C_RESET} git pull failed — continuing with existing tree\n"
|
|
85
|
+
if [ -s /tmp/claws-pull-err.$$ ]; then
|
|
86
|
+
sed 's/^/ /' /tmp/claws-pull-err.$$
|
|
87
|
+
fi
|
|
88
|
+
printf " ${C_YELLOW}!${C_RESET} You will be installing from the LOCAL clone (last commit: $(cd "$INSTALL_DIR" && git log --oneline -1 2>/dev/null))\n"
|
|
89
|
+
printf " ${C_YELLOW}!${C_RESET} CLAUDE.md re-injection skipped — stale source would overwrite tool set (M-21)\n"
|
|
90
|
+
rm -f /tmp/claws-pull-err.$$
|
|
91
|
+
fi
|
|
92
|
+
export GIT_PULL_OK
|
|
93
|
+
unset _claws_prev_sha _claws_new_sha
|
|
94
|
+
|
|
95
|
+
# ─── Step 1: Sync marketplace-facing docs ──────────────────────────────────
|
|
96
|
+
# The extension's README and CHANGELOG mirror the repo root so the VSIX (and
|
|
97
|
+
# the installed extension folder) stay current.
|
|
98
|
+
header "Syncing extension docs"
|
|
99
|
+
cp "$INSTALL_DIR/README.md" "$INSTALL_DIR/extension/README.md" 2>/dev/null && note "README.md → extension/" || true
|
|
100
|
+
cp "$INSTALL_DIR/CHANGELOG.md" "$INSTALL_DIR/extension/CHANGELOG.md" 2>/dev/null && note "CHANGELOG.md → extension/" || true
|
|
101
|
+
|
|
102
|
+
# ─── Step 2: Run the installer against the user's project ──────────────────
|
|
103
|
+
# install.sh is the single source of truth for everything the update does:
|
|
104
|
+
# extension build + symlink, project-local files, CLAUDE.md migration +
|
|
105
|
+
# injection, shell hook, verification, banner, install log. Anything new we
|
|
106
|
+
# want the update flow to do — add it to install.sh, not here.
|
|
107
|
+
header "Running installer"
|
|
108
|
+
if [ "$DRY_RUN" = "1" ]; then
|
|
109
|
+
note "--dry-run: skipping installer. Pending git diff from $INSTALL_DIR:"
|
|
110
|
+
( cd "$INSTALL_DIR" && git log --oneline origin/main..HEAD 2>/dev/null || true )
|
|
111
|
+
( cd "$INSTALL_DIR" && git diff --stat origin/main 2>/dev/null || true )
|
|
112
|
+
else
|
|
113
|
+
( cd "$PROJECT_ROOT" && bash "$INSTALL_DIR/scripts/install.sh" )
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# ─── Step 3: Print the newest CHANGELOG entry ──────────────────────────────
|
|
117
|
+
header "What's new"
|
|
118
|
+
if [ -f "$INSTALL_DIR/CHANGELOG.md" ]; then
|
|
119
|
+
# Extract exactly the most-recent `## [x.y.z]` section.
|
|
120
|
+
awk '/^## \[/{c++} c==1 && NR>1{print} c==2{exit}' "$INSTALL_DIR/CHANGELOG.md" | head -80
|
|
121
|
+
echo ""
|
|
122
|
+
note "Full CHANGELOG: $INSTALL_DIR/CHANGELOG.md"
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# ─── Step 4: Post-update hooks ─────────────────────────────────────────────
|
|
126
|
+
# Any future one-off migrations (cleaning up stale sockets, retiring old
|
|
127
|
+
# config keys, notifying of breaking changes) go here. On first run each
|
|
128
|
+
# migration can write a marker file so it never runs twice.
|
|
129
|
+
#
|
|
130
|
+
# M-26: Socket probe is health-check only. The previous version deleted the
|
|
131
|
+
# socket when the probe failed, which races with VS Code's extension hot-reload
|
|
132
|
+
# after a VSIX install — the extension may be momentarily unreachable while
|
|
133
|
+
# reactivating, so a failed probe does NOT mean the socket is stale.
|
|
134
|
+
# Destructive cleanup is deferred to /claws-fix (user-explicit step).
|
|
135
|
+
if [ -S "$PROJECT_ROOT/.claws/claws.sock" ]; then
|
|
136
|
+
_claws_sock="$PROJECT_ROOT/.claws/claws.sock"
|
|
137
|
+
_claws_alive=0
|
|
138
|
+
if command -v node >/dev/null 2>&1; then
|
|
139
|
+
# M-20: pass path via env var — handles project roots with apostrophes/backslashes
|
|
140
|
+
# without causing JS syntax errors from string interpolation in -e argument.
|
|
141
|
+
if CLAWS_PROBE_PATH="$_claws_sock" node --no-deprecation -e "
|
|
142
|
+
const net = require('net');
|
|
143
|
+
const s = net.createConnection(process.env.CLAWS_PROBE_PATH);
|
|
144
|
+
const t = setTimeout(() => { try { s.destroy(); } catch {} process.exit(1); }, 800);
|
|
145
|
+
s.on('connect', () => { s.write('{\"id\":1,\"cmd\":\"list\"}\n'); });
|
|
146
|
+
s.on('data', () => { clearTimeout(t); try { s.destroy(); } catch {} process.exit(0); });
|
|
147
|
+
s.on('error', () => { clearTimeout(t); process.exit(1); });
|
|
148
|
+
" 2>/dev/null; then
|
|
149
|
+
_claws_alive=1
|
|
150
|
+
fi
|
|
151
|
+
fi
|
|
152
|
+
if [ "$_claws_alive" = "1" ]; then
|
|
153
|
+
note "claws.sock is live (extension responding)"
|
|
154
|
+
else
|
|
155
|
+
note "claws.sock probe returned no response (extension may be reloading after VSIX install)"
|
|
156
|
+
note "If 'claws_*' tools fail in your next Claude Code session, run /claws-fix to repair"
|
|
157
|
+
# Do NOT delete the socket here — defer to user-explicit /claws-fix (M-26)
|
|
158
|
+
fi
|
|
159
|
+
unset _claws_sock _claws_alive
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
# ─── Step 5: Re-source the shell hook ──────────────────────────────────────
|
|
163
|
+
# Forces the in-terminal CLAWS banner + aliases to refresh without a new
|
|
164
|
+
# shell. Best-effort — won't fail the update if the hook is missing.
|
|
165
|
+
header "Refreshing shell hook"
|
|
166
|
+
unset CLAWS_BANNER_SHOWN 2>/dev/null || true
|
|
167
|
+
# shellcheck disable=SC1090
|
|
168
|
+
source "$INSTALL_DIR/scripts/shell-hook.sh" 2>/dev/null \
|
|
169
|
+
&& note "shell hook re-sourced" \
|
|
170
|
+
|| note "shell hook not sourced (non-interactive shell)"
|
|
171
|
+
|
|
172
|
+
# ─── Step 6: Post-update health check (v0.7.3) ────────────────────────────
|
|
173
|
+
# Runs the same diagnostic checks /claws-fix would. If any fail, surface
|
|
174
|
+
# them BEFORE the success banner so the user knows recovery steps to take.
|
|
175
|
+
header "Post-update health check"
|
|
176
|
+
_claws_health_ok=1
|
|
177
|
+
_claws_health_warns=()
|
|
178
|
+
|
|
179
|
+
# pty.node ABI parity — the most common silent breakage after an update.
|
|
180
|
+
if [ -f "$INSTALL_DIR/extension/native/.metadata.json" ]; then
|
|
181
|
+
_claws_built_for=$(node -e "try{console.log(require('$INSTALL_DIR/extension/native/.metadata.json').electronVersion||'')}catch(e){}" 2>/dev/null || echo "")
|
|
182
|
+
_claws_using=""
|
|
183
|
+
if [ "$(uname)" = "Darwin" ]; then
|
|
184
|
+
# M-35: prefer the editor that launched this shell ($TERM_PROGRAM) so the
|
|
185
|
+
# user's daily-driver Electron version is checked first, not first-found.
|
|
186
|
+
_claws_u_tp="${TERM_PROGRAM:-}"
|
|
187
|
+
[ "$_claws_u_tp" = "vscode" ] && [ -n "${CURSOR_CHANNEL:-}" ] && _claws_u_tp="cursor"
|
|
188
|
+
_claws_u_tp=$(echo "$_claws_u_tp" | tr '[:upper:]' '[:lower:]')
|
|
189
|
+
case "$_claws_u_tp" in
|
|
190
|
+
cursor) _claws_update_apps=('/Applications/Cursor.app' '/Applications/Visual Studio Code.app' '/Applications/Windsurf.app') ;;
|
|
191
|
+
windsurf) _claws_update_apps=('/Applications/Windsurf.app' '/Applications/Visual Studio Code.app' '/Applications/Cursor.app') ;;
|
|
192
|
+
*) _claws_update_apps=('/Applications/Visual Studio Code.app' '/Applications/Cursor.app' '/Applications/Windsurf.app') ;;
|
|
193
|
+
esac
|
|
194
|
+
for _claws_app in "${_claws_update_apps[@]}"; do
|
|
195
|
+
_claws_plist="$_claws_app/Contents/Frameworks/Electron Framework.framework/Resources/Info.plist"
|
|
196
|
+
[ -f "$_claws_plist" ] && _claws_using=$(plutil -extract CFBundleVersion raw "$_claws_plist" 2>/dev/null || true) && break
|
|
197
|
+
done
|
|
198
|
+
unset _claws_u_tp _claws_update_apps
|
|
199
|
+
elif [ "$(uname)" = "Linux" ]; then
|
|
200
|
+
# F-5: port Linux Electron detection from install.sh so ABI mismatch is caught on Linux too.
|
|
201
|
+
_claws_u_tp="${TERM_PROGRAM:-}"
|
|
202
|
+
[ "$_claws_u_tp" = "vscode" ] && [ -n "${CURSOR_CHANNEL:-}" ] && _claws_u_tp="cursor"
|
|
203
|
+
_claws_u_tp=$(echo "$_claws_u_tp" | tr '[:upper:]' '[:lower:]')
|
|
204
|
+
case "$_claws_u_tp" in
|
|
205
|
+
cursor)
|
|
206
|
+
_claws_linux_eps="/usr/share/cursor/electron /opt/cursor/electron /snap/cursor/current/usr/share/cursor/electron /usr/share/code/electron /usr/lib/code/electron /opt/visual-studio-code/electron /snap/code/current/usr/share/code/electron /usr/share/windsurf/electron /opt/windsurf/electron"
|
|
207
|
+
;;
|
|
208
|
+
windsurf)
|
|
209
|
+
_claws_linux_eps="/usr/share/windsurf/electron /opt/windsurf/electron /usr/share/code/electron /usr/lib/code/electron /opt/visual-studio-code/electron /snap/code/current/usr/share/code/electron /usr/share/cursor/electron /opt/cursor/electron"
|
|
210
|
+
;;
|
|
211
|
+
*)
|
|
212
|
+
_claws_linux_eps="/usr/share/code/electron /usr/lib/code/electron /opt/visual-studio-code/electron /snap/code/current/usr/share/code/electron /usr/share/cursor/electron /opt/cursor/electron /snap/cursor/current/usr/share/cursor/electron /usr/share/windsurf/electron /opt/windsurf/electron"
|
|
213
|
+
;;
|
|
214
|
+
esac
|
|
215
|
+
for _claws_ep in $_claws_linux_eps; do
|
|
216
|
+
if [ -x "$_claws_ep" ]; then
|
|
217
|
+
_claws_using=$("$_claws_ep" --version 2>/dev/null | sed 's/^v//' | head -1)
|
|
218
|
+
[ -n "$_claws_using" ] && break
|
|
219
|
+
fi
|
|
220
|
+
done
|
|
221
|
+
unset _claws_u_tp _claws_linux_eps _claws_ep
|
|
222
|
+
fi
|
|
223
|
+
if [ -n "$_claws_using" ] && [ -n "$_claws_built_for" ] && [ "$_claws_using" != "$_claws_built_for" ]; then
|
|
224
|
+
_claws_health_ok=0
|
|
225
|
+
_claws_health_warns+=("pty.node was built for Electron $_claws_built_for but VS Code is running $_claws_using — wrapped terminals will fall into pipe-mode")
|
|
226
|
+
_claws_health_warns+=(" fix: CLAWS_FORCE_REBUILD_NPTY=1 bash $INSTALL_DIR/scripts/install.sh")
|
|
227
|
+
else
|
|
228
|
+
note "pty.node ABI matches editor Electron ($_claws_using)"
|
|
229
|
+
fi
|
|
230
|
+
unset _claws_built_for _claws_using _claws_app _claws_plist
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
# BUG-28: MCP spawn-class PreToolUse hooks — Monitor arm gate
|
|
234
|
+
if [ -f "$HOME/.claude/settings.json" ]; then
|
|
235
|
+
if CLAWS_SETTINGS_CHECK="$HOME/.claude/settings.json" node --no-deprecation -e "
|
|
236
|
+
const s = JSON.parse(require('fs').readFileSync(process.env.CLAWS_SETTINGS_CHECK, 'utf8'));
|
|
237
|
+
const h = (s.hooks && s.hooks.PreToolUse) || [];
|
|
238
|
+
process.exit(h.some(e => e.matcher && e.matcher.includes('mcp__claws__claws_worker')) ? 0 : 1);
|
|
239
|
+
" 2>/dev/null; then
|
|
240
|
+
note "MCP spawn-class PreToolUse hooks registered (Monitor arm gate active)"
|
|
241
|
+
else
|
|
242
|
+
_claws_health_ok=0
|
|
243
|
+
_claws_health_warns+=("MCP spawn-class PreToolUse hooks missing — Monitor arm gate is inactive")
|
|
244
|
+
_claws_health_warns+=(" fix: node $INSTALL_DIR/scripts/inject-settings-hooks.js $INSTALL_DIR/scripts --update")
|
|
245
|
+
fi
|
|
246
|
+
fi
|
|
247
|
+
|
|
248
|
+
# Wave C: PostToolUse spawn-class hooks — monitor race-close
|
|
249
|
+
if [ -f "$HOME/.claude/settings.json" ]; then
|
|
250
|
+
if CLAWS_SETTINGS_CHECK="$HOME/.claude/settings.json" node --no-deprecation -e "
|
|
251
|
+
const s = JSON.parse(require('fs').readFileSync(process.env.CLAWS_SETTINGS_CHECK, 'utf8'));
|
|
252
|
+
const h = (s.hooks && s.hooks.PostToolUse) || [];
|
|
253
|
+
process.exit(h.some(e => e.matcher && e.matcher.includes('mcp__claws__claws_worker')) ? 0 : 1);
|
|
254
|
+
" 2>/dev/null; then
|
|
255
|
+
note "PostToolUse spawn-class hooks registered (Wave C monitor race-close active)"
|
|
256
|
+
else
|
|
257
|
+
_claws_health_ok=0
|
|
258
|
+
_claws_health_warns+=("PostToolUse spawn-class hooks missing — Wave C monitor race-close is inactive")
|
|
259
|
+
_claws_health_warns+=(" fix: node $INSTALL_DIR/scripts/inject-settings-hooks.js $INSTALL_DIR/scripts --update")
|
|
260
|
+
fi
|
|
261
|
+
fi
|
|
262
|
+
|
|
263
|
+
# Project .mcp.json sanity
|
|
264
|
+
# M-47: path passed via env var — handles project roots with apostrophes/backslashes
|
|
265
|
+
# without causing JS syntax errors from string interpolation in -e argument.
|
|
266
|
+
if [ -f "$PROJECT_ROOT/.mcp.json" ]; then
|
|
267
|
+
if CLAWS_MCP_CHECK="$PROJECT_ROOT/.mcp.json" node -e "JSON.parse(require('fs').readFileSync(process.env.CLAWS_MCP_CHECK,'utf8'))" 2>/dev/null; then
|
|
268
|
+
note "project .mcp.json is valid JSON"
|
|
269
|
+
else
|
|
270
|
+
_claws_health_ok=0
|
|
271
|
+
_claws_health_warns+=("$PROJECT_ROOT/.mcp.json is not valid JSON — MCP server will not load")
|
|
272
|
+
_claws_health_warns+=(" fix: review and repair the file, or rm and re-run install.sh")
|
|
273
|
+
fi
|
|
274
|
+
fi
|
|
275
|
+
|
|
276
|
+
# Fix #1 (v0.7.12): ensure .claws-bin/package.json exists with {"type":"commonjs"}.
|
|
277
|
+
# Auto-restores on every update so existing installs without it get fixed automatically.
|
|
278
|
+
if [ -d "$PROJECT_ROOT/.claws-bin" ] && [ ! -f "$PROJECT_ROOT/.claws-bin/package.json" ]; then
|
|
279
|
+
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' > "$PROJECT_ROOT/.claws-bin/package.json"
|
|
280
|
+
note "wrote .claws-bin/package.json (ESM compat shim)"
|
|
281
|
+
fi
|
|
282
|
+
|
|
283
|
+
# .claws-bin/mcp_server.js exists and starts
|
|
284
|
+
# M-10: up to 3 attempts with exponential timeouts (8s, 12s, 16s); only YELLOW after all exhausted.
|
|
285
|
+
if [ -f "$PROJECT_ROOT/.claws-bin/mcp_server.js" ]; then
|
|
286
|
+
_claws_mcp_ok=0
|
|
287
|
+
_claws_attempt=0
|
|
288
|
+
for _claws_mcp_ms in 8000 12000 16000; do
|
|
289
|
+
[ "$_claws_mcp_ok" = "1" ] && break
|
|
290
|
+
_claws_attempt=$(( _claws_attempt + 1 ))
|
|
291
|
+
[ "$_claws_attempt" -gt 1 ] && note "MCP handshake timeout — retry $_claws_attempt of 3 (${_claws_mcp_ms}ms)..."
|
|
292
|
+
if CLAWS_MCP_PATH="$PROJECT_ROOT/.claws-bin/mcp_server.js" node --no-deprecation -e "
|
|
293
|
+
const { spawn } = require('child_process');
|
|
294
|
+
const p = spawn('node', [process.env.CLAWS_MCP_PATH], { stdio: ['pipe','pipe','ignore'] });
|
|
295
|
+
let out='';
|
|
296
|
+
p.stdout.on('data', d => { out += d; });
|
|
297
|
+
p.stdin.write(JSON.stringify({jsonrpc:'2.0',id:1,method:'initialize',params:{protocolVersion:'2024-11-05',capabilities:{},clientInfo:{name:'health',version:'1'}}}) + '\n');
|
|
298
|
+
setTimeout(() => {
|
|
299
|
+
p.kill('SIGTERM');
|
|
300
|
+
setTimeout(() => { try { p.kill('SIGKILL'); } catch {} }, 500);
|
|
301
|
+
setTimeout(() => { process.exit(out.includes('claws') ? 0 : 1); }, 600);
|
|
302
|
+
}, $_claws_mcp_ms);
|
|
303
|
+
" 2>/dev/null; then
|
|
304
|
+
_claws_mcp_ok=1
|
|
305
|
+
fi
|
|
306
|
+
done
|
|
307
|
+
unset _claws_mcp_ms _claws_attempt
|
|
308
|
+
if [ "$_claws_mcp_ok" = "1" ]; then
|
|
309
|
+
note "MCP server handshake OK"
|
|
310
|
+
else
|
|
311
|
+
_claws_health_ok=0
|
|
312
|
+
_claws_health_warns+=("MCP server failed to respond to initialize — see install log: $CLAWS_LOG")
|
|
313
|
+
_claws_health_warns+=(" fix: bash $INSTALL_DIR/scripts/fix.sh")
|
|
314
|
+
fi
|
|
315
|
+
unset _claws_mcp_ok
|
|
316
|
+
fi
|
|
317
|
+
|
|
318
|
+
# ─── Done ──────────────────────────────────────────────────────────────────
|
|
319
|
+
echo ""
|
|
320
|
+
if [ "$_claws_health_ok" = "1" ]; then
|
|
321
|
+
printf "${C_GREEN}${C_BOLD}Update complete.${C_RESET}\n"
|
|
322
|
+
else
|
|
323
|
+
printf "${C_YELLOW}${C_BOLD}Update completed WITH WARNINGS:${C_RESET}\n"
|
|
324
|
+
for _w in "${_claws_health_warns[@]}"; do
|
|
325
|
+
printf " ${C_YELLOW}!${C_RESET} %s\n" "$_w"
|
|
326
|
+
done
|
|
327
|
+
fi
|
|
328
|
+
echo ""
|
|
329
|
+
printf " ${C_BOLD}Two things to activate:${C_RESET}\n"
|
|
330
|
+
echo " 1. Reload VS Code: Cmd+Shift+P → Developer: Reload Window"
|
|
331
|
+
echo " 2. Restart Claude Code in this project so the new .mcp.json is picked up"
|
|
332
|
+
echo ""
|
|
333
|
+
printf " ${C_BOLD}If anything looks off:${C_RESET}\n"
|
|
334
|
+
echo " /claws-fix — quick auto-diagnosis + auto-repair"
|
|
335
|
+
echo " /claws-report — bundle logs + state into a shareable file"
|
|
336
|
+
echo ""
|
|
337
|
+
unset _claws_health_ok _claws_health_warns _w
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Run from repo root: bash scripts/verify-release.sh
|
|
3
|
+
# Windows users: run via Git Bash or WSL
|
|
4
|
+
|
|
5
|
+
set -uo pipefail
|
|
6
|
+
|
|
7
|
+
# ── State ──────────────────────────────────────────────────────────────────────
|
|
8
|
+
PASS=0
|
|
9
|
+
FAIL=0
|
|
10
|
+
FAILURES=()
|
|
11
|
+
ROOT_VERSION=""
|
|
12
|
+
|
|
13
|
+
# ── Helpers ────────────────────────────────────────────────────────────────────
|
|
14
|
+
pass() {
|
|
15
|
+
printf '✓ %s\n' "$1"
|
|
16
|
+
PASS=$((PASS + 1))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
fail() {
|
|
20
|
+
local name="$1" reason="$2"
|
|
21
|
+
printf '✗ %s: %s\n' "$name" "$reason"
|
|
22
|
+
FAIL=$((FAIL + 1))
|
|
23
|
+
FAILURES+=("✗ ${name}: ${reason}")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# ── 1) Working tree clean (allow .local/) ──────────────────────────────────────
|
|
27
|
+
check_clean_tree() {
|
|
28
|
+
local dirty
|
|
29
|
+
dirty=$(git status --porcelain 2>/dev/null | grep -v '\.local/' || true)
|
|
30
|
+
if [ -z "$dirty" ]; then
|
|
31
|
+
pass "working-tree-clean"
|
|
32
|
+
else
|
|
33
|
+
local sample
|
|
34
|
+
sample=$(printf '%s\n' "$dirty" | head -3 | tr '\n' ' ')
|
|
35
|
+
fail "working-tree-clean" "uncommitted changes outside .local/: ${sample}"
|
|
36
|
+
fi
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# ── 2) On branch v0.8-alpha ────────────────────────────────────────────────────
|
|
40
|
+
check_branch() {
|
|
41
|
+
local branch
|
|
42
|
+
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || printf 'UNKNOWN')
|
|
43
|
+
if [ "$branch" = "v0.8-alpha" ]; then
|
|
44
|
+
pass "on-branch"
|
|
45
|
+
else
|
|
46
|
+
fail "on-branch" "expected v0.8-alpha, got '${branch}'"
|
|
47
|
+
fi
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# ── 3) Branch pushed to origin ─────────────────────────────────────────────────
|
|
51
|
+
check_pushed() {
|
|
52
|
+
if ! git rev-parse --verify "origin/v0.8-alpha" >/dev/null 2>&1; then
|
|
53
|
+
fail "branch-pushed" "origin/v0.8-alpha not found — run: gh auth refresh -s workflow && git push -u origin v0.8-alpha"
|
|
54
|
+
return
|
|
55
|
+
fi
|
|
56
|
+
local count
|
|
57
|
+
count=$(git rev-list origin/v0.8-alpha..HEAD 2>/dev/null | wc -l | tr -d ' \t')
|
|
58
|
+
if [ "${count}" = "0" ]; then
|
|
59
|
+
pass "branch-pushed"
|
|
60
|
+
else
|
|
61
|
+
fail "branch-pushed" "${count} unpushed commit(s) — run: gh auth refresh -s workflow && git push origin v0.8-alpha"
|
|
62
|
+
fi
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# ── 4) root package.json sanity ────────────────────────────────────────────────
|
|
66
|
+
check_root_pkg() {
|
|
67
|
+
if [ ! -f "package.json" ]; then
|
|
68
|
+
fail "root-pkg" "package.json not found"
|
|
69
|
+
return
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
local name version files_count pub_access bin_has_key
|
|
73
|
+
name=$(node -e "process.stdout.write(require('./package.json').name||'')")
|
|
74
|
+
version=$(node -e "process.stdout.write(require('./package.json').version||'')")
|
|
75
|
+
files_count=$(node -e "const p=require('./package.json'); process.stdout.write(String((p.files||[]).length))")
|
|
76
|
+
pub_access=$(node -e "const p=require('./package.json'); process.stdout.write(((p.publishConfig||{}).access)||'')")
|
|
77
|
+
bin_has_key=$(node -e "const p=require('./package.json'); process.stdout.write(Object.prototype.hasOwnProperty.call(p.bin||{},'claws-code')?'yes':'no')")
|
|
78
|
+
|
|
79
|
+
local ok=1
|
|
80
|
+
|
|
81
|
+
if [ "$name" != "claws-code" ]; then
|
|
82
|
+
fail "root-pkg-name" "expected 'claws-code', got '${name}'"
|
|
83
|
+
ok=0
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
if ! printf '%s' "$version" | grep -qE '^0\.8\.[0-9]+$'; then
|
|
87
|
+
fail "root-pkg-version" "expected v0.8.x semver, got '${version}'"
|
|
88
|
+
ok=0
|
|
89
|
+
else
|
|
90
|
+
ROOT_VERSION="$version"
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
if [ "${files_count:-0}" -lt 10 ] 2>/dev/null; then
|
|
94
|
+
fail "root-pkg-files" "expected >=10 entries in 'files', got ${files_count}"
|
|
95
|
+
ok=0
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
if [ "$pub_access" != "public" ]; then
|
|
99
|
+
fail "root-pkg-publishconfig" "publishConfig.access='${pub_access}', expected 'public'"
|
|
100
|
+
ok=0
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
if [ "$bin_has_key" != "yes" ]; then
|
|
104
|
+
fail "root-pkg-bin" "bin['claws-code'] missing from root package.json"
|
|
105
|
+
ok=0
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
if [ "$ok" = "1" ]; then
|
|
109
|
+
pass "root-pkg (name=${name}, version=${version}, files=${files_count})"
|
|
110
|
+
fi
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# ── 5) extension/package.json sanity ───────────────────────────────────────────
|
|
114
|
+
check_ext_pkg() {
|
|
115
|
+
if [ ! -f "extension/package.json" ]; then
|
|
116
|
+
fail "ext-pkg" "extension/package.json not found"
|
|
117
|
+
return
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
local name publisher version
|
|
121
|
+
name=$(node -e "process.stdout.write(require('./extension/package.json').name||'')")
|
|
122
|
+
publisher=$(node -e "process.stdout.write(require('./extension/package.json').publisher||'')")
|
|
123
|
+
version=$(node -e "process.stdout.write(require('./extension/package.json').version||'')")
|
|
124
|
+
|
|
125
|
+
local ok=1
|
|
126
|
+
|
|
127
|
+
if [ "$name" != "claws" ]; then
|
|
128
|
+
fail "ext-pkg-name" "expected 'claws', got '${name}'"
|
|
129
|
+
ok=0
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
if [ "$publisher" != "neunaha" ]; then
|
|
133
|
+
fail "ext-pkg-publisher" "expected 'neunaha', got '${publisher}'"
|
|
134
|
+
ok=0
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
if [ -n "$ROOT_VERSION" ] && [ "$version" != "$ROOT_VERSION" ]; then
|
|
138
|
+
fail "ext-pkg-version" "version '${version}' does not match root '${ROOT_VERSION}'"
|
|
139
|
+
ok=0
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
if [ "$ok" = "1" ]; then
|
|
143
|
+
pass "ext-pkg (name=${name}, publisher=${publisher}, version=${version})"
|
|
144
|
+
fi
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# ── 6) bin/cli.js is executable ────────────────────────────────────────────────
|
|
148
|
+
check_cli_executable() {
|
|
149
|
+
if [ -x "bin/cli.js" ]; then
|
|
150
|
+
pass "bin-cli-executable"
|
|
151
|
+
else
|
|
152
|
+
fail "bin-cli-executable" "bin/cli.js is not executable — run: git update-index --chmod=+x bin/cli.js"
|
|
153
|
+
fi
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
# ── 7) extension/LICENSE exists and non-empty ──────────────────────────────────
|
|
157
|
+
check_ext_license() {
|
|
158
|
+
if [ ! -f "extension/LICENSE" ]; then
|
|
159
|
+
fail "ext-license" "extension/LICENSE not found"
|
|
160
|
+
elif [ ! -s "extension/LICENSE" ]; then
|
|
161
|
+
fail "ext-license" "extension/LICENSE is empty"
|
|
162
|
+
else
|
|
163
|
+
pass "ext-license"
|
|
164
|
+
fi
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# ── 8) extension/dist/extension.js contains AF-AC Phase 1 marker ───────────────
|
|
168
|
+
check_ext_dist_marker() {
|
|
169
|
+
local dist="extension/dist/extension.js"
|
|
170
|
+
if [ ! -f "$dist" ]; then
|
|
171
|
+
fail "ext-dist-marker" "${dist} not found — run: bash scripts/dev-vsix-install.sh"
|
|
172
|
+
return
|
|
173
|
+
fi
|
|
174
|
+
if grep -q "process_exited" "$dist" 2>/dev/null; then
|
|
175
|
+
pass "ext-dist-marker (AF-AC process_exited present)"
|
|
176
|
+
else
|
|
177
|
+
fail "ext-dist-marker" "'process_exited' not found in ${dist} — run: bash scripts/dev-vsix-install.sh"
|
|
178
|
+
fi
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# ── 9) npm pack --dry-run: size < 20 MB and no forbidden entries ───────────────
|
|
182
|
+
check_npm_pack() {
|
|
183
|
+
local out
|
|
184
|
+
out=$(npm pack --dry-run 2>&1) || { fail "npm-pack" "npm pack --dry-run failed"; return; }
|
|
185
|
+
|
|
186
|
+
# Size check
|
|
187
|
+
local size_line size_val size_unit
|
|
188
|
+
size_line=$(printf '%s\n' "$out" | grep -i "package size:" | sed 's/.*package size:[[:space:]]*//' | tail -1)
|
|
189
|
+
if [ -z "$size_line" ]; then
|
|
190
|
+
fail "npm-pack-size" "could not parse 'package size:' from npm pack output"
|
|
191
|
+
else
|
|
192
|
+
size_val=$(printf '%s\n' "$size_line" | awk '{print $1}')
|
|
193
|
+
size_unit=$(printf '%s\n' "$size_line" | awk '{print $2}')
|
|
194
|
+
|
|
195
|
+
local size_ok
|
|
196
|
+
size_ok=$(awk -v val="$size_val" -v unit="$size_unit" 'BEGIN {
|
|
197
|
+
if (unit ~ /^[Gg]/) { print "NO" }
|
|
198
|
+
else if (unit ~ /^[Mm]/) { print (val + 0 >= 20) ? "NO" : "YES" }
|
|
199
|
+
else { print "YES" }
|
|
200
|
+
}')
|
|
201
|
+
|
|
202
|
+
if [ "$size_ok" = "YES" ]; then
|
|
203
|
+
pass "npm-pack-size (${size_val} ${size_unit})"
|
|
204
|
+
else
|
|
205
|
+
fail "npm-pack-size" "${size_val} ${size_unit} >= 20 MB — check .npmignore chain"
|
|
206
|
+
fi
|
|
207
|
+
fi
|
|
208
|
+
|
|
209
|
+
# Forbidden-entries check
|
|
210
|
+
local bad
|
|
211
|
+
bad=$(printf '%s\n' "$out" | sed 's/^npm notice //' | \
|
|
212
|
+
grep -E "(extension/test/|docs/images/|scripts/ae4-)" | head -5 || true)
|
|
213
|
+
if [ -n "$bad" ]; then
|
|
214
|
+
local sample
|
|
215
|
+
sample=$(printf '%s' "$bad" | tr '\n' ' ')
|
|
216
|
+
fail "npm-pack-exclusions" "forbidden paths in tarball: ${sample}"
|
|
217
|
+
else
|
|
218
|
+
pass "npm-pack-exclusions"
|
|
219
|
+
fi
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# ── 10) vsce package round-trip ────────────────────────────────────────────────
|
|
223
|
+
check_vsce_package() {
|
|
224
|
+
local vsix_out="/tmp/claws-verify-release.vsix"
|
|
225
|
+
rm -f "$vsix_out"
|
|
226
|
+
|
|
227
|
+
local vsce_out vsce_ok=0
|
|
228
|
+
if vsce_out=$(cd extension && npx @vscode/vsce package --no-yarn --out "$vsix_out" 2>&1); then
|
|
229
|
+
vsce_ok=1
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
if [ "$vsce_ok" = "0" ]; then
|
|
233
|
+
local tail3
|
|
234
|
+
tail3=$(printf '%s\n' "$vsce_out" | tail -3 | tr '\n' ' ')
|
|
235
|
+
fail "vsce-package" "vsce package failed — ${tail3}"
|
|
236
|
+
return
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
if [ ! -f "$vsix_out" ]; then
|
|
240
|
+
fail "vsce-package" "VSIX not created at ${vsix_out}"
|
|
241
|
+
return
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
# vsce renames LICENSE → LICENSE.txt automatically; accept either form.
|
|
245
|
+
# Use a temp file to dodge any pipefail / file-not-found quirks across shells.
|
|
246
|
+
local unzip_listing
|
|
247
|
+
unzip_listing=$(unzip -l "$vsix_out" 2>/dev/null || true)
|
|
248
|
+
if printf '%s\n' "$unzip_listing" | grep -qE 'extension/LICENSE(\.txt)?($|[[:space:]])'; then
|
|
249
|
+
pass "vsce-package (VSIX created, contains extension/LICENSE)"
|
|
250
|
+
else
|
|
251
|
+
fail "vsce-package" "VSIX at ${vsix_out} does not contain extension/LICENSE (or .txt variant)"
|
|
252
|
+
fi
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
# ── 11) CHANGELOG.md has version section ───────────────────────────────────────
|
|
256
|
+
check_changelog() {
|
|
257
|
+
if [ ! -f "CHANGELOG.md" ]; then
|
|
258
|
+
fail "changelog" "CHANGELOG.md not found"
|
|
259
|
+
return
|
|
260
|
+
fi
|
|
261
|
+
if [ -z "$ROOT_VERSION" ]; then
|
|
262
|
+
fail "changelog" "skipped — ROOT_VERSION unknown (root package.json check failed)"
|
|
263
|
+
return
|
|
264
|
+
fi
|
|
265
|
+
if grep -q "## \[${ROOT_VERSION}\]" CHANGELOG.md 2>/dev/null; then
|
|
266
|
+
pass "changelog (## [${ROOT_VERSION}] present)"
|
|
267
|
+
else
|
|
268
|
+
fail "changelog" "no '## [${ROOT_VERSION}]' section in CHANGELOG.md"
|
|
269
|
+
fi
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
# ── 12) git tag v<version>-alpha does NOT yet exist ────────────────────────────
|
|
273
|
+
check_tag_pending() {
|
|
274
|
+
if [ -z "$ROOT_VERSION" ]; then
|
|
275
|
+
fail "tag-pending" "skipped — ROOT_VERSION unknown (root package.json check failed)"
|
|
276
|
+
return
|
|
277
|
+
fi
|
|
278
|
+
local tag="v${ROOT_VERSION}-alpha"
|
|
279
|
+
if git tag --list "$tag" 2>/dev/null | grep -q "^${tag}$"; then
|
|
280
|
+
# Path B: tag is created locally BEFORE the publish gate runs.
|
|
281
|
+
# Verify it points to current HEAD; warn if it's drifted.
|
|
282
|
+
local tag_commit
|
|
283
|
+
tag_commit=$(git rev-parse "${tag}^{commit}" 2>/dev/null)
|
|
284
|
+
local head_commit
|
|
285
|
+
head_commit=$(git rev-parse HEAD 2>/dev/null)
|
|
286
|
+
if [ "$tag_commit" = "$head_commit" ]; then
|
|
287
|
+
pass "tag (${tag} created locally, points to HEAD)"
|
|
288
|
+
else
|
|
289
|
+
fail "tag" "${tag} exists but points to ${tag_commit:0:7}, not HEAD ${head_commit:0:7}"
|
|
290
|
+
fi
|
|
291
|
+
else
|
|
292
|
+
pass "tag-pending (${tag} not yet created — will be tagged later)"
|
|
293
|
+
fi
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
# ── Run all checks ─────────────────────────────────────────────────────────────
|
|
297
|
+
printf '=== Claws v0.8 pre-publish gate ===\n\n'
|
|
298
|
+
|
|
299
|
+
check_clean_tree
|
|
300
|
+
check_branch
|
|
301
|
+
check_pushed
|
|
302
|
+
check_root_pkg
|
|
303
|
+
check_ext_pkg
|
|
304
|
+
check_cli_executable
|
|
305
|
+
check_ext_license
|
|
306
|
+
check_ext_dist_marker
|
|
307
|
+
check_npm_pack
|
|
308
|
+
check_vsce_package
|
|
309
|
+
check_changelog
|
|
310
|
+
check_tag_pending
|
|
311
|
+
|
|
312
|
+
# ── Summary ────────────────────────────────────────────────────────────────────
|
|
313
|
+
printf '\n----- summary -----\n'
|
|
314
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
315
|
+
for f in "${FAILURES[@]}"; do
|
|
316
|
+
printf '%s\n' "$f"
|
|
317
|
+
done
|
|
318
|
+
printf '\nNOT READY (%d blocker(s) above)\n' "$FAIL"
|
|
319
|
+
exit 1
|
|
320
|
+
else
|
|
321
|
+
printf 'READY TO PUBLISH\n'
|
|
322
|
+
exit 0
|
|
323
|
+
fi
|