coding-friend-cli 1.2.0 → 1.3.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 (87) hide show
  1. package/README.md +29 -6
  2. package/dist/{chunk-R6ZYK4UX.js → chunk-CSF4FAHL.js} +2 -1
  3. package/dist/{chunk-5HZJX47M.js → chunk-Q4DKU5IG.js} +3 -5
  4. package/dist/{dev-LZASFXZZ.js → dev-VMN2JHA6.js} +89 -18
  5. package/dist/{host-BK6DYFWF.js → host-2WINWEW7.js} +1 -1
  6. package/dist/index.js +30 -13
  7. package/dist/{init-2UKYE2KV.js → init-CTCDQKIQ.js} +23 -12
  8. package/dist/{mcp-CH4SKZSX.js → mcp-43HCE2KD.js} +1 -1
  9. package/dist/postinstall.js +1 -1
  10. package/dist/{update-5A2OP6EY.js → update-GGCBM7U4.js} +45 -15
  11. package/lib/learn-host/.prettierignore +1 -0
  12. package/lib/learn-host/CHANGELOG.md +5 -0
  13. package/lib/learn-host/README.md +114 -0
  14. package/lib/learn-host/next-env.d.ts +1 -1
  15. package/lib/learn-host/next.config.ts +3 -0
  16. package/lib/learn-host/package.json +1 -1
  17. package/lib/learn-host/public/logo.svg +1 -1
  18. package/lib/learn-host/src/app/apple-icon.svg +1 -1
  19. package/lib/learn-host/src/app/icon.svg +1 -1
  20. package/lib/learn-host/src/components/Sidebar.tsx +1 -1
  21. package/lib/learn-host/src/lib/docs.ts +2 -2
  22. package/lib/learn-mcp/CHANGELOG.md +5 -0
  23. package/lib/learn-mcp/README.md +169 -0
  24. package/lib/learn-mcp/package.json +2 -1
  25. package/lib/learn-mcp/src/index.ts +1 -1
  26. package/lib/learn-mcp/src/lib/docs.ts +1 -3
  27. package/lib/learn-mcp/src/lib/knowledge.ts +2 -1
  28. package/lib/learn-mcp/src/tools/get-review-list.ts +1 -4
  29. package/lib/learn-mcp/src/tools/search-docs.ts +1 -4
  30. package/package.json +2 -2
  31. package/lib/learn-host/public/_pagefind/fragment/en_1172b3c.pf_fragment +0 -0
  32. package/lib/learn-host/public/_pagefind/fragment/en_118ad1c.pf_fragment +0 -0
  33. package/lib/learn-host/public/_pagefind/fragment/en_32ab3d8.pf_fragment +0 -0
  34. package/lib/learn-host/public/_pagefind/fragment/en_441f1e1.pf_fragment +0 -0
  35. package/lib/learn-host/public/_pagefind/fragment/en_4452de4.pf_fragment +0 -0
  36. package/lib/learn-host/public/_pagefind/fragment/en_4ae396d.pf_fragment +0 -0
  37. package/lib/learn-host/public/_pagefind/fragment/en_58ee89d.pf_fragment +0 -0
  38. package/lib/learn-host/public/_pagefind/fragment/en_6dd2225.pf_fragment +0 -0
  39. package/lib/learn-host/public/_pagefind/fragment/en_765a297.pf_fragment +0 -0
  40. package/lib/learn-host/public/_pagefind/fragment/en_7a4cc4a.pf_fragment +0 -0
  41. package/lib/learn-host/public/_pagefind/fragment/en_8050261.pf_fragment +0 -0
  42. package/lib/learn-host/public/_pagefind/fragment/en_83eaedf.pf_fragment +0 -0
  43. package/lib/learn-host/public/_pagefind/fragment/en_925bc5f.pf_fragment +0 -0
  44. package/lib/learn-host/public/_pagefind/fragment/en_95f3dd5.pf_fragment +0 -0
  45. package/lib/learn-host/public/_pagefind/fragment/en_96d7a02.pf_fragment +0 -0
  46. package/lib/learn-host/public/_pagefind/fragment/en_971f951.pf_fragment +0 -0
  47. package/lib/learn-host/public/_pagefind/fragment/en_a446c32.pf_fragment +0 -0
  48. package/lib/learn-host/public/_pagefind/fragment/en_a5ee367.pf_fragment +0 -0
  49. package/lib/learn-host/public/_pagefind/fragment/en_b11c248.pf_fragment +0 -0
  50. package/lib/learn-host/public/_pagefind/fragment/en_b13c52e.pf_fragment +0 -0
  51. package/lib/learn-host/public/_pagefind/fragment/en_b5bd69b.pf_fragment +0 -0
  52. package/lib/learn-host/public/_pagefind/fragment/en_b625d7d.pf_fragment +0 -0
  53. package/lib/learn-host/public/_pagefind/fragment/en_bf63915.pf_fragment +0 -0
  54. package/lib/learn-host/public/_pagefind/fragment/en_c52b25b.pf_fragment +0 -0
  55. package/lib/learn-host/public/_pagefind/fragment/en_c9db556.pf_fragment +0 -0
  56. package/lib/learn-host/public/_pagefind/fragment/en_d1537ee.pf_fragment +0 -0
  57. package/lib/learn-host/public/_pagefind/fragment/en_d2e6412.pf_fragment +0 -0
  58. package/lib/learn-host/public/_pagefind/fragment/en_d2f47a4.pf_fragment +0 -0
  59. package/lib/learn-host/public/_pagefind/fragment/en_d361292.pf_fragment +0 -0
  60. package/lib/learn-host/public/_pagefind/fragment/en_d727ec8.pf_fragment +0 -0
  61. package/lib/learn-host/public/_pagefind/fragment/en_e11cd8f.pf_fragment +0 -0
  62. package/lib/learn-host/public/_pagefind/fragment/en_e481f19.pf_fragment +0 -0
  63. package/lib/learn-host/public/_pagefind/fragment/en_eee2805.pf_fragment +0 -0
  64. package/lib/learn-host/public/_pagefind/fragment/en_f4de6c4.pf_fragment +0 -0
  65. package/lib/learn-host/public/_pagefind/index/en_1ecb9d5.pf_index +0 -0
  66. package/lib/learn-host/public/_pagefind/index/en_37e362b.pf_index +0 -0
  67. package/lib/learn-host/public/_pagefind/index/en_538eee7.pf_index +0 -0
  68. package/lib/learn-host/public/_pagefind/index/en_5751dc8.pf_index +0 -0
  69. package/lib/learn-host/public/_pagefind/index/en_67f794d.pf_index +0 -0
  70. package/lib/learn-host/public/_pagefind/index/en_7458f81.pf_index +0 -0
  71. package/lib/learn-host/public/_pagefind/index/en_e21f7e1.pf_index +0 -0
  72. package/lib/learn-host/public/_pagefind/pagefind-entry.json +0 -1
  73. package/lib/learn-host/public/_pagefind/pagefind-highlight.js +0 -1064
  74. package/lib/learn-host/public/_pagefind/pagefind-modular-ui.css +0 -214
  75. package/lib/learn-host/public/_pagefind/pagefind-modular-ui.js +0 -8
  76. package/lib/learn-host/public/_pagefind/pagefind-ui.css +0 -1
  77. package/lib/learn-host/public/_pagefind/pagefind-ui.js +0 -2
  78. package/lib/learn-host/public/_pagefind/pagefind.en_104569cceb.pf_meta +0 -0
  79. package/lib/learn-host/public/_pagefind/pagefind.en_1075df6f16.pf_meta +0 -0
  80. package/lib/learn-host/public/_pagefind/pagefind.en_139f35f6e5.pf_meta +0 -0
  81. package/lib/learn-host/public/_pagefind/pagefind.en_46bfc9f7e1.pf_meta +0 -0
  82. package/lib/learn-host/public/_pagefind/pagefind.en_76b8937bbc.pf_meta +0 -0
  83. package/lib/learn-host/public/_pagefind/pagefind.en_83cbfb0fd5.pf_meta +0 -0
  84. package/lib/learn-host/public/_pagefind/pagefind.en_b1d168d536.pf_meta +0 -0
  85. package/lib/learn-host/public/_pagefind/pagefind.js +0 -6
  86. package/lib/learn-host/public/_pagefind/wasm.en.pagefind +0 -0
  87. package/lib/learn-host/public/_pagefind/wasm.unknown.pagefind +0 -0
package/README.md CHANGED
@@ -75,17 +75,40 @@ npm ls -g coding-friend-cli
75
75
  #└── coding-friend-cli@1.1.1 -> ./../../../../../git/coding-friend/cli
76
76
  ```
77
77
 
78
+ ### Running tests
79
+
80
+ Tests are written with [Vitest](https://vitest.dev/) and live in `src/lib/__tests__/`.
81
+
82
+ ```bash
83
+ cd cli
84
+
85
+ # Run all tests once
86
+ npm test
87
+
88
+ # Watch mode (re-runs on file changes)
89
+ npm run test:watch
90
+ ```
91
+
92
+ Current coverage: `lib/json.ts`, `lib/paths.ts`, `lib/exec.ts`.
93
+
78
94
  ## Publish CLI to npm
79
95
 
96
+ Publishing is automated via GitHub Actions (`.github/workflows/publish-cli.yml`). Push a tag with the `cli-v*` prefix to trigger it:
97
+
98
+ ```bash
99
+ # Bump version in cli/package.json first, then tag and push
100
+ git tag cli-v1.2.3
101
+ git push origin cli-v1.2.3
102
+ ```
103
+
104
+ The workflow will build, bundle, and publish to npm automatically (with provenance), then create a GitHub Release with the changelog for that version.
105
+
106
+ **Manual publish (if needed):**
107
+
80
108
  ```bash
81
- # From the root of coding-friend project
82
109
  cd cli
83
110
  npm login # Login if not already
84
111
  npm publish # Build + bundle + publish
85
-
86
- # To bump a version
87
- npm version patch # 1.0.1 -> 1.0.2
88
- npm version minor # 1.0.1 -> 1.1.0
89
112
  ```
90
113
 
91
114
  `prepublishOnly` runs automatically: builds TypeScript → `dist/` and bundles libs from `lib/`.
@@ -96,4 +119,4 @@ npm version minor # 1.0.1 -> 1.1.0
96
119
 
97
120
  ## License
98
121
 
99
- MIT
122
+ MIT
@@ -103,7 +103,8 @@ function ensureShellCompletion(opts) {
103
103
  const existing = extractExistingBlock(content);
104
104
  const expectedBlock = newBlock.trim();
105
105
  if (existing && existing.trim() === expectedBlock) {
106
- if (!opts?.silent) log.dim(`Tab completion already up-to-date in ~/${rcName}`);
106
+ if (!opts?.silent)
107
+ log.dim(`Tab completion already up-to-date in ~/${rcName}`);
107
108
  return false;
108
109
  }
109
110
  const updated = replaceBlock(content, newBlock);
@@ -29,12 +29,10 @@ import { dirname, join } from "path";
29
29
  import { fileURLToPath } from "url";
30
30
  var __dirname = dirname(fileURLToPath(import.meta.url));
31
31
  function getLibPath(name) {
32
- const bundled = join(__dirname, "..", "..", "lib", name);
33
- if (existsSync(bundled)) return bundled;
34
- const sibling = join(__dirname, "..", "..", "..", "lib", name);
35
- if (existsSync(sibling)) return sibling;
32
+ const libDir = join(__dirname, "..", "..", "lib", name);
33
+ if (existsSync(libDir)) return libDir;
36
34
  throw new Error(
37
- `Could not find lib/${name}. Ensure it exists in the CLI package or repo.`
35
+ `Could not find lib/${name}. Ensure it exists in the CLI package.`
38
36
  );
39
37
  }
40
38
 
@@ -3,6 +3,7 @@ import {
3
3
  run
4
4
  } from "./chunk-UFGNO6CW.js";
5
5
  import {
6
+ claudeSettingsPath,
6
7
  devStatePath,
7
8
  installedPluginsPath,
8
9
  knownMarketplacesPath,
@@ -17,7 +18,14 @@ import {
17
18
  } from "./chunk-IUTXHCP7.js";
18
19
 
19
20
  // src/commands/dev.ts
20
- import { existsSync, unlinkSync, readdirSync, statSync, mkdirSync, copyFileSync } from "fs";
21
+ import {
22
+ existsSync,
23
+ unlinkSync,
24
+ readdirSync,
25
+ statSync,
26
+ mkdirSync,
27
+ copyFileSync
28
+ } from "fs";
21
29
  import { resolve, join } from "path";
22
30
  import chalk from "chalk";
23
31
  var REMOTE_URL = "https://github.com/dinhanhthi/coding-friend.git";
@@ -40,7 +48,9 @@ function isMarketplaceRegistered() {
40
48
  }
41
49
  function ensureClaude() {
42
50
  if (!commandExists("claude")) {
43
- log.error("Claude CLI not found. Install it first: https://docs.anthropic.com/en/docs/claude-code");
51
+ log.error(
52
+ "Claude CLI not found. Install it first: https://docs.anthropic.com/en/docs/claude-code"
53
+ );
44
54
  return false;
45
55
  }
46
56
  return true;
@@ -54,6 +64,28 @@ function runClaude(args, label) {
54
64
  }
55
65
  return true;
56
66
  }
67
+ function updateSettingsCachePaths() {
68
+ const cachePath = pluginCachePath();
69
+ if (!existsSync(cachePath)) return;
70
+ const versions = readdirSync(cachePath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
71
+ const latest = versions[0];
72
+ if (!latest) return;
73
+ const settingsPath = claudeSettingsPath();
74
+ const settings = readJson(settingsPath);
75
+ if (!settings) return;
76
+ const statusLine = settings.statusLine;
77
+ if (!statusLine?.command) return;
78
+ const prefix = cachePath + "/";
79
+ if (!statusLine.command.includes(prefix)) return;
80
+ const updated = statusLine.command.replace(
81
+ new RegExp(`${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[^/]+/`),
82
+ `${prefix}${latest}/`
83
+ );
84
+ if (updated === statusLine.command) return;
85
+ settings.statusLine = { ...statusLine, command: updated };
86
+ writeJson(settingsPath, settings);
87
+ log.info(`Updated statusline path to ${chalk.green(`v${latest}`)}`);
88
+ }
57
89
  async function devOnCommand(path) {
58
90
  const state = getDevState();
59
91
  if (state) {
@@ -73,18 +105,30 @@ async function devOnCommand(path) {
73
105
  `);
74
106
  log.info(`Local path: ${chalk.cyan(localPath)}`);
75
107
  if (isPluginInstalled()) {
76
- if (!runClaude(["plugin", "uninstall", PLUGIN_ID], "Uninstalling remote plugin...")) {
108
+ if (!runClaude(
109
+ ["plugin", "uninstall", PLUGIN_ID],
110
+ "Uninstalling remote plugin..."
111
+ )) {
77
112
  run("claude", ["plugin", "uninstall", PLUGIN_NAME]);
78
113
  }
79
114
  }
80
115
  if (isMarketplaceRegistered()) {
81
- runClaude(["plugin", "marketplace", "remove", MARKETPLACE_NAME], "Removing remote marketplace...");
116
+ runClaude(
117
+ ["plugin", "marketplace", "remove", MARKETPLACE_NAME],
118
+ "Removing remote marketplace..."
119
+ );
82
120
  }
83
- if (!runClaude(["plugin", "marketplace", "add", localPath], "Adding local marketplace...")) {
121
+ if (!runClaude(
122
+ ["plugin", "marketplace", "add", localPath],
123
+ "Adding local marketplace..."
124
+ )) {
84
125
  log.error("Failed to add local marketplace. Aborting.");
85
126
  return;
86
127
  }
87
- if (!runClaude(["plugin", "install", PLUGIN_ID], "Installing plugin from local source...")) {
128
+ if (!runClaude(
129
+ ["plugin", "install", PLUGIN_ID],
130
+ "Installing plugin from local source..."
131
+ )) {
88
132
  if (!runClaude(["plugin", "install", PLUGIN_NAME], "Retrying install...")) {
89
133
  log.error("Failed to install local plugin.");
90
134
  return;
@@ -95,8 +139,11 @@ async function devOnCommand(path) {
95
139
  savedAt: (/* @__PURE__ */ new Date()).toISOString()
96
140
  };
97
141
  writeJson(devStatePath(), devState);
142
+ updateSettingsCachePaths();
98
143
  console.log();
99
- log.success(`Dev mode ${chalk.green("ON")} \u2014 using local plugin from ${chalk.cyan(localPath)}`);
144
+ log.success(
145
+ `Dev mode ${chalk.green("ON")} \u2014 using local plugin from ${chalk.cyan(localPath)}`
146
+ );
100
147
  log.dim("Restart Claude Code to see changes.");
101
148
  }
102
149
  async function devOffCommand() {
@@ -106,22 +153,36 @@ async function devOffCommand() {
106
153
  return;
107
154
  }
108
155
  if (!ensureClaude()) return;
109
- console.log(`
156
+ console.log(
157
+ `
110
158
  === ${chalk.yellow("Switching back to remote marketplace")} ===
111
- `);
159
+ `
160
+ );
112
161
  if (isPluginInstalled()) {
113
- if (!runClaude(["plugin", "uninstall", PLUGIN_ID], "Uninstalling local plugin...")) {
162
+ if (!runClaude(
163
+ ["plugin", "uninstall", PLUGIN_ID],
164
+ "Uninstalling local plugin..."
165
+ )) {
114
166
  run("claude", ["plugin", "uninstall", PLUGIN_NAME]);
115
167
  }
116
168
  }
117
169
  if (isMarketplaceRegistered()) {
118
- runClaude(["plugin", "marketplace", "remove", MARKETPLACE_NAME], "Removing local marketplace...");
170
+ runClaude(
171
+ ["plugin", "marketplace", "remove", MARKETPLACE_NAME],
172
+ "Removing local marketplace..."
173
+ );
119
174
  }
120
- if (!runClaude(["plugin", "marketplace", "add", REMOTE_URL], "Adding remote marketplace...")) {
175
+ if (!runClaude(
176
+ ["plugin", "marketplace", "add", REMOTE_URL],
177
+ "Adding remote marketplace..."
178
+ )) {
121
179
  log.error("Failed to add remote marketplace.");
122
180
  return;
123
181
  }
124
- if (!runClaude(["plugin", "install", PLUGIN_ID], "Installing plugin from remote...")) {
182
+ if (!runClaude(
183
+ ["plugin", "install", PLUGIN_ID],
184
+ "Installing plugin from remote..."
185
+ )) {
125
186
  if (!runClaude(["plugin", "install", PLUGIN_NAME], "Retrying install...")) {
126
187
  log.error("Failed to install remote plugin.");
127
188
  return;
@@ -136,7 +197,9 @@ async function devOffCommand() {
136
197
  log.dim("Restart Claude Code to see changes.");
137
198
  }
138
199
  function getMarketplaceSource() {
139
- const data = readJson(knownMarketplacesPath());
200
+ const data = readJson(
201
+ knownMarketplacesPath()
202
+ );
140
203
  if (!data || !(MARKETPLACE_NAME in data)) return null;
141
204
  const entry = data[MARKETPLACE_NAME];
142
205
  const src = entry.source;
@@ -171,7 +234,9 @@ async function devSyncCommand() {
171
234
  const localPath = state.localPath;
172
235
  const pluginSrcDir = join(localPath, "plugin");
173
236
  if (!existsSync(pluginSrcDir)) {
174
- log.error(`No plugin/ directory found at ${localPath}. Make sure you point to the coding-friend repo root.`);
237
+ log.error(
238
+ `No plugin/ directory found at ${localPath}. Make sure you point to the coding-friend repo root.`
239
+ );
175
240
  return;
176
241
  }
177
242
  const cacheBase = pluginCachePath();
@@ -190,14 +255,18 @@ async function devSyncCommand() {
190
255
  }
191
256
  }
192
257
  if (!cacheVersionDir) {
193
- log.error("No cached plugin version found. Run `cf dev off && cf dev on` first.");
258
+ log.error(
259
+ "No cached plugin version found. Run `cf dev off && cf dev on` first."
260
+ );
194
261
  return;
195
262
  }
196
263
  const shortDest = cacheVersionDir.replace(process.env.HOME ?? "", "~");
197
264
  log.step(`Syncing ${chalk.cyan(pluginSrcDir)} \u2192 ${chalk.dim(shortDest)}`);
198
265
  const fileCount = { n: 0 };
199
266
  copyDirRecursive(pluginSrcDir, cacheVersionDir, fileCount);
200
- log.success(`Synced ${chalk.green(fileCount.n)} files. Restart Claude Code to apply changes.`);
267
+ log.success(
268
+ `Synced ${chalk.green(fileCount.n)} files. Restart Claude Code to apply changes.`
269
+ );
201
270
  }
202
271
  async function devRestartCommand(path) {
203
272
  const state = getDevState();
@@ -232,7 +301,9 @@ async function devStatusCommand() {
232
301
  } else {
233
302
  log.warn(`Marketplace "${MARKETPLACE_NAME}" not registered.`);
234
303
  }
235
- log.info(`Plugin installed: ${installed ? chalk.green("yes") : chalk.yellow("no")}`);
304
+ log.info(
305
+ `Plugin installed: ${installed ? chalk.green("yes") : chalk.yellow("no")}`
306
+ );
236
307
  }
237
308
  export {
238
309
  devOffCommand,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getLibPath,
3
3
  resolveDocsDir
4
- } from "./chunk-5HZJX47M.js";
4
+ } from "./chunk-Q4DKU5IG.js";
5
5
  import "./chunk-HRVSKMNA.js";
6
6
  import {
7
7
  run,
package/dist/index.js CHANGED
@@ -10,17 +10,19 @@ var pkg = JSON.parse(
10
10
  readFileSync(join(__dirname, "..", "package.json"), "utf-8")
11
11
  );
12
12
  var program = new Command();
13
- program.name("cf").description("coding-friend CLI \u2014 host learning docs, setup MCP, init projects").version(pkg.version, "-v, --version");
13
+ program.name("cf").description(
14
+ "coding-friend CLI \u2014 host learning docs, setup MCP, init projects"
15
+ ).version(pkg.version, "-v, --version");
14
16
  program.command("init").description("Initialize coding-friend in current project").action(async () => {
15
- const { initCommand } = await import("./init-2UKYE2KV.js");
17
+ const { initCommand } = await import("./init-CTCDQKIQ.js");
16
18
  await initCommand();
17
19
  });
18
20
  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) => {
19
- const { hostCommand } = await import("./host-BK6DYFWF.js");
21
+ const { hostCommand } = await import("./host-2WINWEW7.js");
20
22
  await hostCommand(path, opts);
21
23
  });
22
24
  program.command("mcp").description("Setup MCP server for learning docs").argument("[path]", "path to docs folder").action(async (path) => {
23
- const { mcpCommand } = await import("./mcp-CH4SKZSX.js");
25
+ const { mcpCommand } = await import("./mcp-43HCE2KD.js");
24
26
  await mcpCommand(path);
25
27
  });
26
28
  program.command("statusline").description("Setup coding-friend statusline in Claude Code").action(async () => {
@@ -28,28 +30,43 @@ program.command("statusline").description("Setup coding-friend statusline in Cla
28
30
  await statuslineCommand();
29
31
  });
30
32
  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").action(async (opts) => {
31
- const { updateCommand } = await import("./update-5A2OP6EY.js");
33
+ const { updateCommand } = await import("./update-GGCBM7U4.js");
32
34
  await updateCommand(opts);
33
35
  });
34
- var dev = program.command("dev").description("Switch between local and remote plugin for development");
36
+ var dev = program.command("dev").description("Development mode commands");
37
+ program.addHelpText(
38
+ "after",
39
+ `
40
+ Dev subcommands:
41
+ dev on [path] Switch to local plugin source
42
+ dev off Switch back to remote marketplace
43
+ dev status Show current dev mode
44
+ dev sync Copy local source to plugin cache
45
+ dev restart [path] Reinstall local dev plugin (off + on)`
46
+ );
35
47
  dev.command("on").description("Switch to local plugin source").argument("[path]", "path to local coding-friend repo (default: cwd)").action(async (path) => {
36
- const { devOnCommand } = await import("./dev-LZASFXZZ.js");
48
+ const { devOnCommand } = await import("./dev-VMN2JHA6.js");
37
49
  await devOnCommand(path);
38
50
  });
39
51
  dev.command("off").description("Switch back to remote marketplace").action(async () => {
40
- const { devOffCommand } = await import("./dev-LZASFXZZ.js");
52
+ const { devOffCommand } = await import("./dev-VMN2JHA6.js");
41
53
  await devOffCommand();
42
54
  });
43
55
  dev.command("status").description("Show current dev mode").action(async () => {
44
- const { devStatusCommand } = await import("./dev-LZASFXZZ.js");
56
+ const { devStatusCommand } = await import("./dev-VMN2JHA6.js");
45
57
  await devStatusCommand();
46
58
  });
47
- dev.command("sync").description("Copy local source files to plugin cache (no version bump needed)").action(async () => {
48
- const { devSyncCommand } = await import("./dev-LZASFXZZ.js");
59
+ dev.command("sync").description(
60
+ "Copy local source files to plugin cache (no version bump needed)"
61
+ ).action(async () => {
62
+ const { devSyncCommand } = await import("./dev-VMN2JHA6.js");
49
63
  await devSyncCommand();
50
64
  });
51
- dev.command("restart").description("Reinstall local dev plugin (off + on)").argument("[path]", "path to local coding-friend repo (default: saved path or cwd)").action(async (path) => {
52
- const { devRestartCommand } = await import("./dev-LZASFXZZ.js");
65
+ dev.command("restart").description("Reinstall local dev plugin (off + on)").argument(
66
+ "[path]",
67
+ "path to local coding-friend repo (default: saved path or cwd)"
68
+ ).action(async (path) => {
69
+ const { devRestartCommand } = await import("./dev-VMN2JHA6.js");
53
70
  await devRestartCommand(path);
54
71
  });
55
72
  program.parse();
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  ensureShellCompletion,
6
6
  hasShellCompletion
7
- } from "./chunk-R6ZYK4UX.js";
7
+ } from "./chunk-CSF4FAHL.js";
8
8
  import {
9
9
  run
10
10
  } from "./chunk-UFGNO6CW.js";
@@ -183,7 +183,9 @@ async function setupLearnConfig(gitAvailable = true) {
183
183
  });
184
184
  let categories = DEFAULT_CONFIG.learn.categories;
185
185
  if (catChoice === "custom") {
186
- console.log('Enter categories (format: "name: description"). Empty line to finish.');
186
+ console.log(
187
+ 'Enter categories (format: "name: description"). Empty line to finish.'
188
+ );
187
189
  const customCats = [];
188
190
  let keepGoing = true;
189
191
  while (keepGoing) {
@@ -265,7 +267,11 @@ async function setupClaudePermissions(outputDir, autoCommit) {
265
267
  }
266
268
  permissions.allow = [...existing, ...missing];
267
269
  settings.permissions = permissions;
268
- const { readJson: _r, writeJson: _w, ...restImports } = await import("./json-2XS56OJY.js");
270
+ const {
271
+ readJson: _r,
272
+ writeJson: _w,
273
+ ...restImports
274
+ } = await import("./json-2XS56OJY.js");
269
275
  _w(settingsPath, settings);
270
276
  log.success(`Added ${missing.length} permission rules.`);
271
277
  }
@@ -287,9 +293,7 @@ function isDefaultConfig(config) {
287
293
  }
288
294
  async function saveConfig(config) {
289
295
  if (isDefaultConfig(config)) {
290
- log.dim(
291
- "All settings match defaults \u2014 no config file needed."
292
- );
296
+ log.dim("All settings match defaults \u2014 no config file needed.");
293
297
  return;
294
298
  }
295
299
  const target = await select({
@@ -321,10 +325,20 @@ async function initCommand() {
321
325
  const hasExternalDir = checkLearnConfig() && isExternalOutputDir(resolvedOutputDir);
322
326
  const steps = [
323
327
  { name: "docs", label: "Create docs folders", done: checkDocsFolders() },
324
- ...gitAvailable ? [{ name: "gitignore", label: "Configure .gitignore", done: checkGitignore() }] : [],
328
+ ...gitAvailable ? [
329
+ {
330
+ name: "gitignore",
331
+ label: "Configure .gitignore",
332
+ done: checkGitignore()
333
+ }
334
+ ] : [],
325
335
  { name: "language", label: "Set docs language", done: checkLanguage() },
326
336
  { name: "learn", label: "Configure /cf-learn", done: checkLearnConfig() },
327
- { name: "completion", label: "Setup shell tab completion", done: hasShellCompletion() }
337
+ {
338
+ name: "completion",
339
+ label: "Setup shell tab completion",
340
+ done: hasShellCompletion()
341
+ }
328
342
  ];
329
343
  if (hasExternalDir && resolvedOutputDir) {
330
344
  steps.push({
@@ -405,10 +419,7 @@ async function initCommand() {
405
419
  break;
406
420
  case "permissions":
407
421
  if (resolvedOutputDir) {
408
- await setupClaudePermissions(
409
- resolvedOutputDir,
410
- learnAutoCommit
411
- );
422
+ await setupClaudePermissions(resolvedOutputDir, learnAutoCommit);
412
423
  }
413
424
  break;
414
425
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getLibPath,
3
3
  resolveDocsDir
4
- } from "./chunk-5HZJX47M.js";
4
+ } from "./chunk-Q4DKU5IG.js";
5
5
  import "./chunk-HRVSKMNA.js";
6
6
  import {
7
7
  run
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ensureShellCompletion
4
- } from "./chunk-R6ZYK4UX.js";
4
+ } from "./chunk-CSF4FAHL.js";
5
5
  import "./chunk-6DUFTBTO.js";
6
6
 
7
7
  // src/postinstall.ts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ensureShellCompletion
3
- } from "./chunk-R6ZYK4UX.js";
3
+ } from "./chunk-CSF4FAHL.js";
4
4
  import {
5
5
  commandExists,
6
6
  run,
@@ -126,10 +126,16 @@ async function updateCommand(opts) {
126
126
  const statuslineVersion = getStatuslineVersion();
127
127
  const cliVersion = getCliVersion();
128
128
  const latestCliVersion = getLatestCliVersion();
129
- log.info(`Plugin version: ${currentVersion ? `v${currentVersion}` : chalk.yellow("not found")}`);
130
- log.info(`Latest plugin version: ${latestVersion ? chalk.green(`v${latestVersion}`) : chalk.yellow("unknown (cannot reach GitHub)")}`);
129
+ log.info(
130
+ `Plugin version: ${currentVersion ? `v${currentVersion}` : chalk.yellow("not found")}`
131
+ );
132
+ log.info(
133
+ `Latest plugin version: ${latestVersion ? chalk.green(`v${latestVersion}`) : chalk.yellow("unknown (cannot reach GitHub)")}`
134
+ );
131
135
  log.info(`CLI version: v${cliVersion}`);
132
- log.info(`Latest CLI version: ${latestCliVersion ? chalk.green(`v${latestCliVersion}`) : chalk.yellow("unknown (cannot reach npm)")}`);
136
+ log.info(
137
+ `Latest CLI version: ${latestCliVersion ? chalk.green(`v${latestCliVersion}`) : chalk.yellow("unknown (cannot reach npm)")}`
138
+ );
133
139
  log.info(
134
140
  `Statusline version: ${statuslineVersion ? chalk.green(`v${statuslineVersion}`) : chalk.yellow("not configured")}`
135
141
  );
@@ -140,15 +146,23 @@ async function updateCommand(opts) {
140
146
  "Cannot check latest plugin version. Verify manually at https://github.com/dinhanhthi/coding-friend/releases"
141
147
  );
142
148
  } else if (!currentVersion) {
143
- log.warn("Plugin not installed. Run: claude plugin install coding-friend@coding-friend-marketplace");
149
+ log.warn(
150
+ "Plugin not installed. Run: claude plugin install coding-friend@coding-friend-marketplace"
151
+ );
144
152
  } else {
145
153
  const cmp = semverCompare(currentVersion, latestVersion);
146
154
  if (cmp === 0) {
147
- log.success(`Plugin already on the latest version (${chalk.green(`v${latestVersion}`)}).`);
155
+ log.success(
156
+ `Plugin already on the latest version (${chalk.green(`v${latestVersion}`)}).`
157
+ );
148
158
  } else if (cmp > 0) {
149
- log.info(`Plugin is ahead of latest release (local: ${chalk.cyan(`v${currentVersion}`)}, latest: v${latestVersion}). Skipping.`);
159
+ log.info(
160
+ `Plugin is ahead of latest release (local: ${chalk.cyan(`v${currentVersion}`)}, latest: v${latestVersion}). Skipping.`
161
+ );
150
162
  } else {
151
- log.step(`Plugin update available: ${chalk.yellow(`v${currentVersion}`)} \u2192 ${chalk.green(`v${latestVersion}`)}`);
163
+ log.step(
164
+ `Plugin update available: ${chalk.yellow(`v${currentVersion}`)} \u2192 ${chalk.green(`v${latestVersion}`)}`
165
+ );
152
166
  if (!commandExists("claude")) {
153
167
  log.error(
154
168
  "Claude CLI not found. Install it first, or run: claude plugin update coding-friend@coding-friend-marketplace"
@@ -161,7 +175,9 @@ async function updateCommand(opts) {
161
175
  "coding-friend@coding-friend-marketplace"
162
176
  ]);
163
177
  if (result === null) {
164
- log.error("Plugin update failed. Try manually: claude plugin update coding-friend@coding-friend-marketplace");
178
+ log.error(
179
+ "Plugin update failed. Try manually: claude plugin update coding-friend@coding-friend-marketplace"
180
+ );
165
181
  } else {
166
182
  log.success("Plugin updated!");
167
183
  let newVersion = currentVersion;
@@ -188,15 +204,27 @@ async function updateCommand(opts) {
188
204
  } else {
189
205
  const cmp = semverCompare(cliVersion, latestCliVersion);
190
206
  if (cmp === 0) {
191
- log.success(`CLI already on the latest version (${chalk.green(`v${latestCliVersion}`)}).`);
207
+ log.success(
208
+ `CLI already on the latest version (${chalk.green(`v${latestCliVersion}`)}).`
209
+ );
192
210
  } else if (cmp > 0) {
193
- log.info(`CLI is ahead of latest release (local: ${chalk.cyan(`v${cliVersion}`)}, latest: v${latestCliVersion}). Skipping.`);
211
+ log.info(
212
+ `CLI is ahead of latest release (local: ${chalk.cyan(`v${cliVersion}`)}, latest: v${latestCliVersion}). Skipping.`
213
+ );
194
214
  } else {
195
- log.step(`CLI update available: ${chalk.yellow(`v${cliVersion}`)} \u2192 ${chalk.green(`v${latestCliVersion}`)}`);
215
+ log.step(
216
+ `CLI update available: ${chalk.yellow(`v${cliVersion}`)} \u2192 ${chalk.green(`v${latestCliVersion}`)}`
217
+ );
196
218
  log.step("Updating CLI...");
197
- const result = run("npm", ["install", "-g", "coding-friend-cli@latest"]);
219
+ const result = run("npm", [
220
+ "install",
221
+ "-g",
222
+ "coding-friend-cli@latest"
223
+ ]);
198
224
  if (result === null) {
199
- log.error("CLI update failed. Try manually: npm install -g coding-friend-cli@latest");
225
+ log.error(
226
+ "CLI update failed. Try manually: npm install -g coding-friend-cli@latest"
227
+ );
200
228
  } else {
201
229
  log.success(`CLI updated to ${chalk.green(`v${latestCliVersion}`)}`);
202
230
  }
@@ -208,7 +236,9 @@ async function updateCommand(opts) {
208
236
  if (targetVersion) {
209
237
  log.step("Updating statusline...");
210
238
  if (updateStatusline(targetVersion)) {
211
- log.success(`Statusline updated to ${chalk.green(`v${targetVersion}`)}`);
239
+ log.success(
240
+ `Statusline updated to ${chalk.green(`v${targetVersion}`)}`
241
+ );
212
242
  }
213
243
  } else {
214
244
  log.warn("No cached plugin version found for statusline update.");
@@ -1,3 +1,4 @@
1
1
  node_modules
2
2
  .next
3
+ out
3
4
  public/_pagefind
@@ -1,5 +1,10 @@
1
1
  # Changelog (Learn Host)
2
2
 
3
+ ## v0.0.2 (2026-03-01)
4
+
5
+ - Update header styling for consistency with ecosystem redesign
6
+ - Code formatting and style improvements
7
+
3
8
  ## v0.0.1
4
9
 
5
10
  - Next.js app for hosting learning docs at `localhost:3333`