coding-friend-cli 1.9.1 → 1.11.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 +19 -7
- package/dist/{chunk-7N64TDZ6.js → chunk-4JL3SR5V.js} +64 -2
- package/dist/{chunk-HFLBFX6J.js → chunk-E2XX5MIM.js} +1 -1
- package/dist/chunk-EL6BAAKL.js +198 -0
- package/dist/{chunk-QQ5SVZET.js → chunk-GJLNN6X5.js} +48 -2
- package/dist/{chunk-BPLN4LDL.js → chunk-HYLS67T7.js} +1 -1
- package/dist/{chunk-VYMXERKM.js → chunk-RN5UYDIT.js} +22 -6
- package/dist/{chunk-FYHHNX7K.js → chunk-XIZJ64JK.js} +1 -1
- package/dist/{chunk-TPRZHSFS.js → chunk-ZS7BLEYT.js} +31 -27
- package/dist/{config-VAML7F7K.js → config-GYJGDXGV.js} +10 -10
- package/dist/{dev-2GBY3GKC.js → dev-GFFXRZJC.js} +4 -4
- package/dist/{host-LOG5RPZ7.js → host-SWIWQRRL.js} +2 -2
- package/dist/index.js +25 -21
- package/dist/{init-CIEDOFNC.js → init-QRUDHFME.js} +42 -32
- package/dist/{install-D4NW3OAA.js → install-FQTMSBGK.js} +32 -11
- package/dist/{mcp-ORMYETXQ.js → mcp-H3K67N2P.js} +2 -2
- package/dist/permission-DIJMCOZX.js +168 -0
- package/dist/postinstall.js +1 -1
- package/dist/{session-74F7L5LV.js → session-2APBPDZF.js} +4 -2
- package/dist/{statusline-5HWRTSVL.js → statusline-H2WUKOJ6.js} +2 -2
- package/dist/{uninstall-SOHU5WGK.js → uninstall-A2LJR2OD.js} +50 -6
- package/dist/{update-LA4B3LN4.js → update-RJSWHCCR.js} +5 -4
- package/package.json +1 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isMarketplaceRegistered,
|
|
3
3
|
isPluginInstalled
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-E2XX5MIM.js";
|
|
5
5
|
import {
|
|
6
6
|
ensureStatusline
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-HYLS67T7.js";
|
|
8
8
|
import {
|
|
9
9
|
ensureShellCompletion
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-4JL3SR5V.js";
|
|
11
11
|
import "./chunk-PGLUEN7D.js";
|
|
12
12
|
import {
|
|
13
13
|
commandExists,
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
pluginCachePath,
|
|
20
20
|
readJson,
|
|
21
21
|
writeJson
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-ZS7BLEYT.js";
|
|
23
23
|
import {
|
|
24
24
|
log
|
|
25
25
|
} from "./chunk-W5CD7WTX.js";
|
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
} from "./chunk-RZRT7NGT.js";
|
|
4
4
|
import {
|
|
5
5
|
resolveDocsDir
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-XIZJ64JK.js";
|
|
7
7
|
import "./chunk-PGLUEN7D.js";
|
|
8
8
|
import {
|
|
9
9
|
run,
|
|
10
10
|
streamExec
|
|
11
11
|
} from "./chunk-UFGNO6CW.js";
|
|
12
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-ZS7BLEYT.js";
|
|
13
13
|
import {
|
|
14
14
|
log
|
|
15
15
|
} from "./chunk-W5CD7WTX.js";
|
package/dist/index.js
CHANGED
|
@@ -13,36 +13,40 @@ var program = new Command();
|
|
|
13
13
|
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
|
-
program.command("install").description("Install the Coding Friend plugin into Claude Code").action(async () => {
|
|
17
|
-
const { installCommand } = await import("./install-
|
|
18
|
-
await installCommand();
|
|
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-FQTMSBGK.js");
|
|
18
|
+
await installCommand(opts);
|
|
19
19
|
});
|
|
20
|
-
program.command("uninstall").description("Uninstall the Coding Friend plugin from Claude Code").action(async () => {
|
|
21
|
-
const { uninstallCommand } = await import("./uninstall-
|
|
22
|
-
await uninstallCommand();
|
|
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-A2LJR2OD.js");
|
|
22
|
+
await uninstallCommand(opts);
|
|
23
23
|
});
|
|
24
24
|
program.command("init").description("Initialize coding-friend in current project").action(async () => {
|
|
25
|
-
const { initCommand } = await import("./init-
|
|
25
|
+
const { initCommand } = await import("./init-QRUDHFME.js");
|
|
26
26
|
await initCommand();
|
|
27
27
|
});
|
|
28
28
|
program.command("config").description("Manage Coding Friend configuration").action(async () => {
|
|
29
|
-
const { configCommand } = await import("./config-
|
|
29
|
+
const { configCommand } = await import("./config-GYJGDXGV.js");
|
|
30
30
|
await configCommand();
|
|
31
31
|
});
|
|
32
32
|
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) => {
|
|
33
|
-
const { hostCommand } = await import("./host-
|
|
33
|
+
const { hostCommand } = await import("./host-SWIWQRRL.js");
|
|
34
34
|
await hostCommand(path, opts);
|
|
35
35
|
});
|
|
36
36
|
program.command("mcp").description("Setup MCP server for learning docs").argument("[path]", "path to docs folder").action(async (path) => {
|
|
37
|
-
const { mcpCommand } = await import("./mcp-
|
|
37
|
+
const { mcpCommand } = await import("./mcp-H3K67N2P.js");
|
|
38
38
|
await mcpCommand(path);
|
|
39
39
|
});
|
|
40
|
+
program.command("permission").description("Manage Claude Code permission rules for Coding Friend").option("--all", "Apply all recommended permissions without prompts").action(async (opts) => {
|
|
41
|
+
const { permissionCommand } = await import("./permission-DIJMCOZX.js");
|
|
42
|
+
await permissionCommand(opts);
|
|
43
|
+
});
|
|
40
44
|
program.command("statusline").description("Setup coding-friend statusline in Claude Code").action(async () => {
|
|
41
|
-
const { statuslineCommand } = await import("./statusline-
|
|
45
|
+
const { statuslineCommand } = await import("./statusline-H2WUKOJ6.js");
|
|
42
46
|
await statuslineCommand();
|
|
43
47
|
});
|
|
44
|
-
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").action(async (opts) => {
|
|
45
|
-
const { updateCommand } = await import("./update-
|
|
48
|
+
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) => {
|
|
49
|
+
const { updateCommand } = await import("./update-RJSWHCCR.js");
|
|
46
50
|
await updateCommand(opts);
|
|
47
51
|
});
|
|
48
52
|
var session = program.command("session").description("Save and load Claude Code sessions across machines");
|
|
@@ -57,11 +61,11 @@ session.command("save").description("Save current Claude Code session to sync fo
|
|
|
57
61
|
"-s, --session-id <id>",
|
|
58
62
|
"session UUID to save (default: auto-detect newest)"
|
|
59
63
|
).option("-l, --label <label>", "label for this session").action(async (opts) => {
|
|
60
|
-
const { sessionSaveCommand } = await import("./session-
|
|
64
|
+
const { sessionSaveCommand } = await import("./session-2APBPDZF.js");
|
|
61
65
|
await sessionSaveCommand(opts);
|
|
62
66
|
});
|
|
63
67
|
session.command("load").description("Load a saved session from sync folder").action(async () => {
|
|
64
|
-
const { sessionLoadCommand } = await import("./session-
|
|
68
|
+
const { sessionLoadCommand } = await import("./session-2APBPDZF.js");
|
|
65
69
|
await sessionLoadCommand();
|
|
66
70
|
});
|
|
67
71
|
var dev = program.command("dev").description("Development mode commands");
|
|
@@ -77,35 +81,35 @@ Dev subcommands:
|
|
|
77
81
|
dev update [path] Update local dev plugin to latest version`
|
|
78
82
|
);
|
|
79
83
|
dev.command("on").description("Switch to local plugin source").argument("[path]", "path to local coding-friend repo (default: cwd)").action(async (path) => {
|
|
80
|
-
const { devOnCommand } = await import("./dev-
|
|
84
|
+
const { devOnCommand } = await import("./dev-GFFXRZJC.js");
|
|
81
85
|
await devOnCommand(path);
|
|
82
86
|
});
|
|
83
87
|
dev.command("off").description("Switch back to remote marketplace").action(async () => {
|
|
84
|
-
const { devOffCommand } = await import("./dev-
|
|
88
|
+
const { devOffCommand } = await import("./dev-GFFXRZJC.js");
|
|
85
89
|
await devOffCommand();
|
|
86
90
|
});
|
|
87
91
|
dev.command("status").description("Show current dev mode").action(async () => {
|
|
88
|
-
const { devStatusCommand } = await import("./dev-
|
|
92
|
+
const { devStatusCommand } = await import("./dev-GFFXRZJC.js");
|
|
89
93
|
await devStatusCommand();
|
|
90
94
|
});
|
|
91
95
|
dev.command("sync").description(
|
|
92
96
|
"Copy local source files to plugin cache (no version bump needed)"
|
|
93
97
|
).action(async () => {
|
|
94
|
-
const { devSyncCommand } = await import("./dev-
|
|
98
|
+
const { devSyncCommand } = await import("./dev-GFFXRZJC.js");
|
|
95
99
|
await devSyncCommand();
|
|
96
100
|
});
|
|
97
101
|
dev.command("restart").description("Reinstall local dev plugin (off + on)").argument(
|
|
98
102
|
"[path]",
|
|
99
103
|
"path to local coding-friend repo (default: saved path or cwd)"
|
|
100
104
|
).action(async (path) => {
|
|
101
|
-
const { devRestartCommand } = await import("./dev-
|
|
105
|
+
const { devRestartCommand } = await import("./dev-GFFXRZJC.js");
|
|
102
106
|
await devRestartCommand(path);
|
|
103
107
|
});
|
|
104
108
|
dev.command("update").description("Update local dev plugin to latest version (off + on)").argument(
|
|
105
109
|
"[path]",
|
|
106
110
|
"path to local coding-friend repo (default: saved path or cwd)"
|
|
107
111
|
).action(async (path) => {
|
|
108
|
-
const { devUpdateCommand } = await import("./dev-
|
|
112
|
+
const { devUpdateCommand } = await import("./dev-GFFXRZJC.js");
|
|
109
113
|
await devUpdateCommand(path);
|
|
110
114
|
});
|
|
111
115
|
program.parse();
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyPermissions,
|
|
3
|
+
buildLearnDirRules,
|
|
4
|
+
getExistingRules,
|
|
5
|
+
getMissingRules
|
|
6
|
+
} from "./chunk-EL6BAAKL.js";
|
|
7
|
+
import {
|
|
8
|
+
findStatuslineHookPath,
|
|
9
|
+
isStatuslineConfigured,
|
|
10
|
+
saveStatuslineConfig,
|
|
11
|
+
selectStatuslineComponents,
|
|
12
|
+
writeStatuslineSettings
|
|
13
|
+
} from "./chunk-HYLS67T7.js";
|
|
1
14
|
import {
|
|
2
15
|
BACK,
|
|
3
16
|
applyDocsDirChange,
|
|
@@ -7,18 +20,11 @@ import {
|
|
|
7
20
|
getScopeLabel,
|
|
8
21
|
injectBackChoice,
|
|
9
22
|
showConfigHint
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import {
|
|
12
|
-
findStatuslineHookPath,
|
|
13
|
-
isStatuslineConfigured,
|
|
14
|
-
saveStatuslineConfig,
|
|
15
|
-
selectStatuslineComponents,
|
|
16
|
-
writeStatuslineSettings
|
|
17
|
-
} from "./chunk-BPLN4LDL.js";
|
|
23
|
+
} from "./chunk-GJLNN6X5.js";
|
|
18
24
|
import {
|
|
19
25
|
ensureShellCompletion,
|
|
20
26
|
hasShellCompletion
|
|
21
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-4JL3SR5V.js";
|
|
22
28
|
import {
|
|
23
29
|
DEFAULT_CONFIG
|
|
24
30
|
} from "./chunk-PGLUEN7D.js";
|
|
@@ -31,9 +37,8 @@ import {
|
|
|
31
37
|
localConfigPath,
|
|
32
38
|
mergeJson,
|
|
33
39
|
readJson,
|
|
34
|
-
resolvePath
|
|
35
|
-
|
|
36
|
-
} from "./chunk-TPRZHSFS.js";
|
|
40
|
+
resolvePath
|
|
41
|
+
} from "./chunk-ZS7BLEYT.js";
|
|
37
42
|
import {
|
|
38
43
|
log
|
|
39
44
|
} from "./chunk-W5CD7WTX.js";
|
|
@@ -411,35 +416,23 @@ async function stepStatusline() {
|
|
|
411
416
|
async function stepClaudePermissions(outputDir, autoCommit) {
|
|
412
417
|
const resolved = resolvePath(outputDir);
|
|
413
418
|
const homePath = resolved.startsWith(homedir()) ? resolved.replace(homedir(), "~") : resolved;
|
|
414
|
-
const rules = [
|
|
415
|
-
`Read(${homePath}/**)`,
|
|
416
|
-
`Edit(${homePath}/**)`,
|
|
417
|
-
`Write(${homePath}/**)`
|
|
418
|
-
];
|
|
419
|
-
if (autoCommit) {
|
|
420
|
-
rules.push(`Bash(cd ${homePath} && git add:*)`);
|
|
421
|
-
rules.push(`Bash(cd ${homePath} && git commit:*)`);
|
|
422
|
-
}
|
|
423
419
|
const settingsPath = claudeSettingsPath();
|
|
424
|
-
const
|
|
425
|
-
if (!
|
|
420
|
+
const existing = getExistingRules(settingsPath);
|
|
421
|
+
if (existing.length === 0 && !readJson(settingsPath)) {
|
|
426
422
|
log.warn(
|
|
427
423
|
"~/.claude/settings.json not found. Create it via Claude Code settings first."
|
|
428
424
|
);
|
|
429
425
|
return;
|
|
430
426
|
}
|
|
431
|
-
const
|
|
432
|
-
const
|
|
433
|
-
const missing = rules.filter(
|
|
434
|
-
(r) => !existing.some((e) => e === r || e.includes(homePath))
|
|
435
|
-
);
|
|
427
|
+
const learnRules = buildLearnDirRules(homePath, autoCommit);
|
|
428
|
+
const missing = getMissingRules(existing, learnRules);
|
|
436
429
|
if (missing.length === 0) {
|
|
437
430
|
log.dim("All permission rules already configured.");
|
|
438
431
|
return;
|
|
439
432
|
}
|
|
440
433
|
console.log("\nTo avoid repeated permission prompts, add these rules:");
|
|
441
434
|
for (const r of missing) {
|
|
442
|
-
console.log(` ${r}`);
|
|
435
|
+
console.log(` ${r.rule}`);
|
|
443
436
|
}
|
|
444
437
|
const ok = await confirm({
|
|
445
438
|
message: "Add these to ~/.claude/settings.json?",
|
|
@@ -449,10 +442,13 @@ async function stepClaudePermissions(outputDir, autoCommit) {
|
|
|
449
442
|
log.dim("Skipped. You'll get prompted each time.");
|
|
450
443
|
return;
|
|
451
444
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
445
|
+
applyPermissions(
|
|
446
|
+
settingsPath,
|
|
447
|
+
missing.map((r) => r.rule),
|
|
448
|
+
[]
|
|
449
|
+
);
|
|
455
450
|
log.success(`Added ${missing.length} permission rules.`);
|
|
451
|
+
log.dim("For all plugin permissions, run: `cf permission`");
|
|
456
452
|
}
|
|
457
453
|
async function initCommand() {
|
|
458
454
|
_stepIndex = 0;
|
|
@@ -485,6 +481,12 @@ async function initCommand() {
|
|
|
485
481
|
const docsDir = getDocsDir(updatedGlobal, updatedLocal);
|
|
486
482
|
if (gitAvailable) {
|
|
487
483
|
await stepGitignore(docsDir);
|
|
484
|
+
} else {
|
|
485
|
+
printStepHeader(
|
|
486
|
+
`Configure .gitignore ${chalk.dim("[skipped]")}`,
|
|
487
|
+
"Keeps AI-generated docs and config out of your git history."
|
|
488
|
+
);
|
|
489
|
+
log.dim("Skipped \u2014 not inside a git repo.");
|
|
488
490
|
}
|
|
489
491
|
await stepDocsLanguage(globalCfg, localCfg);
|
|
490
492
|
const { outputDir, autoCommit, isExternal } = await stepLearnConfig(
|
|
@@ -500,6 +502,14 @@ async function initCommand() {
|
|
|
500
502
|
"Grants Claude read/write access to your external learn folder without repeated prompts."
|
|
501
503
|
);
|
|
502
504
|
await stepClaudePermissions(outputDir, autoCommit);
|
|
505
|
+
} else {
|
|
506
|
+
printStepHeader(
|
|
507
|
+
`Configure Claude permissions ${chalk.dim("[skipped]")}`,
|
|
508
|
+
"Grants Claude read/write access to your external learn folder without repeated prompts."
|
|
509
|
+
);
|
|
510
|
+
log.dim(
|
|
511
|
+
"Skipped \u2014 learn directory is inside the project. Run `cf permission` for other permissions."
|
|
512
|
+
);
|
|
503
513
|
}
|
|
504
514
|
console.log();
|
|
505
515
|
log.congrats("Setup complete!");
|
|
@@ -1,27 +1,35 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getLatestVersion,
|
|
3
3
|
semverCompare
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RN5UYDIT.js";
|
|
5
5
|
import {
|
|
6
6
|
isMarketplaceRegistered
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-E2XX5MIM.js";
|
|
8
8
|
import {
|
|
9
9
|
getInstalledVersion
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import
|
|
10
|
+
} from "./chunk-HYLS67T7.js";
|
|
11
|
+
import {
|
|
12
|
+
resolveScope
|
|
13
|
+
} from "./chunk-GJLNN6X5.js";
|
|
14
|
+
import {
|
|
15
|
+
ensureShellCompletion
|
|
16
|
+
} from "./chunk-4JL3SR5V.js";
|
|
12
17
|
import "./chunk-PGLUEN7D.js";
|
|
13
18
|
import {
|
|
14
19
|
commandExists,
|
|
15
20
|
run
|
|
16
21
|
} from "./chunk-UFGNO6CW.js";
|
|
17
|
-
import
|
|
22
|
+
import {
|
|
23
|
+
devStatePath
|
|
24
|
+
} from "./chunk-ZS7BLEYT.js";
|
|
18
25
|
import {
|
|
19
26
|
log
|
|
20
27
|
} from "./chunk-W5CD7WTX.js";
|
|
21
28
|
|
|
22
29
|
// src/commands/install.ts
|
|
30
|
+
import { existsSync } from "fs";
|
|
23
31
|
import chalk from "chalk";
|
|
24
|
-
async function installCommand() {
|
|
32
|
+
async function installCommand(opts = {}) {
|
|
25
33
|
console.log("=== \u{1F33F} Coding Friend Install \u{1F33F} ===");
|
|
26
34
|
console.log();
|
|
27
35
|
if (!commandExists("claude")) {
|
|
@@ -30,6 +38,14 @@ async function installCommand() {
|
|
|
30
38
|
);
|
|
31
39
|
process.exit(1);
|
|
32
40
|
}
|
|
41
|
+
if (existsSync(devStatePath())) {
|
|
42
|
+
log.warn("Dev mode is currently active.");
|
|
43
|
+
log.dim(
|
|
44
|
+
`Run ${chalk.bold("cf dev off")} first, then install. Or use ${chalk.bold("cf dev sync")} to update the dev plugin.`
|
|
45
|
+
);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const scope = await resolveScope(opts);
|
|
33
49
|
if (isMarketplaceRegistered()) {
|
|
34
50
|
log.success("Marketplace already registered.");
|
|
35
51
|
} else {
|
|
@@ -49,12 +65,14 @@ async function installCommand() {
|
|
|
49
65
|
log.success("Marketplace added.");
|
|
50
66
|
}
|
|
51
67
|
const installedVersion = getInstalledVersion();
|
|
52
|
-
if (!installedVersion) {
|
|
53
|
-
log.step(
|
|
68
|
+
if (!installedVersion || scope !== "user") {
|
|
69
|
+
log.step(`Installing plugin (${chalk.cyan(scope)} scope)...`);
|
|
54
70
|
const result = run("claude", [
|
|
55
71
|
"plugin",
|
|
56
72
|
"install",
|
|
57
|
-
"coding-friend@coding-friend-marketplace"
|
|
73
|
+
"coding-friend@coding-friend-marketplace",
|
|
74
|
+
"--scope",
|
|
75
|
+
scope
|
|
58
76
|
]);
|
|
59
77
|
if (result === null) {
|
|
60
78
|
log.error(
|
|
@@ -81,12 +99,15 @@ async function installCommand() {
|
|
|
81
99
|
log.dim("Could not check for updates (no network or GitHub rate limit).");
|
|
82
100
|
}
|
|
83
101
|
}
|
|
102
|
+
ensureShellCompletion({ silent: false });
|
|
84
103
|
console.log();
|
|
85
104
|
log.info("Next steps:");
|
|
86
|
-
log
|
|
105
|
+
console.log(
|
|
87
106
|
` ${chalk.cyan("cf init")} Initialize workspace (docs folders, config)`
|
|
88
107
|
);
|
|
89
|
-
log
|
|
108
|
+
console.log(
|
|
109
|
+
` ${chalk.cyan("cf statusline")} Setup statusline in Claude Code to show more real-time info`
|
|
110
|
+
);
|
|
90
111
|
console.log();
|
|
91
112
|
log.dim("Restart Claude Code (or start a new session) to use the plugin.");
|
|
92
113
|
}
|
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
} from "./chunk-RZRT7NGT.js";
|
|
4
4
|
import {
|
|
5
5
|
resolveDocsDir
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-XIZJ64JK.js";
|
|
7
7
|
import "./chunk-PGLUEN7D.js";
|
|
8
8
|
import {
|
|
9
9
|
run
|
|
10
10
|
} from "./chunk-UFGNO6CW.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-ZS7BLEYT.js";
|
|
12
12
|
import {
|
|
13
13
|
log
|
|
14
14
|
} from "./chunk-W5CD7WTX.js";
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PERMISSION_RULES,
|
|
3
|
+
applyPermissions,
|
|
4
|
+
getExistingRules,
|
|
5
|
+
groupByCategory
|
|
6
|
+
} from "./chunk-EL6BAAKL.js";
|
|
7
|
+
import {
|
|
8
|
+
claudeLocalSettingsPath
|
|
9
|
+
} from "./chunk-ZS7BLEYT.js";
|
|
10
|
+
import {
|
|
11
|
+
log
|
|
12
|
+
} from "./chunk-W5CD7WTX.js";
|
|
13
|
+
|
|
14
|
+
// src/commands/permission.ts
|
|
15
|
+
import { checkbox, confirm, select } from "@inquirer/prompts";
|
|
16
|
+
import chalk from "chalk";
|
|
17
|
+
import { homedir } from "os";
|
|
18
|
+
var TAG_COLORS = {
|
|
19
|
+
"[read-only]": chalk.green,
|
|
20
|
+
"[modify]": chalk.yellow,
|
|
21
|
+
"[write]": chalk.yellow,
|
|
22
|
+
"[remote]": chalk.red,
|
|
23
|
+
"[execute]": chalk.magenta,
|
|
24
|
+
"[network]": chalk.magenta
|
|
25
|
+
};
|
|
26
|
+
function colorDescription(desc) {
|
|
27
|
+
const parts = desc.split(" \xB7 ");
|
|
28
|
+
const main = parts[0];
|
|
29
|
+
const usedBy = parts.length > 1 ? chalk.dim("\xB7 " + parts.slice(1).join(" \xB7 ")) : "";
|
|
30
|
+
const tagMatch = main.match(/^(\[[^\]]+\])\s*(.*)/);
|
|
31
|
+
if (tagMatch) {
|
|
32
|
+
const colorFn = TAG_COLORS[tagMatch[1]] ?? chalk.cyan;
|
|
33
|
+
return `${colorFn(tagMatch[1])} ${tagMatch[2]} ${usedBy}`;
|
|
34
|
+
}
|
|
35
|
+
return `${main} ${usedBy}`;
|
|
36
|
+
}
|
|
37
|
+
async function interactiveFlow(allRules, existing) {
|
|
38
|
+
const groups = groupByCategory(allRules);
|
|
39
|
+
const allRuleStrings = allRules.map((r) => r.rule);
|
|
40
|
+
const managedExisting = existing.filter((r) => allRuleStrings.includes(r));
|
|
41
|
+
const enabled = new Set(managedExisting);
|
|
42
|
+
console.log(
|
|
43
|
+
chalk.dim(
|
|
44
|
+
"Full reference: https://cf.dinhanhthi.com/docs/reference/permissions/"
|
|
45
|
+
)
|
|
46
|
+
);
|
|
47
|
+
console.log();
|
|
48
|
+
let browsing = true;
|
|
49
|
+
while (browsing) {
|
|
50
|
+
const categoryChoices = [];
|
|
51
|
+
for (const [category, rules] of groups) {
|
|
52
|
+
const configured = rules.filter((r) => enabled.has(r.rule)).length;
|
|
53
|
+
const total = rules.length;
|
|
54
|
+
let suffix;
|
|
55
|
+
if (configured === total)
|
|
56
|
+
suffix = chalk.green(`${configured}/${total} \u2713`);
|
|
57
|
+
else if (configured > 0) suffix = chalk.yellow(`${configured}/${total}`);
|
|
58
|
+
else suffix = chalk.dim(`0/${total}`);
|
|
59
|
+
categoryChoices.push({
|
|
60
|
+
name: `${category} (${suffix})`,
|
|
61
|
+
value: category
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
categoryChoices.push({
|
|
65
|
+
name: chalk.green("\u2192 Apply changes"),
|
|
66
|
+
value: "__apply__"
|
|
67
|
+
});
|
|
68
|
+
const chosen = await select({
|
|
69
|
+
message: "Select a category to configure:",
|
|
70
|
+
choices: categoryChoices
|
|
71
|
+
});
|
|
72
|
+
if (chosen === "__apply__") {
|
|
73
|
+
browsing = false;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const categoryRules = groups.get(chosen);
|
|
77
|
+
const selected = await checkbox({
|
|
78
|
+
message: `${chosen} permissions:`,
|
|
79
|
+
choices: categoryRules.map((rule) => ({
|
|
80
|
+
name: rule.rule,
|
|
81
|
+
value: rule.rule,
|
|
82
|
+
checked: enabled.has(rule.rule),
|
|
83
|
+
description: colorDescription(rule.description)
|
|
84
|
+
}))
|
|
85
|
+
});
|
|
86
|
+
for (const rule of categoryRules) {
|
|
87
|
+
if (selected.includes(rule.rule)) {
|
|
88
|
+
enabled.add(rule.rule);
|
|
89
|
+
} else {
|
|
90
|
+
enabled.delete(rule.rule);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const toAdd = [...enabled].filter((r) => !existing.includes(r));
|
|
95
|
+
const toRemove = managedExisting.filter((r) => !enabled.has(r));
|
|
96
|
+
return { toAdd, toRemove };
|
|
97
|
+
}
|
|
98
|
+
async function permissionCommand(opts) {
|
|
99
|
+
console.log("=== \u{1F33F} Coding Friend Permissions \u{1F33F} ===");
|
|
100
|
+
console.log();
|
|
101
|
+
const settingsPath = claudeLocalSettingsPath();
|
|
102
|
+
const settingsLabel = settingsPath.replace(homedir(), "~");
|
|
103
|
+
const existing = getExistingRules(settingsPath);
|
|
104
|
+
const allRules = PERMISSION_RULES;
|
|
105
|
+
const allRuleStrings = allRules.map((r) => r.rule);
|
|
106
|
+
const managedExisting = existing.filter((r) => allRuleStrings.includes(r));
|
|
107
|
+
const unmanagedExisting = existing.filter((r) => !allRuleStrings.includes(r));
|
|
108
|
+
log.dim(`Settings: ${settingsLabel} (project-local)`);
|
|
109
|
+
log.dim(
|
|
110
|
+
`Current: ${managedExisting.length}/${allRules.length} Coding Friend rules configured`
|
|
111
|
+
);
|
|
112
|
+
if (unmanagedExisting.length > 0) {
|
|
113
|
+
log.dim(
|
|
114
|
+
`(${unmanagedExisting.length} other permission rules will not be touched)`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
console.log();
|
|
118
|
+
if (opts.all) {
|
|
119
|
+
const recommended = allRules.filter((r) => r.recommended);
|
|
120
|
+
const toAdd2 = recommended.map((r) => r.rule).filter((r) => !existing.includes(r));
|
|
121
|
+
if (toAdd2.length === 0) {
|
|
122
|
+
log.success("All recommended permissions already configured.");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
console.log("Adding recommended permissions:");
|
|
126
|
+
for (const r of toAdd2) {
|
|
127
|
+
console.log(` ${chalk.green("+")} ${r}`);
|
|
128
|
+
}
|
|
129
|
+
console.log();
|
|
130
|
+
applyPermissions(settingsPath, toAdd2, []);
|
|
131
|
+
log.success(`Added ${toAdd2.length} permission rules.`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const { toAdd, toRemove } = await interactiveFlow(allRules, existing);
|
|
135
|
+
if (toAdd.length === 0 && toRemove.length === 0) {
|
|
136
|
+
log.dim("No changes.");
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
console.log();
|
|
140
|
+
if (toAdd.length > 0) {
|
|
141
|
+
console.log(chalk.green(`Adding ${toAdd.length} rules:`));
|
|
142
|
+
for (const r of toAdd) {
|
|
143
|
+
console.log(` ${chalk.green("+")} ${r}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (toRemove.length > 0) {
|
|
147
|
+
console.log(chalk.red(`Removing ${toRemove.length} rules:`));
|
|
148
|
+
for (const r of toRemove) {
|
|
149
|
+
console.log(` ${chalk.red("-")} ${r}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
console.log();
|
|
153
|
+
const ok = await confirm({
|
|
154
|
+
message: `Apply changes to ${settingsLabel}?`,
|
|
155
|
+
default: true
|
|
156
|
+
});
|
|
157
|
+
if (!ok) {
|
|
158
|
+
log.dim("Skipped.");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
applyPermissions(settingsPath, toAdd, toRemove);
|
|
162
|
+
log.success(
|
|
163
|
+
`Done \u2014 added ${toAdd.length}, removed ${toRemove.length} rules.`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
export {
|
|
167
|
+
permissionCommand
|
|
168
|
+
};
|
package/dist/postinstall.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadConfig
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XIZJ64JK.js";
|
|
4
4
|
import "./chunk-PGLUEN7D.js";
|
|
5
5
|
import {
|
|
6
6
|
claudeSessionDir,
|
|
7
7
|
encodeProjectPath,
|
|
8
8
|
readJson,
|
|
9
|
+
resolvePath,
|
|
9
10
|
writeJson
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-ZS7BLEYT.js";
|
|
11
12
|
import {
|
|
12
13
|
log
|
|
13
14
|
} from "./chunk-W5CD7WTX.js";
|
|
@@ -212,6 +213,7 @@ Remapped to: ${remapped}`
|
|
|
212
213
|
});
|
|
213
214
|
localProjectPath = confirmed.trim() || remapped;
|
|
214
215
|
}
|
|
216
|
+
localProjectPath = resolvePath(localProjectPath);
|
|
215
217
|
loadSession(chosen, localProjectPath, docsDir);
|
|
216
218
|
log.success(`Session "${chosen.label}" loaded.`);
|
|
217
219
|
log.info(`To resume, run:`);
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
saveStatuslineConfig,
|
|
5
5
|
selectStatuslineComponents,
|
|
6
6
|
writeStatuslineSettings
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-HYLS67T7.js";
|
|
8
8
|
import {
|
|
9
9
|
ALL_COMPONENT_IDS
|
|
10
10
|
} from "./chunk-PGLUEN7D.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-ZS7BLEYT.js";
|
|
12
12
|
import {
|
|
13
13
|
log
|
|
14
14
|
} from "./chunk-W5CD7WTX.js";
|