ClaudeMods
☰
KO
● 0 명 접속 중 · 조회 0 회
후원프로젝트 제출
GitHub 저장소 · 작성자 KilimcininKorOglu

action-pin

GitHub Actions 워크플로의 Edit/Write 편집을 감시하는 Claude Code mod입니다. 단계가 움직이는 ref(tag 또는 branch)를 가리키면 대신 쓸 commit SHA를 모델에 알려 주며, 선택적 deny 모드는 ref가 고정될 때까지 git commit, push, merge를 막습니다.

KilimcininKorOglu@KilimcininKorOglu

KilimcininKorOglu/claude-code-mods/tree/main/plugins/action-pin

번역 완료

이 mod 소개

action-pin은 claude-code-mods 마켓플레이스용 Claude Code 플러그인입니다. Edit, Write, Bash 도구 호출을 hook하고 편집이 .github/workflows/.yml(및 .yaml) 또는 .github/actions//action.yml에 추가한 줄만 검사합니다. 40/64자리 hex commit을 가리키는 uses: 값은 이미 고정된 것으로 봅니다. 로컬 action(./.github/actions/...)과 컨테이너(docker://)는 통과하고, 그 밖의 모든 tag 또는 branch ref(actions/와 github/ 포함)는 보고됩니다. 보고된 각 ref에 대해 Accept: application/vnd.github.sha를 사용해 https://api.github.com/repos/<owner>/<repo>/commits/<ref>를 조회합니다(익명, 시간당 60회 요청, token 없음). 그리고 tag를 주석으로 남긴 SHA를 쓰라고 모델에 알리는 메모를 반환합니다. 편집 하나에서 조회하는 action은 최대 10개입니다. 워크플로가 ref를 고정할 때까지 발견 항목은 열린 상태로 남고 매 턴 메모가 해결되지 않은 항목을 알려 줍니다. deny 모드에서는 열린 워크플로 중 하나라도 움직이는 ref를 사용하는 동안 git commit, git push, git merge가 차단됩니다(commit은 git diff --cached --name-only -z로 스테이징 파일로 범위를 줄이고, push와 merge에는 그런 범위 제한이 없으며 /action-pin mode note 외에는 우회 방법이 없습니다). 사이드바 통합은 워크플로별 발견 항목을 보여 줍니다. function hooks에는 Claude Code 2.1.288 이상이 필요합니다. L3(네트워크 액세스) 수준입니다. 설치: claude plugin marketplace add KilimcininKorOglu/claude-code-mods를 실행한 뒤 claude plugin install action-pin@kilimcininkoroglu-mods를 실행하고 Claude Code를 다시 시작합니다.

설치

먼저 작성자의 README에서 marketplace와 플러그인 이름을 확인하세요. 저장소 구조에 따라 명령어가 달라질 수 있습니다.

claude plugin marketplace add KilimcininKorOglu/claude-code-mods
claude plugin install action-pin
원문 / README

action-pin

A GitHub Actions step written as actions/checkout@v4 runs whatever code that tag points at on the day the workflow runs, and a tag or a branch can be moved after you reviewed it. This mod watches the workflows the model edits: when an edit adds a step pinned to a moving ref, it tells the model the commit SHA to write instead. By default it stops nothing; in deny mode a commit, a push and a merge wait until every ref is pinned.

What it does

  1. It watches the Edit and Write tools. A call is checked when its path is .github/workflows/<name>.yml or .github/actions/<name>/action.yml (.yaml too).

  2. Only the lines the edit adds are read. A uses: value whose ref is a 40 or 64 character hex commit is already pinned and passes. A local action (./.github/actions/setup) and a container (docker://alpine:3.20) have no commit to pin, so they pass too. Every other ref, a tag (@v4) or a branch (@main), is reported, actions/* and github/* included.

  3. For each reported action it asks https://api.github.com/repos/<owner>/<repo>/commits/<ref> with the Accept: application/vnd.github.sha header, and GitHub answers the commit as plain text. No token is sent, so the anonymous rate limit applies (60 requests an hour per address). Each action and ref is asked once per session.

  4. Right after the tool's result, the model reads this note:

    action-pin: this edit uses actions by a moving ref: actions/checkout@v4 → 08c6903cd8c0fde910a37f88322edcfb5dd907a8. A tag or a branch can be moved to other code after a review, so a workflow with write access runs whatever it points at then. Write each as the SHA with the tag as a comment, for example: uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4
    

    At most 10 actions are named and looked up; the rest are counted. When GitHub does not answer, the action is named without a SHA, the note still asks for the pin, and the error is logged once until a different one comes.

  5. At the same moment you get one line in the transcript, so you see what the model was told. It holds the workflow and its actions, without the instruction. The workflow is named because the model saw the edit and you did not:

    action-pin: .github/workflows/ci.yml uses actions by a moving ref: actions/checkout@v4 → 08c6903cd8c0fde910a37f88322edcfb5dd907a8
    

    The note and the line are separate channels: the model never reads the line, and you never read the note. The workflow is shown relative to the git repository the session started in, or to the session's directory outside a repository. That path also keys its sidebar entry, so each workflow keeps an entry of its own. The root is read once at the session's start, because a Bash cd moves the session's own directory.

  6. With the sidebar open, the finding goes into its stream instead and the transcript stays clean. The workflow comes first in red, then one line per action: the action in the default colour, the moving ref red, the commit it points at faint. Without the sidebar, the line lands in the transcript as above.

  7. A finding stays open until the workflow pins those actions. After each later Edit or Write the mod reads every open workflow again, and one whose refs are all pinned closes. A workflow that is no longer there closes too, because it uses no action any more. One that is there but cannot be read keeps its finding, because an unread file proves nothing:

    action-pin: every action of .github/workflows/ci.yml is pinned to a commit now: actions/checkout@v4
    action-pin: .github/workflows/ci.yml is no longer there: actions/checkout@v4
    

    In the sidebar the red entry is removed and a green one takes its place; with the sidebar closed the same text is one transcript line. The model reads none of this, because it wrote the SHA itself.

  8. A finding the model did not close is measured again at the end of each main-loop turn, and what is left reaches the model as one note with your next prompt. The SHAs are already in memory, so this asks GitHub nothing:

    action-pin: 1 action(s) are still used by a moving ref: actions/checkout@v4. Pin each to the commit SHA of that ref, or take the step out.
    

    One note per turn, not one per prompt. Without it the finding would be said once, at the edit, and then stand in the pane while the model forgot it. You read nothing new, because the pane already shows the same finding.

  9. In deny mode the mod also stops git commit, git push and git merge while a workflow still uses an action by a moving ref. Before it stops one it reads each open workflow again, so a file the model pinned opens the gate by itself. A git commit answers for its own files alone: the mod reads the index (git diff --cached --name-only -z) and lets the commit run when it holds none of the open workflows, with one line to you saying how many still stand. A push and a merge have no index to read, so every finding stands there. There is no bypass; only you turn the gate off, with /action-pin mode note. note mode is the default and stops nothing.

Command

/action-pin                 on or off, the mode, and the workflows that still move
/action-pin on | off        on by default
/action-pin mode note       note only; the default
/action-pin mode deny       a commit, a push and a merge also stop while a ref moves

Install

claude plugin marketplace add KilimcininKorOglu/claude-code-mods
claude plugin install action-pin@kilimcininkoroglu-mods

Function hooks are early access. Claude Code 2.1.288 and later load them by default, so there is nothing to switch on.

After installing

  1. Restart Claude Code.

What it can reach

Validated with claude plugin validate on Claude Code 2.1.283:

❯ ./register.ts hooks: session.start, command.run{command=action-pin}, turn.complete, prompt.submit, tool.call{tool=Bash}, tool.call{tool=Edit}, tool.call{tool=Write}
❯ ./register.ts calls: $.command.register, $.fs.exists (via isThere), $.fs.read (via stillMoving), $.http.fetch (via resolveSha), $.process.run (via shownRootOf, stagedPaths), $.session.cwd (via stagedPaths), $.sidebar.clear (via dropEntry), $.sidebar.set (via toPerson), $.store.get (via readSettings), $.store.set (via runCommand, setMode), $.ui.log (via gate, report, toPerson)

Reach L3: it reaches the network.

1. Reads:    the path and the new text of each Edit and Write; the Bash command text; each open workflow again while a finding stands, also at the turn's end
2. Runs:     git rev-parse --show-toplevel once at the session's start, to show workflows against the repository root; git rev-parse --show-toplevel and git diff --cached --name-only -z, at a commit in deny mode, to read which files the commit holds
3. Sends:    the public action name and its ref (for example actions/checkout and v4) to api.github.com, at most 10 per edit, once each per session; no token, no repository content, no file path
4. Persists: in $.store, the on/off setting and the mode; the resolved SHAs live in memory for one session
5. Hostile input: the answer is used only when it is 40 hex characters, and it is written into the note alone; the mod never edits a file

Limits

  • The check is lexical: a uses: line inside a block comment or a YAML string still counts.
  • A workflow already in the repository is not checked; only the lines an edit adds are.
  • An action that the anonymous rate limit or a private repository hides gets the note without a SHA.
  • A SHA resolved once is kept for the session, so a tag moved during the session keeps its first answer.
  • A finding closes only when the workflow no longer uses those actions by a ref. A file that cannot be read keeps it open.
  • The deny mode has no bypass. When a finding cannot be fixed, you turn the gate off with /action-pin mode note.
  • The gate reads the command text. A commit through a script or an alias that hides git commit is not stopped.
  • A git commit -a, a -am and a commit with a pathspec after -- are not narrowed to the index, because they commit files the index does not hold yet. Every open finding stands for those.
  • The index is read in the repository of the session's own directory. A finding of a workflow in another repository never matches it, so such a commit runs.

Development

make install     # eslint, typescript-eslint, typescript
make lint        # complexity limit 10, the build fails above it
make typecheck   # needs .claude/types/ from /plugin-types
make validate
make test        # claude plugin test

비슷한 프로젝트