ccperm 1.9.2 → 1.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +47 -53
- package/README.md +47 -53
- package/dist/aggregator.js +1 -1
- package/dist/interactive.js +2 -3
- package/dist/renderer.js +2 -3
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -4,84 +4,78 @@
|
|
|
4
4
|
|
|
5
5
|
[English](README.md)
|
|
6
6
|
|
|
7
|
-
Claude Code는 프로젝트마다 `.claude/settings*.json`에 허용한 권한(Bash 명령, WebFetch 도메인, MCP 도구 등)을 저장합니다. 여러 프로젝트를 오가다 보면 어디서 뭘 허용했는지 파악하기 어려운데, **ccperm
|
|
7
|
+
Claude Code는 프로젝트마다 `.claude/settings*.json`에 허용한 권한(Bash 명령, WebFetch 도메인, MCP 도구 등)을 저장합니다. 여러 프로젝트를 오가다 보면 어디서 뭘 허용했는지 파악하기 어려운데, **ccperm**은 홈 디렉토리 전체를 스캔해서 모든 설정 파일을 찾고, 인터랙티브 TUI 또는 텍스트로 보여줍니다.
|
|
8
8
|
|
|
9
9
|
## 빠른 시작
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx ccperm
|
|
12
|
+
npx ccperm
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
설치 없이 바로 실행됩니다. 글로벌 설치도 가능:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
npm i -g ccperm
|
|
19
|
-
ccperm
|
|
19
|
+
ccperm
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
npx ccperm # 현재 프로젝트 권한 점검
|
|
26
|
-
npx ccperm --all # 홈 디렉토리 아래 모든 프로젝트 점검
|
|
27
|
-
npx ccperm --fix # deprecated 패턴 자동 수정
|
|
28
|
-
npx ccperm --all --fix # 전체 점검 + 수정
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## 출력 예시
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
━━━ Claude Code Permission Audit ━━━
|
|
35
|
-
|
|
36
|
-
Scope: ~ (all projects)
|
|
37
|
-
Scanned 12 files:
|
|
38
|
-
|
|
39
|
-
~/Documents/project-a/.claude/settings.local.json (Bash: 5, WebFetch: 3, Tools: 1)
|
|
40
|
-
Bash (5)
|
|
41
|
-
npm run build *
|
|
42
|
-
docker compose *
|
|
43
|
-
curl *
|
|
44
|
-
git add *
|
|
45
|
-
ssh *
|
|
46
|
-
WebFetch (3)
|
|
47
|
-
github.com
|
|
48
|
-
docs.anthropic.com
|
|
49
|
-
api.example.com
|
|
50
|
-
Tools (1)
|
|
51
|
-
WebSearch
|
|
52
|
-
|
|
53
|
-
~/Documents/project-b/.claude/settings.local.json (Bash: 2, MCP: 3)
|
|
54
|
-
Bash (2)
|
|
55
|
-
python3 *
|
|
56
|
-
pytest *
|
|
57
|
-
MCP (3)
|
|
58
|
-
browseros__browser_navigate
|
|
59
|
-
browseros__browser_click_element
|
|
60
|
-
browseros__browser_get_screenshot
|
|
61
|
-
|
|
62
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
63
|
-
All clean! No deprecated :* patterns found.
|
|
64
|
-
```
|
|
22
|
+
기본 동작: `~` 아래 모든 프로젝트를 스캔하고 인터랙티브 TUI를 실행합니다.
|
|
65
23
|
|
|
66
24
|
## 옵션
|
|
67
25
|
|
|
68
26
|
| 플래그 | 설명 |
|
|
69
27
|
|--------|------|
|
|
70
|
-
| `--
|
|
71
|
-
| `--
|
|
28
|
+
| `--cwd` | 현재 디렉토리만 스캔 (기본값: `~` 아래 전체) |
|
|
29
|
+
| `--static` | 텍스트 출력 강제 (파이프/비TTY 환경에서 기본값) |
|
|
30
|
+
| `--verbose` | 모든 권한을 상세 나열하는 텍스트 출력 |
|
|
31
|
+
| `--update` | `npm install -g ccperm@latest`로 자체 업데이트 |
|
|
32
|
+
| `--debug` | 스캔 진단 정보 표시 (파일 경로, 소요 시간) |
|
|
72
33
|
| `--help`, `-h` | 도움말 표시 |
|
|
73
34
|
| `--version`, `-v` | 버전 표시 |
|
|
74
|
-
| `--update` | 업데이트 확인 |
|
|
75
35
|
|
|
76
|
-
##
|
|
36
|
+
## 인터랙티브 TUI
|
|
37
|
+
|
|
38
|
+
TTY 환경(기본)에서는 박스 프레임 TUI가 실행됩니다:
|
|
77
39
|
|
|
78
|
-
|
|
40
|
+
**목록 뷰** -- 프로젝트가 권한 수 기준으로 정렬됩니다. 상단에 `GLOBAL` 섹션이 `~/.claude/` 설정을 보여줍니다. 각 행은 카테고리별 개수(Bash, WebFetch, MCP, Tools)와 `shared`/`local` 라벨로 `settings.json`과 `settings.local.json`을 구분합니다. 최대 25행 표시, 나머지는 스크롤.
|
|
79
41
|
|
|
80
42
|
```
|
|
81
|
-
|
|
82
|
-
|
|
43
|
+
┌ ccperm ──────────────────────────────── 1/8 ┐
|
|
44
|
+
│ PROJECT Bash WebFetch MCP TOTAL │
|
|
45
|
+
├──────────────────────────────────────────────┤
|
|
46
|
+
│ ★ GLOBAL 2 2 │
|
|
47
|
+
├──────────────────────────────────────────────┤
|
|
48
|
+
│▸ my-project local 5 3 · 8 │
|
|
49
|
+
│ other-app shared 2 · 3 5 │
|
|
50
|
+
│ ... │
|
|
51
|
+
└ [↑↓] navigate [Enter] detail [q] quit ────┘
|
|
83
52
|
```
|
|
84
53
|
|
|
54
|
+
**상세 뷰** -- Enter로 프로젝트를 펼칩니다. 카테고리(Bash, WebFetch, MCP, Tools)를 Enter로 접고 펼 수 있습니다.
|
|
55
|
+
|
|
56
|
+
**정보 모드** -- `[i]`를 누르면 각 권한에 위험도 표시(`●` 초록/노랑/빨강)와 설명이 나타납니다.
|
|
57
|
+
|
|
58
|
+
키 조작: `↑↓` 이동, `Enter` 선택/펼치기, `[i]` 정보 토글, `Esc`/`Backspace` 뒤로, `q`/`Ctrl+C` 종료.
|
|
59
|
+
|
|
60
|
+
## 텍스트 출력
|
|
61
|
+
|
|
62
|
+
`--static` 플래그(또는 파이프)로 텍스트 출력:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
ccperm --static # 요약 테이블
|
|
66
|
+
ccperm --static --verbose # 전체 권한 상세 나열
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 권한 레벨
|
|
70
|
+
|
|
71
|
+
ccperm은 Claude Code 설정을 세 단계로 구분합니다:
|
|
72
|
+
|
|
73
|
+
| 레벨 | 파일 | 범위 |
|
|
74
|
+
|------|------|------|
|
|
75
|
+
| **global** | `~/.claude/settings.json` | 모든 프로젝트에 적용 |
|
|
76
|
+
| **shared** | `<project>/.claude/settings.json` | 프로젝트별, git에 커밋됨 |
|
|
77
|
+
| **local** | `<project>/.claude/settings.local.json` | 프로젝트별, gitignore 대상 |
|
|
78
|
+
|
|
85
79
|
## 요구사항
|
|
86
80
|
|
|
87
81
|
- Node.js >= 18
|
package/README.md
CHANGED
|
@@ -4,84 +4,78 @@ Audit Claude Code permissions across all your projects.
|
|
|
4
4
|
|
|
5
5
|
[한국어](README.ko.md)
|
|
6
6
|
|
|
7
|
-
Claude Code stores allowed permissions (Bash commands, WebFetch domains, MCP tools, etc.) in `.claude/settings*.json` per project. As you work across many projects, these permissions pile up silently. **ccperm**
|
|
7
|
+
Claude Code stores allowed permissions (Bash commands, WebFetch domains, MCP tools, etc.) in `.claude/settings*.json` per project. As you work across many projects, these permissions pile up silently. **ccperm** scans your home directory, finds every settings file, and shows what you've allowed -- in an interactive TUI or static text output.
|
|
8
8
|
|
|
9
9
|
## Quick Start
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx ccperm
|
|
12
|
+
npx ccperm
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
No install needed. Or install globally:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
npm i -g ccperm
|
|
19
|
-
ccperm
|
|
19
|
+
ccperm
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
npx ccperm # Audit current project
|
|
26
|
-
npx ccperm --all # Audit all projects under ~
|
|
27
|
-
npx ccperm --fix # Auto-fix deprecated patterns
|
|
28
|
-
npx ccperm --all --fix # Audit + fix all projects
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Output example
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
━━━ Claude Code Permission Audit ━━━
|
|
35
|
-
|
|
36
|
-
Scope: ~ (all projects)
|
|
37
|
-
Scanned 12 files:
|
|
38
|
-
|
|
39
|
-
~/Documents/project-a/.claude/settings.local.json (Bash: 5, WebFetch: 3, Tools: 1)
|
|
40
|
-
Bash (5)
|
|
41
|
-
npm run build *
|
|
42
|
-
docker compose *
|
|
43
|
-
curl *
|
|
44
|
-
git add *
|
|
45
|
-
ssh *
|
|
46
|
-
WebFetch (3)
|
|
47
|
-
github.com
|
|
48
|
-
docs.anthropic.com
|
|
49
|
-
api.example.com
|
|
50
|
-
Tools (1)
|
|
51
|
-
WebSearch
|
|
52
|
-
|
|
53
|
-
~/Documents/project-b/.claude/settings.local.json (Bash: 2, MCP: 3)
|
|
54
|
-
Bash (2)
|
|
55
|
-
python3 *
|
|
56
|
-
pytest *
|
|
57
|
-
MCP (3)
|
|
58
|
-
browseros__browser_navigate
|
|
59
|
-
browseros__browser_click_element
|
|
60
|
-
browseros__browser_get_screenshot
|
|
61
|
-
|
|
62
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
63
|
-
All clean! No deprecated :* patterns found.
|
|
64
|
-
```
|
|
22
|
+
By default, ccperm scans all projects under `~` and launches an interactive TUI.
|
|
65
23
|
|
|
66
24
|
## Options
|
|
67
25
|
|
|
68
26
|
| Flag | Description |
|
|
69
27
|
|------|-------------|
|
|
70
|
-
| `--
|
|
71
|
-
| `--
|
|
28
|
+
| `--cwd` | Scan current directory only (default: all projects under `~`) |
|
|
29
|
+
| `--static` | Force text output (default when piped / non-TTY) |
|
|
30
|
+
| `--verbose` | Detailed static output with all permissions listed |
|
|
31
|
+
| `--update` | Self-update via `npm install -g ccperm@latest` |
|
|
32
|
+
| `--debug` | Show scan diagnostics (file paths, timing) |
|
|
72
33
|
| `--help`, `-h` | Show help |
|
|
73
34
|
| `--version`, `-v` | Show version |
|
|
74
|
-
| `--update` | Check for updates |
|
|
75
35
|
|
|
76
|
-
##
|
|
36
|
+
## Interactive TUI
|
|
37
|
+
|
|
38
|
+
When running in a TTY (the default), ccperm opens a box-frame TUI:
|
|
77
39
|
|
|
78
|
-
|
|
40
|
+
**List view** -- Projects sorted by permission count. `GLOBAL` section at top shows `~/.claude/` settings. Each row shows category counts (Bash, WebFetch, MCP, Tools) and a `shared`/`local` label distinguishing `settings.json` vs `settings.local.json`. Max 25 visible rows; scroll for more.
|
|
79
41
|
|
|
80
42
|
```
|
|
81
|
-
|
|
82
|
-
|
|
43
|
+
┌ ccperm ──────────────────────────────── 1/8 ┐
|
|
44
|
+
│ PROJECT Bash WebFetch MCP TOTAL │
|
|
45
|
+
├──────────────────────────────────────────────┤
|
|
46
|
+
│ ★ GLOBAL 2 2 │
|
|
47
|
+
├──────────────────────────────────────────────┤
|
|
48
|
+
│▸ my-project local 5 3 · 8 │
|
|
49
|
+
│ other-app shared 2 · 3 5 │
|
|
50
|
+
│ ... │
|
|
51
|
+
└ [↑↓] navigate [Enter] detail [q] quit ────┘
|
|
83
52
|
```
|
|
84
53
|
|
|
54
|
+
**Detail view** -- Press Enter to expand a project. Categories (Bash, WebFetch, MCP, Tools) are collapsible; press Enter to toggle.
|
|
55
|
+
|
|
56
|
+
**Info mode** -- Press `[i]` to show risk indicators (`●` green/yellow/red) and descriptions for each permission.
|
|
57
|
+
|
|
58
|
+
Keys: `↑↓` navigate, `Enter` select/expand, `[i]` toggle info, `Esc`/`Backspace` back, `q`/`Ctrl+C` quit.
|
|
59
|
+
|
|
60
|
+
## Static Output
|
|
61
|
+
|
|
62
|
+
Use `--static` (or pipe to another command) for text output:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
ccperm --static # compact table
|
|
66
|
+
ccperm --static --verbose # full permission listing
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Permission Levels
|
|
70
|
+
|
|
71
|
+
ccperm distinguishes three levels of Claude Code settings:
|
|
72
|
+
|
|
73
|
+
| Level | File | Scope |
|
|
74
|
+
|-------|------|-------|
|
|
75
|
+
| **global** | `~/.claude/settings.json` | Applies to all projects |
|
|
76
|
+
| **shared** | `<project>/.claude/settings.json` | Per-project, committed to git |
|
|
77
|
+
| **local** | `<project>/.claude/settings.local.json` | Per-project, gitignored |
|
|
78
|
+
|
|
85
79
|
## Requirements
|
|
86
80
|
|
|
87
81
|
- Node.js >= 18
|
package/dist/aggregator.js
CHANGED
|
@@ -19,7 +19,7 @@ function toFileEntries(results) {
|
|
|
19
19
|
groups.set(g.category, g.items.length);
|
|
20
20
|
}
|
|
21
21
|
const fileType = r.isGlobal ? 'global' : r.display.includes('settings.local.json') ? 'local' : 'shared';
|
|
22
|
-
const name = r.isGlobal ? '
|
|
22
|
+
const name = r.isGlobal ? '~/.claude' : shortPath(r.display);
|
|
23
23
|
return { display: r.display, shortName: name, totalCount: r.totalCount, groups, isGlobal: r.isGlobal, fileType };
|
|
24
24
|
});
|
|
25
25
|
}
|
package/dist/interactive.js
CHANGED
|
@@ -120,7 +120,7 @@ function renderList(state, withPerms, emptyCount) {
|
|
|
120
120
|
const cats = ['Bash', 'WebFetch', 'MCP', 'Tools'];
|
|
121
121
|
const catsPresent = cats.filter((c) => withPerms.some((r) => r.groups.has(c)));
|
|
122
122
|
const catColWidth = catsPresent.length * 7;
|
|
123
|
-
const maxName = Math.max(...withPerms.map((r) => r.
|
|
123
|
+
const maxName = Math.max(...withPerms.map((r) => r.shortName.length), 7);
|
|
124
124
|
const nameWidth = Math.min(maxName, inner - catColWidth - 16);
|
|
125
125
|
const hasGlobalSep = withPerms.some((r) => r.isGlobal) && withPerms.some((r) => !r.isGlobal);
|
|
126
126
|
// box takes: top(1) + header(2) + sep(1) + content + globalSep?(1) + emptyLine?(1) + bottom(1)
|
|
@@ -140,8 +140,7 @@ function renderList(state, withPerms, emptyCount) {
|
|
|
140
140
|
for (let i = state.scrollOffset; i < end; i++) {
|
|
141
141
|
const r = withPerms[i];
|
|
142
142
|
const isCursor = i === state.cursor;
|
|
143
|
-
const
|
|
144
|
-
const truncName = displayName.length > nameWidth ? displayName.slice(0, nameWidth - 1) + '…' : displayName;
|
|
143
|
+
const truncName = r.shortName.length > nameWidth ? r.shortName.slice(0, nameWidth - 1) + '…' : r.shortName;
|
|
145
144
|
const typeTag = r.isGlobal ? pad('', 7) : `${colors_js_1.DIM} ${pad(r.fileType, 6)}${colors_js_1.NC}`;
|
|
146
145
|
const marker = isCursor ? `${colors_js_1.CYAN}▸ ` : ' ';
|
|
147
146
|
const nameStyle = isCursor ? `${colors_js_1.BOLD}` : r.isGlobal ? `${colors_js_1.YELLOW}` : '';
|
package/dist/renderer.js
CHANGED
|
@@ -19,7 +19,7 @@ function printCompact(entries, summary) {
|
|
|
19
19
|
const withPerms = [...globals, ...projects];
|
|
20
20
|
const emptyCount = entries.filter((r) => r.totalCount === 0 && !r.isGlobal).length;
|
|
21
21
|
// header
|
|
22
|
-
const maxName = Math.max(...withPerms.map((r) => r.
|
|
22
|
+
const maxName = Math.max(...withPerms.map((r) => r.shortName.length), 7);
|
|
23
23
|
const nameWidth = Math.min(maxName, 40);
|
|
24
24
|
const header = ` ${colors_js_1.DIM}${pad('PROJECT', nameWidth)} ${catsPresent.map((c) => rpad(c, 5)).join(' ')} TOTAL${colors_js_1.NC}`;
|
|
25
25
|
console.log(header);
|
|
@@ -27,8 +27,7 @@ function printCompact(entries, summary) {
|
|
|
27
27
|
// rows
|
|
28
28
|
for (let i = 0; i < withPerms.length; i++) {
|
|
29
29
|
const result = withPerms[i];
|
|
30
|
-
const
|
|
31
|
-
const truncName = displayName.length > nameWidth ? displayName.slice(0, nameWidth - 1) + '…' : displayName;
|
|
30
|
+
const truncName = result.shortName.length > nameWidth ? result.shortName.slice(0, nameWidth - 1) + '…' : result.shortName;
|
|
32
31
|
const typeTag = result.isGlobal ? pad('', 7) : `${colors_js_1.DIM} ${pad(result.fileType, 6)}${colors_js_1.NC}`;
|
|
33
32
|
const nameStyle = result.isGlobal ? `${colors_js_1.YELLOW}` : '';
|
|
34
33
|
const nameCol = ` ${nameStyle}${pad(truncName, nameWidth)}${colors_js_1.NC}${typeTag}`;
|