coding-friend-cli 1.0.1 → 1.0.3

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
@@ -24,7 +24,10 @@ cf mcp [path] # Setup MCP server for LLM integration
24
24
  # [path] is optional, default is `docs/learn`
25
25
  # This prints a JSON config snippet to add to your client's MCP
26
26
  cf statusline # Setup coding-friend statusline
27
- cf update # Update plugin + fix statusline
27
+ cf update # Update plugin + CLI + statusline
28
+ cf update --cli # Update only the CLI (npm package)
29
+ cf update --plugin # Update only the Claude Code plugin
30
+ cf update --statusline # Update only the statusline
28
31
  cf help # Show all commands
29
32
  ```
30
33
 
@@ -21,6 +21,9 @@ function streamExec(cmd, args, opts) {
21
21
  child.on("error", reject);
22
22
  });
23
23
  }
24
+ function sleepSync(ms) {
25
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
26
+ }
24
27
  function commandExists(cmd) {
25
28
  return run("which", [cmd]) !== null;
26
29
  }
@@ -28,5 +31,6 @@ function commandExists(cmd) {
28
31
  export {
29
32
  run,
30
33
  streamExec,
34
+ sleepSync,
31
35
  commandExists
32
36
  };
@@ -6,7 +6,7 @@ import "./chunk-HRVSKMNA.js";
6
6
  import {
7
7
  run,
8
8
  streamExec
9
- } from "./chunk-6CGGT2FD.js";
9
+ } from "./chunk-UFGNO6CW.js";
10
10
  import "./chunk-AQXTNLQD.js";
11
11
  import {
12
12
  log
package/dist/index.js CHANGED
@@ -12,23 +12,23 @@ var pkg = JSON.parse(
12
12
  var program = new Command();
13
13
  program.name("cf").description("coding-friend CLI \u2014 host learning docs, setup MCP, init projects").version(pkg.version);
14
14
  program.command("init").description("Initialize coding-friend in current project").action(async () => {
15
- const { initCommand } = await import("./init-ONRXFOZ5.js");
15
+ const { initCommand } = await import("./init-E6CL3UZQ.js");
16
16
  await initCommand();
17
17
  });
18
18
  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-3GAEZKKJ.js");
19
+ const { hostCommand } = await import("./host-4CEAT6TL.js");
20
20
  await hostCommand(path, opts);
21
21
  });
22
22
  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-LMMIFH4B.js");
23
+ const { mcpCommand } = await import("./mcp-MWESK6UX.js");
24
24
  await mcpCommand(path);
25
25
  });
26
26
  program.command("statusline").description("Setup coding-friend statusline in Claude Code").action(async () => {
27
27
  const { statuslineCommand } = await import("./statusline-7D6YU5YM.js");
28
28
  await statuslineCommand();
29
29
  });
30
- program.command("update").description("Update coding-friend plugin and refresh statusline").action(async () => {
31
- const { updateCommand } = await import("./update-K5PYOB52.js");
32
- await updateCommand();
30
+ 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-IH3G4SN5.js");
32
+ await updateCommand(opts);
33
33
  });
34
34
  program.parse();
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-VHZQ6KEU.js";
8
8
  import {
9
9
  run
10
- } from "./chunk-6CGGT2FD.js";
10
+ } from "./chunk-UFGNO6CW.js";
11
11
  import {
12
12
  claudeSettingsPath,
13
13
  globalConfigPath,
@@ -5,7 +5,7 @@ import {
5
5
  import "./chunk-HRVSKMNA.js";
6
6
  import {
7
7
  run
8
- } from "./chunk-6CGGT2FD.js";
8
+ } from "./chunk-UFGNO6CW.js";
9
9
  import "./chunk-AQXTNLQD.js";
10
10
  import {
11
11
  log
@@ -0,0 +1,202 @@
1
+ import {
2
+ ensureShellCompletion
3
+ } from "./chunk-VHZQ6KEU.js";
4
+ import {
5
+ commandExists,
6
+ run,
7
+ sleepSync
8
+ } from "./chunk-UFGNO6CW.js";
9
+ import {
10
+ claudeSettingsPath,
11
+ installedPluginsPath,
12
+ pluginCachePath
13
+ } from "./chunk-AQXTNLQD.js";
14
+ import {
15
+ log
16
+ } from "./chunk-6DUFTBTO.js";
17
+ import {
18
+ readJson,
19
+ writeJson
20
+ } from "./chunk-IUTXHCP7.js";
21
+
22
+ // src/commands/update.ts
23
+ import { existsSync, readFileSync, readdirSync } from "fs";
24
+ import { dirname, join } from "path";
25
+ import { fileURLToPath } from "url";
26
+ import chalk from "chalk";
27
+ var __dirname = dirname(fileURLToPath(import.meta.url));
28
+ function getCliVersion() {
29
+ const pkg = JSON.parse(
30
+ readFileSync(join(__dirname, "..", "package.json"), "utf-8")
31
+ );
32
+ return pkg.version;
33
+ }
34
+ function getLatestCliVersion() {
35
+ return run("npm", ["view", "coding-friend-cli", "version"]);
36
+ }
37
+ function getInstalledVersion() {
38
+ const data = readJson(installedPluginsPath());
39
+ if (!data) return null;
40
+ const plugins = data.plugins ?? data;
41
+ for (const [key, value] of Object.entries(plugins)) {
42
+ if (!key.includes("coding-friend")) continue;
43
+ if (Array.isArray(value) && value.length > 0) {
44
+ const entry = value[0];
45
+ if (typeof entry.version === "string") return entry.version;
46
+ }
47
+ if (typeof value === "object" && value !== null && "version" in value) {
48
+ return value.version;
49
+ }
50
+ }
51
+ return null;
52
+ }
53
+ function getLatestVersion() {
54
+ let tag = run("gh", [
55
+ "api",
56
+ "repos/dinhanhthi/coding-friend/releases/latest",
57
+ "--jq",
58
+ ".tag_name"
59
+ ]);
60
+ if (!tag) {
61
+ const json = run("curl", [
62
+ "-s",
63
+ "https://api.github.com/repos/dinhanhthi/coding-friend/releases/latest"
64
+ ]);
65
+ if (json) {
66
+ try {
67
+ const data = JSON.parse(json);
68
+ tag = data.tag_name;
69
+ } catch {
70
+ }
71
+ }
72
+ }
73
+ if (!tag) return null;
74
+ return tag.replace(/^v/, "");
75
+ }
76
+ function getStatuslineVersion() {
77
+ const settings = readJson(claudeSettingsPath());
78
+ if (!settings?.statusLine) return null;
79
+ const sl = settings.statusLine;
80
+ if (!sl.command) return null;
81
+ const match = sl.command.match(
82
+ /coding-friend-marketplace\/coding-friend\/([^/]+)\//
83
+ );
84
+ return match?.[1] ?? null;
85
+ }
86
+ function findLatestCacheVersion() {
87
+ const cachePath = pluginCachePath();
88
+ if (!existsSync(cachePath)) return null;
89
+ const versions = readdirSync(cachePath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
90
+ return versions[0] ?? null;
91
+ }
92
+ function updateStatusline(version) {
93
+ const cachePath = pluginCachePath();
94
+ const hookPath = `${cachePath}/${version}/hooks/statusline.sh`;
95
+ if (!existsSync(hookPath)) {
96
+ log.warn(`Statusline hook not found for v${version}`);
97
+ return false;
98
+ }
99
+ const settingsPath = claudeSettingsPath();
100
+ const settings = readJson(settingsPath) ?? {};
101
+ settings.statusLine = {
102
+ type: "command",
103
+ command: `bash ${hookPath}`
104
+ };
105
+ writeJson(settingsPath, settings);
106
+ return true;
107
+ }
108
+ async function updateCommand(opts) {
109
+ const updateAll = !opts.cli && !opts.plugin && !opts.statusline;
110
+ const doCli = updateAll || !!opts.cli;
111
+ const doPlugin = updateAll || !!opts.plugin;
112
+ const doStatusline = updateAll || !!opts.statusline;
113
+ console.log("=== \u{1F33F} Coding Friend Update \u{1F33F} ===");
114
+ console.log();
115
+ const currentVersion = getInstalledVersion();
116
+ const latestVersion = getLatestVersion();
117
+ const statuslineVersion = getStatuslineVersion();
118
+ const cliVersion = getCliVersion();
119
+ const latestCliVersion = getLatestCliVersion();
120
+ log.info(`Plugin version: ${currentVersion ? `v${currentVersion}` : chalk.yellow("not found")}`);
121
+ log.info(`Latest plugin version: ${latestVersion ? chalk.green(`v${latestVersion}`) : chalk.yellow("unknown (cannot reach GitHub)")}`);
122
+ log.info(`CLI version: v${cliVersion}`);
123
+ log.info(`Latest CLI version: ${latestCliVersion ? chalk.green(`v${latestCliVersion}`) : chalk.yellow("unknown (cannot reach npm)")}`);
124
+ log.info(
125
+ `Statusline version: ${statuslineVersion ? chalk.green(`v${statuslineVersion}`) : chalk.yellow("not configured")}`
126
+ );
127
+ console.log();
128
+ if (doPlugin) {
129
+ if (!latestVersion) {
130
+ log.warn(
131
+ "Cannot check latest plugin version. Verify manually at https://github.com/dinhanhthi/coding-friend/releases"
132
+ );
133
+ } else if (currentVersion === latestVersion) {
134
+ log.success(`Plugin already on the latest version (${chalk.green(`v${latestVersion}`)}).`);
135
+ } else {
136
+ log.step(`Plugin update available: ${chalk.yellow(`v${currentVersion}`)} \u2192 ${chalk.green(`v${latestVersion}`)}`);
137
+ if (!commandExists("claude")) {
138
+ log.error(
139
+ "Claude CLI not found. Install it first, or run: claude plugin update coding-friend@coding-friend-marketplace"
140
+ );
141
+ } else {
142
+ log.step("Updating plugin...");
143
+ const result = run("claude", [
144
+ "plugin",
145
+ "update",
146
+ "coding-friend@coding-friend-marketplace"
147
+ ]);
148
+ if (result === null) {
149
+ log.error("Plugin update failed. Try manually: claude plugin update coding-friend@coding-friend-marketplace");
150
+ } else {
151
+ log.success("Plugin updated!");
152
+ let newVersion = currentVersion;
153
+ for (let i = 0; i < 5; i++) {
154
+ newVersion = getInstalledVersion();
155
+ if (newVersion !== currentVersion) break;
156
+ if (i < 4) sleepSync(1e3);
157
+ }
158
+ if (newVersion !== currentVersion) {
159
+ log.success(`Plugin updated to ${chalk.green(`v${newVersion}`)}`);
160
+ } else {
161
+ log.warn(
162
+ "Version in installed_plugins.json unchanged. Cache may still have been updated."
163
+ );
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }
169
+ if (doCli) {
170
+ if (!latestCliVersion) {
171
+ log.warn("Cannot check latest CLI version from npm.");
172
+ } else if (cliVersion === latestCliVersion) {
173
+ log.success(`CLI already on the latest version (${chalk.green(`v${latestCliVersion}`)}).`);
174
+ } else {
175
+ log.step(`CLI update available: ${chalk.yellow(`v${cliVersion}`)} \u2192 ${chalk.green(`v${latestCliVersion}`)}`);
176
+ log.step("Updating CLI...");
177
+ const result = run("npm", ["install", "-g", "coding-friend-cli@latest"]);
178
+ if (result === null) {
179
+ log.error("CLI update failed. Try manually: npm install -g coding-friend-cli@latest");
180
+ } else {
181
+ log.success(`CLI updated to ${chalk.green(`v${latestCliVersion}`)}`);
182
+ }
183
+ }
184
+ }
185
+ if (doStatusline) {
186
+ const targetVersion = findLatestCacheVersion();
187
+ if (targetVersion) {
188
+ log.step("Updating statusline...");
189
+ if (updateStatusline(targetVersion)) {
190
+ log.success(`Statusline updated to ${chalk.green(`v${targetVersion}`)}`);
191
+ }
192
+ } else {
193
+ log.warn("No cached plugin version found for statusline update.");
194
+ }
195
+ }
196
+ ensureShellCompletion({ silent: false });
197
+ console.log();
198
+ log.dim("Restart Claude Code (or start a new session) to see changes.");
199
+ }
200
+ export {
201
+ updateCommand
202
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coding-friend-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "CLI for coding-friend — host learning docs, setup MCP server, initialize projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,160 +0,0 @@
1
- import {
2
- ensureShellCompletion
3
- } from "./chunk-VHZQ6KEU.js";
4
- import {
5
- commandExists,
6
- run
7
- } from "./chunk-6CGGT2FD.js";
8
- import {
9
- claudeSettingsPath,
10
- installedPluginsPath,
11
- pluginCachePath
12
- } from "./chunk-AQXTNLQD.js";
13
- import {
14
- log
15
- } from "./chunk-6DUFTBTO.js";
16
- import {
17
- readJson,
18
- writeJson
19
- } from "./chunk-IUTXHCP7.js";
20
-
21
- // src/commands/update.ts
22
- import { existsSync, readdirSync } from "fs";
23
- import chalk from "chalk";
24
- function getInstalledVersion() {
25
- const data = readJson(installedPluginsPath());
26
- if (!data) return null;
27
- const plugins = data.plugins ?? data;
28
- for (const [key, value] of Object.entries(plugins)) {
29
- if (!key.includes("coding-friend")) continue;
30
- if (Array.isArray(value) && value.length > 0) {
31
- const entry = value[0];
32
- if (typeof entry.version === "string") return entry.version;
33
- }
34
- if (typeof value === "object" && value !== null && "version" in value) {
35
- return value.version;
36
- }
37
- }
38
- return null;
39
- }
40
- function getLatestVersion() {
41
- let tag = run("gh", [
42
- "api",
43
- "repos/dinhanhthi/coding-friend/releases/latest",
44
- "--jq",
45
- ".tag_name"
46
- ]);
47
- if (!tag) {
48
- const json = run("curl", [
49
- "-s",
50
- "https://api.github.com/repos/dinhanhthi/coding-friend/releases/latest"
51
- ]);
52
- if (json) {
53
- try {
54
- const data = JSON.parse(json);
55
- tag = data.tag_name;
56
- } catch {
57
- }
58
- }
59
- }
60
- if (!tag) return null;
61
- return tag.replace(/^v/, "");
62
- }
63
- function getStatuslineVersion() {
64
- const settings = readJson(claudeSettingsPath());
65
- if (!settings?.statusLine) return null;
66
- const sl = settings.statusLine;
67
- if (!sl.command) return null;
68
- const match = sl.command.match(
69
- /coding-friend-marketplace\/coding-friend\/([^/]+)\//
70
- );
71
- return match?.[1] ?? null;
72
- }
73
- function findLatestCacheVersion() {
74
- const cachePath = pluginCachePath();
75
- if (!existsSync(cachePath)) return null;
76
- const versions = readdirSync(cachePath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
77
- return versions[0] ?? null;
78
- }
79
- function updateStatusline(version) {
80
- const cachePath = pluginCachePath();
81
- const hookPath = `${cachePath}/${version}/hooks/statusline.sh`;
82
- if (!existsSync(hookPath)) {
83
- log.warn(`Statusline hook not found for v${version}`);
84
- return false;
85
- }
86
- const settingsPath = claudeSettingsPath();
87
- const settings = readJson(settingsPath) ?? {};
88
- settings.statusLine = {
89
- type: "command",
90
- command: `bash ${hookPath}`
91
- };
92
- writeJson(settingsPath, settings);
93
- return true;
94
- }
95
- async function updateCommand() {
96
- console.log("=== \u{1F33F} Coding Friend Update \u{1F33F} ===");
97
- console.log();
98
- const currentVersion = getInstalledVersion();
99
- const latestVersion = getLatestVersion();
100
- const statuslineVersion = getStatuslineVersion();
101
- log.info(`Installed version: ${currentVersion ? chalk.green(`v${currentVersion}`) : chalk.yellow("not found")}`);
102
- log.info(`Latest version: ${latestVersion ? chalk.green(`v${latestVersion}`) : chalk.yellow("unknown (cannot reach GitHub)")}`);
103
- log.info(
104
- `Statusline version: ${statuslineVersion ? chalk.green(`v${statuslineVersion}`) : chalk.yellow("not configured")}`
105
- );
106
- console.log();
107
- if (!latestVersion) {
108
- log.warn(
109
- "Cannot check latest version. Verify manually at https://github.com/dinhanhthi/coding-friend/releases"
110
- );
111
- return;
112
- }
113
- const isUpToDate = currentVersion === latestVersion;
114
- const statuslineMismatch = statuslineVersion !== null && statuslineVersion !== (currentVersion ?? latestVersion);
115
- if (isUpToDate && !statuslineMismatch) {
116
- log.success(`Already on the latest version (${chalk.green(`v${latestVersion}`)}). No update needed.`);
117
- return;
118
- }
119
- if (!isUpToDate) {
120
- log.step(`Update available: ${chalk.yellow(`v${currentVersion}`)} \u2192 ${chalk.green(`v${latestVersion}`)}`);
121
- if (!commandExists("claude")) {
122
- log.error(
123
- "Claude CLI not found. Install it first, or run: claude plugin update coding-friend@coding-friend-marketplace"
124
- );
125
- return;
126
- }
127
- log.step("Updating plugin...");
128
- const result = run("claude", [
129
- "plugin",
130
- "update",
131
- "coding-friend@coding-friend-marketplace"
132
- ]);
133
- if (result === null) {
134
- log.error("Plugin update failed. Try manually: claude plugin update coding-friend@coding-friend-marketplace");
135
- return;
136
- }
137
- log.success("Plugin updated!");
138
- const newVersion = getInstalledVersion();
139
- if (newVersion === currentVersion) {
140
- log.warn(
141
- "Version unchanged after update. Restart Claude Code and try cf update again."
142
- );
143
- return;
144
- }
145
- log.success(`Updated to ${chalk.green(`v${newVersion}`)}`);
146
- }
147
- const targetVersion = findLatestCacheVersion();
148
- if (targetVersion) {
149
- log.step("Updating statusline...");
150
- if (updateStatusline(targetVersion)) {
151
- log.success(`Statusline updated to ${chalk.green(`v${targetVersion}`)}`);
152
- }
153
- }
154
- ensureShellCompletion({ silent: false });
155
- console.log();
156
- log.dim("Restart Claude Code (or start a new session) to see changes.");
157
- }
158
- export {
159
- updateCommand
160
- };