chore: regenerate SKILL.md files after preamble/epilogue changes

Regenerated via bun run gen:skill-docs. Preamble now persists TEL_START
and SESSION_ID to $PPID files + echoes them. Epilogue reads from files
and passes --source flag.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan 2026-03-23 15:48:30 -07:00
parent 6bd6d5ba0f
commit ea66b4d2f5
No known key found for this signature in database
GPG Key ID: C1F69E85C74EFE1D
23 changed files with 1771 additions and 437 deletions

View File

@ -46,8 +46,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"gstack","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -74,28 +84,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -104,6 +117,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -242,6 +282,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -250,17 +300,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -47,8 +47,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"autoplan","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -75,28 +85,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -105,6 +118,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -243,6 +283,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -251,17 +301,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -40,8 +40,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"benchmark","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -68,28 +78,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -98,6 +111,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -236,6 +276,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -244,17 +294,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -40,8 +40,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"browse","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -68,28 +78,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -98,6 +111,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -236,6 +276,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -244,17 +294,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -40,8 +40,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"canary","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -68,28 +78,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -98,6 +111,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -236,6 +276,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -244,17 +294,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -41,8 +41,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"codex","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -69,28 +79,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -99,6 +112,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -237,6 +277,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -245,17 +295,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -44,8 +44,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"cso","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -72,28 +82,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -102,6 +115,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -240,6 +280,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -248,17 +298,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -45,8 +45,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"design-consultation","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -73,28 +83,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -103,6 +116,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -241,6 +281,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -249,17 +299,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -45,8 +45,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"design-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -73,28 +83,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -103,6 +116,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -241,6 +281,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -249,17 +299,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -42,8 +42,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"document-release","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -70,28 +80,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -100,6 +113,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -238,6 +278,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -246,17 +296,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -56,8 +56,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"investigate","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -84,28 +94,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -114,6 +127,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -252,6 +292,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -260,17 +310,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -39,8 +39,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"land-and-deploy","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -67,28 +77,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -97,6 +110,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -235,6 +275,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -243,17 +293,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -47,8 +47,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"office-hours","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -75,28 +85,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -105,6 +118,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -243,6 +283,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -251,17 +301,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -45,8 +45,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"plan-ceo-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -73,28 +83,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -103,6 +116,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -241,6 +281,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -249,17 +299,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -43,8 +43,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"plan-design-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -71,28 +81,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -101,6 +114,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -239,6 +279,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -247,17 +297,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -44,8 +44,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"plan-eng-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -72,28 +82,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -102,6 +115,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -240,6 +280,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -248,17 +298,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -40,8 +40,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"qa-only","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -68,28 +78,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -98,6 +111,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -236,6 +276,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -244,17 +294,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -46,8 +46,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"qa","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -74,28 +84,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -104,6 +117,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -242,6 +282,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -250,17 +300,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -40,8 +40,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"retro","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -68,28 +78,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -98,6 +111,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -236,6 +276,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -244,17 +294,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -43,8 +43,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -71,28 +81,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -101,6 +114,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -239,6 +279,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -247,17 +297,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -37,8 +37,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"setup-browser-cookies","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -65,28 +75,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -95,6 +108,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -233,6 +273,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -241,17 +291,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -43,8 +43,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"setup-deploy","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -71,28 +81,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -101,6 +114,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -239,6 +279,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -247,17 +297,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer

View File

@ -41,8 +41,18 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || tr
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") _TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s) _TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)" _SESSION_ID="$$-$(date +%s)"
echo $_TEL_START > ~/.gstack/analytics/.tel-start-$PPID
echo $_SESSION_ID > ~/.gstack/analytics/.tel-session-$PPID
echo "TELEMETRY: ${_TEL:-off}" echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED" echo "TEL_PROMPTED: $_TEL_PROMPTED"
echo "TEL_START: $_TEL_START"
echo "SESSION_ID: $_SESSION_ID"
_EMAIL=$(~/.claude/skills/gstack/bin/gstack-config get email 2>/dev/null || true)
_COMM_PROMPTED=$([ -f ~/.gstack/.community-prompted ] && echo "yes" || echo "no")
_AUTH_OK=$(~/.claude/skills/gstack/bin/gstack-auth-refresh --check 2>/dev/null && echo "yes" || echo "no")
echo "EMAIL: ${_EMAIL:-none}"
echo "COMM_PROMPTED: $_COMM_PROMPTED"
echo "AUTH: $_AUTH_OK"
mkdir -p ~/.gstack/analytics mkdir -p ~/.gstack/analytics
echo '{"skill":"ship","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true 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
# zsh-compatible: use find instead of glob to avoid NOMATCH error # zsh-compatible: use find instead of glob to avoid NOMATCH error
@ -69,28 +79,31 @@ Only run `open` if the user says yes. Always run `touch` to mark as seen. This o
If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled,
ask the user about telemetry. Use AskUserQuestion: ask the user about telemetry. Use AskUserQuestion:
> Help gstack get better! Community mode shares usage data (which skills you use, how long > gstack can share usage data (which skills you use, how long they take, crash info)
> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. > to help improve the project. No code, file paths, or repo names are ever sent.
> No code, file paths, or repo names are ever sent. >
> The **community tier** unlocks extra features:
> - **Cloud backup** of your gstack config + history (restore on new machines)
> - **Benchmarks**: see how your usage compares to other builders
> - **Skill recommendations** based on community patterns
>
> Change anytime with `gstack-config set telemetry off`. > Change anytime with `gstack-config set telemetry off`.
Options: Options:
- A) Help gstack get better! (recommended) - A) Community — share data + email for backup, benchmarks & recommendations (recommended)
- B) No thanks - B) Anonymous — share data only, no account
- C) No thanks
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` If A: ask for their email via a follow-up AskUserQuestion, then run:
```bash
~/.claude/skills/gstack/bin/gstack-config set telemetry community
~/.claude/skills/gstack/bin/gstack-auth <user-provided-email>
```
The auth script will send a verification code to their email. Wait for them to enter the 6-digit code.
If auth succeeds, continue with the skill. If it fails, fall back to anonymous tier.
If B: ask a follow-up AskUserQuestion: If B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If C: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
> How about anonymous mode? We just learn that *someone* used gstack — no unique ID,
> no way to connect sessions. Just a counter that helps us know if anyone's out there.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
Always run: Always run:
```bash ```bash
@ -99,6 +112,33 @@ touch ~/.gstack/.telemetry-prompted
This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely.
If `TELEMETRY` is `anonymous` AND `COMM_PROMPTED` is `no`: After the main skill workflow
begins (not during preamble), offer the community tier upgrade once. Use AskUserQuestion:
> You're already sharing anonymous usage data — nice! Want to unlock more?
>
> The **community tier** adds:
> - Cloud backup of your gstack config (restore on new machines)
> - Benchmarks: see how your /qa times compare to the community
> - Skill recommendations based on what other builders use
>
> Just needs your email (verified via a one-time code).
Options:
- A) Yes, join community (enter email)
- B) Not now
If A: ask for their email, then run `~/.claude/skills/gstack/bin/gstack-auth <email>`.
Wait for the verification code. On success, run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`.
If B: do nothing.
Always run:
```bash
touch ~/.gstack/.community-prompted
```
This only happens once. If `COMM_PROMPTED` is `yes`, skip this entirely.
## AskUserQuestion Format ## AskUserQuestion Format
**ALWAYS follow this structure for every AskUserQuestion call:** **ALWAYS follow this structure for every AskUserQuestion call:**
@ -237,6 +277,16 @@ Determine the skill name from the `name:` field in this file's YAML frontmatter.
Determine the outcome from the workflow result (success if completed normally, error Determine the outcome from the workflow result (success if completed normally, error
if it failed, abort if the user interrupted). if it failed, abort if the user interrupted).
**For errors:** Also determine:
- `ERROR_CLASS`: a short category — one of: `timeout`, `test_failure`, `build_failure`,
`git_error`, `auth_error`, `network_error`, `browse_error`, `lint_error`,
`merge_conflict`, `permission_error`, `unknown_error`. Pick the most specific match.
- `ERROR_MESSAGE`: a one-line summary of what went wrong (max 200 chars). Include the
command that failed and the key error text. Example: `"bun test: 3 tests failed in
auth.test.ts — expected 200 got 401"`. Never include file paths, secrets, or PII.
- `FAILED_STEP`: which step in the skill workflow failed. Example: `"run_tests"`,
`"create_pr"`, `"merge_base"`, `"build"`, `"qa_browse"`. Use snake_case, max 30 chars.
**PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to
`~/.gstack/analytics/` (user config directory, not project files). The skill `~/.gstack/analytics/` (user config directory, not project files). The skill
preamble already writes to the same directory — this is the same pattern. preamble already writes to the same directory — this is the same pattern.
@ -245,17 +295,25 @@ Skipping this command loses session duration and outcome data.
Run this bash: Run this bash:
```bash ```bash
_TEL_START=$(cat ~/.gstack/analytics/.tel-start-$PPID 2>/dev/null || echo 0)
_SESSION_ID=$(cat ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || echo "")
_TEL_END=$(date +%s) _TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START )) _TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.tel-start-$PPID ~/.gstack/analytics/.tel-session-$PPID 2>/dev/null || true
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-telemetry-log \ ~/.claude/skills/gstack/bin/gstack-telemetry-log \
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \ --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & --source "${GSTACK_TELEMETRY_SOURCE:-live}" \
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" \
--error-class "ERROR_CLASS" --error-message "ERROR_MESSAGE" \
--failed-step "FAILED_STEP" 2>/dev/null &
``` ```
Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with
success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used.
If you cannot determine the outcome, use "unknown". This runs in the background and For `ERROR_CLASS`, `ERROR_MESSAGE`, and `FAILED_STEP`: use empty string `""` if the
outcome is not error. If the outcome is error but you cannot determine the details,
use `"unknown_error"`, `""`, and `""` respectively. This runs in the background and
never blocks the user. never blocks the user.
## Plan Status Footer ## Plan Status Footer