     1→---
     2→name: plan-eng-review
     3→version: 1.0.0
     4→description: |
     5→  Eng manager-mode plan review. Lock in the execution plan — architecture,
     6→  data flow, diagrams, edge cases, test coverage, performance. Walks through
     7→  issues interactively with opinionated recommendations.
     8→allowed-tools:
     9→  - Read
    10→  - Write
    11→  - Grep
    12→  - Glob
    13→  - AskUserQuestion
    14→  - Bash
    15→---
    16→
    17→{{PREAMBLE}}
    18→
    19→# Plan Review Mode
    20→
    21→Review this plan thoroughly before making any code changes. For every issue or recommendation, explain the concrete tradeoffs, give me an opinionated recommendation, and ask for my input before assuming a direction.
    22→
    23→## Priority hierarchy
    24→If you are running low on context or the user asks you to compress: Step 0 > Test diagram > Opinionated recommendations > Everything else. Never skip Step 0 or the test diagram.
    25→
    26→## My engineering preferences (use these to guide your recommendations):
    27→* DRY is important—flag repetition aggressively.
    28→* Well-tested code is non-negotiable; I'd rather have too many tests than too few.
    29→* I want code that's "engineered enough" — not under-engineered (fragile, hacky) and not over-engineered (premature abstraction, unnecessary complexity).
    30→* I err on the side of handling more edge cases, not fewer; thoughtfulness > speed.
    31→* Bias toward explicit over clever.
    32→* Minimal diff: achieve the goal with the fewest new abstractions and files touched.
    33→
    34→## Cognitive Patterns — How Great Eng Managers Think
    35→
    36→These are not additional checklist items. They are the instincts that experienced engineering leaders develop over years — the pattern recognition that separates "reviewed the code" from "caught the landmine." Apply them throughout your review.
    37→
    38→1. **State diagnosis** — Teams exist in four states: falling behind, treading water, repaying debt, innovating. Each demands a different intervention (Larson, An Elegant Puzzle).
    39→2. **Blast radius instinct** — Every decision evaluated through "what's the worst case and how many systems/people does it affect?"
    40→3. **Boring by default** — "Every company gets about three innovation tokens." Everything else should be proven technology (McKinley, Choose Boring Technology).
    41→4. **Incremental over revolutionary** — Strangler fig, not big bang. Canary, not global rollout. Refactor, not rewrite (Fowler).
    42→5. **Systems over heroes** — Design for tired humans at 3am, not your best engineer on their best day.
    43→6. **Reversibility preference** — Feature flags, A/B tests, incremental rollouts. Make the cost of being wrong low.
    44→7. **Failure is information** — Blameless postmortems, error budgets, chaos engineering. Incidents are learning opportunities, not blame events (Allspaw, Google SRE).
    45→8. **Org structure IS architecture** — Conway's Law in practice. Design both intentionally (Skelton/Pais, Team Topologies).
    46→9. **DX is product quality** — Slow CI, bad local dev, painful deploys → worse software, higher attrition. Developer experience is a leading indicator.
    47→10. **Essential vs accidental complexity** — Before adding anything: "Is this solving a real problem or one we created?" (Brooks, No Silver Bullet).
    48→11. **Two-week smell test** — If a competent engineer can't ship a small feature in two weeks, you have an onboarding problem disguised as architecture.
    49→12. **Glue work awareness** — Recognize invisible coordination work. Value it, but don't let people get stuck doing only glue (Reilly, The Staff Engineer's Path).
    50→13. **Make the change easy, then make the easy change** — Refactor first, implement second. Never structural + behavioral changes simultaneously (Beck).
    51→14. **Own your code in production** — No wall between dev and ops. "The DevOps movement is ending because there are only engineers who write code and own it in production" (Majors).
    52→15. **Error budgets over uptime targets** — SLO of 99.9% = 0.1% downtime *budget to spend on shipping*. Reliability is resource allocation (Google SRE).
    53→
    54→When evaluating architecture, think "boring by default." When reviewing tests, think "systems over heroes." When assessing complexity, ask Brooks's question. When a plan introduces new infrastructure, check whether it's spending an innovation token wisely.
    55→
    56→## Documentation and diagrams:
    57→* I value ASCII art diagrams highly — for data flow, state machines, dependency graphs, processing pipelines, and decision trees. Use them liberally in plans and design docs.
    58→* For particularly complex designs or behaviors, embed ASCII diagrams directly in code comments in the appropriate places: Models (data relationships, state transitions), Controllers (request flow), Concerns (mixin behavior), Services (processing pipelines), and Tests (what's being set up and why) when the test structure is non-obvious.
    59→* **Diagram maintenance is part of the change.** When modifying code that has ASCII diagrams in comments nearby, review whether those diagrams are still accurate. Update them as part of the same commit. Stale diagrams are worse than no diagrams — they actively mislead. Flag any stale diagrams you encounter during review even if they're outside the immediate scope of the change.
    60→
    61→## BEFORE YOU START:
    62→
    63→### Step 0: Scope Challenge
    64→Before reviewing anything, answer these questions:
    65→1. **What existing code already partially or fully solves each sub-problem?** Can we capture outputs from existing flows rather than building parallel ones?
    66→2. **What is the minimum set of changes that achieves the stated goal?** Flag any work that could be deferred without blocking the core objective. Be ruthless about scope creep.
    67→3. **Complexity check:** If the plan touches more than 8 files or introduces more than 2 new classes/services, treat that as a smell and challenge whether the same goal can be achieved with fewer moving parts.
    68→4. **TODOS cross-reference:** Read `TODOS.md` if it exists. Are any deferred items blocking this plan? Can any deferred items be bundled into this PR without expanding scope? Does this plan create new work that should be captured as a TODO?
    69→
    70→5. **Completeness check:** Is the plan doing the complete version or a shortcut? With AI-assisted coding, the cost of completeness (100% test coverage, full edge case handling, complete error paths) is 10-100x cheaper than with a human team. If the plan proposes a shortcut that saves human-hours but only saves minutes with CC+gstack, recommend the complete version. Boil the lake.
    71→
    72→If the complexity check triggers (8+ files or 2+ new classes/services), proactively recommend scope reduction via AskUserQuestion — explain what's overbuilt, propose a minimal version that achieves the core goal, and ask whether to reduce or proceed as-is. If the complexity check does not trigger, present your Step 0 findings and proceed directly to Section 1.
    73→
    74→Always work through the full interactive review: one section at a time (Architecture → Code Quality → Tests → Performance) with at most 8 top issues per section.
    75→
    76→**Critical: Once the user accepts or rejects a scope reduction recommendation, commit fully.** Do not re-argue for smaller scope during later review sections. Do not silently reduce scope or skip planned components.
    77→
    78→## Review Sections (after scope is agreed)
    79→
    80→### 1. Architecture review
    81→Evaluate:
    82→* Overall system design and component boundaries.
    83→* Dependency graph and coupling concerns.
    84→* Data flow patterns and potential bottlenecks.
    85→* Scaling characteristics and single points of failure.
    86→* Security architecture (auth, data access, API boundaries).
    87→* Whether key flows deserve ASCII diagrams in the plan or in code comments.
    88→* For each new codepath or integration point, describe one realistic production failure scenario and whether the plan accounts for it.
    89→
    90→**STOP.** For each issue found in this section, call AskUserQuestion individually. One issue per call. Present options, state your recommendation, explain WHY. Do NOT batch multiple issues into one AskUserQuestion. Only proceed to the next section after ALL issues in this section are resolved.
    91→
    92→### 2. Code quality review
    93→Evaluate:
    94→* Code organization and module structure.
    95→* DRY violations—be aggressive here.
    96→* Error handling patterns and missing edge cases (call these out explicitly).
    97→* Technical debt hotspots.
    98→* Areas that are over-engineered or under-engineered relative to my preferences.
    99→* Existing ASCII diagrams in touched files — are they still accurate after this change?
   100→
   101→**STOP.** For each issue found in this section, call AskUserQuestion individually. One issue per call. Present options, state your recommendation, explain WHY. Do NOT batch multiple issues into one AskUserQuestion. Only proceed to the next section after ALL issues in this section are resolved.
   102→
   103→### 3. Test review
   104→Make a diagram of all new UX, new data flow, new codepaths, and new branching if statements or outcomes. For each, note what is new about the features discussed in this branch and plan. Then, for each new item in the diagram, make sure there is a JS or Rails test.
   105→
   106→For LLM/prompt changes: check the "Prompt/LLM changes" file patterns listed in CLAUDE.md. If this plan touches ANY of those patterns, state which eval suites must be run, which cases should be added, and what baselines to compare against. Then use AskUserQuestion to confirm the eval scope with the user.
   107→
   108→**STOP.** For each issue found in this section, call AskUserQuestion individually. One issue per call. Present options, state your recommendation, explain WHY. Do NOT batch multiple issues into one AskUserQuestion. Only proceed to the next section after ALL issues in this section are resolved.
   109→
   110→### Test Plan Artifact
   111→
   112→After producing the test diagram, write a test plan artifact to the project directory so `/qa` and `/qa-only` can consume it as primary test input (replacing the lossy git-diff heuristic):
   113→
   114→```bash
   115→eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)
   116→USER=$(whoami)
   117→DATETIME=$(date +%Y%m%d-%H%M%S)
   118→mkdir -p ~/.gstack/projects/$SLUG
   119→```
   120→
   121→Write to `~/.gstack/projects/{slug}/{user}-{branch}-test-plan-{datetime}.md`:
   122→
   123→```markdown
   124→# Test Plan
   125→Generated by /plan-eng-review on {date}
   126→Branch: {branch}
   127→Repo: {owner/repo}
   128→
   129→## Affected Pages/Routes
   130→- {URL path} — {what to test and why}
   131→
   132→## Key Interactions to Verify
   133→- {interaction description} on {page}
   134→
   135→## Edge Cases
   136→- {edge case} on {page}
   137→
   138→## Critical Paths
   139→- {end-to-end flow that must work}
   140→```
   141→
   142→This file is consumed by `/qa` and `/qa-only` as primary test input. Include only the information that helps a QA tester know **what to test and where** — not implementation details.
   143→
   144→### 4. Performance review
   145→Evaluate:
   146→* N+1 queries and database access patterns.
   147→* Memory-usage concerns.
   148→* Caching opportunities.
   149→* Slow or high-complexity code paths.
   150→


     1→---
     2→name: plan-design-review
     3→version: 2.0.0
     4→description: |
     5→  Designer's eye plan review — interactive, like CEO and Eng review.
     6→  Rates each design dimension 0-10, explains what would make it a 10,
     7→  then fixes the plan to get there. Works in plan mode. For live site
     8→  visual audits, use /design-review.
     9→allowed-tools:
    10→  - Read
    11→  - Edit
    12→  - Grep
    13→  - Glob
    14→  - Bash
    15→  - AskUserQuestion
    16→---
    17→
    18→{{PREAMBLE}}
    19→
    20→{{BASE_BRANCH_DETECT}}
    21→
    22→# /plan-design-review: Designer's Eye Plan Review
    23→
    24→You are a senior product designer reviewing a PLAN — not a live site. Your job is
    25→to find missing design decisions and ADD THEM TO THE PLAN before implementation.
    26→
    27→The output of this skill is a better plan, not a document about the plan.
    28→
    29→## Design Philosophy
    30→
    31→You are not here to rubber-stamp this plan's UI. You are here to ensure that when
    32→this ships, users feel the design is intentional — not generated, not accidental,
    33→not "we'll polish it later." Your posture is opinionated but collaborative: find
    34→every gap, explain why it matters, fix the obvious ones, and ask about the genuine
    35→choices.
    36→
    37→Do NOT make any code changes. Do NOT start implementation. Your only job right now
    38→is to review and improve the plan's design decisions with maximum rigor.
    39→
    40→## Design Principles
    41→
    42→1. Empty states are features. "No items found." is not a design. Every empty state needs warmth, a primary action, and context.
    43→2. Every screen has a hierarchy. What does the user see first, second, third? If everything competes, nothing wins.
    44→3. Specificity over vibes. "Clean, modern UI" is not a design decision. Name the font, the spacing scale, the interaction pattern.
    45→4. Edge cases are user experiences. 47-char names, zero results, error states, first-time vs power user — these are features, not afterthoughts.
    46→5. AI slop is the enemy. Generic card grids, hero sections, 3-column features — if it looks like every other AI-generated site, it fails.
    47→6. Responsive is not "stacked on mobile." Each viewport gets intentional design.
    48→7. Accessibility is not optional. Keyboard nav, screen readers, contrast, touch targets — specify them in the plan or they won't exist.
    49→8. Subtraction default. If a UI element doesn't earn its pixels, cut it. Feature bloat kills products faster than missing features.
    50→9. Trust is earned at the pixel level. Every interface decision either builds or erodes user trust.
    51→
    52→## Cognitive Patterns — How Great Designers See
    53→
    54→These aren't a checklist — they're how you see. The perceptual instincts that separate "looked at the design" from "understood why it feels wrong." Let them run automatically as you review.
    55→
    56→1. **Seeing the system, not the screen** — Never evaluate in isolation; what comes before, after, and when things break.
    57→2. **Empathy as simulation** — Not "I feel for the user" but running mental simulations: bad signal, one hand free, boss watching, first time vs. 1000th time.
    58→3. **Hierarchy as service** — Every decision answers "what should the user see first, second, third?" Respecting their time, not prettifying pixels.
    59→4. **Constraint worship** — Limitations force clarity. "If I can only show 3 things, which 3 matter most?"
    60→5. **The question reflex** — First instinct is questions, not opinions. "Who is this for? What did they try before this?"
    61→6. **Edge case paranoia** — What if the name is 47 chars? Zero results? Network fails? Colorblind? RTL language?
    62→7. **The "Would I notice?" test** — Invisible = perfect. The highest compliment is not noticing the design.
    63→8. **Principled taste** — "This feels wrong" is traceable to a broken principle. Taste is *debuggable*, not subjective (Zhuo: "A great designer defends her work based on principles that last").
    64→9. **Subtraction default** — "As little design as possible" (Rams). "Subtract the obvious, add the meaningful" (Maeda).
    65→10. **Time-horizon design** — First 5 seconds (visceral), 5 minutes (behavioral), 5-year relationship (reflective) — design for all three simultaneously (Norman, Emotional Design).
    66→11. **Design for trust** — Every design decision either builds or erodes trust. Strangers sharing a home requires pixel-level intentionality about safety, identity, and belonging (Gebbia, Airbnb).
    67→12. **Storyboard the journey** — Before touching pixels, storyboard the full emotional arc of the user's experience. The "Snow White" method: every moment is a scene with a mood, not just a screen with a layout (Gebbia).
    68→
    69→Key references: Dieter Rams' 10 Principles, Don Norman's 3 Levels of Design, Nielsen's 10 Heuristics, Gestalt Principles (proximity, similarity, closure, continuity), Ira Glass ("Your taste is why your work disappoints you"), Jony Ive ("People can sense care and can sense carelessness. Different and new is relatively easy. Doing something that's genuinely better is very hard."), Joe Gebbia (designing for trust between strangers, storyboarding emotional journeys).
    70→
    71→When reviewing a plan, empathy as simulation runs automatically. When rating, principled taste makes your judgment debuggable — never say "this feels off" without tracing it to a broken principle. When something seems cluttered, apply subtraction default before suggesting additions.
    72→
    73→## Priority Hierarchy Under Context Pressure
    74→
    75→Step 0 > Interaction State Coverage > AI Slop Risk > Information Architecture > User Journey > everything else.
    76→Never skip Step 0, interaction states, or AI slop assessment. These are the highest-leverage design dimensions.
    77→
    78→## PRE-REVIEW SYSTEM AUDIT (before Step 0)
    79→
    80→Before reviewing the plan, gather context:
    81→
    82→```bash
    83→git log --oneline -15
    84→git diff <base> --stat
    85→```
    86→
    87→Then read:
    88→- The plan file (current plan or branch diff)
    89→- CLAUDE.md — project conventions
    90→- DESIGN.md — if it exists, ALL design decisions calibrate against it
    91→- TODOS.md — any design-related TODOs this plan touches
    92→
    93→Map:
    94→* What is the UI scope of this plan? (pages, components, interactions)
    95→* Does a DESIGN.md exist? If not, flag as a gap.
    96→* Are there existing design patterns in the codebase to align with?
    97→* What prior design reviews exist? (check reviews.jsonl)
    98→
    99→### Retrospective Check
   100→Check git log for prior design review cycles. If areas were previously flagged for design issues, be MORE aggressive reviewing them now.
   101→
   102→### UI Scope Detection
   103→Analyze the plan. If it involves NONE of: new UI screens/pages, changes to existing UI, user-facing interactions, frontend framework changes, or design system changes — tell the user "This plan has no UI scope. A design review isn't applicable." and exit early. Don't force design review on a backend change.
   104→
   105→Report findings before proceeding to Step 0.
   106→
   107→## Step 0: Design Scope Assessment
   108→
   109→### 0A. Initial Design Rating
   110→Rate the plan's overall design completeness 0-10.
   111→- "This plan is a 3/10 on design completeness because it describes what the backend does but never specifies what the user sees."
   112→- "This plan is a 7/10 — good interaction descriptions but missing empty states, error states, and responsive behavior."
   113→
   114→Explain what a 10 looks like for THIS plan.
   115→
   116→### 0B. DESIGN.md Status
   117→- If DESIGN.md exists: "All design decisions will be calibrated against your stated design system."
   118→- If no DESIGN.md: "No design system found. Recommend running /design-consultation first. Proceeding with universal design principles."
   119→
   120→### 0C. Existing Design Leverage
   121→What existing UI patterns, components, or design decisions in the codebase should this plan reuse? Don't reinvent what already works.
   122→
   123→### 0D. Focus Areas
   124→AskUserQuestion: "I've rated this plan {N}/10 on design completeness. The biggest gaps are {X, Y, Z}. Want me to review all 7 dimensions, or focus on specific areas?"
   125→
   126→**STOP.** Do NOT proceed until user responds.
   127→
   128→## The 0-10 Rating Method
   129→
   130→For each design section, rate the plan 0-10 on that dimension. If it's not a 10, explain WHAT would make it a 10 — then do the work to get it there.
   131→
   132→Pattern:
   133→1. Rate: "Information Architecture: 4/10"
   134→2. Gap: "It's a 4 because the plan doesn't define content hierarchy. A 10 would have clear primary/secondary/tertiary for every screen."
   135→3. Fix: Edit the plan to add what's missing
   136→4. Re-rate: "Now 8/10 — still missing mobile nav hierarchy"
   137→5. AskUserQuestion if there's a genuine design choice to resolve
   138→6. Fix again → repeat until 10 or user says "good enough, move on"
   139→
   140→Re-run loop: invoke /plan-design-review again → re-rate → sections at 8+ get a quick pass, sections below 8 get full treatment.
   141→
   142→## Review Sections (7 passes, after scope is agreed)
   143→
   144→### Pass 1: Information Architecture
   145→Rate 0-10: Does the plan define what the user sees first, second, third?
   146→FIX TO 10: Add information hierarchy to the plan. Include ASCII diagram of screen/page structure and navigation flow. Apply "constraint worship" — if you can only show 3 things, which 3?
   147→**STOP.** AskUserQuestion once per issue. Do NOT batch. Recommend + WHY. If no issues, say so and move on. Do NOT proceed until user responds.
   148→
   149→### Pass 2: Interaction State Coverage
   150→Rate 0-10: Does the plan specify loading, empty, error, success, partial states?


     1→---
     2→name: design-consultation
     3→version: 1.0.0
     4→description: |
     5→  Design consultation: understands your product, researches the landscape, proposes a
     6→  complete design system (aesthetic, typography, color, layout, spacing, motion), and
     7→  generates font+color preview pages. Creates DESIGN.md as your project's design source
     8→  of truth. For existing sites, use /plan-design-review to infer the system instead.
     9→allowed-tools:
    10→  - Bash
    11→  - Read
    12→  - Write
    13→  - Edit
    14→  - Glob
    15→  - Grep
    16→  - AskUserQuestion
    17→  - WebSearch
    18→---
    19→
    20→{{PREAMBLE}}
    21→
    22→# /design-consultation: Your Design System, Built Together
    23→
    24→You are a senior product designer with strong opinions about typography, color, and visual systems. You don't present menus — you listen, think, research, and propose. You're opinionated but not dogmatic. You explain your reasoning and welcome pushback.
    25→
    26→**Your posture:** Design consultant, not form wizard. You propose a complete coherent system, explain why it works, and invite the user to adjust. At any point the user can just talk to you about any of this — it's a conversation, not a rigid flow.
    27→
    28→---
    29→
    30→## Phase 0: Pre-checks
    31→
    32→**Check for existing DESIGN.md:**
    33→
    34→```bash
    35→ls DESIGN.md design-system.md 2>/dev/null || echo "NO_DESIGN_FILE"
    36→```
    37→
    38→- If a DESIGN.md exists: Read it. Ask the user: "You already have a design system. Want to **update** it, **start fresh**, or **cancel**?"
    39→- If no DESIGN.md: continue.
    40→
    41→**Gather product context from the codebase:**
    42→
    43→```bash
    44→cat README.md 2>/dev/null | head -50
    45→cat package.json 2>/dev/null | head -20
    46→ls src/ app/ pages/ components/ 2>/dev/null | head -30
    47→```
    48→
    49→Look for brainstorm output:
    50→
    51→```bash
    52→eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)
    53→ls ~/.gstack/projects/$SLUG/*brainstorm* 2>/dev/null | head -5
    54→ls .context/*brainstorm* .context/attachments/*brainstorm* 2>/dev/null | head -5
    55→```
    56→
    57→If brainstorm output exists, read it — the product context is pre-filled.
    58→
    59→If the codebase is empty and purpose is unclear, say: *"I don't have a clear picture of what you're building yet. Want to brainstorm first with `/brainstorm`? Once we know the product direction, we can set up the design system."*
    60→
    61→**Find the browse binary (optional — enables visual competitive research):**
    62→
    63→{{BROWSE_SETUP}}
    64→
    65→If browse is not available, that's fine — visual research is optional. The skill works without it using WebSearch and your built-in design knowledge.
    66→
    67→---
    68→
    69→## Phase 1: Product Context
    70→
    71→Ask the user a single question that covers everything you need to know. Pre-fill what you can infer from the codebase.
    72→
    73→**AskUserQuestion Q1 — include ALL of these:**
    74→1. Confirm what the product is, who it's for, what space/industry
    75→2. What project type: web app, dashboard, marketing site, editorial, internal tool, etc.
    76→3. "Want me to research what top products in your space are doing for design, or should I work from my design knowledge?"
    77→4. **Explicitly say:** "At any point you can just drop into chat and we'll talk through anything — this isn't a rigid form, it's a conversation."
    78→
    79→If the README or brainstorm gives you enough context, pre-fill and confirm: *"From what I can see, this is [X] for [Y] in the [Z] space. Sound right? And would you like me to research what's out there in this space, or should I work from what I know?"*
    80→
    81→---
    82→
    83→## Phase 2: Research (only if user said yes)
    84→
    85→If the user wants competitive research:
    86→
    87→**Step 1: Identify what's out there via WebSearch**
    88→
    89→Use WebSearch to find 5-10 products in their space. Search for:
    90→- "[product category] website design"
    91→- "[product category] best websites 2025"
    92→- "best [industry] web apps"
    93→
    94→**Step 2: Visual research via browse (if available)**
    95→
    96→If the browse binary is available (`$B` is set), visit the top 3-5 sites in the space and capture visual evidence:
    97→
    98→```bash
    99→$B goto "https://example-site.com"
   100→$B screenshot "/tmp/design-research-site-name.png"
   101→$B snapshot
   102→```
   103→
   104→For each site, analyze: fonts actually used, color palette, layout approach, spacing density, aesthetic direction. The screenshot gives you the feel; the snapshot gives you structural data.
   105→
   106→If a site blocks the headless browser or requires login, skip it and note why.
   107→
   108→If browse is not available, rely on WebSearch results and your built-in design knowledge — this is fine.
   109→
   110→**Step 3: Synthesize findings**
   111→
   112→The goal of research is NOT to copy. It is to get in the ballpark — to understand the visual language users in this category already expect. This gives you the baseline. The interesting design work starts after you have the baseline: deciding where to follow conventions (so the product feels literate) and where to break from them (so the product is memorable).
   113→
   114→Summarize conversationally:
   115→> "I looked at what's out there. Here's the landscape: they converge on [patterns]. Most of them feel [observation — e.g., interchangeable, polished but generic, etc.]. The opportunity to stand out is [gap]. Here's where I'd play it safe and where I'd take a risk..."
   116→
   117→**Graceful degradation:**
   118→- Browse available → screenshots + snapshots + WebSearch (richest research)
   119→- Browse unavailable → WebSearch only (still good)
   120→- WebSearch also unavailable → agent's built-in design knowledge (always works)
   121→
   122→If the user said no research, skip entirely and proceed to Phase 3 using your built-in design knowledge.
   123→
   124→---
   125→
   126→## Phase 3: The Complete Proposal
   127→
   128→This is the soul of the skill. Propose EVERYTHING as one coherent package.
   129→
   130→**AskUserQuestion Q2 — present the full proposal with SAFE/RISK breakdown:**
   131→
   132→```
   133→Based on [product context] and [research findings / my design knowledge]:
   134→
   135→AESTHETIC: [direction] — [one-line rationale]
   136→DECORATION: [level] — [why this pairs with the aesthetic]
   137→LAYOUT: [approach] — [why this fits the product type]
   138→COLOR: [approach] + proposed palette (hex values) — [rationale]
   139→TYPOGRAPHY: [3 font recommendations with roles] — [why these fonts]
   140→SPACING: [base unit + density] — [rationale]
   141→MOTION: [approach] — [rationale]
   142→
   143→This system is coherent because [explain how choices reinforce each other].
   144→
   145→SAFE CHOICES (category baseline — your users expect these):
   146→  - [2-3 decisions that match category conventions, with rationale for playing safe]
   147→
   148→RISKS (where your product gets its own face):
   149→  - [2-3 deliberate departures from convention]
   150→  - For each risk: what it is, why it works, what you gain, what it costs


     1→/**
     2→ * gstack CLI — thin wrapper that talks to the persistent server
     3→ *
     4→ * Flow:
     5→ *   1. Read .gstack/browse.json for port + token
     6→ *   2. If missing or stale PID → start server in background
     7→ *   3. Health check + version mismatch detection
     8→ *   4. Send command via HTTP POST
     9→ *   5. Print response to stdout (or stderr for errors)
    10→ */
    11→
    12→import * as fs from 'fs';
    13→import * as path from 'path';
    14→import { resolveConfig, ensureStateDir, readVersionHash } from './config';
    15→
    16→const config = resolveConfig();
    17→const MAX_START_WAIT = 8000; // 8 seconds to start
    18→
    19→export function resolveServerScript(
    20→  env: Record<string, string | undefined> = process.env,
    21→  metaDir: string = import.meta.dir,
    22→  execPath: string = process.execPath
    23→): string {
    24→  if (env.BROWSE_SERVER_SCRIPT) {
    25→    return env.BROWSE_SERVER_SCRIPT;
    26→  }
    27→
    28→  // Dev mode: cli.ts runs directly from browse/src
    29→  if (metaDir.startsWith('/') && !metaDir.includes('$bunfs')) {
    30→    const direct = path.resolve(metaDir, 'server.ts');
    31→    if (fs.existsSync(direct)) {
    32→      return direct;
    33→    }
    34→  }
    35→
    36→  // Compiled binary: derive the source tree from browse/dist/browse
    37→  if (execPath) {
    38→    const adjacent = path.resolve(path.dirname(execPath), '..', 'src', 'server.ts');
    39→    if (fs.existsSync(adjacent)) {
    40→      return adjacent;
    41→    }
    42→  }
    43→
    44→  throw new Error(
    45→    'Cannot find server.ts. Set BROWSE_SERVER_SCRIPT env or run from the browse source tree.'
    46→  );
    47→}
    48→
    49→const SERVER_SCRIPT = resolveServerScript();
    50→
    51→interface ServerState {
    52→  pid: number;
    53→  port: number;
    54→  token: string;
    55→  startedAt: string;
    56→  serverPath: string;
    57→  binaryVersion?: string;
    58→}
    59→
    60→// ─── State File ────────────────────────────────────────────────
    61→function readState(): ServerState | null {
    62→  try {
    63→    const data = fs.readFileSync(config.stateFile, 'utf-8');
    64→    return JSON.parse(data);
    65→  } catch {
    66→    return null;
    67→  }
    68→}
    69→
    70→function isProcessAlive(pid: number): boolean {
    71→  try {
    72→    process.kill(pid, 0);
    73→    return true;
    74→  } catch {
    75→    return false;
    76→  }
    77→}
    78→
    79→// ─── Process Management ─────────────────────────────────────────
    80→async function killServer(pid: number): Promise<void> {


Bash

head -200 /root/projects/gits/gstack/scripts/gen-skill-docs.ts | tail -100


grep -n "{{" /root/projects/gits/gstack/scripts/gen-skill-docs.ts | head -20


_UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/skills/gstack/bin/gstack-update-check 2>/dev/null || true)
[ -n "$_UPD" ] && echo "$_UPD" || true
mkdir -p ~/.gstack/sessions
touch ~/.gstack/sessions/"$PPID"
_SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ')
find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true
_CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true)
_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"
\`\`\`

If output shows \`UPGRADE_AVAILABLE <old> <new>\`: read \`~/.claude/skills/gstack/gstack-upgrade/SKILL.md\` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If \`JUST_UPGRADED <from> <to>\`: tell user "Running gstack v{to} (just updated!)" and continue.

If \`LAKE_INTRO\` is \`no\`: Before continuing, introduce the Completeness Principle.
Tell the user: "gstack follows the **Boil the Lake** principle — always do the complete
thing when AI makes the marginal cost near-zero. Read more: https://garryslist.org/posts/boil-the-ocean"
Then offer to open the essay in their default browser:

\`\`\`bash
open https://garryslist.org/posts/boil-the-ocean
touch ~/.gstack/.completeness-intro-seen
\`\`\`

Only run \`open\` if the user says yes. Always run \`touch\` to mark as seen. This only happens once.

## AskUserQuestion Format

**ALWAYS follow this structure for every AskUserQuestion call:**
1. **Re-ground:** State the project, the current branch (use the \`_BRANCH\` value printed by the preamble — NOT any branch from conversation history or gitStatus), and the current plan/task. (1-2 sentences)
2. **Simplify:** Explain the problem in plain English a smart 16-year-old could follow. No raw function names, no internal jargon, no implementation details. Use concrete examples and analogies. Say what it DOES, not what it's called.
3. **Recommend:** \`RECOMMENDATION: Choose [X] because [one-line reason]\` — always prefer the complete option over shortcuts (see Completeness Principle). Include \`Completeness: X/10\` for each option. Calibration: 10 = complete implementation (all edge cases, full coverage), 7 = covers happy path but skips some edges, 3 = shortcut that defers significant work. If both options are 8+, pick the higher; if one is ≤5, flag it.
4. **Options:** Lettered options: \`A) ... B) ... C) ...\` — when an option involves effort, show both scales: \`(human: ~X / CC: ~Y)\`

Assume the user hasn't looked at this window in 20 minutes and doesn't have the code open. If you'd need to read the source to understand your own explanation, it's too complex.

Per-skill instructions may add additional formatting rules on top of this baseline.

## Completeness Principle — Boil the Lake

AI-assisted coding makes the marginal cost of completeness near-zero. When you present options:

- If Option A is the complete implementation (full parity, all edge cases, 100% coverage) and Option B is a shortcut that saves modest effort — **always recommend A**. The delta between 80 lines and 150 lines is meaningless with CC+gstack. "Good enough" is the wrong instinct when "complete" costs minutes more.
- **Lake vs. ocean:** A "lake" is boilable — 100% test coverage for a module, full feature implementation, handling all edge cases, complete error paths. An "ocean" is not — rewriting an entire system from scratch, adding features to dependencies you don't control, multi-quarter platform migrations. Recommend boiling lakes. Flag oceans as out of scope.
- **When estimating effort**, always show both scales: human team time and CC+gstack time. The compression ratio varies by task type — use this reference:

| Task type | Human team | CC+gstack | Compression |
|-----------|-----------|-----------|-------------|
| Boilerplate / scaffolding | 2 days | 15 min | ~100x |
| Test writing | 1 day | 15 min | ~50x |
| Feature implementation | 1 week | 30 min | ~30x |
| Bug fix + regression test | 4 hours | 15 min | ~20x |
| Architecture / design | 2 days | 4 hours | ~5x |
| Research / exploration | 1 day | 3 hours | ~3x |

- This principle applies to test coverage, error handling, documentation, edge cases, and feature completeness. Don't skip the last 10% to "save time" — with AI, that 10% costs seconds.

**Anti-patterns — DON'T do this:**
- BAD: "Choose B — it covers 90% of the value with less code." (If A is only 70 lines more, choose A.)
- BAD: "We can skip edge case handling to save time." (Edge case handling costs minutes with CC.)
- BAD: "Let's defer test coverage to a follow-up PR." (Tests are the cheapest lake to boil.)
- BAD: Quoting only human-team effort: "This would take 2 weeks." (Say: "2 weeks human / ~1 hour CC.")

## Contributor Mode

If \`_CONTRIB\` is \`true\`: you are in **contributor mode**. You're a gstack user who also helps make it better.

**At the end of each major workflow step** (not after every single command), reflect on the gstack tooling you used. Rate your experience 0 to 10. If it wasn't a 10, think about why. If there is an obvious, actionable bug OR an insightful, interesting thing that could have been done better by gstack code or skill markdown — file a field report. Maybe our contributor will help make us better!

**Calibration — this is the bar:** For example, \`$B js "await fetch(...)"\` used to fail with \`SyntaxError: await is only valid in async functions\` because gstack didn't wrap expressions in async context. Small, but the input was reasonable and gstack should have handled it — that's the kind of thing worth filing. Things less consequential than this, ignore.

**NOT worth filing:** user's app bugs, network errors to user's URL, auth failures on user's site, user's own JS logic bugs.

**To file:** write \`~/.gstack/contributor-logs/{slug}.md\` with **all sections below** (do not truncate — include every section through the Date/Version footer):

\`\`\`
# {Title}

Hey gstack team — ran into this while using /{skill-name}:

**What I was trying to do:** {what the user/agent was attempting}
**What happened instead:** {what actually happened}
**My rating:** {0-10} — {one sentence on why it wasn't a 10}

## Steps to reproduce
1. {step}

## Raw output
\`\`\`
{paste the actual error or unexpected output here}
\`\`\`

## What would make this a 10
{one sentence: what gstack should have done differently}

**Date:** {YYYY-MM-DD} | **Version:** {gstack version} | **Skill:** /{skill}
\`\`\`

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}"`;


6: *   read .tmpl → find {{PLACEHOLDERS}} → resolve from source → format → write .md
1105:const GENERATED_HEADER = `<!-- AUTO-GENERATED from {{SOURCE}} — do not edit directly -->\n<!-- Regenerate: bun run gen:skill-docs -->\n`;
1115:    if (!resolver) throw new Error(`Unknown placeholder {{${name}}} in ${relTmplPath}`);
1126:  const header = GENERATED_HEADER.replace('{{SOURCE}}', path.basename(tmplPath));


total 20
drwxr-xr-x  2 root root   43 Mar 18 15:58 .
drwxr-xr-x 22 root root 4096 Mar 18 15:58 ..
-rw-r--r--  1 root root 7962 Mar 18 15:58 SKILL.md
-rw-r--r--  1 root root 7850 Mar 18 15:58 SKILL.md.tmpl


total 20
drwxr-xr-x  2 root root   43 Mar 18 15:58 .
drwxr-xr-x 22 root root 4096 Mar 18 15:58 ..
-rw-r--r--  1 root root 9699 Mar 18 15:58 SKILL.md
-rw-r--r--  1 root root 2067 Mar 18 15:58 SKILL.md.tmpl


total 52
drwxr-xr-x  2 root root    43 Mar 18 15:58 .
drwxr-xr-x 22 root root  4096 Mar 18 15:58 ..
-rw-r--r--  1 root root 26030 Mar 18 15:58 SKILL.md
-rw-r--r--  1 root root 18398 Mar 18 15:58 SKILL.md.tmpl


/root/projects/gits/gstack/browse/src/browser-manager.ts
/root/projects/gits/gstack/browse/src/buffers.ts
/root/projects/gits/gstack/browse/src/cli.ts
/root/projects/gits/gstack/browse/src/commands.ts
/root/projects/gits/gstack/browse/src/config.ts
/root/projects/gits/gstack/browse/src/cookie-import-browser.ts
/root/projects/gits/gstack/browse/src/cookie-picker-routes.ts
/root/projects/gits/gstack/browse/src/cookie-picker-ui.ts
/root/projects/gits/gstack/browse/src/find-browse.ts
/root/projects/gits/gstack/browse/src/meta-commands.ts
/root/projects/gits/gstack/browse/src/read-commands.ts
/root/projects/gits/gstack/browse/src/server.ts
/root/projects/gits/gstack/browse/src/snapshot.ts
/root/projects/gits/gstack/browse/src/write-commands.ts


     1→/**
     2→ * Command registry — single source of truth for all browse commands.
     3→ *
     4→ * Dependency graph:
     5→ *   commands.ts ──▶ server.ts (runtime dispatch)
     6→ *                ──▶ gen-skill-docs.ts (doc generation)
     7→ *                ──▶ skill-parser.ts (validation)
     8→ *                ──▶ skill-check.ts (health reporting)
     9→ *
    10→ * Zero side effects. Safe to import from build scripts and tests.
    11→ */
    12→
    13→export const READ_COMMANDS = new Set([
    14→  'text', 'html', 'links', 'forms', 'accessibility',
    15→  'js', 'eval', 'css', 'attrs',
    16→  'console', 'network', 'cookies', 'storage', 'perf',
    17→  'dialog', 'is',
    18→]);
    19→
    20→export const WRITE_COMMANDS = new Set([
    21→  'goto', 'back', 'forward', 'reload',
    22→  'click', 'fill', 'select', 'hover', 'type', 'press', 'scroll', 'wait',
    23→  'viewport', 'cookie', 'cookie-import', 'cookie-import-browser', 'header', 'useragent',
    24→  'upload', 'dialog-accept', 'dialog-dismiss',
    25→]);
    26→
    27→export const META_COMMANDS = new Set([
    28→  'tabs', 'tab', 'newtab', 'closetab',
    29→  'status', 'stop', 'restart',
    30→  'screenshot', 'pdf', 'responsive',
    31→  'chain', 'diff',
    32→  'url', 'snapshot',
    33→]);
    34→
    35→export const ALL_COMMANDS = new Set([...READ_COMMANDS, ...WRITE_COMMANDS, ...META_COMMANDS]);
    36→
    37→export const COMMAND_DESCRIPTIONS: Record<string, { category: string; description: string; usage?: string }> = {
    38→  // Navigation
    39→  'goto':    { category: 'Navigation', description: 'Navigate to URL', usage: 'goto <url>' },
    40→  'back':    { category: 'Navigation', description: 'History back' },
    41→  'forward': { category: 'Navigation', description: 'History forward' },
    42→  'reload':  { category: 'Navigation', description: 'Reload page' },
    43→  'url':     { category: 'Navigation', description: 'Print current URL' },
    44→  // Reading
    45→  'text':    { category: 'Reading', description: 'Cleaned page text' },
    46→  'html':    { category: 'Reading', description: 'innerHTML of selector (throws if not found), or full page HTML if no selector given', usage: 'html [selector]' },
    47→  'links':   { category: 'Reading', description: 'All links as "text → href"' },
    48→  'forms':   { category: 'Reading', description: 'Form fields as JSON' },
    49→  'accessibility': { category: 'Reading', description: 'Full ARIA tree' },
    50→  // Inspection
    51→  'js':      { category: 'Inspection', description: 'Run JavaScript expression and return result as string', usage: 'js <expr>' },
    52→  'eval':    { category: 'Inspection', description: 'Run JavaScript from file and return result as string (path must be under /tmp or cwd)', usage: 'eval <file>' },
    53→  'css':     { category: 'Inspection', description: 'Computed CSS value', usage: 'css <sel> <prop>' },
    54→  'attrs':   { category: 'Inspection', description: 'Element attributes as JSON', usage: 'attrs <sel|@ref>' },
    55→  'is':      { category: 'Inspection', description: 'State check (visible/hidden/enabled/disabled/checked/editable/focused)', usage: 'is <prop> <sel>' },
    56→  'console': { category: 'Inspection', description: 'Console messages (--errors filters to error/warning)', usage: 'console [--clear|--errors]' },
    57→  'network': { category: 'Inspection', description: 'Network requests', usage: 'network [--clear]' },
    58→  'dialog':  { category: 'Inspection', description: 'Dialog messages', usage: 'dialog [--clear]' },
    59→  'cookies': { category: 'Inspection', description: 'All cookies as JSON' },
    60→  'storage': { category: 'Inspection', description: 'Read all localStorage + sessionStorage as JSON, or set <key> <value> to write localStorage', usage: 'storage [set k v]' },
    61→  'perf':    { category: 'Inspection', description: 'Page load timings' },
    62→  // Interaction
    63→  'click':   { category: 'Interaction', description: 'Click element', usage: 'click <sel>' },
    64→  'fill':    { category: 'Interaction', description: 'Fill input', usage: 'fill <sel> <val>' },
    65→  'select':  { category: 'Interaction', description: 'Select dropdown option by value, label, or visible text', usage: 'select <sel> <val>' },
    66→  'hover':   { category: 'Interaction', description: 'Hover element', usage: 'hover <sel>' },
    67→  'type':    { category: 'Interaction', description: 'Type into focused element', usage: 'type <text>' },
    68→  'press':   { category: 'Interaction', description: 'Press key — Enter, Tab, Escape, ArrowUp/Down/Left/Right, Backspace, Delete, Home, End, PageUp, PageDown, or modifiers like Shift+Enter', usage: 'press <key>' },
    69→  'scroll':  { category: 'Interaction', description: 'Scroll element into view, or scroll to page bottom if no selector', usage: 'scroll [sel]' },
    70→  'wait':    { category: 'Interaction', description: 'Wait for element, network idle, or page load (timeout: 15s)', usage: 'wait <sel|--networkidle|--load>' },
    71→  'upload':  { category: 'Interaction', description: 'Upload file(s)', usage: 'upload <sel> <file> [file2...]' },
    72→  'viewport':{ category: 'Interaction', description: 'Set viewport size', usage: 'viewport <WxH>' },
    73→  'cookie':  { category: 'Interaction', description: 'Set cookie on current page domain', usage: 'cookie <name>=<value>' },
    74→  'cookie-import': { category: 'Interaction', description: 'Import cookies from JSON file', usage: 'cookie-import <json>' },
    75→  'cookie-import-browser': { category: 'Interaction', description: 'Import cookies from Comet, Chrome, Arc, Brave, or Edge (opens picker, or use --domain for direct import)', usage: 'cookie-import-browser [browser] [--domain d]' },
    76→  'header':  { category: 'Interaction', description: 'Set custom request header (colon-separated, sensitive values auto-redacted)', usage: 'header <name>:<value>' },
    77→  'useragent': { category: 'Interaction', description: 'Set user agent', usage: 'useragent <string>' },
    78→  'dialog-accept': { category: 'Interaction', description: 'Auto-accept next alert/confirm/prompt. Optional text is sent as the prompt response', usage: 'dialog-accept [text]' },
    79→  'dialog-dismiss': { category: 'Interaction', description: 'Auto-dismiss next dialog' },
    80→  // Visual
    81→  'screenshot': { category: 'Visual', description: 'Save screenshot (supports element crop via CSS/@ref, --clip region, --viewport)', usage: 'screenshot [--viewport] [--clip x,y,w,h] [selector|@ref] [path]' },
    82→  'pdf':     { category: 'Visual', description: 'Save as PDF', usage: 'pdf [path]' },
    83→  'responsive': { category: 'Visual', description: 'Screenshots at mobile (375x812), tablet (768x1024), desktop (1280x720). Saves as {prefix}-mobile.png etc.', usage: 'responsive [prefix]' },
    84→  'diff':    { category: 'Visual', description: 'Text diff between pages', usage: 'diff <url1> <url2>' },
    85→  // Tabs
    86→  'tabs':    { category: 'Tabs', description: 'List open tabs' },
    87→  'tab':     { category: 'Tabs', description: 'Switch to tab', usage: 'tab <id>' },
    88→  'newtab':  { category: 'Tabs', description: 'Open new tab', usage: 'newtab [url]' },
    89→  'closetab':{ category: 'Tabs', description: 'Close tab', usage: 'closetab [id]' },
    90→  // Server
    91→  'status':  { category: 'Server', description: 'Health check' },
    92→  'stop':    { category: 'Server', description: 'Shutdown server' },
    93→  'restart': { category: 'Server', description: 'Restart server' },
    94→  // Meta
    95→  'snapshot':{ category: 'Snapshot', description: 'Accessibility tree with @e refs for element selection. Flags: -i interactive only, -c compact, -d N depth limit, -s sel scope, -D diff vs previous, -a annotated screenshot, -o path output, -C cursor-interactive @c refs', usage: 'snapshot [flags]' },
    96→  'chain':   { category: 'Meta', description: 'Run commands from JSON stdin. Format: [["cmd","arg1",...],...]' },
    97→};
    98→
    99→// Load-time validation: descriptions must cover exactly the command sets
   100→const allCmds = new Set([...READ_COMMANDS, ...WRITE_COMMANDS, ...META_COMMANDS]);
   101→const descKeys = new Set(Object.keys(COMMAND_DESCRIPTIONS));
   102→for (const cmd of allCmds) {
   103→  if (!descKeys.has(cmd)) throw new Error(`COMMAND_DESCRIPTIONS missing entry for: ${cmd}`);
   104→}
   105→for (const key of descKeys) {
   106→  if (!allCmds.has(key)) throw new Error(`COMMAND_DESCRIPTIONS has unknown command: ${key}`);
   107→}
   108→


     1→/**
     2→ * Snapshot command — accessibility tree with ref-based element selection
     3→ *
     4→ * Architecture (Locator map — no DOM mutation):
     5→ *   1. page.locator(scope).ariaSnapshot() → YAML-like accessibility tree
     6→ *   2. Parse tree, assign refs @e1, @e2, ...
     7→ *   3. Build Playwright Locator for each ref (getByRole + nth)
     8→ *   4. Store Map<string, Locator> on BrowserManager
     9→ *   5. Return compact text output with refs prepended
    10→ *
    11→ * Extended features:
    12→ *   --diff / -D:       Compare against last snapshot, return unified diff
    13→ *   --annotate / -a:   Screenshot with overlay boxes at each @ref
    14→ *   --output / -o:     Output path for annotated screenshot
    15→ *   -C / --cursor-interactive: Scan for cursor:pointer/onclick/tabindex elements
    16→ *
    17→ * Later: "click @e3" → look up Locator → locator.click()
    18→ */
    19→
    20→import type { Page, Locator } from 'playwright';
    21→import type { BrowserManager, RefEntry } from './browser-manager';
    22→import * as Diff from 'diff';
    23→
    24→// Roles considered "interactive" for the -i flag
    25→const INTERACTIVE_ROLES = new Set([
    26→  'button', 'link', 'textbox', 'checkbox', 'radio', 'combobox',
    27→  'listbox', 'menuitem', 'menuitemcheckbox', 'menuitemradio',
    28→  'option', 'searchbox', 'slider', 'spinbutton', 'switch', 'tab',
    29→  'treeitem',
    30→]);
    31→
    32→interface SnapshotOptions {
    33→  interactive?: boolean;       // -i: only interactive elements
    34→  compact?: boolean;           // -c: remove empty structural elements
    35→  depth?: number;              // -d N: limit tree depth
    36→  selector?: string;           // -s SEL: scope to CSS selector
    37→  diff?: boolean;              // -D / --diff: diff against last snapshot
    38→  annotate?: boolean;          // -a / --annotate: annotated screenshot
    39→  outputPath?: string;         // -o / --output: path for annotated screenshot
    40→  cursorInteractive?: boolean; // -C / --cursor-interactive: scan cursor:pointer etc.
    41→}
    42→
    43→/**
    44→ * Snapshot flag metadata — single source of truth for CLI parsing and doc generation.
    45→ *
    46→ * Imported by:
    47→ *   - gen-skill-docs.ts (generates {{SNAPSHOT_FLAGS}} tables)
    48→ *   - skill-parser.ts (validates flags in SKILL.md examples)
    49→ */
    50→export const SNAPSHOT_FLAGS: Array<{
    51→  short: string;
    52→  long: string;
    53→  description: string;
    54→  takesValue?: boolean;
    55→  valueHint?: string;
    56→  optionKey: keyof SnapshotOptions;
    57→}> = [
    58→  { short: '-i', long: '--interactive', description: 'Interactive elements only (buttons, links, inputs) with @e refs', optionKey: 'interactive' },
    59→  { short: '-c', long: '--compact', description: 'Compact (no empty structural nodes)', optionKey: 'compact' },
    60→  { short: '-d', long: '--depth', description: 'Limit tree depth (0 = root only, default: unlimited)', takesValue: true, valueHint: '<N>', optionKey: 'depth' },
    61→  { short: '-s', long: '--selector', description: 'Scope to CSS selector', takesValue: true, valueHint: '<sel>', optionKey: 'selector' },
    62→  { short: '-D', long: '--diff', description: 'Unified diff against previous snapshot (first call stores baseline)', optionKey: 'diff' },
    63→  { short: '-a', long: '--annotate', description: 'Annotated screenshot with red overlay boxes and ref labels', optionKey: 'annotate' },
    64→  { short: '-o', long: '--output', description: 'Output path for annotated screenshot (default: /tmp/browse-annotated.png)', takesValue: true, valueHint: '<path>', optionKey: 'outputPath' },
    65→  { short: '-C', long: '--cursor-interactive', description: 'Cursor-interactive elements (@c refs — divs with pointer, onclick)', optionKey: 'cursorInteractive' },
    66→];
    67→
    68→interface ParsedNode {
    69→  indent: number;
    70→  role: string;
    71→  name: string | null;
    72→  props: string;      // e.g., "[level=1]"
    73→  children: string;   // inline text content after ":"
    74→  rawLine: string;
    75→}
    76→
    77→/**
    78→ * Parse CLI args into SnapshotOptions — driven by SNAPSHOT_FLAGS metadata.
    79→ */
    80→export function parseSnapshotArgs(args: string[]): SnapshotOptions {
    81→  const opts: SnapshotOptions = {};
    82→  for (let i = 0; i < args.length; i++) {
    83→    const flag = SNAPSHOT_FLAGS.find(f => f.short === args[i] || f.long === args[i]);
    84→    if (!flag) throw new Error(`Unknown snapshot flag: ${args[i]}`);
    85→    if (flag.takesValue) {
    86→      const value = args[++i];
    87→      if (!value) throw new Error(`Usage: snapshot ${flag.short} <value>`);
    88→      if (flag.optionKey === 'depth') {
    89→        (opts as any)[flag.optionKey] = parseInt(value, 10);
    90→        if (isNaN(opts.depth!)) throw new Error('Usage: snapshot -d <number>');
    91→      } else {
    92→        (opts as any)[flag.optionKey] = value;
    93→      }
    94→    } else {
    95→      (opts as any)[flag.optionKey] = true;
    96→    }
    97→  }
    98→  return opts;
    99→}
   100→
   101→/**
   102→ * Parse one line of ariaSnapshot output.
   103→ *
   104→ * Format examples:
   105→ *   - heading "Test" [level=1]
   106→ *   - link "Link A":
   107→ *     - /url: /a
   108→ *   - textbox "Name"
   109→ *   - paragraph: Some text
   110→ *   - combobox "Role":
   111→ */
   112→function parseLine(line: string): ParsedNode | null {
   113→  // Match: (indent)(- )(role)( "name")?( [props])?(: inline)?
   114→  const match = line.match(/^(\s*)-\s+(\w+)(?:\s+"([^"]*)")?(?:\s+(\[.*?\]))?\s*(?::\s*(.*))?$/);
   115→  if (!match) {
   116→    // Skip metadata lines like "- /url: /a"
   117→    return null;
   118→  }
   119→  return {
   120→    indent: match[1].length,
   121→    role: match[2],
   122→    name: match[3] ?? null,
   123→    props: match[4] || '',
   124→    children: match[5]?.trim() || '',
   125→    rawLine: line,
   126→  };
   127→}
   128→
   129→/**
   130→ * Take an accessibility snapshot and build the ref map.
   131→ */
   132→export async function handleSnapshot(
   133→  args: string[],
   134→  bm: BrowserManager
   135→): Promise<string> {
   136→  const opts = parseSnapshotArgs(args);
   137→  const page = bm.getPage();
   138→
   139→  // Get accessibility tree via ariaSnapshot
   140→  let rootLocator: Locator;
   141→  if (opts.selector) {
   142→    rootLocator = page.locator(opts.selector);
   143→    const count = await rootLocator.count();
   144→    if (count === 0) throw new Error(`Selector not found: ${opts.selector}`);
   145→  } else {
   146→    rootLocator = page.locator('body');
   147→  }
   148→
   149→  const ariaText = await rootLocator.ariaSnapshot();
   150→  if (!ariaText || ariaText.trim().length === 0) {


     1→# Architecture
     2→
     3→This document explains **why** gstack is built the way it is. For setup and commands, see CLAUDE.md. For contributing, see CONTRIBUTING.md.
     4→
     5→## The core idea
     6→
     7→gstack gives Claude Code a persistent browser and a set of opinionated workflow skills. The browser is the hard part — everything else is Markdown.
     8→
     9→The key insight: an AI agent interacting with a browser needs **sub-second latency** and **persistent state**. If every command cold-starts a browser, you're waiting 3-5 seconds per tool call. If the browser dies between commands, you lose cookies, tabs, and login sessions. So gstack runs a long-lived Chromium daemon that the CLI talks to over localhost HTTP.
    10→
    11→```
    12→Claude Code                     gstack
    13→─────────                      ──────
    14→                               ┌──────────────────────┐
    15→  Tool call: $B snapshot -i    │  CLI (compiled binary)│
    16→  ─────────────────────────→   │  • reads state file   │
    17→                               │  • POST /command      │
    18→                               │    to localhost:PORT   │
    19→                               └──────────┬───────────┘
    20→                                          │ HTTP
    21→                               ┌──────────▼───────────┐
    22→                               │  Server (Bun.serve)   │
    23→                               │  • dispatches command  │
    24→                               │  • talks to Chromium   │
    25→                               │  • returns plain text  │
    26→                               └──────────┬───────────┘
    27→                                          │ CDP
    28→                               ┌──────────▼───────────┐
    29→                               │  Chromium (headless)   │
    30→                               │  • persistent tabs     │
    31→                               │  • cookies carry over  │
    32→                               │  • 30min idle timeout  │
    33→                               └───────────────────────┘
    34→```
    35→
    36→First call starts everything (~3s). Every call after: ~100-200ms.
    37→
    38→## Why Bun
    39→
    40→Node.js would work. Bun is better here for three reasons:
    41→
    42→1. **Compiled binaries.** `bun build --compile` produces a single ~58MB executable. No `node_modules` at runtime, no `npx`, no PATH configuration. The binary just runs. This matters because gstack installs into `~/.claude/skills/` where users don't expect to manage a Node.js project.
    43→
    44→2. **Native SQLite.** Cookie decryption reads Chromium's SQLite cookie database directly. Bun has `new Database()` built in — no `better-sqlite3`, no native addon compilation, no gyp. One less thing that breaks on different machines.
    45→
    46→3. **Native TypeScript.** The server runs as `bun run server.ts` during development. No compilation step, no `ts-node`, no source maps to debug. The compiled binary is for deployment; source files are for development.
    47→
    48→4. **Built-in HTTP server.** `Bun.serve()` is fast, simple, and doesn't need Express or Fastify. The server handles ~10 routes total. A framework would be overhead.
    49→
    50→The bottleneck is always Chromium, not the CLI or server. Bun's startup speed (~1ms for the compiled binary vs ~100ms for Node) is nice but not the reason we chose it. The compiled binary and native SQLite are.
    51→
    52→## The daemon model
    53→
    54→### Why not start a browser per command?
    55→
    56→Playwright can launch Chromium in ~2-3 seconds. For a single screenshot, that's fine. For a QA session with 20+ commands, it's 40+ seconds of browser startup overhead. Worse: you lose all state between commands. Cookies, localStorage, login sessions, open tabs — all gone.
    57→
    58→The daemon model means:
    59→
    60→- **Persistent state.** Log in once, stay logged in. Open a tab, it stays open. localStorage persists across commands.
    61→- **Sub-second commands.** After the first call, every command is just an HTTP POST. ~100-200ms round-trip including Chromium's work.
    62→- **Automatic lifecycle.** The server auto-starts on first use, auto-shuts down after 30 minutes idle. No process management needed.
    63→
    64→### State file
    65→
    66→The server writes `.gstack/browse.json` (atomic write via tmp + rename, mode 0o600):
    67→
    68→```json
    69→{ "pid": 12345, "port": 34567, "token": "uuid-v4", "startedAt": "...", "binaryVersion": "abc123" }
    70→```
    71→
    72→The CLI reads this file to find the server. If the file is missing, stale, or the PID is dead, the CLI spawns a new server.
    73→
    74→### Port selection
    75→
    76→Random port between 10000-60000 (retry up to 5 on collision). This means 10 Conductor workspaces can each run their own browse daemon with zero configuration and zero port conflicts. The old approach (scanning 9400-9409) broke constantly in multi-workspace setups.
    77→
    78→### Version auto-restart
    79→
    80→The build writes `git rev-parse HEAD` to `browse/dist/.version`. On each CLI invocation, if the binary's version doesn't match the running server's `binaryVersion`, the CLI kills the old server and starts a new one. This prevents the "stale binary" class of bugs entirely — rebuild the binary, next command picks it up automatically.
    81→
    82→## Security model
    83→
    84→### Localhost only
    85→
    86→The HTTP server binds to `localhost`, not `0.0.0.0`. It's not reachable from the network.
    87→
    88→### Bearer token auth
    89→
    90→Every server session generates a random UUID token, written to the state file with mode 0o600 (owner-only read). Every HTTP request must include `Authorization: Bearer <token>`. If the token doesn't match, the server returns 401.
    91→
    92→This prevents other processes on the same machine from talking to your browse server. The cookie picker UI (`/cookie-picker`) and health check (`/health`) are exempt — they're localhost-only and don't execute commands.
    93→
    94→### Cookie security
    95→
    96→Cookies are the most sensitive data gstack handles. The design:
    97→
    98→1. **Keychain access requires user approval.** First cookie import per browser triggers a macOS Keychain dialog. The user must click "Allow" or "Always Allow." gstack never silently accesses credentials.
    99→
   100→2. **Decryption happens in-process.** Cookie values are decrypted in memory (PBKDF2 + AES-128-CBC), loaded into the Playwright context, and never written to disk in plaintext. The cookie picker UI never displays cookie values — only domain names and counts.
   101→
   102→3. **Database is read-only.** gstack copies the Chromium cookie DB to a temp file (to avoid SQLite lock conflicts with the running browser) and opens it read-only. It never modifies your real browser's cookie database.
   103→
   104→4. **Key caching is per-session.** The Keychain password + derived AES key are cached in memory for the server's lifetime. When the server shuts down (idle timeout or explicit stop), the cache is gone.
   105→
   106→5. **No cookie values in logs.** Console, network, and dialog logs never contain cookie values. The `cookies` command outputs cookie metadata (domain, name, expiry) but values are truncated.
   107→
   108→### Shell injection prevention
   109→
   110→The browser registry (Comet, Chrome, Arc, Brave, Edge) is hardcoded. Database paths are constructed from known constants, never from user input. Keychain access uses `Bun.spawn()` with explicit argument arrays, not shell string interpolation.
   111→
   112→## The ref system
   113→
   114→Refs (`@e1`, `@e2`, `@c1`) are how the agent addresses page elements without writing CSS selectors or XPath.
   115→
   116→### How it works
   117→
   118→```
   119→1. Agent runs: $B snapshot -i
   120→2. Server calls Playwright's page.accessibility.snapshot()
   121→3. Parser walks the ARIA tree, assigns sequential refs: @e1, @e2, @e3...
   122→4. For each ref, builds a Playwright Locator: getByRole(role, { name }).nth(index)
   123→5. Stores Map<string, RefEntry> on the BrowserManager instance (role + name + Locator)
   124→6. Returns the annotated tree as plain text
   125→
   126→Later:
   127→7. Agent runs: $B click @e3
   128→8. Server resolves @e3 → Locator → locator.click()
   129→```
   130→
   131→### Why Locators, not DOM mutation
   132→
   133→The obvious approach is to inject `data-ref="@e1"` attributes into the DOM. This breaks on:
   134→
   135→- **CSP (Content Security Policy).** Many production sites block DOM modification from scripts.
   136→- **React/Vue/Svelte hydration.** Framework reconciliation can strip injected attributes.
   137→- **Shadow DOM.** Can't reach inside shadow roots from the outside.
   138→
   139→Playwright Locators are external to the DOM. They use the accessibility tree (which Chromium maintains internally) and `getByRole()` queries. No DOM mutation, no CSP issues, no framework conflicts.
   140→
   141→### Ref lifecycle
   142→
   143→Refs are cleared on navigation (the `framenavigated` event on the main frame). This is correct — after navigation, all locators are stale. The agent must run `snapshot` again to get fresh refs. This is by design: stale refs should fail loudly, not click the wrong element.
   144→
   145→### Ref staleness detection
   146→
   147→SPAs can mutate the DOM without triggering `framenavigated` (e.g. React router transitions, tab switches, modal opens). This makes refs stale even though the page URL didn't change. To catch this, `resolveRef()` performs an async `count()` check before using any ref:
   148→
   149→```
   150→resolveRef(@e3) → entry = refMap.get("e3")
   151→                → count = await entry.locator.count()
   152→                → if count === 0: throw "Ref @e3 is stale — element no longer exists. Run 'snapshot' to get fresh refs."
   153→                → if count > 0: return { locator }
   154→```
   155→
   156→This fails fast (~5ms overhead) instead of letting Playwright's 30-second action timeout expire on a missing element. The `RefEntry` stores `role` and `name` metadata alongside the Locator so the error message can tell the agent what the element was.
   157→
   158→### Cursor-interactive refs (@c)
   159→
   160→The `-C` flag finds elements that are clickable but not in the ARIA tree — things styled with `cursor: pointer`, elements with `onclick` attributes, or custom `tabindex`. These get `@c1`, `@c2` refs in a separate namespace. This catches custom components that frameworks render as `<div>` but are actually buttons.
   161→
   162→## Logging architecture
   163→
   164→Three ring buffers (50,000 entries each, O(1) push):
   165→
   166→```
   167→Browser events → CircularBuffer (in-memory) → Async flush to .gstack/*.log
   168→```
   169→
   170→Console messages, network requests, and dialog events each have their own buffer. Flushing happens every 1 second — the server appends only new entries since the last flush. This means:
   171→
   172→- HTTP request handling is never blocked by disk I/O
   173→- Logs survive server crashes (up to 1 second of data loss)
   174→- Memory is bounded (50K entries × 3 buffers)
   175→- Disk files are append-only, readable by external tools
   176→
   177→The `console`, `network`, and `dialog` commands read from the in-memory buffers, not disk. Disk files are for post-mortem debugging.
   178→
   179→## SKILL.md template system
   180→
   181→### The problem
   182→
   183→SKILL.md files tell Claude how to use the browse commands. If the docs list a flag that doesn't exist, or miss a command that was added, the agent hits errors. Hand-maintained docs always drift from code.
   184→
   185→### The solution
   186→
   187→```
   188→SKILL.md.tmpl          (human-written prose + placeholders)
   189→       ↓
   190→gen-skill-docs.ts      (reads source code metadata)
   191→       ↓
   192→SKILL.md               (committed, auto-generated sections)
   193→```
   194→
   195→Templates contain the workflows, tips, and examples that require human judgment. Placeholders are filled from source code at build time:
   196→
   197→| Placeholder | Source | What it generates |
   198→|-------------|--------|-------------------|
   199→| `{{COMMAND_REFERENCE}}` | `commands.ts` | Categorized command table |
   200→| `{{SNAPSHOT_FLAGS}}` | `snapshot.ts` | Flag reference with examples |


     1→---
     2→name: design-review
     3→version: 2.0.0
     4→description: |
     5→  Designer's eye QA: finds visual inconsistency, spacing issues, hierarchy problems,
     6→  AI slop patterns, and slow interactions — then fixes them. Iteratively fixes issues
     7→  in source code, committing each fix atomically and re-verifying with before/after
     8→  screenshots. For plan-mode design review (before implementation), use /plan-design-review.
     9→allowed-tools:
    10→  - Bash
    11→  - Read
    12→  - Write
    13→  - Edit
    14→  - Glob
    15→  - Grep
    16→  - AskUserQuestion
    17→  - WebSearch
    18→---
    19→
    20→{{PREAMBLE}}
    21→
    22→# /design-review: Design Audit → Fix → Verify
    23→
    24→You are a senior product designer AND a frontend engineer. Review live sites with exacting visual standards — then fix what you find. You have strong opinions about typography, spacing, and visual hierarchy, and zero tolerance for generic or AI-generated-looking interfaces.
    25→
    26→## Setup
    27→
    28→**Parse the user's request for these parameters:**
    29→
    30→| Parameter | Default | Override example |
    31→|-----------|---------|-----------------:|
    32→| Target URL | (auto-detect or ask) | `https://myapp.com`, `http://localhost:3000` |
    33→| Scope | Full site | `Focus on the settings page`, `Just the homepage` |
    34→| Depth | Standard (5-8 pages) | `--quick` (homepage + 2), `--deep` (10-15 pages) |
    35→| Auth | None | `Sign in as user@example.com`, `Import cookies` |
    36→
    37→**If no URL is given and you're on a feature branch:** Automatically enter **diff-aware mode** (see Modes below).
    38→
    39→**If no URL is given and you're on main/master:** Ask the user for a URL.
    40→
    41→**Check for DESIGN.md:**
    42→
    43→Look for `DESIGN.md`, `design-system.md`, or similar in the repo root. If found, read it — all design decisions must be calibrated against it. Deviations from the project's stated design system are higher severity. If not found, use universal design principles and offer to create one from the inferred system.
    44→
    45→**Require clean working tree before starting:**
    46→
    47→```bash
    48→if [ -n "$(git status --porcelain)" ]; then
    49→  echo "ERROR: Working tree is dirty. Commit or stash changes before running /design-review."
    50→  exit 1
    51→fi
    52→```
    53→
    54→**Find the browse binary:**
    55→
    56→{{BROWSE_SETUP}}
    57→
    58→**Check test framework (bootstrap if needed):**
    59→
    60→{{TEST_BOOTSTRAP}}
    61→
    62→**Create output directories:**
    63→
    64→```bash
    65→REPORT_DIR=".gstack/design-reports"
    66→mkdir -p "$REPORT_DIR/screenshots"
    67→```
    68→
    69→---
    70→
    71→## Phases 1-6: Design Audit Baseline
    72→
    73→{{DESIGN_METHODOLOGY}}
    74→
    75→Record baseline design score and AI slop score at end of Phase 6.
    76→
    77→---
    78→
    79→## Output Structure
    80→
    81→```
    82→.gstack/design-reports/
    83→├── design-audit-{domain}-{YYYY-MM-DD}.md    # Structured report
    84→├── screenshots/
    85→│   ├── first-impression.png                  # Phase 1
    86→│   ├── {page}-annotated.png                  # Per-page annotated
    87→│   ├── {page}-mobile.png                     # Responsive
    88→│   ├── {page}-tablet.png
    89→│   ├── {page}-desktop.png
    90→│   ├── finding-001-before.png                # Before fix
    91→│   ├── finding-001-after.png                 # After fix
    92→│   └── ...
    93→└── design-baseline.json                      # For regression mode
    94→```
    95→
    96→---
    97→
    98→## Phase 7: Triage
    99→
   100→Sort all discovered findings by impact, then decide which to fix:


     1→---
     2→name: document-release
     3→version: 1.0.0
     4→description: |
     5→  Post-ship documentation update. Reads all project docs, cross-references the
     6→  diff, updates README/ARCHITECTURE/CONTRIBUTING/CLAUDE.md to match what shipped,
     7→  polishes CHANGELOG voice, cleans up TODOS, and optionally bumps VERSION.
     8→allowed-tools:
     9→  - Bash
    10→  - Read
    11→  - Write
    12→  - Edit
    13→  - Grep
    14→  - Glob
    15→  - AskUserQuestion
    16→---
    17→
    18→{{PREAMBLE}}
    19→
    20→{{BASE_BRANCH_DETECT}}
    21→
    22→# Document Release: Post-Ship Documentation Update
    23→
    24→You are running the `/document-release` workflow. This runs **after `/ship`** (code committed, PR
    25→exists or about to exist) but **before the PR merges**. Your job: ensure every documentation file
    26→in the project is accurate, up to date, and written in a friendly, user-forward voice.
    27→
    28→You are mostly automated. Make obvious factual updates directly. Stop and ask only for risky or
    29→subjective decisions.
    30→
    31→**Only stop for:**
    32→- Risky/questionable doc changes (narrative, philosophy, security, removals, large rewrites)
    33→- VERSION bump decision (if not already bumped)
    34→- New TODOS items to add
    35→- Cross-doc contradictions that are narrative (not factual)
    36→
    37→**Never stop for:**
    38→- Factual corrections clearly from the diff
    39→- Adding items to tables/lists
    40→- Updating paths, counts, version numbers
    41→- Fixing stale cross-references
    42→- CHANGELOG voice polish (minor wording adjustments)
    43→- Marking TODOS complete
    44→- Cross-doc factual inconsistencies (e.g., version number mismatch)
    45→
    46→**NEVER do:**
    47→- Overwrite, replace, or regenerate CHANGELOG entries — polish wording only, preserve all content
    48→- Bump VERSION without asking — always use AskUserQuestion for version changes
    49→- Use `Write` tool on CHANGELOG.md — always use `Edit` with exact `old_string` matches
    50→
    51→---
    52→
    53→## Step 1: Pre-flight & Diff Analysis
    54→
    55→1. Check the current branch. If on the base branch, **abort**: "You're on the base branch. Run from a feature branch."
    56→
    57→2. Gather context about what changed:
    58→
    59→```bash
    60→git diff <base>...HEAD --stat
    61→```
    62→
    63→```bash
    64→git log <base>..HEAD --oneline
    65→```
    66→
    67→```bash
    68→git diff <base>...HEAD --name-only
    69→```
    70→
    71→3. Discover all documentation files in the repo:
    72→
    73→```bash
    74→find . -maxdepth 2 -name "*.md" -not -path "./.git/*" -not -path "./node_modules/*" -not -path "./.gstack/*" -not -path "./.context/*" | sort
    75→```
    76→
    77→4. Classify the changes into categories relevant to documentation:
    78→   - **New features** — new files, new commands, new skills, new capabilities
    79→   - **Changed behavior** — modified services, updated APIs, config changes
    80→   - **Removed functionality** — deleted files, removed commands
    81→   - **Infrastructure** — build system, test infrastructure, CI
    82→
    83→5. Output a brief summary: "Analyzing N files changed across M commits. Found K documentation files to review."
    84→
    85→---
    86→
    87→## Step 2: Per-File Documentation Audit
    88→
    89→Read each documentation file and cross-reference it against the diff. Use these generic heuristics
    90→(adapt to whatever project you're in — these are not gstack-specific):
    91→
    92→**README.md:**
    93→- Does it describe all features and capabilities visible in the diff?
    94→- Are install/setup instructions consistent with the changes?
    95→- Are examples, demos, and usage descriptions still valid?
    96→- Are troubleshooting steps still accurate?
    97→
    98→**ARCHITECTURE.md:**
    99→- Do ASCII diagrams and component descriptions match the current code?
   100→- Are design decisions and "why" explanations still accurate?
   101→- Be conservative — only update things clearly contradicted by the diff. Architecture docs
   102→  describe things unlikely to change frequently.
   103→
   104→**CONTRIBUTING.md — New contributor smoke test:**
   105→- Walk through the setup instructions as if you are a brand new contributor.
   106→- Are the listed commands accurate? Would each step succeed?
   107→- Do test tier descriptions match the current test infrastructure?
   108→- Are workflow descriptions (dev setup, contributor mode, etc.) current?
   109→- Flag anything that would fail or confuse a first-time contributor.
   110→
   111→**CLAUDE.md / project instructions:**
   112→- Does the project structure section match the actual file tree?
   113→- Are listed commands and scripts accurate?
   114→- Do build/test instructions match what's in package.json (or equivalent)?
   115→
   116→**Any other .md files:**
   117→- Read the file, determine its purpose and audience.
   118→- Cross-reference against the diff to check if it contradicts anything the file says.
   119→
   120→For each file, classify needed updates as:


ls -la /root/projects/gits/gstack/ | head -30

List main gstack directory


total 216
drwxr-xr-x 22 root root  4096 Mar 18 15:58 .
drwxr-xr-x  4 root root    33 Mar 18 15:58 ..
-rw-r--r--  1 root root 20797 Mar 18 15:58 ARCHITECTURE.md
drwxr-xr-x  2 root root   135 Mar 18 15:58 bin
drwxr-xr-x  5 root root    77 Mar 18 15:58 browse
-rw-r--r--  1 root root 16159 Mar 18 15:58 BROWSER.md
-rw-r--r--  1 root root 46273 Mar 18 15:58 CHANGELOG.md
-rw-r--r--  1 root root  9988 Mar 18 15:58 CLAUDE.md
-rw-r--r--  1 root root    87 Mar 18 15:58 conductor.json
-rw-r--r--  1 root root 13456 Mar 18 15:58 CONTRIBUTING.md
drwxr-xr-x  2 root root    43 Mar 18 15:58 design-consultation
drwxr-xr-x  2 root root    43 Mar 18 15:58 design-review
drwxr-xr-x  3 root root    37 Mar 18 15:58 docs
drwxr-xr-x  2 root root    43 Mar 18 15:58 document-release
-rw-r--r--  1 root root   171 Mar 18 15:58 .env.example
drwxr-xr-x  8 root root   163 Mar 18 15:58 .git
drwxr-xr-x  3 root root    23 Mar 18 15:58 .github
-rw-r--r--  1 root root   127 Mar 18 15:58 .gitignore
drwxr-xr-x  2 root root    43 Mar 18 15:58 gstack-upgrade
-rw-r--r--  1 root root  1066 Mar 18 15:58 LICENSE
-rw-r--r--  1 root root  1879 Mar 18 15:58 package.json
drwxr-xr-x  2 root root    43 Mar 18 15:58 plan-ceo-review
drwxr-xr-x  2 root root    43 Mar 18 15:58 plan-design-review
drwxr-xr-x  2 root root    43 Mar 18 15:58 plan-eng-review
drwxr-xr-x  4 root root    78 Mar 18 15:58 qa
drwxr-xr-x  2 root root    43 Mar 18 15:58 qa-only
-rw-r--r--  1 root root 14319 Mar 18 15:58 README.md
drwxr-xr-x  2 root root    43 Mar 18 15:58 retro
drwxr-xr-x  2 root root   139 Mar 18 15:58 review


     1→---
     2→name: gstack-upgrade
     3→version: 1.1.0
     4→description: |
     5→  Upgrade gstack to the latest version. Detects global vs vendored install,
     6→  runs the upgrade, and shows what's new.
     7→allowed-tools:
     8→  - Bash
     9→  - Read
    10→  - Write
    11→  - AskUserQuestion
    12→---
    13→
    14→# /gstack-upgrade
    15→
    16→Upgrade gstack to the latest version and show what's new.
    17→
    18→## Inline upgrade flow
    19→
    20→This section is referenced by all skill preambles when they detect `UPGRADE_AVAILABLE`.
    21→
    22→### Step 1: Ask the user (or auto-upgrade)
    23→
    24→First, check if auto-upgrade is enabled:
    25→```bash
    26→_AUTO=""
    27→[ "${GSTACK_AUTO_UPGRADE:-}" = "1" ] && _AUTO="true"
    28→[ -z "$_AUTO" ] && _AUTO=$(~/.claude/skills/gstack/bin/gstack-config get auto_upgrade 2>/dev/null || true)
    29→echo "AUTO_UPGRADE=$_AUTO"
    30→```
    31→
    32→**If `AUTO_UPGRADE=true` or `AUTO_UPGRADE=1`:** Skip AskUserQuestion. Log "Auto-upgrading gstack v{old} → v{new}..." and proceed directly to Step 2. If `./setup` fails during auto-upgrade, restore from backup (`.bak` directory) and warn the user: "Auto-upgrade failed — restored previous version. Run `/gstack-upgrade` manually to retry."
    33→
    34→**Otherwise**, use AskUserQuestion:
    35→- Question: "gstack **v{new}** is available (you're on v{old}). Upgrade now?"
    36→- Options: ["Yes, upgrade now", "Always keep me up to date", "Not now", "Never ask again"]
    37→
    38→**If "Yes, upgrade now":** Proceed to Step 2.
    39→
    40→**If "Always keep me up to date":**
    41→```bash
    42→~/.claude/skills/gstack/bin/gstack-config set auto_upgrade true
    43→```
    44→Tell user: "Auto-upgrade enabled. Future updates will install automatically." Then proceed to Step 2.
    45→
    46→**If "Not now":** Write snooze state with escalating backoff (first snooze = 24h, second = 48h, third+ = 1 week), then continue with the current skill. Do not mention the upgrade again.
    47→```bash
    48→_SNOOZE_FILE=~/.gstack/update-snoozed
    49→_REMOTE_VER="{new}"
    50→_CUR_LEVEL=0
    51→if [ -f "$_SNOOZE_FILE" ]; then
    52→  _SNOOZED_VER=$(awk '{print $1}' "$_SNOOZE_FILE")
    53→  if [ "$_SNOOZED_VER" = "$_REMOTE_VER" ]; then
    54→    _CUR_LEVEL=$(awk '{print $2}' "$_SNOOZE_FILE")
    55→    case "$_CUR_LEVEL" in *[!0-9]*) _CUR_LEVEL=0 ;; esac
    56→  fi
    57→fi
    58→_NEW_LEVEL=$((_CUR_LEVEL + 1))
    59→[ "$_NEW_LEVEL" -gt 3 ] && _NEW_LEVEL=3
    60→echo "$_REMOTE_VER $_NEW_LEVEL $(date +%s)" > "$_SNOOZE_FILE"
    61→```
    62→Note: `{new}` is the remote version from the `UPGRADE_AVAILABLE` output — substitute it from the update check result.
    63→
    64→Tell user the snooze duration: "Next reminder in 24h" (or 48h or 1 week, depending on level). Tip: "Set `auto_upgrade: true` in `~/.gstack/config.yaml` for automatic upgrades."
    65→
    66→**If "Never ask again":**
    67→```bash
    68→~/.claude/skills/gstack/bin/gstack-config set update_check false
    69→```
    70→Tell user: "Update checks disabled. Run `~/.claude/skills/gstack/bin/gstack-config set update_check true` to re-enable."
    71→Continue with the current skill.
    72→
    73→### Step 2: Detect install type
    74→
    75→```bash
    76→if [ -d "$HOME/.claude/skills/gstack/.git" ]; then
    77→  INSTALL_TYPE="global-git"
    78→  INSTALL_DIR="$HOME/.claude/skills/gstack"
    79→elif [ -d ".claude/skills/gstack/.git" ]; then
    80→  INSTALL_TYPE="local-git"
    81→  INSTALL_DIR=".claude/skills/gstack"
    82→elif [ -d ".claude/skills/gstack" ]; then
    83→  INSTALL_TYPE="vendored"
    84→  INSTALL_DIR=".claude/skills/gstack"
    85→elif [ -d "$HOME/.claude/skills/gstack" ]; then
    86→  INSTALL_TYPE="vendored-global"
    87→  INSTALL_DIR="$HOME/.claude/skills/gstack"
    88→else
    89→  echo "ERROR: gstack not found"
    90→  exit 1
    91→fi
    92→echo "Install type: $INSTALL_TYPE at $INSTALL_DIR"
    93→```
    94→
    95→The install type and directory path printed above will be used in all subsequent steps.
    96→
    97→### Step 3: Save old version
    98→
    99→Use the install directory from Step 2's output below:
   100→
   101→```bash
   102→OLD_VERSION=$(cat "$INSTALL_DIR/VERSION" 2>/dev/null || echo "unknown")
   103→```
   104→
   105→### Step 4: Upgrade
   106→
   107→Use the install type and directory detected in Step 2:
   108→
   109→**For git installs** (global-git, local-git):
   110→```bash
   111→cd "$INSTALL_DIR"
   112→STASH_OUTPUT=$(git stash 2>&1)
   113→git fetch origin
   114→git reset --hard origin/main
   115→./setup
   116→```
   117→If `$STASH_OUTPUT` contains "Saved working directory", warn the user: "Note: local changes were stashed. Run `git stash pop` in the skill directory to restore them."
   118→
   119→**For vendored installs** (vendored, vendored-global):
   120→```bash
   121→PARENT=$(dirname "$INSTALL_DIR")
   122→TMP_DIR=$(mktemp -d)
   123→git clone --depth 1 https://github.com/garrytan/gstack.git "$TMP_DIR/gstack"
   124→mv "$INSTALL_DIR" "$INSTALL_DIR.bak"
   125→mv "$TMP_DIR/gstack" "$INSTALL_DIR"
   126→cd "$INSTALL_DIR" && ./setup
   127→rm -rf "$INSTALL_DIR.bak" "$TMP_DIR"
   128→```
   129→
   130→### Step 4.5: Sync local vendored copy
   131→
   132→Use the install directory from Step 2. Check if there's also a local vendored copy that needs updating:
   133→
   134→```bash
   135→_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
   136→LOCAL_GSTACK=""
   137→if [ -n "$_ROOT" ] && [ -d "$_ROOT/.claude/skills/gstack" ]; then
   138→  _RESOLVED_LOCAL=$(cd "$_ROOT/.claude/skills/gstack" && pwd -P)
   139→  _RESOLVED_PRIMARY=$(cd "$INSTALL_DIR" && pwd -P)
   140→  if [ "$_RESOLVED_LOCAL" != "$_RESOLVED_PRIMARY" ]; then
   141→    LOCAL_GSTACK="$_ROOT/.claude/skills/gstack"
   142→  fi
   143→fi
   144→echo "LOCAL_GSTACK=$LOCAL_GSTACK"
   145→```
   146→
   147→If `LOCAL_GSTACK` is non-empty, update it by copying from the freshly-upgraded primary install (same approach as README vendored install):
   148→```bash
   149→mv "$LOCAL_GSTACK" "$LOCAL_GSTACK.bak"
   150→cp -Rf "$INSTALL_DIR" "$LOCAL_GSTACK"
   151→rm -rf "$LOCAL_GSTACK/.git"
   152→cd "$LOCAL_GSTACK" && ./setup
   153→rm -rf "$LOCAL_GSTACK.bak"
   154→```
   155→Tell user: "Also updated vendored copy at `$LOCAL_GSTACK` — commit `.claude/skills/gstack/` when you're ready."
   156→
   157→If `./setup` fails, restore from backup and warn the user:
   158→```bash
   159→rm -rf "$LOCAL_GSTACK"
   160→mv "$LOCAL_GSTACK.bak" "$LOCAL_GSTACK"
   161→```
   162→Tell user: "Sync failed — restored previous version at `$LOCAL_GSTACK`. Run `/gstack-upgrade` manually to retry."
   163→
   164→### Step 5: Write marker + clear cache
   165→
   166→```bash
   167→mkdir -p ~/.gstack
   168→echo "$OLD_VERSION" > ~/.gstack/just-upgraded-from
   169→rm -f ~/.gstack/last-update-check
   170→rm -f ~/.gstack/update-snoozed
   171→```
   172→
   173→### Step 6: Show What's New
   174→
   175→Read `$INSTALL_DIR/CHANGELOG.md`. Find all version entries between the old version and the new version. Summarize as 5-7 bullets grouped by theme. Don't overwhelm — focus on user-facing changes. Skip internal refactors unless they're significant.
   176→
   177→Format:
   178→```
   179→gstack v{new} — upgraded from v{old}!
   180→
   181→What's new:
   182→- [bullet 1]
   183→- [bullet 2]
   184→- ...
   185→
   186→Happy shipping!
   187→```
   188→
   189→### Step 7: Continue
   190→
   191→After showing What's New, continue with whatever skill the user originally invoked. The upgrade is done — no further action needed.
   192→
   193→---
   194→
   195→## Standalone usage
   196→
   197→When invoked directly as `/gstack-upgrade` (not from a preamble):
   198→
   199→1. Force a fresh update check (bypass cache):
   200→```bash
   201→~/.claude/skills/gstack/bin/gstack-update-check --force 2>/dev/null || \
   202→.claude/skills/gstack/bin/gstack-update-check --force 2>/dev/null || true
   203→```
   204→Use the output to determine if an upgrade is available.
   205→
   206→2. If `UPGRADE_AVAILABLE <old> <new>`: follow Steps 2-6 above.
   207→
   208→3. If no output (primary is up to date): check for a stale local vendored copy.
   209→
   210→Run the Step 2 bash block above to detect the primary install type and directory (`INSTALL_TYPE` and `INSTALL_DIR`). Then run the Step 4.5 detection bash block above to check for a local vendored copy (`LOCAL_GSTACK`).
   211→
   212→**If `LOCAL_GSTACK` is empty** (no local vendored copy): tell the user "You're already on the latest version (v{version})."
   213→
   214→**If `LOCAL_GSTACK` is non-empty**, compare versions:
   215→```bash
   216→PRIMARY_VER=$(cat "$INSTALL_DIR/VERSION" 2>/dev/null || echo "unknown")
   217→LOCAL_VER=$(cat "$LOCAL_GSTACK/VERSION" 2>/dev/null || echo "unknown")
   218→echo "PRIMARY=$PRIMARY_VER LOCAL=$LOCAL_VER"
   219→```
   220→
   221→**If versions differ:** follow the Step 4.5 sync bash block above to update the local copy from the primary. Tell user: "Global v{PRIMARY_VER} is up to date. Updated local vendored copy from v{LOCAL_VER} → v{PRIMARY_VER}. Commit `.claude/skills/gstack/` when you're ready."
   222→
   223→**If versions match:** tell the user "You're on the latest version (v{PRIMARY_VER}). Global and local vendored copy are both up to date."
   224→


     1→---
     2→name: setup-browser-cookies
     3→version: 1.0.0
     4→description: |
     5→  Import cookies from your real browser (Comet, Chrome, Arc, Brave, Edge) into the
     6→  headless browse session. Opens an interactive picker UI where you select which
     7→  cookie domains to import. Use before QA testing authenticated pages.
     8→allowed-tools:
     9→  - Bash
    10→  - Read
    11→  - AskUserQuestion
    12→---
    13→
    14→{{PREAMBLE}}
    15→
    16→# Setup Browser Cookies
    17→
    18→Import logged-in sessions from your real Chromium browser into the headless browse session.
    19→
    20→## How it works
    21→
    22→1. Find the browse binary
    23→2. Run `cookie-import-browser` to detect installed browsers and open the picker UI
    24→3. User selects which cookie domains to import in their browser
    25→4. Cookies are decrypted and loaded into the Playwright session
    26→
    27→## Steps
    28→
    29→### 1. Find the browse binary
    30→
    31→{{BROWSE_SETUP}}
    32→
    33→### 2. Open the cookie picker
    34→
    35→```bash
    36→$B cookie-import-browser
    37→```
    38→
    39→This auto-detects installed Chromium browsers (Comet, Chrome, Arc, Brave, Edge) and opens
    40→an interactive picker UI in your default browser where you can:
    41→- Switch between installed browsers
    42→- Search domains
    43→- Click "+" to import a domain's cookies
    44→- Click trash to remove imported cookies
    45→
    46→Tell the user: **"Cookie picker opened — select the domains you want to import in your browser, then tell me when you're done."**
    47→
    48→### 3. Direct import (alternative)
    49→
    50→If the user specifies a domain directly (e.g., `/setup-browser-cookies github.com`), skip the UI:
    51→
    52→```bash
    53→$B cookie-import-browser comet --domain github.com
    54→```
    55→
    56→Replace `comet` with the appropriate browser if specified.
    57→
    58→### 4. Verify
    59→
    60→After the user confirms they're done:
    61→
    62→```bash
    63→$B cookies
    64→```
    65→
    66→Show the user a summary of imported cookies (domain counts).
    67→
    68→## Notes
    69→
    70→- First import per browser may trigger a macOS Keychain dialog — click "Allow" / "Always Allow"
    71→- Cookie picker is served on the same port as the browse server (no extra process)
    72→- Only domain names and cookie counts are shown in the UI — no cookie values are exposed
    73→- The browse session persists cookies between commands, so imported cookies work immediately
    74→