From c43c850cae779bcd134b71a28eeec00628ba0792 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Tue, 2 Jun 2026 22:36:34 -0700 Subject: [PATCH] v1.55.1.0 fix: telemetry consent accuracy + gstack-slug cache sanitization (#1848) * fix(gstack-slug): sanitize cached slug before eval The compute and fallback paths filter slug output to [a-zA-Z0-9._-], but a value read straight from ~/.gstack/slug-cache was echoed into eval output unsanitized. A locally-planted cache file could inject shell into eval "$(gstack-slug)". Re-sanitize on every path so the invariant the file header promises actually holds, and heal a poisoned cache on the next write. Co-Authored-By: Claude Opus 4.8 (1M context) * fix(telemetry): accurate consent copy + JSON-safe repo basename The telemetry consent prompt promised "no repo names" while the preamble epilogue records the repo basename in the local skill-usage.jsonl. It is already stripped before any remote upload, so it never left the machine, but the copy was unqualified. Reword it to state repo name is local-only and stripped before upload. Also sanitize the basename to [a-zA-Z0-9._-] before it goes into the hand-built JSON, so a repo directory name containing quotes or newlines can neither break the JSON nor leak a fragment past the regex stripper. Co-Authored-By: Claude Opus 4.8 (1M context) * chore(docs): regenerate SKILL.md + ship goldens for telemetry change Generated output of the preceding resolver change: the corrected consent copy and sanitized repo basename now appear in every skill preamble. Golden ship fixtures refreshed to match. Co-Authored-By: Claude Opus 4.8 (1M context) * test(telemetry): enforce no-repo-identity-egress invariant Pins the contract that repo/branch identity in the synced skill-usage.jsonl is stripped before the remote POST. Three checks: a floor (the three known fields), coverage (every repo/branch field a producer writes into skill-usage.jsonl is stripped, so a future producer rename can't silently leak), and behavior (runs the actual sed strip expressions over a sample event). Scoped to the synced file, so the local-only timeline branch field is correctly excluded. Co-Authored-By: Claude Opus 4.8 (1M context) * test(gstack-slug): regression test for cached-slug eval injection Proves a poisoned ~/.gstack/slug-cache file cannot inject shell metacharacters into gstack-slug output (the value consumed by eval). Verified red when the cache-read sanitization is removed. Co-Authored-By: Claude Opus 4.8 (1M context) * chore: bump version and changelog (v1.55.1.0) Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 38 +++++ SKILL.md | 4 +- VERSION | 2 +- autoplan/SKILL.md | 4 +- benchmark-models/SKILL.md | 4 +- benchmark/SKILL.md | 4 +- bin/gstack-slug | 8 + browse/SKILL.md | 4 +- canary/SKILL.md | 4 +- codex/SKILL.md | 4 +- context-restore/SKILL.md | 4 +- context-save/SKILL.md | 4 +- cso/SKILL.md | 4 +- design-consultation/SKILL.md | 4 +- design-html/SKILL.md | 4 +- design-review/SKILL.md | 4 +- design-shotgun/SKILL.md | 4 +- devex-review/SKILL.md | 4 +- document-generate/SKILL.md | 4 +- document-release/SKILL.md | 4 +- health/SKILL.md | 4 +- investigate/SKILL.md | 4 +- ios-clean/SKILL.md | 4 +- ios-design-review/SKILL.md | 4 +- ios-fix/SKILL.md | 4 +- ios-qa/SKILL.md | 4 +- ios-sync/SKILL.md | 4 +- land-and-deploy/SKILL.md | 4 +- landing-report/SKILL.md | 4 +- learn/SKILL.md | 4 +- make-pdf/SKILL.md | 4 +- office-hours/SKILL.md | 4 +- open-gstack-browser/SKILL.md | 4 +- package.json | 2 +- pair-agent/SKILL.md | 4 +- plan-ceo-review/SKILL.md | 4 +- plan-design-review/SKILL.md | 4 +- plan-devex-review/SKILL.md | 4 +- plan-eng-review/SKILL.md | 4 +- plan-tune/SKILL.md | 4 +- qa-only/SKILL.md | 4 +- qa/SKILL.md | 4 +- retro/SKILL.md | 4 +- review/SKILL.md | 4 +- scrape/SKILL.md | 4 +- .../preamble/generate-preamble-bash.ts | 2 +- .../preamble/generate-telemetry-prompt.ts | 2 +- setup-browser-cookies/SKILL.md | 4 +- setup-deploy/SKILL.md | 4 +- setup-gbrain/SKILL.md | 4 +- ship/SKILL.md | 4 +- skillify/SKILL.md | 4 +- spec/SKILL.md | 8 +- sync-gbrain/SKILL.md | 4 +- test/fixtures/golden/claude-ship-SKILL.md | 4 +- test/fixtures/golden/codex-ship-SKILL.md | 4 +- test/fixtures/golden/factory-ship-SKILL.md | 4 +- test/gstack-slug-sanitize.test.ts | 65 +++++++++ test/telemetry-repo-strip.test.ts | 137 ++++++++++++++++++ 59 files changed, 356 insertions(+), 108 deletions(-) create mode 100644 test/gstack-slug-sanitize.test.ts create mode 100644 test/telemetry-repo-strip.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ef75b9b50..e9e2fbb1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ # Changelog +## [1.55.1.0] - 2026-06-02 + +## **Telemetry now tells you exactly what it records and where it stays. The project-slug helper hands the shell a safe identifier on every path.** + +The telemetry opt-in screen now states the truth without asterisks: it shares skill name, duration, crashes, and a stable device ID, with no code and no file paths, and your repo name is recorded locally only and stripped before any upload. Under the hood, the helper that every skill uses to find your project (`gstack-slug`) now filters its output to `[a-zA-Z0-9._-]` on every path, including the cached one, so the value that gets handed to the shell is always a plain identifier. Two regression tests lock both behaviors so they can't quietly drift back. + +### The guarantees that matter + +These are enforced by tests in this release, not promises (`bun test test/telemetry-repo-strip.test.ts test/gstack-slug-sanitize.test.ts`): + +| Guarantee | Pinned by | +|-----------|-----------| +| Your repo name never leaves the machine (stripped before upload) | `telemetry-repo-strip.test.ts` — floor + producer-coverage + runs the real strip over a sample event | +| A tampered slug cache can't put shell characters into the helper's output | `gstack-slug-sanitize.test.ts` — fails if the sanitization is removed | +| The consent copy matches what the code actually does | `generate-telemetry-prompt.ts` (regenerated into every skill) | + +The repo-identity test covers all three producer fields (`repo`, `_repo_slug`, `_branch`), so adding a new field that forgets to get stripped fails CI rather than shipping silently. + +### What this means for you + +Your telemetry choice screen now describes what actually happens, so you can opt in (or not) on accurate information. If you share a machine or have ever worried about a tampered `~/.gstack` cache, the slug helper now refuses to pass anything but a safe identifier to the shell. Nothing to do — both land automatically on upgrade. + +### Itemized changes + +#### Changed +- Telemetry consent copy is now accurate: "No code or file paths. Your repo name is recorded locally only and stripped before any upload" (was "No code, file paths, or repo names"). + +#### Fixed +- `gstack-slug` sanitizes its output to `[a-zA-Z0-9._-]` on every path, including values read from its on-disk cache, so `eval "$(gstack-slug)"` always receives a plain identifier. A tampered cache file is also healed on the next write. +- The telemetry preamble sanitizes the repo basename before building its JSON line, so an unusual repo directory name can't malform the local analytics record. + +#### Added +- `test/telemetry-repo-strip.test.ts` — enforces that no repo/branch identity field reaches the upload batch (floor + producer-coverage + real-strip behavior). +- `test/gstack-slug-sanitize.test.ts` — regression test proving a poisoned slug cache cannot inject shell metacharacters. + +#### For contributors +- The consent copy and repo-basename handling live in `scripts/resolvers/preamble/`; all `SKILL.md` files and the ship goldens were regenerated from those resolvers. + ## [1.55.0.0] - 2026-05-30 ## **`/sync-gbrain` can no longer be the trigger that lets gbrain delete your repo. The headed browser stops crash-looping, and gbrain installs the current release instead of a pin 23 versions stale.** diff --git a/SKILL.md b/SKILL.md index a35e923c6..60405f27d 100644 --- a/SKILL.md +++ b/SKILL.md @@ -60,7 +60,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"gstack","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -170,7 +170,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/VERSION b/VERSION index 7bd316a72..1e425c5dd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.55.0.0 +1.55.1.0 diff --git a/autoplan/SKILL.md b/autoplan/SKILL.md index f8c20cd59..57299734a 100644 --- a/autoplan/SKILL.md +++ b/autoplan/SKILL.md @@ -69,7 +69,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"autoplan","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -179,7 +179,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/benchmark-models/SKILL.md b/benchmark-models/SKILL.md index 38baa8851..c7b011d02 100644 --- a/benchmark-models/SKILL.md +++ b/benchmark-models/SKILL.md @@ -63,7 +63,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"benchmark-models","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 +echo '{"skill":"benchmark-models","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -173,7 +173,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/benchmark/SKILL.md b/benchmark/SKILL.md index d2b54a2c4..099b3bc61 100644 --- a/benchmark/SKILL.md +++ b/benchmark/SKILL.md @@ -63,7 +63,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"benchmark","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -173,7 +173,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/bin/gstack-slug b/bin/gstack-slug index 6b853b6d7..24bbca4f1 100755 --- a/bin/gstack-slug +++ b/bin/gstack-slug @@ -31,6 +31,14 @@ fi # 3. Fallback to basename only when there's truly no git remote configured SLUG="${SLUG:-$(basename "$PWD" | tr -cd 'a-zA-Z0-9._-')}" +# 3b. Re-sanitize unconditionally before the value is echoed into `eval`/`source` +# output. The compute (2) and fallback (3) paths already filter, but a value +# read straight from the cache file (1) does NOT — a poisoned +# ~/.gstack/slug-cache/ would otherwise inject shell into +# `eval "$(gstack-slug)"`. Filtering here honors the [a-zA-Z0-9._-] invariant +# promised in the header on every path, and heals a poisoned cache on write (4). +SLUG=$(printf '%s' "$SLUG" | tr -cd 'a-zA-Z0-9._-') + # 4. Cache the slug for future sessions (atomic write, fail silently) if [[ -n "$SLUG" ]]; then mkdir -p "$CACHE_DIR" 2>/dev/null || true diff --git a/browse/SKILL.md b/browse/SKILL.md index 9f73f0005..ef562d6c1 100644 --- a/browse/SKILL.md +++ b/browse/SKILL.md @@ -61,7 +61,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"browse","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -171,7 +171,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/canary/SKILL.md b/canary/SKILL.md index e7a1715f8..1626e194c 100644 --- a/canary/SKILL.md +++ b/canary/SKILL.md @@ -61,7 +61,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"canary","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -171,7 +171,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/codex/SKILL.md b/codex/SKILL.md index af351d7f1..111f1ebc8 100644 --- a/codex/SKILL.md +++ b/codex/SKILL.md @@ -64,7 +64,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"codex","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -174,7 +174,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/context-restore/SKILL.md b/context-restore/SKILL.md index 7a272722e..eadeb344b 100644 --- a/context-restore/SKILL.md +++ b/context-restore/SKILL.md @@ -65,7 +65,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"context-restore","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 +echo '{"skill":"context-restore","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -175,7 +175,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/context-save/SKILL.md b/context-save/SKILL.md index 014407fbe..9fa4338cb 100644 --- a/context-save/SKILL.md +++ b/context-save/SKILL.md @@ -64,7 +64,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"context-save","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 +echo '{"skill":"context-save","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -174,7 +174,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/cso/SKILL.md b/cso/SKILL.md index ebacf1ac0..8dc3bc9b5 100644 --- a/cso/SKILL.md +++ b/cso/SKILL.md @@ -67,7 +67,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"cso","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -177,7 +177,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/design-consultation/SKILL.md b/design-consultation/SKILL.md index 9bab21e2d..f733836b2 100644 --- a/design-consultation/SKILL.md +++ b/design-consultation/SKILL.md @@ -87,7 +87,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"design-consultation","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -197,7 +197,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/design-html/SKILL.md b/design-html/SKILL.md index f6e9e17f8..293d9e790 100644 --- a/design-html/SKILL.md +++ b/design-html/SKILL.md @@ -68,7 +68,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"design-html","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 +echo '{"skill":"design-html","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -178,7 +178,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/design-review/SKILL.md b/design-review/SKILL.md index e874a94aa..db3b27a25 100644 --- a/design-review/SKILL.md +++ b/design-review/SKILL.md @@ -65,7 +65,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"design-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -175,7 +175,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/design-shotgun/SKILL.md b/design-shotgun/SKILL.md index 9fd662ce6..09dbaa490 100644 --- a/design-shotgun/SKILL.md +++ b/design-shotgun/SKILL.md @@ -82,7 +82,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"design-shotgun","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -192,7 +192,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/devex-review/SKILL.md b/devex-review/SKILL.md index 14ed560d2..e8147eb49 100644 --- a/devex-review/SKILL.md +++ b/devex-review/SKILL.md @@ -67,7 +67,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"devex-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 +echo '{"skill":"devex-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -177,7 +177,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/document-generate/SKILL.md b/document-generate/SKILL.md index 2c7e6f072..96decbeec 100644 --- a/document-generate/SKILL.md +++ b/document-generate/SKILL.md @@ -67,7 +67,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"document-generate","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 +echo '{"skill":"document-generate","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -177,7 +177,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/document-release/SKILL.md b/document-release/SKILL.md index 43ba9adb1..2ec8d58f5 100644 --- a/document-release/SKILL.md +++ b/document-release/SKILL.md @@ -65,7 +65,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"document-release","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -175,7 +175,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/health/SKILL.md b/health/SKILL.md index 921a7b5b4..dbabb57f5 100644 --- a/health/SKILL.md +++ b/health/SKILL.md @@ -63,7 +63,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"health","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 +echo '{"skill":"health","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -173,7 +173,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/investigate/SKILL.md b/investigate/SKILL.md index daf6be6d8..23c1c098b 100644 --- a/investigate/SKILL.md +++ b/investigate/SKILL.md @@ -102,7 +102,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"investigate","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -212,7 +212,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/ios-clean/SKILL.md b/ios-clean/SKILL.md index c9073b6d5..894d968d2 100644 --- a/ios-clean/SKILL.md +++ b/ios-clean/SKILL.md @@ -65,7 +65,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"ios-clean","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 +echo '{"skill":"ios-clean","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -175,7 +175,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/ios-design-review/SKILL.md b/ios-design-review/SKILL.md index 7bfbdd851..d77ab8bad 100644 --- a/ios-design-review/SKILL.md +++ b/ios-design-review/SKILL.md @@ -67,7 +67,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"ios-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 +echo '{"skill":"ios-design-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -177,7 +177,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/ios-fix/SKILL.md b/ios-fix/SKILL.md index 2d1c3d4b1..35516ac86 100644 --- a/ios-fix/SKILL.md +++ b/ios-fix/SKILL.md @@ -68,7 +68,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"ios-fix","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 +echo '{"skill":"ios-fix","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -178,7 +178,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/ios-qa/SKILL.md b/ios-qa/SKILL.md index 0d40c16e5..a0d8cdca6 100644 --- a/ios-qa/SKILL.md +++ b/ios-qa/SKILL.md @@ -71,7 +71,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"ios-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 +echo '{"skill":"ios-qa","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -181,7 +181,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/ios-sync/SKILL.md b/ios-sync/SKILL.md index e7a803924..53c630edf 100644 --- a/ios-sync/SKILL.md +++ b/ios-sync/SKILL.md @@ -65,7 +65,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"ios-sync","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 +echo '{"skill":"ios-sync","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -175,7 +175,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/land-and-deploy/SKILL.md b/land-and-deploy/SKILL.md index 2eb9faa6c..7c6f9e8c8 100644 --- a/land-and-deploy/SKILL.md +++ b/land-and-deploy/SKILL.md @@ -60,7 +60,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"land-and-deploy","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -170,7 +170,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/landing-report/SKILL.md b/landing-report/SKILL.md index aec9978ba..ad70f6197 100644 --- a/landing-report/SKILL.md +++ b/landing-report/SKILL.md @@ -61,7 +61,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"landing-report","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 +echo '{"skill":"landing-report","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -171,7 +171,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/learn/SKILL.md b/learn/SKILL.md index 08a78b23c..a1892d60f 100644 --- a/learn/SKILL.md +++ b/learn/SKILL.md @@ -63,7 +63,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"learn","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -173,7 +173,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/make-pdf/SKILL.md b/make-pdf/SKILL.md index 141c60a31..d231010b3 100644 --- a/make-pdf/SKILL.md +++ b/make-pdf/SKILL.md @@ -62,7 +62,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"make-pdf","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 +echo '{"skill":"make-pdf","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -208,7 +208,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/office-hours/SKILL.md b/office-hours/SKILL.md index efa58f7de..31f3853d1 100644 --- a/office-hours/SKILL.md +++ b/office-hours/SKILL.md @@ -98,7 +98,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"office-hours","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -208,7 +208,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/open-gstack-browser/SKILL.md b/open-gstack-browser/SKILL.md index 64a93770e..b4f04033f 100644 --- a/open-gstack-browser/SKILL.md +++ b/open-gstack-browser/SKILL.md @@ -60,7 +60,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"open-gstack-browser","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 +echo '{"skill":"open-gstack-browser","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -170,7 +170,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/package.json b/package.json index 280299e0c..5be9c32bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gstack", - "version": "1.55.0.0", + "version": "1.55.1.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/pair-agent/SKILL.md b/pair-agent/SKILL.md index 533a29dc7..c5b1b129e 100644 --- a/pair-agent/SKILL.md +++ b/pair-agent/SKILL.md @@ -62,7 +62,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"pair-agent","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 +echo '{"skill":"pair-agent","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -172,7 +172,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/plan-ceo-review/SKILL.md b/plan-ceo-review/SKILL.md index 57cbf5464..ccf186ebd 100644 --- a/plan-ceo-review/SKILL.md +++ b/plan-ceo-review/SKILL.md @@ -92,7 +92,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"plan-ceo-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -202,7 +202,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/plan-design-review/SKILL.md b/plan-design-review/SKILL.md index b1b110ae1..d3cef28af 100644 --- a/plan-design-review/SKILL.md +++ b/plan-design-review/SKILL.md @@ -64,7 +64,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"plan-design-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -174,7 +174,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/plan-devex-review/SKILL.md b/plan-devex-review/SKILL.md index 7336b70a5..6cad1d4b2 100644 --- a/plan-devex-review/SKILL.md +++ b/plan-devex-review/SKILL.md @@ -70,7 +70,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"plan-devex-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 +echo '{"skill":"plan-devex-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -180,7 +180,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/plan-eng-review/SKILL.md b/plan-eng-review/SKILL.md index c4ec10bb6..836145857 100644 --- a/plan-eng-review/SKILL.md +++ b/plan-eng-review/SKILL.md @@ -68,7 +68,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"plan-eng-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -178,7 +178,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/plan-tune/SKILL.md b/plan-tune/SKILL.md index 41c45342e..922656244 100644 --- a/plan-tune/SKILL.md +++ b/plan-tune/SKILL.md @@ -73,7 +73,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"plan-tune","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 +echo '{"skill":"plan-tune","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -183,7 +183,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/qa-only/SKILL.md b/qa-only/SKILL.md index db1c3dd08..3e39337f3 100644 --- a/qa-only/SKILL.md +++ b/qa-only/SKILL.md @@ -63,7 +63,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"qa-only","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -173,7 +173,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/qa/SKILL.md b/qa/SKILL.md index c5fdf9b56..1a7a4f0a4 100644 --- a/qa/SKILL.md +++ b/qa/SKILL.md @@ -69,7 +69,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"qa","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -179,7 +179,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/retro/SKILL.md b/retro/SKILL.md index 287f24e35..1d87ce9c7 100644 --- a/retro/SKILL.md +++ b/retro/SKILL.md @@ -80,7 +80,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"retro","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -190,7 +190,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/review/SKILL.md b/review/SKILL.md index 4d8049d54..a2e1d00d6 100644 --- a/review/SKILL.md +++ b/review/SKILL.md @@ -65,7 +65,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -175,7 +175,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/scrape/SKILL.md b/scrape/SKILL.md index 0af5db506..6633ac63f 100644 --- a/scrape/SKILL.md +++ b/scrape/SKILL.md @@ -61,7 +61,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"scrape","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 +echo '{"skill":"scrape","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -171,7 +171,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/scripts/resolvers/preamble/generate-preamble-bash.ts b/scripts/resolvers/preamble/generate-preamble-bash.ts index 2bf5fc182..d8552421a 100644 --- a/scripts/resolvers/preamble/generate-preamble-bash.ts +++ b/scripts/resolvers/preamble/generate-preamble-bash.ts @@ -48,7 +48,7 @@ _QUESTION_TUNING=$(${ctx.paths.binDir}/gstack-config get question_tuning 2>/dev/ echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"${ctx.skillName}","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "\${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then diff --git a/scripts/resolvers/preamble/generate-telemetry-prompt.ts b/scripts/resolvers/preamble/generate-telemetry-prompt.ts index 2226a0452..a87652730 100644 --- a/scripts/resolvers/preamble/generate-telemetry-prompt.ts +++ b/scripts/resolvers/preamble/generate-telemetry-prompt.ts @@ -3,7 +3,7 @@ import type { TemplateContext } from '../types'; export function generateTelemetryPrompt(ctx: TemplateContext): string { return `If \`TEL_PROMPTED\` is \`no\` AND \`LAKE_INTRO\` is \`yes\`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/setup-browser-cookies/SKILL.md b/setup-browser-cookies/SKILL.md index 2f91f4d68..f1e7cb0fa 100644 --- a/setup-browser-cookies/SKILL.md +++ b/setup-browser-cookies/SKILL.md @@ -57,7 +57,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"setup-browser-cookies","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -167,7 +167,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/setup-deploy/SKILL.md b/setup-deploy/SKILL.md index a35ab9764..0eb71a643 100644 --- a/setup-deploy/SKILL.md +++ b/setup-deploy/SKILL.md @@ -64,7 +64,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"setup-deploy","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -174,7 +174,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/setup-gbrain/SKILL.md b/setup-gbrain/SKILL.md index cad27fcec..e2e0a4bfb 100644 --- a/setup-gbrain/SKILL.md +++ b/setup-gbrain/SKILL.md @@ -63,7 +63,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"setup-gbrain","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 +echo '{"skill":"setup-gbrain","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -173,7 +173,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/ship/SKILL.md b/ship/SKILL.md index ecf203787..afd853314 100644 --- a/ship/SKILL.md +++ b/ship/SKILL.md @@ -65,7 +65,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"ship","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -175,7 +175,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/skillify/SKILL.md b/skillify/SKILL.md index e7911473e..f0d96856a 100644 --- a/skillify/SKILL.md +++ b/skillify/SKILL.md @@ -61,7 +61,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"skillify","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 +echo '{"skill":"skillify","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -171,7 +171,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/spec/SKILL.md b/spec/SKILL.md index 7279b9c37..41930b954 100644 --- a/spec/SKILL.md +++ b/spec/SKILL.md @@ -62,7 +62,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"spec","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 +echo '{"skill":"spec","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -172,7 +172,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) @@ -1073,7 +1073,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"spec","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 +echo '{"skill":"spec","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -1183,7 +1183,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/sync-gbrain/SKILL.md b/sync-gbrain/SKILL.md index 4a3a5bc1d..c98d04814 100644 --- a/sync-gbrain/SKILL.md +++ b/sync-gbrain/SKILL.md @@ -63,7 +63,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then -echo '{"skill":"sync-gbrain","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 +echo '{"skill":"sync-gbrain","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -173,7 +173,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/test/fixtures/golden/claude-ship-SKILL.md b/test/fixtures/golden/claude-ship-SKILL.md index ecf203787..afd853314 100644 --- a/test/fixtures/golden/claude-ship-SKILL.md +++ b/test/fixtures/golden/claude-ship-SKILL.md @@ -65,7 +65,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"ship","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -175,7 +175,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/test/fixtures/golden/codex-ship-SKILL.md b/test/fixtures/golden/codex-ship-SKILL.md index 3636fff95..8107f7bcf 100644 --- a/test/fixtures/golden/codex-ship-SKILL.md +++ b/test/fixtures/golden/codex-ship-SKILL.md @@ -51,7 +51,7 @@ _QUESTION_TUNING=$($GSTACK_BIN/gstack-config get question_tuning 2>/dev/null || echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"ship","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -161,7 +161,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/test/fixtures/golden/factory-ship-SKILL.md b/test/fixtures/golden/factory-ship-SKILL.md index c654feefc..26635893a 100644 --- a/test/fixtures/golden/factory-ship-SKILL.md +++ b/test/fixtures/golden/factory-ship-SKILL.md @@ -53,7 +53,7 @@ _QUESTION_TUNING=$($GSTACK_BIN/gstack-config get question_tuning 2>/dev/null || echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "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 +echo '{"skill":"ship","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then @@ -163,7 +163,7 @@ Only run `open` if yes. Always run `touch`. If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code or file paths. Your repo name is recorded locally only and stripped before any upload. Options: - A) Help gstack get better! (recommended) diff --git a/test/gstack-slug-sanitize.test.ts b/test/gstack-slug-sanitize.test.ts new file mode 100644 index 000000000..7c5dc8f7d --- /dev/null +++ b/test/gstack-slug-sanitize.test.ts @@ -0,0 +1,65 @@ +/** + * gstack-slug cache-read sanitization. + * + * `eval "$(gstack-slug)"` is how callers load SLUG/BRANCH. The compute and + * fallback paths filter to [a-zA-Z0-9._-], but a value read straight from the + * cache file used to be echoed unsanitized — a planted cache file could inject + * shell. This pins the fix: a poisoned cache must never produce shell + * metacharacters in the SLUG= output line. + */ + +import { describe, test, expect } from 'bun:test'; +import { spawnSync } from 'bun'; +import fs from 'fs'; +import os from 'os'; +import path from 'path'; + +const ROOT = path.resolve(__dirname, '..'); +const SLUG_BIN = path.join(ROOT, 'bin', 'gstack-slug'); + +/** Reproduce the script's cache-key derivation: absolute path with / -> _. */ +function cacheKeyFor(dir: string): string { + return dir.replace(/\//g, '_'); +} + +function runSlug(cwd: string, home: string) { + return spawnSync([SLUG_BIN], { + cwd, + env: { ...process.env, HOME: home }, + }); +} + +describe('gstack-slug cache-read sanitization', () => { + test('a poisoned cache file cannot inject shell metacharacters into output', () => { + const home = fs.mkdtempSync(path.join(os.tmpdir(), 'gslug-home-')); + const proj = fs.mkdtempSync(path.join(os.tmpdir(), 'gslug-proj-')); + try { + const cacheDir = path.join(home, '.gstack', 'slug-cache'); + fs.mkdirSync(cacheDir, { recursive: true }); + // realpath: macOS tmpdir is a symlink (/var -> /private/var); the script + // runs in the resolved cwd, so key off the resolved path. + const realProj = fs.realpathSync(proj); + const payload = 'evil"; touch ' + path.join(home, 'pwned') + '; echo "x'; + fs.writeFileSync(path.join(cacheDir, cacheKeyFor(realProj)), payload); + + const out = runSlug(realProj, home); + const stdout = out.stdout.toString(); + + const slugLine = stdout.split('\n').find((l) => l.startsWith('SLUG=')); + expect(slugLine).toBeDefined(); + const slugValue = slugLine!.slice('SLUG='.length); + + // The value must be sanitized: only [a-zA-Z0-9._-], no quotes/semicolons/spaces. + expect(slugValue).toMatch(/^[a-zA-Z0-9._-]*$/); + expect(slugLine).not.toContain('"'); + expect(slugLine).not.toContain(';'); + expect(slugLine).not.toContain(' '); + + // And the injection must not have fired during the script's own run. + expect(fs.existsSync(path.join(home, 'pwned'))).toBe(false); + } finally { + fs.rmSync(home, { recursive: true, force: true }); + fs.rmSync(proj, { recursive: true, force: true }); + } + }); +}); diff --git a/test/telemetry-repo-strip.test.ts b/test/telemetry-repo-strip.test.ts new file mode 100644 index 000000000..31ba9186a --- /dev/null +++ b/test/telemetry-repo-strip.test.ts @@ -0,0 +1,137 @@ +/** + * Telemetry "no repo identity egress" invariant. + * + * The telemetry consent copy promises a user's repo name is recorded locally + * only and stripped before any upload (scripts/resolvers/preamble/ + * generate-telemetry-prompt.ts). Two producers write repo/branch identity into + * the local skill-usage.jsonl: + * + * - the preamble epilogue → "repo" + * (scripts/resolvers/preamble/generate-preamble-bash.ts) + * - gstack-telemetry-log → "_repo_slug", "_branch" + * (bin/gstack-telemetry-log) + * + * gstack-telemetry-sync MUST strip every one of those fields before the remote + * POST (bin/gstack-telemetry-sync). This test enforces that contract three ways: + * + * 1. Coverage — every repo/branch field the producers emit is also stripped. + * Catches "added a new repo field, forgot to strip it" (the rename-to-_repo + * landmine, or any future producer drift). + * 2. Behavior — run the ACTUAL sed strip expressions from the sync script over + * a sample event line and assert no repo/branch field survives, while benign + * fields do. Catches a broken/edited regex, not just a missing line. + * 3. Floor — the three known fields are always in the stripped set, so deleting + * a strip rule fails CI even if a producer also stops emitting it. + */ + +import { describe, test, expect } from 'bun:test'; +import { spawnSync } from 'bun'; +import fs from 'fs'; +import path from 'path'; + +const ROOT = path.resolve(__dirname, '..'); +const SYNC = path.join(ROOT, 'bin', 'gstack-telemetry-sync'); +const PREAMBLE = path.join(ROOT, 'scripts', 'resolvers', 'preamble', 'generate-preamble-bash.ts'); +const TEL_LOG = path.join(ROOT, 'bin', 'gstack-telemetry-log'); + +// Fields that identify the user's repo/branch. The promise is that NONE of +// these reach the network. Add to this floor if a new identity field is born. +const REPO_IDENTITY_FLOOR = ['repo', '_repo_slug', '_branch']; + +const isRepoIdentity = (field: string) => /repo|branch/i.test(field); + +/** Pull every `sed -e 's/.../g'` expression out of the sync script. */ +function extractSedExprs(scriptText: string): string[] { + return [...scriptText.matchAll(/-e\s+'(s\/[^']*)'/g)].map((m) => m[1]); +} + +/** The JSON key a strip expression targets, e.g. `,"repo":"[^"]*"` -> `repo`. */ +function fieldFromSedExpr(expr: string): string | null { + const m = expr.match(/,"([A-Za-z_][A-Za-z0-9_]*)":/); + return m ? m[1] : null; +} + +/** + * Repo/branch JSON keys a producer writes INTO skill-usage.jsonl — the only + * file gstack-telemetry-sync reads and uploads. Scoped to the emission lines + * that target the synced file so local-only sinks (e.g. the timeline log, which + * carries "branch" but is never synced) don't count against the egress invariant. + */ +function emittedRepoFields(lines: string[]): string[] { + const text = lines.join('\n'); + const keys = [...text.matchAll(/"([A-Za-z_][A-Za-z0-9_]*)":/g)].map((m) => m[1]); + return [...new Set(keys.filter(isRepoIdentity))]; +} + +describe('telemetry no-repo-identity-egress invariant', () => { + const syncText = fs.readFileSync(SYNC, 'utf-8'); + const sedExprs = extractSedExprs(syncText); + const strippedRepoExprs = sedExprs.filter((e) => { + const f = fieldFromSedExpr(e); + return f !== null && isRepoIdentity(f); + }); + const strippedFields = new Set( + strippedRepoExprs.map(fieldFromSedExpr).filter((f): f is string => f !== null), + ); + + test('floor: the three known repo-identity fields are stripped', () => { + for (const field of REPO_IDENTITY_FLOOR) { + expect(strippedFields.has(field)).toBe(true); + } + }); + + test('coverage: every repo/branch field the producers emit into skill-usage.jsonl is stripped', () => { + // Only emission lines that target the synced file (skill-usage.jsonl). The + // preamble appends directly; gstack-telemetry-log builds the synced event + // with a `printf '{"v":1,...` line into $JSONL_FILE (= skill-usage.jsonl). + const preambleSynced = fs + .readFileSync(PREAMBLE, 'utf-8') + .split('\n') + .filter((l) => l.includes('skill-usage.jsonl')); + const telLogSynced = fs + .readFileSync(TEL_LOG, 'utf-8') + .split('\n') + .filter((l) => l.includes('"v":1') || l.includes('skill-usage')); + const emitted = new Set([ + ...emittedRepoFields(preambleSynced), + ...emittedRepoFields(telLogSynced), + ]); + // The preamble must emit "repo" — guards against the test silently passing + // because a regex stopped matching the producer. + expect(emitted.has('repo')).toBe(true); + for (const field of emitted) { + expect( + strippedFields.has(field), + `producer emits repo-identity field "${field}" but gstack-telemetry-sync does not strip it (would leak to remote)`, + ).toBe(true); + } + }); + + test('behavior: the real sed expressions remove repo identity, keep benign fields', () => { + const sample = + '{"v":1,"ts":"2026-06-02T00:00:00Z","skill":"design-shotgun",' + + '"repo":"my-secret-repo","_repo_slug":"acme-my-secret-repo","_branch":"feature-x",' + + '"sessions":3,"installation_id":"abc123"}'; + + const sedArgs: string[] = []; + for (const e of strippedRepoExprs) { + sedArgs.push('-e', e); + } + const out = spawnSync(['sed', ...sedArgs], { + stdin: Buffer.from(sample), + }); + const cleaned = out.stdout.toString(); + + // No repo/branch identity survives, value or key. + expect(cleaned).not.toContain('my-secret-repo'); + expect(cleaned).not.toContain('feature-x'); + expect(cleaned).not.toContain('"repo"'); + expect(cleaned).not.toContain('_repo_slug'); + expect(cleaned).not.toContain('_branch'); + + // Benign fields are untouched — the strip is surgical, not a blanket wipe. + expect(cleaned).toContain('"skill":"design-shotgun"'); + expect(cleaned).toContain('"sessions":3'); + expect(cleaned).toContain('"ts":"2026-06-02T00:00:00Z"'); + }); +});