ccperm 1.6.0 → 1.7.0
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/aggregator.js +2 -1
- package/dist/interactive.js +17 -7
- package/dist/renderer.js +15 -5
- package/dist/scanner.js +2 -1
- package/package.json +1 -1
package/dist/aggregator.js
CHANGED
|
@@ -18,7 +18,8 @@ function mergeByProject(results) {
|
|
|
18
18
|
const dir = projectDir(r.display);
|
|
19
19
|
let merged = map.get(dir);
|
|
20
20
|
if (!merged) {
|
|
21
|
-
|
|
21
|
+
const name = r.isGlobal ? 'GLOBAL' : shortPath(r.display);
|
|
22
|
+
merged = { display: r.display, shortName: name, totalCount: 0, groups: new Map(), isGlobal: r.isGlobal };
|
|
22
23
|
map.set(dir, merged);
|
|
23
24
|
}
|
|
24
25
|
merged.totalCount += r.totalCount;
|
package/dist/interactive.js
CHANGED
|
@@ -33,7 +33,9 @@ function boxSep(width) {
|
|
|
33
33
|
}
|
|
34
34
|
function startInteractive(merged, results) {
|
|
35
35
|
return new Promise((resolve) => {
|
|
36
|
-
const
|
|
36
|
+
const globals = merged.filter((r) => r.totalCount > 0 && r.isGlobal).sort((a, b) => b.totalCount - a.totalCount);
|
|
37
|
+
const projects = merged.filter((r) => r.totalCount > 0 && !r.isGlobal).sort((a, b) => b.totalCount - a.totalCount);
|
|
38
|
+
const withPerms = [...globals, ...projects];
|
|
37
39
|
const emptyCount = merged.filter((r) => r.totalCount === 0).length;
|
|
38
40
|
if (withPerms.length === 0) {
|
|
39
41
|
console.log(`\n ${colors_js_1.GREEN}No projects with permissions found.${colors_js_1.NC}\n`);
|
|
@@ -114,10 +116,12 @@ function renderList(state, withPerms, emptyCount) {
|
|
|
114
116
|
const cats = ['Bash', 'WebFetch', 'MCP', 'Tools'];
|
|
115
117
|
const catsPresent = cats.filter((c) => withPerms.some((r) => r.groups.has(c)));
|
|
116
118
|
const catColWidth = catsPresent.length * 7;
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
119
|
+
const nameWidths = withPerms.map((r) => r.isGlobal ? r.shortName.length + 2 : r.shortName.length);
|
|
120
|
+
const nameWidth = Math.min(Math.max(...nameWidths, 7), inner - catColWidth - 8);
|
|
121
|
+
const hasGlobalSep = withPerms.some((r) => r.isGlobal) && withPerms.some((r) => !r.isGlobal);
|
|
122
|
+
// box takes: top(1) + header(2) + sep(1) + content + globalSep?(1) + emptyLine?(1) + bottom(1)
|
|
123
|
+
const chrome = 5 + (hasGlobalSep ? 1 : 0) + (emptyCount > 0 ? 1 : 0);
|
|
124
|
+
const visibleRows = Math.min(25, Math.max(1, rows - chrome));
|
|
121
125
|
if (state.cursor < state.scrollOffset)
|
|
122
126
|
state.scrollOffset = state.cursor;
|
|
123
127
|
if (state.cursor >= state.scrollOffset + visibleRows)
|
|
@@ -127,13 +131,15 @@ function renderList(state, withPerms, emptyCount) {
|
|
|
127
131
|
lines.push(boxTop('ccperm', scrollInfo, w));
|
|
128
132
|
lines.push(boxLine(`${colors_js_1.DIM}${pad('PROJECT', nameWidth)} ${catsPresent.map((c) => rpad(c, 5)).join(' ')} TOTAL${colors_js_1.NC}`, w));
|
|
129
133
|
lines.push(boxSep(w));
|
|
134
|
+
const globalCount = withPerms.filter((r) => r.isGlobal).length;
|
|
130
135
|
const end = Math.min(state.scrollOffset + visibleRows, withPerms.length);
|
|
131
136
|
for (let i = state.scrollOffset; i < end; i++) {
|
|
132
137
|
const r = withPerms[i];
|
|
133
138
|
const isCursor = i === state.cursor;
|
|
134
|
-
const
|
|
139
|
+
const displayName = r.isGlobal ? `★ ${r.shortName}` : r.shortName;
|
|
140
|
+
const truncName = displayName.length > nameWidth ? displayName.slice(0, nameWidth - 1) + '…' : displayName;
|
|
135
141
|
const marker = isCursor ? `${colors_js_1.CYAN}▸ ` : ' ';
|
|
136
|
-
const nameStyle = isCursor ? `${colors_js_1.BOLD}` : `${colors_js_1.DIM}`;
|
|
142
|
+
const nameStyle = isCursor ? `${colors_js_1.BOLD}` : r.isGlobal ? `${colors_js_1.YELLOW}` : `${colors_js_1.DIM}`;
|
|
137
143
|
const nameCol = `${marker}${nameStyle}${pad(truncName, nameWidth)}${colors_js_1.NC}`;
|
|
138
144
|
const catCols = catsPresent.map((c) => {
|
|
139
145
|
const count = r.groups.get(c) || 0;
|
|
@@ -143,6 +149,10 @@ function renderList(state, withPerms, emptyCount) {
|
|
|
143
149
|
}).join(' ');
|
|
144
150
|
const totalCol = isCursor ? `${colors_js_1.BOLD}${rpad(r.totalCount, 5)}${colors_js_1.NC}` : rpad(r.totalCount, 5);
|
|
145
151
|
lines.push(boxLine(`${nameCol} ${catCols} ${totalCol}`, w));
|
|
152
|
+
// separator after global section
|
|
153
|
+
if (r.isGlobal && i + 1 < withPerms.length && !withPerms[i + 1].isGlobal) {
|
|
154
|
+
lines.push(boxSep(w));
|
|
155
|
+
}
|
|
146
156
|
}
|
|
147
157
|
if (emptyCount > 0) {
|
|
148
158
|
lines.push(boxLine(`${colors_js_1.DIM}+ ${emptyCount} projects with no permissions${colors_js_1.NC}`, w));
|
package/dist/renderer.js
CHANGED
|
@@ -14,23 +14,33 @@ function rpad(s, n) {
|
|
|
14
14
|
function printCompact(merged, summary) {
|
|
15
15
|
const cats = ['Bash', 'WebFetch', 'MCP', 'Tools'];
|
|
16
16
|
const catsPresent = cats.filter((c) => merged.some((r) => r.groups.has(c)));
|
|
17
|
-
const
|
|
17
|
+
const globals = merged.filter((r) => r.totalCount > 0 && r.isGlobal).sort((a, b) => b.totalCount - a.totalCount);
|
|
18
|
+
const projects = merged.filter((r) => r.totalCount > 0 && !r.isGlobal).sort((a, b) => b.totalCount - a.totalCount);
|
|
19
|
+
const withPerms = [...globals, ...projects];
|
|
18
20
|
const emptyCount = merged.filter((r) => r.totalCount === 0).length;
|
|
19
21
|
// header
|
|
20
|
-
const
|
|
22
|
+
const nameWidths = withPerms.map((r) => r.isGlobal ? r.shortName.length + 2 : r.shortName.length);
|
|
23
|
+
const nameWidth = Math.min(Math.max(...nameWidths, 7), 40);
|
|
21
24
|
const header = ` ${colors_js_1.DIM}${pad('PROJECT', nameWidth)} ${catsPresent.map((c) => rpad(c, 5)).join(' ')} TOTAL${colors_js_1.NC}`;
|
|
22
25
|
console.log(header);
|
|
23
26
|
console.log(` ${colors_js_1.DIM}${'─'.repeat(nameWidth + catsPresent.length * 7 + 8)}${colors_js_1.NC}`);
|
|
24
27
|
// rows
|
|
25
|
-
for (
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
+
for (let i = 0; i < withPerms.length; i++) {
|
|
29
|
+
const result = withPerms[i];
|
|
30
|
+
const displayName = result.isGlobal ? `★ ${result.shortName}` : result.shortName;
|
|
31
|
+
const truncName = displayName.length > nameWidth ? displayName.slice(0, nameWidth - 1) + '…' : displayName;
|
|
32
|
+
const nameStyle = result.isGlobal ? `${colors_js_1.YELLOW}` : `${colors_js_1.DIM}`;
|
|
33
|
+
const nameCol = ` ${nameStyle}${pad(truncName, nameWidth)}${colors_js_1.NC}`;
|
|
28
34
|
const catCols = catsPresent.map((c) => {
|
|
29
35
|
const count = result.groups.get(c) || 0;
|
|
30
36
|
return count > 0 ? rpad(count, 5) : `${colors_js_1.DIM}${rpad('·', 5)}${colors_js_1.NC}`;
|
|
31
37
|
}).join(' ');
|
|
32
38
|
const totalCol = rpad(result.totalCount, 5);
|
|
33
39
|
console.log(`${nameCol} ${catCols} ${colors_js_1.BOLD}${totalCol}${colors_js_1.NC}`);
|
|
40
|
+
// separator after global section
|
|
41
|
+
if (result.isGlobal && i + 1 < withPerms.length && !withPerms[i + 1].isGlobal) {
|
|
42
|
+
console.log(` ${colors_js_1.DIM}${'─'.repeat(nameWidth + catsPresent.length * 7 + 8)}${colors_js_1.NC}`);
|
|
43
|
+
}
|
|
34
44
|
}
|
|
35
45
|
if (emptyCount > 0) {
|
|
36
46
|
console.log(`\n ${colors_js_1.DIM}+ ${emptyCount} projects with no permissions${colors_js_1.NC}`);
|
package/dist/scanner.js
CHANGED
|
@@ -93,6 +93,7 @@ async function findSettingsFiles(searchDir, onProgress, debug = false) {
|
|
|
93
93
|
function scanFile(filePath) {
|
|
94
94
|
const home = node_os_1.default.homedir();
|
|
95
95
|
const display = filePath.startsWith(home) ? '~' + filePath.slice(home.length) : filePath;
|
|
96
|
+
const isGlobal = node_path_1.default.dirname(node_path_1.default.dirname(filePath)) === home;
|
|
96
97
|
let content;
|
|
97
98
|
try {
|
|
98
99
|
content = node_fs_1.default.readFileSync(filePath, 'utf8');
|
|
@@ -103,7 +104,7 @@ function scanFile(filePath) {
|
|
|
103
104
|
const perms = [...new Set((content.match(PERM_RE) || []).map((s) => s.slice(1, -1)))].sort();
|
|
104
105
|
const groups = groupPermissions(perms);
|
|
105
106
|
const totalCount = perms.length;
|
|
106
|
-
return { path: filePath, display, permissions: perms, groups, totalCount };
|
|
107
|
+
return { path: filePath, display, permissions: perms, groups, totalCount, isGlobal };
|
|
107
108
|
}
|
|
108
109
|
function categorize(perm) {
|
|
109
110
|
if (perm.startsWith('Bash')) {
|