claudex-setup 1.7.0 → 1.9.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/src/setup.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Setup engine - applies recommended Claude Code configuration to a project.
3
- * v1.7.0 - Starter-safe setup engine with reusable planning primitives.
3
+ * v1.8.0 - Starter-safe setup engine with reusable planning primitives.
4
4
  */
5
5
 
6
6
  const fs = require('fs');
@@ -8,6 +8,7 @@ const path = require('path');
8
8
  const { TECHNIQUES, STACKS } = require('./techniques');
9
9
  const { ProjectContext } = require('./context');
10
10
  const { audit } = require('./audit');
11
+ const { buildSettingsForProfile } = require('./governance');
11
12
 
12
13
  // ============================================================
13
14
  // Helper: detect project scripts from package.json
@@ -826,6 +827,19 @@ mkdir -p "$LOG_DIR"
826
827
  TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
827
828
  echo "[$TIMESTAMP] $TOOL_NAME: $FILE_PATH" >> "$LOG_FILE"
828
829
 
830
+ exit 0
831
+ `,
832
+ 'session-start.sh': `#!/bin/bash
833
+ # SessionStart hook - prepares logs and records session entry
834
+
835
+ LOG_DIR=".claude/logs"
836
+ LOG_FILE="$LOG_DIR/sessions.log"
837
+
838
+ mkdir -p "$LOG_DIR"
839
+
840
+ TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
841
+ echo "[$TIMESTAMP] session started" >> "$LOG_FILE"
842
+
829
843
  exit 0
830
844
  `,
831
845
  }),
@@ -877,6 +891,15 @@ exit 0
877
891
  1. Run \`git diff\` to see all changes
878
892
  2. Check for: bugs, security issues, missing tests, code style
879
893
  3. Provide actionable feedback
894
+ `;
895
+
896
+ cmds['security-review.md'] = `Run a focused security review using Claude Code's built-in security workflow.
897
+
898
+ ## Steps:
899
+ 1. Review auth, permissions, secrets handling, and data access paths
900
+ 2. Run \`/security-review\` for OWASP-focused analysis
901
+ 3. Check for unsafe shell commands, token leakage, and risky file access
902
+ 4. Report findings ordered by severity with concrete fixes
880
903
  `;
881
904
 
882
905
  // Deploy - stack-specific
@@ -980,6 +1003,17 @@ Fix the GitHub issue: $ARGUMENTS
980
1003
  3. Implement the fix
981
1004
  4. Write tests
982
1005
  5. Create a descriptive commit
1006
+ `,
1007
+ 'release-check/SKILL.md': `---
1008
+ name: release-check
1009
+ description: Prepare a release candidate and verify publish readiness
1010
+ ---
1011
+ Prepare a release candidate for: $ARGUMENTS
1012
+
1013
+ 1. Read CHANGELOG.md and package.json version
1014
+ 2. Run the test suite and packaging checks
1015
+ 3. Verify docs, tags, and release notes are aligned
1016
+ 4. Flag anything that would make the release unsafe or misleading
983
1017
  `,
984
1018
  }),
985
1019
 
@@ -1027,6 +1061,12 @@ Fix the GitHub issue: $ARGUMENTS
1027
1061
  - Never skip or disable tests without a tracking issue
1028
1062
  - Mock external dependencies, not internal logic
1029
1063
  - Include both happy path and edge case tests
1064
+ `;
1065
+ rules['repository.md'] = `When changing release, packaging, or workflow files:
1066
+ - Keep package.json, CHANGELOG.md, README.md, and docs in sync
1067
+ - Prefer tagged release references over floating branch references in public docs
1068
+ - Preserve backward compatibility in CLI flags where practical
1069
+ - Any automation that writes files must document rollback expectations
1030
1070
  `;
1031
1071
  return rules;
1032
1072
  },
@@ -1037,12 +1077,26 @@ name: security-reviewer
1037
1077
  description: Reviews code for security vulnerabilities
1038
1078
  tools: [Read, Grep, Glob]
1039
1079
  model: sonnet
1080
+ maxTurns: 50
1040
1081
  ---
1041
1082
  Review code for security issues:
1042
1083
  - Injection vulnerabilities (SQL, XSS, command injection)
1043
1084
  - Authentication and authorization flaws
1044
1085
  - Secrets or credentials in code
1045
1086
  - Insecure data handling
1087
+ `,
1088
+ 'release-manager.md': `---
1089
+ name: release-manager
1090
+ description: Checks release readiness and packaging consistency
1091
+ tools: [Read, Grep, Glob]
1092
+ model: sonnet
1093
+ maxTurns: 50
1094
+ ---
1095
+ Review release readiness:
1096
+ - version alignment across package.json, changelog, and docs
1097
+ - publish safety and packaging scope
1098
+ - missing rollback or migration notes
1099
+ - documentation drift that would confuse adopters
1046
1100
  `,
1047
1101
  }),
1048
1102
 
@@ -1164,41 +1218,11 @@ async function setup(options) {
1164
1218
  if (fs.existsSync(hooksDir) && !fs.existsSync(settingsPath)) {
1165
1219
  const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.sh'));
1166
1220
  if (hookFiles.length > 0) {
1167
- const settings = {
1168
- permissions: {
1169
- defaultMode: "acceptEdits",
1170
- deny: [
1171
- "Read(./.env*)",
1172
- "Read(./secrets/**)",
1173
- "Bash(rm -rf *)",
1174
- "Bash(git reset --hard *)",
1175
- "Bash(git checkout -- *)",
1176
- "Bash(git clean *)",
1177
- "Bash(git push --force *)"
1178
- ]
1179
- },
1180
- hooks: {
1181
- PostToolUse: [{
1182
- matcher: "Write|Edit",
1183
- hooks: hookFiles.filter(f => f !== 'protect-secrets.sh').map(f => ({
1184
- type: "command",
1185
- command: `bash .claude/hooks/${f}`,
1186
- timeout: 10
1187
- }))
1188
- }]
1189
- }
1190
- };
1191
- // Add protect-secrets as PreToolUse if it exists
1192
- if (hookFiles.includes('protect-secrets.sh')) {
1193
- settings.hooks.PreToolUse = [{
1194
- matcher: "Read|Write|Edit",
1195
- hooks: [{
1196
- type: "command",
1197
- command: "bash .claude/hooks/protect-secrets.sh",
1198
- timeout: 5
1199
- }]
1200
- }];
1201
- }
1221
+ const settings = buildSettingsForProfile({
1222
+ profileKey: options.profile || 'safe-write',
1223
+ hookFiles,
1224
+ mcpPackKeys: options.mcpPacks || [],
1225
+ });
1202
1226
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
1203
1227
  writtenFiles.push('.claude/settings.json');
1204
1228
  log(` \x1b[32m✅\x1b[0m Created .claude/settings.json (hooks registered)`);
package/src/techniques.js CHANGED
@@ -4,6 +4,12 @@
4
4
  * Each technique includes: what to check, how to fix, impact level.
5
5
  */
6
6
 
7
+ function hasFrontendSignals(ctx) {
8
+ const pkg = ctx.fileContent('package.json') || '';
9
+ return /react|vue|angular|next|svelte|tailwind|vite|astro/i.test(pkg) ||
10
+ ctx.files.some(f => /tailwind\.config|vite\.config|next\.config|svelte\.config|nuxt\.config|pages\/|components\/|app\//i.test(f));
11
+ }
12
+
7
13
  const TECHNIQUES = {
8
14
  // ============================================================
9
15
  // === MEMORY & CONTEXT (category: 'memory') ==================
@@ -142,8 +148,13 @@ const TECHNIQUES = {
142
148
  name: '.claude/ tracked in git',
143
149
  check: (ctx) => {
144
150
  if (!ctx.fileContent('.gitignore')) return true; // no gitignore = ok
145
- const content = ctx.fileContent('.gitignore');
146
- return !content.includes('.claude/') || content.includes('!.claude/');
151
+ const lines = ctx.fileContent('.gitignore')
152
+ .split(/\r?\n/)
153
+ .map(line => line.trim())
154
+ .filter(line => line && !line.startsWith('#'));
155
+ const ignoresClaudeDir = lines.some(line => /^(\/|\*\*\/)?\.claude\/?$/.test(line));
156
+ const unignoresClaudeDir = lines.some(line => /^!(\/)?\.claude(\/|\*\*)?$/.test(line));
157
+ return !ignoresClaudeDir || unignoresClaudeDir;
147
158
  },
148
159
  impact: 'high',
149
160
  rating: 4,
@@ -318,7 +329,7 @@ const TECHNIQUES = {
318
329
  name: 'Permission configuration',
319
330
  check: (ctx) => {
320
331
  const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
321
- return settings && settings.permissions;
332
+ return !!(settings && settings.permissions);
322
333
  },
323
334
  impact: 'medium',
324
335
  rating: 4,
@@ -347,9 +358,15 @@ const TECHNIQUES = {
347
358
  id: 2402,
348
359
  name: 'Default mode is not bypassPermissions',
349
360
  check: (ctx) => {
350
- const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
351
- if (!settings || !settings.permissions) return null; // no settings = skip (not applicable)
352
- return settings.permissions.defaultMode !== 'bypassPermissions';
361
+ // Check shared settings first (committed to git) if the shared baseline
362
+ // is safe, a personal settings.local.json override should not fail the audit.
363
+ const shared = ctx.jsonFile('.claude/settings.json');
364
+ if (shared && shared.permissions) {
365
+ return shared.permissions.defaultMode !== 'bypassPermissions';
366
+ }
367
+ const local = ctx.jsonFile('.claude/settings.local.json');
368
+ if (!local || !local.permissions) return null;
369
+ return local.permissions.defaultMode !== 'bypassPermissions';
353
370
  },
354
371
  impact: 'critical',
355
372
  rating: 5,
@@ -362,7 +379,8 @@ const TECHNIQUES = {
362
379
  id: 1096,
363
380
  name: 'Secrets protection configured',
364
381
  check: (ctx) => {
365
- const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
382
+ // Prefer shared settings.json (committed) over local override
383
+ const settings = ctx.jsonFile('.claude/settings.json') || ctx.jsonFile('.claude/settings.local.json');
366
384
  if (!settings || !settings.permissions) return false;
367
385
  const deny = JSON.stringify(settings.permissions.deny || []);
368
386
  return deny.includes('.env') || deny.includes('secrets');
@@ -471,6 +489,7 @@ const TECHNIQUES = {
471
489
  id: 1025,
472
490
  name: 'Frontend design skill for anti-AI-slop',
473
491
  check: (ctx) => {
492
+ if (!hasFrontendSignals(ctx)) return null;
474
493
  const md = ctx.fileContent('CLAUDE.md') || '';
475
494
  return md.includes('frontend_aesthetics') || md.includes('anti-AI-slop') || md.includes('frontend-design');
476
495
  },
@@ -485,6 +504,7 @@ const TECHNIQUES = {
485
504
  id: 102501,
486
505
  name: 'Tailwind CSS configured',
487
506
  check: (ctx) => {
507
+ if (!hasFrontendSignals(ctx)) return null;
488
508
  const pkg = ctx.fileContent('package.json') || '';
489
509
  return pkg.includes('tailwind') ||
490
510
  ctx.files.some(f => /tailwind\.config/.test(f));
@@ -613,7 +633,7 @@ const TECHNIQUES = {
613
633
  if (!hasNodeSignals) return null;
614
634
  if (ctx.files.includes('.nvmrc') || ctx.files.includes('.node-version')) return true;
615
635
  const pkg = ctx.jsonFile('package.json');
616
- return pkg && pkg.engines && pkg.engines.node;
636
+ return !!(pkg && pkg.engines && pkg.engines.node);
617
637
  },
618
638
  impact: 'low',
619
639
  rating: 3,
@@ -663,7 +683,7 @@ const TECHNIQUES = {
663
683
  name: 'MCP servers configured',
664
684
  check: (ctx) => {
665
685
  const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
666
- return settings && settings.mcpServers && Object.keys(settings.mcpServers).length > 0;
686
+ return !!(settings && settings.mcpServers && Object.keys(settings.mcpServers).length > 0);
667
687
  },
668
688
  impact: 'medium',
669
689
  rating: 3,
@@ -677,7 +697,7 @@ const TECHNIQUES = {
677
697
  name: '2+ MCP servers for rich tooling',
678
698
  check: (ctx) => {
679
699
  const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
680
- return settings && settings.mcpServers && Object.keys(settings.mcpServers).length >= 2;
700
+ return !!(settings && settings.mcpServers && Object.keys(settings.mcpServers).length >= 2);
681
701
  },
682
702
  impact: 'medium',
683
703
  rating: 4,