@soulbatical/tetra-dev-toolkit 1.9.0 → 1.9.2

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.
@@ -122,35 +122,42 @@ async function setupHooks(options) {
122
122
  execSync('npx husky init', { stdio: 'inherit' })
123
123
  }
124
124
 
125
- // Create pre-commit hook
125
+ // Create or extend pre-commit hook with tetra-audit quick
126
126
  const preCommitPath = join(huskyDir, 'pre-commit')
127
- if (!existsSync(preCommitPath) || options.force) {
128
- const preCommitContent = `#!/bin/sh
129
- . "$(dirname "$0")/_/husky.sh"
130
-
131
- echo "🔍 Running Tetra quality checks..."
132
-
133
- # Run quick security checks (fast, blocks commit on critical issues)
127
+ const tetraAuditBlock = `
128
+ # Tetra quick security checks (hardcoded secrets, direct createClient, service key exposure)
129
+ echo "🔍 Running Tetra security checks..."
134
130
  npx tetra-audit quick
135
131
  if [ $? -ne 0 ]; then
136
132
  echo ""
137
133
  echo "❌ Security issues found! Fix before committing."
138
- echo " Run 'tetra-audit' for detailed report."
134
+ echo " Run 'npx tetra-audit security --verbose' for details."
139
135
  exit 1
140
136
  fi
141
-
142
- # Run lint-staged if configured
143
- if [ -f "package.json" ] && grep -q "lint-staged" package.json; then
144
- npx lint-staged
145
- fi
146
-
147
- echo "✅ Pre-commit checks passed"
148
137
  `
138
+
139
+ if (!existsSync(preCommitPath)) {
140
+ // No pre-commit hook — create one
141
+ const preCommitContent = `#!/bin/sh\n${tetraAuditBlock}\necho "✅ Pre-commit checks passed"\n`
142
+ writeFileSync(preCommitPath, preCommitContent)
143
+ execSync(`chmod +x ${preCommitPath}`)
144
+ console.log(' ✅ Created .husky/pre-commit with tetra-audit quick')
145
+ } else if (options.force) {
146
+ // Force overwrite
147
+ const preCommitContent = `#!/bin/sh\n${tetraAuditBlock}\necho "✅ Pre-commit checks passed"\n`
149
148
  writeFileSync(preCommitPath, preCommitContent)
150
149
  execSync(`chmod +x ${preCommitPath}`)
151
- console.log(' ✅ Created .husky/pre-commit')
150
+ console.log(' ✅ Overwrote .husky/pre-commit with tetra-audit quick')
152
151
  } else {
153
- console.log(' ⏭️ .husky/pre-commit already exists (use --force to overwrite)')
152
+ // Pre-commit exists add tetra-audit quick if missing
153
+ const existing = readFileSync(preCommitPath, 'utf-8')
154
+ if (!existing.includes('tetra-audit')) {
155
+ const updated = existing.trimEnd() + '\n' + tetraAuditBlock
156
+ writeFileSync(preCommitPath, updated)
157
+ console.log(' ✅ Added tetra-audit quick to existing .husky/pre-commit')
158
+ } else {
159
+ console.log(' ⏭️ .husky/pre-commit already has tetra-audit')
160
+ }
154
161
  }
155
162
 
156
163
  // Create or extend pre-push hook with hygiene check + RLS security gate
@@ -37,3 +37,4 @@ export { check as checkDependencyAutomation } from './dependency-automation.js'
37
37
  export { check as checkLicenseAudit } from './license-audit.js'
38
38
  export { check as checkSast } from './sast.js'
39
39
  export { check as checkBundleSize } from './bundle-size.js'
40
+ export { check as checkSecurityLayers } from './security-layers.js'
@@ -33,6 +33,7 @@ import { check as checkDependencyAutomation } from './dependency-automation.js'
33
33
  import { check as checkLicenseAudit } from './license-audit.js'
34
34
  import { check as checkSast } from './sast.js'
35
35
  import { check as checkBundleSize } from './bundle-size.js'
36
+ import { check as checkSecurityLayers } from './security-layers.js'
36
37
  import { calculateHealthStatus } from './types.js'
37
38
 
38
39
  /**
@@ -74,7 +75,8 @@ export async function scanProjectHealth(projectPath, projectName, options = {})
74
75
  checkDependencyAutomation(projectPath),
75
76
  checkLicenseAudit(projectPath),
76
77
  checkSast(projectPath),
77
- checkBundleSize(projectPath)
78
+ checkBundleSize(projectPath),
79
+ checkSecurityLayers(projectPath)
78
80
  ])
79
81
 
80
82
  const totalScore = checks.reduce((sum, c) => sum + c.score, 0)
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Health Check: 3-Layer Security Model
3
+ *
4
+ * Verifies the project has all 3 security layers active:
5
+ * Layer 1 (pre-commit): tetra-audit quick
6
+ * Layer 2 (pre-push): tetra-check-rls
7
+ * Layer 3 (build/deploy): tetra-check-rls --errors-only in railway.json
8
+ *
9
+ * Score: 0-3 (1 point per layer)
10
+ */
11
+
12
+ import { existsSync, readFileSync } from 'fs'
13
+ import { join } from 'path'
14
+ import { createCheck } from './types.js'
15
+
16
+ export async function check(projectPath) {
17
+ const result = createCheck('security-layers', 3, {
18
+ layer1_precommit: false,
19
+ layer2_prepush: false,
20
+ layer3_build: false,
21
+ missing: []
22
+ })
23
+
24
+ // Layer 1: pre-commit has tetra-audit
25
+ const preCommitPaths = [
26
+ join(projectPath, '.husky', 'pre-commit'),
27
+ // monorepo: check parent dir too
28
+ join(projectPath, '..', '.husky', 'pre-commit')
29
+ ]
30
+
31
+ for (const p of preCommitPaths) {
32
+ if (existsSync(p)) {
33
+ try {
34
+ const content = readFileSync(p, 'utf-8')
35
+ if (content.includes('tetra-audit')) {
36
+ result.details.layer1_precommit = true
37
+ result.score += 1
38
+ break
39
+ }
40
+ } catch { /* ignore read errors */ }
41
+ }
42
+ }
43
+
44
+ if (!result.details.layer1_precommit) {
45
+ result.details.missing.push('Layer 1: .husky/pre-commit missing tetra-audit quick')
46
+ }
47
+
48
+ // Layer 2: pre-push has tetra-check-rls
49
+ const prePushPaths = [
50
+ join(projectPath, '.husky', 'pre-push'),
51
+ join(projectPath, '..', '.husky', 'pre-push')
52
+ ]
53
+
54
+ for (const p of prePushPaths) {
55
+ if (existsSync(p)) {
56
+ try {
57
+ const content = readFileSync(p, 'utf-8')
58
+ if (content.includes('tetra-check-rls')) {
59
+ result.details.layer2_prepush = true
60
+ result.score += 1
61
+ break
62
+ }
63
+ } catch { /* ignore read errors */ }
64
+ }
65
+ }
66
+
67
+ if (!result.details.layer2_prepush) {
68
+ result.details.missing.push('Layer 2: .husky/pre-push missing tetra-check-rls')
69
+ }
70
+
71
+ // Layer 3: railway.json has tetra-check-rls --errors-only
72
+ const railwayPaths = [
73
+ join(projectPath, 'railway.json'),
74
+ join(projectPath, '..', 'railway.json'),
75
+ join(projectPath, 'backend', 'railway.json')
76
+ ]
77
+
78
+ for (const p of railwayPaths) {
79
+ if (existsSync(p)) {
80
+ try {
81
+ const content = readFileSync(p, 'utf-8')
82
+ if (content.includes('tetra-check-rls')) {
83
+ result.details.layer3_build = true
84
+ result.score += 1
85
+ break
86
+ }
87
+ } catch { /* ignore read errors */ }
88
+ }
89
+ }
90
+
91
+ if (!result.details.layer3_build) {
92
+ result.details.missing.push('Layer 3: railway.json missing tetra-check-rls --errors-only in buildCommand')
93
+ }
94
+
95
+ // Set status
96
+ if (result.score === 3) {
97
+ result.status = 'ok'
98
+ } else if (result.score >= 2) {
99
+ result.status = 'warning'
100
+ } else {
101
+ result.status = 'error'
102
+ }
103
+
104
+ result.details.fix = 'Run: npx tetra-setup hooks'
105
+
106
+ return result
107
+ }
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  /**
8
- * @typedef {'plugins'|'mcps'|'git'|'tests'|'secrets'|'quality-toolkit'|'naming-conventions'|'rls-audit'|'rpc-param-mismatch'|'typescript-strict'|'prettier'|'coverage-thresholds'|'eslint-security'|'dependency-cruiser'|'conventional-commits'|'knip'|'dependency-automation'|'license-audit'|'sast'|'bundle-size'|'gitignore'|'repo-visibility'|'vincifox-widget'|'stella-integration'|'claude-md'|'doppler-compliance'|'infrastructure-yml'|'file-organization'} HealthCheckType
8
+ * @typedef {'plugins'|'mcps'|'git'|'tests'|'secrets'|'quality-toolkit'|'naming-conventions'|'rls-audit'|'rpc-param-mismatch'|'typescript-strict'|'prettier'|'coverage-thresholds'|'eslint-security'|'dependency-cruiser'|'conventional-commits'|'knip'|'dependency-automation'|'license-audit'|'sast'|'bundle-size'|'gitignore'|'repo-visibility'|'vincifox-widget'|'stella-integration'|'claude-md'|'doppler-compliance'|'infrastructure-yml'|'file-organization'|'security-layers'} HealthCheckType
9
9
  *
10
10
  * @typedef {'ok'|'warning'|'error'} HealthStatus
11
11
  *
@@ -66,7 +66,7 @@ export function calculateHealthStatus(checks) {
66
66
 
67
67
  // Critical checks override percentage
68
68
  if (checks.some(c =>
69
- (c.type === 'secrets' || c.type === 'rls-audit' || c.type === 'rpc-param-mismatch' || c.type === 'repo-visibility') && c.status === 'error'
69
+ (c.type === 'secrets' || c.type === 'rls-audit' || c.type === 'rpc-param-mismatch' || c.type === 'repo-visibility' || c.type === 'security-layers') && c.status === 'error'
70
70
  )) {
71
71
  return 'unhealthy'
72
72
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulbatical/tetra-dev-toolkit",
3
- "version": "1.9.0",
3
+ "version": "1.9.2",
4
4
  "publishConfig": {
5
5
  "access": "restricted"
6
6
  },