From cdd6f7865d0edf741f658a256115cbf77dace61b Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Sun, 29 Mar 2026 21:43:36 -0600 Subject: [PATCH] =?UTF-8?q?feat:=20community=20wave=20=E2=80=94=207=20fixe?= =?UTF-8?q?s,=20relink,=20sidebar=20Write,=20discoverability=20(v0.13.5.0)?= =?UTF-8?q?=20(#641)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: add 16 failing tests for 6 community fixes Tests-first for all fixes in this PR wave: - #594 discoverability: gstack tag in descriptions, 120-char first line - #573 feature signals: ship/SKILL.md Step 4 detection - #510 context warnings: no preemptive warnings in generated files - #474 Safety Net: no find -delete in generated files - #467 telemetry: JSONL writes gated by _TEL conditional - #584 sidebar: Write in allowedTools, stderr capture - #578 relink: prefixed/flat symlinks, cleanup, error, config hook Co-Authored-By: Claude Opus 4.6 (1M context) * fix: replace find -delete with find -exec rm for Safety Net (#474) -delete is a non-POSIX extension that fails on Safety Net environments. -exec rm {} + is POSIX-compliant and works everywhere. Co-Authored-By: Claude Opus 4.6 (1M context) * fix: gate local JSONL writes by telemetry setting (#467) When telemetry is off, nothing is written anywhere — not just remote, but local JSONL too. Clean trust contract: off means off everywhere. Co-Authored-By: Claude Opus 4.6 (1M context) * fix: remove preemptive context warnings from plan-eng-review (#510) The system handles context compaction automatically. Preemptive warnings waste tokens and create false urgency. Skills should not warn about context limits — just describe the compression priority order. Co-Authored-By: Claude Opus 4.6 (1M context) * feat: add (gstack) tag to skill descriptions for discoverability (#594) Every SKILL.md.tmpl description now contains "gstack" on the last line, making skills findable in Claude Code's command palette. First-line hooks stay under 120 chars. Split ship description to fix wrapping. Co-Authored-By: Claude Opus 4.6 (1M context) * feat: auto-relink skill symlinks on prefix config change (#578) New bin/gstack-relink creates prefixed (gstack-*) or flat symlinks based on skill_prefix config. gstack-config auto-triggers relink when skill_prefix changes. Setup guards against recursive calls with GSTACK_SETUP_RUNNING env var. Co-Authored-By: Claude Opus 4.6 (1M context) * feat: add feature signal detection to version bump heuristic (#573) /ship Step 4 now checks for feature signals (new routes, migrations, test+source pairs, feat/ branches) when deciding version bumps. PATCH requires no feature signals. MINOR asks the user if any signal is detected or 500+ lines changed. Co-Authored-By: Claude Opus 4.6 (1M context) * feat: sidebar Write tool, stderr capture, cross-platform URL opener (#584) Add Write to sidebar allowedTools (both sidebar-agent.ts and server.ts). Write doesn't expand attack surface beyond what Bash already provides. Replace empty stderr handler with buffer capture for better error diagnostics. New bin/gstack-open-url for cross-platform URL opening. Does NOT include Search Before Building intro flow (deferred). Co-Authored-By: Claude Opus 4.6 (1M context) * fix: update sidebar-security test for Write tool addition The fallback allowedTools string now includes Write, matching the sidebar-agent.ts change from commit 68dc957. Co-Authored-By: Claude Opus 4.6 (1M context) * chore: bump version and changelog (v0.13.5.0) Co-Authored-By: Claude Opus 4.6 (1M context) * fix: prevent gstack-relink from double-prefixing gstack-upgrade gstack-relink now checks if a skill directory is already named gstack-* before prepending the prefix. Previously, setting skill_prefix=true would create gstack-gstack-upgrade, breaking the /gstack-upgrade command. Matches setup script behavior (setup:260) which already has this guard. Co-Authored-By: Claude Opus 4.6 (1M context) * chore: add double-prefix fix to changelog Co-Authored-By: Claude Opus 4.6 (1M context) * chore: remove .factory/ from git tracking and add to .gitignore Generated Factory Droid skills are build output, same as .agents/. They should not be committed to the repo. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 21 ++++ SKILL.md | 28 ++--- SKILL.md.tmpl | 2 +- VERSION | 2 +- autoplan/SKILL.md | 28 ++--- autoplan/SKILL.md.tmpl | 2 +- benchmark/SKILL.md | 28 ++--- benchmark/SKILL.md.tmpl | 2 +- bin/gstack-config | 5 + bin/gstack-open-url | 14 +++ bin/gstack-relink | 73 +++++++++++++ browse/SKILL.md | 28 ++--- browse/SKILL.md.tmpl | 2 +- browse/src/server.ts | 2 +- browse/src/sidebar-agent.ts | 25 ++++- browse/test/sidebar-security.test.ts | 2 +- canary/SKILL.md | 28 ++--- canary/SKILL.md.tmpl | 2 +- careful/SKILL.md | 2 +- careful/SKILL.md.tmpl | 2 +- codex/SKILL.md | 28 ++--- codex/SKILL.md.tmpl | 2 +- connect-chrome/SKILL.md | 26 +++-- cso/SKILL.md | 28 ++--- cso/SKILL.md.tmpl | 2 +- design-consultation/SKILL.md | 28 ++--- design-consultation/SKILL.md.tmpl | 2 +- design-review/SKILL.md | 28 ++--- design-review/SKILL.md.tmpl | 2 +- design-shotgun/SKILL.md | 28 ++--- design-shotgun/SKILL.md.tmpl | 2 +- document-release/SKILL.md | 28 ++--- document-release/SKILL.md.tmpl | 2 +- freeze/SKILL.md | 2 +- freeze/SKILL.md.tmpl | 2 +- guard/SKILL.md | 2 +- guard/SKILL.md.tmpl | 2 +- investigate/SKILL.md | 28 ++--- investigate/SKILL.md.tmpl | 2 +- land-and-deploy/SKILL.md | 28 ++--- land-and-deploy/SKILL.md.tmpl | 2 +- learn/SKILL.md | 26 +++-- office-hours/SKILL.md | 28 ++--- office-hours/SKILL.md.tmpl | 2 +- package.json | 2 +- plan-ceo-review/SKILL.md | 28 ++--- plan-ceo-review/SKILL.md.tmpl | 2 +- plan-design-review/SKILL.md | 28 ++--- plan-design-review/SKILL.md.tmpl | 2 +- plan-eng-review/SKILL.md | 30 +++--- plan-eng-review/SKILL.md.tmpl | 4 +- qa-only/SKILL.md | 28 ++--- qa-only/SKILL.md.tmpl | 2 +- qa/SKILL.md | 28 ++--- qa/SKILL.md.tmpl | 2 +- retro/SKILL.md | 28 ++--- retro/SKILL.md.tmpl | 2 +- review/SKILL.md | 28 ++--- review/SKILL.md.tmpl | 2 +- scripts/resolvers/preamble.ts | 29 ++--- setup | 1 + setup-browser-cookies/SKILL.md | 28 ++--- setup-browser-cookies/SKILL.md.tmpl | 2 +- setup-deploy/SKILL.md | 26 +++-- ship/SKILL.md | 37 ++++--- ship/SKILL.md.tmpl | 11 +- test/gen-skill-docs.test.ts | 94 +++++++++++++++++ test/relink.test.ts | 152 +++++++++++++++++++++++++++ test/skill-validation.test.ts | 27 +++++ test/telemetry.test.ts | 22 ++++ unfreeze/SKILL.md | 2 +- unfreeze/SKILL.md.tmpl | 2 +- 72 files changed, 908 insertions(+), 369 deletions(-) create mode 100755 bin/gstack-open-url create mode 100755 bin/gstack-relink create mode 100644 test/relink.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d938c9f5d7c2461749ceed202f822f1d6d4ec3b7..cee02336e0efa20264f5f8ad6581a370c39dec88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## [0.13.7.0] - 2026-03-29 — Community Wave + +Six community fixes with 16 new tests. Telemetry off now means off everywhere. Skills are findable by name. And changing your prefix setting actually works now. + +### Fixed + +- **Telemetry off means off everywhere.** When you set telemetry to off, gstack no longer writes local JSONL analytics files. Previously "off" only stopped remote reporting. Now nothing is written anywhere. Clean trust contract. +- **`find -delete` replaced with POSIX `-exec rm`.** Safety Net and other non-GNU environments no longer choke on session cleanup. +- **No more preemptive context warnings.** `/plan-eng-review` no longer warns you about running low on context. The system handles compaction automatically. +- **Sidebar security test updated** for Write tool fallback string change. +- **`gstack-relink` no longer double-prefixes `gstack-upgrade`.** Setting `skill_prefix=true` was creating `gstack-gstack-upgrade` instead of keeping the existing name. Now matches `setup` script behavior. + +### Added + +- **Skill discoverability.** Every skill description now contains "(gstack)" so you can find gstack skills by searching in Claude Code's command palette. +- **Feature signal detection in `/ship`.** Version bump now checks for new routes, migrations, test+source pairs, and `feat/` branches. Catches MINOR-worthy changes that line count alone misses. +- **Sidebar Write tool.** Both the sidebar agent and headed-mode server now include Write in allowedTools. Write doesn't expand the attack surface beyond what Bash already provides. +- **Sidebar stderr capture.** The sidebar agent now buffers stderr and includes it in error and timeout messages instead of silently discarding it. +- **`bin/gstack-relink`** re-creates skill symlinks when you change `skill_prefix` via `gstack-config set`. No more manual `./setup` re-run needed. +- **`bin/gstack-open-url`** cross-platform URL opener (macOS: `open`, Linux: `xdg-open`, Windows: `start`). + ## [0.13.6.0] - 2026-03-29 — GStack Learns Every session now makes the next one smarter. gstack remembers patterns, pitfalls, and preferences across sessions and uses them to improve every review, plan, debug, and ship. The more you use it, the better it gets on your codebase. diff --git a/SKILL.md b/SKILL.md index ec0ed5aa57333f209244ad3ec75583432cb78aed..d4840cf1b55237dfd8bec1ccf8f0ede0a64d48d7 100644 --- a/SKILL.md +++ b/SKILL.md @@ -6,7 +6,7 @@ description: | Fast headless browser for QA testing and site dogfooding. Navigate pages, interact with elements, verify state, diff before/after, take annotated screenshots, test responsive layouts, forms, uploads, dialogs, and capture bug evidence. Use when asked to open or - test a site, verify a deployment, dogfood a user flow, or file a bug with screenshots. + test a site, verify a deployment, dogfood a user flow, or file a bug with screenshots. (gstack) allowed-tools: - Bash - Read @@ -24,7 +24,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -46,7 +46,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"gstack","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"gstack","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -216,20 +218,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/SKILL.md.tmpl b/SKILL.md.tmpl index 39b6873e222474d5d4b49eb3276c3cd9c7427426..fcc0900b0a1afd5c4f55839a1ffb38dcf8561e41 100644 --- a/SKILL.md.tmpl +++ b/SKILL.md.tmpl @@ -6,7 +6,7 @@ description: | Fast headless browser for QA testing and site dogfooding. Navigate pages, interact with elements, verify state, diff before/after, take annotated screenshots, test responsive layouts, forms, uploads, dialogs, and capture bug evidence. Use when asked to open or - test a site, verify a deployment, dogfood a user flow, or file a bug with screenshots. + test a site, verify a deployment, dogfood a user flow, or file a bug with screenshots. (gstack) allowed-tools: - Bash - Read diff --git a/VERSION b/VERSION index 3006dba97b78c678168c6eda2b8acf63229f0b47..a4029e21a4a12c93bbc7049fcc27056c29b8d106 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.13.6.0 +0.13.7.0 diff --git a/autoplan/SKILL.md b/autoplan/SKILL.md index 338a1af89992107714c1aa140d37b8ff42c085ea..f827fcba80d5290df420d23fc650f31bd59568c9 100644 --- a/autoplan/SKILL.md +++ b/autoplan/SKILL.md @@ -10,7 +10,7 @@ description: | Use when asked to "auto review", "autoplan", "run all reviews", "review this plan automatically", or "make the decisions for me". Proactively suggest when the user has a plan file and wants to run the full review - gauntlet without answering 15-30 intermediate questions. + gauntlet without answering 15-30 intermediate questions. (gstack) benefits-from: [office-hours] allowed-tools: - Bash @@ -33,7 +33,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -55,7 +55,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"autoplan","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"autoplan","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -308,20 +310,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/autoplan/SKILL.md.tmpl b/autoplan/SKILL.md.tmpl index 5577b64bc7f7b5de3c26de9b38d44ee4c5b342f7..38ab2816e0485af6883c7002e73e1d89e22643c9 100644 --- a/autoplan/SKILL.md.tmpl +++ b/autoplan/SKILL.md.tmpl @@ -10,7 +10,7 @@ description: | Use when asked to "auto review", "autoplan", "run all reviews", "review this plan automatically", or "make the decisions for me". Proactively suggest when the user has a plan file and wants to run the full review - gauntlet without answering 15-30 intermediate questions. + gauntlet without answering 15-30 intermediate questions. (gstack) benefits-from: [office-hours] allowed-tools: - Bash diff --git a/benchmark/SKILL.md b/benchmark/SKILL.md index 10d71673752b4a93fcc06fd483af5680740e6b7d..417092269da0b263d9ec8d8c29350d15430217be 100644 --- a/benchmark/SKILL.md +++ b/benchmark/SKILL.md @@ -7,7 +7,7 @@ description: | baselines for page load times, Core Web Vitals, and resource sizes. Compares before/after on every PR. Tracks performance trends over time. Use when: "performance", "benchmark", "page speed", "lighthouse", "web vitals", - "bundle size", "load time". + "bundle size", "load time". (gstack) allowed-tools: - Bash - Read @@ -26,7 +26,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -48,7 +48,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"benchmark","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"benchmark","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -218,20 +220,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/benchmark/SKILL.md.tmpl b/benchmark/SKILL.md.tmpl index 5149ea441c3fb4d62d2eeebe4fa9a29a308199cd..dca820142613e55a85bfd3589591fcfae4591ea1 100644 --- a/benchmark/SKILL.md.tmpl +++ b/benchmark/SKILL.md.tmpl @@ -7,7 +7,7 @@ description: | baselines for page load times, Core Web Vitals, and resource sizes. Compares before/after on every PR. Tracks performance trends over time. Use when: "performance", "benchmark", "page speed", "lighthouse", "web vitals", - "bundle size", "load time". + "bundle size", "load time". (gstack) allowed-tools: - Bash - Read diff --git a/bin/gstack-config b/bin/gstack-config index 821a342a7d70e4240c3a9ffd3adefc6998ac0f71..08549a29d946dfae4ac885219274edcc6357055a 100755 --- a/bin/gstack-config +++ b/bin/gstack-config @@ -41,6 +41,11 @@ case "${1:-}" in else echo "${KEY}: ${VALUE}" >> "$CONFIG_FILE" fi + # Auto-relink skills when prefix setting changes (skip during setup to avoid recursive call) + if [ "$KEY" = "skill_prefix" ] && [ -z "${GSTACK_SETUP_RUNNING:-}" ]; then + GSTACK_RELINK="$(dirname "$0")/gstack-relink" + [ -x "$GSTACK_RELINK" ] && "$GSTACK_RELINK" || true + fi ;; list) cat "$CONFIG_FILE" 2>/dev/null || true diff --git a/bin/gstack-open-url b/bin/gstack-open-url new file mode 100755 index 0000000000000000000000000000000000000000..7252313765addcc12d0ec5cbfa65649994005dcc --- /dev/null +++ b/bin/gstack-open-url @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# gstack-open-url — cross-platform URL opener +# +# Usage: gstack-open-url +set -euo pipefail + +URL="${1:?Usage: gstack-open-url }" + +case "$(uname -s)" in + Darwin) open "$URL" ;; + Linux) xdg-open "$URL" 2>/dev/null || echo "$URL" ;; + MINGW*|MSYS*|CYGWIN*) start "$URL" ;; + *) echo "$URL" ;; +esac diff --git a/bin/gstack-relink b/bin/gstack-relink new file mode 100755 index 0000000000000000000000000000000000000000..49d0ccacfe8b3360ad79ccf46201d929161e0857 --- /dev/null +++ b/bin/gstack-relink @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# gstack-relink — re-create skill symlinks based on skill_prefix config +# +# Usage: +# gstack-relink +# +# Env overrides (for testing): +# GSTACK_STATE_DIR — override ~/.gstack state directory +# GSTACK_INSTALL_DIR — override gstack install directory +# GSTACK_SKILLS_DIR — override target skills directory +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +GSTACK_CONFIG="${SCRIPT_DIR}/gstack-config" + +# Detect install dir +INSTALL_DIR="${GSTACK_INSTALL_DIR:-}" +if [ -z "$INSTALL_DIR" ]; then + if [ -d "$HOME/.claude/skills/gstack" ]; then + INSTALL_DIR="$HOME/.claude/skills/gstack" + elif [ -d "${SCRIPT_DIR}/.." ] && [ -f "${SCRIPT_DIR}/../setup" ]; then + INSTALL_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" + fi +fi + +if [ -z "$INSTALL_DIR" ] || [ ! -d "$INSTALL_DIR" ]; then + echo "Error: gstack install directory not found." >&2 + echo "Run: cd ~/.claude/skills/gstack && ./setup" >&2 + exit 1 +fi + +# Detect target skills dir +SKILLS_DIR="${GSTACK_SKILLS_DIR:-$(dirname "$INSTALL_DIR")}" +[ -d "$SKILLS_DIR" ] || mkdir -p "$SKILLS_DIR" + +# Read prefix setting +PREFIX=$("$GSTACK_CONFIG" get skill_prefix 2>/dev/null || echo "false") + +# Discover skills (directories with SKILL.md, excluding meta dirs) +SKILL_COUNT=0 +for skill_dir in "$INSTALL_DIR"/*/; do + [ -d "$skill_dir" ] || continue + skill=$(basename "$skill_dir") + # Skip non-skill directories + case "$skill" in bin|browse|design|docs|extension|lib|node_modules|scripts|test|.git|.github) continue ;; esac + [ -f "$skill_dir/SKILL.md" ] || continue + + if [ "$PREFIX" = "true" ]; then + # Don't double-prefix directories already named gstack-* + case "$skill" in + gstack-*) link_name="$skill" ;; + *) link_name="gstack-$skill" ;; + esac + ln -sfn "$INSTALL_DIR/$skill" "$SKILLS_DIR/$link_name" + # Remove old flat symlink if it exists (and isn't the same as the new link) + [ "$link_name" != "$skill" ] && [ -L "$SKILLS_DIR/$skill" ] && rm -f "$SKILLS_DIR/$skill" + else + # Create flat symlink, remove gstack-* if exists + ln -sfn "$INSTALL_DIR/$skill" "$SKILLS_DIR/$skill" + # Don't remove gstack-* dirs that are their real name (e.g., gstack-upgrade) + case "$skill" in + gstack-*) ;; # Already the real name, no old prefixed link to clean + *) [ -L "$SKILLS_DIR/gstack-$skill" ] && rm -f "$SKILLS_DIR/gstack-$skill" ;; + esac + fi + SKILL_COUNT=$((SKILL_COUNT + 1)) +done + +if [ "$PREFIX" = "true" ]; then + echo "Relinked $SKILL_COUNT skills as gstack-*" +else + echo "Relinked $SKILL_COUNT skills as flat names" +fi diff --git a/browse/SKILL.md b/browse/SKILL.md index 440871c863434e76e3dbf844160f1fe02a25c19b..9448edabe963ceb17b9791301ef4b77073a2bd13 100644 --- a/browse/SKILL.md +++ b/browse/SKILL.md @@ -8,7 +8,7 @@ description: | responsive layouts, test forms and uploads, handle dialogs, and assert element states. ~100ms per command. Use when you need to test a feature, verify a deployment, dogfood a user flow, or file a bug with evidence. Use when asked to "open in browser", "test the - site", "take a screenshot", or "dogfood this". + site", "take a screenshot", or "dogfood this". (gstack) allowed-tools: - Bash - Read @@ -26,7 +26,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -48,7 +48,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -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 +if [ "${_TEL:-off}" != "off" ]; then + 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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -218,20 +220,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/browse/SKILL.md.tmpl b/browse/SKILL.md.tmpl index a11505ea6442d5ba66cc645cdd083acbf2bbba85..df70a685ad1a47ed4262b5a62528477295086998 100644 --- a/browse/SKILL.md.tmpl +++ b/browse/SKILL.md.tmpl @@ -8,7 +8,7 @@ description: | responsive layouts, test forms and uploads, handle dialogs, and assert element states. ~100ms per command. Use when you need to test a feature, verify a deployment, dogfood a user flow, or file a bug with evidence. Use when asked to "open in browser", "test the - site", "take a screenshot", or "dogfood this". + site", "take a screenshot", or "dogfood this". (gstack) allowed-tools: - Bash - Read diff --git a/browse/src/server.ts b/browse/src/server.ts index f3f8d68dd81de61829c1d1612a30fd50ad2335b5..99ce4e9c0868482a1cddfd8ac7a1cd4721753747 100644 --- a/browse/src/server.ts +++ b/browse/src/server.ts @@ -430,7 +430,7 @@ function spawnClaude(userMessage: string, extensionUrl?: string | null): void { const prompt = `${systemPrompt}\n\n\n${escapedMessage}\n`; const args = ['-p', prompt, '--model', 'opus', '--output-format', 'stream-json', '--verbose', - '--allowedTools', 'Bash,Read,Glob,Grep']; + '--allowedTools', 'Bash,Read,Glob,Grep,Write']; if (sidebarSession?.claudeSessionId) { args.push('--resume', sidebarSession.claudeSessionId); } diff --git a/browse/src/sidebar-agent.ts b/browse/src/sidebar-agent.ts index db56022114093d4138cbae0ee3be978e166e8c19..644d45b05e2330a45aa5831a53b7cb7705a44f25 100644 --- a/browse/src/sidebar-agent.ts +++ b/browse/src/sidebar-agent.ts @@ -161,8 +161,10 @@ async function askClaude(queueEntry: any): Promise { return new Promise((resolve) => { // Use args from queue entry (server sets --model, --allowedTools, prompt framing). // Fall back to defaults only if queue entry has no args (backward compat). + // Write doesn't expand attack surface beyond what Bash already provides. + // The security boundary is the localhost-only message path, not the tool allowlist. let claudeArgs = args || ['-p', prompt, '--output-format', 'stream-json', '--verbose', - '--allowedTools', 'Bash,Read,Glob,Grep']; + '--allowedTools', 'Bash,Read,Glob,Grep,Write']; // Validate cwd exists — queue may reference a stale worktree let effectiveCwd = cwd || process.cwd(); @@ -188,20 +190,30 @@ async function askClaude(queueEntry: any): Promise { } }); - proc.stderr.on('data', () => {}); // Claude logs to stderr, ignore + let stderrBuffer = ''; + proc.stderr.on('data', (data: Buffer) => { + stderrBuffer += data.toString(); + }); proc.on('close', (code) => { if (buffer.trim()) { try { handleStreamEvent(JSON.parse(buffer)); } catch {} } - sendEvent({ type: 'agent_done' }).then(() => { + const doneEvent: Record = { type: 'agent_done' }; + if (code !== 0 && stderrBuffer.trim()) { + doneEvent.stderr = stderrBuffer.trim().slice(-500); + } + sendEvent(doneEvent).then(() => { isProcessing = false; resolve(); }); }); proc.on('error', (err) => { - sendEvent({ type: 'agent_error', error: err.message }).then(() => { + const errorMsg = stderrBuffer.trim() + ? `${err.message}\nstderr: ${stderrBuffer.trim().slice(-500)}` + : err.message; + sendEvent({ type: 'agent_error', error: errorMsg }).then(() => { isProcessing = false; resolve(); }); @@ -211,7 +223,10 @@ async function askClaude(queueEntry: any): Promise { const timeoutMs = parseInt(process.env.SIDEBAR_AGENT_TIMEOUT || '300000', 10); setTimeout(() => { try { proc.kill(); } catch {} - sendEvent({ type: 'agent_error', error: `Timed out after ${timeoutMs / 1000}s` }).then(() => { + const timeoutMsg = stderrBuffer.trim() + ? `Timed out after ${timeoutMs / 1000}s\nstderr: ${stderrBuffer.trim().slice(-500)}` + : `Timed out after ${timeoutMs / 1000}s`; + sendEvent({ type: 'agent_error', error: timeoutMsg }).then(() => { isProcessing = false; resolve(); }); diff --git a/browse/test/sidebar-security.test.ts b/browse/test/sidebar-security.test.ts index b953f5b7702e10940d96df35381412675a497988..33c64b4974f74fc418e4339c4fbd425b9097eb08 100644 --- a/browse/test/sidebar-security.test.ts +++ b/browse/test/sidebar-security.test.ts @@ -115,6 +115,6 @@ describe('Sidebar prompt injection defense', () => { test('sidebar-agent falls back to defaults if queue has no args', () => { // Backward compatibility: if old queue entries lack args, use defaults - expect(AGENT_SRC).toContain("'--allowedTools', 'Bash,Read,Glob,Grep'"); + expect(AGENT_SRC).toContain("'--allowedTools', 'Bash,Read,Glob,Grep,Write'"); }); }); diff --git a/canary/SKILL.md b/canary/SKILL.md index c91bf15d54bdfcc7b70e221b54103d9e95293221..0ea349efc3123bf732bda5f28c8912bda93615fc 100644 --- a/canary/SKILL.md +++ b/canary/SKILL.md @@ -7,7 +7,7 @@ description: | performance regressions, and page failures using the browse daemon. Takes periodic screenshots, compares against pre-deploy baselines, and alerts on anomalies. Use when: "monitor deploy", "canary", "post-deploy check", - "watch production", "verify deploy". + "watch production", "verify deploy". (gstack) allowed-tools: - Bash - Read @@ -26,7 +26,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -48,7 +48,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"canary","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"canary","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -283,20 +285,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/canary/SKILL.md.tmpl b/canary/SKILL.md.tmpl index 680b581475b10f3dbae2bd01d73dd20fa00f774e..41218304008c6e8d2a3b2f46d97f1a9a7f4e978b 100644 --- a/canary/SKILL.md.tmpl +++ b/canary/SKILL.md.tmpl @@ -7,7 +7,7 @@ description: | performance regressions, and page failures using the browse daemon. Takes periodic screenshots, compares against pre-deploy baselines, and alerts on anomalies. Use when: "monitor deploy", "canary", "post-deploy check", - "watch production", "verify deploy". + "watch production", "verify deploy". (gstack) allowed-tools: - Bash - Read diff --git a/careful/SKILL.md b/careful/SKILL.md index 7513b293776c4222f3de8b9ceb6e81357152562a..5f9aea3f23415c3a27ac86722109ece184d18382 100644 --- a/careful/SKILL.md +++ b/careful/SKILL.md @@ -6,7 +6,7 @@ description: | force-push, git reset --hard, kubectl delete, and similar destructive operations. User can override each warning. Use when touching prod, debugging live systems, or working in a shared environment. Use when asked to "be careful", "safety mode", - "prod mode", or "careful mode". + "prod mode", or "careful mode". (gstack) allowed-tools: - Bash - Read diff --git a/careful/SKILL.md.tmpl b/careful/SKILL.md.tmpl index 33c38ef8171c8857d9990bfdd77ade6dd22e5cf2..dd8f0ded1d244d9f5f3ba6b9f73d5846e48389d5 100644 --- a/careful/SKILL.md.tmpl +++ b/careful/SKILL.md.tmpl @@ -6,7 +6,7 @@ description: | force-push, git reset --hard, kubectl delete, and similar destructive operations. User can override each warning. Use when touching prod, debugging live systems, or working in a shared environment. Use when asked to "be careful", "safety mode", - "prod mode", or "careful mode". + "prod mode", or "careful mode". (gstack) allowed-tools: - Bash - Read diff --git a/codex/SKILL.md b/codex/SKILL.md index a9f409cf46d4299f7eba815ac27a338445facc24..a3c826212b68ec66839bbf004edca5f4b39e0bb6 100644 --- a/codex/SKILL.md +++ b/codex/SKILL.md @@ -7,7 +7,7 @@ description: | codex review with pass/fail gate. Challenge: adversarial mode that tries to break your code. Consult: ask codex anything with session continuity for follow-ups. The "200 IQ autistic developer" second opinion. Use when asked to "codex review", - "codex challenge", "ask codex", "second opinion", or "consult codex". + "codex challenge", "ask codex", "second opinion", or "consult codex". (gstack) allowed-tools: - Bash - Read @@ -27,7 +27,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -49,7 +49,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"codex","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"codex","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -302,20 +304,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/codex/SKILL.md.tmpl b/codex/SKILL.md.tmpl index c44480a9f8c5cef5a4a5d4a0af069a6743497e35..86500003c55fedd75ea53ea226aa38ffe2d8e77a 100644 --- a/codex/SKILL.md.tmpl +++ b/codex/SKILL.md.tmpl @@ -7,7 +7,7 @@ description: | codex review with pass/fail gate. Challenge: adversarial mode that tries to break your code. Consult: ask codex anything with session continuity for follow-ups. The "200 IQ autistic developer" second opinion. Use when asked to "codex review", - "codex challenge", "ask codex", "second opinion", or "consult codex". + "codex challenge", "ask codex", "second opinion", or "consult codex". (gstack) allowed-tools: - Bash - Read diff --git a/connect-chrome/SKILL.md b/connect-chrome/SKILL.md index 58df84f996d049f9a136db4803a17759fc4cda5a..f9529d315e9206a7bec720e75de995ed908385db 100644 --- a/connect-chrome/SKILL.md +++ b/connect-chrome/SKILL.md @@ -24,7 +24,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -46,7 +46,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"connect-chrome","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"connect-chrome","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -299,20 +301,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/cso/SKILL.md b/cso/SKILL.md index 14b712f49c16e6ce1950285f3f328dbe596d3877..783a5ee0119ad2f355ac080b99bc5897679f786f 100644 --- a/cso/SKILL.md +++ b/cso/SKILL.md @@ -8,7 +8,7 @@ description: | scanning, plus OWASP Top 10, STRIDE threat modeling, and active verification. Two modes: daily (zero-noise, 8/10 confidence gate) and comprehensive (monthly deep scan, 2/10 bar). Trend tracking across audit runs. - Use when: "security audit", "threat model", "pentest review", "OWASP", "CSO review". + Use when: "security audit", "threat model", "pentest review", "OWASP", "CSO review". (gstack) allowed-tools: - Bash - Read @@ -30,7 +30,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -52,7 +52,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"cso","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"cso","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -287,20 +289,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/cso/SKILL.md.tmpl b/cso/SKILL.md.tmpl index 5bd86a9ce2b810d2b29792e547bfbe6a5cfea4ca..120319f65cd4897532bd49bf45cefd32eb9db388 100644 --- a/cso/SKILL.md.tmpl +++ b/cso/SKILL.md.tmpl @@ -8,7 +8,7 @@ description: | scanning, plus OWASP Top 10, STRIDE threat modeling, and active verification. Two modes: daily (zero-noise, 8/10 confidence gate) and comprehensive (monthly deep scan, 2/10 bar). Trend tracking across audit runs. - Use when: "security audit", "threat model", "pentest review", "OWASP", "CSO review". + Use when: "security audit", "threat model", "pentest review", "OWASP", "CSO review". (gstack) allowed-tools: - Bash - Read diff --git a/design-consultation/SKILL.md b/design-consultation/SKILL.md index dc5c9ec7ec4fbc77b40a9d7af144a0479f48bcc3..41793f2e8d8bf4089c134fe21d88a7fce9d835da 100644 --- a/design-consultation/SKILL.md +++ b/design-consultation/SKILL.md @@ -9,7 +9,7 @@ description: | of truth. For existing sites, use /plan-design-review to infer the system instead. Use when asked to "design system", "brand guidelines", or "create DESIGN.md". Proactively suggest when starting a new project's UI with no existing - design system or DESIGN.md. + design system or DESIGN.md. (gstack) allowed-tools: - Bash - Read @@ -31,7 +31,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -53,7 +53,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"design-consultation","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"design-consultation","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -306,20 +308,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/design-consultation/SKILL.md.tmpl b/design-consultation/SKILL.md.tmpl index 2ce7c1d3bfc87272e19f97beaaace7c25f9bf266..5f46317c89d9ff29a8a7b53c429575083011c325 100644 --- a/design-consultation/SKILL.md.tmpl +++ b/design-consultation/SKILL.md.tmpl @@ -9,7 +9,7 @@ description: | of truth. For existing sites, use /plan-design-review to infer the system instead. Use when asked to "design system", "brand guidelines", or "create DESIGN.md". Proactively suggest when starting a new project's UI with no existing - design system or DESIGN.md. + design system or DESIGN.md. (gstack) allowed-tools: - Bash - Read diff --git a/design-review/SKILL.md b/design-review/SKILL.md index fa1f104f8c31ae4f9834c9216a62ca27a797fe4d..3a69484daeb31fea82f343c3989584a4d9c90a72 100644 --- a/design-review/SKILL.md +++ b/design-review/SKILL.md @@ -9,7 +9,7 @@ description: | screenshots. For plan-mode design review (before implementation), use /plan-design-review. Use when asked to "audit the design", "visual QA", "check if it looks good", or "design polish". Proactively suggest when the user mentions visual inconsistencies or - wants to polish the look of a live site. + wants to polish the look of a live site. (gstack) allowed-tools: - Bash - Read @@ -31,7 +31,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -53,7 +53,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"design-review","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"design-review","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -306,20 +308,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/design-review/SKILL.md.tmpl b/design-review/SKILL.md.tmpl index 904a732c46af24c73d1bffbc5140d6b65f010b76..de57c217b92b1f8e02f21bdd4dc5ff0e1cb86b6e 100644 --- a/design-review/SKILL.md.tmpl +++ b/design-review/SKILL.md.tmpl @@ -9,7 +9,7 @@ description: | screenshots. For plan-mode design review (before implementation), use /plan-design-review. Use when asked to "audit the design", "visual QA", "check if it looks good", or "design polish". Proactively suggest when the user mentions visual inconsistencies or - wants to polish the look of a live site. + wants to polish the look of a live site. (gstack) allowed-tools: - Bash - Read diff --git a/design-shotgun/SKILL.md b/design-shotgun/SKILL.md index e1e4d02d056fdb9c6222e68a32c5fb12a3aaa8fb..ac30aa5fa5f8479bed16379ac371be3604fe5ba4 100644 --- a/design-shotgun/SKILL.md +++ b/design-shotgun/SKILL.md @@ -8,7 +8,7 @@ description: | run anytime. Use when: "explore designs", "show me options", "design variants", "visual brainstorm", or "I don't like how this looks". Proactively suggest when the user describes a UI feature but hasn't seen - what it could look like. + what it could look like. (gstack) allowed-tools: - Bash - Read @@ -28,7 +28,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -50,7 +50,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"design-shotgun","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"design-shotgun","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -285,20 +287,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/design-shotgun/SKILL.md.tmpl b/design-shotgun/SKILL.md.tmpl index 436c8bc6542d8d52644c4eb2e8916dafe0c1a60d..6581e3c622ee1cd78fc44bd73d2beffd158ca5b0 100644 --- a/design-shotgun/SKILL.md.tmpl +++ b/design-shotgun/SKILL.md.tmpl @@ -8,7 +8,7 @@ description: | run anytime. Use when: "explore designs", "show me options", "design variants", "visual brainstorm", or "I don't like how this looks". Proactively suggest when the user describes a UI feature but hasn't seen - what it could look like. + what it could look like. (gstack) allowed-tools: - Bash - Read diff --git a/document-release/SKILL.md b/document-release/SKILL.md index f75962d7e427a1cbee22be5efb5deacc6fd26305..e7f80c9e7b8b60c11c7b6eb60844d2a39e20e894 100644 --- a/document-release/SKILL.md +++ b/document-release/SKILL.md @@ -7,7 +7,7 @@ description: | diff, updates README/ARCHITECTURE/CONTRIBUTING/CLAUDE.md to match what shipped, polishes CHANGELOG voice, cleans up TODOS, and optionally bumps VERSION. Use when asked to "update the docs", "sync documentation", or "post-ship docs". - Proactively suggest after a PR is merged or code is shipped. + Proactively suggest after a PR is merged or code is shipped. (gstack) allowed-tools: - Bash - Read @@ -28,7 +28,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -50,7 +50,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"document-release","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"document-release","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -285,20 +287,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/document-release/SKILL.md.tmpl b/document-release/SKILL.md.tmpl index 6b1fb7e34e3e0d094c9439b68f336b721a3a4c3a..b1b6f684aeeaaf5caba96f8e50c09d3702d181e5 100644 --- a/document-release/SKILL.md.tmpl +++ b/document-release/SKILL.md.tmpl @@ -7,7 +7,7 @@ description: | diff, updates README/ARCHITECTURE/CONTRIBUTING/CLAUDE.md to match what shipped, polishes CHANGELOG voice, cleans up TODOS, and optionally bumps VERSION. Use when asked to "update the docs", "sync documentation", or "post-ship docs". - Proactively suggest after a PR is merged or code is shipped. + Proactively suggest after a PR is merged or code is shipped. (gstack) allowed-tools: - Bash - Read diff --git a/freeze/SKILL.md b/freeze/SKILL.md index 00aaef61315517ccf9ba46885d1a19c2cbc9f8fe..abab021c714b47b584e184a67bd019dafcf9f1f6 100644 --- a/freeze/SKILL.md +++ b/freeze/SKILL.md @@ -6,7 +6,7 @@ description: | Write outside the allowed path. Use when debugging to prevent accidentally "fixing" unrelated code, or when you want to scope changes to one module. Use when asked to "freeze", "restrict edits", "only edit this folder", - or "lock down edits". + or "lock down edits". (gstack) allowed-tools: - Bash - Read diff --git a/freeze/SKILL.md.tmpl b/freeze/SKILL.md.tmpl index b2b1de53c66ec723b731113056ef44c584b4053c..42329c41c1be15f31e97b424d7c31d7c1ba724b5 100644 --- a/freeze/SKILL.md.tmpl +++ b/freeze/SKILL.md.tmpl @@ -6,7 +6,7 @@ description: | Write outside the allowed path. Use when debugging to prevent accidentally "fixing" unrelated code, or when you want to scope changes to one module. Use when asked to "freeze", "restrict edits", "only edit this folder", - or "lock down edits". + or "lock down edits". (gstack) allowed-tools: - Bash - Read diff --git a/guard/SKILL.md b/guard/SKILL.md index f846d38a1be151738301fcf160d76cf6470e1a8e..289b4f939734c68a2e0bd24db4552c513b3ceced 100644 --- a/guard/SKILL.md +++ b/guard/SKILL.md @@ -6,7 +6,7 @@ description: | Combines /careful (warns before rm -rf, DROP TABLE, force-push, etc.) with /freeze (blocks edits outside a specified directory). Use for maximum safety when touching prod or debugging live systems. Use when asked to "guard mode", - "full safety", "lock it down", or "maximum safety". + "full safety", "lock it down", or "maximum safety". (gstack) allowed-tools: - Bash - Read diff --git a/guard/SKILL.md.tmpl b/guard/SKILL.md.tmpl index a96108fb239b371cc3ca76531b660ca29627915c..fe385c98c7eaa31a56fbdd8474551fa21f100494 100644 --- a/guard/SKILL.md.tmpl +++ b/guard/SKILL.md.tmpl @@ -6,7 +6,7 @@ description: | Combines /careful (warns before rm -rf, DROP TABLE, force-push, etc.) with /freeze (blocks edits outside a specified directory). Use for maximum safety when touching prod or debugging live systems. Use when asked to "guard mode", - "full safety", "lock it down", or "maximum safety". + "full safety", "lock it down", or "maximum safety". (gstack) allowed-tools: - Bash - Read diff --git a/investigate/SKILL.md b/investigate/SKILL.md index 365a9ca98f00ce6cbcbf589a48618cbac227dcab..565cc64048d2be2fd75bf84816ab4e9ea4da8cb0 100644 --- a/investigate/SKILL.md +++ b/investigate/SKILL.md @@ -8,7 +8,7 @@ description: | Use when asked to "debug this", "fix this bug", "why is this broken", "investigate this error", or "root cause analysis". Proactively suggest when the user reports errors, unexpected behavior, or - is troubleshooting why something stopped working. + is troubleshooting why something stopped working. (gstack) allowed-tools: - Bash - Read @@ -42,7 +42,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -64,7 +64,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"investigate","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"investigate","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -299,20 +301,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/investigate/SKILL.md.tmpl b/investigate/SKILL.md.tmpl index 8ba17fb8eff0e3c4da87f3e2ac0641a8693dea04..4da2a708f06b78d2851cdaec662ccb7b7a400d15 100644 --- a/investigate/SKILL.md.tmpl +++ b/investigate/SKILL.md.tmpl @@ -8,7 +8,7 @@ description: | Use when asked to "debug this", "fix this bug", "why is this broken", "investigate this error", or "root cause analysis". Proactively suggest when the user reports errors, unexpected behavior, or - is troubleshooting why something stopped working. + is troubleshooting why something stopped working. (gstack) allowed-tools: - Bash - Read diff --git a/land-and-deploy/SKILL.md b/land-and-deploy/SKILL.md index e36426f0f9c61043ab291928aade20b530fbe024..d568c509d2f4961bf6a3c93e48f6b60c1f66d06e 100644 --- a/land-and-deploy/SKILL.md +++ b/land-and-deploy/SKILL.md @@ -6,7 +6,7 @@ description: | Land and deploy workflow. Merges the PR, waits for CI and deploy, verifies production health via canary checks. Takes over after /ship creates the PR. Use when: "merge", "land", "deploy", "merge and verify", - "land it", "ship it to production". + "land it", "ship it to production". (gstack) allowed-tools: - Bash - Read @@ -25,7 +25,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -47,7 +47,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"land-and-deploy","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"land-and-deploy","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -300,20 +302,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/land-and-deploy/SKILL.md.tmpl b/land-and-deploy/SKILL.md.tmpl index a7ac546dc6f42b7bd68db1c60bd24a929bf3d1a1..9c01fc02bbd124a2e258f3ae04aa2e96ec938e1a 100644 --- a/land-and-deploy/SKILL.md.tmpl +++ b/land-and-deploy/SKILL.md.tmpl @@ -6,7 +6,7 @@ description: | Land and deploy workflow. Merges the PR, waits for CI and deploy, verifies production health via canary checks. Takes over after /ship creates the PR. Use when: "merge", "land", "deploy", "merge and verify", - "land it", "ship it to production". + "land it", "ship it to production". (gstack) allowed-tools: - Bash - Read diff --git a/learn/SKILL.md b/learn/SKILL.md index 254c7dcfbcb81ef15c90ec94bb1359d8a29051fa..67fa311e7fc2acf99d2720bc56d9a39a9c0d7cce 100644 --- a/learn/SKILL.md +++ b/learn/SKILL.md @@ -28,7 +28,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -50,7 +50,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"learn","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"learn","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -285,20 +287,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/office-hours/SKILL.md b/office-hours/SKILL.md index d624dc37747ba9964cd226e9b6d4a59db4512c3e..c5986b7ce91698fae2c2e9ac75a8a06def3f4d0c 100644 --- a/office-hours/SKILL.md +++ b/office-hours/SKILL.md @@ -11,7 +11,7 @@ description: | this", "office hours", or "is this worth building". Proactively suggest when the user describes a new product idea or is exploring whether something is worth building — before any code is written. - Use before /plan-ceo-review or /plan-eng-review. + Use before /plan-ceo-review or /plan-eng-review. (gstack) allowed-tools: - Bash - Read @@ -33,7 +33,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -55,7 +55,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"office-hours","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"office-hours","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -308,20 +310,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/office-hours/SKILL.md.tmpl b/office-hours/SKILL.md.tmpl index 358f8290021d48cc698a1b5174df69b111b89272..1e340cf97ca94d67a92bb3e692e24d944a294987 100644 --- a/office-hours/SKILL.md.tmpl +++ b/office-hours/SKILL.md.tmpl @@ -11,7 +11,7 @@ description: | this", "office hours", or "is this worth building". Proactively suggest when the user describes a new product idea or is exploring whether something is worth building — before any code is written. - Use before /plan-ceo-review or /plan-eng-review. + Use before /plan-ceo-review or /plan-eng-review. (gstack) allowed-tools: - Bash - Read diff --git a/package.json b/package.json index 037d0358aff5b2e18878965b0f6909f1aea371b9..750b20e851172c39f7e83344e10d600baaa6d5ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gstack", - "version": "0.13.6.0", + "version": "0.13.7.0", "description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.", "license": "MIT", "type": "module", diff --git a/plan-ceo-review/SKILL.md b/plan-ceo-review/SKILL.md index 0090752c3455438f9e0e0cc7673bd495cd40ecd2..40d03ef6ed3dbd3f741c4bfc992ad8ba4d5a0839 100644 --- a/plan-ceo-review/SKILL.md +++ b/plan-ceo-review/SKILL.md @@ -10,7 +10,7 @@ description: | Use when asked to "think bigger", "expand scope", "strategy review", "rethink this", or "is this ambitious enough". Proactively suggest when the user is questioning scope or ambition of a plan, - or when the plan feels like it could be thinking bigger. + or when the plan feels like it could be thinking bigger. (gstack) benefits-from: [office-hours] allowed-tools: - Read @@ -31,7 +31,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -53,7 +53,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"plan-ceo-review","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"plan-ceo-review","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -306,20 +308,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/plan-ceo-review/SKILL.md.tmpl b/plan-ceo-review/SKILL.md.tmpl index d32a076ee4a058e99572a75f3069bb37abf0f8f4..d0f7476428aee743c9efc6242b1c6e5549fc20b1 100644 --- a/plan-ceo-review/SKILL.md.tmpl +++ b/plan-ceo-review/SKILL.md.tmpl @@ -10,7 +10,7 @@ description: | Use when asked to "think bigger", "expand scope", "strategy review", "rethink this", or "is this ambitious enough". Proactively suggest when the user is questioning scope or ambition of a plan, - or when the plan feels like it could be thinking bigger. + or when the plan feels like it could be thinking bigger. (gstack) benefits-from: [office-hours] allowed-tools: - Read diff --git a/plan-design-review/SKILL.md b/plan-design-review/SKILL.md index a6be97823f9175998e1ddf7ccf92f84664c0e975..452537cbed756775b8a00a0ce66e5e56a03ab06e 100644 --- a/plan-design-review/SKILL.md +++ b/plan-design-review/SKILL.md @@ -9,7 +9,7 @@ description: | visual audits, use /design-review. Use when asked to "review the design plan" or "design critique". Proactively suggest when the user has a plan with UI/UX components that - should be reviewed before implementation. + should be reviewed before implementation. (gstack) allowed-tools: - Read - Edit @@ -29,7 +29,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -51,7 +51,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"plan-design-review","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"plan-design-review","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -304,20 +306,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/plan-design-review/SKILL.md.tmpl b/plan-design-review/SKILL.md.tmpl index cfafa6e6a08459511c09ebf3272e7d220b4d8eb1..2edfe379516d79bc97db005cb12bdce32590895d 100644 --- a/plan-design-review/SKILL.md.tmpl +++ b/plan-design-review/SKILL.md.tmpl @@ -9,7 +9,7 @@ description: | visual audits, use /design-review. Use when asked to "review the design plan" or "design critique". Proactively suggest when the user has a plan with UI/UX components that - should be reviewed before implementation. + should be reviewed before implementation. (gstack) allowed-tools: - Read - Edit diff --git a/plan-eng-review/SKILL.md b/plan-eng-review/SKILL.md index 52b6993d84ccfd152c74a486700f7ba270a0bb4f..109f6b2be1ca68ef936401e7f3e099949f852530 100644 --- a/plan-eng-review/SKILL.md +++ b/plan-eng-review/SKILL.md @@ -8,7 +8,7 @@ description: | issues interactively with opinionated recommendations. Use when asked to "review the architecture", "engineering review", or "lock in the plan". Proactively suggest when the user has a plan or design doc and is about to - start coding — to catch architecture issues before implementation. + start coding — to catch architecture issues before implementation. (gstack) benefits-from: [office-hours] allowed-tools: - Read @@ -30,7 +30,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -52,7 +52,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"plan-eng-review","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"plan-eng-review","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -305,20 +307,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer @@ -361,7 +365,7 @@ plan's living status. Review this plan thoroughly before making any code changes. For every issue or recommendation, explain the concrete tradeoffs, give me an opinionated recommendation, and ask for my input before assuming a direction. ## Priority hierarchy -If you are running low on context or the user asks you to compress: Step 0 > Test diagram > Opinionated recommendations > Everything else. Never skip Step 0 or the test diagram. +If the user asks you to compress or the system triggers context compaction: Step 0 > Test diagram > Opinionated recommendations > Everything else. Never skip Step 0 or the test diagram. Do not preemptively warn about context limits -- the system handles compaction automatically. ## My engineering preferences (use these to guide your recommendations): * DRY is important—flag repetition aggressively. diff --git a/plan-eng-review/SKILL.md.tmpl b/plan-eng-review/SKILL.md.tmpl index 415315a83b1411e2cc409f35ea1c7969db98dd69..f15fc7f5890b554c55a5e61b5d6e5e1a18e93e00 100644 --- a/plan-eng-review/SKILL.md.tmpl +++ b/plan-eng-review/SKILL.md.tmpl @@ -8,7 +8,7 @@ description: | issues interactively with opinionated recommendations. Use when asked to "review the architecture", "engineering review", or "lock in the plan". Proactively suggest when the user has a plan or design doc and is about to - start coding — to catch architecture issues before implementation. + start coding — to catch architecture issues before implementation. (gstack) benefits-from: [office-hours] allowed-tools: - Read @@ -27,7 +27,7 @@ allowed-tools: Review this plan thoroughly before making any code changes. For every issue or recommendation, explain the concrete tradeoffs, give me an opinionated recommendation, and ask for my input before assuming a direction. ## Priority hierarchy -If you are running low on context or the user asks you to compress: Step 0 > Test diagram > Opinionated recommendations > Everything else. Never skip Step 0 or the test diagram. +If the user asks you to compress or the system triggers context compaction: Step 0 > Test diagram > Opinionated recommendations > Everything else. Never skip Step 0 or the test diagram. Do not preemptively warn about context limits -- the system handles compaction automatically. ## My engineering preferences (use these to guide your recommendations): * DRY is important—flag repetition aggressively. diff --git a/qa-only/SKILL.md b/qa-only/SKILL.md index 3aa960427db6c4db0bc37242d24c07b4c48b047b..b882261416e88d3b3c9bc29b49ebf61c81ef7da4 100644 --- a/qa-only/SKILL.md +++ b/qa-only/SKILL.md @@ -7,7 +7,7 @@ description: | structured report with health score, screenshots, and repro steps — but never fixes anything. Use when asked to "just report bugs", "qa report only", or "test but don't fix". For the full test-fix-verify loop, use /qa instead. - Proactively suggest when the user wants a bug report without any code changes. + Proactively suggest when the user wants a bug report without any code changes. (gstack) allowed-tools: - Bash - Read @@ -26,7 +26,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -48,7 +48,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"qa-only","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"qa-only","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -301,20 +303,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/qa-only/SKILL.md.tmpl b/qa-only/SKILL.md.tmpl index 0bb59c0c05d3574b885ad0840808ff8b134e485e..d9fc965850a3bf04b6baea4a5f19000e3aa990c8 100644 --- a/qa-only/SKILL.md.tmpl +++ b/qa-only/SKILL.md.tmpl @@ -7,7 +7,7 @@ description: | structured report with health score, screenshots, and repro steps — but never fixes anything. Use when asked to "just report bugs", "qa report only", or "test but don't fix". For the full test-fix-verify loop, use /qa instead. - Proactively suggest when the user wants a bug report without any code changes. + Proactively suggest when the user wants a bug report without any code changes. (gstack) allowed-tools: - Bash - Read diff --git a/qa/SKILL.md b/qa/SKILL.md index 89d281e4fbc61f9057e49496f16b67b919d63127..f912077823dc4528d08ff0674c4c4845112bdcc4 100644 --- a/qa/SKILL.md +++ b/qa/SKILL.md @@ -10,7 +10,7 @@ description: | Proactively suggest when the user says a feature is ready for testing or asks "does this work?". Three tiers: Quick (critical/high only), Standard (+ medium), Exhaustive (+ cosmetic). Produces before/after health scores, - fix evidence, and a ship-readiness summary. For report-only mode, use /qa-only. + fix evidence, and a ship-readiness summary. For report-only mode, use /qa-only. (gstack) allowed-tools: - Bash - Read @@ -32,7 +32,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -54,7 +54,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"qa","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"qa","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -307,20 +309,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/qa/SKILL.md.tmpl b/qa/SKILL.md.tmpl index 0283ffc7ce85273b8fb8f8785039923546ac506e..20f70ef94c0750471f63764da9d94444b6e6a540 100644 --- a/qa/SKILL.md.tmpl +++ b/qa/SKILL.md.tmpl @@ -10,7 +10,7 @@ description: | Proactively suggest when the user says a feature is ready for testing or asks "does this work?". Three tiers: Quick (critical/high only), Standard (+ medium), Exhaustive (+ cosmetic). Produces before/after health scores, - fix evidence, and a ship-readiness summary. For report-only mode, use /qa-only. + fix evidence, and a ship-readiness summary. For report-only mode, use /qa-only. (gstack) allowed-tools: - Bash - Read diff --git a/retro/SKILL.md b/retro/SKILL.md index 41058c8c45493ec76118a59768509932e8aee31e..7f4511586c235e2fc01b5ea2a335699d0e6d4cd4 100644 --- a/retro/SKILL.md +++ b/retro/SKILL.md @@ -7,7 +7,7 @@ description: | and code quality metrics with persistent history and trend tracking. Team-aware: breaks down per-person contributions with praise and growth areas. Use when asked to "weekly retro", "what did we ship", or "engineering retrospective". - Proactively suggest at the end of a work week or sprint. + Proactively suggest at the end of a work week or sprint. (gstack) allowed-tools: - Bash - Read @@ -26,7 +26,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -48,7 +48,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"retro","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"retro","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -283,20 +285,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/retro/SKILL.md.tmpl b/retro/SKILL.md.tmpl index b1b44ee1a164e319a6948e4c0081d18dd35fb7de..5b201cf6640aadac0c2486eebd1019d79e319653 100644 --- a/retro/SKILL.md.tmpl +++ b/retro/SKILL.md.tmpl @@ -7,7 +7,7 @@ description: | and code quality metrics with persistent history and trend tracking. Team-aware: breaks down per-person contributions with praise and growth areas. Use when asked to "weekly retro", "what did we ship", or "engineering retrospective". - Proactively suggest at the end of a work week or sprint. + Proactively suggest at the end of a work week or sprint. (gstack) allowed-tools: - Bash - Read diff --git a/review/SKILL.md b/review/SKILL.md index 52560d7743a84d347d87dbdf90c6a33fcabbcce1..462123a66194572d2924128757517746fcf4c8b1 100644 --- a/review/SKILL.md +++ b/review/SKILL.md @@ -6,7 +6,7 @@ description: | Pre-landing PR review. Analyzes diff against the base branch for SQL safety, LLM trust boundary violations, conditional side effects, and other structural issues. Use when asked to "review this PR", "code review", "pre-landing review", or "check my diff". - Proactively suggest when the user is about to merge or land code changes. + Proactively suggest when the user is about to merge or land code changes. (gstack) allowed-tools: - Bash - Read @@ -29,7 +29,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -51,7 +51,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"review","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"review","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -304,20 +306,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/review/SKILL.md.tmpl b/review/SKILL.md.tmpl index fa14f26a77588afde927e4a34318e4f66ce1e467..b748483a09a16d947806ee430c9a56f4778ead81 100644 --- a/review/SKILL.md.tmpl +++ b/review/SKILL.md.tmpl @@ -6,7 +6,7 @@ description: | Pre-landing PR review. Analyzes diff against the base branch for SQL safety, LLM trust boundary violations, conditional side effects, and other structural issues. Use when asked to "review this PR", "code review", "pre-landing review", or "check my diff". - Proactively suggest when the user is about to merge or land code changes. + Proactively suggest when the user is about to merge or land code changes. (gstack) allowed-tools: - Bash - Read diff --git a/scripts/resolvers/preamble.ts b/scripts/resolvers/preamble.ts index aa0441a206cb81d38692f7021f4f92036da01fb1..cf88325a3b52e152f20040df202da18f8c50ffa5 100644 --- a/scripts/resolvers/preamble.ts +++ b/scripts/resolvers/preamble.ts @@ -8,8 +8,9 @@ import type { TemplateContext } from './types'; * repo mode detection, and telemetry. * * Telemetry data flow: - * 1. Always: local JSONL append to ~/.gstack/analytics/ (inline, inspectable) + * 1. If _TEL != "off": local JSONL append to ~/.gstack/analytics/ (inline, inspectable) * 2. If _TEL != "off" AND binary exists: gstack-telemetry-log for remote reporting + * When telemetry is off, nothing is written anywhere. Clean trust contract. */ function generatePreambleBash(ctx: TemplateContext): string { @@ -32,7 +33,7 @@ ${runtimeRoot}_UPD=$(${ctx.paths.binDir}/gstack-update-check 2>/dev/null || ${ct mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(${ctx.paths.binDir}/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(${ctx.paths.binDir}/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -54,7 +55,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: \${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"${ctx.skillName}","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 +if [ "\${_TEL:-off}" != "off" ]; then + echo '{"skill":"${ctx.skillName}","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -387,20 +390,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \\ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \\ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \\ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \\ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi \`\`\` Replace \`SKILL_NAME\` with the actual skill name from frontmatter, \`OUTCOME\` with success/error/abort, and \`USED_BROWSE\` with true/false based on whether \`$B\` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/setup b/setup index b9260713ff219255e4d57a697aeec1f1bc81a100..85d9567266c5288c448c39253416399b7bcb1681 100755 --- a/setup +++ b/setup @@ -46,6 +46,7 @@ esac # ─── Resolve skill prefix preference ───────────────────────── # Priority: CLI flag > saved config > interactive prompt (or flat default for non-TTY) GSTACK_CONFIG="$SOURCE_GSTACK_DIR/bin/gstack-config" +export GSTACK_SETUP_RUNNING=1 # Prevent gstack-config post-set hook from triggering relink mid-setup if [ "$SKILL_PREFIX_FLAG" -eq 0 ]; then _saved_prefix="$("$GSTACK_CONFIG" get skill_prefix 2>/dev/null || true)" if [ "$_saved_prefix" = "true" ]; then diff --git a/setup-browser-cookies/SKILL.md b/setup-browser-cookies/SKILL.md index 3272d6135831d380d5ec868b64b20073f205736a..824d2059dd3c7bb03755a3b729400c7c1bf7cac5 100644 --- a/setup-browser-cookies/SKILL.md +++ b/setup-browser-cookies/SKILL.md @@ -6,7 +6,7 @@ description: | Import cookies from your real Chromium browser into the headless browse session. Opens an interactive picker UI where you select which cookie domains to import. Use before QA testing authenticated pages. Use when asked to "import cookies", - "login to the site", or "authenticate the browser". + "login to the site", or "authenticate the browser". (gstack) allowed-tools: - Bash - Read @@ -23,7 +23,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -45,7 +45,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"setup-browser-cookies","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"setup-browser-cookies","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -215,20 +217,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/setup-browser-cookies/SKILL.md.tmpl b/setup-browser-cookies/SKILL.md.tmpl index 88b1f55330eec1517a41cf7814d7ca524c3236f2..f3b72b714d370779d5bb9e161c7d4b8e4c2282ee 100644 --- a/setup-browser-cookies/SKILL.md.tmpl +++ b/setup-browser-cookies/SKILL.md.tmpl @@ -6,7 +6,7 @@ description: | Import cookies from your real Chromium browser into the headless browse session. Opens an interactive picker UI where you select which cookie domains to import. Use before QA testing authenticated pages. Use when asked to "import cookies", - "login to the site", or "authenticate the browser". + "login to the site", or "authenticate the browser". (gstack) allowed-tools: - Bash - Read diff --git a/setup-deploy/SKILL.md b/setup-deploy/SKILL.md index 3c353e912125b2b7628e3e82f8d4428baa7e48bc..f0879c9667fb3c08b43d2b1f724929d8b8c03ba6 100644 --- a/setup-deploy/SKILL.md +++ b/setup-deploy/SKILL.md @@ -29,7 +29,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -51,7 +51,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"setup-deploy","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"setup-deploy","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -286,20 +288,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer diff --git a/ship/SKILL.md b/ship/SKILL.md index a4ff1bd2b507574ddd4e878b64851c3bbacd8234..4ce665fb050d639674d6f4b1f4e3c10b3b241209 100644 --- a/ship/SKILL.md +++ b/ship/SKILL.md @@ -3,8 +3,10 @@ name: ship preamble-tier: 4 version: 1.0.0 description: | - Ship workflow: detect + merge base branch, run tests, review diff, bump VERSION, update CHANGELOG, commit, push, create PR. Use when asked to "ship", "deploy", "push to main", "create a PR", or "merge and push". - Proactively suggest when the user says code is ready or asks about deploying. + Ship workflow: detect + merge base branch, run tests, review diff, bump VERSION, + update CHANGELOG, commit, push, create PR. Use when asked to "ship", "deploy", + "push to main", "create a PR", or "merge and push". + Proactively suggest when the user says code is ready or asks about deploying. (gstack) allowed-tools: - Bash - Read @@ -27,7 +29,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk mkdir -p ~/.gstack/sessions touch ~/.gstack/sessions/"$PPID" _SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') -find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true _CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true) _PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no") @@ -49,7 +51,9 @@ _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics -echo '{"skill":"ship","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 +if [ "${_TEL:-off}" != "off" ]; then + echo '{"skill":"ship","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 +fi # zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -302,20 +306,22 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -# Local analytics (always available, no binary needed) -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 -# Remote telemetry (opt-in, requires binary) -if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then - ~/.claude/skills/gstack/bin/gstack-telemetry-log \ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local + remote telemetry (both gated by _TEL setting) +if [ "$_TEL" != "off" ]; then + 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 + if [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & + fi fi ``` Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +If you cannot determine the outcome, use "unknown". Both local JSONL and remote +telemetry only run if telemetry is not off. The remote binary additionally requires +the binary to exist. ## Plan Status Footer @@ -1701,9 +1707,10 @@ already knows. A good test: would this insight save time in a future session? If 2. **Auto-decide the bump level based on the diff:** - Count lines changed (`git diff origin/...HEAD --stat | tail -1`) + - Check for feature signals: new route/page files (e.g. `app/*/page.tsx`, `pages/*.ts`), new DB migration/schema files, new test files alongside new source files, or branch name starting with `feat/` - **MICRO** (4th digit): < 50 lines changed, trivial tweaks, typos, config - - **PATCH** (3rd digit): 50+ lines changed, bug fixes, small-medium features - - **MINOR** (2nd digit): **ASK the user** — only for major features or significant architectural changes + - **PATCH** (3rd digit): 50+ lines changed, no feature signals detected + - **MINOR** (2nd digit): **ASK the user** if ANY feature signal is detected, OR 500+ lines changed, OR new modules/packages added - **MAJOR** (1st digit): **ASK the user** — only for milestones or breaking changes 3. Compute the new version: diff --git a/ship/SKILL.md.tmpl b/ship/SKILL.md.tmpl index 98e2d8eab610403c7aa64a9ef0feedd93fcfd31d..7c7f1b2b1ecdd21f0569b90b8c1c237a48a42245 100644 --- a/ship/SKILL.md.tmpl +++ b/ship/SKILL.md.tmpl @@ -3,8 +3,10 @@ name: ship preamble-tier: 4 version: 1.0.0 description: | - Ship workflow: detect + merge base branch, run tests, review diff, bump VERSION, update CHANGELOG, commit, push, create PR. Use when asked to "ship", "deploy", "push to main", "create a PR", or "merge and push". - Proactively suggest when the user says code is ready or asks about deploying. + Ship workflow: detect + merge base branch, run tests, review diff, bump VERSION, + update CHANGELOG, commit, push, create PR. Use when asked to "ship", "deploy", + "push to main", "create a PR", or "merge and push". + Proactively suggest when the user says code is ready or asks about deploying. (gstack) allowed-tools: - Bash - Read @@ -329,9 +331,10 @@ For each classified comment: 2. **Auto-decide the bump level based on the diff:** - Count lines changed (`git diff origin/...HEAD --stat | tail -1`) + - Check for feature signals: new route/page files (e.g. `app/*/page.tsx`, `pages/*.ts`), new DB migration/schema files, new test files alongside new source files, or branch name starting with `feat/` - **MICRO** (4th digit): < 50 lines changed, trivial tweaks, typos, config - - **PATCH** (3rd digit): 50+ lines changed, bug fixes, small-medium features - - **MINOR** (2nd digit): **ASK the user** — only for major features or significant architectural changes + - **PATCH** (3rd digit): 50+ lines changed, no feature signals detected + - **MINOR** (2nd digit): **ASK the user** if ANY feature signal is detected, OR 500+ lines changed, OR new modules/packages added - **MAJOR** (1st digit): **ASK the user** — only for milestones or breaking changes 3. Compute the new version: diff --git a/test/gen-skill-docs.test.ts b/test/gen-skill-docs.test.ts index 27672ede80f332521fd5e2ead3d3dbebac92f86f..21aebb2785f88ffd99183823175c3f5e9f55fb2a 100644 --- a/test/gen-skill-docs.test.ts +++ b/test/gen-skill-docs.test.ts @@ -2036,6 +2036,100 @@ describe('telemetry', () => { }); }); +describe('community fixes wave', () => { + // Helper to get all generated SKILL.md files + function getAllSkillMds(): Array<{ name: string; content: string }> { + const results: Array<{ name: string; content: string }> = []; + const rootPath = path.join(ROOT, 'SKILL.md'); + if (fs.existsSync(rootPath)) { + results.push({ name: 'root', content: fs.readFileSync(rootPath, 'utf-8') }); + } + for (const entry of fs.readdirSync(ROOT, { withFileTypes: true })) { + if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'node_modules') continue; + const skillPath = path.join(ROOT, entry.name, 'SKILL.md'); + if (fs.existsSync(skillPath)) { + results.push({ name: entry.name, content: fs.readFileSync(skillPath, 'utf-8') }); + } + } + return results; + } + + // #594 — Discoverability: every SKILL.md.tmpl description contains "gstack" + test('every SKILL.md.tmpl description contains "gstack"', () => { + for (const skill of ALL_SKILLS) { + const tmplPath = skill.dir === '.' ? path.join(ROOT, 'SKILL.md.tmpl') : path.join(ROOT, skill.dir, 'SKILL.md.tmpl'); + const content = fs.readFileSync(tmplPath, 'utf-8'); + const desc = extractDescription(content); + expect(desc.toLowerCase()).toContain('gstack'); + } + }); + + // #594 — Discoverability: first line of each description is under 120 chars + test('every SKILL.md.tmpl description first line is under 120 chars', () => { + for (const skill of ALL_SKILLS) { + const tmplPath = skill.dir === '.' ? path.join(ROOT, 'SKILL.md.tmpl') : path.join(ROOT, skill.dir, 'SKILL.md.tmpl'); + const content = fs.readFileSync(tmplPath, 'utf-8'); + const desc = extractDescription(content); + const firstLine = desc.split('\n')[0]; + expect(firstLine.length).toBeLessThanOrEqual(120); + } + }); + + // #573 — Feature signals: ship/SKILL.md contains feature signal detection + test('ship/SKILL.md contains feature signal detection in Step 4', () => { + const content = fs.readFileSync(path.join(ROOT, 'ship', 'SKILL.md'), 'utf-8'); + expect(content.toLowerCase()).toContain('feature signal'); + }); + + // #510 — Context warnings: no SKILL.md contains "running low on context" + test('no generated SKILL.md contains "running low on context"', () => { + const skills = getAllSkillMds(); + for (const { name, content } of skills) { + expect(content).not.toContain('running low on context'); + } + }); + + // #510 — Context warnings: plan-eng-review has explicit anti-warning + test('plan-eng-review/SKILL.md contains "Do not preemptively warn"', () => { + const content = fs.readFileSync(path.join(ROOT, 'plan-eng-review', 'SKILL.md'), 'utf-8'); + expect(content).toContain('Do not preemptively warn'); + }); + + // #474 — Safety Net: no SKILL.md uses find with -delete + test('no generated SKILL.md contains find with -delete flag', () => { + const skills = getAllSkillMds(); + for (const { name, content } of skills) { + // Match find commands that use -delete (but not prose mentioning the word "delete") + const lines = content.split('\n'); + for (const line of lines) { + if (line.includes('find ') && line.includes('-delete')) { + throw new Error(`${name}/SKILL.md contains find with -delete: ${line.trim()}`); + } + } + } + }); + + // #467 — Telemetry: preamble JSONL writes are gated by telemetry setting + test('preamble JSONL writes are inside telemetry conditional', () => { + const preamble = fs.readFileSync(path.join(ROOT, 'scripts/resolvers/preamble.ts'), 'utf-8'); + // Find all skill-usage.jsonl write lines + const lines = preamble.split('\n'); + for (let i = 0; i < lines.length; i++) { + if (lines[i].includes('skill-usage.jsonl') && lines[i].includes('>>')) { + // Look backwards for a telemetry conditional within 5 lines + let foundConditional = false; + for (let j = i - 1; j >= Math.max(0, i - 5); j--) { + if (lines[j].includes('_TEL') && lines[j].includes('off')) { + foundConditional = true; + break; + } + } + expect(foundConditional).toBe(true); + } + } + }); +}); + describe('codex commands must not use inline $(git rev-parse --show-toplevel) for cwd', () => { // Regression test: inline $(git rev-parse --show-toplevel) in codex exec -C // or codex review without cd evaluates in whatever cwd the background shell diff --git a/test/relink.test.ts b/test/relink.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..39af8891bf7f883647f0cb46824c82b291df6f6d --- /dev/null +++ b/test/relink.test.ts @@ -0,0 +1,152 @@ +import { describe, test, expect, beforeEach, afterEach } from 'bun:test'; +import { execSync } from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; + +const ROOT = path.resolve(import.meta.dir, '..'); +const BIN = path.join(ROOT, 'bin'); + +let tmpDir: string; +let skillsDir: string; +let installDir: string; + +function run(cmd: string, env: Record = {}, expectFail = false): string { + try { + return execSync(cmd, { + cwd: ROOT, + env: { ...process.env, GSTACK_STATE_DIR: tmpDir, ...env }, + encoding: 'utf-8', + timeout: 10000, + stdio: ['pipe', 'pipe', 'pipe'], + }).trim(); + } catch (e: any) { + if (expectFail) return (e.stderr || e.stdout || '').toString().trim(); + throw e; + } +} + +// Create a mock gstack install directory with skill subdirs +function setupMockInstall(skills: string[]): void { + installDir = path.join(tmpDir, 'gstack-install'); + skillsDir = path.join(tmpDir, 'skills'); + fs.mkdirSync(installDir, { recursive: true }); + fs.mkdirSync(skillsDir, { recursive: true }); + + // Copy the real gstack-config and gstack-relink to the mock install + const mockBin = path.join(installDir, 'bin'); + fs.mkdirSync(mockBin, { recursive: true }); + fs.copyFileSync(path.join(BIN, 'gstack-config'), path.join(mockBin, 'gstack-config')); + fs.chmodSync(path.join(mockBin, 'gstack-config'), 0o755); + if (fs.existsSync(path.join(BIN, 'gstack-relink'))) { + fs.copyFileSync(path.join(BIN, 'gstack-relink'), path.join(mockBin, 'gstack-relink')); + fs.chmodSync(path.join(mockBin, 'gstack-relink'), 0o755); + } + + // Create mock skill directories + for (const skill of skills) { + fs.mkdirSync(path.join(installDir, skill), { recursive: true }); + fs.writeFileSync(path.join(installDir, skill, 'SKILL.md'), `# ${skill}`); + } +} + +beforeEach(() => { + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-relink-test-')); +}); + +afterEach(() => { + fs.rmSync(tmpDir, { recursive: true, force: true }); +}); + +describe('gstack-relink (#578)', () => { + // Test 11: prefixed symlinks when skill_prefix=true + test('creates gstack-* symlinks when skill_prefix=true', () => { + setupMockInstall(['qa', 'ship', 'review']); + // Set config to prefix mode + run(`${path.join(installDir, 'bin', 'gstack-config')} set skill_prefix true`); + // Run relink with env pointing to the mock install + const output = run(`${path.join(installDir, 'bin', 'gstack-relink')}`, { + GSTACK_INSTALL_DIR: installDir, + GSTACK_SKILLS_DIR: skillsDir, + }); + // Verify gstack-* symlinks exist + expect(fs.existsSync(path.join(skillsDir, 'gstack-qa'))).toBe(true); + expect(fs.existsSync(path.join(skillsDir, 'gstack-ship'))).toBe(true); + expect(fs.existsSync(path.join(skillsDir, 'gstack-review'))).toBe(true); + expect(output).toContain('gstack-'); + }); + + // Test 12: flat symlinks when skill_prefix=false + test('creates flat symlinks when skill_prefix=false', () => { + setupMockInstall(['qa', 'ship', 'review']); + run(`${path.join(installDir, 'bin', 'gstack-config')} set skill_prefix false`); + const output = run(`${path.join(installDir, 'bin', 'gstack-relink')}`, { + GSTACK_INSTALL_DIR: installDir, + GSTACK_SKILLS_DIR: skillsDir, + }); + expect(fs.existsSync(path.join(skillsDir, 'qa'))).toBe(true); + expect(fs.existsSync(path.join(skillsDir, 'ship'))).toBe(true); + expect(fs.existsSync(path.join(skillsDir, 'review'))).toBe(true); + expect(output).toContain('flat'); + }); + + // Test 13: cleans stale symlinks from opposite mode + test('cleans up stale symlinks from opposite mode', () => { + setupMockInstall(['qa', 'ship']); + // Create prefixed symlinks first + run(`${path.join(installDir, 'bin', 'gstack-config')} set skill_prefix true`); + run(`${path.join(installDir, 'bin', 'gstack-relink')}`, { + GSTACK_INSTALL_DIR: installDir, + GSTACK_SKILLS_DIR: skillsDir, + }); + expect(fs.existsSync(path.join(skillsDir, 'gstack-qa'))).toBe(true); + + // Switch to flat mode + run(`${path.join(installDir, 'bin', 'gstack-config')} set skill_prefix false`); + run(`${path.join(installDir, 'bin', 'gstack-relink')}`, { + GSTACK_INSTALL_DIR: installDir, + GSTACK_SKILLS_DIR: skillsDir, + }); + + // Flat symlinks should exist, prefixed should be gone + expect(fs.existsSync(path.join(skillsDir, 'qa'))).toBe(true); + expect(fs.existsSync(path.join(skillsDir, 'gstack-qa'))).toBe(false); + }); + + // Test 14: error when install dir missing + test('prints error when install dir missing', () => { + const output = run(`${BIN}/gstack-relink`, { + GSTACK_INSTALL_DIR: '/nonexistent/path/gstack', + GSTACK_SKILLS_DIR: '/nonexistent/path/skills', + }, true); + expect(output).toContain('setup'); + }); + + // Test: gstack-upgrade does NOT get double-prefixed + test('does not double-prefix gstack-upgrade directory', () => { + setupMockInstall(['qa', 'ship', 'gstack-upgrade']); + run(`${path.join(installDir, 'bin', 'gstack-config')} set skill_prefix true`); + run(`${path.join(installDir, 'bin', 'gstack-relink')}`, { + GSTACK_INSTALL_DIR: installDir, + GSTACK_SKILLS_DIR: skillsDir, + }); + // gstack-upgrade should keep its name, NOT become gstack-gstack-upgrade + expect(fs.existsSync(path.join(skillsDir, 'gstack-upgrade'))).toBe(true); + expect(fs.existsSync(path.join(skillsDir, 'gstack-gstack-upgrade'))).toBe(false); + // Regular skills still get prefixed + expect(fs.existsSync(path.join(skillsDir, 'gstack-qa'))).toBe(true); + }); + + // Test 15: gstack-config set skill_prefix triggers relink + test('gstack-config set skill_prefix triggers relink', () => { + setupMockInstall(['qa', 'ship']); + // Run gstack-config set which should auto-trigger relink + run(`${path.join(installDir, 'bin', 'gstack-config')} set skill_prefix true`, { + GSTACK_INSTALL_DIR: installDir, + GSTACK_SKILLS_DIR: skillsDir, + }); + // If relink was triggered, symlinks should exist + expect(fs.existsSync(path.join(skillsDir, 'gstack-qa'))).toBe(true); + expect(fs.existsSync(path.join(skillsDir, 'gstack-ship'))).toBe(true); + }); +}); diff --git a/test/skill-validation.test.ts b/test/skill-validation.test.ts index 7bb163d84e0f934cd6d3f5337b90b177e685269e..46398d5aca11deedd0afbd92ae4ab0eac918fb18 100644 --- a/test/skill-validation.test.ts +++ b/test/skill-validation.test.ts @@ -1547,3 +1547,30 @@ describe('Test failure triage in ship skill', () => { expect(content).toContain('In-branch test failures'); }); }); + +describe('sidebar agent (#584)', () => { + // #584 — Sidebar Write: sidebar-agent.ts allowedTools includes Write + test('sidebar-agent.ts allowedTools includes Write', () => { + const content = fs.readFileSync(path.join(ROOT, 'browse', 'src', 'sidebar-agent.ts'), 'utf-8'); + // Find the allowedTools line in the askClaude function + const match = content.match(/--allowedTools['"]\s*,\s*['"]([^'"]+)['"]/); + expect(match).not.toBeNull(); + expect(match![1]).toContain('Write'); + }); + + // #584 — Server Write: server.ts allowedTools includes Write (DRY parity) + test('server.ts allowedTools includes Write', () => { + const content = fs.readFileSync(path.join(ROOT, 'browse', 'src', 'server.ts'), 'utf-8'); + // Find the sidebar allowedTools in the headed-mode path + const match = content.match(/--allowedTools['"]\s*,\s*['"]([^'"]+)['"]/); + expect(match).not.toBeNull(); + expect(match![1]).toContain('Write'); + }); + + // #584 — Sidebar stderr: stderr handler is not empty + test('sidebar-agent.ts stderr handler is not empty', () => { + const content = fs.readFileSync(path.join(ROOT, 'browse', 'src', 'sidebar-agent.ts'), 'utf-8'); + // The stderr handler should NOT be an empty arrow function + expect(content).not.toContain("proc.stderr.on('data', () => {})"); + }); +}); diff --git a/test/telemetry.test.ts b/test/telemetry.test.ts index dd63509f6d33f2a48d0f5c0b3a3116de330d888a..96bdf54c77a1d991f32cb45600be3f1a4da789f3 100644 --- a/test/telemetry.test.ts +++ b/test/telemetry.test.ts @@ -396,3 +396,25 @@ describe('gstack-community-dashboard', () => { expect(output).not.toContain('Supabase not configured'); }); }); + +describe('preamble telemetry gating (#467)', () => { + test('preamble source does not write JSONL unconditionally', () => { + const preamble = fs.readFileSync(path.join(ROOT, 'scripts', 'resolvers', 'preamble.ts'), 'utf-8'); + const lines = preamble.split('\n'); + for (let i = 0; i < lines.length; i++) { + if (lines[i].includes('skill-usage.jsonl') && lines[i].includes('>>')) { + // Each JSONL write must be inside a _TEL conditional (within 5 lines above) + let foundConditional = false; + for (let j = i - 1; j >= Math.max(0, i - 5); j--) { + if (lines[j].includes('_TEL') && lines[j].includes('off')) { + foundConditional = true; + break; + } + } + if (!foundConditional) { + throw new Error(`Unconditional JSONL write at preamble.ts line ${i + 1}: ${lines[i].trim()}`); + } + } + } + }); +}); diff --git a/unfreeze/SKILL.md b/unfreeze/SKILL.md index d4ad37e2d074abb8d5ae1895c149472d1b200eb2..0d265f0d1506a3da82533ca17a1b3529c889b504 100644 --- a/unfreeze/SKILL.md +++ b/unfreeze/SKILL.md @@ -5,7 +5,7 @@ description: | Clear the freeze boundary set by /freeze, allowing edits to all directories again. Use when you want to widen edit scope without ending the session. Use when asked to "unfreeze", "unlock edits", "remove freeze", or - "allow all edits". + "allow all edits". (gstack) allowed-tools: - Bash - Read diff --git a/unfreeze/SKILL.md.tmpl b/unfreeze/SKILL.md.tmpl index 074ba8052e38a4ce2c1af7bb9fd3fa2b6099ede0..c35d42393598be46c799545baf4c064dd8c2fc3a 100644 --- a/unfreeze/SKILL.md.tmpl +++ b/unfreeze/SKILL.md.tmpl @@ -5,7 +5,7 @@ description: | Clear the freeze boundary set by /freeze, allowing edits to all directories again. Use when you want to widen edit scope without ending the session. Use when asked to "unfreeze", "unlock edits", "remove freeze", or - "allow all edits". + "allow all edits". (gstack) allowed-tools: - Bash - Read