coding-friend-cli 1.0.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 (61) hide show
  1. package/dist/chunk-6CGGT2FD.js +32 -0
  2. package/dist/chunk-6DUFTBTO.js +14 -0
  3. package/dist/chunk-AQXTNLQD.js +39 -0
  4. package/dist/chunk-HRVSKMNA.js +31 -0
  5. package/dist/chunk-IUTXHCP7.js +28 -0
  6. package/dist/chunk-KZT4AFDW.js +44 -0
  7. package/dist/chunk-VHZQ6KEU.js +73 -0
  8. package/dist/host-3GAEZKKJ.js +83 -0
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.js +34 -0
  11. package/dist/init-ONRXFOZ5.js +431 -0
  12. package/dist/json-2XS56OJY.js +10 -0
  13. package/dist/mcp-LMMIFH4B.js +104 -0
  14. package/dist/postinstall.d.ts +1 -0
  15. package/dist/postinstall.js +8 -0
  16. package/dist/statusline-7D6YU5YM.js +64 -0
  17. package/dist/update-K5PYOB52.js +160 -0
  18. package/lib/learn-host/next-env.d.ts +6 -0
  19. package/lib/learn-host/next.config.ts +9 -0
  20. package/lib/learn-host/package-lock.json +3943 -0
  21. package/lib/learn-host/package.json +31 -0
  22. package/lib/learn-host/postcss.config.mjs +7 -0
  23. package/lib/learn-host/scripts/build-search-index.ts +11 -0
  24. package/lib/learn-host/src/app/[category]/[slug]/page.tsx +61 -0
  25. package/lib/learn-host/src/app/[category]/page.tsx +35 -0
  26. package/lib/learn-host/src/app/globals.css +31 -0
  27. package/lib/learn-host/src/app/layout.tsx +32 -0
  28. package/lib/learn-host/src/app/page.tsx +63 -0
  29. package/lib/learn-host/src/app/search/page.tsx +94 -0
  30. package/lib/learn-host/src/app/search/search-index.json +42 -0
  31. package/lib/learn-host/src/components/Breadcrumbs.tsx +28 -0
  32. package/lib/learn-host/src/components/DocCard.tsx +32 -0
  33. package/lib/learn-host/src/components/MarkdownRenderer.tsx +13 -0
  34. package/lib/learn-host/src/components/MobileNav.tsx +56 -0
  35. package/lib/learn-host/src/components/SearchBar.tsx +36 -0
  36. package/lib/learn-host/src/components/Sidebar.tsx +44 -0
  37. package/lib/learn-host/src/components/TagBadge.tsx +12 -0
  38. package/lib/learn-host/src/components/ThemeToggle.tsx +30 -0
  39. package/lib/learn-host/src/lib/docs.ts +113 -0
  40. package/lib/learn-host/src/lib/search.ts +27 -0
  41. package/lib/learn-host/src/lib/types.ts +31 -0
  42. package/lib/learn-host/tsconfig.json +21 -0
  43. package/lib/learn-mcp/package-lock.json +1829 -0
  44. package/lib/learn-mcp/package.json +24 -0
  45. package/lib/learn-mcp/src/bin/learn-mcp.ts +2 -0
  46. package/lib/learn-mcp/src/index.ts +17 -0
  47. package/lib/learn-mcp/src/lib/docs.ts +199 -0
  48. package/lib/learn-mcp/src/lib/knowledge.ts +95 -0
  49. package/lib/learn-mcp/src/lib/types.ts +36 -0
  50. package/lib/learn-mcp/src/server.ts +22 -0
  51. package/lib/learn-mcp/src/tools/create-doc.ts +29 -0
  52. package/lib/learn-mcp/src/tools/get-review-list.ts +29 -0
  53. package/lib/learn-mcp/src/tools/improve-doc.ts +95 -0
  54. package/lib/learn-mcp/src/tools/list-categories.ts +19 -0
  55. package/lib/learn-mcp/src/tools/list-docs.ts +30 -0
  56. package/lib/learn-mcp/src/tools/read-doc.ts +29 -0
  57. package/lib/learn-mcp/src/tools/search-docs.ts +23 -0
  58. package/lib/learn-mcp/src/tools/track-knowledge.ts +35 -0
  59. package/lib/learn-mcp/src/tools/update-doc.ts +43 -0
  60. package/lib/learn-mcp/tsconfig.json +15 -0
  61. package/package.json +47 -0
@@ -0,0 +1,32 @@
1
+ // src/lib/exec.ts
2
+ import { execFileSync, spawn } from "child_process";
3
+ function run(cmd, args = [], opts) {
4
+ try {
5
+ return execFileSync(cmd, args, {
6
+ encoding: "utf-8",
7
+ cwd: opts?.cwd,
8
+ stdio: ["pipe", "pipe", "pipe"]
9
+ }).trim();
10
+ } catch {
11
+ return null;
12
+ }
13
+ }
14
+ function streamExec(cmd, args, opts) {
15
+ return new Promise((resolve, reject) => {
16
+ const child = spawn(cmd, args, {
17
+ stdio: "inherit",
18
+ ...opts
19
+ });
20
+ child.on("close", (code) => resolve(code ?? 0));
21
+ child.on("error", reject);
22
+ });
23
+ }
24
+ function commandExists(cmd) {
25
+ return run("which", [cmd]) !== null;
26
+ }
27
+
28
+ export {
29
+ run,
30
+ streamExec,
31
+ commandExists
32
+ };
@@ -0,0 +1,14 @@
1
+ // src/lib/log.ts
2
+ import chalk from "chalk";
3
+ var log = {
4
+ info: (msg) => console.log(chalk.blue("\u2139"), msg),
5
+ success: (msg) => console.log(chalk.green("\u2714"), msg),
6
+ warn: (msg) => console.log(chalk.yellow("\u26A0"), msg),
7
+ error: (msg) => console.log(chalk.red("\u2716"), msg),
8
+ step: (msg) => console.log(chalk.cyan("\u2192"), msg),
9
+ dim: (msg) => console.log(chalk.dim(msg))
10
+ };
11
+
12
+ export {
13
+ log
14
+ };
@@ -0,0 +1,39 @@
1
+ // src/lib/paths.ts
2
+ import { homedir } from "os";
3
+ import { resolve, join } from "path";
4
+ function resolvePath(p, base = process.cwd()) {
5
+ if (p.startsWith("/")) return p;
6
+ if (p.startsWith("~/")) return join(homedir(), p.slice(2));
7
+ return resolve(base, p);
8
+ }
9
+ function localConfigPath() {
10
+ return resolve(process.cwd(), ".coding-friend", "config.json");
11
+ }
12
+ function globalConfigPath() {
13
+ return join(homedir(), ".coding-friend", "config.json");
14
+ }
15
+ function claudeSettingsPath() {
16
+ return join(homedir(), ".claude", "settings.json");
17
+ }
18
+ function installedPluginsPath() {
19
+ return join(homedir(), ".claude", "plugins", "installed_plugins.json");
20
+ }
21
+ function pluginCachePath() {
22
+ return join(
23
+ homedir(),
24
+ ".claude",
25
+ "plugins",
26
+ "cache",
27
+ "coding-friend-marketplace",
28
+ "coding-friend"
29
+ );
30
+ }
31
+
32
+ export {
33
+ resolvePath,
34
+ localConfigPath,
35
+ globalConfigPath,
36
+ claudeSettingsPath,
37
+ installedPluginsPath,
38
+ pluginCachePath
39
+ };
@@ -0,0 +1,31 @@
1
+ // src/types.ts
2
+ var DEFAULT_CONFIG = {
3
+ language: "en",
4
+ docsDir: "docs",
5
+ devRulesReminder: true,
6
+ learn: {
7
+ outputDir: "docs/learn",
8
+ categories: [
9
+ {
10
+ name: "concepts",
11
+ description: "Design patterns, algorithms, architecture principles"
12
+ },
13
+ { name: "patterns", description: "Repository pattern, observer pattern" },
14
+ {
15
+ name: "languages",
16
+ description: "Language-specific features, syntax, idioms"
17
+ },
18
+ { name: "tools", description: "Libraries, frameworks, CLI tools" },
19
+ {
20
+ name: "debugging",
21
+ description: "Debugging techniques, bug fixes"
22
+ }
23
+ ],
24
+ autoCommit: false,
25
+ readmeIndex: false
26
+ }
27
+ };
28
+
29
+ export {
30
+ DEFAULT_CONFIG
31
+ };
@@ -0,0 +1,28 @@
1
+ // src/lib/json.ts
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
3
+ import { dirname } from "path";
4
+ function readJson(filePath) {
5
+ try {
6
+ const content = readFileSync(filePath, "utf-8");
7
+ return JSON.parse(content);
8
+ } catch {
9
+ return null;
10
+ }
11
+ }
12
+ function writeJson(filePath, data) {
13
+ const dir = dirname(filePath);
14
+ if (!existsSync(dir)) {
15
+ mkdirSync(dir, { recursive: true });
16
+ }
17
+ writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
18
+ }
19
+ function mergeJson(filePath, data) {
20
+ const existing = readJson(filePath) ?? {};
21
+ writeJson(filePath, { ...existing, ...data });
22
+ }
23
+
24
+ export {
25
+ readJson,
26
+ writeJson,
27
+ mergeJson
28
+ };
@@ -0,0 +1,44 @@
1
+ import {
2
+ globalConfigPath,
3
+ localConfigPath,
4
+ resolvePath
5
+ } from "./chunk-AQXTNLQD.js";
6
+ import {
7
+ readJson
8
+ } from "./chunk-IUTXHCP7.js";
9
+
10
+ // src/lib/config.ts
11
+ function resolveDocsDir(explicitPath) {
12
+ if (explicitPath) {
13
+ return resolvePath(explicitPath);
14
+ }
15
+ const local = readJson(localConfigPath());
16
+ if (local?.learn?.outputDir) {
17
+ return resolvePath(local.learn.outputDir);
18
+ }
19
+ const global = readJson(globalConfigPath());
20
+ if (global?.learn?.outputDir) {
21
+ return resolvePath(global.learn.outputDir);
22
+ }
23
+ return resolvePath("docs/learn");
24
+ }
25
+
26
+ // src/lib/lib-path.ts
27
+ import { existsSync } from "fs";
28
+ import { dirname, join } from "path";
29
+ import { fileURLToPath } from "url";
30
+ var __dirname = dirname(fileURLToPath(import.meta.url));
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;
36
+ throw new Error(
37
+ `Could not find lib/${name}. Ensure it exists in the CLI package or repo.`
38
+ );
39
+ }
40
+
41
+ export {
42
+ resolveDocsDir,
43
+ getLibPath
44
+ };
@@ -0,0 +1,73 @@
1
+ import {
2
+ log
3
+ } from "./chunk-6DUFTBTO.js";
4
+
5
+ // src/lib/shell-completion.ts
6
+ import { appendFileSync, existsSync, readFileSync } from "fs";
7
+ import { homedir } from "os";
8
+ var MARKER_START = "# >>> coding-friend CLI completion >>>";
9
+ var MARKER_END = "# <<< coding-friend CLI completion <<<";
10
+ var BASH_BLOCK = `
11
+
12
+ ${MARKER_START}
13
+ _cf_completions() {
14
+ local cur="\${COMP_WORDS[COMP_CWORD]}"
15
+ local commands="init host mcp statusline update"
16
+ COMPREPLY=($(compgen -W "$commands" -- "$cur"))
17
+ }
18
+ complete -o default -F _cf_completions cf
19
+ ${MARKER_END}
20
+ `;
21
+ var ZSH_BLOCK = `
22
+
23
+ ${MARKER_START}
24
+ _cf() {
25
+ local -a commands
26
+ commands=(
27
+ 'init:Initialize coding-friend in current project'
28
+ 'host:Build and serve learning docs as a static website'
29
+ 'mcp:Setup MCP server for learning docs'
30
+ 'statusline:Setup coding-friend statusline in Claude Code'
31
+ 'update:Update coding-friend plugin and refresh statusline'
32
+ )
33
+ _describe 'command' commands
34
+ }
35
+ compdef _cf cf
36
+ ${MARKER_END}
37
+ `;
38
+ function getShellRcPath() {
39
+ const shell = process.env.SHELL ?? "";
40
+ if (shell.includes("zsh")) return `${homedir()}/.zshrc`;
41
+ return `${homedir()}/.bashrc`;
42
+ }
43
+ function getRcName(rcPath) {
44
+ return rcPath.endsWith(".zshrc") ? ".zshrc" : ".bashrc";
45
+ }
46
+ function isZsh(rcPath) {
47
+ return rcPath.endsWith(".zshrc");
48
+ }
49
+ function hasShellCompletion() {
50
+ const rcPath = getShellRcPath();
51
+ if (!existsSync(rcPath)) return false;
52
+ return readFileSync(rcPath, "utf-8").includes(MARKER_START);
53
+ }
54
+ function ensureShellCompletion(opts) {
55
+ const rcPath = getShellRcPath();
56
+ const rcName = getRcName(rcPath);
57
+ if (hasShellCompletion()) {
58
+ if (!opts?.silent) log.dim(`Tab completion already in ~/${rcName}`);
59
+ return false;
60
+ }
61
+ const block = isZsh(rcPath) ? ZSH_BLOCK : BASH_BLOCK;
62
+ appendFileSync(rcPath, block);
63
+ if (!opts?.silent) {
64
+ log.success(`Tab completion added to ~/${rcName}`);
65
+ log.dim(`Run \`source ~/${rcName}\` or open a new terminal to activate.`);
66
+ }
67
+ return true;
68
+ }
69
+
70
+ export {
71
+ hasShellCompletion,
72
+ ensureShellCompletion
73
+ };
@@ -0,0 +1,83 @@
1
+ import {
2
+ getLibPath,
3
+ resolveDocsDir
4
+ } from "./chunk-KZT4AFDW.js";
5
+ import "./chunk-HRVSKMNA.js";
6
+ import {
7
+ run,
8
+ streamExec
9
+ } from "./chunk-6CGGT2FD.js";
10
+ import "./chunk-AQXTNLQD.js";
11
+ import {
12
+ log
13
+ } from "./chunk-6DUFTBTO.js";
14
+ import "./chunk-IUTXHCP7.js";
15
+
16
+ // src/commands/host.ts
17
+ import { existsSync, readdirSync } from "fs";
18
+ import { join } from "path";
19
+ function countMdFiles(dir) {
20
+ let count = 0;
21
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
22
+ if (entry.isDirectory()) {
23
+ count += countMdFiles(join(dir, entry.name));
24
+ } else if (entry.name.endsWith(".md") && entry.name !== "README.md") {
25
+ count++;
26
+ }
27
+ }
28
+ return count;
29
+ }
30
+ function countCategories(dir) {
31
+ return readdirSync(dir, { withFileTypes: true }).filter(
32
+ (e) => e.isDirectory() && !e.name.startsWith(".")
33
+ ).length;
34
+ }
35
+ async function hostCommand(path, opts) {
36
+ const docsDir = resolveDocsDir(path);
37
+ const port = opts?.port ?? "3333";
38
+ const hostDir = getLibPath("learn-host");
39
+ if (!existsSync(docsDir)) {
40
+ log.error(`Docs folder not found: ${docsDir}`);
41
+ log.dim("Run /cf-learn first to generate some docs.");
42
+ process.exit(1);
43
+ }
44
+ const docCount = countMdFiles(docsDir);
45
+ if (docCount === 0) {
46
+ log.error(`No .md files found in ${docsDir}`);
47
+ log.dim("Run /cf-learn first to generate some docs.");
48
+ process.exit(1);
49
+ }
50
+ const catCount = countCategories(docsDir);
51
+ console.log("=== \u{1F33F} Coding Friend Host \u{1F33F} ===");
52
+ log.info(`Docs folder: ${docsDir}`);
53
+ log.info(`Found: ${docCount} docs in ${catCount} categories`);
54
+ console.log();
55
+ if (!existsSync(join(hostDir, "node_modules"))) {
56
+ log.step("Installing dependencies (one-time setup)...");
57
+ const result = run("npm", ["install", "--silent"], { cwd: hostDir });
58
+ if (result === null) {
59
+ log.error("Failed to install dependencies");
60
+ process.exit(1);
61
+ }
62
+ log.success("Done.");
63
+ console.log();
64
+ }
65
+ log.step("Building static site...");
66
+ const buildCode = await streamExec("npm", ["run", "build", "--silent"], {
67
+ cwd: hostDir,
68
+ env: { ...process.env, DOCS_DIR: docsDir }
69
+ });
70
+ if (buildCode !== 0) {
71
+ log.error("Build failed");
72
+ process.exit(1);
73
+ }
74
+ console.log();
75
+ log.step("Starting local preview server...");
76
+ log.info(`Site: http://localhost:${port}`);
77
+ log.dim("Press Ctrl+C to stop.");
78
+ console.log();
79
+ await streamExec("npx", ["serve", "out", "-p", port], { cwd: hostDir });
80
+ }
81
+ export {
82
+ hostCommand
83
+ };
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+ import { readFileSync } from "fs";
6
+ import { dirname, join } from "path";
7
+ import { fileURLToPath } from "url";
8
+ var __dirname = dirname(fileURLToPath(import.meta.url));
9
+ var pkg = JSON.parse(
10
+ readFileSync(join(__dirname, "..", "package.json"), "utf-8")
11
+ );
12
+ var program = new Command();
13
+ program.name("cf").description("coding-friend CLI \u2014 host learning docs, setup MCP, init projects").version(pkg.version);
14
+ program.command("init").description("Initialize coding-friend in current project").action(async () => {
15
+ const { initCommand } = await import("./init-ONRXFOZ5.js");
16
+ await initCommand();
17
+ });
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");
20
+ await hostCommand(path, opts);
21
+ });
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");
24
+ await mcpCommand(path);
25
+ });
26
+ program.command("statusline").description("Setup coding-friend statusline in Claude Code").action(async () => {
27
+ const { statuslineCommand } = await import("./statusline-7D6YU5YM.js");
28
+ await statuslineCommand();
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();
33
+ });
34
+ program.parse();