Files
gstack/scripts/resolvers/preamble/generate-preamble-bash.ts
T
Garry Tan 7529dbb276 merge: integrate origin/main (v0.18.1.0) into open-agents-learnings
Main moved forward 6 commits while this branch was local. Integrated
both sides preserving all functionality:

From main (v0.16.4.0 → v0.18.1.0):
- v0.17.0.0 — UX behavioral foundations + ux-audit (generateUXPrinciples,
  {{UX_PRINCIPLES}} placeholder, triggers frontmatter on skills)
- v0.18.0.0 — Confusion Protocol, Hermes + GBrain hosts, brain-first
  resolver (generateBrainHealthInstruction, generateConfusionProtocol,
  generateGBrainContextLoad, generateGBrainSaveResults, hosts/gbrain.ts,
  hosts/hermes.ts, scripts/resolvers/gbrain.ts, GBrain bash health check)
- v0.18.0.1 — ngrok Windows build fix
- 0cc830b6 — tilde-in-assignment permission fix
- cc42f14a — gstack compact design doc (tabled)
- 822e843a — headed browser auto-shutdown + disconnect cleanup (v0.18.1.0)

Integration approach: keep this branch's preamble.ts submodule refactor
as the structure of record. Extracted main's two new generators into
their own submodules:
- scripts/resolvers/preamble/generate-brain-health-instruction.ts
- scripts/resolvers/preamble/generate-confusion-protocol.ts

Updated scripts/resolvers/preamble/generate-preamble-bash.ts to absorb
main's GBrain health check (host-conditional on gbrain/hermes).

scripts/resolvers/index.ts now imports BOTH:
- This branch's adds: MODEL_OVERLAY, TASTE_PROFILE, BIN_DIR resolvers
- Main's adds: UX_PRINCIPLES, GBRAIN_CONTEXT_LOAD, GBRAIN_SAVE_RESULTS
  resolvers

scripts/resolvers/design.ts keeps both generateTasteProfile (this
branch) and generateUXPrinciples (main). Sibling exports, no overlap.

scripts/gen-skill-docs.ts keeps both this branch's --model flag wiring
and main's edits.

Templates auto-merged where possible. The 35 generated SKILL.md /
golden conflicts auto-resolved via `bun run gen:skill-docs --host all`
followed by re-snapshotting the ship goldens for claude/codex/factory.

Verification:
- bun run gen:skill-docs --host all completes cleanly
- bun test: 1 pre-existing failure (gstack-community-dashboard Supabase
  network test, 235s timeout). NOT related to merge — unchanged Supabase
  test infra times out without live network. Flagged in PR body.

Token-ceiling warnings on plan-ceo-review (29K), office-hours (26K),
and ship (34K). These existed on origin/main before the merge — the
preamble grew substantially from main's GBrain + UX additions plus this
branch's continuous-checkpoint, context-health, model-overlay, taste-profile,
and feature-discovery additions. Worth a follow-up reduction pass but
doesn't block this merge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 13:58:15 +08:00

110 lines
5.4 KiB
TypeScript

import type { TemplateContext } from '../types';
import { getHostConfig } from '../../../hosts/index';
export function generatePreambleBash(ctx: TemplateContext): string {
const hostConfig = getHostConfig(ctx.host);
const runtimeRoot = hostConfig.usesEnvVars
? `_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
GSTACK_ROOT="$HOME/${hostConfig.globalRoot}"
[ -n "$_ROOT" ] && [ -d "$_ROOT/${ctx.paths.localSkillRoot}" ] && GSTACK_ROOT="$_ROOT/${ctx.paths.localSkillRoot}"
GSTACK_BIN="$GSTACK_ROOT/bin"
GSTACK_BROWSE="$GSTACK_ROOT/browse/dist"
GSTACK_DESIGN="$GSTACK_ROOT/design/dist"
`
: '';
return `## Preamble (run first)
\`\`\`bash
${runtimeRoot}_UPD=$(${ctx.paths.binDir}/gstack-update-check 2>/dev/null || ${ctx.paths.localSkillRoot}/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 -exec rm {} + 2>/dev/null || true
_PROACTIVE=$(${ctx.paths.binDir}/gstack-config get proactive 2>/dev/null || echo "true")
_PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no")
_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
echo "BRANCH: $_BRANCH"
_SKILL_PREFIX=$(${ctx.paths.binDir}/gstack-config get skill_prefix 2>/dev/null || echo "false")
echo "PROACTIVE: $_PROACTIVE"
echo "PROACTIVE_PROMPTED: $_PROACTIVE_PROMPTED"
echo "SKILL_PREFIX: $_SKILL_PREFIX"
source <(${ctx.paths.binDir}/gstack-repo-mode 2>/dev/null) || true
REPO_MODE=\${REPO_MODE:-unknown}
echo "REPO_MODE: $REPO_MODE"
_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)
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)"
echo "TELEMETRY: \${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED"
mkdir -p ~/.gstack/analytics
if [ "$_TEL" != "off" ]; then
echo '{"skill":"${ctx.skillName}","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
fi
# zsh-compatible: use find instead of glob to avoid NOMATCH error
for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do
if [ -f "$_PF" ]; then
if [ "$_TEL" != "off" ] && [ -x "${ctx.paths.binDir}/gstack-telemetry-log" ]; then
${ctx.paths.binDir}/gstack-telemetry-log --event-type skill_run --skill _pending_finalize --outcome unknown --session-id "$_SESSION_ID" 2>/dev/null || true
fi
rm -f "$_PF" 2>/dev/null || true
fi
break
done
# Learnings count
eval "$(${ctx.paths.binDir}/gstack-slug 2>/dev/null)" 2>/dev/null || true
_LEARN_FILE="\${GSTACK_HOME:-$HOME/.gstack}/projects/\${SLUG:-unknown}/learnings.jsonl"
if [ -f "$_LEARN_FILE" ]; then
_LEARN_COUNT=$(wc -l < "$_LEARN_FILE" 2>/dev/null | tr -d ' ')
echo "LEARNINGS: $_LEARN_COUNT entries loaded"
if [ "$_LEARN_COUNT" -gt 5 ] 2>/dev/null; then
${ctx.paths.binDir}/gstack-learnings-search --limit 3 2>/dev/null || true
fi
else
echo "LEARNINGS: 0"
fi
# Session timeline: record skill start (local-only, never sent anywhere)
${ctx.paths.binDir}/gstack-timeline-log '{"skill":"${ctx.skillName}","event":"started","branch":"'"$_BRANCH"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null &
# Check if CLAUDE.md has routing rules
_HAS_ROUTING="no"
if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
_HAS_ROUTING="yes"
fi
_ROUTING_DECLINED=$(${ctx.paths.binDir}/gstack-config get routing_declined 2>/dev/null || echo "false")
echo "HAS_ROUTING: $_HAS_ROUTING"
echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
# Vendoring deprecation: detect if CWD has a vendored gstack copy
_VENDORED="no"
if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then
if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then
_VENDORED="yes"
fi
fi
echo "VENDORED_GSTACK: $_VENDORED"
echo "MODEL_OVERLAY: ${ctx.model ?? 'none'}"
# Checkpoint mode (explicit = no auto-commit, continuous = WIP commits as you go)
_CHECKPOINT_MODE=$(${ctx.paths.binDir}/gstack-config get checkpoint_mode 2>/dev/null || echo "explicit")
_CHECKPOINT_PUSH=$(${ctx.paths.binDir}/gstack-config get checkpoint_push 2>/dev/null || echo "false")
echo "CHECKPOINT_MODE: $_CHECKPOINT_MODE"
echo "CHECKPOINT_PUSH: $_CHECKPOINT_PUSH"
# Detect spawned session (OpenClaw or other orchestrator)
[ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true${ctx.host === 'gbrain' || ctx.host === 'hermes' ? `
# GBrain health check (gbrain/hermes host only)
if command -v gbrain &>/dev/null; then
_BRAIN_JSON=$(gbrain doctor --fast --json 2>/dev/null || echo '{}')
_BRAIN_SCORE=$(echo "$_BRAIN_JSON" | grep -o '"health_score":[0-9]*' | cut -d: -f2)
_BRAIN_FAILS=$(echo "$_BRAIN_JSON" | grep -o '"status":"fail"' | wc -l | tr -d ' ')
_BRAIN_WARNS=$(echo "$_BRAIN_JSON" | grep -o '"status":"warn"' | wc -l | tr -d ' ')
echo "BRAIN_HEALTH: \${_BRAIN_SCORE:-unknown} (\${_BRAIN_FAILS:-0} failures, \${_BRAIN_WARNS:-0} warnings)"
if [ "\${_BRAIN_SCORE:-100}" -lt 50 ] 2>/dev/null; then
echo "$_BRAIN_JSON" | grep -o '"name":"[^"]*","status":"[^"]*","message":"[^"]*"' || true
fi
fi` : ''}
\`\`\``;
}