[Chore] AI 에이전트 설정 보완#3
Conversation
📝 WalkthroughWalkthroughClaude/Codex AI 에이전트용 규칙·스킬·훅·API 레퍼런스 체계를 새로 만들고, 이슈 기반 브랜치 자동 생성·PR 정규화·CI 워크플로우를 추가했다. 저장소 툴링으로는 아이콘/인덱스 생성 스크립트, husky pre-commit, tsconfig/vite/ESLint 설정, ChangesAI 에이전트 규칙·스킬·훅 체계
GitHub 워크플로우 자동화
프로젝트 툴링 및 스캐폴딩
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
CI 결과
|
There was a problem hiding this comment.
Actionable comments posted: 13
🤖 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/hooks/guard-rules.ps1:
- Around line 6-23: The guard logic in guard-rules.ps1 is over-matching because
it scans the entire CLAUDE_TOOL_INPUT, so harmless documentation or test edits
that mention banned terms are blocked. Update the checks around the existing
console.log, any, npm/yarn, and tailwind.config rules to evaluate the actual
target file path and the change content separately, and scope the Tailwind rule
in particular to only block creation of a tailwind.config file rather than any
mention of that string.
In @.claude/hooks/skill-suggest.ps1:
- Around line 13-15: The `/create-pr` suggestion trigger in `skill-suggest.ps1`
is too broad because the `-match 'pr|pull request'` pattern also matches common
substrings in unrelated words. Tighten the condition in the prompt-matching
logic so only standalone `pr` or `pull request` phrases are detected, using word
boundaries in the existing `if ($prompt -match ...)` check to keep
`suggestions.Add('/create-pr - create a PR title and body draft')` from firing
on irrelevant prompts.
In @.claude/rules/project-constraints.md:
- Line 33: The UI library priority text is using the wrong package label, so
update the guidance in the project-constraints rule to use the repository’s
standard symbol kusitms.com/ui instead of `@kusitms.com/ui`. Keep the same
priority order in that rule, and make sure the wording around the component
selection hierarchy matches the rest of the project’s conventions.
In @.claude/skills/code-review/SKILL.md:
- Around line 26-33: The review flow in SKILL.md only checks `git diff` and `git
diff --staged`, so untracked new files are skipped and can be missed during
review. Update the review procedure around the staged/unstaged diff steps to
also detect and include newly added files, using the existing diff collection
flow and the relevant shell commands listed there, so that untracked files are
part of the review output.
In @.github/workflows/auto-create-branch.yml:
- Around line 104-113: The Push issue branch step is directly interpolating
issue-derived input into shell commands, which allows command injection through
BRANCH_NAME. Update the workflow in the Push issue branch job to pass
steps.extract.outputs.branch_name via env instead of inline substitution,
validate it with a branch-ref check such as git check-ref-format --branch before
using it, and only then use it in git checkout/git push.
In @.github/workflows/ci.yml:
- Around line 15-18: The workflow permissions are too broad for the main ci job,
exposing write access while running untrusted PR code through pnpm install,
vitest, and build. Update the permissions on the ci workflow so the job that
runs tests and builds only has read-only access, and move pull-requests: write
and issues: write to the separate comment job or any step that actually needs to
post comments. Use the ci job and the comment job identifiers in
.github/workflows/ci.yml to keep the privileges split correctly.
In @.github/workflows/pr-auto-setup.yml:
- Around line 57-78: The branch slug parsing in the PR auto setup workflow can
throw on invalid percent-encoding, which stops the rest of the metadata sync.
Update the branch slug handling in the branchMatch block to safely decode
rawSlug by falling back to the original slug when decodeURIComponent fails, so
titleSource still gets set and the assignee/title/label flow can continue. Keep
the fix localized around the titleSource initialization and related branch
parsing logic.
In `@eslint.config.js`:
- Line 42: The global no-undef override in eslint.config.js is too broad and
hides typos in JavaScript-only files. Update the ESLint config so no-undef stays
enabled by default, and only disable it in the TypeScript-specific override
block if needed; use the existing config structure around the no-undef rule and
the file-pattern overrides to scope the exception correctly.
- Around line 49-55: The current ESLint config applies both browser and Node
globals to `src/**/*.{ts,tsx}` and `scripts/**/*.ts`, which can hide browser
runtime errors in app code. Split this `files` block in `eslint.config.js` into
separate config entries so `src` only gets `globals.browser` and `scripts` only
gets `globals.node`, keeping the relevant globals scoped to each runtime. Use
the existing `languageOptions.globals` setup as the place to separate the two
environments.
In `@scripts/build-icons.ts`:
- Around line 32-45: The component name generation in toComponentName can still
produce an invalid identifier when a SVG file name starts with a number, so
update this helper to guard against numeric leading characters after
toPascalCase. In scripts/build-icons.ts, keep the existing Invalid icon file
name handling, but before returning the final name ensure names like 24Arrow are
prefixed or otherwise normalized so the generated TSX component name is a valid
JavaScript/TypeScript identifier, including the Icon/Logo/Brand/Symbol/Wordmark
suffix path.
- Around line 56-66: readIconSources currently swallows every readdir failure
and returns an empty list, which can hide real filesystem or path regressions;
update this helper to only treat the expected missing-directory case as
recoverable and rethrow any other error. Use the existing readIconSources
function in scripts/build-icons.ts as the fix point, and keep the fallback to []
limited to the ENOENT case so icons:build and icons:check fail loudly on
unexpected directory access problems.
- Around line 47-54: `normalizeToCurrentColor`에서 `style` 속성을 통째로 제거하는 처리가 렌더링을
깨뜨리고 있으니, 색상 관련 값만 currentColor로 정규화하고 `stroke-width`, `opacity`, `fill-rule` 같은
비색상 스타일은 유지하도록 수정하세요. `scripts/build-icons.ts`의 `normalizeToCurrentColor` 로직을
중심으로, `fill`/`stroke` 치환은 유지하되 `style` 전체 삭제 대신 필요한 색상 선언만 선택적으로 변환하거나 보존하도록 바꾸면
됩니다.
In `@scripts/gen-index.ts`:
- Around line 18-39: The FS helpers readEntries, readFileOrEmpty, and
isDirectory are swallowing every error and turning real I/O failures into empty
results, which can produce an empty barrel and hide access/resource issues.
Update these helpers in gen-index so they only treat ENOENT as a benign
missing-path case, while rethrowing other errors from readdir, readFile, and
stat; keep the existing helper names and call sites but make the fallback
behavior error-specific rather than blanket empty values.
🪄 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
Run ID: ffe9ac38-7470-49c5-8260-90bbc2ef728c
⛔ Files ignored due to path filters (2)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml,!pnpm-lock.yamlsrc/assets/icons/generated/.gitkeepis excluded by!**/generated/**
📒 Files selected for processing (82)
.claude/decisions/README.md.claude/decisions/template.md.claude/hooks/guard-rules.ps1.claude/hooks/lint-check.ps1.claude/hooks/skill-suggest.ps1.claude/manifest.md.claude/references/api/admin-auth.md.claude/references/api/admin-blog-reviews.md.claude/references/api/admin-introductions.md.claude/references/api/admin-members.md.claude/references/api/admin-projects.md.claude/references/api/admin-reviews.md.claude/references/api/admin.md.claude/references/component-patterns.md.claude/references/domain/blog-reviews.md.claude/references/domain/introductions.md.claude/references/domain/members.md.claude/references/domain/projects.md.claude/references/domain/reviews.md.claude/rules/architecture.md.claude/rules/code-quality.md.claude/rules/code-style.md.claude/rules/commit-convention.md.claude/rules/component-guide.md.claude/rules/data-fetching.md.claude/rules/generated-workflows.md.claude/rules/project-constraints.md.claude/rules/testing.md.claude/rules/verification.md.claude/rules/workflow.md.claude/settings.json.claude/skills/code-review/SKILL.md.claude/skills/commit-kr/SKILL.md.claude/skills/create-pr/SKILL.md.claude/skills/create-pr/templates/ai-automation-pr.md.claude/skills/figma-to-component/SKILL.md.claude/skills/gen-test/SKILL.md.claude/skills/refactor/SKILL.md.claude/skills/scaffold-api/SKILL.md.coderabbit.yaml.codex/hooks.json.codex/skills/code-review/SKILL.md.codex/skills/commit-kr/SKILL.md.codex/skills/create-pr/SKILL.md.codex/skills/figma-to-component/SKILL.md.codex/skills/gen-test/SKILL.md.codex/skills/refactor/SKILL.md.codex/skills/scaffold-api/SKILL.md.github/ISSUE_TEMPLATE/bug_report.md.github/ISSUE_TEMPLATE/feature.md.github/ISSUE_TEMPLATE/issue-template.yml.github/PULL_REQUEST_TEMPLATE.md.github/workflows/auto-create-branch.yml.github/workflows/ci.yml.github/workflows/pr-auto-setup.yml.gitignore.husky/pre-commit.prettierignoreAGENTS.mdCLAUDE.mdREADME.mddocs/development.mdeslint.config.jspackage.jsonscripts/build-icons.tsscripts/gen-index.tssrc/App.csssrc/App.tsxsrc/api/.gitkeepsrc/assets/icons/svg-preserve/.gitkeepsrc/assets/icons/svg/.gitkeepsrc/components/.gitkeepsrc/components/index.tssrc/hooks/.gitkeepsrc/hooks/index.tssrc/index.csssrc/lib/utils.tssrc/pages/.gitkeeptsconfig.app.jsontsconfig.jsontsconfig.node.jsonvite.config.ts
💤 Files with no reviewable changes (1)
- .github/ISSUE_TEMPLATE/feature.md
| ## 의존성 | ||
|
|
||
| - production dependency 추가 전에는 확인을 받습니다. | ||
| - UI 라이브러리는 `@kusitms.com/ui` -> shadcn/ui -> Base UI 순으로 우선합니다. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
UI 라이브러리 표기를 맞춰 주세요.
@kusitms.com/ui는 이 저장소의 기준 표기와 다릅니다. kusitms.com/ui로 수정해야 에이전트가 같은 우선순위를 따릅니다. Based on learnings: 컴포넌트 선택 우선순위는 kusitms.com/ui -> shadcn/ui -> Base UI 순입니다.
🤖 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 @.claude/rules/project-constraints.md at line 33, The UI library priority
text is using the wrong package label, so update the guidance in the
project-constraints rule to use the repository’s standard symbol kusitms.com/ui
instead of `@kusitms.com/ui`. Keep the same priority order in that rule, and make
sure the wording around the component selection hierarchy matches the rest of
the project’s conventions.
Source: Learnings
| 인자가 없으면 현재 worktree의 staged + unstaged diff를 리뷰합니다. | ||
|
|
||
| ```bash | ||
| git diff --name-only | ||
| git diff --staged --name-only | ||
| git diff | ||
| git diff --staged | ||
| ``` |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
새 파일 누락을 막아야 합니다.
현재 절차는 git diff만 읽어서 미추적 파일이 빠집니다. 새로 추가된 파일은 리뷰에서 아예 제외되어 회귀를 놓칠 수 있습니다.
💡 제안
- 인자가 없으면 현재 worktree의 staged + unstaged diff를 리뷰합니다.
+ 인자가 없으면 현재 worktree의 staged + unstaged diff와 미추적 파일까지 리뷰합니다.🤖 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 @.claude/skills/code-review/SKILL.md around lines 26 - 33, The review flow in
SKILL.md only checks `git diff` and `git diff --staged`, so untracked new files
are skipped and can be missed during review. Update the review procedure around
the staged/unstaged diff steps to also detect and include newly added files,
using the existing diff collection flow and the relevant shell commands listed
there, so that untracked files are part of the review output.
| - name: Push issue branch | ||
| run: | | ||
| BRANCH_NAME="${{ steps.extract.outputs.branch_name }}" | ||
|
|
||
| git config user.name "github-actions[bot]" | ||
| git config user.email "github-actions[bot]@users.noreply.github.com" | ||
|
|
||
| git fetch origin main | ||
| git checkout -b "$BRANCH_NAME" origin/main | ||
| git push origin "$BRANCH_NAME" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
이슈 본문에서 온 값을 셸에 직접 주입하고 있습니다.
steps.extract.outputs.branch_name 는 이슈 본문에서 파생된 사용자 입력이라, 현재처럼 Line 106에서 직접 문자열 치환하면 "나 $()를 섞은 값으로 명령 주입이 가능합니다. env:로 전달하고 git check-ref-format --branch 같은 검증을 넣어야 합니다.
예시 수정
- name: Push issue branch
+ env:
+ BRANCH_NAME: ${{ steps.extract.outputs.branch_name }}
run: |
- BRANCH_NAME="${{ steps.extract.outputs.branch_name }}"
+ git check-ref-format --branch "$BRANCH_NAME"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]`@users.noreply.github.com`"
git fetch origin main
git checkout -b "$BRANCH_NAME" origin/main
git push origin "$BRANCH_NAME"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Push issue branch | |
| run: | | |
| BRANCH_NAME="${{ steps.extract.outputs.branch_name }}" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git fetch origin main | |
| git checkout -b "$BRANCH_NAME" origin/main | |
| git push origin "$BRANCH_NAME" | |
| - name: Push issue branch | |
| env: | |
| BRANCH_NAME: ${{ steps.extract.outputs.branch_name }} | |
| run: | | |
| git check-ref-format --branch "$BRANCH_NAME" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]`@users.noreply.github.com`" | |
| git fetch origin main | |
| git checkout -b "$BRANCH_NAME" origin/main | |
| git push origin "$BRANCH_NAME" |
🧰 Tools
🪛 zizmor (1.26.1)
[info] 106-106: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 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 @.github/workflows/auto-create-branch.yml around lines 104 - 113, The Push
issue branch step is directly interpolating issue-derived input into shell
commands, which allows command injection through BRANCH_NAME. Update the
workflow in the Push issue branch job to pass steps.extract.outputs.branch_name
via env instead of inline substitution, validate it with a branch-ref check such
as git check-ref-format --branch before using it, and only then use it in git
checkout/git push.
| files: ['src/**/*.{ts,tsx}', 'scripts/**/*.ts'], | ||
| extends: [...tseslint.configs.strictTypeChecked, ...tseslint.configs.stylisticTypeChecked], | ||
| languageOptions: { | ||
| globals: globals.browser, | ||
| globals: { | ||
| ...globals.browser, | ||
| ...globals.node, | ||
| }, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
src와 scripts에 같은 globals를 주면 브라우저 런타임 오류를 숨깁니다.
이 블록은 src/**/*.{ts,tsx}에도 globals.node를 허용합니다. 그 결과 process, Buffer, __dirname 같은 Node 전역을 앱 코드에서 써도 린트가 통과해서, 브라우저에서만 터지는 참조 오류를 놓치게 됩니다. src와 scripts를 별도 블록으로 나눠 browser/node globals를 분리하세요.
수정 예시
- {
- files: ['src/**/*.{ts,tsx}', 'scripts/**/*.ts'],
+ {
+ files: ['src/**/*.{ts,tsx}'],
extends: [...tseslint.configs.strictTypeChecked, ...tseslint.configs.stylisticTypeChecked],
languageOptions: {
globals: {
...globals.browser,
- ...globals.node,
},
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
+ rules: {
+ // ...
+ },
+ },
+ {
+ files: ['scripts/**/*.ts'],
+ extends: [...tseslint.configs.strictTypeChecked, ...tseslint.configs.stylisticTypeChecked],
+ languageOptions: {
+ globals: {
+ ...globals.node,
+ },
+ parserOptions: {
+ projectService: true,
+ tsconfigRootDir: import.meta.dirname,
+ },
+ },
rules: {
'`@typescript-eslint/no-explicit-any`': 'warn',
'`@typescript-eslint/no-empty-function`': 'off',🤖 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 `@eslint.config.js` around lines 49 - 55, The current ESLint config applies
both browser and Node globals to `src/**/*.{ts,tsx}` and `scripts/**/*.ts`,
which can hide browser runtime errors in app code. Split this `files` block in
`eslint.config.js` into separate config entries so `src` only gets
`globals.browser` and `scripts` only gets `globals.node`, keeping the relevant
globals scoped to each runtime. Use the existing `languageOptions.globals` setup
as the place to separate the two environments.
| const toComponentName = (fileName: string) => { | ||
| const baseName = basename(fileName, extname(fileName)) | ||
| const pascalName = toPascalCase(baseName) | ||
|
|
||
| if (!pascalName) { | ||
| throw new Error(`Invalid icon file name: ${fileName}`) | ||
| } | ||
|
|
||
| if (/(Icon|Logo|Brand|Symbol|Wordmark)$/.test(pascalName)) { | ||
| return pascalName | ||
| } | ||
|
|
||
| return `${pascalName}Icon` | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
숫자로 시작하는 SVG 파일명은 생성된 TSX를 문법 오류로 만듭니다.
24-arrow.svg 같은 이름은 toPascalCase 이후에도 숫자로 시작할 수 있고, 그 값이 그대로 componentName에 들어가면 유효하지 않은 식별자가 생성됩니다. 파일명 입력이 외부 SVG 자산에 의해 결정되는 만큼, 첫 글자가 숫자인 경우 접두사를 강제로 붙여야 합니다.
가능한 수정 예시
const toComponentName = (fileName: string) => {
const baseName = basename(fileName, extname(fileName))
const pascalName = toPascalCase(baseName)
if (!pascalName) {
throw new Error(`Invalid icon file name: ${fileName}`)
}
- if (/(Icon|Logo|Brand|Symbol|Wordmark)$/.test(pascalName)) {
- return pascalName
+ const normalizedName = /^[0-9]/.test(pascalName) ? `Icon${pascalName}` : pascalName
+
+ if (/(Icon|Logo|Brand|Symbol|Wordmark)$/.test(normalizedName)) {
+ return normalizedName
}
- return `${pascalName}Icon`
+ return `${normalizedName}Icon`
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const toComponentName = (fileName: string) => { | |
| const baseName = basename(fileName, extname(fileName)) | |
| const pascalName = toPascalCase(baseName) | |
| if (!pascalName) { | |
| throw new Error(`Invalid icon file name: ${fileName}`) | |
| } | |
| if (/(Icon|Logo|Brand|Symbol|Wordmark)$/.test(pascalName)) { | |
| return pascalName | |
| } | |
| return `${pascalName}Icon` | |
| } | |
| const toComponentName = (fileName: string) => { | |
| const baseName = basename(fileName, extname(fileName)) | |
| const pascalName = toPascalCase(baseName) | |
| if (!pascalName) { | |
| throw new Error(`Invalid icon file name: ${fileName}`) | |
| } | |
| const normalizedName = /^[0-9]/.test(pascalName) ? `Icon${pascalName}` : pascalName | |
| if (/(Icon|Logo|Brand|Symbol|Wordmark)$/.test(normalizedName)) { | |
| return normalizedName | |
| } | |
| return `${normalizedName}Icon` | |
| } |
🤖 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 `@scripts/build-icons.ts` around lines 32 - 45, The component name generation
in toComponentName can still produce an invalid identifier when a SVG file name
starts with a number, so update this helper to guard against numeric leading
characters after toPascalCase. In scripts/build-icons.ts, keep the existing
Invalid icon file name handling, but before returning the final name ensure
names like 24Arrow are prefixed or otherwise normalized so the generated TSX
component name is a valid JavaScript/TypeScript identifier, including the
Icon/Logo/Brand/Symbol/Wordmark suffix path.
| const normalizeToCurrentColor = (svg: string) => { | ||
| return svg | ||
| .replace( | ||
| /\s(fill|stroke)=["'](?!none|currentColor|transparent|url\()[^"']+["']/gi, | ||
| ' $1="currentColor"', | ||
| ) | ||
| .replace(/style=["'][^"']*["']/gi, '') | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
style 전체 제거는 아이콘 렌더링을 깨뜨릴 수 있습니다.
지금 구현은 색상 정규화와 함께 style 속성을 통째로 삭제해서 stroke-width, opacity, fill-rule 같은 비색상 선언도 잃어버립니다. svg 디렉터리의 아이콘도 currentColor 치환만 해야지, 스타일 전체를 지우면 생성된 TSX가 원본 SVG와 다른 모양이 됩니다.
가능한 수정 예시
const normalizeToCurrentColor = (svg: string) => {
return svg
.replace(
/\s(fill|stroke)=["'](?!none|currentColor|transparent|url\()[^"']+["']/gi,
' $1="currentColor"',
)
- .replace(/style=["'][^"']*["']/gi, '')
+ .replace(/style=(["'])(.*?)\1/gi, (_match, quote: string, styleValue: string) => {
+ const nextStyle = styleValue
+ .split(';')
+ .map((rule) => rule.trim())
+ .filter(Boolean)
+ .map((rule) =>
+ /^(fill|stroke)\s*:/i.test(rule) &&
+ !/^(fill|stroke)\s*:\s*(none|currentColor|transparent|url\()/i.test(rule)
+ ? rule.replace(/^(fill|stroke)\s*:\s*[^;]+/i, '$1: currentColor')
+ : rule,
+ )
+ .join('; ')
+
+ return nextStyle ? `style=${quote}${nextStyle}${quote}` : ''
+ })
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const normalizeToCurrentColor = (svg: string) => { | |
| return svg | |
| .replace( | |
| /\s(fill|stroke)=["'](?!none|currentColor|transparent|url\()[^"']+["']/gi, | |
| ' $1="currentColor"', | |
| ) | |
| .replace(/style=["'][^"']*["']/gi, '') | |
| } | |
| const normalizeToCurrentColor = (svg: string) => { | |
| return svg | |
| .replace( | |
| /\s(fill|stroke)=["'](?!none|currentColor|transparent|url\()[^"']+["']/gi, | |
| ' $1="currentColor"', | |
| ) | |
| .replace(/style=(["'])(.*?)\1/gi, (_match, quote: string, styleValue: string) => { | |
| const nextStyle = styleValue | |
| .split(';') | |
| .map((rule) => rule.trim()) | |
| .filter(Boolean) | |
| .map((rule) => | |
| /^(fill|stroke)\s*:/i.test(rule) && | |
| !/^(fill|stroke)\s*:\s*(none|currentColor|transparent|url\()/i.test(rule) | |
| ? rule.replace(/^(fill|stroke)\s*:\s*[^;]+/i, '$1: currentColor') | |
| : rule, | |
| ) | |
| .join('; ') | |
| return nextStyle ? `style=${quote}${nextStyle}${quote}` : '' | |
| }) | |
| } |
🤖 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 `@scripts/build-icons.ts` around lines 47 - 54, `normalizeToCurrentColor`에서
`style` 속성을 통째로 제거하는 처리가 렌더링을 깨뜨리고 있으니, 색상 관련 값만 currentColor로 정규화하고
`stroke-width`, `opacity`, `fill-rule` 같은 비색상 스타일은 유지하도록 수정하세요.
`scripts/build-icons.ts`의 `normalizeToCurrentColor` 로직을 중심으로, `fill`/`stroke`
치환은 유지하되 `style` 전체 삭제 대신 필요한 색상 선언만 선택적으로 변환하거나 보존하도록 바꾸면 됩니다.
| const readIconSources = async ( | ||
| sourceDir: string, | ||
| preserveColors: boolean, | ||
| ): Promise<IconSource[]> => { | ||
| let entries | ||
|
|
||
| try { | ||
| entries = await readdir(sourceDir, { withFileTypes: true }) | ||
| } catch { | ||
| return [] | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
소스 디렉터리 읽기 실패를 빈 목록으로 숨기면 잘못된 생성 결과를 정상으로 오인합니다.
이 PR 컨텍스트상 src/assets/icons/svg와 src/assets/icons/svg-preserve는 커밋된 디렉터리입니다. 그런데 여기서 모든 readdir 에러를 []로 바꾸면, 실제 파일시스템 오류나 경로 회귀가 발생해도 icons:build가 누락된 세트로 생성물을 덮어쓰고 icons:check도 통과시킬 수 있습니다. 최소한 ENOENT만 예외 처리하고 나머지는 다시 던져야 합니다.
가능한 수정 예시
try {
entries = await readdir(sourceDir, { withFileTypes: true })
- } catch {
+ } catch (error) {
+ if (
+ error &&
+ typeof error === 'object' &&
+ 'code' in error &&
+ error.code === 'ENOENT'
+ ) {
+ return []
+ }
+
+ throw error
+ }
- return []
- }
+ 📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const readIconSources = async ( | |
| sourceDir: string, | |
| preserveColors: boolean, | |
| ): Promise<IconSource[]> => { | |
| let entries | |
| try { | |
| entries = await readdir(sourceDir, { withFileTypes: true }) | |
| } catch { | |
| return [] | |
| } | |
| const readIconSources = async ( | |
| sourceDir: string, | |
| preserveColors: boolean, | |
| ): Promise<IconSource[]> => { | |
| let entries | |
| try { | |
| entries = await readdir(sourceDir, { withFileTypes: true }) | |
| } catch (error) { | |
| if ( | |
| error && | |
| typeof error === 'object' && | |
| 'code' in error && | |
| error.code === 'ENOENT' | |
| ) { | |
| return [] | |
| } | |
| throw error | |
| } |
🤖 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 `@scripts/build-icons.ts` around lines 56 - 66, readIconSources currently
swallows every readdir failure and returns an empty list, which can hide real
filesystem or path regressions; update this helper to only treat the expected
missing-directory case as recoverable and rethrow any other error. Use the
existing readIconSources function in scripts/build-icons.ts as the fix point,
and keep the fallback to [] limited to the ENOENT case so icons:build and
icons:check fail loudly on unexpected directory access problems.
| const readEntries = async (dir: string) => { | ||
| try { | ||
| return await readdir(dir, { withFileTypes: true }) | ||
| } catch { | ||
| return [] | ||
| } | ||
| } | ||
|
|
||
| const readFileOrEmpty = async (filePath: string) => { | ||
| try { | ||
| return await readFile(filePath, 'utf8') | ||
| } catch { | ||
| return '' | ||
| } | ||
| } | ||
|
|
||
| const isDirectory = async (dir: string) => { | ||
| try { | ||
| return (await stat(dir)).isDirectory() | ||
| } catch { | ||
| return false | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
모든 FS 예외를 빈 상태로 삼키면 정상 배럴을 비워 쓸 수 있습니다.
여기서는 readdir/readFile/stat의 모든 실패를 각각 [], '', false로 바꿉니다. 그래서 EACCES, EMFILE 같은 실제 I/O 장애도 “파일이 없음”으로 처리되고, write 모드에서는 비어 있는 index.ts를 생성해 기존 export surface를 깨뜨릴 수 있습니다. ENOENT만 없는 상태로 취급하고 나머지는 바로 실패시키는 편이 안전합니다.
수정 예시
+const isMissing = (error: unknown) => {
+ return (error as NodeJS.ErrnoException).code === 'ENOENT'
+}
+
const readEntries = async (dir: string) => {
try {
return await readdir(dir, { withFileTypes: true })
- } catch {
+ } catch (error) {
+ if (isMissing(error)) {
+ return []
+ }
+ throw error
+ }
- return []
- }
}
const readFileOrEmpty = async (filePath: string) => {
try {
return await readFile(filePath, 'utf8')
- } catch {
+ } catch (error) {
+ if (isMissing(error)) {
+ return ''
+ }
+ throw error
+ }
- return ''
- }
}
const isDirectory = async (dir: string) => {
try {
return (await stat(dir)).isDirectory()
- } catch {
+ } catch (error) {
+ if (isMissing(error)) {
+ return false
+ }
+ throw error
+ }
- return false
- }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const readEntries = async (dir: string) => { | |
| try { | |
| return await readdir(dir, { withFileTypes: true }) | |
| } catch { | |
| return [] | |
| } | |
| } | |
| const readFileOrEmpty = async (filePath: string) => { | |
| try { | |
| return await readFile(filePath, 'utf8') | |
| } catch { | |
| return '' | |
| } | |
| } | |
| const isDirectory = async (dir: string) => { | |
| try { | |
| return (await stat(dir)).isDirectory() | |
| } catch { | |
| return false | |
| } | |
| const isMissing = (error: unknown) => { | |
| return (error as NodeJS.ErrnoException).code === 'ENOENT' | |
| } | |
| const readEntries = async (dir: string) => { | |
| try { | |
| return await readdir(dir, { withFileTypes: true }) | |
| } catch (error) { | |
| if (isMissing(error)) { | |
| return [] | |
| } | |
| throw error | |
| } | |
| } | |
| const readFileOrEmpty = async (filePath: string) => { | |
| try { | |
| return await readFile(filePath, 'utf8') | |
| } catch (error) { | |
| if (isMissing(error)) { | |
| return '' | |
| } | |
| throw error | |
| } | |
| } | |
| const isDirectory = async (dir: string) => { | |
| try { | |
| return (await stat(dir)).isDirectory() | |
| } catch (error) { | |
| if (isMissing(error)) { | |
| return false | |
| } | |
| throw error | |
| } | |
| } |
🤖 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 `@scripts/gen-index.ts` around lines 18 - 39, The FS helpers readEntries,
readFileOrEmpty, and isDirectory are swallowing every error and turning real I/O
failures into empty results, which can produce an empty barrel and hide
access/resource issues. Update these helpers in gen-index so they only treat
ENOENT as a benign missing-path case, while rethrowing other errors from
readdir, readFile, and stat; keep the existing helper names and call sites but
make the fallback behavior error-specific rather than blanket empty values.
CI 결과
|
CI 결과
|
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 @.claude/hooks/guard-rules.ps1:
- Around line 1-3: The input env var contract in guard-rules.ps1 is mismatched:
the script only reads CLAUDE_TOOL_INPUT while the rest of the hook setup uses
TOOL_INPUT, so the guard can exit early with no input. Update the guard logic in
the script to read TOOL_INPUT with fallback to CLAUDE_TOOL_INPUT (or vice versa)
before the empty check, and keep the existing exit behavior only when both are
unavailable.
🪄 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
Run ID: f23bbb47-db97-40a5-bb33-ea95516020c6
📒 Files selected for processing (4)
.claude/hooks/guard-rules.ps1.claude/hooks/skill-suggest.ps1.github/workflows/auto-create-branch.yml.github/workflows/ci.yml
🚧 Files skipped from review as they are similar to previous changes (3)
- .claude/hooks/skill-suggest.ps1
- .github/workflows/ci.yml
- .github/workflows/auto-create-branch.yml
| $inputText = $env:CLAUDE_TOOL_INPUT | ||
| if (-not $inputText) { | ||
| exit 0 |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Hook env var usage =="
fd -a '.*\.ps1$' .claude/hooks | xargs -r rg -n '\$env:(TOOL_INPUT|CLAUDE_TOOL_INPUT)'
echo
echo "== Hook registrations =="
sed -n '1,80p' .claude/settings.json
echo
sed -n '1,80p' .codex/hooks.jsonRepository: kusitms-com/makers-admin-fe
Length of output: 2461
입력 payload env var 계약이 어긋납니다. guard-rules.ps1는 CLAUDE_TOOL_INPUT만 읽는데, 같은 훅 계열과 등록 설정은 TOOL_INPUT 기준입니다. 입력이 비면 바로 종료되어 guard가 무력화되므로 둘 다 fallback으로 받게 해주세요.
🤖 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 @.claude/hooks/guard-rules.ps1 around lines 1 - 3, The input env var contract
in guard-rules.ps1 is mismatched: the script only reads CLAUDE_TOOL_INPUT while
the rest of the hook setup uses TOOL_INPUT, so the guard can exit early with no
input. Update the guard logic in the script to read TOOL_INPUT with fallback to
CLAUDE_TOOL_INPUT (or vice versa) before the empty check, and keep the existing
exit behavior only when both are unavailable.
CI 결과
|
#️⃣연관된 이슈
🚧 Work in Progress
📌 주요 변경사항
📝작업 내용
CLAUDE.md를 Claude의 메인 진입점으로 두고,AGENTS.md는 Codex가CLAUDE.md와.claude/manifest.md를 참조하는 프록시 역할로 정리.claude/manifest.md에서 기본 rules와 작업별 reference/API/domain 문서를 나눠 읽도록 구성해 불필요한 토큰 사용과 임의 구조 생성을 줄임/create-pr,/scaffold-api,/figma-to-component,/gen-test,/code-review같은 반복 작업을 프로젝트 규칙에 맞게 실행하도록 구성console.log, 명시적any,npm/yarn, Tailwind v3 설정 생성 같은 프로젝트 금지 패턴을 차단하도록 구성components/hooksbarrel index 생성을 스크립트화하고 CI의Generated항목에서 동기화 여부를 확인하도록 구성pnpm format:check,pnpm gen:index:check,pnpm icons:check,pnpm type-check,pnpm lint,git diff --check통과📸 스크린샷 (선택)
💬리뷰 요구사항(선택)
Summary by CodeRabbit