diff --git a/SKILL.md b/SKILL.md index 72bdb682..0cb3967b 100644 --- a/SKILL.md +++ b/SKILL.md @@ -30,6 +30,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"gstack","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true ``` If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. diff --git a/browse/SKILL.md b/browse/SKILL.md index 7d13f0a7..34a1fc31 100644 --- a/browse/SKILL.md +++ b/browse/SKILL.md @@ -31,6 +31,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"browse","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true ``` If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. diff --git a/design-consultation/SKILL.md b/design-consultation/SKILL.md index 277c762a..7f5683cd 100644 --- a/design-consultation/SKILL.md +++ b/design-consultation/SKILL.md @@ -34,6 +34,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"design-consultation","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true ``` If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. diff --git a/design-review/SKILL.md b/design-review/SKILL.md index 3726098b..22fb16d8 100644 --- a/design-review/SKILL.md +++ b/design-review/SKILL.md @@ -34,6 +34,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"design-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true ``` If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. diff --git a/document-release/SKILL.md b/document-release/SKILL.md index dacf3d13..9cb7ee5a 100644 --- a/document-release/SKILL.md +++ b/document-release/SKILL.md @@ -32,6 +32,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"document-release","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true ``` If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. diff --git a/plan-ceo-review/SKILL.md b/plan-ceo-review/SKILL.md index f40a0131..d668721f 100644 --- a/plan-ceo-review/SKILL.md +++ b/plan-ceo-review/SKILL.md @@ -32,6 +32,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"plan-ceo-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true ``` If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. diff --git a/plan-design-review/SKILL.md b/plan-design-review/SKILL.md index e8d49df3..5ea11636 100644 --- a/plan-design-review/SKILL.md +++ b/plan-design-review/SKILL.md @@ -32,6 +32,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"plan-design-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true ``` If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. diff --git a/plan-eng-review/SKILL.md b/plan-eng-review/SKILL.md index b780ad69..e85fef6f 100644 --- a/plan-eng-review/SKILL.md +++ b/plan-eng-review/SKILL.md @@ -31,6 +31,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"plan-eng-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true ``` If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. diff --git a/qa-only/SKILL.md b/qa-only/SKILL.md index 594979b9..a63e6b51 100644 --- a/qa-only/SKILL.md +++ b/qa-only/SKILL.md @@ -29,6 +29,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"qa-only","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true ``` If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. diff --git a/qa/SKILL.md b/qa/SKILL.md index 10e50715..f72eaa68 100644 --- a/qa/SKILL.md +++ b/qa/SKILL.md @@ -35,6 +35,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"qa","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true ``` If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. diff --git a/retro/SKILL.md b/retro/SKILL.md index c849dd8b..95596972 100644 --- a/retro/SKILL.md +++ b/retro/SKILL.md @@ -30,6 +30,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"retro","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true ``` If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. @@ -263,6 +265,14 @@ Include in the metrics table: If TODOS.md doesn't exist, skip the Backlog Health row. +**Skill Usage (if analytics exist):** Read `~/.gstack/analytics/skill-usage.jsonl` if it exists. Filter entries within the retro time window by `ts` field. Separate skill activations (no `event` field) from hook fires (`event: "hook_fire"`). Aggregate by skill name. Present as: + +``` +| Skill Usage | /ship(12) /qa(8) /review(5) · 3 safety hook fires | +``` + +If the JSONL file doesn't exist or has no entries in the window, skip the Skill Usage row. + ### Step 3: Commit Time Distribution Show hourly histogram in Pacific time using bar chart: diff --git a/review/SKILL.md b/review/SKILL.md index bfd29a7f..1909db05 100644 --- a/review/SKILL.md +++ b/review/SKILL.md @@ -31,6 +31,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true ``` If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. diff --git a/scripts/gen-skill-docs.ts b/scripts/gen-skill-docs.ts index 687143c0..6677b78b 100644 --- a/scripts/gen-skill-docs.ts +++ b/scripts/gen-skill-docs.ts @@ -17,9 +17,16 @@ import * as path from 'path'; const ROOT = path.resolve(import.meta.dir, '..'); const DRY_RUN = process.argv.includes('--dry-run'); +// ─── Template Context ─────────────────────────────────────── + +interface TemplateContext { + skillName: string; + tmplPath: string; +} + // ─── Placeholder Resolvers ────────────────────────────────── -function generateCommandReference(): string { +function generateCommandReference(_ctx: TemplateContext): string { // Group commands by category const groups = new Map>(); for (const [cmd, meta] of Object.entries(COMMAND_DESCRIPTIONS)) { @@ -55,7 +62,7 @@ function generateCommandReference(): string { return sections.join('\n').trimEnd(); } -function generateSnapshotFlags(): string { +function generateSnapshotFlags(_ctx: TemplateContext): string { const lines: string[] = [ 'The snapshot is your primary tool for understanding and interacting with pages.', '', @@ -94,7 +101,7 @@ function generateSnapshotFlags(): string { return lines.join('\n'); } -function generatePreamble(): string { +function generatePreamble(ctx: TemplateContext): string { return `## Preamble (run first) \`\`\`bash @@ -109,6 +116,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"${ctx.skillName}","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true \`\`\` If output shows \`UPGRADE_AVAILABLE \`: read \`~/.claude/skills/gstack/gstack-upgrade/SKILL.md\` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If \`JUST_UPGRADED \`: tell user "Running gstack v{to} (just updated!)" and continue. @@ -200,7 +209,7 @@ Hey gstack team — ran into this while using /{skill-name}: Slug: lowercase, hyphens, max 60 chars (e.g. \`browse-js-no-await\`). Skip if file already exists. Max 3 reports per session. File inline and continue — don't stop the workflow. Tell user: "Filed gstack field report: {title}"`; } -function generateBrowseSetup(): string { +function generateBrowseSetup(_ctx: TemplateContext): string { return `## SETUP (run this check BEFORE any browse command) \`\`\`bash @@ -221,7 +230,7 @@ If \`NEEDS_SETUP\`: 3. If \`bun\` is not installed: \`curl -fsSL https://bun.sh/install | bash\``; } -function generateBaseBranchDetect(): string { +function generateBaseBranchDetect(_ctx: TemplateContext): string { return `## Step 0: Detect base branch Determine which branch this PR targets. Use the result as "the base branch" in all subsequent steps. @@ -242,7 +251,7 @@ branch name wherever the instructions say "the base branch." ---`; } -function generateQAMethodology(): string { +function generateQAMethodology(_ctx: TemplateContext): string { return `## Modes ### Diff-aware (automatic when on a feature branch with no URL) @@ -519,7 +528,7 @@ Minimum 0 per category. 11. **Show screenshots to the user.** After every \`$B screenshot\`, \`$B snapshot -a -o\`, or \`$B responsive\` command, use the Read tool on the output file(s) so the user can see them inline. For \`responsive\` (3 files), Read all three. This is critical — without it, screenshots are invisible to the user.`; } -function generateDesignReviewLite(): string { +function generateDesignReviewLite(_ctx: TemplateContext): string { return `## Design Review (conditional, diff-scoped) Check if the diff touches frontend files using \`gstack-diff-scope\`: @@ -558,7 +567,7 @@ Substitute: TIMESTAMP = ISO 8601 datetime, STATUS = "clean" if 0 findings or "is // NOTE: design-checklist.md is a subset of this methodology for code-level detection. // When adding items here, also update review/design-checklist.md, and vice versa. -function generateDesignMethodology(): string { +function generateDesignMethodology(_ctx: TemplateContext): string { return `## Modes ### Full (default) @@ -892,7 +901,7 @@ Tie everything to user goals and product objectives. Always suggest specific imp 11. **Show screenshots to the user.** After every \`$B screenshot\`, \`$B snapshot -a -o\`, or \`$B responsive\` command, use the Read tool on the output file(s) so the user can see them inline. For \`responsive\` (3 files), Read all three. This is critical — without it, screenshots are invisible to the user.`; } -function generateReviewDashboard(): string { +function generateReviewDashboard(_ctx: TemplateContext): string { return `## Review Readiness Dashboard After completing the review, read the review log and config to display the dashboard. @@ -932,7 +941,7 @@ Parse the output. Find the most recent entry for each skill (plan-ceo-review, pl - If \\\`skip_eng_review\\\` config is \\\`true\\\`, Eng Review shows "SKIPPED (global)" and verdict is CLEARED`; } -function generateTestBootstrap(): string { +function generateTestBootstrap(_ctx: TemplateContext): string { return `## Test Framework Bootstrap **Detect existing test framework and project runtime:** @@ -1087,7 +1096,7 @@ Only commit if there are changes. Stage all bootstrap files (config, test direct ---`; } -const RESOLVERS: Record string> = { +const RESOLVERS: Record string> = { COMMAND_REFERENCE: generateCommandReference, SNAPSHOT_FLAGS: generateSnapshotFlags, PREAMBLE: generatePreamble, @@ -1109,11 +1118,16 @@ function processTemplate(tmplPath: string): { outputPath: string; content: strin const relTmplPath = path.relative(ROOT, tmplPath); const outputPath = tmplPath.replace(/\.tmpl$/, ''); + // Extract skill name from frontmatter for TemplateContext + const nameMatch = tmplContent.match(/^name:\s*(.+)$/m); + const skillName = nameMatch ? nameMatch[1].trim() : path.basename(path.dirname(tmplPath)); + const ctx: TemplateContext = { skillName, tmplPath }; + // Replace placeholders let content = tmplContent.replace(/\{\{(\w+)\}\}/g, (match, name) => { const resolver = RESOLVERS[name]; if (!resolver) throw new Error(`Unknown placeholder {{${name}}} in ${relTmplPath}`); - return resolver(); + return resolver(ctx); }); // Check for any remaining unresolved placeholders @@ -1155,6 +1169,10 @@ function findTemplates(): string[] { path.join(ROOT, 'design-review', 'SKILL.md.tmpl'), path.join(ROOT, 'design-consultation', 'SKILL.md.tmpl'), path.join(ROOT, 'document-release', 'SKILL.md.tmpl'), + path.join(ROOT, 'careful', 'SKILL.md.tmpl'), + path.join(ROOT, 'freeze', 'SKILL.md.tmpl'), + path.join(ROOT, 'guard', 'SKILL.md.tmpl'), + path.join(ROOT, 'unfreeze', 'SKILL.md.tmpl'), ]; for (const p of candidates) { if (fs.existsSync(p)) templates.push(p); diff --git a/setup-browser-cookies/SKILL.md b/setup-browser-cookies/SKILL.md index cf37fc82..772eef21 100644 --- a/setup-browser-cookies/SKILL.md +++ b/setup-browser-cookies/SKILL.md @@ -28,6 +28,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"setup-browser-cookies","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true ``` If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. diff --git a/ship/SKILL.md b/ship/SKILL.md index c59455d0..b1dddfad 100644 --- a/ship/SKILL.md +++ b/ship/SKILL.md @@ -30,6 +30,8 @@ _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" +mkdir -p ~/.gstack/analytics +echo '{"skill":"ship","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true ``` If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue.