coding-friend-cli 1.35.7 → 1.36.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.
Files changed (40) hide show
  1. package/README.md +5 -3
  2. package/dist/{chunk-RLUNNJWJ.js → chunk-667NMPN4.js} +47 -4
  3. package/dist/{chunk-DTSWPWPA.js → chunk-7QFJODWB.js} +14 -9
  4. package/dist/{chunk-UCYGDDFS.js → chunk-7SMP5CZ4.js} +6 -6
  5. package/dist/{chunk-B2NFXRFP.js → chunk-DUKBBMVM.js} +11 -18
  6. package/dist/{chunk-TAPL6NF3.js → chunk-EFRPDFO7.js} +4 -4
  7. package/dist/{chunk-YBK44KZM.js → chunk-GHR3TNL6.js} +8 -8
  8. package/dist/{chunk-R6HDMRYL.js → chunk-GZT3YPAP.js} +5 -5
  9. package/dist/{chunk-UIPLDRGI.js → chunk-KIKN5HEH.js} +6 -6
  10. package/dist/{chunk-O27FVQFU.js → chunk-O5ZKLFSL.js} +6 -4
  11. package/dist/{chunk-XMSSEJYL.js → chunk-PQUACN4U.js} +1 -1
  12. package/dist/{chunk-WSUYLIYX.js → chunk-TEC25H6C.js} +3 -3
  13. package/dist/{chunk-TWKNGPBO.js → chunk-X2H7JJMR.js} +15 -10
  14. package/dist/{clean-VFWQ4AJP.js → clean-2IRHH4SU.js} +24 -25
  15. package/dist/{config-Z3GE6HAP.js → config-36YY3XLM.js} +41 -17
  16. package/dist/{dev-DWIZTH25.js → dev-JSYPSXEZ.js} +81 -19
  17. package/dist/{disable-5UK4QINT.js → disable-35RSJICU.js} +3 -3
  18. package/dist/{enable-UQZNZRPI.js → enable-RXWDQRIU.js} +3 -3
  19. package/dist/{guide-LQ2BHU5Y.js → guide-ECSMH2AT.js} +4 -4
  20. package/dist/{host-HKA4OHBM.js → host-BYJEZN6T.js} +10 -5
  21. package/dist/index.js +55 -37
  22. package/dist/{init-MFUX5INO.js → init-QXCMAPXT.js} +19 -19
  23. package/dist/{install-VFD6C3LZ.js → install-NALHBTGG.js} +8 -8
  24. package/dist/learn-PNG2TI2E.js +111 -0
  25. package/dist/{mcp-AG7J6CEP.js → mcp-6GRHKIQW.js} +9 -9
  26. package/dist/{mcp-serve-learn-OKVC5WV3.js → mcp-serve-learn-36ZQQQTF.js} +3 -3
  27. package/dist/{memory-RZR672Q7.js → memory-A7OUTQ54.js} +6 -6
  28. package/dist/{permission-KLT2TLZY.js → permission-PB3OMKRB.js} +9 -9
  29. package/dist/{permissions-LYADLYYK.js → permissions-J46VP6US.js} +2 -1
  30. package/dist/postinstall.js +1 -1
  31. package/dist/{session-B3B3CSRF.js → session-YJJRGQPX.js} +6 -6
  32. package/dist/{status-DDNQ5QNJ.js → status-SG5LCXER.js} +16 -16
  33. package/dist/{statusline-EYTWM5X6.js → statusline-3Z4DE24F.js} +2 -2
  34. package/dist/{uninstall-MLSKOCOZ.js → uninstall-JLSEHXS5.js} +11 -11
  35. package/dist/{update-G5YXZRMQ.js → update-MO5BT3C5.js} +5 -5
  36. package/dist/{update-check-BPCQ25AR.js → update-check-I2RSVGFO.js} +3 -3
  37. package/lib/learn-host/README.md +8 -9
  38. package/lib/learn-host/src/components/Breadcrumbs.tsx +30 -19
  39. package/package.json +1 -1
  40. package/lib/learn-host/tsconfig.tsbuildinfo +0 -1
package/README.md CHANGED
@@ -78,10 +78,12 @@ cf init # Initialize workspace (interactive)
78
78
  # 💡 You can run this anywhere, anytime.
79
79
  cf config # Manage Coding Friend configuration (interactive menu)
80
80
  # 💡 Edit docsDir, language, learn settings, and more.
81
- cf host [path] # Build and serve learning docs at localhost:3333
82
- # [path] is optional, default is `docs/learn`
81
+ cf learn host [path] # Build and serve learning docs at localhost:3333
82
+ # [path] is optional, default is `~/.coding-friend/learn`
83
+ cf learn push [path] # Commit and push learning docs (only if learn dir is the git root)
84
+ # [path] is optional, default is `~/.coding-friend/learn`
83
85
  cf mcp [path] # Setup MCP server for LLM integration
84
- # [path] is optional, default is `docs/learn`
86
+ # [path] is optional, default is `~/.coding-friend/learn`
85
87
  # This prints a JSON config snippet to add to your client's MCP
86
88
  cf permission # Manage Claude Code permission rules (interactive)
87
89
  cf permission --all # Apply all recommended permissions without prompts
@@ -21,7 +21,7 @@ ${MARKER_START}
21
21
  _cf_completions() {
22
22
  local cur="\${COMP_WORDS[COMP_CWORD]}"
23
23
  local prev="\${COMP_WORDS[COMP_CWORD-1]}"
24
- local commands="install uninstall disable enable init config host mcp memory permission statusline update status clean dev session"
24
+ local commands="install uninstall disable enable init config learn mcp memory permission statusline update status clean dev session guide"
25
25
  local scope_flags="--user --global --project --local"
26
26
  local update_flags="--cli --plugin --statusline --user --global --project --local"
27
27
 
@@ -43,6 +43,18 @@ _cf_completions() {
43
43
  return
44
44
  fi
45
45
 
46
+ # Subcommands for 'guide'
47
+ if [[ "\${COMP_WORDS[1]}" == "guide" && \${COMP_CWORD} -eq 2 ]]; then
48
+ COMPREPLY=($(compgen -W "create list" -- "$cur"))
49
+ return
50
+ fi
51
+
52
+ # Subcommands for 'learn'
53
+ if [[ "\${COMP_WORDS[1]}" == "learn" && \${COMP_CWORD} -eq 2 ]]; then
54
+ COMPREPLY=($(compgen -W "host push" -- "$cur"))
55
+ return
56
+ fi
57
+
46
58
  # Path completion for 'dev on|restart|update'
47
59
  if [[ "\${COMP_WORDS[1]}" == "dev" && ("$prev" == "on" || "$prev" == "restart" || "$prev" == "update") ]]; then
48
60
  COMPREPLY=($(compgen -d -- "$cur"))
@@ -81,7 +93,7 @@ var ZSH_FUNCTION_BODY = `_cf() {
81
93
  'enable:Re-enable the Coding Friend plugin'
82
94
  'init:Initialize coding-friend in current project'
83
95
  'config:Manage Coding Friend configuration'
84
- 'host:Build and serve learning docs as a static website'
96
+ 'learn:Manage learning docs \u2014 host as a website or push to git'
85
97
  'mcp:Setup MCP server for learning docs'
86
98
  'memory:AI memory system \u2014 store and search project knowledge'
87
99
  'permission:Manage Claude Code permission rules for Coding Friend'
@@ -91,6 +103,7 @@ var ZSH_FUNCTION_BODY = `_cf() {
91
103
  'status:Show comprehensive Coding Friend status'
92
104
  'clean:Clean files generated by Coding Friend in docs/'
93
105
  'session:Save and load Claude Code sessions across machines'
106
+ 'guide:Manage custom skill guides'
94
107
  )
95
108
 
96
109
  local -a scope_flags
@@ -159,6 +172,20 @@ var ZSH_FUNCTION_BODY = `_cf() {
159
172
  'load:Load a saved session from docs/sessions/'
160
173
  )
161
174
  _describe 'subcommand' subcommands
175
+ elif (( CURRENT == 3 )) && [[ "\${words[2]}" == "guide" ]]; then
176
+ local -a subcommands
177
+ subcommands=(
178
+ 'create:Create a custom guide for a skill'
179
+ 'list:List existing custom guides'
180
+ )
181
+ _describe 'subcommand' subcommands
182
+ elif (( CURRENT == 3 )) && [[ "\${words[2]}" == "learn" ]]; then
183
+ local -a subcommands
184
+ subcommands=(
185
+ 'host:Build and serve learning docs as a static website'
186
+ 'push:Commit and push learning docs to origin'
187
+ )
188
+ _describe 'subcommand' subcommands
162
189
  elif (( CURRENT == 4 )) && [[ "\${words[2]}" == "dev" && ("\${words[3]}" == "on" || "\${words[3]}" == "restart" || "\${words[3]}" == "update") ]]; then
163
190
  _path_files -/
164
191
  fi
@@ -181,7 +208,7 @@ complete -c cf -n "__fish_use_subcommand" -a disable -d "Disable the Coding Frie
181
208
  complete -c cf -n "__fish_use_subcommand" -a enable -d "Re-enable the Coding Friend plugin"
182
209
  complete -c cf -n "__fish_use_subcommand" -a init -d "Initialize coding-friend in current project"
183
210
  complete -c cf -n "__fish_use_subcommand" -a config -d "Manage Coding Friend configuration"
184
- complete -c cf -n "__fish_use_subcommand" -a host -d "Build and serve learning docs as a static website"
211
+ complete -c cf -n "__fish_use_subcommand" -a learn -d "Manage learning docs \u2014 host as a website or push to git"
185
212
  complete -c cf -n "__fish_use_subcommand" -a mcp -d "Setup MCP server for learning docs"
186
213
  complete -c cf -n "__fish_use_subcommand" -a memory -d "AI memory system \u2014 store and search project knowledge"
187
214
  complete -c cf -n "__fish_use_subcommand" -a permission -d "Manage Claude Code permission rules"
@@ -189,7 +216,9 @@ complete -c cf -n "__fish_use_subcommand" -a statusline -d "Setup coding-friend
189
216
  complete -c cf -n "__fish_use_subcommand" -a update -d "Update coding-friend plugin and refresh statusline"
190
217
  complete -c cf -n "__fish_use_subcommand" -a dev -d "Switch between local and remote plugin for development"
191
218
  complete -c cf -n "__fish_use_subcommand" -a status -d "Show comprehensive Coding Friend status"
219
+ complete -c cf -n "__fish_use_subcommand" -a clean -d "Clean files generated by Coding Friend in docs/"
192
220
  complete -c cf -n "__fish_use_subcommand" -a session -d "Save and load Claude Code sessions across machines"
221
+ complete -c cf -n "__fish_use_subcommand" -a guide -d "Manage custom skill guides"
193
222
  # Scope flags for install/uninstall/disable/enable
194
223
  complete -c cf -n "__fish_seen_subcommand_from install uninstall disable enable" -l user -d "User scope (all projects)"
195
224
  complete -c cf -n "__fish_seen_subcommand_from install uninstall disable enable" -l global -d "User scope (all projects)"
@@ -228,16 +257,24 @@ complete -c cf -n "__fish_seen_subcommand_from memory" -a mcp -d "Show MCP serve
228
257
  # Session subcommands
229
258
  complete -c cf -n "__fish_seen_subcommand_from session" -a save -d "Save current session to docs/sessions/"
230
259
  complete -c cf -n "__fish_seen_subcommand_from session" -a load -d "Load a saved session from docs/sessions/"
260
+ # Guide subcommands
261
+ complete -c cf -n "__fish_seen_subcommand_from guide" -a create -d "Create a custom guide for a skill"
262
+ complete -c cf -n "__fish_seen_subcommand_from guide" -a list -d "List existing custom guides"
263
+ # Learn subcommands
264
+ complete -c cf -n "__fish_seen_subcommand_from learn" -a host -d "Build and serve learning docs as a static website"
265
+ complete -c cf -n "__fish_seen_subcommand_from learn" -a push -d "Commit and push learning docs to origin"
231
266
  `;
232
267
  var POWERSHELL_BLOCK = `
233
268
 
234
269
  ${MARKER_START}
235
270
  Register-ArgumentCompleter -Native -CommandName cf -ScriptBlock {
236
271
  param($wordToComplete, $commandAst, $cursorPosition)
237
- $commands = @('install','uninstall','disable','enable','init','config','host','mcp','memory','permission','statusline','update','status','dev','session')
272
+ $commands = @('install','uninstall','disable','enable','init','config','learn','mcp','memory','permission','statusline','update','status','clean','dev','session','guide')
238
273
  $devSubcommands = @('on','off','status','restart','sync','update')
239
274
  $memorySubcommands = @('status','search','list','rm','init','config','start-daemon','stop-daemon','rebuild','mcp')
240
275
  $sessionSubcommands = @('save','load')
276
+ $guideSubcommands = @('create','list')
277
+ $learnSubcommands = @('host','push')
241
278
  $scopeFlags = @('--user','--global','--project','--local')
242
279
  $updateFlags = @('--cli','--plugin','--statusline','--user','--global','--project','--local')
243
280
  $words = $commandAst.CommandElements
@@ -250,6 +287,12 @@ Register-ArgumentCompleter -Native -CommandName cf -ScriptBlock {
250
287
  } elseif ($words.Count -ge 2 -and $words[1].ToString() -eq 'session') {
251
288
  $sessionSubcommands | Where-Object { $_ -like "$wordToComplete*" } |
252
289
  ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
290
+ } elseif ($words.Count -ge 2 -and $words[1].ToString() -eq 'guide') {
291
+ $guideSubcommands | Where-Object { $_ -like "$wordToComplete*" } |
292
+ ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
293
+ } elseif ($words.Count -ge 2 -and $words[1].ToString() -eq 'learn') {
294
+ $learnSubcommands | Where-Object { $_ -like "$wordToComplete*" } |
295
+ ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
253
296
  } 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')) {
254
297
  $scopeFlags | Where-Object { $_ -like "$wordToComplete*" } |
255
298
  ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveMemoryDir
3
- } from "./chunk-B2NFXRFP.js";
3
+ } from "./chunk-DUKBBMVM.js";
4
4
  import {
5
5
  getLibPath
6
6
  } from "./chunk-RZRT7NGT.js";
@@ -9,11 +9,7 @@ import {
9
9
  askScope,
10
10
  formatScopeLabel,
11
11
  injectBackChoice
12
- } from "./chunk-TAPL6NF3.js";
13
- import {
14
- globalConfigPath,
15
- localConfigPath
16
- } from "./chunk-TWKNGPBO.js";
12
+ } from "./chunk-EFRPDFO7.js";
17
13
  import {
18
14
  log
19
15
  } from "./chunk-NREZK463.js";
@@ -22,11 +18,16 @@ import {
22
18
  readJson,
23
19
  writeJson
24
20
  } from "./chunk-5UVDWG5L.js";
21
+ import {
22
+ claudeConfigDir,
23
+ globalConfigPath,
24
+ localConfigPath
25
+ } from "./chunk-X2H7JJMR.js";
25
26
 
26
27
  // src/lib/memory-prompts.ts
27
- import { homedir } from "os";
28
28
  import { confirm, input, select } from "@inquirer/prompts";
29
29
  import chalk from "chalk";
30
+ import { homedir } from "os";
30
31
  import { join } from "path";
31
32
  function getMemoryFieldScope(field, globalCfg, localCfg) {
32
33
  const localSection = localCfg?.memory;
@@ -238,7 +239,7 @@ function getMemoryMcpStatus() {
238
239
  if (localServers != null && "coding-friend-memory" in localServers) {
239
240
  return { configured: true, scope: "local" };
240
241
  }
241
- const globalMcpPath = join(homedir(), ".claude", ".mcp.json");
242
+ const globalMcpPath = join(claudeConfigDir(), ".mcp.json");
242
243
  const globalMcp = readJson(globalMcpPath);
243
244
  const globalServers = globalMcp?.mcpServers;
244
245
  if (globalServers != null && "coding-friend-memory" in globalServers) {
@@ -249,7 +250,11 @@ function getMemoryMcpStatus() {
249
250
  async function editMemoryMcp() {
250
251
  const status = getMemoryMcpStatus();
251
252
  if (status.configured) {
252
- const label = status.scope === "local" ? chalk.green("configured") + chalk.dim(" (local .mcp.json)") : chalk.green("configured") + chalk.dim(" (global ~/.claude/.mcp.json)") + " " + chalk.yellow("\u26A0 only works for one project");
253
+ const globalMcpDisplay = join(claudeConfigDir(), ".mcp.json").replace(
254
+ homedir(),
255
+ "~"
256
+ );
257
+ const label = status.scope === "local" ? chalk.green("configured") + chalk.dim(" (local .mcp.json)") : chalk.green("configured") + chalk.dim(` (global ${globalMcpDisplay})`) + " " + chalk.yellow("\u26A0 only works for one project");
253
258
  log.info(`MCP: ${label}`);
254
259
  const reconfigure = await confirm({
255
260
  message: "Reconfigure Memory MCP in local .mcp.json?",
@@ -2,17 +2,17 @@ import {
2
2
  ALL_COMPONENT_IDS,
3
3
  STATUSLINE_COMPONENTS
4
4
  } from "./chunk-57EGAXYI.js";
5
- import {
6
- claudeSettingsPath,
7
- globalConfigPath,
8
- installedPluginsPath,
9
- pluginCachePath
10
- } from "./chunk-TWKNGPBO.js";
11
5
  import {
12
6
  mergeJson,
13
7
  readJson,
14
8
  writeJson
15
9
  } from "./chunk-5UVDWG5L.js";
10
+ import {
11
+ claudeSettingsPath,
12
+ globalConfigPath,
13
+ installedPluginsPath,
14
+ pluginCachePath
15
+ } from "./chunk-X2H7JJMR.js";
16
16
 
17
17
  // src/lib/statusline.ts
18
18
  import { existsSync, readdirSync } from "fs";
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  DEFAULT_CONFIG
3
3
  } from "./chunk-57EGAXYI.js";
4
- import {
5
- globalConfigPath,
6
- localConfigPath,
7
- resolvePath
8
- } from "./chunk-TWKNGPBO.js";
9
4
  import {
10
5
  log
11
6
  } from "./chunk-NREZK463.js";
12
7
  import {
13
8
  readJson
14
9
  } from "./chunk-5UVDWG5L.js";
10
+ import {
11
+ globalConfigPath,
12
+ localConfigPath,
13
+ resolvePath
14
+ } from "./chunk-X2H7JJMR.js";
15
15
 
16
16
  // src/lib/config.ts
17
17
  import { z } from "zod";
@@ -67,6 +67,9 @@ var MemoryConfigSchema = z.object({
67
67
  autoCapture: z.boolean().optional(),
68
68
  autoStart: z.boolean().optional()
69
69
  });
70
+ var ReviewConfigSchema = z.strictObject({
71
+ withCodex: z.boolean().optional()
72
+ });
70
73
  var ConfigSchema = z.strictObject({
71
74
  language: z.string().optional(),
72
75
  docsDir: z.string().optional(),
@@ -74,6 +77,7 @@ var ConfigSchema = z.strictObject({
74
77
  learn: LearnConfigSchema.optional(),
75
78
  statusline: StatuslineConfigSchema.optional(),
76
79
  memory: MemoryConfigSchema.optional(),
80
+ review: ReviewConfigSchema.optional(),
77
81
  autoApprove: z.boolean().optional(),
78
82
  autoApproveIgnore: z.array(z.string()).optional(),
79
83
  autoApproveAllowExtra: z.array(z.string()).optional()
@@ -85,6 +89,7 @@ var KNOWN_KEYS = [
85
89
  "learn",
86
90
  "statusline",
87
91
  "memory",
92
+ "review",
88
93
  "autoApprove",
89
94
  "autoApproveIgnore",
90
95
  "autoApproveAllowExtra"
@@ -184,21 +189,10 @@ function loadConfig() {
184
189
  const merged = deepMerge(base, local ?? {});
185
190
  return validateConfig(merged);
186
191
  }
187
- function resolveDocsDir(explicitPath) {
192
+ function resolveLearnDir(globalCfg, explicitPath) {
188
193
  if (explicitPath) {
189
194
  return resolvePath(explicitPath);
190
195
  }
191
- const local = readJson(localConfigPath());
192
- if (local?.learn?.outputDir) {
193
- return resolvePath(local.learn.outputDir);
194
- }
195
- const global = readJson(globalConfigPath());
196
- if (global?.learn?.outputDir) {
197
- return resolvePath(global.learn.outputDir);
198
- }
199
- return resolvePath("docs/learn");
200
- }
201
- function resolveLearnDir(globalCfg) {
202
196
  if (globalCfg?.learn?.outputDir) {
203
197
  return resolvePath(globalCfg.learn.outputDir);
204
198
  }
@@ -218,7 +212,6 @@ function resolveMemoryDir(explicitPath) {
218
212
  export {
219
213
  sanitizeRawConfig,
220
214
  loadConfig,
221
- resolveDocsDir,
222
215
  resolveLearnDir,
223
216
  resolveMemoryDir
224
217
  };
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  run
3
3
  } from "./chunk-EVGXUDX4.js";
4
- import {
5
- globalConfigPath,
6
- localConfigPath
7
- } from "./chunk-TWKNGPBO.js";
8
4
  import {
9
5
  log
10
6
  } from "./chunk-NREZK463.js";
11
7
  import {
12
8
  readJson
13
9
  } from "./chunk-5UVDWG5L.js";
10
+ import {
11
+ globalConfigPath,
12
+ localConfigPath
13
+ } from "./chunk-X2H7JJMR.js";
14
14
 
15
15
  // src/lib/prompt-utils.ts
16
16
  import { select, Separator } from "@inquirer/prompts";
@@ -2,30 +2,26 @@ import {
2
2
  checkMemoryMcpHealth,
3
3
  printHealthSection,
4
4
  warnStaleMcpJson
5
- } from "./chunk-XMSSEJYL.js";
5
+ } from "./chunk-PQUACN4U.js";
6
6
  import {
7
7
  getMemoryMcpStatus,
8
8
  memoryConfigMenu,
9
9
  writeMemoryMcpEntry
10
- } from "./chunk-DTSWPWPA.js";
10
+ } from "./chunk-7QFJODWB.js";
11
11
  import {
12
12
  loadConfig,
13
13
  resolveMemoryDir
14
- } from "./chunk-B2NFXRFP.js";
14
+ } from "./chunk-DUKBBMVM.js";
15
15
  import {
16
16
  getLibPath
17
17
  } from "./chunk-RZRT7NGT.js";
18
18
  import {
19
19
  showConfigHint
20
- } from "./chunk-TAPL6NF3.js";
20
+ } from "./chunk-EFRPDFO7.js";
21
21
  import {
22
22
  run,
23
23
  runWithStderr
24
24
  } from "./chunk-EVGXUDX4.js";
25
- import {
26
- globalConfigPath,
27
- localConfigPath
28
- } from "./chunk-TWKNGPBO.js";
29
25
  import {
30
26
  log,
31
27
  printBanner
@@ -34,6 +30,10 @@ import {
34
30
  mergeJson,
35
31
  readJson
36
32
  } from "./chunk-5UVDWG5L.js";
33
+ import {
34
+ globalConfigPath,
35
+ localConfigPath
36
+ } from "./chunk-X2H7JJMR.js";
37
37
 
38
38
  // src/commands/memory.ts
39
39
  import { existsSync, readdirSync, statSync, rmSync } from "fs";
@@ -1,14 +1,14 @@
1
+ import {
2
+ readJson,
3
+ writeJson
4
+ } from "./chunk-5UVDWG5L.js";
1
5
  import {
2
6
  claudeLocalSettingsPath,
3
7
  claudeProjectSettingsPath,
4
8
  claudeSettingsPath,
5
9
  installedPluginsPath,
6
10
  knownMarketplacesPath
7
- } from "./chunk-TWKNGPBO.js";
8
- import {
9
- readJson,
10
- writeJson
11
- } from "./chunk-5UVDWG5L.js";
11
+ } from "./chunk-X2H7JJMR.js";
12
12
 
13
13
  // src/lib/plugin-state.ts
14
14
  var MARKETPLACE_NAME = "coding-friend-marketplace";
@@ -1,22 +1,19 @@
1
1
  import {
2
2
  ensureStatusline,
3
3
  getInstalledVersion
4
- } from "./chunk-UCYGDDFS.js";
4
+ } from "./chunk-7SMP5CZ4.js";
5
5
  import {
6
6
  ensureShellCompletion
7
- } from "./chunk-RLUNNJWJ.js";
7
+ } from "./chunk-667NMPN4.js";
8
8
  import {
9
9
  resolveScope
10
- } from "./chunk-TAPL6NF3.js";
10
+ } from "./chunk-EFRPDFO7.js";
11
11
  import {
12
12
  commandExists,
13
13
  run,
14
14
  runWithStderr,
15
15
  sleepSync
16
16
  } from "./chunk-EVGXUDX4.js";
17
- import {
18
- claudeSettingsPath
19
- } from "./chunk-TWKNGPBO.js";
20
17
  import {
21
18
  log,
22
19
  printBanner
@@ -24,6 +21,9 @@ import {
24
21
  import {
25
22
  readJson
26
23
  } from "./chunk-5UVDWG5L.js";
24
+ import {
25
+ claudeSettingsPath
26
+ } from "./chunk-X2H7JJMR.js";
27
27
 
28
28
  // src/commands/update.ts
29
29
  import { readFileSync } from "fs";
@@ -2,9 +2,11 @@ import {
2
2
  readJson,
3
3
  writeJson
4
4
  } from "./chunk-5UVDWG5L.js";
5
+ import {
6
+ pluginCachePath
7
+ } from "./chunk-X2H7JJMR.js";
5
8
 
6
9
  // src/lib/permissions.ts
7
- import { homedir } from "os";
8
10
  var STATIC_RULES = [
9
11
  // Core Utilities (hooks & infrastructure)
10
12
  {
@@ -255,9 +257,9 @@ var STATIC_RULES = [
255
257
  recommended: true
256
258
  }
257
259
  ];
258
- var PLUGIN_CACHE_TILDE = "~/.claude/plugins/cache/coding-friend-marketplace/coding-friend";
259
260
  function buildPluginScriptRules() {
260
- const absBase = `${homedir()}/.claude/plugins/cache/coding-friend-marketplace/coding-friend`;
261
+ const absBase = pluginCachePath();
262
+ const readBase = process.env.CLAUDE_CONFIG_DIR?.trim() ? absBase : "~/.claude/plugins/cache/coding-friend-marketplace/coding-friend";
261
263
  return [
262
264
  {
263
265
  rule: `Bash(bash ${absBase}/*)`,
@@ -272,7 +274,7 @@ function buildPluginScriptRules() {
272
274
  recommended: true
273
275
  },
274
276
  {
275
- rule: `Read(${PLUGIN_CACHE_TILDE}/**)`,
277
+ rule: `Read(${readBase}/**)`,
276
278
  description: "[read-only] Read plugin files \xB7 Used by: hooks, agents",
277
279
  category: "Plugin Scripts",
278
280
  recommended: true
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  writeMemoryMcpEntry
3
- } from "./chunk-DTSWPWPA.js";
3
+ } from "./chunk-7QFJODWB.js";
4
4
  import {
5
5
  log
6
6
  } from "./chunk-NREZK463.js";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  runWithStderr
3
3
  } from "./chunk-EVGXUDX4.js";
4
- import {
5
- resolvePath
6
- } from "./chunk-TWKNGPBO.js";
7
4
  import {
8
5
  log
9
6
  } from "./chunk-NREZK463.js";
7
+ import {
8
+ resolvePath
9
+ } from "./chunk-X2H7JJMR.js";
10
10
 
11
11
  // src/lib/learn-prompts.ts
12
12
  var MCP_NAME = "coding-friend-learn";
@@ -12,8 +12,15 @@ function localConfigPath() {
12
12
  function globalConfigPath() {
13
13
  return join(homedir(), ".coding-friend", "config.json");
14
14
  }
15
+ function claudeConfigDir() {
16
+ const env = process.env.CLAUDE_CONFIG_DIR?.trim();
17
+ if (!env) return join(homedir(), ".claude");
18
+ if (env === "~") return homedir();
19
+ if (env.startsWith("~/")) return join(homedir(), env.slice(2));
20
+ return env;
21
+ }
15
22
  function claudeSettingsPath() {
16
- return join(homedir(), ".claude", "settings.json");
23
+ return join(claudeConfigDir(), "settings.json");
17
24
  }
18
25
  function claudeProjectSettingsPath() {
19
26
  return resolve(process.cwd(), ".claude", "settings.json");
@@ -22,12 +29,11 @@ function claudeLocalSettingsPath() {
22
29
  return resolve(process.cwd(), ".claude", "settings.local.json");
23
30
  }
24
31
  function installedPluginsPath() {
25
- return join(homedir(), ".claude", "plugins", "installed_plugins.json");
32
+ return join(claudeConfigDir(), "plugins", "installed_plugins.json");
26
33
  }
27
34
  function pluginCachePath() {
28
35
  return join(
29
- homedir(),
30
- ".claude",
36
+ claudeConfigDir(),
31
37
  "plugins",
32
38
  "cache",
33
39
  "coding-friend-marketplace",
@@ -38,12 +44,11 @@ function devStatePath() {
38
44
  return join(homedir(), ".coding-friend", "dev-state.json");
39
45
  }
40
46
  function knownMarketplacesPath() {
41
- return join(homedir(), ".claude", "plugins", "known_marketplaces.json");
47
+ return join(claudeConfigDir(), "plugins", "known_marketplaces.json");
42
48
  }
43
49
  function marketplaceCachePath() {
44
50
  return join(
45
- homedir(),
46
- ".claude",
51
+ claudeConfigDir(),
47
52
  "plugins",
48
53
  "cache",
49
54
  "coding-friend-marketplace"
@@ -51,8 +56,7 @@ function marketplaceCachePath() {
51
56
  }
52
57
  function marketplaceClonePath() {
53
58
  return join(
54
- homedir(),
55
- ".claude",
59
+ claudeConfigDir(),
56
60
  "plugins",
57
61
  "marketplaces",
58
62
  "coding-friend-marketplace"
@@ -68,7 +72,7 @@ function encodeProjectPath(absolutePath) {
68
72
  return absolutePath.replace(/[\\/:]/g, "-");
69
73
  }
70
74
  function claudeProjectsDir() {
71
- return join(homedir(), ".claude", "projects");
75
+ return join(claudeConfigDir(), "projects");
72
76
  }
73
77
  function claudeSessionDir(encodedPath) {
74
78
  return join(claudeProjectsDir(), encodedPath);
@@ -78,6 +82,7 @@ export {
78
82
  resolvePath,
79
83
  localConfigPath,
80
84
  globalConfigPath,
85
+ claudeConfigDir,
81
86
  claudeSettingsPath,
82
87
  claudeProjectSettingsPath,
83
88
  claudeLocalSettingsPath,
@@ -1,21 +1,21 @@
1
1
  import {
2
2
  loadConfig,
3
3
  resolveMemoryDir
4
- } from "./chunk-B2NFXRFP.js";
4
+ } from "./chunk-DUKBBMVM.js";
5
5
  import "./chunk-57EGAXYI.js";
6
- import {
7
- resolvePath
8
- } from "./chunk-TWKNGPBO.js";
9
6
  import {
10
7
  log,
11
8
  printBanner
12
9
  } from "./chunk-NREZK463.js";
13
10
  import "./chunk-5UVDWG5L.js";
11
+ import {
12
+ resolvePath
13
+ } from "./chunk-X2H7JJMR.js";
14
14
 
15
15
  // src/commands/clean.ts
16
16
  import { existsSync, readdirSync, rmSync, statSync } from "fs";
17
17
  import { join, relative } from "path";
18
- import { checkbox, confirm, input, select } from "@inquirer/prompts";
18
+ import { confirm, input, select } from "@inquirer/prompts";
19
19
  import chalk from "chalk";
20
20
  var DATE_PREFIX_RE = /^(\d{4}-\d{2}-\d{2})/;
21
21
  function parseDateFromName(name) {
@@ -122,27 +122,25 @@ async function cleanCommand() {
122
122
  { key: "sessions", path: join(docsDir, "sessions") },
123
123
  { key: "learn", path: join(docsDir, "learn") }
124
124
  ];
125
- const cleanable = candidates.map((c) => ({ ...c, fileCount: countFiles(c.path) })).filter((c) => existsSync(c.path) && c.fileCount > 0);
126
- if (cleanable.length === 0) {
127
- log.info("Nothing to clean.");
128
- console.log();
129
- return;
130
- }
131
- const selected = await checkbox({
132
- message: "Select directories to clean:",
133
- choices: cleanable.map((c) => ({
134
- name: `${c.key} \u2014 ${c.fileCount} ${c.fileCount === 1 ? "file" : "files"}`,
135
- value: c.key
136
- }))
137
- });
138
- if (selected.length === 0) {
139
- log.dim(" Nothing selected.");
140
- console.log();
141
- return;
142
- }
143
- const now = /* @__PURE__ */ new Date();
144
125
  const summary = [];
145
- for (const entry of cleanable.filter((c) => selected.includes(c.key))) {
126
+ while (true) {
127
+ const cleanable = candidates.map((c) => ({ ...c, fileCount: countFiles(c.path) })).filter((c) => existsSync(c.path) && c.fileCount > 0);
128
+ if (cleanable.length === 0) {
129
+ log.info("Nothing to clean.");
130
+ break;
131
+ }
132
+ const selected = await select({
133
+ message: "Select a directory to clean:",
134
+ choices: [
135
+ ...cleanable.map((c) => ({
136
+ name: `${c.key} \u2014 ${c.fileCount} ${c.fileCount === 1 ? "file" : "files"}`,
137
+ value: c.key
138
+ })),
139
+ { name: chalk.dim("Done (exit)"), value: "__done__" }
140
+ ]
141
+ });
142
+ if (selected === "__done__") break;
143
+ const entry = cleanable.find((c) => c.key === selected);
146
144
  const relPath = relative(process.cwd(), entry.path);
147
145
  console.log();
148
146
  console.log(`${chalk.yellow("\u2192")} ${chalk.bold(entry.key)} (${relPath})`);
@@ -151,6 +149,7 @@ async function cleanCommand() {
151
149
  const message = isMemory ? `Delete matching contents of ${relPath}/? (includes SQLite databases \u2014 stop memory daemon first if running)` : `Delete matching contents of ${relPath}/?`;
152
150
  const ok = await confirm({ message, default: false });
153
151
  if (ok) {
152
+ const now = /* @__PURE__ */ new Date();
154
153
  const { deleted, failed } = deleteMatchingEntries(
155
154
  entry.path,
156
155
  range,