coding-friend-cli 1.34.1 → 1.35.1
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-DHH6SRXV.js → chunk-57EGAXYI.js} +3 -2
- package/dist/{chunk-AZ6AK5M4.js → chunk-B2NFXRFP.js} +12 -2
- package/dist/{chunk-QCKIBJF6.js → chunk-C5YWESSF.js} +2 -6
- package/dist/chunk-E5J24GD5.js +190 -0
- package/dist/{chunk-G4QGJEV7.js → chunk-H5TXJZD7.js} +6 -6
- package/dist/{chunk-FAANBGCB.js → chunk-L53RNEV3.js} +116 -73
- package/dist/{chunk-DMJCO6LJ.js → chunk-RLUNNJWJ.js} +2 -1
- package/dist/{chunk-LXZ47XCD.js → chunk-WNTMKIKC.js} +1 -1
- package/dist/chunk-WSUYLIYX.js +61 -0
- package/dist/clean-67ACB4OS.js +95 -0
- package/dist/{config-44VQBNMS.js → config-HTQAALMA.js} +181 -120
- package/dist/{dev-QXVERU42.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-6IBKKCPA.js → host-HKA4OHBM.js} +3 -3
- package/dist/index.js +38 -34
- package/dist/{init-KY3ZPEWP.js → init-YM7YEEGK.js} +153 -85
- package/dist/{install-22BLHQ6D.js → install-VYZSTXN6.js} +8 -8
- package/dist/{mcp-OGZAZZRF.js → mcp-7MO2MCLE.js} +69 -66
- package/dist/{mcp-serve-learn-UNW6VH5M.js → mcp-serve-learn-OKVC5WV3.js} +19 -3
- package/dist/{memory-JM45DEIX.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-RCEE6P35.js → session-M4KRQ4VU.js} +2 -2
- package/dist/{status-NMSVOSC4.js → status-2XM5OZDR.js} +19 -14
- package/dist/{statusline-GKVAX7GR.js → statusline-VFIA7OS2.js} +3 -3
- package/dist/{uninstall-6ZJRS2H4.js → uninstall-QKEC3O64.js} +6 -6
- package/dist/{update-P5ZR2JVQ.js → update-IV4Y3T72.js} +6 -6
- package/package.json +3 -2
- package/dist/chunk-CZDXMFEZ.js +0 -118
- package/dist/chunk-FI5HEQ43.js +0 -85
- package/dist/{chunk-Z5Q533UG.js → chunk-HPNRQYLM.js} +3 -3
- package/dist/{update-check-5SCD2JR2.js → update-check-BPCQ25AR.js} +3 -3
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveDocsDir
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-B2NFXRFP.js";
|
|
4
4
|
import {
|
|
5
5
|
getLibPath
|
|
6
6
|
} from "./chunk-RZRT7NGT.js";
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-TWKNGPBO.js";
|
|
7
|
+
import "./chunk-57EGAXYI.js";
|
|
9
8
|
import {
|
|
10
9
|
run,
|
|
11
10
|
streamExec
|
|
12
11
|
} from "./chunk-EVGXUDX4.js";
|
|
12
|
+
import "./chunk-TWKNGPBO.js";
|
|
13
13
|
import {
|
|
14
14
|
log,
|
|
15
15
|
printBanner
|
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();
|
|
@@ -2,12 +2,16 @@ import {
|
|
|
2
2
|
ensureMemoryBuilt,
|
|
3
3
|
isMemoryInitialized,
|
|
4
4
|
memoryInitWizard
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-L53RNEV3.js";
|
|
6
|
+
import "./chunk-E5J24GD5.js";
|
|
7
|
+
import {
|
|
8
|
+
isLearnMcpRegistered,
|
|
9
|
+
registerLearnMcp
|
|
10
|
+
} from "./chunk-WSUYLIYX.js";
|
|
7
11
|
import {
|
|
8
12
|
memoryConfigMenu
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-C5YWESSF.js";
|
|
14
|
+
import "./chunk-B2NFXRFP.js";
|
|
11
15
|
import {
|
|
12
16
|
getLibPath
|
|
13
17
|
} from "./chunk-RZRT7NGT.js";
|
|
@@ -21,14 +25,14 @@ import {
|
|
|
21
25
|
saveStatuslineConfig,
|
|
22
26
|
selectStatuslineComponents,
|
|
23
27
|
writeStatuslineSettings
|
|
24
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-WNTMKIKC.js";
|
|
25
29
|
import {
|
|
26
30
|
DEFAULT_CONFIG
|
|
27
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-57EGAXYI.js";
|
|
28
32
|
import {
|
|
29
33
|
ensureShellCompletion,
|
|
30
34
|
hasShellCompletion
|
|
31
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-RLUNNJWJ.js";
|
|
32
36
|
import {
|
|
33
37
|
BACK,
|
|
34
38
|
applyDocsDirChange,
|
|
@@ -39,7 +43,11 @@ import {
|
|
|
39
43
|
getScopeLabel,
|
|
40
44
|
injectBackChoice,
|
|
41
45
|
showConfigHint
|
|
42
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-HPNRQYLM.js";
|
|
47
|
+
import {
|
|
48
|
+
commandExists,
|
|
49
|
+
run
|
|
50
|
+
} from "./chunk-EVGXUDX4.js";
|
|
43
51
|
import {
|
|
44
52
|
claudeLocalSettingsPath,
|
|
45
53
|
claudeProjectSettingsPath,
|
|
@@ -48,10 +56,6 @@ import {
|
|
|
48
56
|
localConfigPath,
|
|
49
57
|
resolvePath
|
|
50
58
|
} from "./chunk-TWKNGPBO.js";
|
|
51
|
-
import {
|
|
52
|
-
commandExists,
|
|
53
|
-
run
|
|
54
|
-
} from "./chunk-EVGXUDX4.js";
|
|
55
59
|
import {
|
|
56
60
|
log,
|
|
57
61
|
printBanner
|
|
@@ -73,7 +77,7 @@ import {
|
|
|
73
77
|
|
|
74
78
|
// src/commands/init.ts
|
|
75
79
|
import { checkbox, confirm, input, select } from "@inquirer/prompts";
|
|
76
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
80
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
77
81
|
import { homedir } from "os";
|
|
78
82
|
import { join } from "path";
|
|
79
83
|
import chalk from "chalk";
|
|
@@ -115,14 +119,7 @@ function paddedScopeLabel(scope) {
|
|
|
115
119
|
}
|
|
116
120
|
function printSetupStatus(globalCfg, localCfg, gitAvailable) {
|
|
117
121
|
const docsDir = getDocsDir(globalCfg, localCfg);
|
|
118
|
-
const subfolders = [
|
|
119
|
-
"plans",
|
|
120
|
-
"memory",
|
|
121
|
-
"research",
|
|
122
|
-
"learn",
|
|
123
|
-
"sessions",
|
|
124
|
-
"reviews"
|
|
125
|
-
];
|
|
122
|
+
const subfolders = ["plans", "memory", "research", "sessions", "reviews"];
|
|
126
123
|
const folderStatus = subfolders.map((sub) => ({
|
|
127
124
|
name: `${docsDir}/${sub}`,
|
|
128
125
|
exists: existsSync(`${docsDir}/${sub}`)
|
|
@@ -246,7 +243,6 @@ async function stepDocsDir(globalCfg, localCfg) {
|
|
|
246
243
|
"plans",
|
|
247
244
|
"memory",
|
|
248
245
|
"research",
|
|
249
|
-
"learn",
|
|
250
246
|
"sessions",
|
|
251
247
|
"reviews"
|
|
252
248
|
];
|
|
@@ -272,7 +268,6 @@ async function stepDocsDir(globalCfg, localCfg) {
|
|
|
272
268
|
"plans",
|
|
273
269
|
"memory",
|
|
274
270
|
"research",
|
|
275
|
-
"learn",
|
|
276
271
|
"sessions",
|
|
277
272
|
"reviews"
|
|
278
273
|
];
|
|
@@ -313,7 +308,6 @@ async function stepGitignore(docsDir) {
|
|
|
313
308
|
`${docsDir}/plans/`,
|
|
314
309
|
`${docsDir}/memory/`,
|
|
315
310
|
`${docsDir}/research/`,
|
|
316
|
-
`${docsDir}/learn/`,
|
|
317
311
|
`${docsDir}/sessions/`,
|
|
318
312
|
`${docsDir}/reviews/`,
|
|
319
313
|
`${docsDir}/context/`,
|
|
@@ -389,56 +383,43 @@ async function selectLanguage(message) {
|
|
|
389
383
|
}
|
|
390
384
|
return choice;
|
|
391
385
|
}
|
|
392
|
-
async function stepLearnConfig(globalCfg
|
|
393
|
-
const
|
|
394
|
-
const
|
|
395
|
-
const
|
|
386
|
+
async function stepLearnConfig(globalCfg) {
|
|
387
|
+
const globalLearn = globalCfg?.learn;
|
|
388
|
+
const defaultLearnDir = DEFAULT_CONFIG.learn.outputDir;
|
|
389
|
+
const currentOutputDir = globalLearn?.outputDir ?? defaultLearnDir;
|
|
396
390
|
printStepHeader(
|
|
397
|
-
|
|
391
|
+
"/cf-learn config",
|
|
398
392
|
"Controls where and how /cf-learn saves your learning notes."
|
|
399
393
|
);
|
|
400
|
-
const globalLearn = globalCfg?.learn;
|
|
401
|
-
if (globalLearn) {
|
|
402
|
-
const parts = [
|
|
403
|
-
globalLearn.language || "en",
|
|
404
|
-
globalLearn.outputDir || `${docsDir}/learn`
|
|
405
|
-
];
|
|
406
|
-
if (globalLearn.categories) {
|
|
407
|
-
parts.push(`${globalLearn.categories.length} categories`);
|
|
408
|
-
}
|
|
409
|
-
if (await offerGlobalShortcut(parts.join(", "))) {
|
|
410
|
-
const gOutputDir = globalLearn.outputDir || `${docsDir}/learn`;
|
|
411
|
-
const gIsExternal = !gOutputDir.startsWith(`${docsDir}/`);
|
|
412
|
-
return {
|
|
413
|
-
outputDir: gOutputDir,
|
|
414
|
-
autoCommit: globalLearn.autoCommit || false,
|
|
415
|
-
isExternal: gIsExternal
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
394
|
const language = await selectLanguage(
|
|
420
395
|
"What language should /cf-learn notes be written in?"
|
|
421
396
|
);
|
|
422
397
|
const locationChoice = await select({
|
|
423
|
-
message:
|
|
398
|
+
message: `Current learn folder: ${currentOutputDir}`,
|
|
424
399
|
choices: injectBackChoice(
|
|
425
400
|
[
|
|
426
|
-
{
|
|
427
|
-
|
|
401
|
+
{
|
|
402
|
+
name: `Use default (${defaultLearnDir})`,
|
|
403
|
+
value: "default"
|
|
404
|
+
},
|
|
405
|
+
{ name: "Custom path\u2026", value: "custom" }
|
|
428
406
|
],
|
|
429
407
|
"Cancel init"
|
|
430
408
|
)
|
|
431
409
|
});
|
|
432
410
|
handleBack(locationChoice);
|
|
433
|
-
let outputDir =
|
|
434
|
-
|
|
435
|
-
if (locationChoice === "external") {
|
|
411
|
+
let outputDir = defaultLearnDir;
|
|
412
|
+
if (locationChoice === "custom") {
|
|
436
413
|
outputDir = await input({
|
|
437
414
|
message: "Enter path (absolute or ~/...):",
|
|
438
|
-
default:
|
|
439
|
-
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
|
+
}
|
|
440
422
|
});
|
|
441
|
-
isExternal = true;
|
|
442
423
|
const resolved = resolvePath(outputDir);
|
|
443
424
|
if (!existsSync(resolved)) {
|
|
444
425
|
const create = await confirm({
|
|
@@ -446,12 +427,12 @@ async function stepLearnConfig(globalCfg, localCfg, gitAvailable) {
|
|
|
446
427
|
default: true
|
|
447
428
|
});
|
|
448
429
|
if (create) {
|
|
449
|
-
|
|
430
|
+
mkdirSync(resolved, { recursive: true });
|
|
450
431
|
log.success(`Created ${resolved}`);
|
|
451
432
|
}
|
|
452
433
|
}
|
|
453
434
|
}
|
|
454
|
-
const existingCats =
|
|
435
|
+
const existingCats = globalLearn?.categories;
|
|
455
436
|
const defaultNames = DEFAULT_CONFIG.learn.categories.map((c) => c.name).join(", ");
|
|
456
437
|
const catChoices = [
|
|
457
438
|
{ name: `Use defaults (${defaultNames})`, value: "defaults" }
|
|
@@ -507,10 +488,18 @@ async function stepLearnConfig(globalCfg, localCfg, gitAvailable) {
|
|
|
507
488
|
if (customCats.length > 0) categories = customCats;
|
|
508
489
|
}
|
|
509
490
|
let autoCommit = false;
|
|
510
|
-
|
|
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) {
|
|
511
500
|
autoCommit = await confirm({
|
|
512
501
|
message: "Auto-commit learning docs to git after each /cf-learn?",
|
|
513
|
-
default:
|
|
502
|
+
default: globalLearn?.autoCommit ?? false
|
|
514
503
|
});
|
|
515
504
|
}
|
|
516
505
|
const indexChoice = await select({
|
|
@@ -535,17 +524,11 @@ async function stepLearnConfig(globalCfg, localCfg, gitAvailable) {
|
|
|
535
524
|
autoCommit,
|
|
536
525
|
readmeIndex
|
|
537
526
|
};
|
|
538
|
-
const
|
|
539
|
-
if (scope === "back") {
|
|
540
|
-
log.dim("Skipped /cf-learn config.");
|
|
541
|
-
return { outputDir, autoCommit, isExternal };
|
|
542
|
-
}
|
|
543
|
-
const targetPath = scope === "global" ? globalConfigPath() : localConfigPath();
|
|
544
|
-
const existingConfig = readJson(targetPath);
|
|
527
|
+
const existingConfig = readJson(globalConfigPath());
|
|
545
528
|
const existingLearn = existingConfig?.learn ?? {};
|
|
546
|
-
mergeJson(
|
|
547
|
-
log.success(`Saved to ${
|
|
548
|
-
return { outputDir, autoCommit
|
|
529
|
+
mergeJson(globalConfigPath(), { learn: { ...existingLearn, ...learnObj } });
|
|
530
|
+
log.success(`Saved to ${globalConfigPath()}`);
|
|
531
|
+
return { outputDir, autoCommit };
|
|
549
532
|
}
|
|
550
533
|
async function stepShellCompletion() {
|
|
551
534
|
if (hasShellCompletion()) {
|
|
@@ -657,6 +640,62 @@ async function stepMemory(docsDir, opts) {
|
|
|
657
640
|
const memoryDir = join(process.cwd(), docsDir, "memory");
|
|
658
641
|
await memoryInitWizard(memoryDir, mcpDir);
|
|
659
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
|
+
}
|
|
660
699
|
async function stepClaudePermissions(externalLearnDir, autoCommit) {
|
|
661
700
|
const scope = await select({
|
|
662
701
|
message: "Where should permissions be saved?",
|
|
@@ -736,6 +775,8 @@ async function initMenu(gitAvailable) {
|
|
|
736
775
|
const completionStatus = hasShellCompletion() ? chalk.green("installed") : chalk.yellow("not installed");
|
|
737
776
|
const statuslineStatus = isStatuslineConfigured() ? chalk.green("configured") : chalk.yellow("not configured");
|
|
738
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);
|
|
739
780
|
const autoApproveScope = getScopeLabel("autoApprove", globalCfg, localCfg);
|
|
740
781
|
const autoApproveVal = getMergedValue(
|
|
741
782
|
"autoApprove",
|
|
@@ -788,6 +829,11 @@ async function initMenu(gitAvailable) {
|
|
|
788
829
|
value: "memory",
|
|
789
830
|
description: " Persistent project knowledge \u2014 search tier, embeddings, MCP"
|
|
790
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
|
+
},
|
|
791
837
|
{
|
|
792
838
|
name: `Auto-approve ${formatScopeLabel(autoApproveScope)}${autoApproveVal !== void 0 ? ` (${autoApproveVal})` : ""}`,
|
|
793
839
|
value: "autoApprove",
|
|
@@ -819,7 +865,7 @@ async function initMenu(gitAvailable) {
|
|
|
819
865
|
await stepDocsLanguage(globalCfg, localCfg);
|
|
820
866
|
break;
|
|
821
867
|
case "learn":
|
|
822
|
-
await stepLearnConfig(globalCfg
|
|
868
|
+
await stepLearnConfig(globalCfg);
|
|
823
869
|
break;
|
|
824
870
|
case "completion":
|
|
825
871
|
await stepShellCompletion();
|
|
@@ -830,6 +876,19 @@ async function initMenu(gitAvailable) {
|
|
|
830
876
|
case "memory":
|
|
831
877
|
await stepMemory(docsDir, { menuMode: true });
|
|
832
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
|
+
}
|
|
833
892
|
case "autoApprove": {
|
|
834
893
|
const autoApproveChoice = await confirm({
|
|
835
894
|
message: "Enable auto-approve? (auto-approves read-only tools + working-dir file edits, LLM classifier for unknowns)",
|
|
@@ -862,14 +921,10 @@ async function initMenu(gitAvailable) {
|
|
|
862
921
|
break;
|
|
863
922
|
}
|
|
864
923
|
case "permissions": {
|
|
865
|
-
const learnCfg =
|
|
866
|
-
const learnOutputDir = learnCfg?.outputDir
|
|
867
|
-
const learnIsExternal = !learnOutputDir.startsWith(`${docsDir}/`);
|
|
924
|
+
const learnCfg = globalCfg?.learn;
|
|
925
|
+
const learnOutputDir = learnCfg?.outputDir ?? DEFAULT_CONFIG.learn.outputDir;
|
|
868
926
|
const learnAutoCommit = learnCfg?.autoCommit || false;
|
|
869
|
-
await stepClaudePermissions(
|
|
870
|
-
learnIsExternal ? learnOutputDir : null,
|
|
871
|
-
learnAutoCommit
|
|
872
|
-
);
|
|
927
|
+
await stepClaudePermissions(learnOutputDir, learnAutoCommit);
|
|
873
928
|
break;
|
|
874
929
|
}
|
|
875
930
|
}
|
|
@@ -982,19 +1037,32 @@ async function initCommand() {
|
|
|
982
1037
|
log.dim("Skipped \u2014 not inside a git repo.");
|
|
983
1038
|
}
|
|
984
1039
|
await stepDocsLanguage(globalCfg, localCfg);
|
|
985
|
-
const { outputDir, autoCommit
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
)
|
|
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
|
+
}
|
|
990
1054
|
await stepShellCompletion();
|
|
991
1055
|
await stepStatusline();
|
|
992
1056
|
await stepMemory(docsDir);
|
|
1057
|
+
const finalGlobal = readJson(globalConfigPath());
|
|
1058
|
+
const finalLocal = readJson(localConfigPath());
|
|
1059
|
+
await stepTdd(finalGlobal, finalLocal);
|
|
1060
|
+
await stepAutoApprove(finalGlobal, finalLocal);
|
|
993
1061
|
printStepHeader(
|
|
994
1062
|
"Configure Claude permissions",
|
|
995
1063
|
"Grants Coding Friend skills/hooks the permissions they need, so you get fewer prompts."
|
|
996
1064
|
);
|
|
997
|
-
await stepClaudePermissions(
|
|
1065
|
+
await stepClaudePermissions(outputDir, autoCommit);
|
|
998
1066
|
if (!existsSync(localConfigPath())) {
|
|
999
1067
|
writeJson(localConfigPath(), {});
|
|
1000
1068
|
}
|