Skip to content

fix(gstack-paths): add --get <key> so /codex skips eval-tilde footgun#1496

Open
0xDevNinja wants to merge 1 commit into
garrytan:mainfrom
0xDevNinja:fix/1329-gstack-paths-get-flag
Open

fix(gstack-paths): add --get <key> so /codex skips eval-tilde footgun#1496
0xDevNinja wants to merge 1 commit into
garrytan:mainfrom
0xDevNinja:fix/1329-gstack-paths-get-flag

Conversation

@0xDevNinja
Copy link
Copy Markdown

Summary

  • Pattern 2 from Multiple Claude Code security hook triggers in /codex and /autoplan skill templates #1329: eval "$(~/.claude/skills/gstack/bin/gstack-paths)" trips Claude Code's PreToolUse bash AST parser (Unhandled node type: string) because of tilde inside $() inside a double-quoted eval arg. /codex forces a manual approval on every run as a result.

  • gstack-paths --get state-root|plan-root|tmp-root prints a single path with no KEY= prefix, so callers can use:

    PLAN_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get plan-root)
    TMP_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get tmp-root)
  • Bare invocation stays backward-compatible (KEY=VALUE form). No compat break for skills that haven't been migrated yet.

  • codex/SKILL.md.tmpl Step 0.6 switched to --get. codex/SKILL.md regenerated via bun run gen:skill-docs.

Refs #1329.

Scope

Fix narrow on purpose — Pattern 2 in /codex only. Patterns 1 / 3 / 4 each need their own design (Pattern 1 = split gstack-codex-probe functions into binaries; Pattern 3 = subshell or codex -C; Pattern 4 = extract inline Python to gstack-codex-jsonl-parser). Bundling all four into one PR would balloon the diff across multiple binaries + skill surfaces.

Other skills that still use eval "$(gstack-paths)" (learn, freeze, investigate, context-restore, office-hours, context-save, guard, unfreeze, plan-tune) are unchanged here — migration can land one skill at a time on top of this PR with no compat risk because bare invocation still works.

Tests

7 new cases in test/gstack-paths.test.ts:

  • --get state-root / plan-root / tmp-root each return the single resolved path, no KEY= prefix.
  • Each honors the same env override chain as the bare form (GSTACK_PLAN_DIR, TMPDIR, etc.).
  • --get bogus exits 1 with unknown key.
  • --get without a key exits 1 with --get requires a key.
  • --bogus top-level flag exits 1 with unknown argument.
  • Bare invocation still emits GSTACK_STATE_ROOT= / PLAN_ROOT= / TMP_ROOT= (backward-compat guard).
bun test test/gstack-paths.test.ts
# 15 pass, 0 fail (8 existing + 7 new)

bun test test/skill-validation.test.ts test/gen-skill-docs.test.ts test/gstack-paths.test.ts
# 727 pass, 0 fail

Verification

Manual:

$ bin/gstack-paths --get plan-root
/Users/lavesh/.claude/plans

$ bin/gstack-paths --get bogus
gstack-paths: unknown key 'bogus' (expected state-root|plan-root|tmp-root)
# exit 1

$ bin/gstack-paths
GSTACK_STATE_ROOT=/Users/lavesh/.gstack
PLAN_ROOT=/Users/lavesh/.claude/plans
TMP_ROOT=/var/folders/.../T/

- Pattern 2 in garrytan#1329: `eval "$(~/.claude/skills/gstack/bin/gstack-paths)"` trips Claude Code's bash AST parser ("Unhandled node type: string") because of tilde inside $() inside double-quoted eval arg. Forces manual approval on every /codex.
- `gstack-paths --get state-root|plan-root|tmp-root` prints one path, no `KEY=` prefix. Callers use `PLAN_ROOT=$(gstack-paths --get plan-root)`.
- Bare invocation unchanged (KEY=VALUE form), so unmigrated skills keep working.
- `codex/SKILL.md.tmpl` Step 0.6 switched + regenerated SKILL.md.
- Patterns 1/3/4 + other skills using the eval form out of scope; one-at-a-time migration.

Refs garrytan#1329
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant