coding-friend-cli 1.34.0 → 1.35.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 +1 -0
- package/dist/chunk-2DTXVMPQ.js +26 -0
- package/dist/{chunk-OYPOADCX.js → chunk-57EGAXYI.js} +3 -3
- package/dist/{chunk-K6EDQGDN.js → chunk-B2NFXRFP.js} +17 -12
- package/dist/{chunk-3UIH5U3Y.js → chunk-C5YWESSF.js} +2 -6
- package/dist/chunk-E5J24GD5.js +190 -0
- package/dist/{chunk-UGTNIFBH.js → chunk-H5TXJZD7.js} +6 -6
- package/dist/{chunk-5X3JZKYW.js → chunk-L53RNEV3.js} +116 -73
- package/dist/{chunk-DMJCO6LJ.js → chunk-RLUNNJWJ.js} +2 -1
- package/dist/{chunk-XOHLBLN2.js → chunk-WNTMKIKC.js} +1 -1
- package/dist/chunk-WSUYLIYX.js +61 -0
- package/dist/clean-67ACB4OS.js +95 -0
- package/dist/{chunk-CXG6I6ZF.js → config-HTQAALMA.js} +182 -194
- package/dist/{dev-S6VZUD33.js → dev-PFUF67SH.js} +7 -7
- package/dist/{disable-ZWQVU3QZ.js → disable-CDOM7SSI.js} +2 -2
- package/dist/{enable-NXP6APNI.js → enable-SF7742UR.js} +2 -2
- package/dist/{host-Y3WIXDSS.js → host-HKA4OHBM.js} +3 -3
- package/dist/index.js +38 -34
- package/dist/{init-WDFYY5MM.js → init-YM7YEEGK.js} +154 -99
- package/dist/{install-FKL6WLIG.js → install-VYZSTXN6.js} +8 -8
- package/dist/{mcp-J4SD646S.js → mcp-7MO2MCLE.js} +72 -67
- package/dist/{mcp-serve-learn-UNW6VH5M.js → mcp-serve-learn-OKVC5WV3.js} +19 -3
- package/dist/{memory-RLZR7XQY.js → memory-XALEMWQF.js} +7 -7
- package/dist/{permission-DNW7DE24.js → permission-C5R3LMZQ.js} +2 -2
- package/dist/postinstall.js +1 -1
- package/dist/{session-6WQGKIRG.js → session-M4KRQ4VU.js} +2 -2
- package/dist/{status-BX2SVBBW.js → status-2XM5OZDR.js} +19 -14
- package/dist/{statusline-MLLKBHHR.js → statusline-VFIA7OS2.js} +3 -3
- package/dist/{uninstall-6ZJRS2H4.js → uninstall-QKEC3O64.js} +6 -6
- package/dist/{update-G5HZINBK.js → update-IV4Y3T72.js} +6 -6
- package/package.json +1 -1
- package/dist/chunk-AYIREX73.js +0 -85
- package/dist/chunk-TVAY4OQL.js +0 -118
- package/dist/config-MCEQOLE5.js +0 -22
- package/dist/{chunk-Z5Q533UG.js → chunk-HPNRQYLM.js} +3 -3
- package/dist/{update-check-5SCD2JR2.js → update-check-BPCQ25AR.js} +3 -3
package/dist/index.js
CHANGED
|
@@ -14,64 +14,68 @@ 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-VYZSTXN6.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-QKEC3O64.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-CDOM7SSI.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-SF7742UR.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-YM7YEEGK.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-HTQAALMA.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) => {
|
|
41
|
-
const { hostCommand } = await import("./host-
|
|
41
|
+
const { hostCommand } = await import("./host-HKA4OHBM.js");
|
|
42
42
|
await hostCommand(path, opts);
|
|
43
43
|
});
|
|
44
|
-
program.command("mcp").description("Setup MCP server for learning docs").
|
|
45
|
-
const { mcpCommand } = await import("./mcp-
|
|
46
|
-
await mcpCommand(
|
|
44
|
+
program.command("mcp").description("Setup MCP server for learning docs").action(async () => {
|
|
45
|
+
const { mcpCommand } = await import("./mcp-7MO2MCLE.js");
|
|
46
|
+
await mcpCommand();
|
|
47
47
|
});
|
|
48
48
|
program.command("mcp-serve").description("Start the cf-memory MCP server (used internally by npx)").argument("<memoryDir>", "path to memory directory").action(async (memoryDir) => {
|
|
49
49
|
const { mcpServeCommand } = await import("./mcp-serve-3FEPFVVQ.js");
|
|
50
50
|
await mcpServeCommand(memoryDir);
|
|
51
51
|
});
|
|
52
52
|
program.command("mcp-serve-learn").description("Start the learn MCP server (used internally by npx)").argument("<docsDir>", "path to docs directory").action(async (docsDir) => {
|
|
53
|
-
const { mcpServeLearnCommand } = await import("./mcp-serve-learn-
|
|
53
|
+
const { mcpServeLearnCommand } = await import("./mcp-serve-learn-OKVC5WV3.js");
|
|
54
54
|
await mcpServeLearnCommand(docsDir);
|
|
55
55
|
});
|
|
56
56
|
program.command("permission").description("Manage Claude Code permission rules for Coding Friend").option("--all", "Apply all recommended permissions without prompts").option("--user", "Save to user-level settings (~/.claude/settings.json)").option(
|
|
57
57
|
"--project",
|
|
58
58
|
"Save to project-level settings (.claude/settings.local.json)"
|
|
59
59
|
).action(async (opts) => {
|
|
60
|
-
const { permissionCommand } = await import("./permission-
|
|
60
|
+
const { permissionCommand } = await import("./permission-C5R3LMZQ.js");
|
|
61
61
|
await permissionCommand(opts);
|
|
62
62
|
});
|
|
63
63
|
program.command("statusline").description("Setup coding-friend statusline in Claude Code").action(async () => {
|
|
64
|
-
const { statuslineCommand } = await import("./statusline-
|
|
64
|
+
const { statuslineCommand } = await import("./statusline-VFIA7OS2.js");
|
|
65
65
|
await statuslineCommand();
|
|
66
66
|
});
|
|
67
67
|
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) => {
|
|
68
|
-
const { updateCommand } = await import("./update-
|
|
68
|
+
const { updateCommand } = await import("./update-IV4Y3T72.js");
|
|
69
69
|
await updateCommand(opts);
|
|
70
70
|
});
|
|
71
71
|
program.command("status").description("Show comprehensive Coding Friend status").action(async () => {
|
|
72
|
-
const { statusCommand } = await import("./status-
|
|
72
|
+
const { statusCommand } = await import("./status-2XM5OZDR.js");
|
|
73
73
|
await statusCommand();
|
|
74
74
|
});
|
|
75
|
+
program.command("clean").description("Clean files generated by Coding Friend in docs/").action(async () => {
|
|
76
|
+
const { cleanCommand } = await import("./clean-67ACB4OS.js");
|
|
77
|
+
await cleanCommand();
|
|
78
|
+
});
|
|
75
79
|
var session = program.command("session").description("Save and load Claude Code sessions across machines");
|
|
76
80
|
program.addHelpText(
|
|
77
81
|
"after",
|
|
@@ -84,11 +88,11 @@ session.command("save").description("Save current Claude Code session to sync fo
|
|
|
84
88
|
"-s, --session-id <id>",
|
|
85
89
|
"session UUID to save (default: auto-detect newest)"
|
|
86
90
|
).option("-l, --label <label>", "label for this session").action(async (opts) => {
|
|
87
|
-
const { sessionSaveCommand } = await import("./session-
|
|
91
|
+
const { sessionSaveCommand } = await import("./session-M4KRQ4VU.js");
|
|
88
92
|
await sessionSaveCommand(opts);
|
|
89
93
|
});
|
|
90
94
|
session.command("load").description("Load a saved session from sync folder").action(async () => {
|
|
91
|
-
const { sessionLoadCommand } = await import("./session-
|
|
95
|
+
const { sessionLoadCommand } = await import("./session-M4KRQ4VU.js");
|
|
92
96
|
await sessionLoadCommand();
|
|
93
97
|
});
|
|
94
98
|
var memory = program.command("memory").description("AI memory system \u2014 store and search project knowledge");
|
|
@@ -108,43 +112,43 @@ Memory subcommands:
|
|
|
108
112
|
memory mcp Show MCP server setup instructions`
|
|
109
113
|
);
|
|
110
114
|
memory.command("status").description("Show memory system status").action(async () => {
|
|
111
|
-
const { memoryStatusCommand } = await import("./memory-
|
|
115
|
+
const { memoryStatusCommand } = await import("./memory-XALEMWQF.js");
|
|
112
116
|
await memoryStatusCommand();
|
|
113
117
|
});
|
|
114
118
|
memory.command("search").description("Search memories by query").argument("<query>", "search query").action(async (query) => {
|
|
115
|
-
const { memorySearchCommand } = await import("./memory-
|
|
119
|
+
const { memorySearchCommand } = await import("./memory-XALEMWQF.js");
|
|
116
120
|
await memorySearchCommand(query);
|
|
117
121
|
});
|
|
118
122
|
memory.command("list").description(
|
|
119
123
|
"List memories in current project, or all projects with --projects"
|
|
120
124
|
).option("--projects", "List all project databases with size and metadata").action(async (opts) => {
|
|
121
|
-
const { memoryListCommand } = await import("./memory-
|
|
125
|
+
const { memoryListCommand } = await import("./memory-XALEMWQF.js");
|
|
122
126
|
await memoryListCommand(opts);
|
|
123
127
|
});
|
|
124
128
|
memory.command("init").description(
|
|
125
129
|
"Initialize memory system \u2014 interactive wizard (first time) or config menu"
|
|
126
130
|
).action(async () => {
|
|
127
|
-
const { memoryInitCommand } = await import("./memory-
|
|
131
|
+
const { memoryInitCommand } = await import("./memory-XALEMWQF.js");
|
|
128
132
|
await memoryInitCommand();
|
|
129
133
|
});
|
|
130
134
|
memory.command("config").description("Configure memory system settings").action(async () => {
|
|
131
|
-
const { memoryConfigCommand } = await import("./memory-
|
|
135
|
+
const { memoryConfigCommand } = await import("./memory-XALEMWQF.js");
|
|
132
136
|
await memoryConfigCommand();
|
|
133
137
|
});
|
|
134
138
|
memory.command("start-daemon").description("Start the memory daemon (Tier 2 \u2014 MiniSearch)").action(async () => {
|
|
135
|
-
const { memoryStartDaemonCommand } = await import("./memory-
|
|
139
|
+
const { memoryStartDaemonCommand } = await import("./memory-XALEMWQF.js");
|
|
136
140
|
await memoryStartDaemonCommand();
|
|
137
141
|
});
|
|
138
142
|
memory.command("stop-daemon").description("Stop the memory daemon").action(async () => {
|
|
139
|
-
const { memoryStopDaemonCommand } = await import("./memory-
|
|
143
|
+
const { memoryStopDaemonCommand } = await import("./memory-XALEMWQF.js");
|
|
140
144
|
await memoryStopDaemonCommand();
|
|
141
145
|
});
|
|
142
146
|
memory.command("rebuild").description("Rebuild the daemon search index").action(async () => {
|
|
143
|
-
const { memoryRebuildCommand } = await import("./memory-
|
|
147
|
+
const { memoryRebuildCommand } = await import("./memory-XALEMWQF.js");
|
|
144
148
|
await memoryRebuildCommand();
|
|
145
149
|
});
|
|
146
150
|
memory.command("mcp").description("Show MCP server setup instructions").action(async () => {
|
|
147
|
-
const { memoryMcpCommand } = await import("./memory-
|
|
151
|
+
const { memoryMcpCommand } = await import("./memory-XALEMWQF.js");
|
|
148
152
|
await memoryMcpCommand();
|
|
149
153
|
});
|
|
150
154
|
memory.command("rm").description("Remove a project database").option("--project-id <id>", "Project ID to remove").option("--all", "Remove all project databases").option(
|
|
@@ -152,7 +156,7 @@ memory.command("rm").description("Remove a project database").option("--project-
|
|
|
152
156
|
"Remove orphaned projects (source dir missing or 0 memories)"
|
|
153
157
|
).action(
|
|
154
158
|
async (opts) => {
|
|
155
|
-
const { memoryRmCommand } = await import("./memory-
|
|
159
|
+
const { memoryRmCommand } = await import("./memory-XALEMWQF.js");
|
|
156
160
|
await memoryRmCommand(opts);
|
|
157
161
|
}
|
|
158
162
|
);
|
|
@@ -178,41 +182,41 @@ Dev subcommands:
|
|
|
178
182
|
dev update [path] Update local dev plugin to latest version`
|
|
179
183
|
);
|
|
180
184
|
dev.command("on").description("Switch to local plugin source").argument("[path]", "path to local coding-friend repo (default: cwd)").action(async (path) => {
|
|
181
|
-
const { devOnCommand } = await import("./dev-
|
|
185
|
+
const { devOnCommand } = await import("./dev-PFUF67SH.js");
|
|
182
186
|
await devOnCommand(path);
|
|
183
187
|
});
|
|
184
188
|
dev.command("off").description("Switch back to remote marketplace").action(async () => {
|
|
185
|
-
const { devOffCommand } = await import("./dev-
|
|
189
|
+
const { devOffCommand } = await import("./dev-PFUF67SH.js");
|
|
186
190
|
await devOffCommand();
|
|
187
191
|
});
|
|
188
192
|
dev.command("status").description("Show current dev mode").action(async () => {
|
|
189
|
-
const { devStatusCommand } = await import("./dev-
|
|
193
|
+
const { devStatusCommand } = await import("./dev-PFUF67SH.js");
|
|
190
194
|
await devStatusCommand();
|
|
191
195
|
});
|
|
192
196
|
dev.command("sync").description(
|
|
193
197
|
"Copy local source files to plugin cache (no version bump needed)"
|
|
194
198
|
).action(async () => {
|
|
195
|
-
const { devSyncCommand } = await import("./dev-
|
|
199
|
+
const { devSyncCommand } = await import("./dev-PFUF67SH.js");
|
|
196
200
|
await devSyncCommand();
|
|
197
201
|
});
|
|
198
202
|
dev.command("restart").description("Reinstall local dev plugin (off + on)").argument(
|
|
199
203
|
"[path]",
|
|
200
204
|
"path to local coding-friend repo (default: saved path or cwd)"
|
|
201
205
|
).action(async (path) => {
|
|
202
|
-
const { devRestartCommand } = await import("./dev-
|
|
206
|
+
const { devRestartCommand } = await import("./dev-PFUF67SH.js");
|
|
203
207
|
await devRestartCommand(path);
|
|
204
208
|
});
|
|
205
209
|
dev.command("update").description("Update local dev plugin to latest version (off + on)").argument(
|
|
206
210
|
"[path]",
|
|
207
211
|
"path to local coding-friend repo (default: saved path or cwd)"
|
|
208
212
|
).action(async (path) => {
|
|
209
|
-
const { devUpdateCommand } = await import("./dev-
|
|
213
|
+
const { devUpdateCommand } = await import("./dev-PFUF67SH.js");
|
|
210
214
|
await devUpdateCommand(path);
|
|
211
215
|
});
|
|
212
216
|
program.hook("postAction", async () => {
|
|
213
217
|
const topCmd = process.argv[2];
|
|
214
218
|
if (topCmd === "update") return;
|
|
215
|
-
const { checkAndNotifyCliUpdate } = await import("./update-check-
|
|
219
|
+
const { checkAndNotifyCliUpdate } = await import("./update-check-BPCQ25AR.js");
|
|
216
220
|
checkAndNotifyCliUpdate(pkg.version);
|
|
217
221
|
});
|
|
218
222
|
await program.parseAsync();
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
editCodex
|
|
3
|
-
} from "./chunk-CXG6I6ZF.js";
|
|
4
1
|
import {
|
|
5
2
|
ensureMemoryBuilt,
|
|
6
3
|
isMemoryInitialized,
|
|
7
4
|
memoryInitWizard
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-L53RNEV3.js";
|
|
6
|
+
import "./chunk-E5J24GD5.js";
|
|
7
|
+
import {
|
|
8
|
+
isLearnMcpRegistered,
|
|
9
|
+
registerLearnMcp
|
|
10
|
+
} from "./chunk-WSUYLIYX.js";
|
|
10
11
|
import {
|
|
11
12
|
memoryConfigMenu
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-C5YWESSF.js";
|
|
14
|
+
import "./chunk-B2NFXRFP.js";
|
|
14
15
|
import {
|
|
15
16
|
getLibPath
|
|
16
17
|
} from "./chunk-RZRT7NGT.js";
|
|
@@ -24,14 +25,14 @@ import {
|
|
|
24
25
|
saveStatuslineConfig,
|
|
25
26
|
selectStatuslineComponents,
|
|
26
27
|
writeStatuslineSettings
|
|
27
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-WNTMKIKC.js";
|
|
28
29
|
import {
|
|
29
30
|
DEFAULT_CONFIG
|
|
30
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-57EGAXYI.js";
|
|
31
32
|
import {
|
|
32
33
|
ensureShellCompletion,
|
|
33
34
|
hasShellCompletion
|
|
34
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-RLUNNJWJ.js";
|
|
35
36
|
import {
|
|
36
37
|
BACK,
|
|
37
38
|
applyDocsDirChange,
|
|
@@ -42,7 +43,11 @@ import {
|
|
|
42
43
|
getScopeLabel,
|
|
43
44
|
injectBackChoice,
|
|
44
45
|
showConfigHint
|
|
45
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-HPNRQYLM.js";
|
|
47
|
+
import {
|
|
48
|
+
commandExists,
|
|
49
|
+
run
|
|
50
|
+
} from "./chunk-EVGXUDX4.js";
|
|
46
51
|
import {
|
|
47
52
|
claudeLocalSettingsPath,
|
|
48
53
|
claudeProjectSettingsPath,
|
|
@@ -51,10 +56,6 @@ import {
|
|
|
51
56
|
localConfigPath,
|
|
52
57
|
resolvePath
|
|
53
58
|
} from "./chunk-TWKNGPBO.js";
|
|
54
|
-
import {
|
|
55
|
-
commandExists,
|
|
56
|
-
run
|
|
57
|
-
} from "./chunk-EVGXUDX4.js";
|
|
58
59
|
import {
|
|
59
60
|
log,
|
|
60
61
|
printBanner
|
|
@@ -76,7 +77,7 @@ import {
|
|
|
76
77
|
|
|
77
78
|
// src/commands/init.ts
|
|
78
79
|
import { checkbox, confirm, input, select } from "@inquirer/prompts";
|
|
79
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
80
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
80
81
|
import { homedir } from "os";
|
|
81
82
|
import { join } from "path";
|
|
82
83
|
import chalk from "chalk";
|
|
@@ -118,14 +119,7 @@ function paddedScopeLabel(scope) {
|
|
|
118
119
|
}
|
|
119
120
|
function printSetupStatus(globalCfg, localCfg, gitAvailable) {
|
|
120
121
|
const docsDir = getDocsDir(globalCfg, localCfg);
|
|
121
|
-
const subfolders = [
|
|
122
|
-
"plans",
|
|
123
|
-
"memory",
|
|
124
|
-
"research",
|
|
125
|
-
"learn",
|
|
126
|
-
"sessions",
|
|
127
|
-
"reviews"
|
|
128
|
-
];
|
|
122
|
+
const subfolders = ["plans", "memory", "research", "sessions", "reviews"];
|
|
129
123
|
const folderStatus = subfolders.map((sub) => ({
|
|
130
124
|
name: `${docsDir}/${sub}`,
|
|
131
125
|
exists: existsSync(`${docsDir}/${sub}`)
|
|
@@ -249,7 +243,6 @@ async function stepDocsDir(globalCfg, localCfg) {
|
|
|
249
243
|
"plans",
|
|
250
244
|
"memory",
|
|
251
245
|
"research",
|
|
252
|
-
"learn",
|
|
253
246
|
"sessions",
|
|
254
247
|
"reviews"
|
|
255
248
|
];
|
|
@@ -275,7 +268,6 @@ async function stepDocsDir(globalCfg, localCfg) {
|
|
|
275
268
|
"plans",
|
|
276
269
|
"memory",
|
|
277
270
|
"research",
|
|
278
|
-
"learn",
|
|
279
271
|
"sessions",
|
|
280
272
|
"reviews"
|
|
281
273
|
];
|
|
@@ -316,7 +308,6 @@ async function stepGitignore(docsDir) {
|
|
|
316
308
|
`${docsDir}/plans/`,
|
|
317
309
|
`${docsDir}/memory/`,
|
|
318
310
|
`${docsDir}/research/`,
|
|
319
|
-
`${docsDir}/learn/`,
|
|
320
311
|
`${docsDir}/sessions/`,
|
|
321
312
|
`${docsDir}/reviews/`,
|
|
322
313
|
`${docsDir}/context/`,
|
|
@@ -392,56 +383,43 @@ async function selectLanguage(message) {
|
|
|
392
383
|
}
|
|
393
384
|
return choice;
|
|
394
385
|
}
|
|
395
|
-
async function stepLearnConfig(globalCfg
|
|
396
|
-
const
|
|
397
|
-
const
|
|
398
|
-
const
|
|
386
|
+
async function stepLearnConfig(globalCfg) {
|
|
387
|
+
const globalLearn = globalCfg?.learn;
|
|
388
|
+
const defaultLearnDir = DEFAULT_CONFIG.learn.outputDir;
|
|
389
|
+
const currentOutputDir = globalLearn?.outputDir ?? defaultLearnDir;
|
|
399
390
|
printStepHeader(
|
|
400
|
-
|
|
391
|
+
"/cf-learn config",
|
|
401
392
|
"Controls where and how /cf-learn saves your learning notes."
|
|
402
393
|
);
|
|
403
|
-
const globalLearn = globalCfg?.learn;
|
|
404
|
-
if (globalLearn) {
|
|
405
|
-
const parts = [
|
|
406
|
-
globalLearn.language || "en",
|
|
407
|
-
globalLearn.outputDir || `${docsDir}/learn`
|
|
408
|
-
];
|
|
409
|
-
if (globalLearn.categories) {
|
|
410
|
-
parts.push(`${globalLearn.categories.length} categories`);
|
|
411
|
-
}
|
|
412
|
-
if (await offerGlobalShortcut(parts.join(", "))) {
|
|
413
|
-
const gOutputDir = globalLearn.outputDir || `${docsDir}/learn`;
|
|
414
|
-
const gIsExternal = !gOutputDir.startsWith(`${docsDir}/`);
|
|
415
|
-
return {
|
|
416
|
-
outputDir: gOutputDir,
|
|
417
|
-
autoCommit: globalLearn.autoCommit || false,
|
|
418
|
-
isExternal: gIsExternal
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
394
|
const language = await selectLanguage(
|
|
423
395
|
"What language should /cf-learn notes be written in?"
|
|
424
396
|
);
|
|
425
397
|
const locationChoice = await select({
|
|
426
|
-
message:
|
|
398
|
+
message: `Current learn folder: ${currentOutputDir}`,
|
|
427
399
|
choices: injectBackChoice(
|
|
428
400
|
[
|
|
429
|
-
{
|
|
430
|
-
|
|
401
|
+
{
|
|
402
|
+
name: `Use default (${defaultLearnDir})`,
|
|
403
|
+
value: "default"
|
|
404
|
+
},
|
|
405
|
+
{ name: "Custom path\u2026", value: "custom" }
|
|
431
406
|
],
|
|
432
407
|
"Cancel init"
|
|
433
408
|
)
|
|
434
409
|
});
|
|
435
410
|
handleBack(locationChoice);
|
|
436
|
-
let outputDir =
|
|
437
|
-
|
|
438
|
-
if (locationChoice === "external") {
|
|
411
|
+
let outputDir = defaultLearnDir;
|
|
412
|
+
if (locationChoice === "custom") {
|
|
439
413
|
outputDir = await input({
|
|
440
414
|
message: "Enter path (absolute or ~/...):",
|
|
441
|
-
default:
|
|
442
|
-
validate: (val) =>
|
|
415
|
+
default: currentOutputDir !== defaultLearnDir ? currentOutputDir : void 0,
|
|
416
|
+
validate: (val) => {
|
|
417
|
+
if (val.length === 0) return "Path cannot be empty";
|
|
418
|
+
if (!val.startsWith("/") && !val.startsWith("~/"))
|
|
419
|
+
return "Path must be absolute (e.g. /home/user/learn) or start with ~/ (e.g. ~/learn)";
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
443
422
|
});
|
|
444
|
-
isExternal = true;
|
|
445
423
|
const resolved = resolvePath(outputDir);
|
|
446
424
|
if (!existsSync(resolved)) {
|
|
447
425
|
const create = await confirm({
|
|
@@ -449,12 +427,12 @@ async function stepLearnConfig(globalCfg, localCfg, gitAvailable) {
|
|
|
449
427
|
default: true
|
|
450
428
|
});
|
|
451
429
|
if (create) {
|
|
452
|
-
|
|
430
|
+
mkdirSync(resolved, { recursive: true });
|
|
453
431
|
log.success(`Created ${resolved}`);
|
|
454
432
|
}
|
|
455
433
|
}
|
|
456
434
|
}
|
|
457
|
-
const existingCats =
|
|
435
|
+
const existingCats = globalLearn?.categories;
|
|
458
436
|
const defaultNames = DEFAULT_CONFIG.learn.categories.map((c) => c.name).join(", ");
|
|
459
437
|
const catChoices = [
|
|
460
438
|
{ name: `Use defaults (${defaultNames})`, value: "defaults" }
|
|
@@ -510,10 +488,18 @@ async function stepLearnConfig(globalCfg, localCfg, gitAvailable) {
|
|
|
510
488
|
if (customCats.length > 0) categories = customCats;
|
|
511
489
|
}
|
|
512
490
|
let autoCommit = false;
|
|
513
|
-
|
|
491
|
+
const resolvedOutputDir = resolvePath(outputDir);
|
|
492
|
+
const gitInLearnDir = run("git", [
|
|
493
|
+
"-C",
|
|
494
|
+
resolvedOutputDir,
|
|
495
|
+
"rev-parse",
|
|
496
|
+
"--is-inside-work-tree"
|
|
497
|
+
]);
|
|
498
|
+
const learnDirIsGit = gitInLearnDir !== null;
|
|
499
|
+
if (learnDirIsGit) {
|
|
514
500
|
autoCommit = await confirm({
|
|
515
501
|
message: "Auto-commit learning docs to git after each /cf-learn?",
|
|
516
|
-
default:
|
|
502
|
+
default: globalLearn?.autoCommit ?? false
|
|
517
503
|
});
|
|
518
504
|
}
|
|
519
505
|
const indexChoice = await select({
|
|
@@ -538,17 +524,11 @@ async function stepLearnConfig(globalCfg, localCfg, gitAvailable) {
|
|
|
538
524
|
autoCommit,
|
|
539
525
|
readmeIndex
|
|
540
526
|
};
|
|
541
|
-
const
|
|
542
|
-
if (scope === "back") {
|
|
543
|
-
log.dim("Skipped /cf-learn config.");
|
|
544
|
-
return { outputDir, autoCommit, isExternal };
|
|
545
|
-
}
|
|
546
|
-
const targetPath = scope === "global" ? globalConfigPath() : localConfigPath();
|
|
547
|
-
const existingConfig = readJson(targetPath);
|
|
527
|
+
const existingConfig = readJson(globalConfigPath());
|
|
548
528
|
const existingLearn = existingConfig?.learn ?? {};
|
|
549
|
-
mergeJson(
|
|
550
|
-
log.success(`Saved to ${
|
|
551
|
-
return { outputDir, autoCommit
|
|
529
|
+
mergeJson(globalConfigPath(), { learn: { ...existingLearn, ...learnObj } });
|
|
530
|
+
log.success(`Saved to ${globalConfigPath()}`);
|
|
531
|
+
return { outputDir, autoCommit };
|
|
552
532
|
}
|
|
553
533
|
async function stepShellCompletion() {
|
|
554
534
|
if (hasShellCompletion()) {
|
|
@@ -660,6 +640,62 @@ async function stepMemory(docsDir, opts) {
|
|
|
660
640
|
const memoryDir = join(process.cwd(), docsDir, "memory");
|
|
661
641
|
await memoryInitWizard(memoryDir, mcpDir);
|
|
662
642
|
}
|
|
643
|
+
async function stepTdd(globalCfg, localCfg) {
|
|
644
|
+
const currentValue = getMergedValue("tdd", globalCfg, localCfg);
|
|
645
|
+
const scopeLabel = getScopeLabel("tdd", globalCfg, localCfg);
|
|
646
|
+
printStepHeader(
|
|
647
|
+
`TDD ${formatScopeLabel(scopeLabel)}${currentValue !== void 0 ? ` (${currentValue})` : ""}`,
|
|
648
|
+
"Enable TDD (RED\u2192GREEN\u2192REFACTOR) by default for all implementations."
|
|
649
|
+
);
|
|
650
|
+
const tddChoice = await confirm({
|
|
651
|
+
message: "Enable TDD by default? (writes failing tests before code \u2014 RED \u2192 GREEN \u2192 REFACTOR)",
|
|
652
|
+
default: currentValue ?? false
|
|
653
|
+
});
|
|
654
|
+
const scope = await askScope();
|
|
655
|
+
if (scope === "back") {
|
|
656
|
+
log.dim("Skipped TDD config.");
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
const targetPath = scope === "global" ? globalConfigPath() : localConfigPath();
|
|
660
|
+
mergeJson(targetPath, { tdd: tddChoice });
|
|
661
|
+
log.success(`Saved to ${targetPath}`);
|
|
662
|
+
}
|
|
663
|
+
async function stepAutoApprove(globalCfg, localCfg) {
|
|
664
|
+
const currentValue = getMergedValue("autoApprove", globalCfg, localCfg);
|
|
665
|
+
const scopeLabel = getScopeLabel("autoApprove", globalCfg, localCfg);
|
|
666
|
+
printStepHeader(
|
|
667
|
+
`Auto-approve ${formatScopeLabel(scopeLabel)}${currentValue !== void 0 ? ` (${currentValue})` : ""}`,
|
|
668
|
+
"Auto-approves safe tool calls, blocks destructive ones, prompts for ambiguous."
|
|
669
|
+
);
|
|
670
|
+
const autoApproveChoice = await confirm({
|
|
671
|
+
message: "Enable auto-approve? (auto-approves read-only tools + working-dir file edits, LLM classifier for unknowns)",
|
|
672
|
+
default: currentValue ?? false
|
|
673
|
+
});
|
|
674
|
+
const scope = await askScope();
|
|
675
|
+
if (scope === "back") {
|
|
676
|
+
log.dim("Skipped auto-approve config.");
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
const targetPath = scope === "global" ? globalConfigPath() : localConfigPath();
|
|
680
|
+
mergeJson(targetPath, { autoApprove: autoApproveChoice });
|
|
681
|
+
log.success(`Saved to ${targetPath}`);
|
|
682
|
+
if (autoApproveChoice) {
|
|
683
|
+
const { runDangerousRulesAudit } = await import("./permissions-LYADLYYK.js");
|
|
684
|
+
await runDangerousRulesAudit(
|
|
685
|
+
[
|
|
686
|
+
claudeProjectSettingsPath(),
|
|
687
|
+
claudeLocalSettingsPath(),
|
|
688
|
+
claudeSettingsPath()
|
|
689
|
+
],
|
|
690
|
+
log,
|
|
691
|
+
(message) => confirm({ message, default: true })
|
|
692
|
+
);
|
|
693
|
+
log.dim(
|
|
694
|
+
"Tip: Fine-tune with autoApproveAllowExtra / autoApproveIgnore in config.json"
|
|
695
|
+
);
|
|
696
|
+
log.dim("Docs: https://cf.dinhanhthi.com/docs/reference/auto-approve/");
|
|
697
|
+
}
|
|
698
|
+
}
|
|
663
699
|
async function stepClaudePermissions(externalLearnDir, autoCommit) {
|
|
664
700
|
const scope = await select({
|
|
665
701
|
message: "Where should permissions be saved?",
|
|
@@ -739,14 +775,14 @@ async function initMenu(gitAvailable) {
|
|
|
739
775
|
const completionStatus = hasShellCompletion() ? chalk.green("installed") : chalk.yellow("not installed");
|
|
740
776
|
const statuslineStatus = isStatuslineConfigured() ? chalk.green("configured") : chalk.yellow("not configured");
|
|
741
777
|
const memoryStatus = isMemoryInitialized() ? chalk.green("initialized") : chalk.yellow("not configured");
|
|
778
|
+
const tddScope = getScopeLabel("tdd", globalCfg, localCfg);
|
|
779
|
+
const tddVal = getMergedValue("tdd", globalCfg, localCfg);
|
|
742
780
|
const autoApproveScope = getScopeLabel("autoApprove", globalCfg, localCfg);
|
|
743
781
|
const autoApproveVal = getMergedValue(
|
|
744
782
|
"autoApprove",
|
|
745
783
|
globalCfg,
|
|
746
784
|
localCfg
|
|
747
785
|
);
|
|
748
|
-
const codexScope = getScopeLabel("codex", globalCfg, localCfg);
|
|
749
|
-
const codexEnabled = getMergedValue("codex", globalCfg, localCfg)?.enabled;
|
|
750
786
|
const projectRules = getExistingRules(claudeLocalSettingsPath());
|
|
751
787
|
const userRules = getExistingRules(claudeSettingsPath());
|
|
752
788
|
const allRules = getAllRules();
|
|
@@ -793,16 +829,16 @@ async function initMenu(gitAvailable) {
|
|
|
793
829
|
value: "memory",
|
|
794
830
|
description: " Persistent project knowledge \u2014 search tier, embeddings, MCP"
|
|
795
831
|
},
|
|
832
|
+
{
|
|
833
|
+
name: `TDD ${formatScopeLabel(tddScope)}${tddVal !== void 0 ? ` (${tddVal})` : ""}`,
|
|
834
|
+
value: "tdd",
|
|
835
|
+
description: " Enable TDD (RED\u2192GREEN\u2192REFACTOR) by default for all implementations"
|
|
836
|
+
},
|
|
796
837
|
{
|
|
797
838
|
name: `Auto-approve ${formatScopeLabel(autoApproveScope)}${autoApproveVal !== void 0 ? ` (${autoApproveVal})` : ""}`,
|
|
798
839
|
value: "autoApprove",
|
|
799
840
|
description: " Auto-approve safe tool calls, block destructive ones, prompt for ambiguous"
|
|
800
841
|
},
|
|
801
|
-
{
|
|
802
|
-
name: `Codex cross-engine review ${formatScopeLabel(codexScope)}${codexEnabled !== void 0 ? ` (${codexEnabled ? "enabled" : "disabled"})` : ""}`,
|
|
803
|
-
value: "codex",
|
|
804
|
-
description: " Enable OpenAI Codex as 6th specialist in /cf-review (opt-in)"
|
|
805
|
-
},
|
|
806
842
|
{
|
|
807
843
|
name: `Permissions (${permissionStatus} rules)`,
|
|
808
844
|
value: "permissions",
|
|
@@ -829,7 +865,7 @@ async function initMenu(gitAvailable) {
|
|
|
829
865
|
await stepDocsLanguage(globalCfg, localCfg);
|
|
830
866
|
break;
|
|
831
867
|
case "learn":
|
|
832
|
-
await stepLearnConfig(globalCfg
|
|
868
|
+
await stepLearnConfig(globalCfg);
|
|
833
869
|
break;
|
|
834
870
|
case "completion":
|
|
835
871
|
await stepShellCompletion();
|
|
@@ -840,6 +876,19 @@ async function initMenu(gitAvailable) {
|
|
|
840
876
|
case "memory":
|
|
841
877
|
await stepMemory(docsDir, { menuMode: true });
|
|
842
878
|
break;
|
|
879
|
+
case "tdd": {
|
|
880
|
+
const tddChoice = await confirm({
|
|
881
|
+
message: "Enable TDD by default? (writes failing tests before code \u2014 RED \u2192 GREEN \u2192 REFACTOR)",
|
|
882
|
+
default: tddVal ?? false
|
|
883
|
+
});
|
|
884
|
+
const tddTargetScope = await askScope();
|
|
885
|
+
if (tddTargetScope !== "back") {
|
|
886
|
+
const targetPath = tddTargetScope === "global" ? globalConfigPath() : localConfigPath();
|
|
887
|
+
mergeJson(targetPath, { tdd: tddChoice });
|
|
888
|
+
log.success(`Saved to ${targetPath}`);
|
|
889
|
+
}
|
|
890
|
+
break;
|
|
891
|
+
}
|
|
843
892
|
case "autoApprove": {
|
|
844
893
|
const autoApproveChoice = await confirm({
|
|
845
894
|
message: "Enable auto-approve? (auto-approves read-only tools + working-dir file edits, LLM classifier for unknowns)",
|
|
@@ -871,18 +920,11 @@ async function initMenu(gitAvailable) {
|
|
|
871
920
|
}
|
|
872
921
|
break;
|
|
873
922
|
}
|
|
874
|
-
case "codex":
|
|
875
|
-
await editCodex(globalCfg, localCfg);
|
|
876
|
-
break;
|
|
877
923
|
case "permissions": {
|
|
878
|
-
const learnCfg =
|
|
879
|
-
const learnOutputDir = learnCfg?.outputDir
|
|
880
|
-
const learnIsExternal = !learnOutputDir.startsWith(`${docsDir}/`);
|
|
924
|
+
const learnCfg = globalCfg?.learn;
|
|
925
|
+
const learnOutputDir = learnCfg?.outputDir ?? DEFAULT_CONFIG.learn.outputDir;
|
|
881
926
|
const learnAutoCommit = learnCfg?.autoCommit || false;
|
|
882
|
-
await stepClaudePermissions(
|
|
883
|
-
learnIsExternal ? learnOutputDir : null,
|
|
884
|
-
learnAutoCommit
|
|
885
|
-
);
|
|
927
|
+
await stepClaudePermissions(learnOutputDir, learnAutoCommit);
|
|
886
928
|
break;
|
|
887
929
|
}
|
|
888
930
|
}
|
|
@@ -995,26 +1037,39 @@ async function initCommand() {
|
|
|
995
1037
|
log.dim("Skipped \u2014 not inside a git repo.");
|
|
996
1038
|
}
|
|
997
1039
|
await stepDocsLanguage(globalCfg, localCfg);
|
|
998
|
-
const { outputDir, autoCommit
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
)
|
|
1040
|
+
const { outputDir, autoCommit } = await stepLearnConfig(updatedGlobal);
|
|
1041
|
+
if (isLearnMcpRegistered()) {
|
|
1042
|
+
log.dim("coding-friend-learn: already registered");
|
|
1043
|
+
log.dim(
|
|
1044
|
+
" (If it points to an old path, run: claude mcp remove --scope user coding-friend-learn && cf mcp)"
|
|
1045
|
+
);
|
|
1046
|
+
} else {
|
|
1047
|
+
const registered = registerLearnMcp(outputDir);
|
|
1048
|
+
if (registered) {
|
|
1049
|
+
log.success(
|
|
1050
|
+
"Registered CF Learn MCP (user scope). Restart Claude Code to activate."
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1003
1054
|
await stepShellCompletion();
|
|
1004
1055
|
await stepStatusline();
|
|
1005
1056
|
await stepMemory(docsDir);
|
|
1057
|
+
const finalGlobal = readJson(globalConfigPath());
|
|
1058
|
+
const finalLocal = readJson(localConfigPath());
|
|
1059
|
+
await stepTdd(finalGlobal, finalLocal);
|
|
1060
|
+
await stepAutoApprove(finalGlobal, finalLocal);
|
|
1006
1061
|
printStepHeader(
|
|
1007
1062
|
"Configure Claude permissions",
|
|
1008
1063
|
"Grants Coding Friend skills/hooks the permissions they need, so you get fewer prompts."
|
|
1009
1064
|
);
|
|
1010
|
-
await stepClaudePermissions(
|
|
1065
|
+
await stepClaudePermissions(outputDir, autoCommit);
|
|
1011
1066
|
if (!existsSync(localConfigPath())) {
|
|
1012
1067
|
writeJson(localConfigPath(), {});
|
|
1013
1068
|
}
|
|
1014
1069
|
console.log();
|
|
1015
1070
|
log.congrats("Setup complete!");
|
|
1016
1071
|
log.dim(
|
|
1017
|
-
"Available commands: /cf-ask, /cf-plan, /cf-
|
|
1072
|
+
"Available commands: /cf-ask, /cf-plan, /cf-design, /cf-fix, /cf-commit, /cf-review, /cf-review-out, /cf-review-in, /cf-ship, /cf-optimize, /cf-scan, /cf-remember, /cf-learn, /cf-teach, /cf-research, /cf-session, /cf-warm, /cf-help"
|
|
1018
1073
|
);
|
|
1019
1074
|
}
|
|
1020
1075
|
export {
|