ccperm 1.9.1 → 1.9.2

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/dist/explain.js CHANGED
@@ -1,112 +1,127 @@
1
1
  "use strict";
2
2
  // Pattern-based permission explainer
3
+ // Input is the "label" from categorize(), not the raw permission string
3
4
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.explainPermission = explainPermission;
5
+ exports.explainBash = explainBash;
6
+ exports.explainWebFetch = explainWebFetch;
7
+ exports.explainMcp = explainMcp;
8
+ exports.explainTool = explainTool;
9
+ exports.explain = explain;
5
10
  const BASH_COMMANDS = {
6
11
  // [description, risk: green/yellow/red]
7
- 'git': ['Git version control commands', 'green'],
8
- 'npm': ['Node.js package manager can run scripts', 'yellow'],
9
- 'npx': ['Run npm packages — can execute arbitrary code', 'yellow'],
12
+ 'git': ['Git version control', 'green'],
13
+ 'npm': ['Package manager (can run scripts)', 'yellow'],
14
+ 'npx': ['Run npm packages', 'yellow'],
10
15
  'node': ['Run Node.js scripts', 'yellow'],
11
- 'bun': ['Bun runtime — run scripts, install packages', 'yellow'],
12
- 'deno': ['Deno runtime — run scripts', 'yellow'],
16
+ 'bun': ['Bun runtime', 'yellow'],
17
+ 'deno': ['Deno runtime', 'yellow'],
13
18
  'python': ['Run Python scripts', 'yellow'],
14
19
  'python3': ['Run Python scripts', 'yellow'],
15
- 'pip': ['Python package manager — can run setup scripts', 'yellow'],
16
- 'pip3': ['Python package manager — can run setup scripts', 'yellow'],
17
- 'docker': ['Docker container management', 'yellow'],
18
- 'docker-compose': ['Docker Compose multi-container management', 'yellow'],
19
- 'curl': ['HTTP requests from command line', 'yellow'],
20
- 'wget': ['Download files from the web', 'yellow'],
20
+ 'pip': ['Python package manager', 'yellow'],
21
+ 'pip3': ['Python package manager', 'yellow'],
22
+ 'docker': ['Container management', 'yellow'],
23
+ 'docker-compose': ['Multi-container management', 'yellow'],
24
+ 'curl': ['HTTP requests', 'yellow'],
25
+ 'wget': ['Download files', 'yellow'],
21
26
  'ssh': ['Remote shell access', 'red'],
22
- 'scp': ['Remote file copy over SSH', 'red'],
23
- 'rsync': ['File synchronization (local or remote)', 'yellow'],
24
- 'rm': ['Delete files and directories', 'red'],
25
- 'chmod': ['Change file permissions', 'yellow'],
26
- 'chown': ['Change file ownership', 'red'],
27
+ 'scp': ['Remote file copy', 'red'],
28
+ 'rsync': ['File sync (local/remote)', 'yellow'],
29
+ 'rm': ['Delete files', 'red'],
30
+ 'chmod': ['Change permissions', 'yellow'],
31
+ 'chown': ['Change ownership', 'red'],
27
32
  'kill': ['Terminate processes', 'yellow'],
28
- 'sudo': ['Run commands as superuser', 'red'],
29
- 'apt': ['System package manager (Debian/Ubuntu)', 'red'],
30
- 'apt-get': ['System package manager (Debian/Ubuntu)', 'red'],
31
- 'brew': ['Homebrew package manager (macOS)', 'yellow'],
32
- 'make': ['Build automation — runs Makefile targets', 'yellow'],
33
- 'cargo': ['Rust package manager and build tool', 'yellow'],
34
- 'go': ['Go build and package tool', 'yellow'],
35
- 'mvn': ['Maven Java build tool', 'yellow'],
36
- 'gradle': ['Gradle build tool', 'yellow'],
37
- 'yarn': ['Yarn package manager — can run scripts', 'yellow'],
38
- 'pnpm': ['pnpm package manager — can run scripts', 'yellow'],
33
+ 'sudo': ['Superuser access', 'red'],
34
+ 'apt': ['System packages (Debian)', 'red'],
35
+ 'apt-get': ['System packages (Debian)', 'red'],
36
+ 'brew': ['Homebrew packages', 'yellow'],
37
+ 'make': ['Build automation', 'yellow'],
38
+ 'cargo': ['Rust build tool', 'yellow'],
39
+ 'go': ['Go build tool', 'yellow'],
40
+ 'mvn': ['Maven build', 'yellow'],
41
+ 'gradle': ['Gradle build', 'yellow'],
42
+ 'yarn': ['Package manager', 'yellow'],
43
+ 'pnpm': ['Package manager', 'yellow'],
39
44
  'tsc': ['TypeScript compiler', 'green'],
40
- 'eslint': ['JavaScript/TypeScript linter', 'green'],
41
- 'prettier': ['Code formatter', 'green'],
42
- 'jest': ['JavaScript test runner', 'green'],
43
- 'vitest': ['Vite-based test runner', 'green'],
44
- 'cat': ['Read file contents', 'green'],
45
- 'ls': ['List directory contents', 'green'],
46
- 'find': ['Search for files', 'green'],
47
- 'grep': ['Search text patterns in files', 'green'],
48
- 'sed': ['Stream editor — modify file contents', 'yellow'],
49
- 'awk': ['Text processing language', 'green'],
50
- 'wc': ['Count lines/words/bytes', 'green'],
51
- 'head': ['Show first lines of file', 'green'],
52
- 'tail': ['Show last lines of file', 'green'],
45
+ 'eslint': ['Linter', 'green'],
46
+ 'prettier': ['Formatter', 'green'],
47
+ 'jest': ['Test runner', 'green'],
48
+ 'vitest': ['Test runner', 'green'],
49
+ 'cat': ['Read files', 'green'],
50
+ 'ls': ['List directories', 'green'],
51
+ 'find': ['Search files', 'green'],
52
+ 'grep': ['Search text', 'green'],
53
+ 'sed': ['Stream editor', 'yellow'],
54
+ 'awk': ['Text processing', 'green'],
55
+ 'wc': ['Count lines/words', 'green'],
56
+ 'head': ['First lines of file', 'green'],
57
+ 'tail': ['Last lines of file', 'green'],
53
58
  'mkdir': ['Create directories', 'green'],
54
59
  'cp': ['Copy files', 'green'],
55
60
  'mv': ['Move/rename files', 'yellow'],
56
61
  'echo': ['Print text', 'green'],
57
- 'env': ['Show/set environment variables', 'green'],
58
- 'which': ['Locate a command', 'green'],
59
- 'gh': ['GitHub CLI — repos, PRs, issues', 'yellow'],
60
- 'heroku': ['Heroku platform CLI', 'yellow'],
61
- 'vercel': ['Vercel deployment CLI', 'yellow'],
62
- 'aws': ['AWS CLI — cloud infrastructure', 'red'],
62
+ 'env': ['Environment variables', 'green'],
63
+ 'which': ['Locate command', 'green'],
64
+ 'gh': ['GitHub CLI', 'yellow'],
65
+ 'heroku': ['Heroku CLI', 'yellow'],
66
+ 'vercel': ['Vercel CLI', 'yellow'],
67
+ 'aws': ['AWS CLI', 'red'],
63
68
  'gcloud': ['Google Cloud CLI', 'red'],
64
69
  'az': ['Azure CLI', 'red'],
65
- 'kubectl': ['Kubernetes cluster management', 'red'],
70
+ 'kubectl': ['Kubernetes CLI', 'red'],
66
71
  'terraform': ['Infrastructure as Code', 'red'],
72
+ 'dd': ['Low-level disk copy', 'red'],
73
+ 'jq': ['JSON processor', 'green'],
74
+ 'bunx': ['Run bun packages', 'yellow'],
75
+ 'claude': ['Claude Code CLI', 'green'],
76
+ 'defaults': ['macOS defaults', 'yellow'],
67
77
  };
68
78
  const TOOL_DESCRIPTIONS = {
69
- 'Read': 'Read file contents from disk',
70
- 'Write': 'Create or overwrite files',
71
- 'Edit': 'Modify existing files (partial edits)',
72
- 'Glob': 'Search for files by name pattern',
73
- 'Grep': 'Search file contents with regex',
74
- 'WebSearch': 'Search the web via search engine',
79
+ 'Read': ['Read file contents', 'green'],
80
+ 'Write': ['Create/overwrite files', 'yellow'],
81
+ 'Edit': ['Modify existing files', 'yellow'],
82
+ 'Glob': ['Search files by pattern', 'green'],
83
+ 'Grep': ['Search file contents', 'green'],
84
+ 'WebSearch': ['Web search', 'green'],
75
85
  };
76
- function explainPermission(perm) {
77
- // Bash permissions
78
- const bashMatch = perm.match(/^Bash\((.+?)[\s)]/);
79
- if (bashMatch || perm === 'Bash') {
80
- const cmd = bashMatch ? bashMatch[1] : '';
81
- const entry = BASH_COMMANDS[cmd];
82
- if (entry) {
83
- return { description: entry[0], risk: entry[1], detail: `Command: ${cmd}` };
84
- }
85
- if (cmd) {
86
- return { description: `Run "${cmd}" command`, risk: 'yellow', detail: `Command: ${cmd}` };
87
- }
88
- return { description: 'Run shell commands', risk: 'red' };
89
- }
90
- // WebFetch
91
- const fetchMatch = perm.match(/^WebFetch\(domain:(.+)\)$/);
92
- if (fetchMatch) {
93
- const domain = fetchMatch[1];
94
- return { description: `HTTP requests to ${domain}`, risk: 'yellow', detail: `Domain: ${domain}` };
95
- }
96
- if (perm.startsWith('WebFetch')) {
97
- return { description: 'HTTP requests to external URLs', risk: 'yellow' };
98
- }
99
- // MCP tools
100
- if (perm.startsWith('mcp__') || perm.startsWith('mcp_')) {
101
- const parts = perm.split('__');
102
- const server = parts[1] || 'unknown';
103
- const tool = parts.slice(2).join('__') || 'unknown';
104
- return { description: `MCP: ${server} → ${tool}`, risk: 'yellow', detail: `Server: ${server}, Tool: ${tool}` };
105
- }
106
- // Standard tools
107
- const toolName = perm.match(/^(Read|Write|Edit|Glob|Grep|WebSearch)/)?.[1];
86
+ // Extract first command word from a bash label like "git branch:*" or "npm run build"
87
+ function extractCmd(label) {
88
+ // Remove :* or * suffix patterns
89
+ const clean = label.replace(/[:]\*.*$/, '').replace(/\s\*.*$/, '');
90
+ // Get first word
91
+ return clean.split(/[\s(]/)[0];
92
+ }
93
+ function explainBash(label) {
94
+ const cmd = extractCmd(label);
95
+ const entry = BASH_COMMANDS[cmd];
96
+ if (entry)
97
+ return { description: entry[0], risk: entry[1] };
98
+ return { description: '', risk: 'yellow' };
99
+ }
100
+ function explainWebFetch(label) {
101
+ return { description: label, risk: 'yellow' };
102
+ }
103
+ function explainMcp(label) {
104
+ const parts = label.replace(/^mcp__?/, '').split('__');
105
+ const server = parts[0] || '';
106
+ const tool = parts.slice(1).join(' ') || '';
107
+ return { description: tool ? `${server}: ${tool}` : server, risk: 'yellow' };
108
+ }
109
+ function explainTool(label) {
110
+ const toolName = label.match(/^(Read|Write|Edit|Glob|Grep|WebSearch)/)?.[1];
108
111
  if (toolName && TOOL_DESCRIPTIONS[toolName]) {
109
- return { description: TOOL_DESCRIPTIONS[toolName], risk: 'green' };
112
+ const entry = TOOL_DESCRIPTIONS[toolName];
113
+ return { description: entry[0], risk: entry[1] };
110
114
  }
111
- return { description: perm, risk: 'yellow' };
115
+ return { description: '', risk: 'yellow' };
116
+ }
117
+ function explain(category, label) {
118
+ if (category === 'Bash')
119
+ return explainBash(label);
120
+ if (category === 'WebFetch')
121
+ return explainWebFetch(label);
122
+ if (category === 'MCP')
123
+ return explainMcp(label);
124
+ if (category === 'Tools')
125
+ return explainTool(label);
126
+ return { description: '', risk: 'yellow' };
112
127
  }
@@ -185,16 +185,16 @@ function renderDetail(state, withPerms, results) {
185
185
  if (isOpen) {
186
186
  for (const item of group.items) {
187
187
  if (state.showInfo) {
188
- const info = (0, explain_js_1.explainPermission)(item.name);
188
+ const info = (0, explain_js_1.explain)(group.category, item.name);
189
189
  const riskColor = info.risk === 'red' ? colors_js_1.RED : info.risk === 'yellow' ? colors_js_1.YELLOW : colors_js_1.GREEN;
190
190
  const dot = `${riskColor}●${colors_js_1.NC}`;
191
- const maxLen = w - 16;
192
- const name = item.name.length > maxLen ? item.name.slice(0, maxLen - 1) + '…' : item.name;
193
- navRows.push({ text: ` ${dot} ${colors_js_1.DIM}${name}${colors_js_1.NC}`, perm: item.name });
194
- navRows.push({ text: ` ${colors_js_1.DIM}${info.description}${colors_js_1.NC}` });
191
+ const desc = info.description ? ` ${colors_js_1.DIM}${info.description}${colors_js_1.NC}` : '';
192
+ const maxLen = w - 8 - (info.description ? info.description.length + 2 : 0);
193
+ const name = item.name.length > maxLen ? item.name.slice(0, Math.max(8, maxLen) - 1) + '…' : item.name;
194
+ navRows.push({ text: ` ${dot} ${name}${desc}`, perm: item.name });
195
195
  }
196
196
  else {
197
- const maxLen = w - 12;
197
+ const maxLen = w - 8;
198
198
  const name = item.name.length > maxLen ? item.name.slice(0, maxLen - 1) + '…' : item.name;
199
199
  navRows.push({ text: ` ${colors_js_1.DIM}${name}${colors_js_1.NC}`, perm: item.name });
200
200
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccperm",
3
- "version": "1.9.1",
3
+ "version": "1.9.2",
4
4
  "description": "Audit Claude Code permissions across all your projects",
5
5
  "bin": {
6
6
  "ccperm": "bin/ccperm.js"