feat(voice-prompt): 보이스 모드 STT 입력 정규화 플러그인 — 3단 분류 + 저장소 대조 식별자 해소 - #193
Conversation
한국어 보이스 모드가 넘겨준 음성 인식 결과를 실행 전에 명령으로 되돌리는 플러그인. 명시 호출 후 해제까지 모든 입력에 적용된다. 핵심은 텍스트 청소가 아니다. 말버릇 제거는 모델이 이미 하는 일이라 값이 낮고, 실제 값은 추측으로 넘어가던 자리에 확인 절차를 끼워넣는 데 있다. "로더 파일" 을 들으면 모델은 파일명을 지어내는데, loader.py 가 실재하는지 찾아보지 않으면 알 수 없다. 독해 실패가 아니라 행동 누락이고, 그것만은 지침으로 고칠 수 있다. 3단 분류 — plaud-note-taking 의 4-state 는 문서에 표시를 붙일 표면이 있어서 성립하는 구조이고, 실행형 스킬에는 그 표면이 없어 셋으로 줄어든다. - 자동 수정: 말버릇 부류, 맞춤법, 저장소 단일 후보, 스킬 목록 단일 후보, 발화 내 자기수정 - 질문: 후보 0건·다건, 두 해석이 다른 행동을 유발, 되돌리기 어려운 작업 대상 (1라운드 배치) - 손대지 않음: 숫자·날짜·버전·PR 번호·금액·경로 리터럴 세 번째가 안전장치다. PR 189 가 PR 180 으로 들리면 조용히 잘못된 PR 을 건드리는데, 그 실패는 다른 오류와 달리 그럴듯하게 성공한다. 말버릇은 정지 단어 목록이 아니라 기능 잔존 검사로 지운다. "그냥 지워" 의 그냥은 "다른 건 하지 말고" 이고 "일단 커밋해" 의 일단은 순서 지시다. 지웠을 때 행동이 달라지면 말버릇이 아니고, 애매하면 보존한다. 자기수정 표지는 삭제 대상이 아니라 정정 근거로, 그 뒤의 발화가 이긴다. 식별자 대조는 git ls-files -co --exclude-standard 에 grep -iF 를 건다. -F 가 음성에서 온 문자열의 정규식 메타문자를 무력화하고, grep 종료 상태 1은 에러가 아니라 후보 0건 신호이므로 || true 로 삼키지 않는다. 일반 한국어 말버릇은 번들 레퍼런스에, 개인 발음 습관과 도메인 용어는 .claude/voice-prompt/speech-profile.md 에 분리한다. 사용자가 말한 어쨌든·하여튼·그냥·뭐는 개인 특징이 아니라 일반 한국어 담화 표지라 번들에 넣었다. 동작 기제상 이것은 전처리 필터가 아니라 지침이다. UserPromptSubmit 훅 출력도 additionalContext 로 덧붙을 뿐 발화를 치환하지 않으므로, 정규화는 모델 추론 안에서 일어나고 보장 수준도 다른 스킬과 같다. 에코 라인이 그 관측 지점이라 필수로 두었고, 마커 파일이나 재주입 훅으로 지속을 강제하지는 않았다. 문서 동기화: marketplace(항목 + metadata 2.10.0), AGENTS 23→24 + eligible 22, README 배지·모음·트리·22/24·상세절·산문 총계, settings.json, PILOTS 등록, Codex 매니페스트 재생성. 가드 6종 전부 통과. Closes #192
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 9 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds the ChangesVoice Prompt Plugin
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant VoiceUser
participant VoicePromptSkill
participant Repository
participant InteractiveInput
participant Execution
VoiceUser->>VoicePromptSkill: Invoke /voice-prompt:voice-prompt
VoiceUser->>VoicePromptSkill: Provide spoken input
VoicePromptSkill->>Repository: Resolve identifier candidates
Repository-->>VoicePromptSkill: Return matches
VoicePromptSkill->>InteractiveInput: Request clarification or confirmation
InteractiveInput-->>VoicePromptSkill: Return response
VoicePromptSkill->>VoiceUser: Echo normalized content
VoicePromptSkill->>Execution: Execute permitted action
Possibly related PRs
🚥 Pre-merge checks | ✅ 9✅ Passed checks (9 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ef97e06f53
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| ```bash | ||
| mkdir -p .claude/voice-prompt | ||
| cp "<skill dir>/templates/speech-profile.md" .claude/voice-prompt/speech-profile.md |
There was a problem hiding this comment.
Resolve the bundled template before copying it
When the marketplace-installed skill first persists a confirmed profile from a normal user repository, this command treats <skill dir> as a literal directory name, so cp fails and the promised .claude/voice-prompt/speech-profile.md is never initialized. Define a cross-runtime skill-root resolver first—covering CLAUDE_PLUGIN_ROOT, the source tree, and the Codex cache—and copy through that resolved path.
AGENTS.md reference: AGENTS.md:L120-L120
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.claude/spec/2026-07-30-voice-prompt.md:
- Line 40: MD040 warnings occur because the affected Markdown code fences lack
language tags. Add the specified tags at .claude/spec/2026-07-30-voice-prompt.md
lines 40, 161, and 239, and plugins/voice-prompt/CLAUDE.md line 30 using text;
add bash at plugins/voice-prompt/skills/voice-prompt/SKILL.md line 108; and add
text at lines 141 and 155.
In `@plugins/voice-prompt/skills/voice-prompt/references/korean-filler.md`:
- Around line 19-20: Update the guidance for `그냥` in the Korean filler reference
so it is not classified as unconditionally removable. Apply the function-residue
test to each occurrence, retaining `그냥` when it scopes the action or the
intended scope is unclear, including in “그냥 이 파일 고쳐”.
In `@plugins/voice-prompt/skills/voice-prompt/references/stt-error-classes.md`:
- Line 24: Update the “Sentence boundaries and punctuation” guidance to require
clarification before executing when alternate segmentations would produce
different actions, targets, or ordering. Permit automatic merging only when all
segmentations are behaviorally equivalent, rather than relying on echoing to
reveal the interpretation.
- Around line 18-20: The English loanwords and Homophone substitution rows
currently permit automatic corrections based on context alone; revise their
guidance to require an explicit corroborating evidence basis, using context only
as a lookup signal. Preserve the rule that ambiguous interpretations requiring
different actions must prompt the user, and keep identifier handling in the
English loanwords guidance.
In `@plugins/voice-prompt/skills/voice-prompt/SKILL.md`:
- Line 3: Remove the general-utterance trigger “음성으로 말할게” from the description’s
Triggers list, while retaining explicit /voice-prompt and voice-input
normalization activation triggers and the existing no-auto-fire behavior.
- Around line 110-118: The candidate-search commands in the git/grep section
must distinguish “no matches” from command failures. Update the file and branch
lookup flow to capture each git and grep exit status separately, treating only
grep status 1 as zero candidates and propagating or reporting git failures and
grep statuses 2 or higher as errors; do not let the pipeline mask git failures.
- Around line 110-113: Update the git file and branch search commands in the
voice-prompt skill so the stem value is never interpolated directly into shell
source. Bind the STT identifier to a separate argument or environment variable,
then pass that safely quoted value to grep -iF -- "$STEM", preserving the
existing fixed-string, case-insensitive matching behavior.
- Around line 162-172: Unify the profile-template creation contract so
initialization never writes without user confirmation: in
plugins/voice-prompt/skills/voice-prompt/SKILL.md, make the mkdir/cp commands
conditional on explicit approval, and in
.claude/spec/2026-07-30-voice-prompt.md, revise the first-run automatic-copy
requirement to use the same confirmation gate.
- Around line 167-172: Replace the undefined <skill dir> placeholder in the
first-use seeding instructions with the standard cross-runtime PLUGIN_ROOT
resolver, and reference the template at
$PLUGIN_ROOT/skills/voice-prompt/templates/speech-profile.md so the command
works consistently in Claude, Codex, and Hermes.
In `@plugins/voice-prompt/skills/voice-prompt/templates/speech-profile.md`:
- Around line 12-13: Update the speech-profile template guidance so entries are
persisted only after explicit user confirmation. Remove the authorization to add
entries based solely on a trusted project document; allow such documents to
support proposals, but never to silently modify the profile or correction
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: f510e516-84f1-4424-a8d0-ad7b9129c359
📒 Files selected for processing (14)
.agents/plugins/marketplace.json.claude-plugin/marketplace.json.claude/settings.json.claude/spec/2026-07-30-voice-prompt.mdAGENTS.mdREADME.mdplugins/voice-prompt/.claude-plugin/plugin.jsonplugins/voice-prompt/.codex-plugin/plugin.jsonplugins/voice-prompt/CLAUDE.mdplugins/voice-prompt/skills/voice-prompt/SKILL.mdplugins/voice-prompt/skills/voice-prompt/references/korean-filler.mdplugins/voice-prompt/skills/voice-prompt/references/stt-error-classes.mdplugins/voice-prompt/skills/voice-prompt/templates/speech-profile.mdscripts/check-skill-tool-portability.mjs
CodeRabbit 10건 + Codex P2 1건 전부 real 로 판정해 적용. 대부분 한 번에 쓴 5개 문서 사이의 내부 모순이고, 두 건은 이 저장소 자체 P1 규칙 위반이었다. 셸 주입 (CR Critical) — Step 2 의 `<stem>` 을 명령문에 직접 보간하도록 써서, 전사록의 따옴표·백틱·$()·개행이 셸 구문으로 탈출할 수 있었다. grep -F 는 셸 파싱 이후만 보므로 방어가 아니다. 변수 바인딩으로 바꿨다. 주입 시도가 무력화되는 것을 실행으로 확인. 종료 상태 (CR Major) — "grep non-zero = 후보 0건" 이라 썼는데 status 1만 불일치이고 2 이상은 오류다. 또 한 파이프라인이라 git 실패가 grep status 1로 가려져, 깨진 인덱스가 "그런 파일 없음" 질문으로 새어나갔다. 저장소 자체 P1 의 "API 실패를 빈 결과로 삼키는 패턴" 이다. git 과 grep 을 분리하고 상태를 각각 검사한다. 저장소 밖에서 시끄럽게 중단하는 것을 실행으로 확인. PLUGIN_ROOT (CR Major + Codex P2, 양쪽 동의) — `<skill dir>` 는 실행 불가 placeholder 라 marketplace 설치 사용자에게서 cp 가 실패하고 프로필이 초기화되지 않았다. AGENTS.md 가 요구하는 크로스 런타임 resolver(CLAUDE_PLUGIN_ROOT → 소스트리 → Codex 캐시)로 교체. 3분기 전부 실행으로 확인. 문서 간 모순 (CR Major 5건): - 트리거 `음성으로 말할게` 는 호출 요청이 아니라 평서문이라, 명시 활성화 계약을 깨고 incidental mention 으로 자동 로드될 수 있었다. 제거. - korean-filler 예문이 "그냥 이 파일 고쳐" 의 그냥을 무조건 필러로 단정해, 같은 문서의 기능 잔존 검사 규칙과 어긋났다. 여기서는 "이 파일만" 으로도 읽히므로 보존이 정답이다. - 영어 차용어·동음이의 행이 "문맥상 명확하면 수정" 을 허용해, 같은 문서의 "문맥은 후보를 표시할 뿐 재작성 권한이 아니다" 와 정면 충돌했다. 표기만 바뀌고 지시 대상이 안 바뀌는 경우로 한정. - 문장 경계 행의 "에코가 해석을 드러낸다" 는 사후 통보라 잘못된 행동을 되돌리지 못한다. 분절이 행동·대상·순서를 다르게 만들면 실행 전에 묻도록 변경. - speech-profile 템플릿이 "신뢰할 문서가 검증하면" 추가를 허용해 SKILL.md 의 사용자 확인 금지 조항을 우회했다. 프로필 항목은 이후 명령 해석을 바꾸므로 사용자만 게이트다. 프로필 시딩 순서 (CR Major, 2파일) — 시딩이 별도 권한처럼 읽혀 스킬이 켜지면 파일이 생기는 것으로 보였다. 확인이 먼저이고 시딩은 이미 승인된 쓰기의 첫 단계임을 SKILL.md 와 스펙 양쪽에 명시. 별도 확인 게이트를 추가하지 않은 것은 그게 왕복만 늘리고 위험은 같기 때문이다 — 순서를 명확히 하는 게 최소 수정이다. 가드 6종 + bash 문법 + frontmatter 재검증 통과.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3dcf039cba
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| defense against injection. | ||
|
|
||
| ```bash | ||
| STEM='<the spoken stem, single-quoted>' |
There was a problem hiding this comment.
voice-prompt는 typed input에도 적용되므로, 사용자가 foo'; touch /tmp/pwn; #처럼 작은따옴표가 포함된 stem을 제공했을 때 이 자리의 placeholder를 그대로 치환하면 인용이 종료되어 뒤 문자열이 셸 코드로 실행됩니다. grep -F는 이미 파싱된 뒤라 이를 막지 못하므로, stem을 명령문에 삽입하지 말고 셸의 별도 argv나 환경 변수처럼 코드와 데이터가 분리되는 경로로 전달해야 합니다.
AGENTS.md reference: AGENTS.md:L286-L288
Useful? React with 👍 / 👎.
| # Get the list first, so a git failure cannot hide behind grep's "no match". | ||
| files=$(git ls-files -co --exclude-standard) || { | ||
| echo "voice-prompt: git ls-files failed — a tool error, not zero candidates" >&2; exit 1; } | ||
| candidates=$(printf '%s\n' "$files" | grep -iF -- "$STEM"); rc=$? |
There was a problem hiding this comment.
핵심 사용례인 로더 파일 → loader.py에서는 $STEM이 로더이므로 git ls-files의 영문 경로에 대한 grep -iF 결과가 항상 0건입니다. 따라서 실제 저장소에 단일 loader.py가 있어도 near-miss를 제시하거나 자동 해소하지 못하고, 플러그인이 추가하려는 코드스위칭 식별자 해소 기능이 동작하지 않습니다. 한글 발음에서 가능한 영문 stem 후보를 먼저 생성한 뒤 각 후보를 실제 경로와 교차 검증하고, 실재 후보가 정확히 하나일 때만 확정하도록 절차를 보강해야 합니다.
Useful? React with 👍 / 👎.
| [ -z "$PLUGIN_ROOT" ] && [ -d plugins/voice-prompt/skills ] && PLUGIN_ROOT=plugins/voice-prompt | ||
| if [ -z "$PLUGIN_ROOT" ]; then | ||
| cache_root="${CODEX_PLUGIN_CACHE:-$HOME/.codex/plugins/cache}" | ||
| PLUGIN_ROOT=$(ls -1d "$cache_root"/*/voice-prompt/* 2>/dev/null | sort | tail -1) |
There was a problem hiding this comment.
Codex 캐시에 여러 marketplace 별칭이나 버전이 남아 있으면 전체 경로의 사전식 sort가 버전 순서를 보장하지 않습니다. 예를 들어 zeta/.../0.1.0은 alpha/.../0.2.0보다 뒤에 오고, 같은 marketplace에서도 0.9.0이 0.10.0보다 뒤에 와서 오래되거나 불완전한 캐시를 선택해 template 조회가 실패합니다. 기존 코멘트 뒤 추가된 이 resolver가 새 근거이며, 버전 basename을 수치 기준으로 정렬하면서 실제 template이 존재하는 첫 후보만 선택해야 합니다.
Useful? React with 👍 / 👎.
| if [ -z "$PLUGIN_ROOT" ]; then | ||
| cache_root="${CODEX_PLUGIN_CACHE:-$HOME/.codex/plugins/cache}" | ||
| PLUGIN_ROOT=$(ls -1d "$cache_root"/*/voice-prompt/* 2>/dev/null | sort | tail -1) | ||
| fi |
There was a problem hiding this comment.
voice-prompt는 HERMES_ELIGIBLE에 없어 현재 Hermes에서는 install-skills.mjs를 통한 skill 단위 설치가 유일한 경로인데, 이 resolver는 Claude root·소스트리·Codex 캐시만 검사합니다. 따라서 일반 프로젝트에서 Hermes 설치본으로 확인된 profile 항목을 처음 저장하면 template을 찾지 못하고 중단됩니다. 기존 template-path 코멘트 이후의 새 근거는 최종 resolver에도 $HERMES_HOME/skills/voice-prompt 같은 skill 단위 설치 분기가 없다는 점이므로, Hermes profile/default 설치 경로를 검증하는 fallback을 추가해야 합니다.
AGENTS.md reference: AGENTS.md:L242-L244
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/voice-prompt/skills/voice-prompt/SKILL.md`:
- Around line 190-207: Extend the template-resolution block around PLUGIN_ROOT
and TEMPLATE with the repository’s Hermes fallback, before declaring the bundled
template missing. Preserve the existing Claude, repository-relative, and Codex
cache resolution paths, and ensure Hermes can resolve the shared skill template
while retaining portability across all three runtimes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 63e6463a-730c-4a5e-9eb7-5af0513471b4
📒 Files selected for processing (5)
.claude/spec/2026-07-30-voice-prompt.mdplugins/voice-prompt/skills/voice-prompt/SKILL.mdplugins/voice-prompt/skills/voice-prompt/references/korean-filler.mdplugins/voice-prompt/skills/voice-prompt/references/stt-error-classes.mdplugins/voice-prompt/skills/voice-prompt/templates/speech-profile.md
🚧 Files skipped from review as they are similar to previous changes (3)
- plugins/voice-prompt/skills/voice-prompt/templates/speech-profile.md
- plugins/voice-prompt/skills/voice-prompt/references/stt-error-classes.md
- .claude/spec/2026-07-30-voice-prompt.md
Codex P1 3건 + CR Major 1건 + CR Minor 1건. 첫 항목은 이 플러그인이 존재하는 이유 자체가 동작하지 않던 버그다. 음역 단계 누락 (Codex P1) — Step 2 가 발화된 stem 을 그대로 git ls-files 결과에 grep 하도록 써서, 대표 사례 "로더 파일" 에서 STEM 이 "로더" 가 되어 영문 경로에 대한 검색이 항상 0건이었다. loader.py 가 실재해도 near-miss 조차 제시하지 못하니, 코드스위칭 식별자 해소라는 이 플러그인의 핵심이 조용히 no-op 였다. 한글 발음에서 영문 stem 후보를 먼저 만들고 각 후보를 조회해 union 하는 단계를 추가했다. 실행으로 재현·확인: "로더" 0건, "loader" 적중, 다중 후보(cr-fix/crfix)는 한쪽만 맞아도 union. 후보를 argv 로 전달 (Codex P1 재확인) — stem 을 명령문에 보간하는 대신 set -- 와 "$@" 로 넘겨 코드와 데이터를 분리했다. 이전 커밋의 변수 바인딩보다 강하다. Codex 캐시 버전 정렬 (Codex P1) — 전체 경로 사전식 sort 는 버전 순서를 보장하지 않아 zeta/0.1.0 이 alpha/0.2.0 보다, 0.9.0 이 0.10.0 보다 뒤에 온다. 오래된 캐시를 골라 template 조회가 실패한다. 버전 basename 기준 sort -V + BSD 수치 폴백으로 교체했다 (cr-fix Step 1 의 기존 선례와 동일 패턴). 픽스처로 재현: 수정 전은 실제로 zeta/0.1.0 을 고르고, 두 분기 모두 alpha/0.10.0 을 고른다. Hermes 분기 (CR Major + Codex P2, 양쪽 동의) — resolver 가 Claude·소스트리·Codex 캐시만 봐서 Hermes 에서 프로필 초기화가 실패했다. voice-prompt 는 HERMES_ELIGIBLE 이 아니라 어댑터가 생성되지 않으므로 install-skills.mjs 의 skill 단위 설치가 유일한 경로다. 플러그인 레이아웃과 플랫 레이아웃 둘 다 탐색하고, template 경로도 두 레이아웃을 시도한다. 플랫 분기는 라이브 Hermes 미검증이라 그대로 표기했다 (cr-fix 의 동일 분기와 같은 자세). MD040 (CR Minor) — 언어 태그 없는 펜스 6곳에 text 부여. CR 이 함께 지적한 SKILL.md:108 의 bash 추가는 이미 붙어 있어 stale 로 건너뛴다. 0건 처리도 함께 고쳤다: 후보가 없으면 사용자를 묻기 전에 먼저 자기 음역을 의심하고 다른 표기(하이픈·연결·음역 대 번역)를 시도한다. 가드 6종 + bash 문법 + frontmatter 통과. SKILL.md 288행 (상한 500).
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/voice-prompt/skills/voice-prompt/SKILL.md`:
- Around line 133-147: 검색 루프의 오류 상태가 후속 sort 성공 상태로 덮이지 않도록, 파일 후보와 브랜치 후보를 처리하는
두 `done | sort -u` 파이프라인에 `pipefail`을 적용하거나 파이프라인 없이 종료 상태를 명시적으로 전달하세요. `STEM`별
`grep` 오류 시 기존처럼 전체 명령이 실패하고, 정상적인 후보 없음(`rc=1`)은 계속 허용되도록 유지하세요.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: da2d2ef9-d63c-498c-8f80-240fd8b4403b
📒 Files selected for processing (3)
.claude/spec/2026-07-30-voice-prompt.mdplugins/voice-prompt/CLAUDE.mdplugins/voice-prompt/skills/voice-prompt/SKILL.md
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c4d14d6864
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Plugin skills are explicit opt-in loads in Hermes — reach this body with | ||
| `skill_view("voice-prompt:voice-prompt")` rather than expecting the description to surface it. |
There was a problem hiding this comment.
Use the unqualified Hermes skill-unit name
When voice-prompt is installed on Hermes through the advertised install-skills.mjs route, no plugin adapter registers the voice-prompt: namespace because this plugin is outside HERMES_ELIGIBLE; that qualification is created only by generated plugin adapters. Consequently, skill_view("voice-prompt:voice-prompt") cannot activate the only Hermes installation this change supports. Use the bare skill-unit name or make the plugin Hermes-eligible.
AGENTS.md reference: AGENTS.md:L239-L244
Useful? React with 👍 / 👎.
| for STEM in "$@"; do | ||
| hits=$(printf '%s\n' "$files" | grep -iF -- "$STEM"); rc=$? | ||
| [ "$rc" -le 1 ] || { echo "voice-prompt: grep failed (status $rc)" >&2; exit 1; } | ||
| [ "$rc" -eq 0 ] && printf '%s\n' "$hits" | ||
| done | sort -u |
There was a problem hiding this comment.
Propagate lookup failures past sort
When grep returns status 2 or higher, this exit 1 runs in the left side of the done | sort -u pipeline. Without pipefail, the pipeline returns sort's successful status, so the shell continues into the branch lookup and subsequent action with a partial or empty candidate set despite the stated fail-loud contract. Collect and sort the output only after the loop succeeds, or otherwise propagate the producer's status; the branch loop has the same defect.
Useful? React with 👍 / 👎.
CR Major 1건 + Codex P2 2건. 첫 항목은 iter 2 에서 내가 만든 회귀다.
파이프라인 종료 상태 회귀 (CR Major + Codex P2, 동일 지적) — iter 2 에서 음역 후보
루프를 넣으면서 `for … done | sort -u` 구조가 됐다. 루프가 서브셸에서 돌아 파이프라인
종료 상태가 sort 의 성공으로 덮이므로, 루프 안의 exit 1 이 삼켜진다. iter 1 에서
고쳤던 "도구 오류가 후보 0건으로 새어나가는" 문제를 그대로 되살린 것이다. 실증:
`for x in a; do echo hit; exit 1; done | sort -u` 의 종료 상태는 0이다.
CR 이 제안한 `set -o pipefail` 한 줄은 채택하지 않았다. 실제로 넣고 돌려보니 반대
방향으로 틀린다 — 루프의 마지막 명령이 평범한 no-match 에서 거짓 테스트가 되므로,
모든 후보가 안 맞은 정상 케이스가 실패로 보고된다. 후보 0건은 질문이어야 하고 에러가
아니다. 파이프라인을 없애고 변수에 누적한 뒤 루프 밖에서 sort 하는 형태로 바꿨다.
이 형태만이 "오류" 와 "후보 0건" 을 구분한다. `&&` 대신 if 블록을 쓴 것은 set -e
하에서 거짓 테스트가 루프를 죽이지 않게 하기 위함이다 (AGENTS.md P1 의 흡수 범위
한정 규칙과 동일).
4케이스 실행 검증: 단일 적중 종료 0 / 전 후보 0건 종료 0 (에러 아님) / 부분 적중
union / grep rc=2 강제 시 종료 1 전파. set -e 하에서도 통과.
Hermes bare 스킬명 (Codex P2) — 본문이 `skill_view("voice-prompt:voice-prompt")` 를
안내했으나 `<plugin>:<skill>` 한정자는 생성된 플러그인 어댑터가 만드는 것이고
voice-prompt 는 HERMES_ELIGIBLE 밖이라 어댑터가 없다. 유일한 Hermes 경로인 skill-unit
설치는 frontmatter name 을 그대로 등록한다 (`scripts/install-skills.mjs:76` 의
`id: fm.name`, `:267` 의 전역 유일성 assert 로 확인). bare `skill_view("voice-prompt")`
로 정정하고, 한정자 형태는 allowlist 에 추가될 때만 맞다고 명시했다. 플러그인을
HERMES_ELIGIBLE 로 올리는 대안은 커버리지 결정이라 요청 범위 밖으로 두었다.
가드 6종 + bash 문법 + frontmatter 통과. SKILL.md 309행 (상한 500).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9f59ac6123
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| <details> | ||
| <summary><strong>voice-prompt</strong> - 보이스 모드 STT 입력 정규화</summary> | ||
|
|
||
| 한국어 보이스 모드가 넘겨준 음성 인식 결과를 실행 **전에** 명령으로 되돌립니다. `/voice-prompt` 를 한 번 타이핑하면 해제할 때까지 모든 입력에 적용됩니다 (자동 감지 없음). |
There was a problem hiding this comment.
Advertise the namespaced skill command
For a marketplace or local plugin install, this skill is exposed under the plugin namespace, consistent with the repository's existing /github-dev:resolve-issue example, so /voice-prompt does not identify the added skill. Because automatic activation is explicitly disabled, users following this primary instruction cannot enable the feature; document /voice-prompt:voice-prompt consistently here, in the skill description, and in the plugin guide.
Useful? React with 👍 / 👎.
|
|
||
| | State | Condition | Action | | ||
| |---|---|---| | ||
| | **Auto-fix** | A basis exists: a filler class from `references/korean-filler.md`, plain orthography, a single repo candidate, a single installed-skill candidate, an entry in the speech profile, or the speaker's own self-correction inside the utterance | Fix silently; surface it as one item in the echo | |
There was a problem hiding this comment.
Load the persisted speech profile on activation
In a fresh session, .claude/voice-prompt/speech-profile.md is an ordinary project file and is not automatically placed in context, but no process step checks for or reads it before classifying spans. Consequently, entries confirmed and persisted in an earlier session cannot actually serve as the advertised auto-fix basis unless the model happens to inspect the file independently; add an activation step that conditionally reads the live profile before normalization.
AGENTS.md reference: AGENTS.md:L61-L61
Useful? React with 👍 / 👎.
Codex P2 2건. 둘 다 문서가 약속한 것이 실제로는 닿지 않는 자리였다. 호출어 (Codex P2) — 문서와 description 이 `/voice-prompt` 를 안내했으나 플러그인 스킬은 플러그인 네임스페이스로 등록되므로 그 형태로는 이 스킬이 지목되지 않는다. 자동 활성화를 명시적으로 껐기 때문에, 안내를 그대로 따른 사용자는 기능을 켤 방법이 없었다. 저장소 관례도 qualified 형태다 (`/council:convene`, README:94/316). `/voice-prompt:voice-prompt` 로 통일했다 — SKILL.md description·활성화 계약, README 상세절, 플러그인 CLAUDE.md, 스펙 관계도·본문. iter 3 에서 Hermes 쪽을 bare `voice-prompt` 로 고친 것과 상충하지 않는다. Claude Code 는 플러그인 네임스페이스로 등록하고, Hermes skill-unit 설치는 frontmatter name 을 그대로 등록한다 (install-skills.mjs 확인). 런타임이 다르므로 본문에 그 구분을 명시했다. 프로필 읽기 누락 (Codex P2) — 3단 분류표가 "speech profile 항목" 을 자동 수정 근거로 올려놓고 Step 5 는 그 파일에 쓰지만, 어디에서도 읽지 않았다. `.claude/voice-prompt/speech-profile.md` 는 평범한 프로젝트 파일이라 컨텍스트에 자동으로 올라오지 않으므로, 이전 세션에서 확정한 항목이 다음 세션에서 보이지 않는다. 즉 사전이 쓰기 전용이었고, 같은 오인식을 매 세션 다시 물어보게 된다. 활성화 계약에 조건부 읽기 단계를 추가했다. 셸 cat 이 아니라 파일 읽기 도구를 쓰라고 명시한 것은 저장소 P1 의 "Read/Edit 영역을 Bash cat 으로 우회 금지" 규칙 때문이다. 가드 6종 + bash 문법 + frontmatter(738자) 통과. SKILL.md 320행 (상한 500).
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/voice-prompt/CLAUDE.md`:
- Around line 1-5: CLAUDE.md의 모든 음성 프롬프트 설명과 동작 지침을 제거하고 파일을 `@AGENTS.md` 포인터만
포함하도록 유지하세요. 제거한 활성화 및 speech-profile 관련 내용은 SKILL.md에만 남기며, CLAUDE.md에는 다른 지침이나
중복 설명을 추가하지 마세요.
In `@plugins/voice-prompt/skills/voice-prompt/SKILL.md`:
- Line 40: scripts/install-skills.mjs에서 버전 없이 호출하는 npx skills 의존성을 특정
skills@<version>으로 고정하세요. 해당 설치 동작을 설명하는 SKILL.md의 문구도 고정 버전 사용을 반영하도록 수정하고, 기존
설치 경로는 유지하세요.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: d5ac24e4-bb5a-49ab-baca-a451c0cc1826
📒 Files selected for processing (4)
.claude/spec/2026-07-30-voice-prompt.mdREADME.mdplugins/voice-prompt/CLAUDE.mdplugins/voice-prompt/skills/voice-prompt/SKILL.md
🚧 Files skipped from review as they are similar to previous changes (2)
- README.md
- .claude/spec/2026-07-30-voice-prompt.md
| # voice-prompt | ||
|
|
||
| One skill, `voice-prompt`, that normalizes Korean voice-mode STT input before the session acts | ||
| on it. Typed once as `/voice-prompt:voice-prompt`, it stays active until released. | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
CLAUDE.md를 @AGENTS.md 포인터 전용으로 유지하세요.
영향: Claude 지침이 CLAUDE.md와 AGENTS.md로 분산되어 중복·충돌될 수 있습니다. 근거: 이 파일의 변경 구간에 활성화와 speech-profile 동작 설명이 직접 추가되었습니다. 수정 방향: 해당 설명은 SKILL.md에만 두고 CLAUDE.md에는 @AGENTS.md만 남기세요.
As per coding guidelines, **/CLAUDE.md는 @AGENTS.md 포인터 외의 별도 내용을 갖지 않아야 합니다.
Also applies to: 59-68
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/voice-prompt/CLAUDE.md` around lines 1 - 5, CLAUDE.md의 모든 음성 프롬프트 설명과
동작 지침을 제거하고 파일을 `@AGENTS.md` 포인터만 포함하도록 유지하세요. 제거한 활성화 및 speech-profile 관련 내용은
SKILL.md에만 남기며, CLAUDE.md에는 다른 지침이나 중복 설명을 추가하지 마세요.
Source: Coding guidelines
| its own. **Load it by its bare name: `skill_view("voice-prompt")`.** The qualified | ||
| `<plugin>:<skill>` form comes from a generated plugin adapter, and `voice-prompt` is outside | ||
| `HERMES_ELIGIBLE`, so no adapter exists for it; the skill-unit install | ||
| (`node scripts/install-skills.mjs`, which wraps `npx skills`) registers the frontmatter `name` |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n 'npx[[:space:]]+skills(@|[[:space:]]|$)|skills@[0-9]' scripts/install-skills.mjs
rg --files | rg '(^|/)(package(-lock)?\.json|pnpm-lock\.yaml|yarn\.lock)$'Repository: YoungjaeDev/my-claude-plugins
Length of output: 416
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the installer and the referenced documentation line.
wc -l scripts/install-skills.mjs plugins/voice-prompt/skills/voice-prompt/SKILL.md
cat -n scripts/install-skills.mjs | sed -n '1,260p'
printf '\n--- SKILL excerpt ---\n'
cat -n plugins/voice-prompt/skills/voice-prompt/SKILL.md | sed -n '30,55p'
printf '\n--- lockfiles / package manifests ---\n'
rg -n 'skills(@|[[:space:]]|$)|npx[[:space:]]+skills|npx[[:space:]]+skills@[0-9]' \
scripts/install-skills.mjs plugins/voice-prompt/skills/voice-prompt/SKILL.md package.json \
package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null || trueRepository: YoungjaeDev/my-claude-plugins
Length of output: 16885
scripts/install-skills.mjs의 npx skills를 고정 버전으로 바꾸세요.
- 영향: 버전 없는
npx skills add는 매번 최신 패키지를 해석해 공급망 리스크와 재현성 저하를 만듭니다. - 근거: 현재 설치 경로가
npx skills에 버전 pin 없이 의존합니다. - 수정 방향:
skills@<version>으로 고정하고, 문서 설명도 그 동작에 맞게 맞추세요.
🧰 Tools
🪛 SkillSpector (2.4.4)
[warning] 40: [RP1] null: npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.
Remediation: Pin the version: npx @scope/server@1.2.3
(MCP Rug Pull (RP1))
[warning] 226: [EA2] Autonomous Decision Making: Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.
Remediation: Add human-in-the-loop confirmation for destructive, irreversible, or high-impact operations. Never auto-execute commands that modify files, send data, or alter system state.
(Excessive Agency (EA2))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/voice-prompt/skills/voice-prompt/SKILL.md` at line 40,
scripts/install-skills.mjs에서 버전 없이 호출하는 npx skills 의존성을 특정 skills@<version>으로
고정하세요. 해당 설치 동작을 설명하는 SKILL.md의 문구도 고정 버전 사용을 반영하도록 수정하고, 기존 설치 경로는 유지하세요.
Source: Linters/SAST tools
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 79a27f2900
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| against injection. `set --` plus `"$@"` keeps the stems as data instead of code. | ||
|
|
||
| ```bash | ||
| set -- loader # the candidate list — one argv entry per stem |
There was a problem hiding this comment.
실제 후보 stem에 작은따옴표·백틱·$(...) 등이 포함되면 이 줄의 loader를 해당 값으로 치환해야 하는데, set --도 셸 파싱이 끝난 뒤 실행되므로 argv 분리를 보장하지 못합니다. 예를 들어 typed input에서 유래한 foo'; touch /tmp/pwn; #를 그대로 넣으면 여전히 명령이 실행됩니다. 기존 지적 이후 추가된 이 set -- 예시가 새 근거이며, stem을 stdin·환경 변수 같은 별도 데이터 채널로 넘기거나 파싱 전에 검증된 방식으로 escape해야 합니다.
AGENTS.md reference: AGENTS.md:L287-L287
Useful? React with 👍 / 👎.
| if [ -z "$PLUGIN_ROOT" ]; then | ||
| for h in "${HERMES_HOME:-$HOME/.hermes}/plugins/voice-prompt" \ | ||
| "${HERMES_HOME:-$HOME/.hermes}/skills/voice-prompt"; do | ||
| [ -d "$h" ] && { PLUGIN_ROOT="$h"; break; } | ||
| done |
There was a problem hiding this comment.
scripts/install-skills.mjs에서 project scope를 선택해 일반 사용자 저장소에 이 스킬만 설치한 경우, 번들은 프로젝트의 스킬 디렉터리에 있고 Claude 플러그인 root·Codex 플러그인 캐시·$HERMES_HOME 어느 곳에도 없습니다. 이 분기는 홈 기반 Hermes 경로만 추가했으므로 첫 프로필 저장 시 bundled template not found로 중단됩니다. 기존 Hermes 경로 지적 이후에도 project-scope 경로가 빠져 있다는 것이 새 근거이므로, .agents/skills/voice-prompt 등 실제 프로젝트 설치 위치에서 템플릿을 찾는 분기를 추가해야 합니다.
AGENTS.md reference: AGENTS.md:L242-L244
Useful? React with 👍 / 👎.
| PLUGIN_ROOT=$(ls -1d "$cache_root"/*/voice-prompt/* 2>/dev/null \ | ||
| | awk -F/ '{print $NF "\t" $0}' | sort -V | tail -1 | cut -f2-) | ||
| else | ||
| PLUGIN_ROOT=$(ls -1d "$cache_root"/*/voice-prompt/* 2>/dev/null \ | ||
| | awk -F/ '{print $NF "\t" $0}' | sort -t. -k1,1n -k2,2n -k3,3n | tail -1 | cut -f2-) |
There was a problem hiding this comment.
여러 Codex 캐시 중 가장 높은 버전 디렉터리가 불완전하고 그 아래 버전에 정상 템플릿이 있는 경우, 여기서는 불완전한 디렉터리 하나를 먼저 확정한 뒤 284–286행에서 실패하므로 유효한 후보로 폴백하지 못합니다. 기존 캐시 선택 지적 이후에도 템플릿 존재 검사가 단일 후보 선택 뒤에만 남아 있다는 것이 새 근거입니다. 각 버전 후보에서 실제 templates/speech-profile.md 존재 여부를 확인하면서 버전 역순으로 첫 유효 후보를 선택해야 합니다.
AGENTS.md reference: AGENTS.md:L243-L243
Useful? React with 👍 / 👎.
Codex P1 1건 + P2 2건 적용. CR 2건은 각각 오탐·범위밖으로 판정했다 (아래). stem 주입 (Codex P1) — iter 2 의 `set --` 은 기계적 보장이 아니다. `set --` 자체도 셸 파싱이 끝난 뒤 실행되므로, 후보 문자열에 작은따옴표나 `$(...)` 가 섞이면 그 줄을 치환하는 순간 여전히 탈출한다. quoted heredoc(`<<'STEMS'`) 에 후보를 쓰고 `grep -iF -f` 로 읽는 형태로 바꿨다 — heredoc 내부는 셸이 파싱하지 않으므로 리터럴 보장이 있다. 주입 3종 (작은따옴표·$()·백틱) 실행으로 무력화 확인. 부수 효과로 코드가 줄었다. `grep -f` 가 모든 stem 을 한 번에 union 하므로 후보 루프, 누적 변수, 루프 밖 정렬이 전부 사라지고 iter 3 의 pipefail 문제도 구조적으로 소멸한다. 남은 파이프라인은 `printf | grep` 하나뿐이고 grep 이 마지막이라 rc 가 그대로 읽힌다. resolver 를 템플릿 존재 기준으로 (Codex P2 2건) — 기존 구조는 루트를 하나 확정한 뒤 템플릿을 검사해서, 최고 버전 캐시 디렉터리가 불완전하면 하위 정상 버전으로 폴백하지 못하고 중단했다. 후보 목록을 버전 역순으로 만들고 각 후보에서 템플릿 존재를 확인해 첫 유효 후보를 고르는 형태로 바꿨다. project-scope skill-unit 설치 후보(.agents/skills, .claude/skills)도 같은 목록에 넣었다 — 이 레이아웃은 `npx skills` 소관이라 검증된 계약이 아니므로 추정임을 본문에 명시했고, -f 검사가 판정하니 틀린 추정은 무해하다. 픽스처 검증: 0.10.0 이 불완전할 때 0.9.0 을 선택, project-scope 후보 해소, 전부 없으면 중단. CR 지적 2건 미적용: - plugins/voice-prompt/CLAUDE.md 를 @AGENTS.md 포인터로 축약하라 — 오탐. 그 규칙은 루트 CLAUDE.md 한정이고(AGENTS.md:3/99/125, dual-integration.md:11), AGENTS.md:289 의 Do-not-flag 이 바로 이 오탐 부류를 명시한다. AGENTS.md:9/132 는 플러그인 CLAUDE.md 를 내용 파일로 취급하며, 형제 플러그인 8개 이상이 내용을 갖는다 (project-init 163행 등). - scripts/install-skills.mjs 의 npx skills 버전 핀 — 실재하는 공급망 우려지만 이 PR 이 건드리지 않는 파일이고 마지막 변경이 커밋 2644165(PR #139)로 이 브랜치보다 앞선다. 여기서 고치면 drive-by 라 surgical-diff 위반이다. 별도 이슈 대상으로 deferred. 가드 6종 + bash 문법 + frontmatter(738자) 통과. SKILL.md 314행 (상한 500).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e348e1a4eb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| ```bash | ||
| stems=$(mktemp) || { echo "voice-prompt: mktemp failed" >&2; exit 1; } | ||
| cat > "$stems" <<'STEMS' |
There was a problem hiding this comment.
사용자가 typed input의 stem에 단독 행 STEMS와 뒤따르는 셸 텍스트를 포함시키는 경우, quoted heredoc도 그 행에서 종료되어 이후 내용이 명령으로 실행됩니다. 이전 코멘트 뒤 새로 추가된 이 heredoc이 새 근거이며, <<'STEMS'는 본문 확장만 막을 뿐 delimiter 충돌은 막지 못합니다. stem을 셸 명령 소스에 넣지 않는 별도 데이터 채널로 전달하거나 개행을 거부하고 충돌 불가능한 delimiter를 생성하세요.
AGENTS.md reference: AGENTS.md:L287-L287
Useful? React with 👍 / 👎.
PR #193 (voice-prompt 플러그인) 머지 후 통합. 리뷰 5회에서 나온 것 중 기존 홈이 있는 것은 그 라인을 고쳤고, 위키에 이미 있던 것은 다시 적지 않았다. code_review.md: - P0 shell injection 을 quote 유무가 아니라 "값이 셸에 코드로 도달하는가" 축으로 다시 썼다. 값이 명령문 텍스트로 치환되면 set -- 도 grep -F 도 방어가 아니다 — 둘 다 셸이 그 줄을 파싱한 뒤에 동작한다. 데이터 채널(변수 바인딩, quoted heredoc + grep -f)만이 기계적 보장이다. - P1 종료 상태 항목에 pipefail 이 만능이 아닌 경우를 붙였다. 생산자가 루프인 for … done | sort 에서 pipefail 은 반대로 틀린다 — 루프 본문의 마지막 명령이 no-match 로 거짓이 되는 정상 케이스를 실패로 보고한다. 정렬을 루프 밖으로 빼는 것이 맞는 수정이다. - Do-not-flag 의 CLAUDE.md 항목에 역방향 오탐을 명시했다. 포인터 규칙은 루트 한정이라 plugins/<name>/CLAUDE.md 가 내용을 갖는 것은 정상이다. AGENTS.md: 같은 Do-not-flag 역방향을 하드 최소본에 한 절로 반영. .llmwiki: - worktree-squash-merge-gotchas 에 gotcha 5 추가. .claude/state/ 와 .llmwiki/.staging/ 는 gitignore 라서 워크트리별로 존재하는데, post-merge Step 1 은 본 저장소만 허용하고 Step 1.5 는 cr-fix 기록을 cwd 기준으로 읽는다. 두 규칙이 서로를 배제하므로 정상 흐름에서 leftover 체크포인트가 실재하는 deferred 를 두고 "none" 을 출력한다. 이번 실행에서 양쪽 디렉터리로 스니펫을 돌려 확인했다. - hermes-plugin-adapter 의 skill_view 한정자 주장을 adapter 범위로 좁혔다. <plugin>: 접두사는 생성된 __init__.py 가 만들므로 HERMES_ELIGIBLE 에만 존재하고, 그 밖의 플러그인은 skill-unit 설치가 유일 경로라 frontmatter name 그대로 등록된다. install-skills.mjs 로 확인. - 이미 위키에 있던 것은 기록하지 않았다: 캐시 후보를 버전 basename 으로 고르는 것과 후보별 산출물 존재 확인은 dual-surface-command-skill-pattern 의 resolver-robustness 규칙과 detector-cannot-look Mode 9 가 이미 담고 있다. 리뷰어가 잡은 것이 위키가 이미 아는 것이었다는 뜻이므로, 새 lore 가 아니라 query-wiki 게이트를 건너뛴 절차 문제로 로그에 남겼다.
* docs: CLAUDE.md.global 344줄 → 130줄로 축약 같은 규칙이 세 층에 중복돼 있었다 — 기본 시스템 프롬프트, 도구 description, 그리고 core-config 의 prompt_inject.sh 가 매 프롬프트에 넣는 규율 블록. 중복 자체보다 층마다 강도가 달라진 것이 문제였다 (AskUserQuestion 이 "항상" / "구현에 영향 줄 때만" / "사용자만이 내릴 결정에 막혔을 때만" 세 버전). - why-first 를 독립 리드 문단 8개에서 각 규칙 bullet 안의 "이유 — 그러므로 지시" 한 문장으로 흡수. 근거가 규칙 본문보다 길어지는 상태를 해소한다. - Part 5 (Python/Notebook) 와 Part 6 (Skill/Plan Mode) 삭제. 후자의 다수는 Skill·AskUserQuestion 도구 description 이 이미 같은 말을 한다. - Part 4 를 121줄에서 46줄로. 표 3개를 산문으로, Serena 호출 시퀀스와 MCP 서버 목록을 capability 표현으로 축약. 검색 티어는 플러그인 지시를 덮어쓰는 역할이라 전역에 유지. - 헤더 자기참조 수정. 소스 파일이 "this file is a copy of CLAUDE.md.global" 이라고 자기 자신의 사본임을 선언하고 있었다. 사본과 소스가 바이트 동일하므로 양쪽에서 참인 문장으로 교체. attribution·emoji·run-and-verify·AskUserQuestion 4항목은 유지한다. 후속 변경에서 core-config 가 제거되면 이 파일이 그 유일 사본이 된다. * docs: CLAUDE.md.global 을 규칙 5개로 축약 130줄 판본도 여전히 기본 시스템 프롬프트·도구 description 과 겹치는 부분이 많았다. 남길 기준을 "어느 층에도 없는 것"으로 다시 잡고 5개로 줄였다 — verify-or-say-unknown, 문서 가독성, 모호함은 AskUserQuestion, 파일 2개 이상 또는 동작 변경은 github-dev 파이프라인, attribution·이모지 금지. github-dev 규칙의 발동 기준을 "파일 2개 이상 또는 동작 변경"으로 명시했다. "웬만한 작업"은 오타 수정에도 이슈를 만들게 되어 기준이 되지 못한다. 의도적으로 포기한 것 — 어느 층에도 대체본이 없으므로 되살리려면 이 파일에 다시 넣어야 한다: - 검색 티어 순서 (brightdata > exa > WebSearch > scrape). 전역 목록이 플러그인의 "내 도구 우선" 문구를 덮어쓰던 장치가 함께 사라진다. - 코드 탐색 우선순위 (Serena MCP > 내장 LSP > Explore agent > Grep/Read). - detached 실행 (setsid nohup + ps -o pid,sid 로 PID == SID 확인). - codex / agy 위임과 자동 적용 금지. * fix: 규칙 4 의 파이프라인 요구를 capability 로 게이트하고 버전 범프 예외를 정정 리뷰 iter 1 에서 두 지적을 적용했다. Codex P1 — 전역 파일은 모든 프로젝트에서 로드되는데 github-dev 는 plugins/github-dev/CLAUDE.md:109-110 에서 설치·인증된 gh 와 권한 있는 GitHub 저장소를 요구한다. 원격이 없거나 gh 가 미인증인 저장소에서는 파일 2개 이상 변경이 실행 불가한 파이프라인을 요구받아 작업이 막힌다. capability 조건을 붙이고, 파이프라인을 쓸 수 없으면 직접 커밋하도록 명시했다. CodeRabbit Major — "a version bump" 예외가 파일 수 제약 없이 적혀 있어 같은 규칙의 2파일 기준과 모순됐다. AGENTS.md:107 은 플러그인 버전 범프에 plugin.json 과 marketplace.json 동시 수정을 요구하므로 버전 범프는 항상 2파일 이상이다. 예외를 파일 수가 아니라 변경의 성질로 규정했다 — 기계적 변경(오타, 매니페스트를 함께 고치는 버전 범프, 단일 파일 문서 수정)은 파일 수와 무관하게 예외. .llmwiki/wiki/llm-wiki-design/deleted-subject-not-stale.md:23 지적은 skip 했다. 그 파일은 a6fe64a 소속이고 이미 origin/main 에 머지돼 있으며, GitHub 의 stale diff 캐시 때문에만 이 PR 에 나타난다. 고치면 PR 범위를 벗어난다. * fix: 규칙 4 를 플러그인 이름이 아니라 흐름으로 규정 Codex P1 iter 2 — "runs through github-dev" 의 엄격한 해석에 합법적 진입점이 없었다. resolve-issue 와 decompose-issue 는 둘 다 frontmatter 에 "Use ONLY when the user explicitly types ... Do NOT auto-fire" 하드 게이트를 갖고 있어, 사용자가 이슈 번호를 대지 않은 일반 요청("이 기능 추가해줘")에서는 어느 스킬도 발화할 수 없다. 그러면 에이전트는 규칙을 우회하거나 스킬의 안전 게이트를 위반하는 두 선택만 남는다. 플러그인 이름 대신 흐름을 규정했다 — 이슈·브랜치·자동 리뷰가 붙은 PR. 이슈와 PR 을 직접 여는 것으로 충족되고, github-dev 스킬은 그 흐름을 운전하는 한 가지 방법일 뿐이다. 이번 세션의 실제 진행 방식과도 일치한다 (gh issue create + gh pr create + cr-fix). Codex 가 제안한 두 해법은 채택하지 않았다. 범용 진입점 스킬 신설은 문구 문제에 스킬을 만드는 over-engineering 이고, 규칙을 opt-in 으로 축소하는 것은 이 규칙을 기본값으로 두려는 사용자 의도를 뒤집는다. * fix: AskUserQuestion 이 노출되지 않는 환경의 폴백을 복원 Codex P2 iter 3 — 130줄 판본(71af5e6:31)에 있던 "Where that tool is unavailable, ask one short direct question instead" 절이 5개 규칙으로 줄이는 과정에서 떨어졌다. headless 나 비대화형 컨텍스트에서 AskUserQuestion 이 노출되지 않으면, 모호함을 만난 실행이 쓸 수 없는 도구를 요구받아 멈춘다. AGENTS.md:99 가 이미 같은 capability-aware 패턴을 Codex request_user_input 에 대해 문서화하고 있다. 있던 절을 되돌리는 것이므로 복잡도 추가가 아니다. * docs: 플러그인 표를 이름·분류만 남기고 dual-integration 을 paths: 로 스코프 플러그인 설명을 AGENTS.md 표와 marketplace.json 두 곳에 두면 손으로 맞춰야 하는데 check-doc-consistency 는 이름 집합만 검사하므로 설명 drift 가 조용히 남는다. 표에서 설명을 걷어내고 marketplace.json 을 읽는 jq 한 줄을 가리키게 했다. dual-integration.md 에 paths: frontmatter 를 붙여 짝 표면(AGENTS.md, .claude/rules, 플러그인 매니페스트·훅·스킬 본문, 생성기)을 만질 때만 로드되게 했다. AGENTS.md 의 포인터도 @import 에서 백틱 참조로 바꿨다 — @import 는 실행 시점에 확장되어 paths: 스코핑을 무력화한다. 항상 로드되는 요약은 "멀티런타임 통합" 절이고, 그것이 Codex 와 Hermes 가 읽는 유일한 사본이다. CLAUDE.md.global / .ko 쌍의 SoT 방향(영어가 정본)과 심볼릭 링크를 걸지 않는 이유를 저장소 구조 절에 기록. * docs: 워크트리별 gitignore 상태와 skill_view 한정자 범위를 기록 PR #193 (voice-prompt 플러그인) 머지 후 통합. 리뷰 5회에서 나온 것 중 기존 홈이 있는 것은 그 라인을 고쳤고, 위키에 이미 있던 것은 다시 적지 않았다. code_review.md: - P0 shell injection 을 quote 유무가 아니라 "값이 셸에 코드로 도달하는가" 축으로 다시 썼다. 값이 명령문 텍스트로 치환되면 set -- 도 grep -F 도 방어가 아니다 — 둘 다 셸이 그 줄을 파싱한 뒤에 동작한다. 데이터 채널(변수 바인딩, quoted heredoc + grep -f)만이 기계적 보장이다. - P1 종료 상태 항목에 pipefail 이 만능이 아닌 경우를 붙였다. 생산자가 루프인 for … done | sort 에서 pipefail 은 반대로 틀린다 — 루프 본문의 마지막 명령이 no-match 로 거짓이 되는 정상 케이스를 실패로 보고한다. 정렬을 루프 밖으로 빼는 것이 맞는 수정이다. - Do-not-flag 의 CLAUDE.md 항목에 역방향 오탐을 명시했다. 포인터 규칙은 루트 한정이라 plugins/<name>/CLAUDE.md 가 내용을 갖는 것은 정상이다. AGENTS.md: 같은 Do-not-flag 역방향을 하드 최소본에 한 절로 반영. .llmwiki: - worktree-squash-merge-gotchas 에 gotcha 5 추가. .claude/state/ 와 .llmwiki/.staging/ 는 gitignore 라서 워크트리별로 존재하는데, post-merge Step 1 은 본 저장소만 허용하고 Step 1.5 는 cr-fix 기록을 cwd 기준으로 읽는다. 두 규칙이 서로를 배제하므로 정상 흐름에서 leftover 체크포인트가 실재하는 deferred 를 두고 "none" 을 출력한다. 이번 실행에서 양쪽 디렉터리로 스니펫을 돌려 확인했다. - hermes-plugin-adapter 의 skill_view 한정자 주장을 adapter 범위로 좁혔다. <plugin>: 접두사는 생성된 __init__.py 가 만들므로 HERMES_ELIGIBLE 에만 존재하고, 그 밖의 플러그인은 skill-unit 설치가 유일 경로라 frontmatter name 그대로 등록된다. install-skills.mjs 로 확인. - 이미 위키에 있던 것은 기록하지 않았다: 캐시 후보를 버전 basename 으로 고르는 것과 후보별 산출물 존재 확인은 dual-surface-command-skill-pattern 의 resolver-robustness 규칙과 detector-cannot-look Mode 9 가 이미 담고 있다. 리뷰어가 잡은 것이 위키가 이미 아는 것이었다는 뜻이므로, 새 lore 가 아니라 query-wiki 게이트를 건너뛴 절차 문제로 로그에 남겼다. * docs: 전역 지침 5개 규칙에 실행 가능한 하위 불릿과 문장 패턴 표를 추가 규칙 5개 구조는 유지하고 각 규칙에 실행 가능한 하위 불릿을 붙였다. 축약판은 규칙의 근거는 담았지만 "그래서 무엇을 하라"가 빠져 있어 판단마다 재해석이 필요했다. 이번 회차는 ko -> en 역방향 sync 다. 사용자가 .ko 를 먼저 재작성했고, 그 내용을 영어 정본에 반영했다. 영어가 SoT 라는 규약 자체는 그대로다. 신설된 요구 두 가지: - 규칙 4: 동작 변경은 실패 테스트나 재현 절차를 먼저 만들고 수정 후 통과를 확인한다. 실행 가능한 테스트가 없으면 사유와 대체 검증을 PR 에 기록한다. 조건도 "gh 인증됨" 에서 "gh auth status 성공"으로 검증 가능한 형태가 됐다. - 규칙 5: 산문에 em dash 를 쓰지 않는다. 범위는 새로 쓰거나 다시 쓰는 산문으로 한정했다. 저장소 기존 문서는 em dash 를 다수 쓰고 있어 무조건 금지로 두면 편집할 때마다 충돌한다. 규칙 2의 문장 패턴 표는 인용 예문만 한국어로 남겼다. 지침 산문은 세 런타임이 한 언어로 읽어야 하지만, 예문을 영어로 번역하면 그 문장이 잡아내려는 한국어 패턴 자체가 사라진다 (AGENTS.md 의 domain-content 예외). 전역 파일 헤더의 em dash 를 제거해 규칙 5가 자기 문서를 위반하지 않게 했다. * docs: 전역 지침에서 래퍼 제목과 sync 안내를 걷어내고 헤딩을 한 단계 올린다 모든 세션에 로드되는 파일이라 규칙이 아닌 줄은 전부 비용이다. "Working principles" 래퍼는 파일 전체가 이미 그것이라 정보가 없었고, sync 안내 한 줄은 이 저장소 사정을 모든 프로젝트 세션에 실어 나르고 있었다. 그 안내는 AGENTS.md 저장소 구조 절에 이미 있다. 래퍼를 걷어내면서 규칙 헤딩을 h3 에서 h2 로, 표 제목을 h4 에서 h3 으로 올렸다. .ko 는 없던 h1 을 얻어 영어 정본과 구조가 같아졌다. AGENTS.md 는 전역 파일이 경고 한 줄을 유지한다고 적고 있었다. 그 줄이 사라졌으므로 문장을 사실에 맞췄다.
Closes #192
무엇을 추가하나
한국어 보이스 모드가 넘겨준 음성 인식 결과를 실행 전에 명령으로 되돌리는 플러그인
voice-prompt./voice-prompt를 한 번 타이핑하면 해제할 때까지 모든 입력에 적용된다(자동 감지 없음).
핵심은 텍스트 청소가 아니다
말버릇 제거는 모델이 이미 하는 일이라 값이 낮다. 실제 값은 추측으로 넘어가던 자리에 확인
절차를 끼워넣는 것이다. "로더 파일" 을 들으면 모델은 파일명을 지어내는데,
loader.py가실재하는지 찾아보지 않으면 알 수 없다. 독해 실패가 아니라 행동 누락이고, 그것만은 지침으로
고칠 수 있다.
그래서 실제로 추가되는 네 가지는 전부 변환이 아니라 결정이다.
3단 분류
plaud-note-taking의 4-state([확인됨]/[정정]/[해석]/[확인 필요])는 문서에 표시를붙일 표면이 있어서 성립하는 구조다. 실행형 스킬에는 그 표면이 없으므로 셋으로 줄어든다.
세 번째가 안전장치다. 다른 오류는 실패가 시끄럽다 — 존재하지 않는 경로는 즉시 에러가 난다.
그런데
PR 189가PR 180으로 들리면 그럴듯하게 성공한다. 실재하는 PR 이므로 아무것도에러나지 않고, 작업만 엉뚱한 곳에 들어간다. 그래서 숫자는 문맥이 아무리 강해도 추측 대상이
아니다.
말버릇은 목록이 아니라 기능 검사로 지운다
정지 단어 목록으로 구현하면 반드시 깨진다.
자기수정 표지("아 아니", "그거 말고")는 삭제 대상이 아니라 정정 근거다. 그 뒤의 발화가
이긴다 —
foo.py 고쳐, 아 아니 bar.py→bar.py 고쳐.검증 (실행함)
가드 6종 + pre-commit 전체 스위트 통과.
저장소 대조 명령을 실제로 돌려 세 가지를 확인했다.
grep -iF의 고정 문자열 매칭이 실효가 있다.check-doc.mjs로 검색하면 0건이 나온다 —.가 와일드카드로 먹으면check-doc-consistency.mjs를 잘못 잡았을 것이다. 음성에서 온문자열에 정규식 메타문자가 섞여도 검색이 변형되지 않는다.
후보가 3건 나온다 (
check-shell-portability,check-skill-tool-portability, 그 테스트).실제로 모호한 발화이므로 질문하는 게 정답이고, 질문이 "무슨 파일이요?" 에서 "이 셋 중
어느 것?" 으로 바뀐다.
|| true로 삼키면 그 신호가사라지므로 (P1 "종료 상태가 사라지는 자리"), 본문에 해석 규칙을 명시했다.
동작 기제 — 리뷰 시 봐야 할 전제
이것은 전처리 필터가 아니다. 스킬 본문은 컨텍스트에 적재될 뿐이고 발화는 원문 그대로
도착한다.
UserPromptSubmit훅 출력도additionalContext로 덧붙지 발화를 치환하지 않는다(
plugins/core-config/hooks/prompt_inject.sh4-7행 계약, 111-113행 출력부로 확인). 즉 이저장소에 발화 텍스트를 바꿔치기하는 표면은 없다.
따라서 보장 수준은 다른 스킬과 같은 지침 준수 수준이며 긴 세션에서 흐려질 수 있다. 훅
재주입으로 강제하는 대안은 마커 상태 파일 + 훅 스크립트 + Codex 대응 디스크립터를 끌고
오므로 채택하지 않았다. 대신 에코 라인이 스킬이 살아있는지 보여주는 관측 지점이고, 에코가
사라지면 재호출하라는 지침을 본문과 플러그인
CLAUDE.md에 명시했다.plaud-note-taking 과의 경계
둘 다 STT 결과를 정정하고 오류 분포도 같지만 규율이 반대다. 회의 전사록은 손대면 안 되는
증거라 말버릇까지 보존하고, 보이스 입력은 실행되고 버려지는 명령이라 말버릇을 지운다. 용어
사전도 겹치지 않는다 — 한쪽은 회의 참석자와 회사명, 다른 쪽은 한 화자의 발음 습관이다.
모드 플래그 하나로 합치지 않은 이유는, 두 규율을 담은 본문은 모든 규칙에 조건 분기가 붙기
때문이다.
개인화 vs 오버피팅
사용자가 말한 말버릇(어쨌든·어찌됐든·하여튼·그냥·뭐)은 개인 특징이 아니라 일반 한국어 담화
표지라서 번들 레퍼런스에 넣었다. 그래야 첫 실행부터 누구에게나 동작한다.
.claude/voice-prompt/speech-profile.md는 진짜로 일반화되지 않는 것만 담는다 — 한 화자의반복되는 오발음, 저장소 조회로 해소 안 되는 도메인 용어. 사용자 확인 없이 쓰지 않는다.
변경 파일 (14개)
신규 6개 —
plugins/voice-prompt/(plugin.json, CLAUDE.md, SKILL.md, references 2개,templates 1개) + 스펙
.claude/spec/2026-07-30-voice-prompt.md+ 생성물.codex-plugin/plugin.json.동기화 6곳 —
marketplace.json(항목 +metadata.version2.10.0),AGENTS.md(23→24,Content 표,
eligible 22개,# 22 entries),README.md(배지·모음 문구·요약표·상세 details·구조 트리·
22 / 24 플러그인·가드 산문 총계),.claude/settings.json,scripts/check-skill-tool-portability.mjsPILOTS,.agents/plugins/marketplace.json.Codex 대상 포함 — 순환도 없고
Agent도구 비의존이라 제외할 이유가 없다. Hermes 는 allowlist밖이므로 어댑터 없음.
범위 밖
가드가 이미 검사한다.
council이 테스트를 가진 이유는 본문에 아무도 실행하지 않는 외부명령 호출 계약이 박혀 있어서인데 여기엔 그런 것이 없다
UserPromptSubmit훅 — 위 "동작 기제" 참조tcrei-prompt와의 통합 — 그쪽은 재사용 프롬프트를 파일로 구조화하고 이쪽은 라이브 발화를즉시 실행한다
Summary by CodeRabbit
voice-promptplugin for Korean voice-mode STT normalization (filler cleanup, orthography/language switching) with safer repo-aware identifier handling and confirmation gating for irreversible actions./voice-prompt:voice-prompt.