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.
- package/README.md +29 -6
- package/dist/{chunk-R6ZYK4UX.js → chunk-CSF4FAHL.js} +2 -1
- package/dist/{chunk-5HZJX47M.js → chunk-Q4DKU5IG.js} +3 -5
- package/dist/{dev-LZASFXZZ.js → dev-VMN2JHA6.js} +89 -18
- package/dist/{host-BK6DYFWF.js → host-2WINWEW7.js} +1 -1
- package/dist/index.js +30 -13
- package/dist/{init-2UKYE2KV.js → init-CTCDQKIQ.js} +23 -12
- package/dist/{mcp-CH4SKZSX.js → mcp-43HCE2KD.js} +1 -1
- package/dist/postinstall.js +1 -1
- package/dist/{update-5A2OP6EY.js → update-GGCBM7U4.js} +45 -15
- package/lib/learn-host/.prettierignore +1 -0
- package/lib/learn-host/CHANGELOG.md +5 -0
- package/lib/learn-host/README.md +114 -0
- package/lib/learn-host/next-env.d.ts +1 -1
- package/lib/learn-host/next.config.ts +3 -0
- package/lib/learn-host/package.json +1 -1
- package/lib/learn-host/public/logo.svg +1 -1
- package/lib/learn-host/src/app/apple-icon.svg +1 -1
- package/lib/learn-host/src/app/icon.svg +1 -1
- package/lib/learn-host/src/components/Sidebar.tsx +1 -1
- package/lib/learn-host/src/lib/docs.ts +2 -2
- package/lib/learn-mcp/CHANGELOG.md +5 -0
- package/lib/learn-mcp/README.md +169 -0
- package/lib/learn-mcp/package.json +2 -1
- package/lib/learn-mcp/src/index.ts +1 -1
- package/lib/learn-mcp/src/lib/docs.ts +1 -3
- package/lib/learn-mcp/src/lib/knowledge.ts +2 -1
- package/lib/learn-mcp/src/tools/get-review-list.ts +1 -4
- package/lib/learn-mcp/src/tools/search-docs.ts +1 -4
- package/package.json +2 -2
- package/lib/learn-host/public/_pagefind/fragment/en_1172b3c.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_118ad1c.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_32ab3d8.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_441f1e1.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_4452de4.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_4ae396d.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_58ee89d.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_6dd2225.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_765a297.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_7a4cc4a.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_8050261.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_83eaedf.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_925bc5f.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_95f3dd5.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_96d7a02.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_971f951.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_a446c32.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_a5ee367.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_b11c248.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_b13c52e.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_b5bd69b.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_b625d7d.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_bf63915.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_c52b25b.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_c9db556.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_d1537ee.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_d2e6412.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_d2f47a4.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_d361292.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_d727ec8.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_e11cd8f.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_e481f19.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_eee2805.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/fragment/en_f4de6c4.pf_fragment +0 -0
- package/lib/learn-host/public/_pagefind/index/en_1ecb9d5.pf_index +0 -0
- package/lib/learn-host/public/_pagefind/index/en_37e362b.pf_index +0 -0
- package/lib/learn-host/public/_pagefind/index/en_538eee7.pf_index +0 -0
- package/lib/learn-host/public/_pagefind/index/en_5751dc8.pf_index +0 -0
- package/lib/learn-host/public/_pagefind/index/en_67f794d.pf_index +0 -0
- package/lib/learn-host/public/_pagefind/index/en_7458f81.pf_index +0 -0
- package/lib/learn-host/public/_pagefind/index/en_e21f7e1.pf_index +0 -0
- package/lib/learn-host/public/_pagefind/pagefind-entry.json +0 -1
- package/lib/learn-host/public/_pagefind/pagefind-highlight.js +0 -1064
- package/lib/learn-host/public/_pagefind/pagefind-modular-ui.css +0 -214
- package/lib/learn-host/public/_pagefind/pagefind-modular-ui.js +0 -8
- package/lib/learn-host/public/_pagefind/pagefind-ui.css +0 -1
- package/lib/learn-host/public/_pagefind/pagefind-ui.js +0 -2
- package/lib/learn-host/public/_pagefind/pagefind.en_104569cceb.pf_meta +0 -0
- package/lib/learn-host/public/_pagefind/pagefind.en_1075df6f16.pf_meta +0 -0
- package/lib/learn-host/public/_pagefind/pagefind.en_139f35f6e5.pf_meta +0 -0
- package/lib/learn-host/public/_pagefind/pagefind.en_46bfc9f7e1.pf_meta +0 -0
- package/lib/learn-host/public/_pagefind/pagefind.en_76b8937bbc.pf_meta +0 -0
- package/lib/learn-host/public/_pagefind/pagefind.en_83cbfb0fd5.pf_meta +0 -0
- package/lib/learn-host/public/_pagefind/pagefind.en_b1d168d536.pf_meta +0 -0
- package/lib/learn-host/public/_pagefind/pagefind.js +0 -6
- package/lib/learn-host/public/_pagefind/wasm.en.pagefind +0 -0
- 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)
|
|
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
|
|
33
|
-
if (existsSync(
|
|
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
|
|
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 {
|
|
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(
|
|
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(
|
|
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(
|
|
116
|
+
runClaude(
|
|
117
|
+
["plugin", "marketplace", "remove", MARKETPLACE_NAME],
|
|
118
|
+
"Removing remote marketplace..."
|
|
119
|
+
);
|
|
82
120
|
}
|
|
83
|
-
if (!runClaude(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
170
|
+
runClaude(
|
|
171
|
+
["plugin", "marketplace", "remove", MARKETPLACE_NAME],
|
|
172
|
+
"Removing local marketplace..."
|
|
173
|
+
);
|
|
119
174
|
}
|
|
120
|
-
if (!runClaude(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
304
|
+
log.info(
|
|
305
|
+
`Plugin installed: ${installed ? chalk.green("yes") : chalk.yellow("no")}`
|
|
306
|
+
);
|
|
236
307
|
}
|
|
237
308
|
export {
|
|
238
309
|
devOffCommand,
|
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(
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
33
|
+
const { updateCommand } = await import("./update-GGCBM7U4.js");
|
|
32
34
|
await updateCommand(opts);
|
|
33
35
|
});
|
|
34
|
-
var dev = program.command("dev").description("
|
|
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-
|
|
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-
|
|
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-
|
|
56
|
+
const { devStatusCommand } = await import("./dev-VMN2JHA6.js");
|
|
45
57
|
await devStatusCommand();
|
|
46
58
|
});
|
|
47
|
-
dev.command("sync").description(
|
|
48
|
-
|
|
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(
|
|
52
|
-
|
|
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-
|
|
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(
|
|
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 {
|
|
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 ? [
|
|
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
|
-
{
|
|
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
|
}
|
package/dist/postinstall.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ensureShellCompletion
|
|
3
|
-
} from "./chunk-
|
|
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(
|
|
130
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
155
|
+
log.success(
|
|
156
|
+
`Plugin already on the latest version (${chalk.green(`v${latestVersion}`)}).`
|
|
157
|
+
);
|
|
148
158
|
} else if (cmp > 0) {
|
|
149
|
-
log.info(
|
|
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(
|
|
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(
|
|
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(
|
|
207
|
+
log.success(
|
|
208
|
+
`CLI already on the latest version (${chalk.green(`v${latestCliVersion}`)}).`
|
|
209
|
+
);
|
|
192
210
|
} else if (cmp > 0) {
|
|
193
|
-
log.info(
|
|
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(
|
|
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", [
|
|
219
|
+
const result = run("npm", [
|
|
220
|
+
"install",
|
|
221
|
+
"-g",
|
|
222
|
+
"coding-friend-cli@latest"
|
|
223
|
+
]);
|
|
198
224
|
if (result === null) {
|
|
199
|
-
log.error(
|
|
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(
|
|
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.");
|