coding-friend-cli 1.17.4 → 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 +5 -2
- package/dist/chunk-7CAIGH2Y.js +365 -0
- package/dist/{chunk-UWQPMVJY.js → chunk-A427XMWE.js} +1 -1
- package/dist/{chunk-DVMWMXDZ.js → chunk-VUAUAO2R.js} +21 -0
- package/dist/{config-HVWEV2K6.js → config-6SBGNTAQ.js} +80 -1
- package/dist/{dev-AZSOM775.js → dev-MC6TGHRT.js} +1 -1
- package/dist/index.js +16 -13
- package/dist/{init-73ECEDU7.js → init-2HLPKYXB.js} +220 -35
- package/dist/{install-USFLRCS5.js → install-3QCRGPTY.js} +2 -2
- package/dist/{permission-L2QQR5PO.js → permission-Z3LOBJ4X.js} +72 -11
- package/dist/postinstall.js +1 -1
- package/dist/{uninstall-QSNKGNHR.js → uninstall-5LRHXFSF.js} +1 -1
- package/dist/{update-PNHTIB6M.js → update-4YUSCBCB.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-56U7US6J.js +0 -198
|
@@ -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";
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
STATIC_RULES,
|
|
3
3
|
applyPermissions,
|
|
4
|
+
cleanupStalePluginRules,
|
|
5
|
+
getAllRules,
|
|
4
6
|
getExistingRules,
|
|
5
|
-
groupByCategory
|
|
6
|
-
|
|
7
|
+
groupByCategory,
|
|
8
|
+
logPluginScriptWarning
|
|
9
|
+
} from "./chunk-7CAIGH2Y.js";
|
|
7
10
|
import {
|
|
8
|
-
claudeLocalSettingsPath
|
|
11
|
+
claudeLocalSettingsPath,
|
|
12
|
+
claudeSettingsPath
|
|
9
13
|
} from "./chunk-RWUTFVRB.js";
|
|
10
14
|
import {
|
|
11
15
|
log
|
|
@@ -34,6 +38,39 @@ function colorDescription(desc) {
|
|
|
34
38
|
}
|
|
35
39
|
return `${main} ${usedBy}`;
|
|
36
40
|
}
|
|
41
|
+
async function resolveSettingsPath(opts) {
|
|
42
|
+
if (opts.user && opts.project) {
|
|
43
|
+
log.error("Cannot use both --user and --project. Pick one.");
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
if (opts.user) {
|
|
47
|
+
const p2 = claudeSettingsPath();
|
|
48
|
+
return { path: p2, label: p2.replace(homedir(), "~"), scope: "user" };
|
|
49
|
+
}
|
|
50
|
+
if (opts.project) {
|
|
51
|
+
const p2 = claudeLocalSettingsPath();
|
|
52
|
+
return { path: p2, label: p2.replace(homedir(), "~"), scope: "project" };
|
|
53
|
+
}
|
|
54
|
+
if (!process.stdin.isTTY) {
|
|
55
|
+
const p2 = claudeLocalSettingsPath();
|
|
56
|
+
return { path: p2, label: p2.replace(homedir(), "~"), scope: "project" };
|
|
57
|
+
}
|
|
58
|
+
const scope = await select({
|
|
59
|
+
message: "Where should permissions be saved?",
|
|
60
|
+
choices: [
|
|
61
|
+
{
|
|
62
|
+
name: "Project \u2014 .claude/settings.local.json (this project only, gitignored)",
|
|
63
|
+
value: "project"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "User \u2014 ~/.claude/settings.json (all projects)",
|
|
67
|
+
value: "user"
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
});
|
|
71
|
+
const p = scope === "user" ? claudeSettingsPath() : claudeLocalSettingsPath();
|
|
72
|
+
return { path: p, label: p.replace(homedir(), "~"), scope };
|
|
73
|
+
}
|
|
37
74
|
async function interactiveFlow(allRules, existing) {
|
|
38
75
|
const groups = groupByCategory(allRules);
|
|
39
76
|
const allRuleStrings = allRules.map((r) => r.rule);
|
|
@@ -98,14 +135,17 @@ async function interactiveFlow(allRules, existing) {
|
|
|
98
135
|
async function permissionCommand(opts) {
|
|
99
136
|
console.log("=== \u{1F33F} Coding Friend Permissions \u{1F33F} ===");
|
|
100
137
|
console.log();
|
|
101
|
-
const
|
|
102
|
-
|
|
138
|
+
const {
|
|
139
|
+
path: settingsPath,
|
|
140
|
+
label: settingsLabel,
|
|
141
|
+
scope
|
|
142
|
+
} = await resolveSettingsPath(opts);
|
|
103
143
|
const existing = getExistingRules(settingsPath);
|
|
104
|
-
const allRules =
|
|
144
|
+
const allRules = getAllRules();
|
|
105
145
|
const allRuleStrings = allRules.map((r) => r.rule);
|
|
106
146
|
const managedExisting = existing.filter((r) => allRuleStrings.includes(r));
|
|
107
147
|
const unmanagedExisting = existing.filter((r) => !allRuleStrings.includes(r));
|
|
108
|
-
log.dim(`Settings: ${settingsLabel} (
|
|
148
|
+
log.dim(`Settings: ${settingsLabel} (${scope})`);
|
|
109
149
|
log.dim(
|
|
110
150
|
`Current: ${managedExisting.length}/${allRules.length} Coding Friend rules configured`
|
|
111
151
|
);
|
|
@@ -122,13 +162,34 @@ async function permissionCommand(opts) {
|
|
|
122
162
|
log.success("All recommended permissions already configured.");
|
|
123
163
|
return;
|
|
124
164
|
}
|
|
125
|
-
|
|
165
|
+
const staticSet = new Set(STATIC_RULES.map((r) => r.rule));
|
|
166
|
+
const staticCount = toAdd2.filter((r) => staticSet.has(r)).length;
|
|
167
|
+
const pluginCount = toAdd2.length - staticCount;
|
|
168
|
+
console.log(
|
|
169
|
+
`Adding ${chalk.bold(toAdd2.length)} recommended permissions${pluginCount > 0 ? ` (${staticCount} static + ${pluginCount} plugin scripts)` : ""}:`
|
|
170
|
+
);
|
|
126
171
|
for (const r of toAdd2) {
|
|
127
172
|
console.log(` ${chalk.green("+")} ${r}`);
|
|
128
173
|
}
|
|
174
|
+
if (pluginCount > 0) {
|
|
175
|
+
console.log();
|
|
176
|
+
logPluginScriptWarning(log, chalk);
|
|
177
|
+
}
|
|
129
178
|
console.log();
|
|
179
|
+
const ok2 = !process.stdin.isTTY || await confirm({
|
|
180
|
+
message: `Apply ${toAdd2.length} permission rules to ${settingsLabel}?`,
|
|
181
|
+
default: true
|
|
182
|
+
});
|
|
183
|
+
if (!ok2) {
|
|
184
|
+
log.dim("Skipped.");
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
130
187
|
applyPermissions(settingsPath, toAdd2, []);
|
|
131
|
-
|
|
188
|
+
const cleaned = cleanupStalePluginRules(settingsPath);
|
|
189
|
+
if (cleaned > 0) {
|
|
190
|
+
log.dim(`Removed ${cleaned} stale old-format plugin rules.`);
|
|
191
|
+
}
|
|
192
|
+
log.success(`Added ${toAdd2.length} permission rules to ${settingsLabel}.`);
|
|
132
193
|
return;
|
|
133
194
|
}
|
|
134
195
|
const { toAdd, toRemove } = await interactiveFlow(allRules, existing);
|
|
@@ -160,7 +221,7 @@ async function permissionCommand(opts) {
|
|
|
160
221
|
}
|
|
161
222
|
applyPermissions(settingsPath, toAdd, toRemove);
|
|
162
223
|
log.success(
|
|
163
|
-
`Done \u2014 added ${toAdd.length}, removed ${toRemove.length} rules.`
|
|
224
|
+
`Done \u2014 added ${toAdd.length}, removed ${toRemove.length} rules in ${settingsLabel}.`
|
|
164
225
|
);
|
|
165
226
|
}
|
|
166
227
|
export {
|
package/dist/postinstall.js
CHANGED
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
getLatestVersion,
|
|
3
3
|
semverCompare,
|
|
4
4
|
updateCommand
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-A427XMWE.js";
|
|
6
6
|
import "./chunk-ORACWEDN.js";
|
|
7
7
|
import "./chunk-POC2WHU2.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-VUAUAO2R.js";
|
|
9
9
|
import "./chunk-C5LYVVEI.js";
|
|
10
10
|
import "./chunk-CYQU33FY.js";
|
|
11
11
|
import "./chunk-RWUTFVRB.js";
|