coding-friend-cli 1.14.0 → 1.16.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/dist/{chunk-PYRGNY5P.js → chunk-D4EWPGBL.js} +8 -0
- package/dist/{chunk-IZFUAIP3.js → chunk-QNLL3ZDF.js} +14 -1
- package/dist/{config-UQ742WPQ.js → config-AIZJJ5D2.js} +1 -1
- package/dist/{dev-X7OJPTTW.js → dev-WJ5QQ35B.js} +21 -5
- package/dist/{disable-AOZ7FLZD.js → disable-JDVOQNZG.js} +1 -1
- package/dist/{enable-MJVTT3RU.js → enable-JBJ4Q2S7.js} +1 -1
- package/dist/index.js +13 -13
- package/dist/{init-AHIEQ27W.js → init-FZ3GG53E.js} +171 -19
- package/dist/{install-YXZGP2MY.js → install-I3GOS56Q.js} +2 -2
- package/dist/{uninstall-2IOZZERP.js → uninstall-JN5YIKKM.js} +1 -1
- package/dist/{update-OADURRFT.js → update-OWS4IJTG.js} +2 -2
- package/lib/learn-host/CHANGELOG.md +0 -1
- package/package.json +1 -1
|
@@ -116,6 +116,13 @@ function getMergedValue(key, globalCfg, localCfg) {
|
|
|
116
116
|
if (localVal !== void 0) return localVal;
|
|
117
117
|
return globalCfg ? globalCfg[key] : void 0;
|
|
118
118
|
}
|
|
119
|
+
function ensureDocsFolders(docsDir, subfolders) {
|
|
120
|
+
if (!existsSync(docsDir)) {
|
|
121
|
+
run("mkdir", ["-p", docsDir]);
|
|
122
|
+
log.success(`Created "${docsDir}"`);
|
|
123
|
+
}
|
|
124
|
+
createSubfolders(docsDir, subfolders);
|
|
125
|
+
}
|
|
119
126
|
function createSubfolders(docsDir, subfolders) {
|
|
120
127
|
const created = [];
|
|
121
128
|
for (const sub of subfolders) {
|
|
@@ -177,5 +184,6 @@ export {
|
|
|
177
184
|
getScopeLabel,
|
|
178
185
|
formatScopeLabel,
|
|
179
186
|
getMergedValue,
|
|
187
|
+
ensureDocsFolders,
|
|
180
188
|
applyDocsDirChange
|
|
181
189
|
};
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-KJUGTLPQ.js";
|
|
8
8
|
import {
|
|
9
9
|
resolveScope
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-D4EWPGBL.js";
|
|
11
11
|
import {
|
|
12
12
|
commandExists,
|
|
13
13
|
run,
|
|
@@ -172,6 +172,19 @@ async function updateCommand(opts) {
|
|
|
172
172
|
if (newVersion !== currentVersion) break;
|
|
173
173
|
if (i < 2) sleepSync(1e3);
|
|
174
174
|
}
|
|
175
|
+
if (newVersion === currentVersion) {
|
|
176
|
+
log.dim("Marketplace cache may be stale. Trying reinstall...");
|
|
177
|
+
const installArgs = [
|
|
178
|
+
"plugin",
|
|
179
|
+
"install",
|
|
180
|
+
"coding-friend@coding-friend-marketplace"
|
|
181
|
+
];
|
|
182
|
+
if (scope) {
|
|
183
|
+
installArgs.push("--scope", scope);
|
|
184
|
+
}
|
|
185
|
+
run("claude", installArgs);
|
|
186
|
+
newVersion = getInstalledVersion();
|
|
187
|
+
}
|
|
175
188
|
if (newVersion !== currentVersion) {
|
|
176
189
|
log.success(`Plugin updated to ${chalk.green(`v${newVersion}`)}`);
|
|
177
190
|
} else {
|
|
@@ -42,6 +42,16 @@ var PLUGIN_ID = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
|
|
|
42
42
|
function getDevState() {
|
|
43
43
|
return readJson(devStatePath());
|
|
44
44
|
}
|
|
45
|
+
function getLocalPluginVersion(localPath) {
|
|
46
|
+
const pluginJsonPath = resolve(
|
|
47
|
+
localPath,
|
|
48
|
+
"plugin",
|
|
49
|
+
".claude-plugin",
|
|
50
|
+
"plugin.json"
|
|
51
|
+
);
|
|
52
|
+
const data = readJson(pluginJsonPath);
|
|
53
|
+
return data?.version ?? null;
|
|
54
|
+
}
|
|
45
55
|
function ensureClaude() {
|
|
46
56
|
if (!commandExists("claude")) {
|
|
47
57
|
log.error(
|
|
@@ -74,9 +84,13 @@ async function devOnCommand(path) {
|
|
|
74
84
|
log.dim("Make sure you point to the coding-friend repo root.");
|
|
75
85
|
return;
|
|
76
86
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
87
|
+
const version = getLocalPluginVersion(localPath);
|
|
88
|
+
const versionLabel = version ? ` ${chalk.dim(`(v${version})`)}` : "";
|
|
89
|
+
console.log(
|
|
90
|
+
`
|
|
91
|
+
=== ${chalk.green("Switching to local dev mode")}${versionLabel} ===
|
|
92
|
+
`
|
|
93
|
+
);
|
|
80
94
|
log.info(`Local path: ${chalk.cyan(localPath)}`);
|
|
81
95
|
if (isPluginInstalled()) {
|
|
82
96
|
if (!runClaude(
|
|
@@ -117,7 +131,7 @@ async function devOnCommand(path) {
|
|
|
117
131
|
ensureShellCompletion({ silent: true });
|
|
118
132
|
console.log();
|
|
119
133
|
log.success(
|
|
120
|
-
`Dev mode ${chalk.green("ON")} \u2014 using local plugin from ${chalk.cyan(localPath)}`
|
|
134
|
+
`Dev mode ${chalk.green("ON")} \u2014 using local plugin${versionLabel} from ${chalk.cyan(localPath)}`
|
|
121
135
|
);
|
|
122
136
|
log.dim("Restart Claude Code to see changes.");
|
|
123
137
|
}
|
|
@@ -247,8 +261,10 @@ async function devReinstall(path, label) {
|
|
|
247
261
|
const state = getDevState();
|
|
248
262
|
if (!ensureClaude()) return;
|
|
249
263
|
const localPath = path ?? state?.localPath;
|
|
264
|
+
const version = localPath ? getLocalPluginVersion(localPath) : null;
|
|
265
|
+
const versionLabel = version ? ` ${chalk.dim(`(v${version})`)}` : "";
|
|
250
266
|
console.log(`
|
|
251
|
-
=== ${chalk.cyan(label)} ===
|
|
267
|
+
=== ${chalk.cyan(label)}${versionLabel} ===
|
|
252
268
|
`);
|
|
253
269
|
if (state) {
|
|
254
270
|
await devOffCommand();
|
package/dist/index.js
CHANGED
|
@@ -14,27 +14,27 @@ program.name("cf").description(
|
|
|
14
14
|
"coding-friend CLI \u2014 host learning docs, setup MCP, init projects"
|
|
15
15
|
).version(pkg.version, "-v, --version");
|
|
16
16
|
program.command("install").description("Install the Coding Friend plugin into Claude Code").option("--user", "Install at user scope (all projects)").option("--global", "Install at user scope (all projects)").option("--project", "Install at project scope (shared via git)").option("--local", "Install at local scope (this machine only)").action(async (opts) => {
|
|
17
|
-
const { installCommand } = await import("./install-
|
|
17
|
+
const { installCommand } = await import("./install-I3GOS56Q.js");
|
|
18
18
|
await installCommand(opts);
|
|
19
19
|
});
|
|
20
20
|
program.command("uninstall").description("Uninstall the Coding Friend plugin from Claude Code").option("--user", "Uninstall from user scope (all projects)").option("--global", "Uninstall from user scope (all projects)").option("--project", "Uninstall from project scope").option("--local", "Uninstall from local scope").action(async (opts) => {
|
|
21
|
-
const { uninstallCommand } = await import("./uninstall-
|
|
21
|
+
const { uninstallCommand } = await import("./uninstall-JN5YIKKM.js");
|
|
22
22
|
await uninstallCommand(opts);
|
|
23
23
|
});
|
|
24
24
|
program.command("disable").description("Disable the Coding Friend plugin without uninstalling").option("--user", "Disable at user scope (all projects)").option("--global", "Disable at user scope (all projects)").option("--project", "Disable at project scope").option("--local", "Disable at local scope").action(async (opts) => {
|
|
25
|
-
const { disableCommand } = await import("./disable-
|
|
25
|
+
const { disableCommand } = await import("./disable-JDVOQNZG.js");
|
|
26
26
|
await disableCommand(opts);
|
|
27
27
|
});
|
|
28
28
|
program.command("enable").description("Re-enable the Coding Friend plugin").option("--user", "Enable at user scope (all projects)").option("--global", "Enable at user scope (all projects)").option("--project", "Enable at project scope").option("--local", "Enable at local scope").action(async (opts) => {
|
|
29
|
-
const { enableCommand } = await import("./enable-
|
|
29
|
+
const { enableCommand } = await import("./enable-JBJ4Q2S7.js");
|
|
30
30
|
await enableCommand(opts);
|
|
31
31
|
});
|
|
32
32
|
program.command("init").description("Initialize coding-friend in current project").action(async () => {
|
|
33
|
-
const { initCommand } = await import("./init-
|
|
33
|
+
const { initCommand } = await import("./init-FZ3GG53E.js");
|
|
34
34
|
await initCommand();
|
|
35
35
|
});
|
|
36
36
|
program.command("config").description("Manage Coding Friend configuration").action(async () => {
|
|
37
|
-
const { configCommand } = await import("./config-
|
|
37
|
+
const { configCommand } = await import("./config-AIZJJ5D2.js");
|
|
38
38
|
await configCommand();
|
|
39
39
|
});
|
|
40
40
|
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) => {
|
|
@@ -54,7 +54,7 @@ program.command("statusline").description("Setup coding-friend statusline in Cla
|
|
|
54
54
|
await statuslineCommand();
|
|
55
55
|
});
|
|
56
56
|
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").option("--user", "Update plugin at user scope (all projects)").option("--global", "Update plugin at user scope (all projects)").option("--project", "Update plugin at project scope").option("--local", "Update plugin at local scope").action(async (opts) => {
|
|
57
|
-
const { updateCommand } = await import("./update-
|
|
57
|
+
const { updateCommand } = await import("./update-OWS4IJTG.js");
|
|
58
58
|
await updateCommand(opts);
|
|
59
59
|
});
|
|
60
60
|
var session = program.command("session").description("Save and load Claude Code sessions across machines");
|
|
@@ -89,35 +89,35 @@ Dev subcommands:
|
|
|
89
89
|
dev update [path] Update local dev plugin to latest version`
|
|
90
90
|
);
|
|
91
91
|
dev.command("on").description("Switch to local plugin source").argument("[path]", "path to local coding-friend repo (default: cwd)").action(async (path) => {
|
|
92
|
-
const { devOnCommand } = await import("./dev-
|
|
92
|
+
const { devOnCommand } = await import("./dev-WJ5QQ35B.js");
|
|
93
93
|
await devOnCommand(path);
|
|
94
94
|
});
|
|
95
95
|
dev.command("off").description("Switch back to remote marketplace").action(async () => {
|
|
96
|
-
const { devOffCommand } = await import("./dev-
|
|
96
|
+
const { devOffCommand } = await import("./dev-WJ5QQ35B.js");
|
|
97
97
|
await devOffCommand();
|
|
98
98
|
});
|
|
99
99
|
dev.command("status").description("Show current dev mode").action(async () => {
|
|
100
|
-
const { devStatusCommand } = await import("./dev-
|
|
100
|
+
const { devStatusCommand } = await import("./dev-WJ5QQ35B.js");
|
|
101
101
|
await devStatusCommand();
|
|
102
102
|
});
|
|
103
103
|
dev.command("sync").description(
|
|
104
104
|
"Copy local source files to plugin cache (no version bump needed)"
|
|
105
105
|
).action(async () => {
|
|
106
|
-
const { devSyncCommand } = await import("./dev-
|
|
106
|
+
const { devSyncCommand } = await import("./dev-WJ5QQ35B.js");
|
|
107
107
|
await devSyncCommand();
|
|
108
108
|
});
|
|
109
109
|
dev.command("restart").description("Reinstall local dev plugin (off + on)").argument(
|
|
110
110
|
"[path]",
|
|
111
111
|
"path to local coding-friend repo (default: saved path or cwd)"
|
|
112
112
|
).action(async (path) => {
|
|
113
|
-
const { devRestartCommand } = await import("./dev-
|
|
113
|
+
const { devRestartCommand } = await import("./dev-WJ5QQ35B.js");
|
|
114
114
|
await devRestartCommand(path);
|
|
115
115
|
});
|
|
116
116
|
dev.command("update").description("Update local dev plugin to latest version (off + on)").argument(
|
|
117
117
|
"[path]",
|
|
118
118
|
"path to local coding-friend repo (default: saved path or cwd)"
|
|
119
119
|
).action(async (path) => {
|
|
120
|
-
const { devUpdateCommand } = await import("./dev-
|
|
120
|
+
const { devUpdateCommand } = await import("./dev-WJ5QQ35B.js");
|
|
121
121
|
await devUpdateCommand(path);
|
|
122
122
|
});
|
|
123
123
|
program.parse();
|
|
@@ -22,12 +22,13 @@ import {
|
|
|
22
22
|
BACK,
|
|
23
23
|
applyDocsDirChange,
|
|
24
24
|
askScope,
|
|
25
|
+
ensureDocsFolders,
|
|
25
26
|
formatScopeLabel,
|
|
26
27
|
getMergedValue,
|
|
27
28
|
getScopeLabel,
|
|
28
29
|
injectBackChoice,
|
|
29
30
|
showConfigHint
|
|
30
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-D4EWPGBL.js";
|
|
31
32
|
import {
|
|
32
33
|
commandExists,
|
|
33
34
|
run
|
|
@@ -84,6 +85,75 @@ function isGitRepo() {
|
|
|
84
85
|
function escapeRegExp(str) {
|
|
85
86
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
86
87
|
}
|
|
88
|
+
function hasGitignoreBlock() {
|
|
89
|
+
if (!existsSync(".gitignore")) return false;
|
|
90
|
+
const content = readFileSync(".gitignore", "utf-8");
|
|
91
|
+
return content.includes(GITIGNORE_START) || content.includes("# coding-friend");
|
|
92
|
+
}
|
|
93
|
+
function paddedScopeLabel(scope) {
|
|
94
|
+
const label = formatScopeLabel(scope);
|
|
95
|
+
const visibleLen = scope.length + 2;
|
|
96
|
+
return label + " ".repeat(Math.max(1, 9 - visibleLen));
|
|
97
|
+
}
|
|
98
|
+
function printSetupStatus(globalCfg, localCfg, gitAvailable) {
|
|
99
|
+
const docsDir = getDocsDir(globalCfg, localCfg);
|
|
100
|
+
const subfolders = ["plans", "memory", "research", "learn", "sessions"];
|
|
101
|
+
const folderStatus = subfolders.map((sub) => ({
|
|
102
|
+
name: `${docsDir}/${sub}`,
|
|
103
|
+
exists: existsSync(`${docsDir}/${sub}`)
|
|
104
|
+
}));
|
|
105
|
+
const foldersReady = folderStatus.filter((f) => f.exists).length;
|
|
106
|
+
const missingFolders = subfolders.length - foldersReady;
|
|
107
|
+
const allFoldersDone = missingFolders === 0;
|
|
108
|
+
const countColor = allFoldersDone ? chalk.green : chalk.yellow;
|
|
109
|
+
console.log(
|
|
110
|
+
` ${chalk.bold("Folders")} ${countColor(`(${foldersReady}/${subfolders.length})`)}:`
|
|
111
|
+
);
|
|
112
|
+
for (const f of folderStatus) {
|
|
113
|
+
const icon = f.exists ? chalk.green("\u2713") : chalk.red("\u2717");
|
|
114
|
+
const name = f.exists ? chalk.dim(f.name) : f.name;
|
|
115
|
+
console.log(` ${icon} ${name}`);
|
|
116
|
+
}
|
|
117
|
+
console.log();
|
|
118
|
+
const configKeys = [
|
|
119
|
+
{ key: "docsDir", label: "Docs folder" },
|
|
120
|
+
{ key: "language", label: "Docs language" },
|
|
121
|
+
{ key: "learn", label: "/cf-learn config" }
|
|
122
|
+
];
|
|
123
|
+
let notLocalCount = 0;
|
|
124
|
+
let notConfiguredCount = 0;
|
|
125
|
+
console.log(` ${chalk.bold("Settings")}:`);
|
|
126
|
+
for (const s of configKeys) {
|
|
127
|
+
const scope = getScopeLabel(s.key, globalCfg, localCfg);
|
|
128
|
+
const value = getMergedValue(s.key, globalCfg, localCfg);
|
|
129
|
+
const valueStr = value && typeof value === "string" ? ` (${chalk.dim(value)})` : "";
|
|
130
|
+
console.log(` ${paddedScopeLabel(scope)}${s.label}${valueStr}`);
|
|
131
|
+
if (scope === "-") notConfiguredCount++;
|
|
132
|
+
if (scope === "-" || scope === "global") notLocalCount++;
|
|
133
|
+
}
|
|
134
|
+
const setupItems = [
|
|
135
|
+
{
|
|
136
|
+
label: ".gitignore",
|
|
137
|
+
done: !gitAvailable || hasGitignoreBlock(),
|
|
138
|
+
skipped: !gitAvailable
|
|
139
|
+
},
|
|
140
|
+
{ label: "Shell completion", done: hasShellCompletion(), skipped: false },
|
|
141
|
+
{ label: "Statusline", done: isStatuslineConfigured(), skipped: false }
|
|
142
|
+
];
|
|
143
|
+
for (const item of setupItems) {
|
|
144
|
+
if (item.skipped) {
|
|
145
|
+
console.log(` ${paddedScopeLabel("skip")}${item.label}`);
|
|
146
|
+
} else if (item.done) {
|
|
147
|
+
console.log(` ${paddedScopeLabel("done")}${item.label}`);
|
|
148
|
+
} else {
|
|
149
|
+
console.log(` ${paddedScopeLabel("-")}${item.label}`);
|
|
150
|
+
notConfiguredCount++;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
console.log();
|
|
154
|
+
const allDone = allFoldersDone && notConfiguredCount === 0;
|
|
155
|
+
return { allDone, notLocalCount, notConfiguredCount, missingFolders };
|
|
156
|
+
}
|
|
87
157
|
function writeToScope(scope, data) {
|
|
88
158
|
const targetPath = scope === "global" ? globalConfigPath() : localConfigPath();
|
|
89
159
|
mergeJson(targetPath, data);
|
|
@@ -99,6 +169,27 @@ function handleBack(value) {
|
|
|
99
169
|
function getDocsDir(globalCfg, localCfg) {
|
|
100
170
|
return localCfg?.docsDir ?? globalCfg?.docsDir ?? DEFAULT_CONFIG.docsDir;
|
|
101
171
|
}
|
|
172
|
+
async function offerGlobalShortcut(globalDisplay) {
|
|
173
|
+
const choice = await select({
|
|
174
|
+
message: "How to configure?",
|
|
175
|
+
choices: injectBackChoice(
|
|
176
|
+
[
|
|
177
|
+
{
|
|
178
|
+
name: `Use global setting (${globalDisplay})`,
|
|
179
|
+
value: "use_global"
|
|
180
|
+
},
|
|
181
|
+
{ name: "Configure manually", value: "configure" }
|
|
182
|
+
],
|
|
183
|
+
"Cancel init"
|
|
184
|
+
)
|
|
185
|
+
});
|
|
186
|
+
handleBack(choice);
|
|
187
|
+
if (choice === "use_global") {
|
|
188
|
+
log.dim("Using global setting.");
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
102
193
|
async function stepDocsDir(globalCfg, localCfg) {
|
|
103
194
|
const currentValue = getMergedValue("docsDir", globalCfg, localCfg);
|
|
104
195
|
const scopeLabel = getScopeLabel("docsDir", globalCfg, localCfg);
|
|
@@ -106,6 +197,18 @@ async function stepDocsDir(globalCfg, localCfg) {
|
|
|
106
197
|
`Docs folder name ${formatScopeLabel(scopeLabel)}${currentValue ? ` (${chalk.dim(currentValue)})` : ""}`,
|
|
107
198
|
"Where plans, memory, research, and session docs are stored in your project."
|
|
108
199
|
);
|
|
200
|
+
const globalValue = globalCfg?.docsDir;
|
|
201
|
+
if (globalValue && await offerGlobalShortcut(globalValue)) {
|
|
202
|
+
const DOCS_SUBFOLDERS2 = [
|
|
203
|
+
"plans",
|
|
204
|
+
"memory",
|
|
205
|
+
"research",
|
|
206
|
+
"learn",
|
|
207
|
+
"sessions"
|
|
208
|
+
];
|
|
209
|
+
ensureDocsFolders(globalValue, DOCS_SUBFOLDERS2);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
109
212
|
const value = await input({
|
|
110
213
|
message: "Docs folder name:",
|
|
111
214
|
default: currentValue ?? DEFAULT_CONFIG.docsDir,
|
|
@@ -126,11 +229,7 @@ async function stepDocsDir(globalCfg, localCfg) {
|
|
|
126
229
|
writeToScope(scope, { docsDir: value });
|
|
127
230
|
}
|
|
128
231
|
async function stepGitignore(docsDir) {
|
|
129
|
-
const hasBlock = (
|
|
130
|
-
if (!existsSync(".gitignore")) return false;
|
|
131
|
-
const content = readFileSync(".gitignore", "utf-8");
|
|
132
|
-
return content.includes(GITIGNORE_START) || content.includes("# coding-friend");
|
|
133
|
-
})();
|
|
232
|
+
const hasBlock = hasGitignoreBlock();
|
|
134
233
|
if (hasBlock) {
|
|
135
234
|
printStepHeader(
|
|
136
235
|
`Configure .gitignore ${chalk.green("[done]")}`,
|
|
@@ -206,6 +305,8 @@ async function stepDocsLanguage(globalCfg, localCfg) {
|
|
|
206
305
|
`Docs language ${formatScopeLabel(scopeLabel)}${currentValue ? ` (${chalk.dim(currentValue)})` : ""}`,
|
|
207
306
|
"Skills like /cf-plan, /cf-ask, /cf-remember will write docs in this language."
|
|
208
307
|
);
|
|
308
|
+
const globalValue = globalCfg?.language;
|
|
309
|
+
if (globalValue && await offerGlobalShortcut(globalValue)) return;
|
|
209
310
|
const lang = await selectLanguage(
|
|
210
311
|
"What language should generated docs be written in?"
|
|
211
312
|
);
|
|
@@ -243,6 +344,25 @@ async function stepLearnConfig(globalCfg, localCfg, gitAvailable) {
|
|
|
243
344
|
`/cf-learn config ${formatScopeLabel(scopeLabel)}`,
|
|
244
345
|
"Controls where and how /cf-learn saves your learning notes."
|
|
245
346
|
);
|
|
347
|
+
const globalLearn = globalCfg?.learn;
|
|
348
|
+
if (globalLearn) {
|
|
349
|
+
const parts = [
|
|
350
|
+
globalLearn.language || "en",
|
|
351
|
+
globalLearn.outputDir || `${docsDir}/learn`
|
|
352
|
+
];
|
|
353
|
+
if (globalLearn.categories) {
|
|
354
|
+
parts.push(`${globalLearn.categories.length} categories`);
|
|
355
|
+
}
|
|
356
|
+
if (await offerGlobalShortcut(parts.join(", "))) {
|
|
357
|
+
const gOutputDir = globalLearn.outputDir || `${docsDir}/learn`;
|
|
358
|
+
const gIsExternal = !gOutputDir.startsWith(`${docsDir}/`);
|
|
359
|
+
return {
|
|
360
|
+
outputDir: gOutputDir,
|
|
361
|
+
autoCommit: globalLearn.autoCommit || false,
|
|
362
|
+
isExternal: gIsExternal
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
}
|
|
246
366
|
const language = await selectLanguage(
|
|
247
367
|
"What language should /cf-learn notes be written in?"
|
|
248
368
|
);
|
|
@@ -472,20 +592,52 @@ async function initCommand() {
|
|
|
472
592
|
}
|
|
473
593
|
const globalCfg = readJson(globalConfigPath());
|
|
474
594
|
const localCfg = readJson(localConfigPath());
|
|
475
|
-
console.log("
|
|
476
|
-
const docsDirScope = getScopeLabel("docsDir", globalCfg, localCfg);
|
|
477
|
-
const docsDirVal = getMergedValue("docsDir", globalCfg, localCfg);
|
|
478
|
-
console.log(
|
|
479
|
-
` ${formatScopeLabel(docsDirScope)} docsDir${docsDirVal ? ` (${chalk.dim(docsDirVal)})` : ""}`
|
|
480
|
-
);
|
|
481
|
-
const langScope = getScopeLabel("language", globalCfg, localCfg);
|
|
482
|
-
const langVal = getMergedValue("language", globalCfg, localCfg);
|
|
483
|
-
console.log(
|
|
484
|
-
` ${formatScopeLabel(langScope)} Docs language${langVal ? ` (${chalk.dim(langVal)})` : ""}`
|
|
485
|
-
);
|
|
486
|
-
const learnScope = getScopeLabel("learn", globalCfg, localCfg);
|
|
487
|
-
console.log(` ${formatScopeLabel(learnScope)} /cf-learn config`);
|
|
595
|
+
console.log("Project status:");
|
|
488
596
|
console.log();
|
|
597
|
+
const { allDone, notLocalCount, notConfiguredCount, missingFolders } = printSetupStatus(globalCfg, localCfg, gitAvailable);
|
|
598
|
+
if (allDone) {
|
|
599
|
+
if (notLocalCount > 0) {
|
|
600
|
+
console.log(
|
|
601
|
+
chalk.dim(
|
|
602
|
+
` ${notLocalCount} setting(s) inherited from global config only.`
|
|
603
|
+
)
|
|
604
|
+
);
|
|
605
|
+
console.log();
|
|
606
|
+
}
|
|
607
|
+
log.success("All settings configured!");
|
|
608
|
+
console.log();
|
|
609
|
+
const proceed = await confirm({
|
|
610
|
+
message: "Modify settings?",
|
|
611
|
+
default: false
|
|
612
|
+
});
|
|
613
|
+
if (!proceed) {
|
|
614
|
+
log.dim("No changes. Run `cf init` anytime to reconfigure.");
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
} else {
|
|
618
|
+
const parts = [];
|
|
619
|
+
if (missingFolders > 0) {
|
|
620
|
+
parts.push(`${missingFolders} folder(s) missing`);
|
|
621
|
+
}
|
|
622
|
+
if (notConfiguredCount > 0) {
|
|
623
|
+
parts.push(`${notConfiguredCount} setting(s) not configured`);
|
|
624
|
+
}
|
|
625
|
+
if (notLocalCount > 0) {
|
|
626
|
+
parts.push(`${notLocalCount} not set locally`);
|
|
627
|
+
}
|
|
628
|
+
if (parts.length > 0) {
|
|
629
|
+
console.log(` ${chalk.yellow("\u26A0")} ${parts.join(" \xB7 ")}`);
|
|
630
|
+
console.log();
|
|
631
|
+
}
|
|
632
|
+
const proceed = await confirm({
|
|
633
|
+
message: "Run setup wizard?",
|
|
634
|
+
default: true
|
|
635
|
+
});
|
|
636
|
+
if (!proceed) {
|
|
637
|
+
log.dim("Init cancelled. Run `cf init` anytime to resume.");
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
489
641
|
await stepDocsDir(globalCfg, localCfg);
|
|
490
642
|
const updatedGlobal = readJson(globalConfigPath());
|
|
491
643
|
const updatedLocal = readJson(localConfigPath());
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getLatestVersion,
|
|
3
3
|
semverCompare
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-QNLL3ZDF.js";
|
|
5
5
|
import {
|
|
6
6
|
enableMarketplaceAutoUpdate,
|
|
7
7
|
isMarketplaceRegistered,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "./chunk-KJUGTLPQ.js";
|
|
17
17
|
import {
|
|
18
18
|
resolveScope
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-D4EWPGBL.js";
|
|
20
20
|
import {
|
|
21
21
|
commandExists,
|
|
22
22
|
run
|
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
getLatestVersion,
|
|
3
3
|
semverCompare,
|
|
4
4
|
updateCommand
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-QNLL3ZDF.js";
|
|
6
6
|
import "./chunk-ORACWEDN.js";
|
|
7
7
|
import "./chunk-POC2WHU2.js";
|
|
8
8
|
import "./chunk-KJUGTLPQ.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-D4EWPGBL.js";
|
|
10
10
|
import "./chunk-X5WEODUD.js";
|
|
11
11
|
import "./chunk-RWUTFVRB.js";
|
|
12
12
|
import "./chunk-W5CD7WTX.js";
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
## v0.2.1 (2026-03-05)
|
|
4
4
|
|
|
5
|
-
- Add package manager tabs (npm, yarn, pnpm) to website ([#72e9e05](https://github.com/dinhanhthi/coding-friend/commit/72e9e05))
|
|
6
5
|
- Fix TOC heading text stripping markdown links from slug generation ([#9a8fb5c](https://github.com/dinhanhthi/coding-friend/commit/9a8fb5c))
|
|
7
6
|
- Decorate inline codes for TOC ([#573d7b0](https://github.com/dinhanhthi/coding-friend/commit/573d7b0))
|
|
8
7
|
|