Files
gstack/package.json
T
Garry Tan 9562ad4e70 v1.53.1.0 fix: non-interactive-safe plan-tune hook install (flags + smart defaults) (#1805)
* feat(config): add plan_tune_hooks setting (prompt|yes|no)

Registers a new gstack-config key controlling whether ./setup installs the
plan-tune Claude Code hooks. Default "prompt". Documented in the config
header and surfaced in `gstack-config defaults` / `list`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(setup): make plan-tune hook install non-interactive-safe

The plan-tune consent prompt used a blocking `read -r` with no timeout. Under
a forwarded/automated TTY (conductor workspace setup, CI with a pty) it hung
setup forever.

Move the decision into flags + env + saved config with a smart default:
  --plan-tune-hooks / --no-plan-tune-hooks / --plan-tune-hooks=yes|no|prompt
  > GSTACK_PLAN_TUNE_HOOKS env > plan_tune_hooks config > prompt-on-real-TTY.

Explicit yes/no act non-interactively. The remaining interactive branch is
gated on a real (non-quiet) TTY and uses a time-bounded `read -t 10 </dev/tty`
that defaults to skip, so it can never hang. A timeout no longer persists a
decline marker, so a later hands-on run can still offer the install.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(dev-setup): run setup non-interactively in dev/workspace mode

Conductor runs bin/dev-setup under a forwarded pty, so any setup prompt
(skill-prefix, plan-tune consent) would hang the workspace. Detach stdin
(`setup </dev/null`) so every prompt takes its smart non-interactive default:
flat skill names, skip the global plan-tune hook install without writing a
decline marker. Saved prefix/config preferences are still honored, and a dev
workspace no longer silently mutates ~/.claude/settings.json.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(setup): guard plan-tune hooks stay non-interactive

Static + binary-level regression test (free, <1s): asserts the flags are
wired, the plan-tune read is time-bounded (no bare blocking read), explicit
yes/no decisions short-circuit before the prompt, and gstack-config knows the
plan_tune_hooks key.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(setup,config): harden plan-tune decision against bad input

Review follow-ups to the non-interactive plan-tune work:
- setup now lowercases + whitespace-strips the resolved decision before the
  case match, so an explicit opt-in via flag/env ("YES", "Yes", " yes") is
  honored instead of silently falling through to "prompt"/skip. Also accepts
  on/off and 1/0.
- gstack-config rejects out-of-domain plan_tune_hooks values (anything but
  prompt|yes|no) with a warning + fallback to prompt, matching the existing
  value-whitelist pattern for explain_level / artifacts_sync_mode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(dev-setup): never mutate global hooks during workspace setup

Closing stdin alone only suppresses the prompt branch; a saved
`plan_tune_hooks: yes` or exported GSTACK_PLAN_TUNE_HOOKS=yes would still
resolve to "install" and rewrite the user's global ~/.claude/settings.json to
point at THIS ephemeral worktree — which breaks once the workspace is deleted.

Pass --plan-tune-hooks=prompt (highest precedence) so dev-setup pins resolution
to prompt-mode; with stdin closed that is a guaranteed no-op skip (no install,
no decline marker). To install the hooks, run ./setup --plan-tune-hooks directly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(setup): isolate config tests from host + cover new guards

- Point gstack-config tests at a temp GSTACK_HOME so `get plan_tune_hooks`
  reads the built-in default, not whatever the host machine has in
  ~/.gstack/config.yaml (the prior test was non-deterministic).
- Add behavioral coverage: yes/no/prompt round-trip, out-of-domain rejection.
- Add a normalization guard (decision input is lowercased/trimmed) and a
  dev-setup guard (runs setup with --plan-tune-hooks=prompt + stdin detached).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: rebaseline parity-suite v1.44.1 -> v1.53.0.0

The frozen v1.44.1 anchor went stale: five planning skills (plan-ceo-review,
plan-eng-review, plan-design-review, investigate, office-hours) crept past the
1.05x ceiling via legitimate v1.49-v1.53 growth (brain-aware planning + the
v1.53 redaction guard), so `bun test` was red on a clean checkout of main.

Capture a fresh baseline at HEAD (bun run scripts/capture-baseline.ts --tag
v1.53.0.0) and re-point the test at it. The per-skill 1.05 ratio is kept, so
future bloat is still caught; only the anchor moved. Mirrors the earlier
skill-size-budget rebase (v1.44.1 -> v1.47.0.0). Historical v1.44.1 / v1.46.0.0
/ v1.47.0.0 baselines are retained for the v1->v2 audit trail. The captured
skill bytes equal origin/main exactly (this branch left every SKILL.md
untouched). Clears the pre-existing failures noted in the v1.53.0.0 CHANGELOG.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(plan-tune): de-flake "derive pushes scope_appetite up"

The test was ~25-50% flaky (worse on main). gstack-question-log fires a
fire-and-forget background `--derive` after every write; the 5 rapid log writes
spawned 5 racing background derives that collided with the test's explicit
--derive — a late one that only saw 3 entries could clobber
developer-profile.json after the explicit one wrote sample_size=5.

Set GSTACK_QUESTION_LOG_NO_DERIVE=1 (the flag the binary documents for exactly
this case) so the writes don't spawn background derives. The explicit --derive
still runs, so real derive behavior is still asserted. 20/20 green after.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: bump version and changelog (v1.53.1.0)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: document non-interactive dev-setup + plan-tune hook flags (v1.53.1.0)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 11:42:13 -07:00

74 lines
4.1 KiB
JSON

{
"name": "gstack",
"version": "1.53.1.0",
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
"license": "MIT",
"type": "module",
"bin": {
"browse": "./browse/dist/browse",
"make-pdf": "./make-pdf/dist/pdf"
},
"scripts": {
"build": "bash scripts/build.sh",
"vendor:xterm": "mkdir -p extension/lib && cp node_modules/xterm/lib/xterm.js extension/lib/xterm.js && cp node_modules/xterm/css/xterm.css extension/lib/xterm.css && cp node_modules/xterm-addon-fit/lib/xterm-addon-fit.js extension/lib/xterm-addon-fit.js",
"dev:make-pdf": "bun run make-pdf/src/cli.ts",
"dev:design": "bun run design/src/cli.ts",
"gen:skill-docs": "bun run scripts/gen-skill-docs.ts",
"dev": "bun run browse/src/cli.ts",
"server": "bun run browse/src/server.ts",
"test": "bun test browse/test/ test/ make-pdf/test/ --ignore 'test/skill-e2e-*.test.ts' --ignore test/skill-llm-eval.test.ts --ignore test/skill-routing-e2e.test.ts --ignore test/codex-e2e.test.ts --ignore test/gemini-e2e.test.ts && (bun run slop:diff 2>/dev/null || true)",
"test:free": "bun run scripts/test-free-shards.ts",
"test:windows": "bun run scripts/test-free-shards.ts --windows-only",
"test:evals": "EVALS=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:evals:all": "EVALS=1 EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:e2e": "EVALS=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:e2e:all": "EVALS=1 EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:gate": "EVALS=1 EVALS_TIER=gate bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:periodic": "EVALS=1 EVALS_TIER=periodic EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:codex": "EVALS=1 bun test test/codex-e2e.test.ts",
"test:codex:all": "EVALS=1 EVALS_ALL=1 bun test test/codex-e2e.test.ts",
"test:gemini": "EVALS=1 bun test test/gemini-e2e.test.ts",
"test:gemini:all": "EVALS=1 EVALS_ALL=1 bun test test/gemini-e2e.test.ts",
"skill:check": "bun run scripts/skill-check.ts",
"dev:skill": "bun run scripts/dev-skill.ts",
"start": "bun run browse/src/server.ts",
"eval:list": "bun run scripts/eval-list.ts",
"eval:compare": "bun run scripts/eval-compare.ts",
"eval:summary": "bun run scripts/eval-summary.ts",
"eval:watch": "bun run scripts/eval-watch.ts",
"eval:select": "bun run scripts/eval-select.ts",
"analytics": "bun run scripts/analytics.ts",
"test:audit": "bun test test/audit-compliance.test.ts",
"slop": "npx slop-scan scan . 2>/dev/null || echo 'slop-scan not available (install with: npm i -g slop-scan)'",
"slop:diff": "bun run scripts/slop-diff.ts"
},
"dependencies": {
"@huggingface/transformers": "^4.1.0",
"@ngrok/ngrok": "^1.7.0",
"diff": "^7.0.0",
"marked": "^18.0.2",
"playwright": "^1.58.2",
"puppeteer-core": "^24.40.0",
"socks": "^2.8.8"
},
"engines": {
"bun": ">=1.0.0"
},
"keywords": [
"browser",
"automation",
"playwright",
"headless",
"cli",
"claude",
"ai-agent",
"devtools"
],
"devDependencies": {
"@anthropic-ai/claude-agent-sdk": "0.2.117",
"@anthropic-ai/sdk": "^0.78.0",
"xterm": "5",
"xterm-addon-fit": "^0.8.0"
}
}