mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-06 05:35:46 +02:00
feat(paths): bin/gstack-paths helper + migrate 8 skills off inline state-root chains
New bin/gstack-paths emits GSTACK_STATE_ROOT, PLAN_ROOT, TMP_ROOT exports for
skill bash blocks to source via eval. Honors GSTACK_HOME → CLAUDE_PLUGIN_DATA →
$HOME/.gstack → .gstack (and parallel chains for plan/tmp roots) so skills work
the same in plugin installs, global installs, and CI containers without HOME.
Eight skills migrate off inline ${CLAUDE_PLUGIN_DATA:-...} or ${GSTACK_HOME:-...}
chains: careful, freeze, guard, unfreeze, investigate, context-save,
context-restore, learn, office-hours, plan-tune, codex. Resolved values are
identical, so existing tests cover correctness; the win is consolidating 11
copy-pasted fallback chains behind one helper.
codex/SKILL.md.tmpl gets a new Step 0.6 Resolve portable roots that sources
gstack-paths once, then replaces hardcoded ~/.claude/plans/*.md and
/tmp/codex-*-XXXXXX.txt with "$PLAN_ROOT"/*.md and "$TMP_ROOT/codex-*-XXXXXX.txt".
Hardening direction credited to the McGluut/gstack fork; this is upstream's
factoring of the per-skill chain the fork inlined.
Tests: test/gstack-paths.test.ts covers all three fallback chains with 8 unit
tests (HOME unset, CLAUDE_PLUGIN_DATA set, GSTACK_HOME wins, etc).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+25
-8
@@ -781,6 +781,23 @@ deadlock fixed in #972.
|
||||
|
||||
---
|
||||
|
||||
## Step 0.6: Resolve portable roots
|
||||
|
||||
Before any mode runs, resolve `$PLAN_ROOT` (where plan files live) and `$TMP_ROOT`
|
||||
(where ephemeral codex stderr / response captures land) via `bin/gstack-paths`.
|
||||
This keeps the skill working whether installed as a Claude Code plugin
|
||||
(`CLAUDE_PLANS_DIR` set), a global `~/.claude/skills/gstack/` install, or a CI
|
||||
container where `HOME` may be unset and `/tmp` may be read-only.
|
||||
|
||||
```bash
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"
|
||||
```
|
||||
|
||||
After this, every subsequent bash block in this skill uses `"$PLAN_ROOT"` and
|
||||
`"$TMP_ROOT"` rather than hardcoded `~/.claude/plans` or `/tmp/codex-*`.
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Detect mode
|
||||
|
||||
Parse the user's input to determine which mode to run:
|
||||
@@ -798,8 +815,8 @@ Parse the user's input to determine which mode to run:
|
||||
C) Something else — I'll provide a prompt
|
||||
```
|
||||
- If no diff, check for plan files scoped to the current project:
|
||||
`ls -t ~/.claude/plans/*.md 2>/dev/null | xargs grep -l "$(basename $(pwd))" 2>/dev/null | head -1`
|
||||
If no project-scoped match, fall back to: `ls -t ~/.claude/plans/*.md 2>/dev/null | head -1`
|
||||
`ls -t "$PLAN_ROOT"/*.md 2>/dev/null | xargs grep -l "$(basename $(pwd))" 2>/dev/null | head -1`
|
||||
If no project-scoped match, fall back to: `ls -t "$PLAN_ROOT"/*.md 2>/dev/null | head -1`
|
||||
but warn the user: "Note: this plan may be from a different project."
|
||||
- If a plan file exists, offer to review it
|
||||
- Otherwise, ask: "What would you like to ask Codex?"
|
||||
@@ -832,7 +849,7 @@ Run Codex code review against the current branch diff.
|
||||
|
||||
1. Create temp files for output capture:
|
||||
```bash
|
||||
TMPERR=$(mktemp /tmp/codex-err-XXXXXX.txt)
|
||||
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX.txt")
|
||||
```
|
||||
|
||||
2. Run the review (5-minute timeout). **Always** pass the filesystem boundary instruction
|
||||
@@ -1015,7 +1032,7 @@ If the user passed `--xhigh`, use `"xhigh"` instead of `"high"`.
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
# Fix 1+2: wrap with timeout (gtimeout/timeout fallback chain via probe helper),
|
||||
# capture stderr to $TMPERR for auth error detection (was: 2>/dev/null).
|
||||
TMPERR=${TMPERR:-$(mktemp /tmp/codex-err-XXXXXX.txt)}
|
||||
TMPERR=${TMPERR:-$(mktemp "$TMP_ROOT/codex-err-XXXXXX.txt")}
|
||||
_gstack_codex_timeout_wrapper 600 codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 python3 -u -c "
|
||||
import sys, json
|
||||
turn_completed_count = 0
|
||||
@@ -1094,17 +1111,17 @@ B) Start a new conversation
|
||||
|
||||
2. Create temp files:
|
||||
```bash
|
||||
TMPRESP=$(mktemp /tmp/codex-resp-XXXXXX.txt)
|
||||
TMPERR=$(mktemp /tmp/codex-err-XXXXXX.txt)
|
||||
TMPRESP=$(mktemp "$TMP_ROOT/codex-resp-XXXXXX.txt")
|
||||
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX.txt")
|
||||
```
|
||||
|
||||
3. **Plan review auto-detection:** If the user's prompt is about reviewing a plan,
|
||||
or if plan files exist and the user said `/codex` with no arguments:
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
ls -t ~/.claude/plans/*.md 2>/dev/null | xargs grep -l "$(basename $(pwd))" 2>/dev/null | head -1
|
||||
ls -t "$PLAN_ROOT"/*.md 2>/dev/null | xargs grep -l "$(basename $(pwd))" 2>/dev/null | head -1
|
||||
```
|
||||
If no project-scoped match, fall back to `ls -t ~/.claude/plans/*.md 2>/dev/null | head -1`
|
||||
If no project-scoped match, fall back to `ls -t "$PLAN_ROOT"/*.md 2>/dev/null | head -1`
|
||||
but warn: "Note: this plan may be from a different project — verify before sending to Codex."
|
||||
|
||||
**IMPORTANT — embed content, don't reference path:** Codex runs sandboxed to the repo
|
||||
|
||||
+25
-8
@@ -90,6 +90,23 @@ deadlock fixed in #972.
|
||||
|
||||
---
|
||||
|
||||
## Step 0.6: Resolve portable roots
|
||||
|
||||
Before any mode runs, resolve `$PLAN_ROOT` (where plan files live) and `$TMP_ROOT`
|
||||
(where ephemeral codex stderr / response captures land) via `bin/gstack-paths`.
|
||||
This keeps the skill working whether installed as a Claude Code plugin
|
||||
(`CLAUDE_PLANS_DIR` set), a global `~/.claude/skills/gstack/` install, or a CI
|
||||
container where `HOME` may be unset and `/tmp` may be read-only.
|
||||
|
||||
```bash
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"
|
||||
```
|
||||
|
||||
After this, every subsequent bash block in this skill uses `"$PLAN_ROOT"` and
|
||||
`"$TMP_ROOT"` rather than hardcoded `~/.claude/plans` or `/tmp/codex-*`.
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Detect mode
|
||||
|
||||
Parse the user's input to determine which mode to run:
|
||||
@@ -107,8 +124,8 @@ Parse the user's input to determine which mode to run:
|
||||
C) Something else — I'll provide a prompt
|
||||
```
|
||||
- If no diff, check for plan files scoped to the current project:
|
||||
`ls -t ~/.claude/plans/*.md 2>/dev/null | xargs grep -l "$(basename $(pwd))" 2>/dev/null | head -1`
|
||||
If no project-scoped match, fall back to: `ls -t ~/.claude/plans/*.md 2>/dev/null | head -1`
|
||||
`ls -t "$PLAN_ROOT"/*.md 2>/dev/null | xargs grep -l "$(basename $(pwd))" 2>/dev/null | head -1`
|
||||
If no project-scoped match, fall back to: `ls -t "$PLAN_ROOT"/*.md 2>/dev/null | head -1`
|
||||
but warn the user: "Note: this plan may be from a different project."
|
||||
- If a plan file exists, offer to review it
|
||||
- Otherwise, ask: "What would you like to ask Codex?"
|
||||
@@ -141,7 +158,7 @@ Run Codex code review against the current branch diff.
|
||||
|
||||
1. Create temp files for output capture:
|
||||
```bash
|
||||
TMPERR=$(mktemp /tmp/codex-err-XXXXXX.txt)
|
||||
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX.txt")
|
||||
```
|
||||
|
||||
2. Run the review (5-minute timeout). **Always** pass the filesystem boundary instruction
|
||||
@@ -254,7 +271,7 @@ If the user passed `--xhigh`, use `"xhigh"` instead of `"high"`.
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
# Fix 1+2: wrap with timeout (gtimeout/timeout fallback chain via probe helper),
|
||||
# capture stderr to $TMPERR for auth error detection (was: 2>/dev/null).
|
||||
TMPERR=${TMPERR:-$(mktemp /tmp/codex-err-XXXXXX.txt)}
|
||||
TMPERR=${TMPERR:-$(mktemp "$TMP_ROOT/codex-err-XXXXXX.txt")}
|
||||
_gstack_codex_timeout_wrapper 600 codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 python3 -u -c "
|
||||
import sys, json
|
||||
turn_completed_count = 0
|
||||
@@ -333,17 +350,17 @@ B) Start a new conversation
|
||||
|
||||
2. Create temp files:
|
||||
```bash
|
||||
TMPRESP=$(mktemp /tmp/codex-resp-XXXXXX.txt)
|
||||
TMPERR=$(mktemp /tmp/codex-err-XXXXXX.txt)
|
||||
TMPRESP=$(mktemp "$TMP_ROOT/codex-resp-XXXXXX.txt")
|
||||
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX.txt")
|
||||
```
|
||||
|
||||
3. **Plan review auto-detection:** If the user's prompt is about reviewing a plan,
|
||||
or if plan files exist and the user said `/codex` with no arguments:
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
ls -t ~/.claude/plans/*.md 2>/dev/null | xargs grep -l "$(basename $(pwd))" 2>/dev/null | head -1
|
||||
ls -t "$PLAN_ROOT"/*.md 2>/dev/null | xargs grep -l "$(basename $(pwd))" 2>/dev/null | head -1
|
||||
```
|
||||
If no project-scoped match, fall back to `ls -t ~/.claude/plans/*.md 2>/dev/null | head -1`
|
||||
If no project-scoped match, fall back to `ls -t "$PLAN_ROOT"/*.md 2>/dev/null | head -1`
|
||||
but warn: "Note: this plan may be from a different project — verify before sending to Codex."
|
||||
|
||||
**IMPORTANT — embed content, don't reference path:** Codex runs sandboxed to the repo
|
||||
|
||||
Reference in New Issue
Block a user