mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-03 23:37:53 +01:00
Bins, careful/freeze hooks, setup, upgrade migrations, browse/src, design, ios-qa daemon, lib and scripts resolve the state root through bin/gstack-state-root.sh (bash) or lib/state-root.ts (TS). Bins source the twin and stop with a reinstall message when it is missing; hooks source it and never spawn gstack-paths. browse/src/config.ts and lib/cso/state.ts delegate to resolveStateRoot. Analytics writers and readers move together so the usage log stays one file. gstack-uninstall deletes state only at ~/.gstack, refuses (exit 2) when it resolves to /, $HOME or an ancestor, the checkout or the git root, and leaves any other resolved root in place with the removal command. Fixtures that copy single bins now copy the twin.
88 lines
3.9 KiB
Bash
Executable File
88 lines
3.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# gstack-review-log — atomically log a review result
|
|
# Usage: gstack-review-log '{"skill":"...","timestamp":"...","status":"..."}'
|
|
#
|
|
# Before reading a diff: gstack-review-log --start review
|
|
# After that pass: gstack-review-log '{...,"completed":true,"converged":true}' --finish TOKEN
|
|
# Diff reviews get wtree only from a consumed, matching start capture on an
|
|
# unchanged tree. Completion/convergence are reviewer-reported, not proof that
|
|
# a model read the code. Caller-supplied binding fields are always discarded.
|
|
# Plan-tier rows retain their legacy binding behavior.
|
|
set -euo pipefail
|
|
export GIT_OPTIONAL_LOCKS=0
|
|
export GIT_CONFIG_PARAMETERS="${GIT_CONFIG_PARAMETERS:+$GIT_CONFIG_PARAMETERS }'core.fsmonitor=false' 'core.untrackedCache=false'"
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
eval "$("$SCRIPT_DIR/gstack-slug" 2>/dev/null)"
|
|
. "$(dirname "$0")/gstack-state-root.sh" 2>/dev/null || { echo "$0: cannot resolve the gstack state root: $(dirname "$0")/gstack-state-root.sh is missing. fix: reinstall with ./setup or /gstack-upgrade (docs/state-root.md)" >&2; exit 1; }
|
|
gstack_state_root_select
|
|
GSTACK_HOME="$_gstack_sr_root"
|
|
mkdir -p "$GSTACK_HOME/projects/$SLUG"
|
|
|
|
INPUT="${1:-}"
|
|
export GSTACK_REVIEW_DIR="$GSTACK_HOME/projects/$SLUG"
|
|
export GSTACK_REVIEW_REPO="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
|
export GSTACK_REVIEW_BRANCH="$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse HEAD 2>/dev/null || true)"
|
|
export GSTACK_REVIEW_LIB="$SCRIPT_DIR/../lib/review-evidence.ts"
|
|
case "$(uname -s)" in
|
|
MINGW*|MSYS*|CYGWIN*)
|
|
if command -v cygpath >/dev/null 2>&1; then
|
|
GSTACK_REVIEW_LIB="$(cygpath -m "$GSTACK_REVIEW_LIB")"
|
|
GSTACK_REVIEW_DIR="$(cygpath -m "$GSTACK_REVIEW_DIR")"
|
|
fi
|
|
;;
|
|
esac
|
|
export GSTACK_REVIEW_LOG="$GSTACK_REVIEW_DIR/$BRANCH-reviews.jsonl"
|
|
|
|
# Compute binding fields (best-effort; empty outside a git repo).
|
|
COMMIT_FULL=$(git rev-parse HEAD 2>/dev/null || true)
|
|
TREE=""
|
|
WTREE=""
|
|
DIRTY=""
|
|
if [ -n "$COMMIT_FULL" ]; then
|
|
TREE=$(git rev-parse 'HEAD^{tree}' 2>/dev/null || true)
|
|
WTREE=$("$SCRIPT_DIR/gstack-wtree" 2>/dev/null || true)
|
|
if [ -n "$(git status --porcelain -uno 2>/dev/null | head -1)" ]; then
|
|
DIRTY="true"
|
|
else
|
|
DIRTY="false"
|
|
fi
|
|
fi
|
|
|
|
export GSTACK_STAMP_COMMIT_FULL="$COMMIT_FULL" GSTACK_STAMP_TREE="$TREE" GSTACK_STAMP_WTREE="$WTREE" GSTACK_STAMP_DIRTY="$DIRTY"
|
|
if [ "$INPUT" = --start ]; then
|
|
GSTACK_REVIEW_SKILL="${2:-}" bun -e '
|
|
const { captureReviewStart } = await import(process.env.GSTACK_REVIEW_LIB);
|
|
console.log(captureReviewStart(process.env.GSTACK_REVIEW_SKILL));
|
|
'
|
|
exit $?
|
|
fi
|
|
if [ "$INPUT" = --check-shared-libs ] && [ "$#" -eq 2 ]; then
|
|
GSTACK_REVIEW_TOKEN="$2" bun -e '
|
|
const { checkSharedLibsReuse } = await import(process.env.GSTACK_REVIEW_LIB);
|
|
console.log(JSON.stringify(checkSharedLibsReuse(JSON.parse(await Bun.stdin.text()), process.env.GSTACK_REVIEW_TOKEN)));
|
|
'
|
|
exit $?
|
|
fi
|
|
if [ "$#" -ne 1 ] && { [ "$#" -ne 3 ] || [ "${2:-}" != --finish ]; }; then
|
|
echo 'Usage: gstack-review-log JSON [--finish TOKEN] | --start SKILL | --check-shared-libs TOKEN < finding.json' >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Validate (reject malformed or injection attempts) AND stamp in one pass.
|
|
# Caller values for the binding keys are dropped before stamping.
|
|
STAMPED=$(printf '%s' "$INPUT" | GSTACK_REVIEW_TOKEN="${3:-}" bun -e "
|
|
const { bindReview } = await import(process.env.GSTACK_REVIEW_LIB);
|
|
const rec = JSON.parse(await Bun.stdin.text());
|
|
if (!rec || Array.isArray(rec) || typeof rec !== 'object') throw new Error('expected object');
|
|
console.log(JSON.stringify(bindReview(rec, process.env.GSTACK_REVIEW_TOKEN)));
|
|
" 2>/dev/null) || {
|
|
# Not valid JSON — refuse to append
|
|
echo "gstack-review-log: invalid JSON, skipping" >&2
|
|
exit 1
|
|
}
|
|
|
|
echo "$STAMPED" >> "$GSTACK_HOME/projects/$SLUG/$BRANCH-reviews.jsonl"
|
|
|
|
# gbrain-sync: enqueue for cross-machine sync (no-op if sync is off).
|
|
"$SCRIPT_DIR/gstack-brain-enqueue" "projects/$SLUG/$BRANCH-reviews.jsonl" 2>/dev/null &
|