coding-friend-cli 1.6.0 → 1.8.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 (35) hide show
  1. package/README.md +2 -0
  2. package/dist/{chunk-FWHEMJS3.js → chunk-BPLN4LDL.js} +3 -5
  3. package/dist/{chunk-4PLV2ENL.js → chunk-FYGACWU6.js} +3 -2
  4. package/dist/chunk-FYHHNX7K.js +35 -0
  5. package/dist/{chunk-MRTR7TJ4.js → chunk-HFLBFX6J.js} +2 -4
  6. package/dist/{chunk-ESIIWKPD.js → chunk-JS75SVQA.js} +6 -8
  7. package/dist/{chunk-JWAJ4XPK.js → chunk-PGLUEN7D.js} +1 -1
  8. package/dist/chunk-QQ5SVZET.js +135 -0
  9. package/dist/chunk-RZRT7NGT.js +18 -0
  10. package/dist/{chunk-WHCJT7E2.js → chunk-TPRZHSFS.js} +38 -1
  11. package/dist/{chunk-6DUFTBTO.js → chunk-W5CD7WTX.js} +1 -0
  12. package/dist/config-JZEFZIPY.js +406 -0
  13. package/dist/{dev-EWSTIVM7.js → dev-U7LPXAHR.js} +9 -11
  14. package/dist/{host-SQEDE3NN.js → host-LOG5RPZ7.js} +7 -6
  15. package/dist/index.js +36 -13
  16. package/dist/init-JJATBCHC.js +512 -0
  17. package/dist/{install-RZFSIPFD.js → install-7MSZ7B5O.js} +7 -8
  18. package/dist/{mcp-QRPBL4ML.js → mcp-ORMYETXQ.js} +7 -6
  19. package/dist/postinstall.js +2 -2
  20. package/dist/session-3MWYAKKY.js +235 -0
  21. package/dist/{statusline-WGPSURDC.js → statusline-5HWRTSVL.js} +4 -5
  22. package/dist/{uninstall-KOAJFPD6.js → uninstall-HDLTWPXG.js} +8 -10
  23. package/dist/update-E4MQDRFC.js +16 -0
  24. package/lib/learn-host/CHANGELOG.md +6 -0
  25. package/lib/learn-host/package.json +1 -1
  26. package/lib/learn-host/src/app/[category]/[slug]/page.tsx +3 -1
  27. package/lib/learn-host/src/app/globals.css +20 -0
  28. package/lib/learn-host/src/components/TableOfContents.tsx +15 -1
  29. package/lib/learn-host/src/lib/docs.ts +2 -1
  30. package/package.json +1 -1
  31. package/dist/chunk-IUTXHCP7.js +0 -28
  32. package/dist/chunk-WK5YYHXM.js +0 -44
  33. package/dist/init-HX5T5DBV.js +0 -468
  34. package/dist/json-2XS56OJY.js +0 -10
  35. package/dist/update-VAFEWOLA.js +0 -17
package/README.md CHANGED
@@ -22,6 +22,8 @@ cf uninstall # Completely remove plugin, marketplace, statusline, comple
22
22
  # 💡 Interactive — asks for confirmation before acting.
23
23
  cf init # Initialize workspace (interactive)
24
24
  # 💡 You can run this anywhere, anytime.
25
+ cf config # Manage Coding Friend configuration (interactive menu)
26
+ # 💡 Edit docsDir, language, learn settings, and more.
25
27
  cf host [path] # Build and serve learning docs at localhost:3333
26
28
  # [path] is optional, default is `docs/learn`
27
29
  cf mcp [path] # Setup MCP server for LLM integration
@@ -1,18 +1,16 @@
1
1
  import {
2
2
  ALL_COMPONENT_IDS,
3
3
  STATUSLINE_COMPONENTS
4
- } from "./chunk-JWAJ4XPK.js";
4
+ } from "./chunk-PGLUEN7D.js";
5
5
  import {
6
6
  claudeSettingsPath,
7
7
  globalConfigPath,
8
8
  installedPluginsPath,
9
- pluginCachePath
10
- } from "./chunk-WHCJT7E2.js";
11
- import {
12
9
  mergeJson,
10
+ pluginCachePath,
13
11
  readJson,
14
12
  writeJson
15
- } from "./chunk-IUTXHCP7.js";
13
+ } from "./chunk-TPRZHSFS.js";
16
14
 
17
15
  // src/lib/statusline.ts
18
16
  import { existsSync, readdirSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  log
3
- } from "./chunk-6DUFTBTO.js";
3
+ } from "./chunk-W5CD7WTX.js";
4
4
 
5
5
  // src/lib/shell-completion.ts
6
6
  import { appendFileSync, existsSync, readFileSync, writeFileSync } from "fs";
@@ -13,7 +13,7 @@ ${MARKER_START}
13
13
  _cf_completions() {
14
14
  local cur="\${COMP_WORDS[COMP_CWORD]}"
15
15
  local prev="\${COMP_WORDS[COMP_CWORD-1]}"
16
- local commands="install uninstall init host mcp statusline update dev"
16
+ local commands="install uninstall init config host mcp statusline update dev"
17
17
 
18
18
  # Subcommands for 'dev'
19
19
  if [[ "\${COMP_WORDS[1]}" == "dev" && \${COMP_CWORD} -eq 2 ]]; then
@@ -41,6 +41,7 @@ _cf() {
41
41
  'install:Install the Coding Friend plugin into Claude Code'
42
42
  'uninstall:Uninstall the Coding Friend plugin from Claude Code'
43
43
  'init:Initialize coding-friend in current project'
44
+ 'config:Manage Coding Friend configuration'
44
45
  'host:Build and serve learning docs as a static website'
45
46
  'mcp:Setup MCP server for learning docs'
46
47
  'statusline:Setup coding-friend statusline in Claude Code'
@@ -0,0 +1,35 @@
1
+ import {
2
+ DEFAULT_CONFIG
3
+ } from "./chunk-PGLUEN7D.js";
4
+ import {
5
+ globalConfigPath,
6
+ localConfigPath,
7
+ readJson,
8
+ resolvePath
9
+ } from "./chunk-TPRZHSFS.js";
10
+
11
+ // src/lib/config.ts
12
+ function loadConfig() {
13
+ const global = readJson(globalConfigPath());
14
+ const local = readJson(localConfigPath());
15
+ return { ...DEFAULT_CONFIG, ...global, ...local };
16
+ }
17
+ function resolveDocsDir(explicitPath) {
18
+ if (explicitPath) {
19
+ return resolvePath(explicitPath);
20
+ }
21
+ const local = readJson(localConfigPath());
22
+ if (local?.learn?.outputDir) {
23
+ return resolvePath(local.learn.outputDir);
24
+ }
25
+ const global = readJson(globalConfigPath());
26
+ if (global?.learn?.outputDir) {
27
+ return resolvePath(global.learn.outputDir);
28
+ }
29
+ return resolvePath("docs/learn");
30
+ }
31
+
32
+ export {
33
+ loadConfig,
34
+ resolveDocsDir
35
+ };
@@ -1,10 +1,8 @@
1
1
  import {
2
2
  installedPluginsPath,
3
- knownMarketplacesPath
4
- } from "./chunk-WHCJT7E2.js";
5
- import {
3
+ knownMarketplacesPath,
6
4
  readJson
7
- } from "./chunk-IUTXHCP7.js";
5
+ } from "./chunk-TPRZHSFS.js";
8
6
 
9
7
  // src/lib/plugin-state.ts
10
8
  var MARKETPLACE_NAME = "coding-friend-marketplace";
@@ -1,24 +1,22 @@
1
1
  import {
2
2
  ensureStatusline,
3
3
  getInstalledVersion
4
- } from "./chunk-FWHEMJS3.js";
4
+ } from "./chunk-BPLN4LDL.js";
5
5
  import {
6
6
  ensureShellCompletion
7
- } from "./chunk-4PLV2ENL.js";
7
+ } from "./chunk-FYGACWU6.js";
8
8
  import {
9
9
  commandExists,
10
10
  run,
11
11
  sleepSync
12
12
  } from "./chunk-UFGNO6CW.js";
13
13
  import {
14
- claudeSettingsPath
15
- } from "./chunk-WHCJT7E2.js";
14
+ claudeSettingsPath,
15
+ readJson
16
+ } from "./chunk-TPRZHSFS.js";
16
17
  import {
17
18
  log
18
- } from "./chunk-6DUFTBTO.js";
19
- import {
20
- readJson
21
- } from "./chunk-IUTXHCP7.js";
19
+ } from "./chunk-W5CD7WTX.js";
22
20
 
23
21
  // src/commands/update.ts
24
22
  import { readFileSync } from "fs";
@@ -11,8 +11,8 @@ var ALL_COMPONENT_IDS = STATUSLINE_COMPONENTS.map((c) => c.id);
11
11
  var DEFAULT_CONFIG = {
12
12
  language: "en",
13
13
  docsDir: "docs",
14
- devRulesReminder: true,
15
14
  learn: {
15
+ language: "en",
16
16
  outputDir: "docs/learn",
17
17
  categories: [
18
18
  {
@@ -0,0 +1,135 @@
1
+ import {
2
+ run
3
+ } from "./chunk-UFGNO6CW.js";
4
+ import {
5
+ globalConfigPath,
6
+ localConfigPath,
7
+ readJson
8
+ } from "./chunk-TPRZHSFS.js";
9
+ import {
10
+ log
11
+ } from "./chunk-W5CD7WTX.js";
12
+
13
+ // src/lib/prompt-utils.ts
14
+ import { select, Separator } from "@inquirer/prompts";
15
+ import { existsSync } from "fs";
16
+ import chalk from "chalk";
17
+ var BACK = "__back__";
18
+ function injectBackChoice(choices, label = "Back") {
19
+ return [
20
+ ...choices,
21
+ new Separator(),
22
+ { name: label, value: BACK }
23
+ ];
24
+ }
25
+ async function askScope(label = "Save to:") {
26
+ return select({
27
+ message: label,
28
+ choices: [
29
+ { name: "Global (all projects)", value: "global" },
30
+ { name: "This project only", value: "local" },
31
+ new Separator(),
32
+ { name: "Back", value: "back" }
33
+ ]
34
+ });
35
+ }
36
+ function showConfigHint() {
37
+ console.log(chalk.dim("Config files:"));
38
+ console.log(chalk.dim(" Global: ~/.coding-friend/config.json"));
39
+ console.log(chalk.dim(" Local: .coding-friend/config.json (this project)"));
40
+ console.log(chalk.dim("Local overrides global at property level."));
41
+ console.log();
42
+ console.log(` ${chalk.yellow("[-]")} ${chalk.dim("not set anywhere")}`);
43
+ console.log(
44
+ ` ${chalk.blue("[global]")} ${chalk.dim("set in global config only")}`
45
+ );
46
+ console.log(
47
+ ` ${chalk.green("[local]")} ${chalk.dim("set in this project only")}`
48
+ );
49
+ console.log(
50
+ ` ${chalk.cyan("[both]")} ${chalk.dim("set in both places \u2014 local value takes effect")}`
51
+ );
52
+ console.log();
53
+ }
54
+ function getScopeLabel(key, globalCfg, localCfg) {
55
+ const inGlobal = globalCfg ? globalCfg[key] !== void 0 : false;
56
+ const inLocal = localCfg ? localCfg[key] !== void 0 : false;
57
+ if (inGlobal && inLocal) return "both";
58
+ if (inGlobal) return "global";
59
+ if (inLocal) return "local";
60
+ return "-";
61
+ }
62
+ function formatScopeLabel(scope) {
63
+ if (scope === "-") return chalk.yellow("[-]");
64
+ if (scope === "both") return chalk.cyan("[both]");
65
+ if (scope === "global") return chalk.blue("[global]");
66
+ if (scope === "local") return chalk.green("[local]");
67
+ return `[${scope}]`;
68
+ }
69
+ function getMergedValue(key, globalCfg, localCfg) {
70
+ const localVal = localCfg ? localCfg[key] : void 0;
71
+ if (localVal !== void 0) return localVal;
72
+ return globalCfg ? globalCfg[key] : void 0;
73
+ }
74
+ function createSubfolders(docsDir, subfolders) {
75
+ const created = [];
76
+ for (const sub of subfolders) {
77
+ const p = `${docsDir}/${sub}`;
78
+ if (!existsSync(p)) {
79
+ run("mkdir", ["-p", p]);
80
+ created.push(sub);
81
+ }
82
+ }
83
+ if (created.length > 0) {
84
+ log.success(
85
+ `Created subfolders: ${created.map((s) => `${docsDir}/${s}`).join(", ")}`
86
+ );
87
+ }
88
+ }
89
+ function applyDocsDirChange(newValue, oldValue, scope, subfolders = []) {
90
+ if (newValue === oldValue) return;
91
+ if (scope === "local") {
92
+ if (oldValue && existsSync(oldValue)) {
93
+ if (existsSync(newValue)) {
94
+ log.warn(`Folder "${newValue}" already exists \u2014 skipped rename.`);
95
+ } else {
96
+ run("mv", [oldValue, newValue]);
97
+ log.success(`Renamed "${oldValue}" \u2192 "${newValue}"`);
98
+ createSubfolders(newValue, subfolders);
99
+ }
100
+ } else if (!existsSync(newValue)) {
101
+ run("mkdir", ["-p", newValue]);
102
+ log.success(`Created "${newValue}"`);
103
+ createSubfolders(newValue, subfolders);
104
+ }
105
+ } else {
106
+ const localCfg = readJson(localConfigPath());
107
+ if (localCfg?.docsDir) return;
108
+ const globalCfg = readJson(globalConfigPath());
109
+ const oldGlobalDir = globalCfg?.docsDir;
110
+ if (oldGlobalDir && existsSync(oldGlobalDir)) {
111
+ if (existsSync(newValue)) {
112
+ log.warn(`Folder "${newValue}" already exists \u2014 skipped rename.`);
113
+ } else {
114
+ run("mv", [oldGlobalDir, newValue]);
115
+ log.success(`Renamed "${oldGlobalDir}" \u2192 "${newValue}"`);
116
+ createSubfolders(newValue, subfolders);
117
+ }
118
+ } else if (!existsSync(newValue)) {
119
+ run("mkdir", ["-p", newValue]);
120
+ log.success(`Created "${newValue}"`);
121
+ createSubfolders(newValue, subfolders);
122
+ }
123
+ }
124
+ }
125
+
126
+ export {
127
+ BACK,
128
+ injectBackChoice,
129
+ askScope,
130
+ showConfigHint,
131
+ getScopeLabel,
132
+ formatScopeLabel,
133
+ getMergedValue,
134
+ applyDocsDirChange
135
+ };
@@ -0,0 +1,18 @@
1
+ // src/lib/lib-path.ts
2
+ import { existsSync } from "fs";
3
+ import { dirname, join } from "path";
4
+ import { fileURLToPath } from "url";
5
+ var __dirname = dirname(fileURLToPath(import.meta.url));
6
+ function getLibPath(name) {
7
+ const bundled = join(__dirname, "..", "lib", name);
8
+ if (existsSync(bundled)) return bundled;
9
+ const dev = join(__dirname, "..", "..", "lib", name);
10
+ if (existsSync(dev)) return dev;
11
+ throw new Error(
12
+ `Could not find lib/${name}. Ensure it exists in the CLI package.`
13
+ );
14
+ }
15
+
16
+ export {
17
+ getLibPath
18
+ };
@@ -1,3 +1,26 @@
1
+ // src/lib/json.ts
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
3
+ import { dirname } from "path";
4
+ function readJson(filePath) {
5
+ try {
6
+ const content = readFileSync(filePath, "utf-8");
7
+ return JSON.parse(content);
8
+ } catch {
9
+ return null;
10
+ }
11
+ }
12
+ function writeJson(filePath, data) {
13
+ const dir = dirname(filePath);
14
+ if (!existsSync(dir)) {
15
+ mkdirSync(dir, { recursive: true });
16
+ }
17
+ writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
18
+ }
19
+ function mergeJson(filePath, data) {
20
+ const existing = readJson(filePath) ?? {};
21
+ writeJson(filePath, { ...existing, ...data });
22
+ }
23
+
1
24
  // src/lib/paths.ts
2
25
  import { homedir } from "os";
3
26
  import { resolve, join } from "path";
@@ -55,8 +78,20 @@ function marketplaceClonePath() {
55
78
  function globalConfigDir() {
56
79
  return join(homedir(), ".coding-friend");
57
80
  }
81
+ function encodeProjectPath(absolutePath) {
82
+ return absolutePath.replace(/\//g, "-");
83
+ }
84
+ function claudeProjectsDir() {
85
+ return join(homedir(), ".claude", "projects");
86
+ }
87
+ function claudeSessionDir(encodedPath) {
88
+ return join(claudeProjectsDir(), encodedPath);
89
+ }
58
90
 
59
91
  export {
92
+ readJson,
93
+ writeJson,
94
+ mergeJson,
60
95
  resolvePath,
61
96
  localConfigPath,
62
97
  globalConfigPath,
@@ -67,5 +102,7 @@ export {
67
102
  knownMarketplacesPath,
68
103
  marketplaceCachePath,
69
104
  marketplaceClonePath,
70
- globalConfigDir
105
+ globalConfigDir,
106
+ encodeProjectPath,
107
+ claudeSessionDir
71
108
  };
@@ -3,6 +3,7 @@ import chalk from "chalk";
3
3
  var log = {
4
4
  info: (msg) => console.log(chalk.blue("\u2139"), msg),
5
5
  success: (msg) => console.log(chalk.green("\u2714"), msg),
6
+ congrats: (msg) => console.log(chalk.green("\u{1F389}"), msg),
6
7
  warn: (msg) => console.log(chalk.yellow("\u26A0"), msg),
7
8
  error: (msg) => console.log(chalk.red("\u2716"), msg),
8
9
  step: (msg) => console.log(chalk.cyan("\u2192"), msg),