coding-friend-cli 1.17.2 → 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 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 # Manage Claude Code permission rules for Coding Friend
52
- cf permission --all # Apply all recommended permissions without prompts
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)
@@ -70,8 +73,8 @@ cf memory list --projects # List all project databases with size and metadata
70
73
  cf memory rm --project-id <id> # Remove a specific project database
71
74
  cf memory rm --all # Remove all project databases
72
75
  cf memory rm --prune # Remove orphaned projects (source dir missing or 0 memories)
73
- cf memory start # Start memory daemon (enables Tier 2 search)
74
- cf memory stop # Stop memory daemon
76
+ cf memory start-daemon # Start memory daemon (enables Tier 2 search)
77
+ cf memory stop-daemon # Stop memory daemon
75
78
  cf memory rebuild # Rebuild search index from markdown files
76
79
  cf memory init # Initialize SQLite backend (Tier 1) and import memories
77
80
  cf memory mcp # Show MCP server config for clients
@@ -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
+ };
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-ORACWEDN.js";
5
5
  import {
6
6
  ensureShellCompletion
7
- } from "./chunk-YO6JKGR3.js";
7
+ } from "./chunk-VUAUAO2R.js";
8
8
  import {
9
9
  resolveScope
10
10
  } from "./chunk-C5LYVVEI.js";
@@ -33,7 +33,7 @@ _cf_completions() {
33
33
 
34
34
  # Subcommands for 'memory'
35
35
  if [[ "\${COMP_WORDS[1]}" == "memory" && \${COMP_CWORD} -eq 2 ]]; then
36
- COMPREPLY=($(compgen -W "status search list rm init start stop rebuild mcp" -- "$cur"))
36
+ COMPREPLY=($(compgen -W "status search list rm init start-daemon stop-daemon rebuild mcp" -- "$cur"))
37
37
  return
38
38
  fi
39
39
 
@@ -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=(
@@ -129,8 +143,8 @@ var ZSH_FUNCTION_BODY = `_cf() {
129
143
  'list:List memories (--projects for all DBs)'
130
144
  'rm:Remove a project database'
131
145
  'init:Initialize Tier 1 (SQLite + Hybrid Search)'
132
- 'start:Start the memory daemon (Tier 2)'
133
- 'stop:Stop the memory daemon'
146
+ 'start-daemon:Start the memory daemon (Tier 2)'
147
+ 'stop-daemon:Stop the memory daemon'
134
148
  'rebuild:Rebuild the daemon search index'
135
149
  'mcp:Show MCP server setup instructions'
136
150
  )
@@ -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"
@@ -198,8 +216,8 @@ complete -c cf -n "__fish_seen_subcommand_from memory" -a search -d "Search memo
198
216
  complete -c cf -n "__fish_seen_subcommand_from memory" -a list -d "List memories (--projects for all DBs)"
199
217
  complete -c cf -n "__fish_seen_subcommand_from memory" -a rm -d "Remove a project database"
200
218
  complete -c cf -n "__fish_seen_subcommand_from memory" -a init -d "Initialize Tier 1 (SQLite + Hybrid Search)"
201
- complete -c cf -n "__fish_seen_subcommand_from memory" -a start -d "Start the memory daemon (Tier 2)"
202
- complete -c cf -n "__fish_seen_subcommand_from memory" -a stop -d "Stop the memory daemon"
219
+ complete -c cf -n "__fish_seen_subcommand_from memory" -a start-daemon -d "Start the memory daemon (Tier 2)"
220
+ complete -c cf -n "__fish_seen_subcommand_from memory" -a stop-daemon -d "Stop the memory daemon"
203
221
  complete -c cf -n "__fish_seen_subcommand_from memory" -a rebuild -d "Rebuild the daemon search index"
204
222
  complete -c cf -n "__fish_seen_subcommand_from memory" -a mcp -d "Show MCP server setup instructions"
205
223
  # Session subcommands
@@ -213,7 +231,7 @@ Register-ArgumentCompleter -Native -CommandName cf -ScriptBlock {
213
231
  param($wordToComplete, $commandAst, $cursorPosition)
214
232
  $commands = @('install','uninstall','disable','enable','init','config','host','mcp','memory','permission','statusline','update','dev','session')
215
233
  $devSubcommands = @('on','off','status','restart','sync','update')
216
- $memorySubcommands = @('status','search','list','rm','init','start','stop','rebuild','mcp')
234
+ $memorySubcommands = @('status','search','list','rm','init','start-daemon','stop-daemon','rebuild','mcp')
217
235
  $sessionSubcommands = @('save','load')
218
236
  $scopeFlags = @('--user','--global','--project','--local')
219
237
  $updateFlags = @('--cli','--plugin','--statusline','--user','--global','--project','--local')
@@ -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-YO6JKGR3.js";
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;
@@ -8,7 +8,7 @@ import {
8
8
  import "./chunk-POC2WHU2.js";
9
9
  import {
10
10
  ensureShellCompletion
11
- } from "./chunk-YO6JKGR3.js";
11
+ } from "./chunk-VUAUAO2R.js";
12
12
  import {
13
13
  commandExists,
14
14
  run