mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-10 15:09:00 +02:00
feat: first-run activation — project-aware scaffold, router front door, onboarding nudges
Adds the activation system that drives a new install toward a concrete first move: - bin/gstack-first-task-detect: local-git+filesystem repo classifier emitting one validated enum bucket (greenfield/code_<lang>/branch_ahead/dirty_default/clean_default), portable timeouts, fail-safe empty output. - generate-first-run-guidance.ts: unified preamble section — first-run project-aware scaffold + returning-session plan->review->ship tip, gated on a persistent .activated marker and never run in headless. Detection wired lazily in generate-preamble-bash.ts. - SKILL.md.tmpl: top-level gstack skill is now a pure router (browse body removed; it lives in /browse), routing any request and sending browser/QA work to /browse. - setup: first-move nudge on first install. office-hours: closing handoff that launches the next review via the Skill tool. - telemetry-ingest: accept onboarding/first_task_scaffold_shown/handoff/route event types.
This commit is contained in:
@@ -32,6 +32,7 @@ import {
|
||||
import { generateLakeIntro } from './preamble/generate-lake-intro';
|
||||
import { generateTelemetryPrompt } from './preamble/generate-telemetry-prompt';
|
||||
import { generateProactivePrompt } from './preamble/generate-proactive-prompt';
|
||||
import { generateFirstRunGuidance } from './preamble/generate-first-run-guidance';
|
||||
import { generateRoutingInjection } from './preamble/generate-routing-injection';
|
||||
import { generateVendoringDeprecation } from './preamble/generate-vendoring-deprecation';
|
||||
import { generateSpawnedSessionCheck } from './preamble/generate-spawned-session-check';
|
||||
@@ -94,6 +95,7 @@ export function generatePreamble(ctx: TemplateContext): string {
|
||||
generateLakeIntro(),
|
||||
generateTelemetryPrompt(ctx),
|
||||
generateProactivePrompt(ctx),
|
||||
generateFirstRunGuidance(ctx),
|
||||
generateRoutingInjection(ctx),
|
||||
generateVendoringDeprecation(ctx),
|
||||
generateSpawnedSessionCheck(),
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { TemplateContext } from '../types';
|
||||
|
||||
// First-run guidance (P4 scaffold + P3 loop tip), unified into one section.
|
||||
// Branches on the persistent `.activated` lifecycle marker — NOT `_SESSIONS`,
|
||||
// which counts concurrent sessions in the last 120 min, not first-vs-returning.
|
||||
//
|
||||
// The FIRST_TASK enum is computed at runtime in generate-preamble-bash.ts (gated
|
||||
// so the detector only runs on the first run) and printed as `FIRST_TASK: <token>`.
|
||||
// This section maps the token the model SAW in that output to a one-line nudge
|
||||
// (no description string ever crosses an eval boundary) and sets markers:
|
||||
// ~/.gstack/.activated — set at the end of the first-ever skill run
|
||||
// ~/.gstack/.first-loop-tip-shown — set when the returning-session tip is shown
|
||||
//
|
||||
// Note: bash blocks run in separate shells, so the runtime token cannot be read
|
||||
// from a shell var here — the model substitutes the token it saw for TASK_TOKEN,
|
||||
// exactly like the Telemetry section substitutes SKILL_NAME/OUTCOME.
|
||||
export function generateFirstRunGuidance(ctx: TemplateContext): string {
|
||||
return `## First-run guidance (one-time)
|
||||
|
||||
If \`ACTIVATED\` is \`no\` (first skill run on this machine) AND the preamble printed a non-empty \`FIRST_TASK:\` value that is NOT \`nongit\`: show ONE short, project-specific line mapped from the token, as a heads-up, then CONTINUE with whatever the user actually asked — do NOT halt their task. Map the token: \`greenfield\` → "Fresh repo — shape it first with \`/spec\` or \`/office-hours\`." \`code_node\`/\`code_python\`/\`code_rust\`/\`code_go\`/\`code_ruby\`/\`code_ios\` → "There's code here — \`/qa\` to see it work, or \`/investigate\` if something's off." \`branch_ahead\` → "Unshipped work on this branch — \`/review\` then \`/ship\`." \`dirty_default\` → "Uncommitted changes — \`/review\` before committing." \`clean_default\` → "Pick one: \`/spec\`, \`/investigate\`, or \`/qa\`." Then substitute the token you saw for TASK_TOKEN and run (best-effort), and mark activated:
|
||||
\`\`\`bash
|
||||
${ctx.paths.binDir}/gstack-telemetry-log --event-type first_task_scaffold_shown --skill "TASK_TOKEN" --outcome shown 2>/dev/null || true
|
||||
touch ~/.gstack/.activated 2>/dev/null || true
|
||||
\`\`\`
|
||||
|
||||
If \`ACTIVATED\` is \`no\` but \`FIRST_TASK:\` is empty or \`nongit\` (headless, non-git, or nothing actionable): show nothing, just run \`touch ~/.gstack/.activated 2>/dev/null || true\`.
|
||||
|
||||
Else if \`ACTIVATED\` is \`yes\` AND \`FIRST_LOOP_SHOWN\` is \`no\`: say once as a heads-up (then continue):
|
||||
|
||||
> Tip: gstack pays off when you complete one loop — **plan → review → ship**. A common first loop: \`/office-hours\` or \`/spec\` to shape it, \`/plan-eng-review\` to lock it, then \`/ship\`.
|
||||
|
||||
Then run \`touch ~/.gstack/.first-loop-tip-shown 2>/dev/null || true\`.
|
||||
|
||||
Skip this section if \`ACTIVATED\` and \`FIRST_LOOP_SHOWN\` are both \`yes\`.`;
|
||||
}
|
||||
@@ -43,6 +43,17 @@ echo "SESSION_KIND: $_SESSION_KIND"
|
||||
if [ "$_SESSION_KIND" != "headless" ] && { [ -n "\${CONDUCTOR_WORKSPACE_PATH:-}" ] || [ -n "\${CONDUCTOR_PORT:-}" ]; }; then
|
||||
echo "CONDUCTOR_SESSION: true"
|
||||
fi
|
||||
_ACTIVATED=$([ -f ~/.gstack/.activated ] && echo "yes" || echo "no")
|
||||
_FIRST_LOOP_SHOWN=$([ -f ~/.gstack/.first-loop-tip-shown ] && echo "yes" || echo "no")
|
||||
echo "ACTIVATED: $_ACTIVATED"
|
||||
echo "FIRST_LOOP_SHOWN: $_FIRST_LOOP_SHOWN"
|
||||
# First-run project detection: run the detector ONLY on the first-ever skill run
|
||||
# (ACTIVATED=no, interactive) so it stays off the hot path for every run after.
|
||||
_FIRST_TASK=""
|
||||
if [ "$_ACTIVATED" = "no" ] && [ "$_SESSION_KIND" != "headless" ]; then
|
||||
_FIRST_TASK=$(${ctx.paths.binDir}/gstack-first-task-detect 2>/dev/null || true)
|
||||
fi
|
||||
echo "FIRST_TASK: $_FIRST_TASK"
|
||||
_LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no")
|
||||
echo "LAKE_INTRO: $_LAKE_SEEN"
|
||||
_TEL=$(${ctx.paths.binDir}/gstack-config get telemetry 2>/dev/null || true)
|
||||
|
||||
Reference in New Issue
Block a user