coding-friend-cli 1.17.4 → 1.18.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/README.md +5 -2
- package/dist/chunk-7CAIGH2Y.js +365 -0
- package/dist/{chunk-UWQPMVJY.js → chunk-A427XMWE.js} +1 -1
- package/dist/{chunk-DVMWMXDZ.js → chunk-VUAUAO2R.js} +21 -0
- package/dist/{config-HVWEV2K6.js → config-6SBGNTAQ.js} +80 -1
- package/dist/{dev-AZSOM775.js → dev-MC6TGHRT.js} +1 -1
- package/dist/index.js +16 -13
- package/dist/{init-73ECEDU7.js → init-2HLPKYXB.js} +220 -35
- package/dist/{install-USFLRCS5.js → install-3QCRGPTY.js} +2 -2
- package/dist/{permission-L2QQR5PO.js → permission-Z3LOBJ4X.js} +72 -11
- package/dist/postinstall.js +1 -1
- package/dist/{uninstall-QSNKGNHR.js → uninstall-5LRHXFSF.js} +1 -1
- package/dist/{update-PNHTIB6M.js → update-4YUSCBCB.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-56U7US6J.js +0 -198
package/README.md
CHANGED
|
@@ -48,8 +48,11 @@ cf host [path] # Build and serve learning docs at localhost:3333
|
|
|
48
48
|
cf mcp [path] # Setup MCP server for LLM integration
|
|
49
49
|
# [path] is optional, default is `docs/learn`
|
|
50
50
|
# This prints a JSON config snippet to add to your client's MCP
|
|
51
|
-
cf permission
|
|
52
|
-
cf permission --all
|
|
51
|
+
cf permission # Manage Claude Code permission rules (interactive)
|
|
52
|
+
cf permission --all # Apply all recommended permissions without prompts
|
|
53
|
+
cf permission --user # Save to user-level settings (~/.claude/settings.json)
|
|
54
|
+
cf permission --project # Save to project-level settings (.claude/settings.local.json)
|
|
55
|
+
cf permission --all --user # Apply all recommended permissions to user settings
|
|
53
56
|
cf statusline # Setup coding-friend statusline
|
|
54
57
|
cf update # Update plugin + CLI + statusline
|
|
55
58
|
cf update --cli # Update only the CLI (npm package)
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readJson,
|
|
3
|
+
writeJson
|
|
4
|
+
} from "./chunk-RWUTFVRB.js";
|
|
5
|
+
|
|
6
|
+
// src/lib/permissions.ts
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
var STATIC_RULES = [
|
|
9
|
+
// Core Utilities (hooks & infrastructure)
|
|
10
|
+
{
|
|
11
|
+
rule: "Bash(cat *)",
|
|
12
|
+
description: "[read-only] Read file contents \xB7 Used by: session-init hook, skills",
|
|
13
|
+
category: "Core Utilities",
|
|
14
|
+
recommended: true
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
rule: "Bash(grep *)",
|
|
18
|
+
description: "[read-only] Search file contents \xB7 Used by: session-init hook, skills",
|
|
19
|
+
category: "Core Utilities",
|
|
20
|
+
recommended: true
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
rule: "Bash(sed *)",
|
|
24
|
+
description: "[modify] Text transformation \xB7 Used by: session-init hook",
|
|
25
|
+
category: "Core Utilities",
|
|
26
|
+
recommended: true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
rule: "Bash(tr *)",
|
|
30
|
+
description: "[read-only] Character translation \xB7 Used by: session-init hook",
|
|
31
|
+
category: "Core Utilities",
|
|
32
|
+
recommended: true
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
rule: "Bash(wc *)",
|
|
36
|
+
description: "[read-only] Count lines/words \xB7 Used by: cf-verification, skills",
|
|
37
|
+
category: "Core Utilities",
|
|
38
|
+
recommended: true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
rule: "Bash(mkdir *)",
|
|
42
|
+
description: "[write] Create directories \xB7 Used by: docs folder setup, /tmp/coding-friend",
|
|
43
|
+
category: "Core Utilities",
|
|
44
|
+
recommended: true
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
rule: "Bash(find *)",
|
|
48
|
+
description: "[read-only] Find files \xB7 Used by: /cf-learn list-learn-files",
|
|
49
|
+
category: "Core Utilities",
|
|
50
|
+
recommended: true
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
rule: "Bash(ls *)",
|
|
54
|
+
description: "[read-only] List files \xB7 Used by: statusline hook, session-init hook",
|
|
55
|
+
category: "Core Utilities",
|
|
56
|
+
recommended: true
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
rule: "Bash(jq *)",
|
|
60
|
+
description: "[read-only] Parse JSON \xB7 Used by: statusline hook, session-init hook, memory-capture hook",
|
|
61
|
+
category: "Core Utilities",
|
|
62
|
+
recommended: true
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
rule: "Bash(date *)",
|
|
66
|
+
description: "[read-only] Format timestamps \xB7 Used by: statusline hook",
|
|
67
|
+
category: "Core Utilities",
|
|
68
|
+
recommended: true
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
rule: "Bash(pwd)",
|
|
72
|
+
description: "[read-only] Get current directory \xB7 Used by: session-init hook, /cf-learn",
|
|
73
|
+
category: "Core Utilities",
|
|
74
|
+
recommended: true
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
rule: "Bash(stat *)",
|
|
78
|
+
description: "[read-only] File stats \xB7 Used by: statusline hook cache check",
|
|
79
|
+
category: "Core Utilities",
|
|
80
|
+
recommended: true
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
rule: "Bash(node -e *)",
|
|
84
|
+
description: "[execute] Run inline Node.js \xB7 Used by: memory-capture hook JSON parsing",
|
|
85
|
+
category: "Core Utilities",
|
|
86
|
+
recommended: true
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
rule: "Bash(touch /tmp/coding-friend/*)",
|
|
90
|
+
description: "[write] Create temp marker files \xB7 Used by: /cf-review mark-reviewed",
|
|
91
|
+
category: "Core Utilities",
|
|
92
|
+
recommended: true
|
|
93
|
+
},
|
|
94
|
+
// Git Operations
|
|
95
|
+
{
|
|
96
|
+
rule: "Bash(git status *)",
|
|
97
|
+
description: "[read-only] Check working tree status \xB7 Used by: /cf-commit, /cf-review, cf-verification",
|
|
98
|
+
category: "Git",
|
|
99
|
+
recommended: true
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
rule: "Bash(git diff *)",
|
|
103
|
+
description: "[read-only] View file changes \xB7 Used by: /cf-commit, /cf-review, cf-verification",
|
|
104
|
+
category: "Git",
|
|
105
|
+
recommended: true
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
rule: "Bash(git log *)",
|
|
109
|
+
description: "[read-only] View commit history \xB7 Used by: /cf-commit, /cf-review, cf-sys-debug",
|
|
110
|
+
category: "Git",
|
|
111
|
+
recommended: true
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
rule: "Bash(git branch *)",
|
|
115
|
+
description: "[read-only] List/manage branches \xB7 Used by: /cf-ship, cf-sys-debug",
|
|
116
|
+
category: "Git",
|
|
117
|
+
recommended: true
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
rule: "Bash(git rev-parse *)",
|
|
121
|
+
description: "[read-only] Check git repo state \xB7 Used by: hooks, /cf-commit",
|
|
122
|
+
category: "Git",
|
|
123
|
+
recommended: true
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
rule: "Bash(git add *)",
|
|
127
|
+
description: "[modify] Stage files for commit \xB7 Used by: /cf-commit, /cf-ship",
|
|
128
|
+
category: "Git",
|
|
129
|
+
recommended: true
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
rule: "Bash(git reset HEAD *)",
|
|
133
|
+
description: "[modify] Unstage files \xB7 Used by: /cf-commit",
|
|
134
|
+
category: "Git",
|
|
135
|
+
recommended: true
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
rule: "Bash(git commit *)",
|
|
139
|
+
description: "[modify] Create commits \xB7 Used by: /cf-commit, /cf-ship",
|
|
140
|
+
category: "Git",
|
|
141
|
+
recommended: true
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
rule: "Bash(git push *)",
|
|
145
|
+
description: "[remote] Push commits to remote \xB7 Used by: /cf-ship",
|
|
146
|
+
category: "Git",
|
|
147
|
+
recommended: true
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
rule: "Bash(git pull *)",
|
|
151
|
+
description: "[remote] Pull changes from remote \xB7 Used by: /cf-ship",
|
|
152
|
+
category: "Git",
|
|
153
|
+
recommended: true
|
|
154
|
+
},
|
|
155
|
+
// GitHub CLI
|
|
156
|
+
{
|
|
157
|
+
rule: "Bash(gh pr *)",
|
|
158
|
+
description: "[remote] Manage GitHub pull requests \xB7 Used by: /cf-ship",
|
|
159
|
+
category: "GitHub CLI",
|
|
160
|
+
recommended: true
|
|
161
|
+
},
|
|
162
|
+
// Testing & Build
|
|
163
|
+
{
|
|
164
|
+
rule: "Bash(npm test *)",
|
|
165
|
+
description: "[execute] Run test suites \xB7 Used by: cf-verification, /cf-fix, cf-tdd",
|
|
166
|
+
category: "Testing & Build",
|
|
167
|
+
recommended: true
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
rule: "Bash(npm run *)",
|
|
171
|
+
description: "[execute] Run npm scripts (build, lint, format) \xB7 Used by: cf-verification, /cf-commit",
|
|
172
|
+
category: "Testing & Build",
|
|
173
|
+
recommended: true
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
rule: "Bash(npx *)",
|
|
177
|
+
description: "[execute] Run npx commands (eslint, tsc) \xB7 Used by: cf-verification",
|
|
178
|
+
category: "Testing & Build",
|
|
179
|
+
recommended: true
|
|
180
|
+
},
|
|
181
|
+
// MCP Memory Tools
|
|
182
|
+
{
|
|
183
|
+
rule: "mcp__coding-friend-memory__memory_search",
|
|
184
|
+
description: "[read-only] Search project memories \xB7 Used by: cf-explorer agent, all skills",
|
|
185
|
+
category: "MCP Memory",
|
|
186
|
+
recommended: true
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
rule: "mcp__coding-friend-memory__memory_list",
|
|
190
|
+
description: "[read-only] List all memories \xB7 Used by: /cf-scan",
|
|
191
|
+
category: "MCP Memory",
|
|
192
|
+
recommended: true
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
rule: "mcp__coding-friend-memory__memory_retrieve",
|
|
196
|
+
description: "[read-only] Get a specific memory by ID \xB7 Used by: skills",
|
|
197
|
+
category: "MCP Memory",
|
|
198
|
+
recommended: true
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
rule: "mcp__coding-friend-memory__memory_store",
|
|
202
|
+
description: "[write] Create new memory \xB7 Used by: /cf-remember, /cf-scan, /cf-ask",
|
|
203
|
+
category: "MCP Memory",
|
|
204
|
+
recommended: true
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
rule: "mcp__coding-friend-memory__memory_update",
|
|
208
|
+
description: "[modify] Update existing memory \xB7 Used by: /cf-remember, /cf-scan",
|
|
209
|
+
category: "MCP Memory",
|
|
210
|
+
recommended: true
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
rule: "mcp__coding-friend-memory__memory_delete",
|
|
214
|
+
description: "[modify] Delete a memory \xB7 Used by: /cf-scan",
|
|
215
|
+
category: "MCP Memory",
|
|
216
|
+
recommended: true
|
|
217
|
+
},
|
|
218
|
+
// Web & Research
|
|
219
|
+
{
|
|
220
|
+
rule: "WebSearch",
|
|
221
|
+
description: "[network] Perform web searches \xB7 Used by: /cf-research",
|
|
222
|
+
category: "Web & Research",
|
|
223
|
+
recommended: false
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
rule: "WebFetch(domain:*)",
|
|
227
|
+
description: "[network] Fetch content from web pages \xB7 Used by: /cf-research",
|
|
228
|
+
category: "Web & Research",
|
|
229
|
+
recommended: false
|
|
230
|
+
}
|
|
231
|
+
];
|
|
232
|
+
var PLUGIN_CACHE_TILDE = "~/.claude/plugins/cache/coding-friend-marketplace/coding-friend";
|
|
233
|
+
function buildPluginScriptRules() {
|
|
234
|
+
const absBase = `${homedir()}/.claude/plugins/cache/coding-friend-marketplace/coding-friend`;
|
|
235
|
+
return [
|
|
236
|
+
{
|
|
237
|
+
rule: `Bash(bash ${absBase}/*)`,
|
|
238
|
+
description: "[execute] Run Coding Friend plugin scripts (unquoted) \xB7 Used by: all skills",
|
|
239
|
+
category: "Plugin Scripts",
|
|
240
|
+
recommended: true
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
rule: `Bash(bash "${absBase}/*)`,
|
|
244
|
+
description: "[execute] Run Coding Friend plugin scripts (quoted) \xB7 Used by: all skills",
|
|
245
|
+
category: "Plugin Scripts",
|
|
246
|
+
recommended: true
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
rule: `Read(${PLUGIN_CACHE_TILDE}/**)`,
|
|
250
|
+
description: "[read-only] Read plugin files \xB7 Used by: hooks, agents",
|
|
251
|
+
category: "Plugin Scripts",
|
|
252
|
+
recommended: true
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
rule: "Read(~/.coding-friend/**)",
|
|
256
|
+
description: "[read-only] Read user global config \xB7 Used by: session-init hook, /cf-learn",
|
|
257
|
+
category: "Plugin Scripts",
|
|
258
|
+
recommended: true
|
|
259
|
+
}
|
|
260
|
+
];
|
|
261
|
+
}
|
|
262
|
+
function getAllRules() {
|
|
263
|
+
return [...STATIC_RULES, ...buildPluginScriptRules()];
|
|
264
|
+
}
|
|
265
|
+
var PERMISSION_RULES = getAllRules();
|
|
266
|
+
function getExistingRules(settingsPath) {
|
|
267
|
+
const settings = readJson(settingsPath);
|
|
268
|
+
if (!settings) return [];
|
|
269
|
+
const permissions = settings.permissions;
|
|
270
|
+
return permissions?.allow ?? [];
|
|
271
|
+
}
|
|
272
|
+
function getMissingRules(existing, rules) {
|
|
273
|
+
return rules.filter((r) => !existing.includes(r.rule));
|
|
274
|
+
}
|
|
275
|
+
function buildLearnDirRules(learnPath, autoCommit) {
|
|
276
|
+
const rules = [
|
|
277
|
+
{
|
|
278
|
+
rule: `Read(${learnPath}/**)`,
|
|
279
|
+
description: "[read-only] Read learning docs \xB7 Used by: /cf-learn",
|
|
280
|
+
category: "External Learn Directory",
|
|
281
|
+
recommended: true
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
rule: `Edit(${learnPath}/**)`,
|
|
285
|
+
description: "[modify] Edit learning docs \xB7 Used by: /cf-learn",
|
|
286
|
+
category: "External Learn Directory",
|
|
287
|
+
recommended: true
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
rule: `Write(${learnPath}/**)`,
|
|
291
|
+
description: "[write] Write learning docs \xB7 Used by: /cf-learn",
|
|
292
|
+
category: "External Learn Directory",
|
|
293
|
+
recommended: true
|
|
294
|
+
}
|
|
295
|
+
];
|
|
296
|
+
if (autoCommit) {
|
|
297
|
+
const quoted = learnPath.includes(" ") ? `"${learnPath}"` : learnPath;
|
|
298
|
+
rules.push({
|
|
299
|
+
rule: `Bash(cd ${quoted} && git add *)`,
|
|
300
|
+
description: "[modify] Stage learning docs for commit \xB7 Used by: /cf-learn auto-commit",
|
|
301
|
+
category: "External Learn Directory",
|
|
302
|
+
recommended: true
|
|
303
|
+
});
|
|
304
|
+
rules.push({
|
|
305
|
+
rule: `Bash(cd ${quoted} && git commit *)`,
|
|
306
|
+
description: "[modify] Commit learning docs \xB7 Used by: /cf-learn auto-commit",
|
|
307
|
+
category: "External Learn Directory",
|
|
308
|
+
recommended: true
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
return rules;
|
|
312
|
+
}
|
|
313
|
+
function applyPermissions(settingsPath, toAdd, toRemove) {
|
|
314
|
+
const settings = readJson(settingsPath) ?? {};
|
|
315
|
+
const permissions = settings.permissions ?? {};
|
|
316
|
+
const existing = permissions.allow ?? [];
|
|
317
|
+
const afterRemove = existing.filter((r) => !toRemove.includes(r));
|
|
318
|
+
const afterAdd = [
|
|
319
|
+
...afterRemove,
|
|
320
|
+
...toAdd.filter((r) => !afterRemove.includes(r))
|
|
321
|
+
];
|
|
322
|
+
permissions.allow = afterAdd;
|
|
323
|
+
settings.permissions = permissions;
|
|
324
|
+
writeJson(settingsPath, settings);
|
|
325
|
+
}
|
|
326
|
+
function groupByCategory(rules) {
|
|
327
|
+
const groups = /* @__PURE__ */ new Map();
|
|
328
|
+
for (const rule of rules) {
|
|
329
|
+
const list = groups.get(rule.category) ?? [];
|
|
330
|
+
list.push(rule);
|
|
331
|
+
groups.set(rule.category, list);
|
|
332
|
+
}
|
|
333
|
+
return groups;
|
|
334
|
+
}
|
|
335
|
+
var OLD_PLUGIN_PATH_PATTERN = "/.claude/plugins/cache/coding-friend-marketplace/coding-friend/";
|
|
336
|
+
function cleanupStalePluginRules(settingsPath) {
|
|
337
|
+
const existing = getExistingRules(settingsPath);
|
|
338
|
+
const currentRules = new Set(getAllRules().map((r) => r.rule));
|
|
339
|
+
const stale = existing.filter(
|
|
340
|
+
(r) => r.includes(OLD_PLUGIN_PATH_PATTERN) && !currentRules.has(r)
|
|
341
|
+
);
|
|
342
|
+
if (stale.length === 0) return 0;
|
|
343
|
+
applyPermissions(settingsPath, [], stale);
|
|
344
|
+
return stale.length;
|
|
345
|
+
}
|
|
346
|
+
function logPluginScriptWarning(log, chalk) {
|
|
347
|
+
log.warn(
|
|
348
|
+
`Plugin script rules use ${chalk.bold("wide patterns")} that allow executing any script in the Coding Friend plugin cache.`
|
|
349
|
+
);
|
|
350
|
+
log.dim(
|
|
351
|
+
"This is scoped to Coding Friend only and survives plugin updates automatically."
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export {
|
|
356
|
+
STATIC_RULES,
|
|
357
|
+
getAllRules,
|
|
358
|
+
getExistingRules,
|
|
359
|
+
getMissingRules,
|
|
360
|
+
buildLearnDirRules,
|
|
361
|
+
applyPermissions,
|
|
362
|
+
groupByCategory,
|
|
363
|
+
cleanupStalePluginRules,
|
|
364
|
+
logPluginScriptWarning
|
|
365
|
+
};
|
|
@@ -61,6 +61,12 @@ _cf_completions() {
|
|
|
61
61
|
return
|
|
62
62
|
fi
|
|
63
63
|
|
|
64
|
+
# Flag completion for permission
|
|
65
|
+
if [[ "\${COMP_WORDS[1]}" == "permission" && "$cur" == -* ]]; then
|
|
66
|
+
COMPREPLY=($(compgen -W "--all --user --project" -- "$cur"))
|
|
67
|
+
return
|
|
68
|
+
fi
|
|
69
|
+
|
|
64
70
|
COMPREPLY=($(compgen -W "$commands" -- "$cur"))
|
|
65
71
|
}
|
|
66
72
|
complete -o default -F _cf_completions cf
|
|
@@ -110,6 +116,14 @@ var ZSH_FUNCTION_BODY = `_cf() {
|
|
|
110
116
|
_values 'flags' $scope_flags
|
|
111
117
|
elif (( CURRENT >= 3 )) && [[ "\${words[2]}" == "update" ]]; then
|
|
112
118
|
_values 'flags' $update_flags
|
|
119
|
+
elif (( CURRENT >= 3 )) && [[ "\${words[2]}" == "permission" ]]; then
|
|
120
|
+
local -a permission_flags
|
|
121
|
+
permission_flags=(
|
|
122
|
+
'--all[Apply all recommended permissions without prompts]'
|
|
123
|
+
'--user[Save to user-level settings]'
|
|
124
|
+
'--project[Save to project-level settings]'
|
|
125
|
+
)
|
|
126
|
+
_values 'flags' $permission_flags
|
|
113
127
|
elif (( CURRENT == 3 )) && [[ "\${words[2]}" == "dev" ]]; then
|
|
114
128
|
local -a subcommands
|
|
115
129
|
subcommands=(
|
|
@@ -185,6 +199,10 @@ complete -c cf -n "__fish_seen_subcommand_from update" -l user -d "User scope (a
|
|
|
185
199
|
complete -c cf -n "__fish_seen_subcommand_from update" -l global -d "User scope (all projects)"
|
|
186
200
|
complete -c cf -n "__fish_seen_subcommand_from update" -l project -d "Project scope"
|
|
187
201
|
complete -c cf -n "__fish_seen_subcommand_from update" -l local -d "Local scope"
|
|
202
|
+
# Flags for permission
|
|
203
|
+
complete -c cf -n "__fish_seen_subcommand_from permission" -l all -d "Apply all recommended permissions"
|
|
204
|
+
complete -c cf -n "__fish_seen_subcommand_from permission" -l user -d "Save to user-level settings"
|
|
205
|
+
complete -c cf -n "__fish_seen_subcommand_from permission" -l project -d "Save to project-level settings"
|
|
188
206
|
# Dev subcommands
|
|
189
207
|
complete -c cf -n "__fish_seen_subcommand_from dev" -a on -d "Switch to local plugin source"
|
|
190
208
|
complete -c cf -n "__fish_seen_subcommand_from dev" -a off -d "Switch back to remote marketplace"
|
|
@@ -230,6 +248,9 @@ Register-ArgumentCompleter -Native -CommandName cf -ScriptBlock {
|
|
|
230
248
|
} elseif ($words.Count -ge 2 -and ($words[1].ToString() -eq 'install' -or $words[1].ToString() -eq 'uninstall' -or $words[1].ToString() -eq 'disable' -or $words[1].ToString() -eq 'enable')) {
|
|
231
249
|
$scopeFlags | Where-Object { $_ -like "$wordToComplete*" } |
|
|
232
250
|
ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
|
|
251
|
+
} elseif ($words.Count -ge 2 -and $words[1].ToString() -eq 'permission') {
|
|
252
|
+
@('--all','--user','--project') | Where-Object { $_ -like "$wordToComplete*" } |
|
|
253
|
+
ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
|
|
233
254
|
} elseif ($words.Count -ge 2 -and $words[1].ToString() -eq 'update') {
|
|
234
255
|
$updateFlags | Where-Object { $_ -like "$wordToComplete*" } |
|
|
235
256
|
ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getAllRules,
|
|
3
|
+
getExistingRules
|
|
4
|
+
} from "./chunk-7CAIGH2Y.js";
|
|
1
5
|
import {
|
|
2
6
|
findStatuslineHookPath,
|
|
3
7
|
isStatuslineConfigured,
|
|
@@ -13,7 +17,7 @@ import {
|
|
|
13
17
|
ensureShellCompletion,
|
|
14
18
|
hasShellCompletion,
|
|
15
19
|
removeShellCompletion
|
|
16
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-VUAUAO2R.js";
|
|
17
21
|
import {
|
|
18
22
|
BACK,
|
|
19
23
|
applyDocsDirChange,
|
|
@@ -28,6 +32,8 @@ import {
|
|
|
28
32
|
run
|
|
29
33
|
} from "./chunk-CYQU33FY.js";
|
|
30
34
|
import {
|
|
35
|
+
claudeLocalSettingsPath,
|
|
36
|
+
claudeSettingsPath,
|
|
31
37
|
globalConfigPath,
|
|
32
38
|
localConfigPath,
|
|
33
39
|
mergeJson,
|
|
@@ -729,6 +735,62 @@ async function editShellCompletion() {
|
|
|
729
735
|
ensureShellCompletion({ silent: false });
|
|
730
736
|
}
|
|
731
737
|
}
|
|
738
|
+
async function editPermissions() {
|
|
739
|
+
const projectPath = claudeLocalSettingsPath();
|
|
740
|
+
const userPath = claudeSettingsPath();
|
|
741
|
+
const projectRules = getExistingRules(projectPath);
|
|
742
|
+
const userRules = getExistingRules(userPath);
|
|
743
|
+
const allRules = getAllRules();
|
|
744
|
+
const allRuleStrings = allRules.map((r) => r.rule);
|
|
745
|
+
const projectManaged = projectRules.filter(
|
|
746
|
+
(r) => allRuleStrings.includes(r)
|
|
747
|
+
).length;
|
|
748
|
+
const userManaged = userRules.filter(
|
|
749
|
+
(r) => allRuleStrings.includes(r)
|
|
750
|
+
).length;
|
|
751
|
+
console.log(
|
|
752
|
+
chalk.dim(
|
|
753
|
+
` Project: ${projectManaged}/${allRules.length} rules \xB7 User: ${userManaged}/${allRules.length} rules`
|
|
754
|
+
)
|
|
755
|
+
);
|
|
756
|
+
console.log();
|
|
757
|
+
const choice = await select({
|
|
758
|
+
message: "Permissions action:",
|
|
759
|
+
choices: injectBackChoice(
|
|
760
|
+
[
|
|
761
|
+
{
|
|
762
|
+
name: "Run full permission setup (interactive)",
|
|
763
|
+
value: "interactive",
|
|
764
|
+
description: " Opens `cf permission` \u2014 pick categories and rules"
|
|
765
|
+
},
|
|
766
|
+
{
|
|
767
|
+
name: "Apply all recommended (project scope)",
|
|
768
|
+
value: "all-project",
|
|
769
|
+
description: " Quick: adds all recommended rules to .claude/settings.local.json"
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
name: "Apply all recommended (user scope)",
|
|
773
|
+
value: "all-user",
|
|
774
|
+
description: " Quick: adds all recommended rules to ~/.claude/settings.json"
|
|
775
|
+
}
|
|
776
|
+
],
|
|
777
|
+
"Back"
|
|
778
|
+
)
|
|
779
|
+
});
|
|
780
|
+
if (choice === BACK) return;
|
|
781
|
+
const { permissionCommand } = await import("./permission-Z3LOBJ4X.js");
|
|
782
|
+
switch (choice) {
|
|
783
|
+
case "interactive":
|
|
784
|
+
await permissionCommand({});
|
|
785
|
+
break;
|
|
786
|
+
case "all-project":
|
|
787
|
+
await permissionCommand({ all: true, project: true });
|
|
788
|
+
break;
|
|
789
|
+
case "all-user":
|
|
790
|
+
await permissionCommand({ all: true, user: true });
|
|
791
|
+
break;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
732
794
|
var em = chalk.hex("#10b981");
|
|
733
795
|
async function configCommand() {
|
|
734
796
|
console.log();
|
|
@@ -752,6 +814,15 @@ async function configCommand() {
|
|
|
752
814
|
const memoryScope = getScopeLabel("memory", globalCfg, localCfg);
|
|
753
815
|
const statuslineStatus = isStatuslineConfigured() ? chalk.green("configured") : chalk.yellow("not configured");
|
|
754
816
|
const completionStatus = hasShellCompletion() ? chalk.green("installed") : chalk.yellow("not installed");
|
|
817
|
+
const projectRules = getExistingRules(claudeLocalSettingsPath());
|
|
818
|
+
const userRules = getExistingRules(claudeSettingsPath());
|
|
819
|
+
const allRules = getAllRules();
|
|
820
|
+
const allRuleStrings = allRules.map((r) => r.rule);
|
|
821
|
+
const configuredCount = (/* @__PURE__ */ new Set([
|
|
822
|
+
...projectRules.filter((r) => allRuleStrings.includes(r)),
|
|
823
|
+
...userRules.filter((r) => allRuleStrings.includes(r))
|
|
824
|
+
])).size;
|
|
825
|
+
const permissionStatus = configuredCount > 0 ? chalk.green(`${configuredCount}/${allRules.length}`) : chalk.yellow(`0/${allRules.length}`);
|
|
755
826
|
const choice = await select({
|
|
756
827
|
message: "What to configure?",
|
|
757
828
|
choices: injectBackChoice(
|
|
@@ -786,6 +857,11 @@ async function configCommand() {
|
|
|
786
857
|
value: "gitignore",
|
|
787
858
|
description: " Add or update coding-friend artifacts in .gitignore"
|
|
788
859
|
},
|
|
860
|
+
{
|
|
861
|
+
name: `Permissions (${permissionStatus} rules)`,
|
|
862
|
+
value: "permissions",
|
|
863
|
+
description: " Manage Claude Code permissions for Coding Friend skills and hooks"
|
|
864
|
+
},
|
|
789
865
|
{
|
|
790
866
|
name: `Shell completion (${completionStatus})`,
|
|
791
867
|
value: "completion",
|
|
@@ -817,6 +893,9 @@ async function configCommand() {
|
|
|
817
893
|
case "gitignore":
|
|
818
894
|
await editGitignore(globalCfg, localCfg);
|
|
819
895
|
break;
|
|
896
|
+
case "permissions":
|
|
897
|
+
await editPermissions();
|
|
898
|
+
break;
|
|
820
899
|
case "completion":
|
|
821
900
|
await editShellCompletion();
|
|
822
901
|
break;
|
package/dist/index.js
CHANGED
|
@@ -14,11 +14,11 @@ program.name("cf").description(
|
|
|
14
14
|
"coding-friend CLI \u2014 host learning docs, setup MCP, init projects"
|
|
15
15
|
).version(pkg.version, "-v, --version");
|
|
16
16
|
program.command("install").description("Install the Coding Friend plugin into Claude Code").option("--user", "Install at user scope (all projects)").option("--global", "Install at user scope (all projects)").option("--project", "Install at project scope (shared via git)").option("--local", "Install at local scope (this machine only)").action(async (opts) => {
|
|
17
|
-
const { installCommand } = await import("./install-
|
|
17
|
+
const { installCommand } = await import("./install-3QCRGPTY.js");
|
|
18
18
|
await installCommand(opts);
|
|
19
19
|
});
|
|
20
20
|
program.command("uninstall").description("Uninstall the Coding Friend plugin from Claude Code").option("--user", "Uninstall from user scope (all projects)").option("--global", "Uninstall from user scope (all projects)").option("--project", "Uninstall from project scope").option("--local", "Uninstall from local scope").action(async (opts) => {
|
|
21
|
-
const { uninstallCommand } = await import("./uninstall-
|
|
21
|
+
const { uninstallCommand } = await import("./uninstall-5LRHXFSF.js");
|
|
22
22
|
await uninstallCommand(opts);
|
|
23
23
|
});
|
|
24
24
|
program.command("disable").description("Disable the Coding Friend plugin without uninstalling").option("--user", "Disable at user scope (all projects)").option("--global", "Disable at user scope (all projects)").option("--project", "Disable at project scope").option("--local", "Disable at local scope").action(async (opts) => {
|
|
@@ -30,11 +30,11 @@ program.command("enable").description("Re-enable the Coding Friend plugin").opti
|
|
|
30
30
|
await enableCommand(opts);
|
|
31
31
|
});
|
|
32
32
|
program.command("init").description("Initialize coding-friend in current project").action(async () => {
|
|
33
|
-
const { initCommand } = await import("./init-
|
|
33
|
+
const { initCommand } = await import("./init-2HLPKYXB.js");
|
|
34
34
|
await initCommand();
|
|
35
35
|
});
|
|
36
36
|
program.command("config").description("Manage Coding Friend configuration").action(async () => {
|
|
37
|
-
const { configCommand } = await import("./config-
|
|
37
|
+
const { configCommand } = await import("./config-6SBGNTAQ.js");
|
|
38
38
|
await configCommand();
|
|
39
39
|
});
|
|
40
40
|
program.command("host").description("Build and serve learning docs as a static website").argument("[path]", "path to docs folder").option("-p, --port <port>", "port number", "3333").action(async (path, opts) => {
|
|
@@ -45,8 +45,11 @@ program.command("mcp").description("Setup MCP server for learning docs").argumen
|
|
|
45
45
|
const { mcpCommand } = await import("./mcp-TBEDYELW.js");
|
|
46
46
|
await mcpCommand(path);
|
|
47
47
|
});
|
|
48
|
-
program.command("permission").description("Manage Claude Code permission rules for Coding Friend").option("--all", "Apply all recommended permissions without prompts").
|
|
49
|
-
|
|
48
|
+
program.command("permission").description("Manage Claude Code permission rules for Coding Friend").option("--all", "Apply all recommended permissions without prompts").option("--user", "Save to user-level settings (~/.claude/settings.json)").option(
|
|
49
|
+
"--project",
|
|
50
|
+
"Save to project-level settings (.claude/settings.local.json)"
|
|
51
|
+
).action(async (opts) => {
|
|
52
|
+
const { permissionCommand } = await import("./permission-Z3LOBJ4X.js");
|
|
50
53
|
await permissionCommand(opts);
|
|
51
54
|
});
|
|
52
55
|
program.command("statusline").description("Setup coding-friend statusline in Claude Code").action(async () => {
|
|
@@ -54,7 +57,7 @@ program.command("statusline").description("Setup coding-friend statusline in Cla
|
|
|
54
57
|
await statuslineCommand();
|
|
55
58
|
});
|
|
56
59
|
program.command("update").description("Update coding-friend plugin, CLI, and statusline").option("--cli", "Update only the CLI (npm package)").option("--plugin", "Update only the Claude Code plugin").option("--statusline", "Update only the statusline").option("--user", "Update plugin at user scope (all projects)").option("--global", "Update plugin at user scope (all projects)").option("--project", "Update plugin at project scope").option("--local", "Update plugin at local scope").action(async (opts) => {
|
|
57
|
-
const { updateCommand } = await import("./update-
|
|
60
|
+
const { updateCommand } = await import("./update-4YUSCBCB.js");
|
|
58
61
|
await updateCommand(opts);
|
|
59
62
|
});
|
|
60
63
|
var session = program.command("session").description("Save and load Claude Code sessions across machines");
|
|
@@ -149,35 +152,35 @@ Dev subcommands:
|
|
|
149
152
|
dev update [path] Update local dev plugin to latest version`
|
|
150
153
|
);
|
|
151
154
|
dev.command("on").description("Switch to local plugin source").argument("[path]", "path to local coding-friend repo (default: cwd)").action(async (path) => {
|
|
152
|
-
const { devOnCommand } = await import("./dev-
|
|
155
|
+
const { devOnCommand } = await import("./dev-MC6TGHRT.js");
|
|
153
156
|
await devOnCommand(path);
|
|
154
157
|
});
|
|
155
158
|
dev.command("off").description("Switch back to remote marketplace").action(async () => {
|
|
156
|
-
const { devOffCommand } = await import("./dev-
|
|
159
|
+
const { devOffCommand } = await import("./dev-MC6TGHRT.js");
|
|
157
160
|
await devOffCommand();
|
|
158
161
|
});
|
|
159
162
|
dev.command("status").description("Show current dev mode").action(async () => {
|
|
160
|
-
const { devStatusCommand } = await import("./dev-
|
|
163
|
+
const { devStatusCommand } = await import("./dev-MC6TGHRT.js");
|
|
161
164
|
await devStatusCommand();
|
|
162
165
|
});
|
|
163
166
|
dev.command("sync").description(
|
|
164
167
|
"Copy local source files to plugin cache (no version bump needed)"
|
|
165
168
|
).action(async () => {
|
|
166
|
-
const { devSyncCommand } = await import("./dev-
|
|
169
|
+
const { devSyncCommand } = await import("./dev-MC6TGHRT.js");
|
|
167
170
|
await devSyncCommand();
|
|
168
171
|
});
|
|
169
172
|
dev.command("restart").description("Reinstall local dev plugin (off + on)").argument(
|
|
170
173
|
"[path]",
|
|
171
174
|
"path to local coding-friend repo (default: saved path or cwd)"
|
|
172
175
|
).action(async (path) => {
|
|
173
|
-
const { devRestartCommand } = await import("./dev-
|
|
176
|
+
const { devRestartCommand } = await import("./dev-MC6TGHRT.js");
|
|
174
177
|
await devRestartCommand(path);
|
|
175
178
|
});
|
|
176
179
|
dev.command("update").description("Update local dev plugin to latest version (off + on)").argument(
|
|
177
180
|
"[path]",
|
|
178
181
|
"path to local coding-friend repo (default: saved path or cwd)"
|
|
179
182
|
).action(async (path) => {
|
|
180
|
-
const { devUpdateCommand } = await import("./dev-
|
|
183
|
+
const { devUpdateCommand } = await import("./dev-MC6TGHRT.js");
|
|
181
184
|
await devUpdateCommand(path);
|
|
182
185
|
});
|
|
183
186
|
program.parse();
|