cc-safe-setup 2.2.0 → 2.3.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/index.mjs +142 -0
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -70,6 +70,7 @@ const INSTALL_EXAMPLE_IDX = process.argv.findIndex(a => a === '--install-example
|
|
|
70
70
|
const INSTALL_EXAMPLE = INSTALL_EXAMPLE_IDX !== -1 ? process.argv[INSTALL_EXAMPLE_IDX + 1] : null;
|
|
71
71
|
const AUDIT = process.argv.includes('--audit');
|
|
72
72
|
const LEARN = process.argv.includes('--learn');
|
|
73
|
+
const SCAN = process.argv.includes('--scan');
|
|
73
74
|
|
|
74
75
|
if (HELP) {
|
|
75
76
|
console.log(`
|
|
@@ -682,6 +683,146 @@ exit 0`;
|
|
|
682
683
|
console.log();
|
|
683
684
|
}
|
|
684
685
|
|
|
686
|
+
function scan() {
|
|
687
|
+
console.log();
|
|
688
|
+
console.log(c.bold + ' cc-safe-setup --scan' + c.reset);
|
|
689
|
+
console.log(c.dim + ' Scanning project to generate safety config...' + c.reset);
|
|
690
|
+
console.log();
|
|
691
|
+
|
|
692
|
+
const cwd = process.cwd();
|
|
693
|
+
const detected = { languages: [], frameworks: [], hasDb: false, hasDocker: false, isMonorepo: false };
|
|
694
|
+
const hooks = ['destructive-guard', 'branch-guard', 'secret-guard', 'syntax-check'];
|
|
695
|
+
const claudeMdRules = ['# Project Safety Rules\n', '## Generated by cc-safe-setup --scan\n'];
|
|
696
|
+
|
|
697
|
+
// Detect languages & frameworks
|
|
698
|
+
if (existsSync(join(cwd, 'package.json'))) {
|
|
699
|
+
detected.languages.push('JavaScript/TypeScript');
|
|
700
|
+
try {
|
|
701
|
+
const pkg = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf-8'));
|
|
702
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
703
|
+
if (allDeps.next) detected.frameworks.push('Next.js');
|
|
704
|
+
if (allDeps.react) detected.frameworks.push('React');
|
|
705
|
+
if (allDeps.express) detected.frameworks.push('Express');
|
|
706
|
+
if (allDeps.prisma || allDeps['@prisma/client']) { detected.frameworks.push('Prisma'); detected.hasDb = true; }
|
|
707
|
+
if (allDeps.sequelize) { detected.frameworks.push('Sequelize'); detected.hasDb = true; }
|
|
708
|
+
if (allDeps.typeorm) { detected.frameworks.push('TypeORM'); detected.hasDb = true; }
|
|
709
|
+
} catch(e) {}
|
|
710
|
+
}
|
|
711
|
+
if (existsSync(join(cwd, 'requirements.txt')) || existsSync(join(cwd, 'pyproject.toml'))) {
|
|
712
|
+
detected.languages.push('Python');
|
|
713
|
+
if (existsSync(join(cwd, 'manage.py'))) { detected.frameworks.push('Django'); detected.hasDb = true; }
|
|
714
|
+
if (existsSync(join(cwd, 'app.py')) || existsSync(join(cwd, 'wsgi.py'))) detected.frameworks.push('Flask');
|
|
715
|
+
}
|
|
716
|
+
if (existsSync(join(cwd, 'Gemfile'))) {
|
|
717
|
+
detected.languages.push('Ruby');
|
|
718
|
+
detected.frameworks.push('Rails');
|
|
719
|
+
detected.hasDb = true;
|
|
720
|
+
}
|
|
721
|
+
if (existsSync(join(cwd, 'composer.json'))) {
|
|
722
|
+
detected.languages.push('PHP');
|
|
723
|
+
try {
|
|
724
|
+
const composer = JSON.parse(readFileSync(join(cwd, 'composer.json'), 'utf-8'));
|
|
725
|
+
if (composer.require?.['laravel/framework']) { detected.frameworks.push('Laravel'); detected.hasDb = true; }
|
|
726
|
+
if (composer.require?.['symfony/framework-bundle']) { detected.frameworks.push('Symfony'); detected.hasDb = true; }
|
|
727
|
+
} catch(e) {}
|
|
728
|
+
}
|
|
729
|
+
if (existsSync(join(cwd, 'go.mod'))) detected.languages.push('Go');
|
|
730
|
+
if (existsSync(join(cwd, 'Cargo.toml'))) detected.languages.push('Rust');
|
|
731
|
+
|
|
732
|
+
// Docker
|
|
733
|
+
if (existsSync(join(cwd, 'Dockerfile')) || existsSync(join(cwd, 'docker-compose.yml')) || existsSync(join(cwd, 'compose.yaml'))) {
|
|
734
|
+
detected.hasDocker = true;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Monorepo
|
|
738
|
+
if (existsSync(join(cwd, 'pnpm-workspace.yaml')) || existsSync(join(cwd, 'lerna.json')) || existsSync(join(cwd, 'nx.json'))) {
|
|
739
|
+
detected.isMonorepo = true;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// .env files
|
|
743
|
+
const hasEnv = existsSync(join(cwd, '.env')) || existsSync(join(cwd, '.env.local'));
|
|
744
|
+
|
|
745
|
+
// Display detection results
|
|
746
|
+
console.log(c.bold + ' Detected:' + c.reset);
|
|
747
|
+
if (detected.languages.length) console.log(' ' + c.green + '✓' + c.reset + ' Languages: ' + detected.languages.join(', '));
|
|
748
|
+
if (detected.frameworks.length) console.log(' ' + c.green + '✓' + c.reset + ' Frameworks: ' + detected.frameworks.join(', '));
|
|
749
|
+
if (detected.hasDb) console.log(' ' + c.green + '✓' + c.reset + ' Database detected');
|
|
750
|
+
if (detected.hasDocker) console.log(' ' + c.green + '✓' + c.reset + ' Docker detected');
|
|
751
|
+
if (detected.isMonorepo) console.log(' ' + c.green + '✓' + c.reset + ' Monorepo detected');
|
|
752
|
+
if (hasEnv) console.log(' ' + c.yellow + '⚠' + c.reset + ' .env file found (secret leak risk)');
|
|
753
|
+
console.log();
|
|
754
|
+
|
|
755
|
+
// Generate recommendations
|
|
756
|
+
const examples = [];
|
|
757
|
+
|
|
758
|
+
if (detected.hasDb) {
|
|
759
|
+
examples.push('block-database-wipe');
|
|
760
|
+
claudeMdRules.push('- Never run destructive database commands (migrate:fresh, DROP DATABASE, prisma migrate reset)');
|
|
761
|
+
claudeMdRules.push('- Always backup database before schema changes');
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (detected.hasDocker) {
|
|
765
|
+
examples.push('auto-approve-docker');
|
|
766
|
+
claudeMdRules.push('- Docker commands are auto-approved for build/compose/ps/logs');
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (hasEnv) {
|
|
770
|
+
claudeMdRules.push('- Never commit .env files. Use .env.example for templates');
|
|
771
|
+
claudeMdRules.push('- Never hardcode API keys or secrets in source files');
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (detected.languages.includes('Python')) {
|
|
775
|
+
examples.push('auto-approve-python');
|
|
776
|
+
claudeMdRules.push('- Run pytest after every code change');
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
if (detected.languages.includes('JavaScript/TypeScript')) {
|
|
780
|
+
examples.push('auto-approve-build');
|
|
781
|
+
claudeMdRules.push('- Run npm test after every code change');
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// Always recommend
|
|
785
|
+
examples.push('scope-guard');
|
|
786
|
+
examples.push('protect-dotfiles');
|
|
787
|
+
claudeMdRules.push('- Always run tests before committing');
|
|
788
|
+
claudeMdRules.push('- Never force-push to main/master');
|
|
789
|
+
claudeMdRules.push('- Create a backup branch before large refactors');
|
|
790
|
+
|
|
791
|
+
// Display recommendations
|
|
792
|
+
console.log(c.bold + ' Recommended hooks:' + c.reset);
|
|
793
|
+
console.log(' ' + c.dim + 'npx cc-safe-setup' + c.reset + ' (8 built-in hooks)');
|
|
794
|
+
for (const ex of examples) {
|
|
795
|
+
console.log(' ' + c.dim + 'npx cc-safe-setup --install-example ' + ex + c.reset);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// Generate CLAUDE.md
|
|
799
|
+
console.log();
|
|
800
|
+
const claudeMdPath = join(cwd, 'CLAUDE.md');
|
|
801
|
+
if (existsSync(claudeMdPath)) {
|
|
802
|
+
console.log(c.yellow + ' CLAUDE.md already exists. Suggested rules to add:' + c.reset);
|
|
803
|
+
console.log();
|
|
804
|
+
for (const rule of claudeMdRules.slice(2)) {
|
|
805
|
+
console.log(' ' + c.dim + rule + c.reset);
|
|
806
|
+
}
|
|
807
|
+
} else {
|
|
808
|
+
const content = claudeMdRules.join('\n') + '\n';
|
|
809
|
+
if (process.argv.includes('--apply')) {
|
|
810
|
+
writeFileSync(claudeMdPath, content);
|
|
811
|
+
console.log(c.green + ' ✓ CLAUDE.md created with ' + (claudeMdRules.length - 2) + ' project-specific rules.' + c.reset);
|
|
812
|
+
} else {
|
|
813
|
+
console.log(c.bold + ' Suggested CLAUDE.md:' + c.reset);
|
|
814
|
+
console.log();
|
|
815
|
+
for (const rule of claudeMdRules) {
|
|
816
|
+
console.log(' ' + c.dim + rule + c.reset);
|
|
817
|
+
}
|
|
818
|
+
console.log();
|
|
819
|
+
console.log(c.dim + ' Run with --apply to create: npx cc-safe-setup --scan --apply' + c.reset);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
console.log();
|
|
824
|
+
}
|
|
825
|
+
|
|
685
826
|
async function main() {
|
|
686
827
|
if (UNINSTALL) return uninstall();
|
|
687
828
|
if (VERIFY) return verify();
|
|
@@ -690,6 +831,7 @@ async function main() {
|
|
|
690
831
|
if (INSTALL_EXAMPLE) return installExample(INSTALL_EXAMPLE);
|
|
691
832
|
if (AUDIT) return audit();
|
|
692
833
|
if (LEARN) return learn();
|
|
834
|
+
if (SCAN) return scan();
|
|
693
835
|
|
|
694
836
|
console.log();
|
|
695
837
|
console.log(c.bold + ' cc-safe-setup' + c.reset);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-safe-setup",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "One command to make Claude Code safe for autonomous operation. 8 built-in hooks + 25 installable examples. Destructive blocker, branch guard, database wipe protection, dotfile guard, and more.",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"bin": {
|