mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-05 08:23:59 +01:00
* perf(gen-skill-docs): stop emitting the "When to invoke" body section
The Claude catalog trim moved each skill's routing prose ("Use when...",
"Proactively invoke...", voice triggers) out of the frontmatter description
into a "## When to invoke this skill" section at the top of the body. Claude
Code reads only name + description to choose a skill and loads the body after
the skill is invoked, so that text cannot route anything and costs tokens on
every invocation. Keep the one-line trimmed description; drop the section.
The routing prose stays in each template's description, which
--catalog-mode=full and the other hosts still render. Tests that pinned the
section now assert its absence, or check the template instead.
(cherry picked from commit 707ccd6f38)
* feat(gen-skill-docs): keep voice triggers in the root routing block
The Claude catalog trim drops each skill's "Voice triggers (speech-to-text
aliases)" line from its description, and the body section that used to hold
it is gone. {{VOICE_TRIGGER_ROUTING}} renders them in the root router's
routing block instead: one "Voice trigger ... → invoke /<skill>" line per
skill, read from the templates' voice-triggers lists, so disabled-skill
filtering applies to them like any other routing line. Other hosts keep the
line in each description and render nothing here.
extractVoiceTriggers moves to the new resolver so the generator and the
resolver share one parser.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 645e75b1e8)
* chore: regenerate SKILL.md files without the "When to invoke" section
Output of `bun run gen:skill-docs --host all` after the previous commits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit ff4c813337)
* test: refresh context-budget ceilings for the smaller skill bodies
`bun test/helpers/capture-context-budget.ts`. Against a recapture of the base
commit, 55 skills that lost the section get a lower ceiling and the root
gstack router rises by 532 tokens for the voice-trigger routing lines (5,369
tokens lower in total). devex-review and eval-plan differ from the committed
fixture only because it already lagged main. alwaysOnTotal is unchanged: the
catalog descriptions did not change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit d886fcecc1)
* test: refresh the claude ship golden without the "When to invoke" section
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit f1789dca5c)
* test: lower the claude design-review size pin
The size guard (#3199) asks for it: claude/design-review shrank by 437 chars
without its "When to invoke" section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 3a446fcc62)
* test: skip alias symlinks in the voice-routing check, keep unrelated budget ceilings, add #3129 CHANGELOG entry
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test: rebuild the historical cookie approval with the dropped section, record the skill-fixture change as non-behavioral
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.157.0 perf(gen-skill-docs): drop the "When to invoke" body section from Claude SKILL.md files (port of #3129)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: thanhnguyentung95 <17350411+thanhnguyentung95@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
93 lines
4.2 KiB
Markdown
93 lines
4.2 KiB
Markdown
---
|
|
name: freeze
|
|
version: 0.1.0
|
|
description: Restrict file edits to a specific directory for the session. (gstack)
|
|
triggers:
|
|
- freeze edits to directory
|
|
- lock editing scope
|
|
- restrict file changes
|
|
allowed-tools:
|
|
- Bash
|
|
- Read
|
|
- AskUserQuestion
|
|
hooks:
|
|
PreToolUse:
|
|
- matcher: "Edit"
|
|
hooks:
|
|
- type: command
|
|
command: 'bash -c "exec bash \"$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh\""'
|
|
statusMessage: "Checking freeze boundary..."
|
|
- matcher: "Write"
|
|
hooks:
|
|
- type: command
|
|
command: 'bash -c "exec bash \"$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh\""'
|
|
statusMessage: "Checking freeze boundary..."
|
|
- matcher: "NotebookEdit"
|
|
hooks:
|
|
- type: command
|
|
command: 'bash -c "exec bash \"$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh\""'
|
|
statusMessage: "Checking freeze boundary..."
|
|
---
|
|
<!-- AUTO-GENERATED from SKILL.md.tmpl — do not edit directly -->
|
|
<!-- Regenerate: bun run gen:skill-docs -->
|
|
|
|
# /freeze — Restrict Edits to a Directory
|
|
|
|
Lock file edits to a specific directory. Any Edit, Write or NotebookEdit
|
|
operation targeting a file outside the allowed path will be **blocked** (not
|
|
just warned).
|
|
|
|
```bash
|
|
GSTACK_STATE_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get GSTACK_STATE_ROOT); : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
|
mkdir -p "$GSTACK_STATE_ROOT"/analytics
|
|
echo '{"skill":"freeze","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> "$GSTACK_STATE_ROOT"/analytics/skill-usage.jsonl 2>/dev/null || true
|
|
```
|
|
|
|
## Setup
|
|
|
|
Ask the user which directory to restrict edits to. Use AskUserQuestion:
|
|
|
|
- Question: "Which directory should I restrict edits to? Files outside this path will be blocked from editing."
|
|
- Text input (not multiple choice) — the user types a path.
|
|
|
|
Once the user provides a directory path:
|
|
|
|
Set the user-selected boundary with the shared state writer. It resolves the physical absolute path and serializes replacement with investigation cleanup:
|
|
```bash
|
|
bash "$HOME/.claude/skills/gstack/freeze/bin/freeze-state.sh" set "<user-provided-path>"
|
|
```
|
|
|
|
Only report success if the helper succeeds. On `FREEZE_BUSY` or unexpected state, preserve it and ask the user to inspect recovery after any active writer finishes; never write or delete the state file directly.
|
|
|
|
Tell the user: "Edits are now restricted to `<path>/`. Any Edit, Write or
|
|
NotebookEdit outside this directory will be blocked. To change the boundary, run `/freeze`
|
|
again. To remove it, run `/unfreeze`."
|
|
|
|
## How it works
|
|
|
|
The hook reads `file_path` from the Edit/Write tool input JSON, or
|
|
`notebook_path` from a NotebookEdit (Jupyter notebook) call (shared
|
|
real-JSON extractor with /careful — one copy, sourced by both hooks), then
|
|
checks whether the path starts with the freeze directory. If not, it returns a
|
|
`hookSpecificOutput` payload with `permissionDecision: "deny"` to block the
|
|
operation (nested under `hookSpecificOutput` — Claude Code ignores a top-level
|
|
`permissionDecision`).
|
|
|
|
Polarity is fail-closed: a tool payload the hook cannot parse is DENIED, not
|
|
allowed — a boundary that fails open is not a boundary. A payload that parses
|
|
but has neither path field (a non-file tool) is allowed. A deny names the
|
|
tool, the path field, the boundary and `/unfreeze`. Symlinks are resolved
|
|
through their FINAL component, so an in-boundary symlink pointing outside the
|
|
boundary is checked against its target.
|
|
|
|
The freeze boundary persists until explicitly removed via the state file. The hook
|
|
script reads it on every Edit/Write/NotebookEdit invocation. Boundaries containing spaces
|
|
are supported.
|
|
|
|
## Notes
|
|
|
|
- The trailing `/` on the freeze directory prevents `/src` from matching `/src-old`
|
|
- Freeze applies to Edit, Write and NotebookEdit only — Read, Bash, PowerShell, Glob, Grep are unaffected
|
|
- This prevents accidental edits, not a security boundary — Bash or PowerShell commands like `sed` or `Set-Content` can still modify files outside the boundary
|
|
- To deactivate, run `/unfreeze`; ending or killing a conversation does not remove persisted state
|