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 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)
@@ -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-DVMWMXDZ.js";
7
+ } from "./chunk-VUAUAO2R.js";
8
8
  import {
9
9
  resolveScope
10
10
  } from "./chunk-C5LYVVEI.js";
@@ -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-DVMWMXDZ.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-DVMWMXDZ.js";
11
+ } from "./chunk-VUAUAO2R.js";
12
12
  import {
13
13
  commandExists,
14
14
  run
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-USFLRCS5.js");
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-QSNKGNHR.js");
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-73ECEDU7.js");
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-HVWEV2K6.js");
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").action(async (opts) => {
49
- const { permissionCommand } = await import("./permission-L2QQR5PO.js");
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-PNHTIB6M.js");
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-AZSOM775.js");
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-AZSOM775.js");
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-AZSOM775.js");
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-AZSOM775.js");
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-AZSOM775.js");
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-AZSOM775.js");
183
+ const { devUpdateCommand } = await import("./dev-MC6TGHRT.js");
181
184
  await devUpdateCommand(path);
182
185
  });
183
186
  program.parse();