Files
gstack/bin/gstack-skill-end
T
Garry TanandClaude Fable 5 b1199afc03 feat(bin): gstack-skill-start + gstack-skill-end — the preamble runtime, consolidated
Absorbs the ~13KB of bash every tier-2+ SKILL.md inlined twice over (bootstrap
fence + artifacts-sync fence) and the skill-end telemetry/sync fences. Same
KEY: value STATUS-line contract the prose interprets, plus SKILL_START_PROTO
handshake (OV5), SESSION_ID/TEL_START echoes, GSTACK_HOME-normalized state
paths (EOV7), --parent-pid session identity (EOV5: $PPID inside the script is
the ephemeral tool-call shell), OV4 sanitization of passthrough output, and a
receipted daily artifacts pull (_receipted_git, brain-sync class, fail-closed).
Per-line || true error style throughout (F3) — a mid-script failure never drops
later STATUS lines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 15:36:35 +00:00

61 lines
2.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# gstack-skill-end — skill-completion telemetry + artifacts-sync drain,
# consolidated (token-reduction Phase 1). Replaces the inline "Telemetry (run
# last)" fence and the skill-end brain-sync fence in every generated SKILL.md.
#
# The generated prose calls this once at workflow completion:
# gstack-skill-end --skill NAME --outcome success|error|abort|unknown \
# --session-id ID --tel-start EPOCH [--used-browse yes|no] \
# [--error-message MSG] [--failed-step STEP]
#
# SESSION_ID and TEL_START come from gstack-skill-start's echoed STATUS lines
# (inline shell vars never survived across Bash tool calls, so durations were
# already unreliable; passing them explicitly makes them real).
# Error style (F3): per-line `|| true`, never `set -e`.
SKILL_NAME="unknown"; OUTCOME="unknown"; SESSION_ID=""; TEL_START=""
USED_BROWSE="no"; ERROR_MESSAGE=""; FAILED_STEP=""
while [ $# -gt 0 ]; do
case "$1" in
--skill) SKILL_NAME="$2"; shift 2 ;;
--outcome) OUTCOME="$2"; shift 2 ;;
--session-id) SESSION_ID="$2"; shift 2 ;;
--tel-start) TEL_START="$2"; shift 2 ;;
--used-browse) USED_BROWSE="$2"; shift 2 ;;
--error-message) ERROR_MESSAGE="$2"; shift 2 ;;
--failed-step) FAILED_STEP="$2"; shift 2 ;;
*) shift ;;
esac
done
_SCRIPT_DIR=$(cd "$(dirname "$0")" 2>/dev/null && pwd)
_BIN="$_SCRIPT_DIR"
_GH="${GSTACK_HOME:-$HOME/.gstack}"
_TEL=$("$_BIN/gstack-config" get telemetry 2>/dev/null || echo off)
_TEL_END=$(date +%s)
case "$TEL_START" in ''|*[!0-9]*) TEL_START="$_TEL_END" ;; esac
_TEL_DUR=$(( _TEL_END - TEL_START ))
[ -n "$SESSION_ID" ] && rm -f "$_GH/analytics/.pending-$SESSION_ID" 2>/dev/null || true
# Skill-end artifacts sync (drain + push; formerly its own inline fence).
"$_BIN/gstack-brain-sync" --discover-new 2>/dev/null || true
"$_BIN/gstack-brain-sync" --once 2>/dev/null || true
# Session timeline: local-only, never sent anywhere.
"$_BIN/gstack-timeline-log" '{"skill":"'"$SKILL_NAME"'","event":"completed","branch":"'"$(git branch --show-current 2>/dev/null || echo unknown)"'","outcome":"'"$OUTCOME"'","duration_s":"'"$_TEL_DUR"'","session":"'"$SESSION_ID"'"}' 2>/dev/null || true
# Local analytics (gated on telemetry setting).
if [ "$_TEL" != "off" ]; then
echo '{"skill":"'"$SKILL_NAME"'","duration_s":"'"$_TEL_DUR"'","outcome":"'"$OUTCOME"'","browse":"'"$USED_BROWSE"'","session":"'"$SESSION_ID"'","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$_GH/analytics/skill-usage.jsonl" 2>/dev/null || true
fi
# Remote telemetry (opt-in, requires binary).
if [ "$_TEL" != "off" ] && [ -x "$_BIN/gstack-telemetry-log" ]; then
"$_BIN/gstack-telemetry-log" \
--skill "$SKILL_NAME" --duration "$_TEL_DUR" --outcome "$OUTCOME" \
--used-browse "$USED_BROWSE" --session-id "$SESSION_ID" \
--error-message "$ERROR_MESSAGE" --failed-step "$FAILED_STEP" 2>/dev/null &
fi
echo "SKILL_END: recorded outcome=$OUTCOME duration_s=$_TEL_DUR"