cc-safe-setup 11.5.0 → 11.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/index.mjs +80 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
**One command to make Claude Code safe for autonomous operation.** [日本語](docs/README.ja.md)
|
|
8
8
|
|
|
9
|
-
8 built-in + 104 examples = **118 hooks**.
|
|
9
|
+
8 built-in + 104 examples = **118 hooks**. 41 CLI commands. 561 tests. 5 languages. [**Hub**](https://yurukusa.github.io/cc-safe-setup/hub.html) · [Cheat Sheet](https://yurukusa.github.io/cc-safe-setup/hooks-cheatsheet.html) · [Builder](https://yurukusa.github.io/cc-safe-setup/builder.html) · [FAQ](https://yurukusa.github.io/cc-safe-setup/faq.html) · [Examples](https://yurukusa.github.io/cc-safe-setup/by-example.html) · [Matrix](https://yurukusa.github.io/cc-safe-setup/matrix.html) · [Playground](https://yurukusa.github.io/cc-hook-registry/playground.html)
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
npx cc-safe-setup
|
package/index.mjs
CHANGED
|
@@ -106,6 +106,8 @@ const PROFILE = PROFILE_IDX !== -1 ? process.argv[PROFILE_IDX + 1] : null;
|
|
|
106
106
|
const COMPARE_IDX = process.argv.findIndex(a => a === '--compare');
|
|
107
107
|
const COMPARE = COMPARE_IDX !== -1 ? { a: process.argv[COMPARE_IDX + 1], b: process.argv[COMPARE_IDX + 2] } : null;
|
|
108
108
|
const REPLAY = process.argv.includes('--replay');
|
|
109
|
+
const SAVE_PROFILE_IDX = process.argv.findIndex(a => a === '--save-profile');
|
|
110
|
+
const SAVE_PROFILE = SAVE_PROFILE_IDX !== -1 ? process.argv[SAVE_PROFILE_IDX + 1] : null;
|
|
109
111
|
const CREATE_IDX = process.argv.findIndex(a => a === '--create');
|
|
110
112
|
const CREATE_DESC = CREATE_IDX !== -1 ? process.argv.slice(CREATE_IDX + 1).join(' ') : null;
|
|
111
113
|
const SUGGEST = process.argv.includes('--suggest');
|
|
@@ -142,6 +144,7 @@ if (HELP) {
|
|
|
142
144
|
npx cc-safe-setup --doctor Diagnose why hooks aren't working
|
|
143
145
|
npx cc-safe-setup --watch Live dashboard of blocked commands
|
|
144
146
|
npx cc-safe-setup --create "<desc>" Generate a custom hook from description
|
|
147
|
+
npx cc-safe-setup --save-profile <name> Save current hooks as a named profile
|
|
145
148
|
npx cc-safe-setup --suggest Analyze project and predict risks → suggest hooks
|
|
146
149
|
npx cc-safe-setup --why <hook> Why this hook exists (real incident + issue link)
|
|
147
150
|
npx cc-safe-setup --replay Replay blocked commands timeline (demo/review)
|
|
@@ -928,6 +931,53 @@ async function fullSetup() {
|
|
|
928
931
|
console.log();
|
|
929
932
|
}
|
|
930
933
|
|
|
934
|
+
async function saveProfile(name) {
|
|
935
|
+
const { readdirSync } = await import('fs');
|
|
936
|
+
const profilesDir = join(HOME, '.claude', 'profiles');
|
|
937
|
+
mkdirSync(profilesDir, { recursive: true });
|
|
938
|
+
|
|
939
|
+
if (!name) {
|
|
940
|
+
// List saved profiles
|
|
941
|
+
console.log();
|
|
942
|
+
console.log(c.bold + ' Saved Profiles' + c.reset);
|
|
943
|
+
console.log();
|
|
944
|
+
const files = existsSync(profilesDir) ? readdirSync(profilesDir).filter(f => f.endsWith('.json')) : [];
|
|
945
|
+
if (files.length === 0) {
|
|
946
|
+
console.log(c.dim + ' No saved profiles yet.' + c.reset);
|
|
947
|
+
console.log(c.dim + ' Save: npx cc-safe-setup --save-profile my-setup' + c.reset);
|
|
948
|
+
} else {
|
|
949
|
+
for (const f of files) {
|
|
950
|
+
const pName = f.replace('.json', '');
|
|
951
|
+
const data = JSON.parse(readFileSync(join(profilesDir, f), 'utf-8'));
|
|
952
|
+
console.log(` ${c.bold}${pName}${c.reset} (${data.hooks?.length || 0} hooks, saved ${data.savedAt?.split('T')[0] || '?'})`);
|
|
953
|
+
console.log(c.dim + ` Load: npx cc-safe-setup --profile ${pName}` + c.reset);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
console.log();
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
// Save current hook state
|
|
961
|
+
const hookDir = join(HOME, '.claude', 'hooks');
|
|
962
|
+
const hooks = existsSync(hookDir) ? readdirSync(hookDir).filter(f => f.endsWith('.sh') || f.endsWith('.py')) : [];
|
|
963
|
+
|
|
964
|
+
const profile = {
|
|
965
|
+
name,
|
|
966
|
+
savedAt: new Date().toISOString(),
|
|
967
|
+
hooks: hooks.map(h => h.replace(/\.(sh|py)$/, '')),
|
|
968
|
+
settings: existsSync(SETTINGS_PATH) ? JSON.parse(readFileSync(SETTINGS_PATH, 'utf-8')) : {},
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
const profilePath = join(profilesDir, `${name}.json`);
|
|
972
|
+
writeFileSync(profilePath, JSON.stringify(profile, null, 2));
|
|
973
|
+
|
|
974
|
+
console.log();
|
|
975
|
+
console.log(c.green + ` ✓ Profile "${name}" saved (${hooks.length} hooks)` + c.reset);
|
|
976
|
+
console.log(c.dim + ` File: ${profilePath}` + c.reset);
|
|
977
|
+
console.log(c.dim + ` Load: npx cc-safe-setup --profile ${name}` + c.reset);
|
|
978
|
+
console.log();
|
|
979
|
+
}
|
|
980
|
+
|
|
931
981
|
async function suggest() {
|
|
932
982
|
const { execSync } = await import('child_process');
|
|
933
983
|
const { readdirSync } = await import('fs');
|
|
@@ -1831,6 +1881,16 @@ async function profile(level) {
|
|
|
1831
1881
|
},
|
|
1832
1882
|
};
|
|
1833
1883
|
|
|
1884
|
+
// Check for saved custom profiles
|
|
1885
|
+
const profilesDir = join(HOME, '.claude', 'profiles');
|
|
1886
|
+
if (level && !PROFILES[level]) {
|
|
1887
|
+
const customPath = join(profilesDir, `${level}.json`);
|
|
1888
|
+
if (existsSync(customPath)) {
|
|
1889
|
+
const custom = JSON.parse(readFileSync(customPath, 'utf-8'));
|
|
1890
|
+
PROFILES[level] = { desc: `Custom profile (saved ${custom.savedAt?.split('T')[0] || '?'})`, hooks: custom.hooks || [] };
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1834
1894
|
if (!level || !PROFILES[level]) {
|
|
1835
1895
|
console.log(c.bold + ' Safety Profiles' + c.reset);
|
|
1836
1896
|
console.log();
|
|
@@ -1840,6 +1900,25 @@ async function profile(level) {
|
|
|
1840
1900
|
console.log(` ${c.dim}npx cc-safe-setup --profile ${name}${c.reset}`);
|
|
1841
1901
|
console.log();
|
|
1842
1902
|
}
|
|
1903
|
+
|
|
1904
|
+
// Show saved profiles too
|
|
1905
|
+
if (existsSync(profilesDir)) {
|
|
1906
|
+
const saved = readdirSync(profilesDir).filter(f => f.endsWith('.json'));
|
|
1907
|
+
if (saved.length > 0) {
|
|
1908
|
+
console.log(c.bold + ' Saved Profiles' + c.reset);
|
|
1909
|
+
console.log();
|
|
1910
|
+
for (const f of saved) {
|
|
1911
|
+
const sName = f.replace('.json', '');
|
|
1912
|
+
try {
|
|
1913
|
+
const data = JSON.parse(readFileSync(join(profilesDir, f), 'utf-8'));
|
|
1914
|
+
console.log(` ${c.bold}${sName}${c.reset} (${data.hooks?.length || 0} hooks)`);
|
|
1915
|
+
console.log(` ${c.dim}Saved ${data.savedAt?.split('T')[0] || '?'}${c.reset}`);
|
|
1916
|
+
console.log(` ${c.dim}npx cc-safe-setup --profile ${sName}${c.reset}`);
|
|
1917
|
+
console.log();
|
|
1918
|
+
} catch {}
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1843
1922
|
return;
|
|
1844
1923
|
}
|
|
1845
1924
|
|
|
@@ -4261,6 +4340,7 @@ async function main() {
|
|
|
4261
4340
|
if (FULL) return fullSetup();
|
|
4262
4341
|
if (DOCTOR) return doctor();
|
|
4263
4342
|
if (WATCH) return watch();
|
|
4343
|
+
if (SAVE_PROFILE_IDX !== -1) return saveProfile(SAVE_PROFILE);
|
|
4264
4344
|
if (SUGGEST) return suggest();
|
|
4265
4345
|
if (WHY_IDX !== -1) return why(WHY_HOOK);
|
|
4266
4346
|
if (REPLAY) return replay();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-safe-setup",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.7.0",
|
|
4
4
|
"description": "One command to make Claude Code safe. 59 hooks (8 built-in + 51 examples). 26 CLI commands: dashboard, create, audit, lint, diff, migrate, compare, generate-ci. 284 tests.",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"bin": {
|