From 65fef05a9207b97e9b33261f8d32c217b2d278e8 Mon Sep 17 00:00:00 2001 From: garrytan Date: Thu, 8 Oct 2026 06:20:39 +0000 Subject: [PATCH] fix(skill-start): echo SESSIONS, the live session-marker count (#1651) The sessions directory is touched and expired at skill start, but the count was never printed, so CLAUDE.md rules that read the preamble's session count had nothing to read. Echo SESSIONS: N (markers fresher than 120 minutes, this session included). Idea from @0xDevNinja's PR #1747, redone in the consolidated skill-start script. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- bin/gstack-skill-start | 2 ++ test/gstack-skill-start.test.ts | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/bin/gstack-skill-start b/bin/gstack-skill-start index 6dbaaa698..c461bdbc5 100755 --- a/bin/gstack-skill-start +++ b/bin/gstack-skill-start @@ -101,6 +101,7 @@ fi mkdir -p "$_GH/sessions" 2>/dev/null || true touch "$_GH/sessions/$PARENT_PID" 2>/dev/null || true find "$_GH/sessions" -mmin +120 -type f -exec rm {} + 2>/dev/null || true +_SESSIONS=$(find "$_GH/sessions" -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') _PROACTIVE=$("$_BIN/gstack-config" get proactive 2>/dev/null || echo "true") _PROACTIVE_PROMPTED=$([ -f "$_GH/.proactive-prompted" ] && echo "yes" || echo "no") @@ -118,6 +119,7 @@ eval "$("$_BIN/gstack-repo-mode" 2>/dev/null)" 2>/dev/null || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" echo "SESSION_KIND: $_SESSION_KIND" +echo "SESSIONS: ${_SESSIONS:-0}" # Tamper visibility (#2733 review): a repo-provided settings env block or a CI # wrapper could set GSTACK_SESSION_KIND or OPENCLAW_SESSION for a real human's # session, silently flipping every gate to auto-choose. Surface the env-driven diff --git a/test/gstack-skill-start.test.ts b/test/gstack-skill-start.test.ts index e0eb737de..b892fd72b 100644 --- a/test/gstack-skill-start.test.ts +++ b/test/gstack-skill-start.test.ts @@ -204,6 +204,24 @@ describe('gstack-skill-start behavior', () => { expect(fs.existsSync(path.join(tmpGstackHome, 'sessions', '424242'))).toBe(true); }); + test('echoes SESSIONS: the count of live session markers, this one included (#1651)', () => { + const state = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-ss-sessions-')); + fs.writeFileSync(path.join(state, 'config.yaml'), 'update_check: false\n'); + try { + const sessions = path.join(state, 'sessions'); + fs.mkdirSync(sessions); + fs.writeFileSync(path.join(sessions, '111'), ''); + fs.writeFileSync(path.join(sessions, 'stale'), ''); + const threeHoursAgo = new Date(Date.now() - 3 * 3600_000); + fs.utimesSync(path.join(sessions, 'stale'), threeHoursAgo, threeHoursAgo); + const out = runStart(['--parent-pid', '222'], { GSTACK_HOME: state }); + expect(out).toMatch(/^SESSIONS: 2$/m); + expect(fs.readdirSync(sessions).sort()).toEqual(['111', '222']); + } finally { + fs.rmSync(state, { recursive: true, force: true }); + } + }); + test('without --parent-pid the script derives the harness pid past tool-call shells (#2763)', () => { // Claude Code runs each Bash call in a fresh `bash -c`; the session must be // the harness (here: this bun process), not that per-call shell, even