Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions bin/gstack-open
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# gstack-open — cross-platform URL/file opener
# Usage: gstack-open <url-or-path>
#
# Replaces bare `open` calls in skill templates, which is macOS-only.
# Works on macOS, Linux (xdg-open / sensible-browser), and Windows (Git Bash / WSL).
set -euo pipefail

TARGET="${1:-}"
if [ -z "$TARGET" ]; then
echo "gstack-open: missing argument" >&2
exit 1
fi

case "$(uname -s)" in
Darwin)
open "$TARGET"
;;
Linux)
# WSL: delegate to Windows explorer
if grep -qi microsoft /proc/version 2>/dev/null; then
explorer.exe "$TARGET" 2>/dev/null || cmd.exe /c start "" "$TARGET" 2>/dev/null || true
else
xdg-open "$TARGET" 2>/dev/null \
|| sensible-browser "$TARGET" 2>/dev/null \
|| x-www-browser "$TARGET" 2>/dev/null \
|| { echo "gstack-open: no browser launcher found (tried xdg-open, sensible-browser, x-www-browser)" >&2; exit 1; }
fi
;;
MINGW*|MSYS*|CYGWIN*)
start "" "$TARGET"
;;
*)
# Unknown OS — try xdg-open as a best-effort fallback
xdg-open "$TARGET" 2>/dev/null || { echo "gstack-open: unsupported OS: $(uname -s)" >&2; exit 1; }
;;
esac
30 changes: 29 additions & 1 deletion bin/gstack-review-log
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,35 @@
# Usage: gstack-review-log '{"skill":"...","timestamp":"...","status":"..."}'
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

# Security: validate the input is a single-line JSON object before writing.
# Reject inputs containing newlines (JSONL injection) or that are not valid JSON.
INPUT="${1:-}"
if [ -z "$INPUT" ]; then
echo "gstack-review-log: missing argument" >&2
exit 1
fi

# Reject multi-line input — a single JSONL entry must be one line
if printf '%s' "$INPUT" | grep -q $'\n'; then
echo "gstack-review-log: input contains newlines — rejected to prevent JSONL injection" >&2
exit 1
fi

# Validate JSON structure using node or python if available; silently skip on failure
if command -v node >/dev/null 2>&1; then
if ! node -e "JSON.parse(process.argv[1])" "$INPUT" 2>/dev/null; then
echo "gstack-review-log: input is not valid JSON — rejected" >&2
exit 1
fi
elif command -v python3 >/dev/null 2>&1; then
if ! python3 -c "import sys,json; json.loads(sys.argv[1])" "$INPUT" 2>/dev/null; then
echo "gstack-review-log: input is not valid JSON — rejected" >&2
exit 1
fi
fi

eval "$("$SCRIPT_DIR/gstack-slug" 2>/dev/null)"
GSTACK_HOME="${GSTACK_HOME:-$HOME/.gstack}"
mkdir -p "$GSTACK_HOME/projects/$SLUG"
echo "$1" >> "$GSTACK_HOME/projects/$SLUG/$BRANCH-reviews.jsonl"
printf '%s\n' "$INPUT" >> "$GSTACK_HOME/projects/$SLUG/$BRANCH-reviews.jsonl"
28 changes: 25 additions & 3 deletions bin/gstack-telemetry-log
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,37 @@ fi
# ─── Construct and append JSON ───────────────────────────────
mkdir -p "$ANALYTICS_DIR"

# Sanitize a string for safe embedding in a JSONL value:
# - strip newlines/carriage returns (prevent JSONL injection — extra lines in the file)
# - escape backslashes and double-quotes (prevent JSON string corruption)
# - truncate to $2 bytes (optional, default 200)
sanitize_str() {
local val="$1"
local maxlen="${2:-200}"
printf '%s' "$val" \
| tr -d '\n\r' \
| head -c "$maxlen" \
| sed 's/\\/\\\\/g; s/"/\\"/g'
}

# Escape null fields
ERR_FIELD="null"
[ -n "$ERROR_CLASS" ] && ERR_FIELD="\"$ERROR_CLASS\""
[ -n "$ERROR_CLASS" ] && ERR_FIELD="\"$(sanitize_str "$ERROR_CLASS" 100)\""

ERR_MSG_FIELD="null"
[ -n "$ERROR_MESSAGE" ] && ERR_MSG_FIELD="\"$(echo "$ERROR_MESSAGE" | head -c 200 | sed 's/"/\\"/g')\""
[ -n "$ERROR_MESSAGE" ] && ERR_MSG_FIELD="\"$(sanitize_str "$ERROR_MESSAGE" 200)\""

STEP_FIELD="null"
[ -n "$FAILED_STEP" ] && STEP_FIELD="\"$(echo "$FAILED_STEP" | head -c 30)\""
[ -n "$FAILED_STEP" ] && STEP_FIELD="\"$(sanitize_str "$FAILED_STEP" 30)\""

# Sanitize fields that appear directly in the printf format string
SKILL="$(sanitize_str "$SKILL" 60)"
SESSION_ID="$(sanitize_str "$SESSION_ID" 60)"
EVENT_TYPE="$(sanitize_str "$EVENT_TYPE" 40)"
OUTCOME="$(sanitize_str "$OUTCOME" 20)"
SOURCE="$(sanitize_str "$SOURCE" 20)"
REPO_SLUG="$(sanitize_str "$REPO_SLUG" 120)"
BRANCH="$(sanitize_str "$BRANCH" 120)"

# Cap unreasonable durations
if [ -n "$DURATION" ] && [ "$DURATION" -gt 86400 ] 2>/dev/null; then
Expand Down
22 changes: 11 additions & 11 deletions codex/SKILL.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,13 @@ TMPERR=$(mktemp /tmp/codex-err-XXXXXX.txt)

2. Run the review (5-minute timeout):
```bash
codex review --base <base> -c 'model_reasoning_effort="xhigh"' --enable web_search_cached 2>"$TMPERR"
codex review --base <base> -c 'model_reasoning_effort="high"' --search 2>"$TMPERR"
```

Use `timeout: 300000` on the Bash call. If the user provided custom instructions
(e.g., `/codex review focus on security`), pass them as the prompt argument:
```bash
codex review "focus on security" --base <base> -c 'model_reasoning_effort="xhigh"' --enable web_search_cached 2>"$TMPERR"
codex review "focus on security" --base <base> -c 'model_reasoning_effort="high"' --search 2>"$TMPERR"
```

3. Capture the output. Then parse cost from stderr:
Expand All @@ -95,8 +95,8 @@ grep "tokens used" "$TMPERR" 2>/dev/null || echo "tokens: unknown"
```

4. Determine gate verdict by checking the review output for critical findings.
If the output contains `[P1]` — the gate is **FAIL**.
If no `[P1]` markers are found (only `[P2]` or no findings) — the gate is **PASS**.
If the output contains `[P0]` or `[P1]` — the gate is **FAIL**.
If no `[P0]` or `[P1]` markers are found (only `[P2]` or no findings) — the gate is **PASS**.

5. Present the output:

Expand Down Expand Up @@ -131,7 +131,7 @@ CROSS-MODEL ANALYSIS:
```

Substitute: TIMESTAMP (ISO 8601), STATUS ("clean" if PASS, "issues_found" if FAIL),
GATE ("pass" or "fail"), findings (count of [P1] + [P2] markers),
GATE ("pass" or "fail"), findings (count of [P0] + [P1] + [P2] markers),
findings_fixed (count of findings that were addressed/fixed before shipping).

8. Clean up temp files:
Expand Down Expand Up @@ -159,7 +159,7 @@ With focus (e.g., "security"):

2. Run codex exec with **JSONL output** to capture reasoning traces and tool calls (5-minute timeout):
```bash
codex exec "<prompt>" -s read-only -c 'model_reasoning_effort="xhigh"' --enable web_search_cached --json 2>/dev/null | python3 -c "
codex exec "<prompt>" -s read-only -c 'model_reasoning_effort="high"' --search --json 2>/dev/null | python3 -c "
import sys, json
for line in sys.stdin:
line = line.strip()
Expand Down Expand Up @@ -244,7 +244,7 @@ THE PLAN:

For a **new session:**
```bash
codex exec "<prompt>" -s read-only -c 'model_reasoning_effort="xhigh"' --enable web_search_cached --json 2>"$TMPERR" | python3 -c "
codex exec "<prompt>" -s read-only -c 'model_reasoning_effort="high"' --search --json 2>"$TMPERR" | python3 -c "
import sys, json
for line in sys.stdin:
line = line.strip()
Expand Down Expand Up @@ -277,7 +277,7 @@ for line in sys.stdin:

For a **resumed session** (user chose "Continue"):
```bash
codex exec resume <session-id> "<prompt>" -s read-only -c 'model_reasoning_effort="xhigh"' --enable web_search_cached --json 2>"$TMPERR" | python3 -c "
codex exec resume <session-id> "<prompt>" -s read-only -c 'model_reasoning_effort="high"' --search --json 2>"$TMPERR" | python3 -c "
<same python streaming parser as above>
"
```
Expand Down Expand Up @@ -313,10 +313,10 @@ Session saved — run /codex again to continue this conversation.
agentic coding model). This means as OpenAI ships newer models, /codex automatically
uses them. If the user wants a specific model, pass `-m` through to codex.

**Reasoning effort:** All modes use `xhigh` — maximum reasoning power. When reviewing code, breaking code, or consulting on architecture, you want the model thinking as hard as possible.
**Reasoning effort:** All modes use `high` — maximum reasoning power. When reviewing code, breaking code, or consulting on architecture, you want the model thinking as hard as possible. (`xhigh` is not a valid value and causes an API error.)

**Web search:** All codex commands use `--enable web_search_cached` so Codex can look up
docs and APIs during review. This is OpenAI's cached index — fast, no extra cost.
**Web search:** All codex commands use `--search` so Codex can look up
docs and APIs during review. (`--enable web_search_cached` is deprecated — use `--search`.)

If the user specifies a model (e.g., `/codex review -m gpt-5.1-codex-max`
or `/codex challenge -m gpt-5.2`), pass the `-m` flag through to codex.
Expand Down
2 changes: 1 addition & 1 deletion office-hours/SKILL.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ Say:

Then use AskUserQuestion: "Would you consider applying to Y Combinator?"

- If yes → run `open https://ycombinator.com/apply?ref=gstack` and say: "Bring this design doc to your YC interview. It's better than most pitch decks."
- If yes → run `~/.claude/skills/gstack/bin/gstack-open https://ycombinator.com/apply?ref=gstack` and say: "Bring this design doc to your YC interview. It's better than most pitch decks."
- If no → respond warmly: "Totally fair. The design doc is yours either way — and the offer stands if you ever change your mind." Then proceed to next-skill recs. No pressure, no guilt, no re-ask.

**Middle tier** — emotional target: *"I might be onto something."* Validation + curiosity.
Expand Down
2 changes: 1 addition & 1 deletion scripts/gen-skill-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ thing when AI makes the marginal cost near-zero. Read more: https://garryslist.o
Then offer to open the essay in their default browser:

\`\`\`bash
open https://garryslist.org/posts/boil-the-ocean
~/.claude/skills/gstack/bin/gstack-open https://garryslist.org/posts/boil-the-ocean
touch ~/.gstack/.completeness-intro-seen
\`\`\`

Expand Down
2 changes: 1 addition & 1 deletion scripts/resolvers/preamble.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ thing when AI makes the marginal cost near-zero. Read more: https://garryslist.o
Then offer to open the essay in their default browser:

\`\`\`bash
open https://garryslist.org/posts/boil-the-ocean
~/.claude/skills/gstack/bin/gstack-open https://garryslist.org/posts/boil-the-ocean
touch ~/.gstack/.completeness-intro-seen
\`\`\`

Expand Down
17 changes: 14 additions & 3 deletions setup
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,20 @@
set -e

if ! command -v bun >/dev/null 2>&1; then
echo "Error: bun is required but not installed." >&2
echo "Install it: curl -fsSL https://bun.sh/install | bash" >&2
exit 1
echo "Bun not found — installing automatically..."
if ! curl -fsSL https://bun.sh/install | bash; then
echo "Error: Bun auto-install failed." >&2
echo "Install it manually: curl -fsSL https://bun.sh/install | bash" >&2
exit 1
fi
# Pick up the newly installed bun from the default location
export PATH="$HOME/.bun/bin:$PATH"
if ! command -v bun >/dev/null 2>&1; then
echo "Error: bun installed but not found in PATH." >&2
echo "Open a new terminal and re-run setup, or add ~/.bun/bin to your PATH." >&2
exit 1
fi
echo "Bun installed successfully."
fi

INSTALL_GSTACK_DIR="$(cd "$(dirname "$0")" && pwd)"
Expand Down