coding-friend-cli 1.17.2 → 1.18.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 +7 -4
- package/dist/chunk-7CAIGH2Y.js +365 -0
- package/dist/{chunk-G6CEEMAR.js → chunk-A427XMWE.js} +1 -1
- package/dist/{chunk-YO6JKGR3.js → chunk-VUAUAO2R.js} +27 -6
- package/dist/{config-LZFXXOI4.js → config-6SBGNTAQ.js} +80 -1
- package/dist/{dev-R3IYWZ3M.js → dev-MC6TGHRT.js} +1 -1
- package/dist/index.js +31 -28
- package/dist/{init-MF7ISADJ.js → init-2HLPKYXB.js} +220 -35
- package/dist/{install-Q4PWEU43.js → install-3QCRGPTY.js} +2 -2
- package/dist/{memory-3M2IY6MR.js → memory-BQK2R7BV.js} +10 -10
- package/dist/{permission-L2QQR5PO.js → permission-Z3LOBJ4X.js} +72 -11
- package/dist/postinstall.js +1 -1
- package/dist/{uninstall-3PSUDGI4.js → uninstall-5LRHXFSF.js} +1 -1
- package/dist/{update-WL6SFGGO.js → update-4YUSCBCB.js} +2 -2
- package/lib/cf-memory/CHANGELOG.md +5 -0
- package/lib/cf-memory/README.md +2 -2
- package/lib/cf-memory/package.json +1 -1
- package/lib/cf-memory/src/__tests__/project-id.test.ts +43 -0
- package/lib/cf-memory/src/backends/sqlite/index.ts +13 -11
- package/package.json +1 -1
- package/dist/chunk-56U7US6J.js +0 -198
package/dist/index.js
CHANGED
|
@@ -14,11 +14,11 @@ 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-3QCRGPTY.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-5LRHXFSF.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) => {
|
|
@@ -30,11 +30,11 @@ program.command("enable").description("Re-enable the Coding Friend plugin").opti
|
|
|
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-2HLPKYXB.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-6SBGNTAQ.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) => {
|
|
@@ -45,8 +45,11 @@ program.command("mcp").description("Setup MCP server for learning docs").argumen
|
|
|
45
45
|
const { mcpCommand } = await import("./mcp-TBEDYELW.js");
|
|
46
46
|
await mcpCommand(path);
|
|
47
47
|
});
|
|
48
|
-
program.command("permission").description("Manage Claude Code permission rules for Coding Friend").option("--all", "Apply all recommended permissions without prompts").
|
|
49
|
-
|
|
48
|
+
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(
|
|
49
|
+
"--project",
|
|
50
|
+
"Save to project-level settings (.claude/settings.local.json)"
|
|
51
|
+
).action(async (opts) => {
|
|
52
|
+
const { permissionCommand } = await import("./permission-Z3LOBJ4X.js");
|
|
50
53
|
await permissionCommand(opts);
|
|
51
54
|
});
|
|
52
55
|
program.command("statusline").description("Setup coding-friend statusline in Claude Code").action(async () => {
|
|
@@ -54,7 +57,7 @@ program.command("statusline").description("Setup coding-friend statusline in Cla
|
|
|
54
57
|
await statuslineCommand();
|
|
55
58
|
});
|
|
56
59
|
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-
|
|
60
|
+
const { updateCommand } = await import("./update-4YUSCBCB.js");
|
|
58
61
|
await updateCommand(opts);
|
|
59
62
|
});
|
|
60
63
|
var session = program.command("session").description("Save and load Claude Code sessions across machines");
|
|
@@ -86,45 +89,45 @@ Memory subcommands:
|
|
|
86
89
|
memory list List memories in current project (--projects for all DBs)
|
|
87
90
|
memory rm Remove a project database (--project-id <id>, --all, or --prune)
|
|
88
91
|
memory init Initialize Tier 1 (install SQLite deps, import existing memories)
|
|
89
|
-
memory start
|
|
90
|
-
memory stop
|
|
92
|
+
memory start-daemon Start the memory daemon (Tier 2)
|
|
93
|
+
memory stop-daemon Stop the memory daemon
|
|
91
94
|
memory rebuild Rebuild the daemon search index
|
|
92
95
|
memory mcp Show MCP server setup instructions`
|
|
93
96
|
);
|
|
94
97
|
memory.command("status").description("Show memory system status").action(async () => {
|
|
95
|
-
const { memoryStatusCommand } = await import("./memory-
|
|
98
|
+
const { memoryStatusCommand } = await import("./memory-BQK2R7BV.js");
|
|
96
99
|
await memoryStatusCommand();
|
|
97
100
|
});
|
|
98
101
|
memory.command("search").description("Search memories by query").argument("<query>", "search query").action(async (query) => {
|
|
99
|
-
const { memorySearchCommand } = await import("./memory-
|
|
102
|
+
const { memorySearchCommand } = await import("./memory-BQK2R7BV.js");
|
|
100
103
|
await memorySearchCommand(query);
|
|
101
104
|
});
|
|
102
105
|
memory.command("list").description(
|
|
103
106
|
"List memories in current project, or all projects with --projects"
|
|
104
107
|
).option("--projects", "List all project databases with size and metadata").action(async (opts) => {
|
|
105
|
-
const { memoryListCommand } = await import("./memory-
|
|
108
|
+
const { memoryListCommand } = await import("./memory-BQK2R7BV.js");
|
|
106
109
|
await memoryListCommand(opts);
|
|
107
110
|
});
|
|
108
111
|
memory.command("init").description(
|
|
109
112
|
"Initialize Tier 1 \u2014 install SQLite deps and import existing memories"
|
|
110
113
|
).action(async () => {
|
|
111
|
-
const { memoryInitCommand } = await import("./memory-
|
|
114
|
+
const { memoryInitCommand } = await import("./memory-BQK2R7BV.js");
|
|
112
115
|
await memoryInitCommand();
|
|
113
116
|
});
|
|
114
|
-
memory.command("start").description("Start the memory daemon (Tier 2 \u2014 MiniSearch)").action(async () => {
|
|
115
|
-
const {
|
|
116
|
-
await
|
|
117
|
+
memory.command("start-daemon").description("Start the memory daemon (Tier 2 \u2014 MiniSearch)").action(async () => {
|
|
118
|
+
const { memoryStartDaemonCommand } = await import("./memory-BQK2R7BV.js");
|
|
119
|
+
await memoryStartDaemonCommand();
|
|
117
120
|
});
|
|
118
|
-
memory.command("stop").description("Stop the memory daemon").action(async () => {
|
|
119
|
-
const {
|
|
120
|
-
await
|
|
121
|
+
memory.command("stop-daemon").description("Stop the memory daemon").action(async () => {
|
|
122
|
+
const { memoryStopDaemonCommand } = await import("./memory-BQK2R7BV.js");
|
|
123
|
+
await memoryStopDaemonCommand();
|
|
121
124
|
});
|
|
122
125
|
memory.command("rebuild").description("Rebuild the daemon search index").action(async () => {
|
|
123
|
-
const { memoryRebuildCommand } = await import("./memory-
|
|
126
|
+
const { memoryRebuildCommand } = await import("./memory-BQK2R7BV.js");
|
|
124
127
|
await memoryRebuildCommand();
|
|
125
128
|
});
|
|
126
129
|
memory.command("mcp").description("Show MCP server setup instructions").action(async () => {
|
|
127
|
-
const { memoryMcpCommand } = await import("./memory-
|
|
130
|
+
const { memoryMcpCommand } = await import("./memory-BQK2R7BV.js");
|
|
128
131
|
await memoryMcpCommand();
|
|
129
132
|
});
|
|
130
133
|
memory.command("rm").description("Remove a project database").option("--project-id <id>", "Project ID to remove").option("--all", "Remove all project databases").option(
|
|
@@ -132,7 +135,7 @@ memory.command("rm").description("Remove a project database").option("--project-
|
|
|
132
135
|
"Remove orphaned projects (source dir missing or 0 memories)"
|
|
133
136
|
).action(
|
|
134
137
|
async (opts) => {
|
|
135
|
-
const { memoryRmCommand } = await import("./memory-
|
|
138
|
+
const { memoryRmCommand } = await import("./memory-BQK2R7BV.js");
|
|
136
139
|
await memoryRmCommand(opts);
|
|
137
140
|
}
|
|
138
141
|
);
|
|
@@ -149,35 +152,35 @@ Dev subcommands:
|
|
|
149
152
|
dev update [path] Update local dev plugin to latest version`
|
|
150
153
|
);
|
|
151
154
|
dev.command("on").description("Switch to local plugin source").argument("[path]", "path to local coding-friend repo (default: cwd)").action(async (path) => {
|
|
152
|
-
const { devOnCommand } = await import("./dev-
|
|
155
|
+
const { devOnCommand } = await import("./dev-MC6TGHRT.js");
|
|
153
156
|
await devOnCommand(path);
|
|
154
157
|
});
|
|
155
158
|
dev.command("off").description("Switch back to remote marketplace").action(async () => {
|
|
156
|
-
const { devOffCommand } = await import("./dev-
|
|
159
|
+
const { devOffCommand } = await import("./dev-MC6TGHRT.js");
|
|
157
160
|
await devOffCommand();
|
|
158
161
|
});
|
|
159
162
|
dev.command("status").description("Show current dev mode").action(async () => {
|
|
160
|
-
const { devStatusCommand } = await import("./dev-
|
|
163
|
+
const { devStatusCommand } = await import("./dev-MC6TGHRT.js");
|
|
161
164
|
await devStatusCommand();
|
|
162
165
|
});
|
|
163
166
|
dev.command("sync").description(
|
|
164
167
|
"Copy local source files to plugin cache (no version bump needed)"
|
|
165
168
|
).action(async () => {
|
|
166
|
-
const { devSyncCommand } = await import("./dev-
|
|
169
|
+
const { devSyncCommand } = await import("./dev-MC6TGHRT.js");
|
|
167
170
|
await devSyncCommand();
|
|
168
171
|
});
|
|
169
172
|
dev.command("restart").description("Reinstall local dev plugin (off + on)").argument(
|
|
170
173
|
"[path]",
|
|
171
174
|
"path to local coding-friend repo (default: saved path or cwd)"
|
|
172
175
|
).action(async (path) => {
|
|
173
|
-
const { devRestartCommand } = await import("./dev-
|
|
176
|
+
const { devRestartCommand } = await import("./dev-MC6TGHRT.js");
|
|
174
177
|
await devRestartCommand(path);
|
|
175
178
|
});
|
|
176
179
|
dev.command("update").description("Update local dev plugin to latest version (off + on)").argument(
|
|
177
180
|
"[path]",
|
|
178
181
|
"path to local coding-friend repo (default: saved path or cwd)"
|
|
179
182
|
).action(async (path) => {
|
|
180
|
-
const { devUpdateCommand } = await import("./dev-
|
|
183
|
+
const { devUpdateCommand } = await import("./dev-MC6TGHRT.js");
|
|
181
184
|
await devUpdateCommand(path);
|
|
182
185
|
});
|
|
183
186
|
program.parse();
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
applyPermissions,
|
|
3
3
|
buildLearnDirRules,
|
|
4
|
+
cleanupStalePluginRules,
|
|
5
|
+
getAllRules,
|
|
4
6
|
getExistingRules,
|
|
5
|
-
getMissingRules
|
|
6
|
-
|
|
7
|
+
getMissingRules,
|
|
8
|
+
logPluginScriptWarning
|
|
9
|
+
} from "./chunk-7CAIGH2Y.js";
|
|
7
10
|
import {
|
|
8
11
|
getLibPath
|
|
9
12
|
} from "./chunk-RZRT7NGT.js";
|
|
@@ -20,7 +23,7 @@ import {
|
|
|
20
23
|
import {
|
|
21
24
|
ensureShellCompletion,
|
|
22
25
|
hasShellCompletion
|
|
23
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-VUAUAO2R.js";
|
|
24
27
|
import {
|
|
25
28
|
BACK,
|
|
26
29
|
applyDocsDirChange,
|
|
@@ -37,6 +40,7 @@ import {
|
|
|
37
40
|
run
|
|
38
41
|
} from "./chunk-CYQU33FY.js";
|
|
39
42
|
import {
|
|
43
|
+
claudeLocalSettingsPath,
|
|
40
44
|
claudeSettingsPath,
|
|
41
45
|
globalConfigPath,
|
|
42
46
|
localConfigPath,
|
|
@@ -71,7 +75,9 @@ function printBanner() {
|
|
|
71
75
|
console.log();
|
|
72
76
|
}
|
|
73
77
|
var _stepIndex = 0;
|
|
78
|
+
var _suppressStepHeaders = false;
|
|
74
79
|
function printStepHeader(label, description) {
|
|
80
|
+
if (_suppressStepHeaders) return;
|
|
75
81
|
_stepIndex++;
|
|
76
82
|
const line = chalk.hex("#10b981")("\u2500".repeat(44));
|
|
77
83
|
console.log();
|
|
@@ -136,6 +142,14 @@ function printSetupStatus(globalCfg, localCfg, gitAvailable) {
|
|
|
136
142
|
if (scope === "-") notConfiguredCount++;
|
|
137
143
|
if (scope === "-" || scope === "global") notLocalCount++;
|
|
138
144
|
}
|
|
145
|
+
const projectRules = getExistingRules(claudeLocalSettingsPath());
|
|
146
|
+
const userRules = getExistingRules(claudeSettingsPath());
|
|
147
|
+
const allRules = getAllRules();
|
|
148
|
+
const allRuleStrings = allRules.map((r) => r.rule);
|
|
149
|
+
const configuredRuleCount = (/* @__PURE__ */ new Set([
|
|
150
|
+
...projectRules.filter((r) => allRuleStrings.includes(r)),
|
|
151
|
+
...userRules.filter((r) => allRuleStrings.includes(r))
|
|
152
|
+
])).size;
|
|
139
153
|
const setupItems = [
|
|
140
154
|
{
|
|
141
155
|
label: ".gitignore",
|
|
@@ -144,7 +158,12 @@ function printSetupStatus(globalCfg, localCfg, gitAvailable) {
|
|
|
144
158
|
},
|
|
145
159
|
{ label: "Shell completion", done: hasShellCompletion(), skipped: false },
|
|
146
160
|
{ label: "Statusline", done: isStatuslineConfigured(), skipped: false },
|
|
147
|
-
{ label: "CF Memory MCP", done: isMemoryMcpConfigured(), skipped: false }
|
|
161
|
+
{ label: "CF Memory MCP", done: isMemoryMcpConfigured(), skipped: false },
|
|
162
|
+
{
|
|
163
|
+
label: `Permissions (${configuredRuleCount}/${allRules.length} rules)`,
|
|
164
|
+
done: configuredRuleCount > 0,
|
|
165
|
+
skipped: false
|
|
166
|
+
}
|
|
148
167
|
];
|
|
149
168
|
for (const item of setupItems) {
|
|
150
169
|
if (item.skipped) {
|
|
@@ -639,33 +658,57 @@ async function stepMemory(docsDir) {
|
|
|
639
658
|
`Tip: Run ${chalk.cyan("/cf-scan")} in Claude Code to populate memory with project knowledge.`
|
|
640
659
|
);
|
|
641
660
|
}
|
|
642
|
-
async function stepClaudePermissions(
|
|
643
|
-
const
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
661
|
+
async function stepClaudePermissions(externalLearnDir, autoCommit) {
|
|
662
|
+
const scope = await select({
|
|
663
|
+
message: "Where should permissions be saved?",
|
|
664
|
+
choices: [
|
|
665
|
+
{
|
|
666
|
+
name: "Project \u2014 .claude/settings.local.json (this project only, gitignored)",
|
|
667
|
+
value: "project"
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
name: "User \u2014 ~/.claude/settings.json (all projects)",
|
|
671
|
+
value: "user"
|
|
672
|
+
},
|
|
673
|
+
{ name: "Skip", value: "skip" }
|
|
674
|
+
]
|
|
675
|
+
});
|
|
676
|
+
if (scope === "skip") {
|
|
677
|
+
log.dim("Skipped. Run `cf permission` later to configure.");
|
|
651
678
|
return;
|
|
652
679
|
}
|
|
653
|
-
const
|
|
654
|
-
const
|
|
680
|
+
const settingsPath = scope === "user" ? claudeSettingsPath() : claudeLocalSettingsPath();
|
|
681
|
+
const existing = getExistingRules(settingsPath);
|
|
682
|
+
const allRules = getAllRules();
|
|
683
|
+
const recommended = allRules.filter((r) => r.recommended);
|
|
684
|
+
let allToAdd = recommended;
|
|
685
|
+
if (externalLearnDir) {
|
|
686
|
+
const resolved = resolvePath(externalLearnDir);
|
|
687
|
+
const homePath = resolved.startsWith(homedir()) ? resolved.replace(homedir(), "~") : resolved;
|
|
688
|
+
const learnRules = buildLearnDirRules(homePath, autoCommit);
|
|
689
|
+
allToAdd = [...recommended, ...learnRules];
|
|
690
|
+
}
|
|
691
|
+
const missing = getMissingRules(existing, allToAdd);
|
|
655
692
|
if (missing.length === 0) {
|
|
656
|
-
log.dim("All permission rules already configured.");
|
|
693
|
+
log.dim("All recommended permission rules already configured.");
|
|
657
694
|
return;
|
|
658
695
|
}
|
|
659
|
-
|
|
696
|
+
log.step(`${missing.length} permission rules to add:`);
|
|
660
697
|
for (const r of missing) {
|
|
661
|
-
console.log(` ${r.rule}`);
|
|
698
|
+
console.log(` ${chalk.green("+")} ${r.rule}`);
|
|
662
699
|
}
|
|
700
|
+
const hasPluginRule = missing.some((r) => r.category === "Plugin Scripts");
|
|
701
|
+
if (hasPluginRule) {
|
|
702
|
+
console.log();
|
|
703
|
+
logPluginScriptWarning(log, chalk);
|
|
704
|
+
}
|
|
705
|
+
console.log();
|
|
663
706
|
const ok = await confirm({
|
|
664
|
-
message:
|
|
707
|
+
message: `Add all ${missing.length} recommended rules?`,
|
|
665
708
|
default: true
|
|
666
709
|
});
|
|
667
710
|
if (!ok) {
|
|
668
|
-
log.dim("Skipped.
|
|
711
|
+
log.dim("Skipped. Run `cf permission` later to configure interactively.");
|
|
669
712
|
return;
|
|
670
713
|
}
|
|
671
714
|
applyPermissions(
|
|
@@ -673,8 +716,125 @@ async function stepClaudePermissions(outputDir, autoCommit) {
|
|
|
673
716
|
missing.map((r) => r.rule),
|
|
674
717
|
[]
|
|
675
718
|
);
|
|
719
|
+
const cleaned = cleanupStalePluginRules(settingsPath);
|
|
720
|
+
if (cleaned > 0) {
|
|
721
|
+
log.dim(`Removed ${cleaned} stale old-format plugin rules.`);
|
|
722
|
+
}
|
|
676
723
|
log.success(`Added ${missing.length} permission rules.`);
|
|
677
|
-
log.dim("
|
|
724
|
+
log.dim("Fine-tune later with: `cf permission` or `cf config` \u2192 Permissions");
|
|
725
|
+
}
|
|
726
|
+
async function initMenu(gitAvailable) {
|
|
727
|
+
while (true) {
|
|
728
|
+
const globalCfg = readJson(globalConfigPath());
|
|
729
|
+
const localCfg = readJson(localConfigPath());
|
|
730
|
+
const docsDir = getDocsDir(globalCfg, localCfg);
|
|
731
|
+
const docsDirScope = getScopeLabel("docsDir", globalCfg, localCfg);
|
|
732
|
+
const docsDirVal = getMergedValue("docsDir", globalCfg, localCfg);
|
|
733
|
+
const langScope = getScopeLabel("language", globalCfg, localCfg);
|
|
734
|
+
const langVal = getMergedValue("language", globalCfg, localCfg);
|
|
735
|
+
const learnScope = getScopeLabel("learn", globalCfg, localCfg);
|
|
736
|
+
const gitignoreStatus = !gitAvailable ? chalk.dim("skipped") : hasGitignoreBlock() ? chalk.green("configured") : chalk.yellow("not configured");
|
|
737
|
+
const completionStatus = hasShellCompletion() ? chalk.green("installed") : chalk.yellow("not installed");
|
|
738
|
+
const statuslineStatus = isStatuslineConfigured() ? chalk.green("configured") : chalk.yellow("not configured");
|
|
739
|
+
const memoryStatus = isMemoryMcpConfigured() ? chalk.green("configured") : chalk.yellow("not configured");
|
|
740
|
+
const projectRules = getExistingRules(claudeLocalSettingsPath());
|
|
741
|
+
const userRules = getExistingRules(claudeSettingsPath());
|
|
742
|
+
const allRules = getAllRules();
|
|
743
|
+
const allRuleStrings = allRules.map((r) => r.rule);
|
|
744
|
+
const configuredRuleCount = (/* @__PURE__ */ new Set([
|
|
745
|
+
...projectRules.filter((r) => allRuleStrings.includes(r)),
|
|
746
|
+
...userRules.filter((r) => allRuleStrings.includes(r))
|
|
747
|
+
])).size;
|
|
748
|
+
const permissionStatus = configuredRuleCount > 0 ? chalk.green(`${configuredRuleCount}/${allRules.length}`) : chalk.yellow(`0/${allRules.length}`);
|
|
749
|
+
const choices = [
|
|
750
|
+
{
|
|
751
|
+
name: `Docs folder ${formatScopeLabel(docsDirScope)}${docsDirVal ? ` (${docsDirVal})` : ""}`,
|
|
752
|
+
value: "docsDir",
|
|
753
|
+
description: " Where plans, memory, research, and session docs are stored"
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
name: `.gitignore (${gitignoreStatus})`,
|
|
757
|
+
value: "gitignore",
|
|
758
|
+
description: " Add or update coding-friend artifacts in .gitignore",
|
|
759
|
+
...gitAvailable ? {} : { disabled: "not in a git repo" }
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
name: `Docs language ${formatScopeLabel(langScope)}${langVal ? ` (${langVal})` : ""}`,
|
|
763
|
+
value: "language",
|
|
764
|
+
description: " Language for /cf-plan, /cf-ask, /cf-remember generated docs"
|
|
765
|
+
},
|
|
766
|
+
{
|
|
767
|
+
name: `/cf-learn config ${formatScopeLabel(learnScope)}`,
|
|
768
|
+
value: "learn",
|
|
769
|
+
description: " Output dir, language, categories, auto-commit, README index"
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
name: `Shell completion (${completionStatus})`,
|
|
773
|
+
value: "completion",
|
|
774
|
+
description: " Tab-complete for cf commands in your shell"
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
name: `Statusline (${statuslineStatus})`,
|
|
778
|
+
value: "statusline",
|
|
779
|
+
description: " Token count, model, and session info in your terminal prompt"
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
name: `CF Memory MCP (${memoryStatus})`,
|
|
783
|
+
value: "memory",
|
|
784
|
+
description: " Connect the memory system to Claude Code via MCP"
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
name: `Permissions (${permissionStatus} rules)`,
|
|
788
|
+
value: "permissions",
|
|
789
|
+
description: " Grant Coding Friend skills/hooks the permissions they need"
|
|
790
|
+
}
|
|
791
|
+
];
|
|
792
|
+
const choice = await select({
|
|
793
|
+
message: "Which setting to configure?",
|
|
794
|
+
choices: injectBackChoice(choices, "Exit")
|
|
795
|
+
});
|
|
796
|
+
if (choice === BACK) {
|
|
797
|
+
log.dim("Done. Run `cf init` anytime to reconfigure.");
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
_suppressStepHeaders = true;
|
|
801
|
+
switch (choice) {
|
|
802
|
+
case "docsDir":
|
|
803
|
+
await stepDocsDir(globalCfg, localCfg);
|
|
804
|
+
break;
|
|
805
|
+
case "gitignore":
|
|
806
|
+
await stepGitignore(docsDir);
|
|
807
|
+
break;
|
|
808
|
+
case "language":
|
|
809
|
+
await stepDocsLanguage(globalCfg, localCfg);
|
|
810
|
+
break;
|
|
811
|
+
case "learn":
|
|
812
|
+
await stepLearnConfig(globalCfg, localCfg, gitAvailable);
|
|
813
|
+
break;
|
|
814
|
+
case "completion":
|
|
815
|
+
await stepShellCompletion();
|
|
816
|
+
break;
|
|
817
|
+
case "statusline":
|
|
818
|
+
await stepStatusline();
|
|
819
|
+
break;
|
|
820
|
+
case "memory":
|
|
821
|
+
await stepMemory(docsDir);
|
|
822
|
+
break;
|
|
823
|
+
case "permissions": {
|
|
824
|
+
const learnCfg = localCfg?.learn ?? globalCfg?.learn;
|
|
825
|
+
const learnOutputDir = learnCfg?.outputDir || `${docsDir}/learn`;
|
|
826
|
+
const learnIsExternal = !learnOutputDir.startsWith(`${docsDir}/`);
|
|
827
|
+
const learnAutoCommit = learnCfg?.autoCommit || false;
|
|
828
|
+
await stepClaudePermissions(
|
|
829
|
+
learnIsExternal ? learnOutputDir : null,
|
|
830
|
+
learnAutoCommit
|
|
831
|
+
);
|
|
832
|
+
break;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
_suppressStepHeaders = false;
|
|
836
|
+
console.log();
|
|
837
|
+
}
|
|
678
838
|
}
|
|
679
839
|
async function initCommand() {
|
|
680
840
|
_stepIndex = 0;
|
|
@@ -685,11 +845,43 @@ async function initCommand() {
|
|
|
685
845
|
log.warn("Not inside a git repo -- git-related steps will be skipped.");
|
|
686
846
|
console.log();
|
|
687
847
|
}
|
|
848
|
+
const alreadyInitialized = existsSync(join(process.cwd(), ".coding-friend"));
|
|
688
849
|
const globalCfg = readJson(globalConfigPath());
|
|
689
850
|
const localCfg = readJson(localConfigPath());
|
|
690
851
|
console.log("Project status:");
|
|
691
852
|
console.log();
|
|
692
853
|
const { allDone, notLocalCount, notConfiguredCount, missingFolders } = printSetupStatus(globalCfg, localCfg, gitAvailable);
|
|
854
|
+
if (alreadyInitialized) {
|
|
855
|
+
if (allDone) {
|
|
856
|
+
if (notLocalCount > 0) {
|
|
857
|
+
console.log(
|
|
858
|
+
chalk.dim(
|
|
859
|
+
` ${notLocalCount} setting(s) inherited from global config only.`
|
|
860
|
+
)
|
|
861
|
+
);
|
|
862
|
+
console.log();
|
|
863
|
+
}
|
|
864
|
+
log.success("All settings configured!");
|
|
865
|
+
console.log();
|
|
866
|
+
} else {
|
|
867
|
+
const parts = [];
|
|
868
|
+
if (missingFolders > 0) {
|
|
869
|
+
parts.push(`${missingFolders} folder(s) missing`);
|
|
870
|
+
}
|
|
871
|
+
if (notConfiguredCount > 0) {
|
|
872
|
+
parts.push(`${notConfiguredCount} setting(s) not configured`);
|
|
873
|
+
}
|
|
874
|
+
if (notLocalCount > 0) {
|
|
875
|
+
parts.push(`${notLocalCount} not set locally`);
|
|
876
|
+
}
|
|
877
|
+
if (parts.length > 0) {
|
|
878
|
+
console.log(` ${chalk.yellow("\u26A0")} ${parts.join(" \xB7 ")}`);
|
|
879
|
+
console.log();
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
await initMenu(gitAvailable);
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
693
885
|
if (allDone) {
|
|
694
886
|
if (notLocalCount > 0) {
|
|
695
887
|
console.log(
|
|
@@ -755,20 +947,13 @@ async function initCommand() {
|
|
|
755
947
|
await stepShellCompletion();
|
|
756
948
|
await stepStatusline();
|
|
757
949
|
await stepMemory(docsDir);
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
printStepHeader(
|
|
766
|
-
`Configure Claude permissions ${chalk.dim("[skipped]")}`,
|
|
767
|
-
"Grants Claude read/write access to your external learn folder without repeated prompts."
|
|
768
|
-
);
|
|
769
|
-
log.dim(
|
|
770
|
-
"Skipped \u2014 learn directory is inside the project. Run `cf permission` for other permissions."
|
|
771
|
-
);
|
|
950
|
+
printStepHeader(
|
|
951
|
+
"Configure Claude permissions",
|
|
952
|
+
"Grants Coding Friend skills/hooks the permissions they need, so you get fewer prompts."
|
|
953
|
+
);
|
|
954
|
+
await stepClaudePermissions(isExternal ? outputDir : null, autoCommit);
|
|
955
|
+
if (!existsSync(localConfigPath())) {
|
|
956
|
+
writeJson(localConfigPath(), {});
|
|
772
957
|
}
|
|
773
958
|
console.log();
|
|
774
959
|
log.congrats("Setup complete!");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getLatestVersion,
|
|
3
3
|
semverCompare
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-A427XMWE.js";
|
|
5
5
|
import {
|
|
6
6
|
enableMarketplaceAutoUpdate,
|
|
7
7
|
isMarketplaceRegistered,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import "./chunk-POC2WHU2.js";
|
|
14
14
|
import {
|
|
15
15
|
ensureShellCompletion
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-VUAUAO2R.js";
|
|
17
17
|
import {
|
|
18
18
|
resolveScope
|
|
19
19
|
} from "./chunk-C5LYVVEI.js";
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
} from "./chunk-W5CD7WTX.js";
|
|
16
16
|
|
|
17
17
|
// src/commands/memory.ts
|
|
18
|
-
import { createHash } from "crypto";
|
|
19
18
|
import { existsSync, readdirSync, statSync, rmSync } from "fs";
|
|
20
19
|
import { join, resolve, sep } from "path";
|
|
21
20
|
import { homedir } from "os";
|
|
@@ -87,7 +86,7 @@ async function memoryStatusCommand() {
|
|
|
87
86
|
if (running && daemonInfo) {
|
|
88
87
|
const uptime = (Date.now() - daemonInfo.startedAt) / 1e3;
|
|
89
88
|
log.info(
|
|
90
|
-
`Daemon: ${chalk.green("running")} (PID ${daemonInfo.pid}, uptime ${formatUptime(uptime)}) ${chalk.dim('Turn it off by "cf memory stop"')}`
|
|
89
|
+
`Daemon: ${chalk.green("running")} (PID ${daemonInfo.pid}, uptime ${formatUptime(uptime)}) ${chalk.dim('Turn it off by "cf memory stop-daemon"')}`
|
|
91
90
|
);
|
|
92
91
|
} else if (sqliteAvailable) {
|
|
93
92
|
log.info(
|
|
@@ -95,7 +94,7 @@ async function memoryStatusCommand() {
|
|
|
95
94
|
);
|
|
96
95
|
} else {
|
|
97
96
|
log.info(
|
|
98
|
-
`Daemon: ${chalk.dim("stopped")} ${chalk.dim('(run "cf memory start" for Tier 2 search)')}`
|
|
97
|
+
`Daemon: ${chalk.dim("stopped")} ${chalk.dim('(run "cf memory start-daemon" for Tier 2 search)')}`
|
|
99
98
|
);
|
|
100
99
|
}
|
|
101
100
|
if (sqliteAvailable) {
|
|
@@ -204,7 +203,7 @@ async function memoryListCommand(opts) {
|
|
|
204
203
|
console.log(result);
|
|
205
204
|
}
|
|
206
205
|
}
|
|
207
|
-
async function
|
|
206
|
+
async function memoryStartDaemonCommand() {
|
|
208
207
|
const memoryDir = getMemoryDir();
|
|
209
208
|
const mcpDir = getLibPath("cf-memory");
|
|
210
209
|
ensureBuilt(mcpDir);
|
|
@@ -226,7 +225,7 @@ async function memoryStartCommand() {
|
|
|
226
225
|
process.exit(1);
|
|
227
226
|
}
|
|
228
227
|
}
|
|
229
|
-
async function
|
|
228
|
+
async function memoryStopDaemonCommand() {
|
|
230
229
|
const mcpDir = getLibPath("cf-memory");
|
|
231
230
|
ensureBuilt(mcpDir);
|
|
232
231
|
const { stopDaemon, isDaemonRunning } = await import(join(mcpDir, "dist/daemon/process.js"));
|
|
@@ -273,7 +272,7 @@ async function memoryRebuildCommand() {
|
|
|
273
272
|
if (!await isDaemonRunning()) {
|
|
274
273
|
log.info("No SQLite deps and daemon not running. Nothing to rebuild.");
|
|
275
274
|
log.dim("Install Tier 1 deps: cf memory init");
|
|
276
|
-
log.dim("Or start the daemon: cf memory start");
|
|
275
|
+
log.dim("Or start the daemon: cf memory start-daemon");
|
|
277
276
|
return;
|
|
278
277
|
}
|
|
279
278
|
const { DaemonClient } = await import(join(mcpDir, "dist/lib/daemon-client.js"));
|
|
@@ -443,12 +442,13 @@ async function memoryListProjectsCommand() {
|
|
|
443
442
|
return;
|
|
444
443
|
}
|
|
445
444
|
const currentMemoryDir = resolve(getMemoryDir());
|
|
446
|
-
const
|
|
445
|
+
const { projectId } = await import(join(mcpDir, "dist/backends/sqlite/index.js"));
|
|
446
|
+
const currentProjectId = projectId(currentMemoryDir);
|
|
447
447
|
log.step(`Scanning ${dirs.length} project(s)...
|
|
448
448
|
`);
|
|
449
449
|
const projects = [];
|
|
450
450
|
for (const id of dirs) {
|
|
451
|
-
const knownDir = id ===
|
|
451
|
+
const knownDir = id === currentProjectId ? currentMemoryDir : void 0;
|
|
452
452
|
projects.push(getProjectInfo(join(baseDir, id), id, mcpDir, knownDir));
|
|
453
453
|
}
|
|
454
454
|
projects.sort((a, b) => b.size - a.size);
|
|
@@ -643,7 +643,7 @@ export {
|
|
|
643
643
|
memoryRebuildCommand,
|
|
644
644
|
memoryRmCommand,
|
|
645
645
|
memorySearchCommand,
|
|
646
|
-
|
|
646
|
+
memoryStartDaemonCommand,
|
|
647
647
|
memoryStatusCommand,
|
|
648
|
-
|
|
648
|
+
memoryStopDaemonCommand
|
|
649
649
|
};
|