browse
Enables fast web browsing and interaction with web pages via a headless Chromium daemon for efficient data extraction and testing.
Install this skill
Security score
The browse skill was audited on May 10, 2026 and we found 98 security issues across 4 threat categories, including 1 high-severity. Review the findings below before installing.
Categories Tested
Security Issues
Command substitution pattern
| 26 | |
| 27 | ```bash |
| 28 | _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/skills/gstack/bin/gstack-update-check 2>/dev/null || true) |
| 29 | [ -n "$_UPD" ] && echo "$_UPD" || true |
| 30 | mkdir -p ~/.gstack/sessions |
Command substitution pattern
| 30 | mkdir -p ~/.gstack/sessions |
| 31 | touch ~/.gstack/sessions/"$PPID" |
| 32 | _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') |
| 33 | find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true |
| 34 | _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") |
Command substitution pattern
| 32 | _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') |
| 33 | find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true |
| 34 | _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") |
| 35 | _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") |
| 36 | _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") |
Command substitution pattern
| 33 | find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true |
| 34 | _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") |
| 35 | _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") |
| 36 | _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") |
| 37 | echo "BRANCH: $_BRANCH" |
Command substitution pattern
| 34 | _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") |
| 35 | _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") |
| 36 | _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") |
| 37 | echo "BRANCH: $_BRANCH" |
| 38 | _SKILL_PREFIX=$(~/.claude/skills/gstack/bin/gstack-config get skill_prefix 2>/dev/null || echo "false") |
Command substitution pattern
| 36 | _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") |
| 37 | echo "BRANCH: $_BRANCH" |
| 38 | _SKILL_PREFIX=$(~/.claude/skills/gstack/bin/gstack-config get skill_prefix 2>/dev/null || echo "false") |
| 39 | echo "PROACTIVE: $_PROACTIVE" |
| 40 | echo "PROACTIVE_PROMPTED: $_PROACTIVE_PROMPTED" |
Command substitution pattern
| 43 | REPO_MODE=${REPO_MODE:-unknown} |
| 44 | echo "REPO_MODE: $REPO_MODE" |
| 45 | _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") |
| 46 | echo "LAKE_INTRO: $_LAKE_SEEN" |
| 47 | _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) |
Command substitution pattern
| 45 | _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") |
| 46 | echo "LAKE_INTRO: $_LAKE_SEEN" |
| 47 | _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) |
| 48 | _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") |
| 49 | _TEL_START=$(date +%s) |
Command substitution pattern
| 46 | echo "LAKE_INTRO: $_LAKE_SEEN" |
| 47 | _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) |
| 48 | _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") |
| 49 | _TEL_START=$(date +%s) |
| 50 | _SESSION_ID="$$-$(date +%s)" |
Command substitution pattern
| 51 | echo "TELEMETRY: ${_TEL:-off}" |
| 52 | echo "TEL_PROMPTED: $_TEL_PROMPTED" |
| 53 | _EXPLAIN_LEVEL=$(~/.claude/skills/gstack/bin/gstack-config get explain_level 2>/dev/null || echo "default") |
| 54 | if [ "$_EXPLAIN_LEVEL" != "default" ] && [ "$_EXPLAIN_LEVEL" != "terse" ]; then _EXPLAIN_LEVEL="default"; fi |
| 55 | echo "EXPLAIN_LEVEL: $_EXPLAIN_LEVEL" |
Command substitution pattern
| 54 | if [ "$_EXPLAIN_LEVEL" != "default" ] && [ "$_EXPLAIN_LEVEL" != "terse" ]; then _EXPLAIN_LEVEL="default"; fi |
| 55 | echo "EXPLAIN_LEVEL: $_EXPLAIN_LEVEL" |
| 56 | _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning 2>/dev/null || echo "false") |
| 57 | echo "QUESTION_TUNING: $_QUESTION_TUNING" |
| 58 | mkdir -p ~/.gstack/analytics |
Command substitution pattern
| 58 | mkdir -p ~/.gstack/analytics |
| 59 | if [ "$_TEL" != "off" ]; then |
| 60 | echo '{"skill":"browse","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true |
| 61 | fi |
| 62 | for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do |
Command substitution pattern
| 60 | echo '{"skill":"browse","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true |
| 61 | fi |
| 62 | for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do |
| 63 | if [ -f "$_PF" ]; then |
| 64 | if [ "$_TEL" != "off" ] && [ -x "~/.claude/skills/gstack/bin/gstack-telemetry-log" ]; then |
Command substitution pattern
| 69 | break |
| 70 | done |
| 71 | eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true |
| 72 | _LEARN_FILE="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}/learnings.jsonl" |
| 73 | if [ -f "$_LEARN_FILE" ]; then |
Command substitution pattern
| 72 | _LEARN_FILE="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}/learnings.jsonl" |
| 73 | if [ -f "$_LEARN_FILE" ]; then |
| 74 | _LEARN_COUNT=$(wc -l < "$_LEARN_FILE" 2>/dev/null | tr -d ' ') |
| 75 | echo "LEARNINGS: $_LEARN_COUNT entries loaded" |
| 76 | if [ "$_LEARN_COUNT" -gt 5 ] 2>/dev/null; then |
Command substitution pattern
| 85 | _HAS_ROUTING="yes" |
| 86 | fi |
| 87 | _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") |
| 88 | echo "HAS_ROUTING: $_HAS_ROUTING" |
| 89 | echo "ROUTING_DECLINED: $_ROUTING_DECLINED" |
Command substitution pattern
| 96 | echo "VENDORED_GSTACK: $_VENDORED" |
| 97 | echo "MODEL_OVERLAY: claude" |
| 98 | _CHECKPOINT_MODE=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_mode 2>/dev/null || echo "explicit") |
| 99 | _CHECKPOINT_PUSH=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_push 2>/dev/null || echo "false") |
| 100 | echo "CHECKPOINT_MODE: $_CHECKPOINT_MODE" |
Command substitution pattern
| 97 | echo "MODEL_OVERLAY: claude" |
| 98 | _CHECKPOINT_MODE=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_mode 2>/dev/null || echo "explicit") |
| 99 | _CHECKPOINT_PUSH=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_push 2>/dev/null || echo "false") |
| 100 | echo "CHECKPOINT_MODE: $_CHECKPOINT_MODE" |
| 101 | echo "CHECKPOINT_PUSH: $_CHECKPOINT_PUSH" |
Command substitution pattern
| 259 | Always run (regardless of choice): |
| 260 | ```bash |
| 261 | eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true |
| 262 | touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} |
| 263 | ``` |
Command substitution pattern
| 294 | _GBRAIN_CONFIG="$HOME/.gbrain/config.json" |
| 295 | if [ -f "$_GBRAIN_CONFIG" ] && command -v gbrain >/dev/null 2>&1; then |
| 296 | _GBRAIN_VERSION_OK=$(gbrain --version 2>/dev/null | grep -c '^gbrain ' || echo 0) |
| 297 | if [ "$_GBRAIN_VERSION_OK" -gt 0 ] 2>/dev/null; then |
| 298 | _GBRAIN_PIN_PATH="" |
Command substitution pattern
| 297 | if [ "$_GBRAIN_VERSION_OK" -gt 0 ] 2>/dev/null; then |
| 298 | _GBRAIN_PIN_PATH="" |
| 299 | _REPO_TOP=$(git rev-parse --show-toplevel 2>/dev/null || echo "") |
| 300 | if [ -n "$_REPO_TOP" ] && [ -f "$_REPO_TOP/.gbrain-source" ]; then |
| 301 | _GBRAIN_PIN_PATH="$_REPO_TOP/.gbrain-source" |
Command substitution pattern
| 314 | fi |
| 315 | |
| 316 | _BRAIN_SYNC_MODE=$("$_BRAIN_CONFIG_BIN" get artifacts_sync_mode 2>/dev/null || echo off) |
| 317 | |
| 318 | # Detect remote-MCP mode (Path 4 of /setup-gbrain). Local artifacts sync is |
Command substitution pattern
| 322 | _GBRAIN_MCP_MODE="none" |
| 323 | if command -v jq >/dev/null 2>&1 && [ -f "$HOME/.claude.json" ]; then |
| 324 | _GBRAIN_MCP_TYPE=$(jq -r '.mcpServers.gbrain.type // .mcpServers.gbrain.transport // empty' "$HOME/.claude.json" 2>/dev/null) |
| 325 | case "$_GBRAIN_MCP_TYPE" in |
| 326 | url|http|sse) _GBRAIN_MCP_MODE="remote-http" ;; |
Command substitution pattern
| 330 | |
| 331 | if [ -f "$_BRAIN_REMOTE_FILE" ] && [ ! -d "$_GSTACK_HOME/.git" ] && [ "$_BRAIN_SYNC_MODE" = "off" ]; then |
| 332 | _BRAIN_NEW_URL=$(head -1 "$_BRAIN_REMOTE_FILE" 2>/dev/null | tr -d '[:space:]') |
| 333 | if [ -n "$_BRAIN_NEW_URL" ]; then |
| 334 | echo "ARTIFACTS_SYNC: artifacts repo detected: $_BRAIN_NEW_URL" |
Command substitution pattern
| 342 | _BRAIN_DO_PULL=1 |
| 343 | if [ -f "$_BRAIN_LAST_PULL_FILE" ]; then |
| 344 | _BRAIN_LAST=$(cat "$_BRAIN_LAST_PULL_FILE" 2>/dev/null || echo 0) |
| 345 | _BRAIN_AGE=$(( _BRAIN_NOW - _BRAIN_LAST )) |
| 346 | [ "$_BRAIN_AGE" -lt 86400 ] && _BRAIN_DO_PULL=0 |
Command substitution pattern
| 343 | if [ -f "$_BRAIN_LAST_PULL_FILE" ]; then |
| 344 | _BRAIN_LAST=$(cat "$_BRAIN_LAST_PULL_FILE" 2>/dev/null || echo 0) |
| 345 | _BRAIN_AGE=$(( _BRAIN_NOW - _BRAIN_LAST )) |
| 346 | [ "$_BRAIN_AGE" -lt 86400 ] && _BRAIN_DO_PULL=0 |
| 347 | fi |
Command substitution pattern
| 347 | fi |
| 348 | if [ "$_BRAIN_DO_PULL" = "1" ]; then |
| 349 | ( cd "$_GSTACK_HOME" && git fetch origin >/dev/null 2>&1 && git merge --ff-only "origin/$(git rev-parse --abbrev-ref HEAD)" >/dev/null 2>&1 ) || true |
| 350 | echo "$_BRAIN_NOW" > "$_BRAIN_LAST_PULL_FILE" |
| 351 | fi |
Command substitution pattern
| 356 | # Remote-MCP mode: local artifacts sync is a no-op (brain admin's server |
| 357 | # pulls from GitHub/GitLab). Show the user this is by design, not broken. |
| 358 | _GBRAIN_HOST=$(jq -r '.mcpServers.gbrain.url // empty' "$HOME/.claude.json" 2>/dev/null | sed -E 's|^https?://([^/:]+).*|\1|') |
| 359 | echo "ARTIFACTS_SYNC: remote-mode (managed by brain server ${_GBRAIN_HOST:-remote})" |
| 360 | elif [ -d "$_GSTACK_HOME/.git" ] && [ "$_BRAIN_SYNC_MODE" != "off" ]; then |
Command substitution pattern
| 360 | elif [ -d "$_GSTACK_HOME/.git" ] && [ "$_BRAIN_SYNC_MODE" != "off" ]; then |
| 361 | _BRAIN_QUEUE_DEPTH=0 |
| 362 | [ -f "$_GSTACK_HOME/.brain-queue.jsonl" ] && _BRAIN_QUEUE_DEPTH=$(wc -l < "$_GSTACK_HOME/.brain-queue.jsonl" | tr -d ' ') |
| 363 | _BRAIN_LAST_PUSH="never" |
| 364 | [ -f "$_GSTACK_HOME/.brain-last-push" ] && _BRAIN_LAST_PUSH=$(cat "$_GSTACK_HOME/.brain-last-push" 2>/dev/null || echo never) |
Command substitution pattern
| 362 | [ -f "$_GSTACK_HOME/.brain-queue.jsonl" ] && _BRAIN_QUEUE_DEPTH=$(wc -l < "$_GSTACK_HOME/.brain-queue.jsonl" | tr -d ' ') |
| 363 | _BRAIN_LAST_PUSH="never" |
| 364 | [ -f "$_GSTACK_HOME/.brain-last-push" ] && _BRAIN_LAST_PUSH=$(cat "$_GSTACK_HOME/.brain-last-push" 2>/dev/null || echo never) |
| 365 | echo "ARTIFACTS_SYNC: mode=$_BRAIN_SYNC_MODE | last_push=$_BRAIN_LAST_PUSH | queue=$_BRAIN_QUEUE_DEPTH" |
| 366 | else |
Command substitution pattern
| 455 | ```bash |
| 456 | _TEL_END=$(date +%s) |
| 457 | _TEL_DUR=$(( _TEL_END - _TEL_START )) |
| 458 | rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true |
| 459 | # Session timeline: record skill completion (local-only, never sent anywhere) |
Command substitution pattern
| 458 | rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true |
| 459 | # Session timeline: record skill completion (local-only, never sent anywhere) |
| 460 | ~/.claude/skills/gstack/bin/gstack-timeline-log '{"skill":"SKILL_NAME","event":"completed","branch":"'$(git branch --show-current 2>/dev/null || echo unknown)'","outcome":"OUTCOME","duration_s":"'"$_TEL_DUR"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null || true |
| 461 | # Local analytics (gated on telemetry setting) |
| 462 | if [ "$_TEL" != "off" ]; then |
Command substitution pattern
| 461 | # Local analytics (gated on telemetry setting) |
| 462 | if [ "$_TEL" != "off" ]; then |
| 463 | echo '{"skill":"SKILL_NAME","duration_s":"'"$_TEL_DUR"'","outcome":"OUTCOME","browse":"USED_BROWSE","session":"'"$_SESSION_ID"'","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true |
| 464 | fi |
| 465 | # Remote telemetry (opt-in, requires binary) |
Command substitution pattern
| 487 | |
| 488 | ```bash |
| 489 | _ROOT=$(git rev-parse --show-toplevel 2>/dev/null) |
| 490 | B="" |
| 491 | [ -n "$_ROOT" ] && [ -x "$_ROOT/.claude/skills/gstack/browse/dist/browse" ] && B="$_ROOT/.claude/skills/gstack/browse/dist/browse" |
Command substitution pattern
| 508 | tmpfile=$(mktemp) |
| 509 | curl -fsSL "https://bun.sh/install" -o "$tmpfile" |
| 510 | actual_sha=$(shasum -a 256 "$tmpfile" | awk '{print $1}') |
| 511 | if [ "$actual_sha" != "$BUN_INSTALL_SHA" ]; then |
| 512 | echo "ERROR: bun install script checksum mismatch" >&2 |
Curl to non-GitHub URL
| 507 | BUN_INSTALL_SHA="bab8acfb046aac8c72407bdcce903957665d655d7acaa3e11c7c4616beae68dd" |
| 508 | tmpfile=$(mktemp) |
| 509 | curl -fsSL "https://bun.sh/install" -o "$tmpfile" |
| 510 | actual_sha=$(shasum -a 256 "$tmpfile" | awk '{print $1}') |
| 511 | if [ "$actual_sha" != "$BUN_INSTALL_SHA" ]; then |
Access to home directory dotfiles
| 26 | |
| 27 | ```bash |
| 28 | _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/skills/gstack/bin/gstack-update-check 2>/dev/null || true) |
| 29 | [ -n "$_UPD" ] && echo "$_UPD" || true |
| 30 | mkdir -p ~/.gstack/sessions |
Access to home directory dotfiles
| 28 | _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/skills/gstack/bin/gstack-update-check 2>/dev/null || true) |
| 29 | [ -n "$_UPD" ] && echo "$_UPD" || true |
| 30 | mkdir -p ~/.gstack/sessions |
| 31 | touch ~/.gstack/sessions/"$PPID" |
| 32 | _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') |
Access to home directory dotfiles
| 29 | [ -n "$_UPD" ] && echo "$_UPD" || true |
| 30 | mkdir -p ~/.gstack/sessions |
| 31 | touch ~/.gstack/sessions/"$PPID" |
| 32 | _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') |
| 33 | find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true |
Access to home directory dotfiles
| 30 | mkdir -p ~/.gstack/sessions |
| 31 | touch ~/.gstack/sessions/"$PPID" |
| 32 | _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') |
| 33 | find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true |
| 34 | _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") |
Access to home directory dotfiles
| 31 | touch ~/.gstack/sessions/"$PPID" |
| 32 | _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') |
| 33 | find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true |
| 34 | _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") |
| 35 | _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") |
Access to home directory dotfiles
| 32 | _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') |
| 33 | find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true |
| 34 | _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") |
| 35 | _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") |
| 36 | _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") |
Access to home directory dotfiles
| 33 | find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true |
| 34 | _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") |
| 35 | _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") |
| 36 | _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") |
| 37 | echo "BRANCH: $_BRANCH" |
Access to home directory dotfiles
| 36 | _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") |
| 37 | echo "BRANCH: $_BRANCH" |
| 38 | _SKILL_PREFIX=$(~/.claude/skills/gstack/bin/gstack-config get skill_prefix 2>/dev/null || echo "false") |
| 39 | echo "PROACTIVE: $_PROACTIVE" |
| 40 | echo "PROACTIVE_PROMPTED: $_PROACTIVE_PROMPTED" |
Access to home directory dotfiles
| 40 | echo "PROACTIVE_PROMPTED: $_PROACTIVE_PROMPTED" |
| 41 | echo "SKILL_PREFIX: $_SKILL_PREFIX" |
| 42 | source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true |
| 43 | REPO_MODE=${REPO_MODE:-unknown} |
| 44 | echo "REPO_MODE: $REPO_MODE" |
Access to home directory dotfiles
| 43 | REPO_MODE=${REPO_MODE:-unknown} |
| 44 | echo "REPO_MODE: $REPO_MODE" |
| 45 | _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") |
| 46 | echo "LAKE_INTRO: $_LAKE_SEEN" |
| 47 | _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) |
Access to home directory dotfiles
| 45 | _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") |
| 46 | echo "LAKE_INTRO: $_LAKE_SEEN" |
| 47 | _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) |
| 48 | _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") |
| 49 | _TEL_START=$(date +%s) |
Access to home directory dotfiles
| 46 | echo "LAKE_INTRO: $_LAKE_SEEN" |
| 47 | _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) |
| 48 | _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") |
| 49 | _TEL_START=$(date +%s) |
| 50 | _SESSION_ID="$$-$(date +%s)" |
Access to home directory dotfiles
| 51 | echo "TELEMETRY: ${_TEL:-off}" |
| 52 | echo "TEL_PROMPTED: $_TEL_PROMPTED" |
| 53 | _EXPLAIN_LEVEL=$(~/.claude/skills/gstack/bin/gstack-config get explain_level 2>/dev/null || echo "default") |
| 54 | if [ "$_EXPLAIN_LEVEL" != "default" ] && [ "$_EXPLAIN_LEVEL" != "terse" ]; then _EXPLAIN_LEVEL="default"; fi |
| 55 | echo "EXPLAIN_LEVEL: $_EXPLAIN_LEVEL" |
Access to home directory dotfiles
| 54 | if [ "$_EXPLAIN_LEVEL" != "default" ] && [ "$_EXPLAIN_LEVEL" != "terse" ]; then _EXPLAIN_LEVEL="default"; fi |
| 55 | echo "EXPLAIN_LEVEL: $_EXPLAIN_LEVEL" |
| 56 | _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning 2>/dev/null || echo "false") |
| 57 | echo "QUESTION_TUNING: $_QUESTION_TUNING" |
| 58 | mkdir -p ~/.gstack/analytics |
Access to home directory dotfiles
| 56 | _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning 2>/dev/null || echo "false") |
| 57 | echo "QUESTION_TUNING: $_QUESTION_TUNING" |
| 58 | mkdir -p ~/.gstack/analytics |
| 59 | if [ "$_TEL" != "off" ]; then |
| 60 | echo '{"skill":"browse","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true |
Access to home directory dotfiles
| 58 | mkdir -p ~/.gstack/analytics |
| 59 | if [ "$_TEL" != "off" ]; then |
| 60 | echo '{"skill":"browse","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true |
| 61 | fi |
| 62 | for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do |
Access to home directory dotfiles
| 60 | echo '{"skill":"browse","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true |
| 61 | fi |
| 62 | for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do |
| 63 | if [ -f "$_PF" ]; then |
| 64 | if [ "$_TEL" != "off" ] && [ -x "~/.claude/skills/gstack/bin/gstack-telemetry-log" ]; then |
Access to home directory dotfiles
| 62 | for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do |
| 63 | if [ -f "$_PF" ]; then |
| 64 | if [ "$_TEL" != "off" ] && [ -x "~/.claude/skills/gstack/bin/gstack-telemetry-log" ]; then |
| 65 | ~/.claude/skills/gstack/bin/gstack-telemetry-log --event-type skill_run --skill _pending_finalize --outcome unknown --session-id "$_SESSION_ID" 2>/dev/null || true |
| 66 | fi |
Access to home directory dotfiles
| 63 | if [ -f "$_PF" ]; then |
| 64 | if [ "$_TEL" != "off" ] && [ -x "~/.claude/skills/gstack/bin/gstack-telemetry-log" ]; then |
| 65 | ~/.claude/skills/gstack/bin/gstack-telemetry-log --event-type skill_run --skill _pending_finalize --outcome unknown --session-id "$_SESSION_ID" 2>/dev/null || true |
| 66 | fi |
| 67 | rm -f "$_PF" 2>/dev/null || true |
Access to home directory dotfiles
| 69 | break |
| 70 | done |
| 71 | eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true |
| 72 | _LEARN_FILE="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}/learnings.jsonl" |
| 73 | if [ -f "$_LEARN_FILE" ]; then |
Access to home directory dotfiles
| 75 | echo "LEARNINGS: $_LEARN_COUNT entries loaded" |
| 76 | if [ "$_LEARN_COUNT" -gt 5 ] 2>/dev/null; then |
| 77 | ~/.claude/skills/gstack/bin/gstack-learnings-search --limit 3 2>/dev/null || true |
| 78 | fi |
| 79 | else |
Access to home directory dotfiles
| 80 | echo "LEARNINGS: 0" |
| 81 | fi |
| 82 | ~/.claude/skills/gstack/bin/gstack-timeline-log '{"skill":"browse","event":"started","branch":"'"$_BRANCH"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null & |
| 83 | _HAS_ROUTING="no" |
| 84 | if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then |
Access to home directory dotfiles
| 85 | _HAS_ROUTING="yes" |
| 86 | fi |
| 87 | _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") |
| 88 | echo "HAS_ROUTING: $_HAS_ROUTING" |
| 89 | echo "ROUTING_DECLINED: $_ROUTING_DECLINED" |
Access to home directory dotfiles
| 96 | echo "VENDORED_GSTACK: $_VENDORED" |
| 97 | echo "MODEL_OVERLAY: claude" |
| 98 | _CHECKPOINT_MODE=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_mode 2>/dev/null || echo "explicit") |
| 99 | _CHECKPOINT_PUSH=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_push 2>/dev/null || echo "false") |
| 100 | echo "CHECKPOINT_MODE: $_CHECKPOINT_MODE" |
Access to home directory dotfiles
| 97 | echo "MODEL_OVERLAY: claude" |
| 98 | _CHECKPOINT_MODE=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_mode 2>/dev/null || echo "explicit") |
| 99 | _CHECKPOINT_PUSH=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_push 2>/dev/null || echo "false") |
| 100 | echo "CHECKPOINT_MODE: $_CHECKPOINT_MODE" |
| 101 | echo "CHECKPOINT_PUSH: $_CHECKPOINT_PUSH" |
Access to home directory dotfiles
| 105 | ## Plan Mode Safe Operations |
| 106 | |
| 107 | In plan mode, allowed because they inform the plan: `$B`, `$D`, `codex exec`/`codex review`, writes to `~/.gstack/`, writes to the plan file, and `open` for generated artifacts. |
| 108 | |
| 109 | ## Skill Invocation During Plan Mode |
Access to home directory dotfiles
| 113 | If `PROACTIVE` is `"false"`, do not auto-invoke or proactively suggest skills. If a skill seems useful, ask: "I think /skillname might help here — want me to run it?" |
| 114 | |
| 115 | If `SKILL_PREFIX` is `"true"`, suggest/invoke `/gstack-*` names. Disk paths stay `~/.claude/skills/gstack/[skill-name]/SKILL.md`. |
| 116 | |
| 117 | If output shows `UPGRADE_AVAILABLE <old> <new>`: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). |
Access to home directory dotfiles
| 115 | If `SKILL_PREFIX` is `"true"`, suggest/invoke `/gstack-*` names. Disk paths stay `~/.claude/skills/gstack/[skill-name]/SKILL.md`. |
| 116 | |
| 117 | If output shows `UPGRADE_AVAILABLE <old> <new>`: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). |
| 118 | |
| 119 | If output shows `JUST_UPGRADED <from> <to>`: print "Running gstack v{to} (just updated!)". If `SPAWNED_SESSION` is true, skip feature discovery. |
Access to home directory dotfiles
| 120 | |
| 121 | Feature discovery, max one prompt per session: |
| 122 | - Missing `~/.claude/skills/gstack/.feature-prompted-continuous-checkpoint`: AskUserQuestion for Continuous checkpoint auto-commits. If accepted, run `~/.claude/skills/gstack/bin/gstack-config set checkpoint_mode continuous`. Always touch marker. |
| 123 | - Missing `~/.claude/skills/gstack/.feature-prompted-model-overlay`: inform "Model overlays are active. MODEL_OVERLAY shows the patch." Always touch marker. |
| 124 |
Access to home directory dotfiles
| 121 | Feature discovery, max one prompt per session: |
| 122 | - Missing `~/.claude/skills/gstack/.feature-prompted-continuous-checkpoint`: AskUserQuestion for Continuous checkpoint auto-commits. If accepted, run `~/.claude/skills/gstack/bin/gstack-config set checkpoint_mode continuous`. Always touch marker. |
| 123 | - Missing `~/.claude/skills/gstack/.feature-prompted-model-overlay`: inform "Model overlays are active. MODEL_OVERLAY shows the patch." Always touch marker. |
| 124 | |
| 125 | After upgrade prompts, continue workflow. |
Access to home directory dotfiles
| 134 | |
| 135 | If A: leave `explain_level` unset (defaults to `default`). |
| 136 | If B: run `~/.claude/skills/gstack/bin/gstack-config set explain_level terse`. |
| 137 | |
| 138 | Always run (regardless of choice): |
Access to home directory dotfiles
| 138 | Always run (regardless of choice): |
| 139 | ```bash |
| 140 | rm -f ~/.gstack/.writing-style-prompt-pending |
| 141 | touch ~/.gstack/.writing-style-prompted |
| 142 | ``` |
Access to home directory dotfiles
| 139 | ```bash |
| 140 | rm -f ~/.gstack/.writing-style-prompt-pending |
| 141 | touch ~/.gstack/.writing-style-prompted |
| 142 | ``` |
| 143 |
Access to home directory dotfiles
| 148 | ```bash |
| 149 | open https://garryslist.org/posts/boil-the-ocean |
| 150 | touch ~/.gstack/.completeness-intro-seen |
| 151 | ``` |
| 152 |
Access to home directory dotfiles
| 161 | - B) No thanks |
| 162 | |
| 163 | If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` |
| 164 | |
| 165 | If B: ask follow-up: |
Access to home directory dotfiles
| 171 | - B) No thanks, fully off |
| 172 | |
| 173 | If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous` |
| 174 | If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off` |
| 175 |
Access to home directory dotfiles
| 172 | |
| 173 | If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous` |
| 174 | If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off` |
| 175 | |
| 176 | Always run: |
Access to home directory dotfiles
| 176 | Always run: |
| 177 | ```bash |
| 178 | touch ~/.gstack/.telemetry-prompted |
| 179 | ``` |
| 180 |
Access to home directory dotfiles
| 189 | - B) Turn it off — I'll type /commands myself |
| 190 | |
| 191 | If A: run `~/.claude/skills/gstack/bin/gstack-config set proactive true` |
| 192 | If B: run `~/.claude/skills/gstack/bin/gstack-config set proactive false` |
| 193 |
Access to home directory dotfiles
| 190 | |
| 191 | If A: run `~/.claude/skills/gstack/bin/gstack-config set proactive true` |
| 192 | If B: run `~/.claude/skills/gstack/bin/gstack-config set proactive false` |
| 193 | |
| 194 | Always run: |
Access to home directory dotfiles
| 194 | Always run: |
| 195 | ```bash |
| 196 | touch ~/.gstack/.proactive-prompted |
| 197 | ``` |
| 198 |
Access to home directory dotfiles
| 235 | Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"` |
| 236 | |
| 237 | If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true` and say they can re-enable with `gstack-config set routing_declined false`. |
| 238 | |
| 239 | This only happens once per project. Skip if `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`. |
Access to home directory dotfiles
| 239 | This only happens once per project. Skip if `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`. |
| 240 | |
| 241 | If `VENDORED_GSTACK` is `yes`, warn once via AskUserQuestion unless `~/.gstack/.vendoring-warned-$SLUG` exists: |
| 242 | |
| 243 | > This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. |
Access to home directory dotfiles
| 251 | 1. Run `git rm -r .claude/skills/gstack/` |
| 252 | 2. Run `echo '.claude/skills/gstack/' >> .gitignore` |
| 253 | 3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) |
| 254 | 4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` |
| 255 | 5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" |
Access to home directory dotfiles
| 253 | 3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) |
| 254 | 4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` |
| 255 | 5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" |
| 256 | |
| 257 | If B: say "OK, you're on your own to keep the vendored copy up to date." |
Access to home directory dotfiles
| 259 | Always run (regardless of choice): |
| 260 | ```bash |
| 261 | eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true |
| 262 | touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} |
| 263 | ``` |
Access to home directory dotfiles
| 260 | ```bash |
| 261 | eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true |
| 262 | touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} |
| 263 | ``` |
| 264 |
Access to home directory dotfiles
| 283 | _BRAIN_REMOTE_FILE="$HOME/.gstack-brain-remote.txt" |
| 284 | fi |
| 285 | _BRAIN_SYNC_BIN="~/.claude/skills/gstack/bin/gstack-brain-sync" |
| 286 | _BRAIN_CONFIG_BIN="~/.claude/skills/gstack/bin/gstack-config" |
| 287 |
Access to home directory dotfiles
| 284 | fi |
| 285 | _BRAIN_SYNC_BIN="~/.claude/skills/gstack/bin/gstack-brain-sync" |
| 286 | _BRAIN_CONFIG_BIN="~/.claude/skills/gstack/bin/gstack-config" |
| 287 | |
| 288 | # /sync-gbrain context-load: teach the agent to use gbrain when it's available. |
Access to home directory dotfiles
| 388 | ``` |
| 389 | |
| 390 | If A/B and `~/.gstack/.git` is missing, ask whether to run `gstack-artifacts-init`. Do not block the skill. |
| 391 | |
| 392 | At skill END before telemetry: |
Access to home directory dotfiles
| 393 | |
| 394 | ```bash |
| 395 | "~/.claude/skills/gstack/bin/gstack-brain-sync" --discover-new 2>/dev/null || true |
| 396 | "~/.claude/skills/gstack/bin/gstack-brain-sync" --once 2>/dev/null || true |
| 397 | ``` |
Access to home directory dotfiles
| 394 | ```bash |
| 395 | "~/.claude/skills/gstack/bin/gstack-brain-sync" --discover-new 2>/dev/null || true |
| 396 | "~/.claude/skills/gstack/bin/gstack-brain-sync" --once 2>/dev/null || true |
| 397 | ``` |
| 398 |
Access to home directory dotfiles
| 439 | |
| 440 | ```bash |
| 441 | ~/.claude/skills/gstack/bin/gstack-learnings-log '{"skill":"SKILL_NAME","type":"operational","key":"SHORT_KEY","insight":"DESCRIPTION","confidence":N,"source":"observed"}' |
| 442 | ``` |
| 443 |
Access to home directory dotfiles
| 449 | |
| 450 | **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to |
| 451 | `~/.gstack/analytics/`, matching preamble analytics writes. |
| 452 | |
| 453 | Run this bash: |
Access to home directory dotfiles
| 456 | _TEL_END=$(date +%s) |
| 457 | _TEL_DUR=$(( _TEL_END - _TEL_START )) |
| 458 | rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true |
| 459 | # Session timeline: record skill completion (local-only, never sent anywhere) |
| 460 | ~/.claude/skills/gstack/bin/gstack-timeline-log '{"skill":"SKILL_NAME","event":"completed","branch":"'$(git branch --show-current 2>/dev/null || echo unknown)'","outcome":"OUTCOME","duration_s":"'"$_TEL_DUR"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null || true |
Access to home directory dotfiles
| 458 | rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true |
| 459 | # Session timeline: record skill completion (local-only, never sent anywhere) |
| 460 | ~/.claude/skills/gstack/bin/gstack-timeline-log '{"skill":"SKILL_NAME","event":"completed","branch":"'$(git branch --show-current 2>/dev/null || echo unknown)'","outcome":"OUTCOME","duration_s":"'"$_TEL_DUR"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null || true |
| 461 | # Local analytics (gated on telemetry setting) |
| 462 | if [ "$_TEL" != "off" ]; then |
Access to home directory dotfiles
| 461 | # Local analytics (gated on telemetry setting) |
| 462 | if [ "$_TEL" != "off" ]; then |
| 463 | echo '{"skill":"SKILL_NAME","duration_s":"'"$_TEL_DUR"'","outcome":"OUTCOME","browse":"USED_BROWSE","session":"'"$_SESSION_ID"'","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true |
| 464 | fi |
| 465 | # Remote telemetry (opt-in, requires binary) |
Access to home directory dotfiles
| 464 | fi |
| 465 | # Remote telemetry (opt-in, requires binary) |
| 466 | if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then |
| 467 | ~/.claude/skills/gstack/bin/gstack-telemetry-log \ |
| 468 | --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ |
Access to home directory dotfiles
| 465 | # Remote telemetry (opt-in, requires binary) |
| 466 | if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then |
| 467 | ~/.claude/skills/gstack/bin/gstack-telemetry-log \ |
| 468 | --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ |
| 469 | --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & |
Access to home directory dotfiles
| 475 | ## Plan Status Footer |
| 476 | |
| 477 | In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. |
| 478 | |
| 479 | PLAN MODE EXCEPTION — always allowed (it's the plan file). |
Access to home directory dotfiles
| 718 | |
| 719 | The snapshot is your primary tool for understanding and interacting with pages. |
| 720 | `$B` is the browse binary (resolved from `$_ROOT/.claude/skills/gstack/browse/dist/browse` or `~/.claude/skills/gstack/browse/dist/browse`). |
| 721 | |
| 722 | **Syntax:** `$B snapshot [flags]` |
Urgency-based manipulation
| 109 | ## Skill Invocation During Plan Mode |
| 110 | |
| 111 | If the user invokes a skill in plan mode, the skill takes precedence over generic plan mode behavior. **Treat the skill file as executable instructions, not reference.** Follow it step by step starting from Step 0; the first AskUserQuestion is the workflow entering plan mode, not a violation of it. AskUserQuestion (any variant — `mcp__*__AskUserQuestion` or native; see "AskUserQuestion Format → Tool resolution") satisfies plan mode's end-of-turn requirement. If no variant is callable, the skill is BLOCKED — stop and report `BLOCKED — AskUserQuestion unavailable` per the AskUserQuestion Format rule. At a STOP point, stop immediately. Do not continue the workflow or call ExitPlanMode there. Commands marked "PLAN MODE EXCEPTION — ALWAYS RUN" execute. Call ExitPlanMode only after the skill workflow completes, or if the user tells you to cancel the skill or leave plan mode. |
| 112 | |
| 113 | If `PROACTIVE` is `"false"`, do not auto-invoke or proactively suggest skills. If a skill seems useful, ask: "I think /skillname might help here — want me to run it?" |