@nerviq/cli 1.17.3 → 1.19.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/LICENSE +23 -23
- package/README.md +4 -4
- package/bin/cli.js +61 -274
- package/package.json +60 -60
- package/src/activity.js +1039 -1039
- package/src/adoption-advisor.js +299 -299
- package/src/aider/config-parser.js +166 -166
- package/src/aider/context.js +158 -158
- package/src/aider/deep-review.js +316 -316
- package/src/aider/domain-packs.js +303 -303
- package/src/aider/freshness.js +93 -93
- package/src/aider/governance.js +253 -253
- package/src/aider/interactive.js +334 -334
- package/src/aider/mcp-packs.js +329 -329
- package/src/aider/patch.js +214 -214
- package/src/aider/plans.js +186 -186
- package/src/aider/premium.js +360 -360
- package/src/aider/setup.js +404 -404
- package/src/aider/techniques.js +16 -16
- package/src/analyze.js +951 -951
- package/src/anti-patterns.js +485 -485
- package/src/audit/instruction-files.js +180 -180
- package/src/audit/recommendations.js +577 -577
- package/src/auto-suggest.js +154 -154
- package/src/badge.js +13 -13
- package/src/behavioral-drift.js +801 -801
- package/src/benchmark.js +67 -67
- package/src/catalog.js +103 -103
- package/src/certification.js +128 -128
- package/src/codex/config-parser.js +183 -183
- package/src/codex/context.js +223 -223
- package/src/codex/deep-review.js +493 -493
- package/src/codex/domain-packs.js +394 -394
- package/src/codex/freshness.js +84 -84
- package/src/codex/governance.js +192 -192
- package/src/codex/interactive.js +618 -618
- package/src/codex/mcp-packs.js +914 -914
- package/src/codex/patch.js +209 -209
- package/src/codex/plans.js +251 -251
- package/src/codex/premium.js +614 -614
- package/src/codex/setup.js +591 -591
- package/src/context.js +320 -320
- package/src/continuous-ops.js +681 -681
- package/src/copilot/activity.js +309 -309
- package/src/copilot/config-parser.js +280 -226
- package/src/copilot/context.js +218 -197
- package/src/copilot/deep-review.js +346 -346
- package/src/copilot/domain-packs.js +372 -372
- package/src/copilot/freshness.js +57 -57
- package/src/copilot/governance.js +222 -222
- package/src/copilot/interactive.js +406 -406
- package/src/copilot/mcp-packs.js +826 -826
- package/src/copilot/plans.js +253 -253
- package/src/copilot/premium.js +451 -451
- package/src/copilot/setup.js +488 -488
- package/src/copilot/techniques.js +219 -78
- package/src/cost-tracking.js +61 -61
- package/src/cursor/activity.js +301 -301
- package/src/cursor/config-parser.js +265 -265
- package/src/cursor/context.js +256 -256
- package/src/cursor/deep-review.js +334 -334
- package/src/cursor/domain-packs.js +368 -368
- package/src/cursor/freshness.js +65 -65
- package/src/cursor/governance.js +229 -229
- package/src/cursor/interactive.js +391 -391
- package/src/cursor/mcp-packs.js +828 -828
- package/src/cursor/plans.js +254 -254
- package/src/cursor/premium.js +469 -469
- package/src/cursor/setup.js +488 -488
- package/src/dashboard.js +493 -493
- package/src/deep-review.js +428 -428
- package/src/deprecation.js +98 -98
- package/src/diff-only.js +280 -280
- package/src/doctor.js +119 -119
- package/src/domain-pack-expansion.js +1033 -1033
- package/src/domain-packs.js +387 -387
- package/src/feedback.js +178 -178
- package/src/fix-engine.js +783 -0
- package/src/fix-prompts.js +122 -122
- package/src/formatters/sarif.js +115 -115
- package/src/freshness.js +74 -74
- package/src/gemini/config-parser.js +275 -275
- package/src/gemini/context.js +221 -221
- package/src/gemini/deep-review.js +559 -559
- package/src/gemini/domain-packs.js +393 -393
- package/src/gemini/freshness.js +66 -66
- package/src/gemini/governance.js +201 -201
- package/src/gemini/interactive.js +860 -860
- package/src/gemini/mcp-packs.js +915 -915
- package/src/gemini/plans.js +269 -269
- package/src/gemini/premium.js +760 -760
- package/src/gemini/setup.js +692 -692
- package/src/gemini/techniques.js +14 -14
- package/src/governance.js +72 -72
- package/src/harmony/add.js +68 -68
- package/src/harmony/advisor.js +333 -333
- package/src/harmony/canon.js +565 -565
- package/src/harmony/cli.js +591 -591
- package/src/harmony/drift.js +401 -401
- package/src/harmony/governance.js +313 -313
- package/src/harmony/memory.js +239 -239
- package/src/harmony/sync.js +475 -475
- package/src/harmony/watch.js +370 -370
- package/src/hook-validation.js +342 -342
- package/src/index.js +271 -271
- package/src/init.js +184 -184
- package/src/instruction-surfaces.js +185 -185
- package/src/integrations.js +144 -144
- package/src/interactive.js +118 -118
- package/src/locales/en.json +1 -1
- package/src/locales/es.json +1 -1
- package/src/mcp-packs.js +830 -830
- package/src/mcp-server.js +726 -726
- package/src/mcp-validation.js +337 -337
- package/src/nerviq-sync.json +7 -7
- package/src/opencode/config-parser.js +109 -109
- package/src/opencode/context.js +247 -247
- package/src/opencode/deep-review.js +313 -313
- package/src/opencode/domain-packs.js +262 -262
- package/src/opencode/freshness.js +66 -66
- package/src/opencode/governance.js +159 -159
- package/src/opencode/interactive.js +392 -392
- package/src/opencode/mcp-packs.js +705 -705
- package/src/opencode/patch.js +184 -184
- package/src/opencode/plans.js +231 -231
- package/src/opencode/premium.js +413 -413
- package/src/opencode/setup.js +449 -449
- package/src/opencode/techniques.js +27 -27
- package/src/operating-profile.js +574 -574
- package/src/org.js +152 -152
- package/src/permission-rules.js +218 -218
- package/src/plans.js +839 -839
- package/src/platform-change-manifest.js +86 -86
- package/src/plugins.js +110 -110
- package/src/policy-layers.js +210 -210
- package/src/profiles.js +124 -124
- package/src/prompt-injection.js +74 -74
- package/src/public-api.js +173 -173
- package/src/recommendation-rules.js +84 -84
- package/src/repo-archetype.js +386 -386
- package/src/secret-patterns.js +39 -39
- package/src/server.js +527 -527
- package/src/setup/analysis.js +607 -607
- package/src/setup/runtime.js +172 -172
- package/src/setup.js +677 -677
- package/src/shared/capabilities.js +194 -194
- package/src/source-urls.js +132 -132
- package/src/stack-checks.js +565 -565
- package/src/supplemental-checks.js +13 -13
- package/src/synergy/adaptive.js +261 -261
- package/src/synergy/compensation.js +137 -137
- package/src/synergy/evidence.js +193 -193
- package/src/synergy/learning.js +199 -199
- package/src/synergy/patterns.js +227 -227
- package/src/synergy/ranking.js +83 -83
- package/src/synergy/report.js +165 -165
- package/src/synergy/routing.js +146 -146
- package/src/techniques/api.js +407 -407
- package/src/techniques/automation.js +316 -316
- package/src/techniques/compliance.js +257 -257
- package/src/techniques/hygiene.js +294 -294
- package/src/techniques/instructions.js +243 -243
- package/src/techniques/observability.js +226 -226
- package/src/techniques/optimization.js +142 -142
- package/src/techniques/quality.js +318 -318
- package/src/techniques/security.js +237 -237
- package/src/techniques/shared.js +443 -443
- package/src/techniques/stacks.js +2294 -2294
- package/src/techniques/tools.js +106 -106
- package/src/techniques/workflow.js +413 -413
- package/src/techniques.js +81 -81
- package/src/terminology.js +73 -73
- package/src/token-estimate.js +35 -35
- package/src/usage-patterns.js +99 -99
- package/src/verification-metadata.js +145 -145
- package/src/watch.js +247 -247
- package/src/windsurf/activity.js +302 -302
- package/src/windsurf/config-parser.js +267 -267
- package/src/windsurf/context.js +249 -249
- package/src/windsurf/deep-review.js +337 -337
- package/src/windsurf/domain-packs.js +370 -370
- package/src/windsurf/freshness.js +36 -36
- package/src/windsurf/governance.js +231 -231
- package/src/windsurf/interactive.js +388 -388
- package/src/windsurf/mcp-packs.js +792 -792
- package/src/windsurf/plans.js +247 -247
- package/src/windsurf/premium.js +468 -468
- package/src/windsurf/setup.js +471 -471
- package/src/windsurf/techniques.js +17 -17
- package/src/workspace.js +375 -375
|
@@ -1,316 +1,316 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Automation technique fragments.
|
|
3
|
-
* Generated mechanically from the legacy techniques.js monolith during HR-09.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
findProjectFiles,
|
|
8
|
-
readProjectFiles,
|
|
9
|
-
} = require('./shared');
|
|
10
|
-
|
|
11
|
-
module.exports = {
|
|
12
|
-
hooks: {
|
|
13
|
-
id: 19,
|
|
14
|
-
name: 'Hooks for automation',
|
|
15
|
-
check: (ctx) => {
|
|
16
|
-
// Hooks are configured in settings.json (not .claude/hooks/ directory)
|
|
17
|
-
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
18
|
-
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
19
|
-
return !!(shared.hooks && Object.keys(shared.hooks).length > 0) || !!(local.hooks && Object.keys(local.hooks).length > 0);
|
|
20
|
-
},
|
|
21
|
-
impact: 'high',
|
|
22
|
-
rating: 4,
|
|
23
|
-
category: 'automation',
|
|
24
|
-
fix: 'Add hooks in .claude/settings.json under the "hooks" key. Supported events: PreToolUse, PostToolUse, Notification, Stop, StopFailure, SubagentStop, and more.',
|
|
25
|
-
template: 'hooks'
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
hooksInSettings: {
|
|
29
|
-
id: 8801,
|
|
30
|
-
name: 'Hooks configured in settings',
|
|
31
|
-
check: (ctx) => {
|
|
32
|
-
const shared = ctx.jsonFile('.claude/settings.json');
|
|
33
|
-
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
34
|
-
const hasSharedHooks = shared && shared.hooks && Object.keys(shared.hooks).length > 0;
|
|
35
|
-
const hasLocalHooks = local && local.hooks && Object.keys(local.hooks).length > 0;
|
|
36
|
-
return hasSharedHooks || hasLocalHooks;
|
|
37
|
-
},
|
|
38
|
-
impact: 'high',
|
|
39
|
-
rating: 4,
|
|
40
|
-
category: 'automation',
|
|
41
|
-
fix: 'Add hooks in .claude/settings.json for automated enforcement (lint-on-save, test-on-commit).',
|
|
42
|
-
template: 'hooks'
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
preToolUseHook: {
|
|
46
|
-
id: 8802,
|
|
47
|
-
name: 'PreToolUse hook configured',
|
|
48
|
-
check: (ctx) => {
|
|
49
|
-
const shared = ctx.jsonFile('.claude/settings.json');
|
|
50
|
-
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
51
|
-
return !!(shared?.hooks?.PreToolUse || local?.hooks?.PreToolUse);
|
|
52
|
-
},
|
|
53
|
-
impact: 'high',
|
|
54
|
-
rating: 4,
|
|
55
|
-
category: 'automation',
|
|
56
|
-
fix: 'Add PreToolUse hooks for validation before tool calls (e.g. block writes to protected files).',
|
|
57
|
-
template: null
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
postToolUseHook: {
|
|
61
|
-
id: 8803,
|
|
62
|
-
name: 'PostToolUse hook configured',
|
|
63
|
-
check: (ctx) => {
|
|
64
|
-
const shared = ctx.jsonFile('.claude/settings.json');
|
|
65
|
-
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
66
|
-
return !!(shared?.hooks?.PostToolUse || local?.hooks?.PostToolUse);
|
|
67
|
-
},
|
|
68
|
-
impact: 'high',
|
|
69
|
-
rating: 4,
|
|
70
|
-
category: 'automation',
|
|
71
|
-
fix: 'Add PostToolUse hooks for auto-lint or auto-format after file writes.',
|
|
72
|
-
template: null
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
sessionStartHook: {
|
|
76
|
-
id: 8804,
|
|
77
|
-
name: 'SessionStart hook configured',
|
|
78
|
-
check: (ctx) => {
|
|
79
|
-
const shared = ctx.jsonFile('.claude/settings.json');
|
|
80
|
-
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
81
|
-
if (!(shared?.hooks || local?.hooks)) return false;
|
|
82
|
-
return !!(shared?.hooks?.SessionStart || local?.hooks?.SessionStart);
|
|
83
|
-
},
|
|
84
|
-
impact: 'medium',
|
|
85
|
-
rating: 4,
|
|
86
|
-
category: 'automation',
|
|
87
|
-
fix: 'Add a SessionStart hook for initialization tasks (log rotation, state loading, etc.).',
|
|
88
|
-
template: null
|
|
89
|
-
},
|
|
90
|
-
|
|
91
|
-
dockerfile: {
|
|
92
|
-
id: 399,
|
|
93
|
-
name: 'Has Dockerfile',
|
|
94
|
-
check: (ctx) => ctx.files.some(f => /^Dockerfile/i.test(f)),
|
|
95
|
-
impact: 'medium',
|
|
96
|
-
rating: 3,
|
|
97
|
-
category: 'devops',
|
|
98
|
-
fix: 'Add a Dockerfile for containerized builds and deployments.',
|
|
99
|
-
template: null
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
dockerCompose: {
|
|
103
|
-
id: 39901,
|
|
104
|
-
name: 'Has docker-compose.yml',
|
|
105
|
-
check: (ctx) => ctx.files.some(f => /^docker-compose\.(yml|yaml)$/i.test(f)),
|
|
106
|
-
impact: 'medium',
|
|
107
|
-
rating: 3,
|
|
108
|
-
category: 'devops',
|
|
109
|
-
fix: 'Add docker-compose.yml for multi-service local development.',
|
|
110
|
-
template: null
|
|
111
|
-
},
|
|
112
|
-
|
|
113
|
-
ciPipeline: {
|
|
114
|
-
id: 260,
|
|
115
|
-
name: 'CI pipeline configured',
|
|
116
|
-
check: (ctx) => ctx.hasDir('.github/workflows') || ctx.hasDir('.circleci') ||
|
|
117
|
-
ctx.files.includes('.gitlab-ci.yml') || ctx.files.includes('Jenkinsfile') ||
|
|
118
|
-
ctx.files.includes('.travis.yml') || ctx.files.includes('bitbucket-pipelines.yml'),
|
|
119
|
-
impact: 'high',
|
|
120
|
-
rating: 4,
|
|
121
|
-
category: 'devops',
|
|
122
|
-
fix: 'Add a CI pipeline (GitHub Actions, GitLab CI, CircleCI, etc.) for automated testing and deployment.',
|
|
123
|
-
template: null
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
terraformFiles: {
|
|
127
|
-
id: 397,
|
|
128
|
-
name: 'Infrastructure as Code (Terraform)',
|
|
129
|
-
check: (ctx) => ctx.files.some(f => /\.tf$/.test(f)) || ctx.files.includes('main.tf'),
|
|
130
|
-
impact: 'medium',
|
|
131
|
-
rating: 3,
|
|
132
|
-
category: 'devops',
|
|
133
|
-
fix: 'Add Terraform files for infrastructure-as-code management.',
|
|
134
|
-
template: null
|
|
135
|
-
},
|
|
136
|
-
|
|
137
|
-
dockerMultiStage: {
|
|
138
|
-
id: 39902,
|
|
139
|
-
name: 'Dockerfile uses multi-stage build',
|
|
140
|
-
check: (ctx) => {
|
|
141
|
-
const df = findProjectFiles(ctx, /^Dockerfile$/i);
|
|
142
|
-
if (df.length === 0) return null;
|
|
143
|
-
const content = ctx.fileContent(df[0]) || '';
|
|
144
|
-
return (content.match(/^FROM\s/gim) || []).length >= 2;
|
|
145
|
-
},
|
|
146
|
-
impact: 'medium',
|
|
147
|
-
rating: 3,
|
|
148
|
-
category: 'devops',
|
|
149
|
-
fix: 'Use multi-stage builds in Dockerfile to reduce image size and avoid leaking build tools into production.',
|
|
150
|
-
template: null
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
dockerignoreExists: {
|
|
154
|
-
id: 39903,
|
|
155
|
-
name: '.dockerignore includes node_modules and .env',
|
|
156
|
-
check: (ctx) => {
|
|
157
|
-
if (!ctx.files.some(f => /^Dockerfile/i.test(f))) return null;
|
|
158
|
-
const di = ctx.fileContent('.dockerignore') || '';
|
|
159
|
-
return di.includes('node_modules') && /\.env/i.test(di);
|
|
160
|
-
},
|
|
161
|
-
impact: 'high',
|
|
162
|
-
rating: 4,
|
|
163
|
-
category: 'devops',
|
|
164
|
-
fix: 'Add .dockerignore with node_modules, .env, and other sensitive/large files to keep images small and secure.',
|
|
165
|
-
template: null
|
|
166
|
-
},
|
|
167
|
-
|
|
168
|
-
dockerNoSecrets: {
|
|
169
|
-
id: 39904,
|
|
170
|
-
name: 'Dockerfile has no secrets in build args',
|
|
171
|
-
check: (ctx) => {
|
|
172
|
-
const df = findProjectFiles(ctx, /^Dockerfile$/i);
|
|
173
|
-
if (df.length === 0) return null;
|
|
174
|
-
const content = ctx.fileContent(df[0]) || '';
|
|
175
|
-
return !/ARG\s+(PASSWORD|SECRET|TOKEN|API_KEY|PRIVATE_KEY)/i.test(content);
|
|
176
|
-
},
|
|
177
|
-
impact: 'critical',
|
|
178
|
-
rating: 5,
|
|
179
|
-
category: 'devops',
|
|
180
|
-
fix: 'Never pass secrets via ARG in Dockerfile — use runtime environment variables or secret mounts instead.',
|
|
181
|
-
template: null
|
|
182
|
-
},
|
|
183
|
-
|
|
184
|
-
terraformFmt: {
|
|
185
|
-
id: 39705,
|
|
186
|
-
name: 'Terraform formatting configured',
|
|
187
|
-
check: (ctx) => {
|
|
188
|
-
if (!ctx.files.some(f => /\.tf$/.test(f))) return null;
|
|
189
|
-
const ci = readProjectFiles(ctx, /\.(yml|yaml)$/i, 10);
|
|
190
|
-
const makefileContent = ctx.fileContent('Makefile') || '';
|
|
191
|
-
const preCommit = ctx.fileContent('.pre-commit-config.yaml') || '';
|
|
192
|
-
return /terraform\s+fmt/i.test(ci) || /terraform\s+fmt/i.test(makefileContent) || /terraform_fmt/i.test(preCommit);
|
|
193
|
-
},
|
|
194
|
-
impact: 'medium',
|
|
195
|
-
rating: 3,
|
|
196
|
-
category: 'devops',
|
|
197
|
-
fix: 'Add `terraform fmt` to CI or pre-commit hooks to enforce consistent formatting.',
|
|
198
|
-
template: null
|
|
199
|
-
},
|
|
200
|
-
|
|
201
|
-
terraformDirIgnored: {
|
|
202
|
-
id: 39706,
|
|
203
|
-
name: '.terraform directory in .gitignore',
|
|
204
|
-
check: (ctx) => {
|
|
205
|
-
if (!ctx.files.some(f => /\.tf$/.test(f))) return null;
|
|
206
|
-
const gi = ctx.fileContent('.gitignore') || '';
|
|
207
|
-
return /\.terraform/i.test(gi);
|
|
208
|
-
},
|
|
209
|
-
impact: 'high',
|
|
210
|
-
rating: 4,
|
|
211
|
-
category: 'devops',
|
|
212
|
-
fix: 'Add .terraform/ to .gitignore — it contains provider binaries and should not be committed.',
|
|
213
|
-
template: null
|
|
214
|
-
},
|
|
215
|
-
|
|
216
|
-
terraformStateNotCommitted: {
|
|
217
|
-
id: 39707,
|
|
218
|
-
name: 'Terraform state file not committed',
|
|
219
|
-
check: (ctx) => {
|
|
220
|
-
if (!ctx.files.some(f => /\.tf$/.test(f))) return null;
|
|
221
|
-
return !ctx.files.some(f => /terraform\.tfstate$/i.test(f));
|
|
222
|
-
},
|
|
223
|
-
impact: 'critical',
|
|
224
|
-
rating: 5,
|
|
225
|
-
category: 'devops',
|
|
226
|
-
fix: 'Never commit terraform.tfstate — it may contain secrets. Use a remote backend (S3, GCS, Terraform Cloud).',
|
|
227
|
-
template: null
|
|
228
|
-
},
|
|
229
|
-
|
|
230
|
-
terraformBackendConfigured: {
|
|
231
|
-
id: 39708,
|
|
232
|
-
name: 'Terraform remote backend configured',
|
|
233
|
-
check: (ctx) => {
|
|
234
|
-
const tfFiles = findProjectFiles(ctx, /\.tf$/);
|
|
235
|
-
if (tfFiles.length === 0) return null;
|
|
236
|
-
const allTf = tfFiles.slice(0, 10).map(f => ctx.fileContent(f) || '').join('\n');
|
|
237
|
-
return /backend\s+"(s3|gcs|azurerm|remote|cloud|consul|http)"/i.test(allTf);
|
|
238
|
-
},
|
|
239
|
-
impact: 'high',
|
|
240
|
-
rating: 4,
|
|
241
|
-
category: 'devops',
|
|
242
|
-
fix: 'Configure a remote backend in Terraform (S3, GCS, Terraform Cloud) for team collaboration and state locking.',
|
|
243
|
-
template: null
|
|
244
|
-
},
|
|
245
|
-
|
|
246
|
-
githubActionsOrCI: {
|
|
247
|
-
id: 2021,
|
|
248
|
-
name: 'GitHub Actions or CI configured',
|
|
249
|
-
check: (ctx) => {
|
|
250
|
-
return ctx.hasDir('.github/workflows') || !!ctx.fileContent('.circleci/config.yml') ||
|
|
251
|
-
!!ctx.fileContent('.gitlab-ci.yml') || !!ctx.fileContent('Jenkinsfile') ||
|
|
252
|
-
!!ctx.fileContent('.travis.yml') || !!ctx.fileContent('bitbucket-pipelines.yml');
|
|
253
|
-
},
|
|
254
|
-
impact: 'medium', rating: 3, category: 'devops',
|
|
255
|
-
fix: 'Add CI pipeline for automated testing. Claude Code has a GitHub Action for audit gates.',
|
|
256
|
-
template: null
|
|
257
|
-
},
|
|
258
|
-
|
|
259
|
-
multipleHookTypes: {
|
|
260
|
-
id: 2024,
|
|
261
|
-
name: '2+ hook event types configured',
|
|
262
|
-
check: (ctx) => {
|
|
263
|
-
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
264
|
-
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
265
|
-
const hooks = { ...(shared.hooks || {}), ...(local.hooks || {}) };
|
|
266
|
-
return Object.keys(hooks).length >= 2;
|
|
267
|
-
},
|
|
268
|
-
impact: 'medium', rating: 3, category: 'automation',
|
|
269
|
-
fix: 'Add at least 2 hook types (e.g. PostToolUse for linting + SessionStart for initialization).',
|
|
270
|
-
template: null
|
|
271
|
-
},
|
|
272
|
-
|
|
273
|
-
stopFailureHook: {
|
|
274
|
-
id: 2025,
|
|
275
|
-
name: 'StopFailure hook for error tracking',
|
|
276
|
-
check: (ctx) => {
|
|
277
|
-
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
278
|
-
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
279
|
-
// StopFailure = error stop (API errors), Stop = normal completion — both useful but different
|
|
280
|
-
return !!(shared.hooks?.StopFailure || local.hooks?.StopFailure);
|
|
281
|
-
},
|
|
282
|
-
impact: 'low', rating: 3, category: 'automation',
|
|
283
|
-
fix: 'Add a StopFailure hook to log API errors and unexpected stops. Note: StopFailure (errors) is different from Stop (normal completion).',
|
|
284
|
-
template: null
|
|
285
|
-
},
|
|
286
|
-
|
|
287
|
-
hooksNotificationEvent: {
|
|
288
|
-
id: 2033,
|
|
289
|
-
name: 'Notification hook for alerts',
|
|
290
|
-
check: (ctx) => {
|
|
291
|
-
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
292
|
-
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
293
|
-
return !!(shared.hooks?.Notification || local.hooks?.Notification);
|
|
294
|
-
},
|
|
295
|
-
impact: 'low',
|
|
296
|
-
rating: 2,
|
|
297
|
-
category: 'automation',
|
|
298
|
-
fix: 'Add a Notification hook to capture alerts and status updates from Claude during long tasks.',
|
|
299
|
-
template: null
|
|
300
|
-
},
|
|
301
|
-
|
|
302
|
-
subagentStopHook: {
|
|
303
|
-
id: 2034,
|
|
304
|
-
name: 'SubagentStop hook for delegation tracking',
|
|
305
|
-
check: (ctx) => {
|
|
306
|
-
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
307
|
-
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
308
|
-
return !!(shared.hooks?.SubagentStop || local.hooks?.SubagentStop);
|
|
309
|
-
},
|
|
310
|
-
impact: 'low',
|
|
311
|
-
rating: 2,
|
|
312
|
-
category: 'automation',
|
|
313
|
-
fix: 'Add a SubagentStop hook to track when delegated subagent tasks complete.',
|
|
314
|
-
template: null
|
|
315
|
-
},
|
|
316
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Automation technique fragments.
|
|
3
|
+
* Generated mechanically from the legacy techniques.js monolith during HR-09.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
findProjectFiles,
|
|
8
|
+
readProjectFiles,
|
|
9
|
+
} = require('./shared');
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
hooks: {
|
|
13
|
+
id: 19,
|
|
14
|
+
name: 'Hooks for automation',
|
|
15
|
+
check: (ctx) => {
|
|
16
|
+
// Hooks are configured in settings.json (not .claude/hooks/ directory)
|
|
17
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
18
|
+
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
19
|
+
return !!(shared.hooks && Object.keys(shared.hooks).length > 0) || !!(local.hooks && Object.keys(local.hooks).length > 0);
|
|
20
|
+
},
|
|
21
|
+
impact: 'high',
|
|
22
|
+
rating: 4,
|
|
23
|
+
category: 'automation',
|
|
24
|
+
fix: 'Add hooks in .claude/settings.json under the "hooks" key. Supported events: PreToolUse, PostToolUse, Notification, Stop, StopFailure, SubagentStop, and more.',
|
|
25
|
+
template: 'hooks'
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
hooksInSettings: {
|
|
29
|
+
id: 8801,
|
|
30
|
+
name: 'Hooks configured in settings',
|
|
31
|
+
check: (ctx) => {
|
|
32
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
33
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
34
|
+
const hasSharedHooks = shared && shared.hooks && Object.keys(shared.hooks).length > 0;
|
|
35
|
+
const hasLocalHooks = local && local.hooks && Object.keys(local.hooks).length > 0;
|
|
36
|
+
return hasSharedHooks || hasLocalHooks;
|
|
37
|
+
},
|
|
38
|
+
impact: 'high',
|
|
39
|
+
rating: 4,
|
|
40
|
+
category: 'automation',
|
|
41
|
+
fix: 'Add hooks in .claude/settings.json for automated enforcement (lint-on-save, test-on-commit).',
|
|
42
|
+
template: 'hooks'
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
preToolUseHook: {
|
|
46
|
+
id: 8802,
|
|
47
|
+
name: 'PreToolUse hook configured',
|
|
48
|
+
check: (ctx) => {
|
|
49
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
50
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
51
|
+
return !!(shared?.hooks?.PreToolUse || local?.hooks?.PreToolUse);
|
|
52
|
+
},
|
|
53
|
+
impact: 'high',
|
|
54
|
+
rating: 4,
|
|
55
|
+
category: 'automation',
|
|
56
|
+
fix: 'Add PreToolUse hooks for validation before tool calls (e.g. block writes to protected files).',
|
|
57
|
+
template: null
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
postToolUseHook: {
|
|
61
|
+
id: 8803,
|
|
62
|
+
name: 'PostToolUse hook configured',
|
|
63
|
+
check: (ctx) => {
|
|
64
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
65
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
66
|
+
return !!(shared?.hooks?.PostToolUse || local?.hooks?.PostToolUse);
|
|
67
|
+
},
|
|
68
|
+
impact: 'high',
|
|
69
|
+
rating: 4,
|
|
70
|
+
category: 'automation',
|
|
71
|
+
fix: 'Add PostToolUse hooks for auto-lint or auto-format after file writes.',
|
|
72
|
+
template: null
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
sessionStartHook: {
|
|
76
|
+
id: 8804,
|
|
77
|
+
name: 'SessionStart hook configured',
|
|
78
|
+
check: (ctx) => {
|
|
79
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
80
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
81
|
+
if (!(shared?.hooks || local?.hooks)) return false;
|
|
82
|
+
return !!(shared?.hooks?.SessionStart || local?.hooks?.SessionStart);
|
|
83
|
+
},
|
|
84
|
+
impact: 'medium',
|
|
85
|
+
rating: 4,
|
|
86
|
+
category: 'automation',
|
|
87
|
+
fix: 'Add a SessionStart hook for initialization tasks (log rotation, state loading, etc.).',
|
|
88
|
+
template: null
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
dockerfile: {
|
|
92
|
+
id: 399,
|
|
93
|
+
name: 'Has Dockerfile',
|
|
94
|
+
check: (ctx) => ctx.files.some(f => /^Dockerfile/i.test(f)),
|
|
95
|
+
impact: 'medium',
|
|
96
|
+
rating: 3,
|
|
97
|
+
category: 'devops',
|
|
98
|
+
fix: 'Add a Dockerfile for containerized builds and deployments.',
|
|
99
|
+
template: null
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
dockerCompose: {
|
|
103
|
+
id: 39901,
|
|
104
|
+
name: 'Has docker-compose.yml',
|
|
105
|
+
check: (ctx) => ctx.files.some(f => /^docker-compose\.(yml|yaml)$/i.test(f)),
|
|
106
|
+
impact: 'medium',
|
|
107
|
+
rating: 3,
|
|
108
|
+
category: 'devops',
|
|
109
|
+
fix: 'Add docker-compose.yml for multi-service local development.',
|
|
110
|
+
template: null
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
ciPipeline: {
|
|
114
|
+
id: 260,
|
|
115
|
+
name: 'CI pipeline configured',
|
|
116
|
+
check: (ctx) => ctx.hasDir('.github/workflows') || ctx.hasDir('.circleci') ||
|
|
117
|
+
ctx.files.includes('.gitlab-ci.yml') || ctx.files.includes('Jenkinsfile') ||
|
|
118
|
+
ctx.files.includes('.travis.yml') || ctx.files.includes('bitbucket-pipelines.yml'),
|
|
119
|
+
impact: 'high',
|
|
120
|
+
rating: 4,
|
|
121
|
+
category: 'devops',
|
|
122
|
+
fix: 'Add a CI pipeline (GitHub Actions, GitLab CI, CircleCI, etc.) for automated testing and deployment.',
|
|
123
|
+
template: null
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
terraformFiles: {
|
|
127
|
+
id: 397,
|
|
128
|
+
name: 'Infrastructure as Code (Terraform)',
|
|
129
|
+
check: (ctx) => ctx.files.some(f => /\.tf$/.test(f)) || ctx.files.includes('main.tf'),
|
|
130
|
+
impact: 'medium',
|
|
131
|
+
rating: 3,
|
|
132
|
+
category: 'devops',
|
|
133
|
+
fix: 'Add Terraform files for infrastructure-as-code management.',
|
|
134
|
+
template: null
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
dockerMultiStage: {
|
|
138
|
+
id: 39902,
|
|
139
|
+
name: 'Dockerfile uses multi-stage build',
|
|
140
|
+
check: (ctx) => {
|
|
141
|
+
const df = findProjectFiles(ctx, /^Dockerfile$/i);
|
|
142
|
+
if (df.length === 0) return null;
|
|
143
|
+
const content = ctx.fileContent(df[0]) || '';
|
|
144
|
+
return (content.match(/^FROM\s/gim) || []).length >= 2;
|
|
145
|
+
},
|
|
146
|
+
impact: 'medium',
|
|
147
|
+
rating: 3,
|
|
148
|
+
category: 'devops',
|
|
149
|
+
fix: 'Use multi-stage builds in Dockerfile to reduce image size and avoid leaking build tools into production.',
|
|
150
|
+
template: null
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
dockerignoreExists: {
|
|
154
|
+
id: 39903,
|
|
155
|
+
name: '.dockerignore includes node_modules and .env',
|
|
156
|
+
check: (ctx) => {
|
|
157
|
+
if (!ctx.files.some(f => /^Dockerfile/i.test(f))) return null;
|
|
158
|
+
const di = ctx.fileContent('.dockerignore') || '';
|
|
159
|
+
return di.includes('node_modules') && /\.env/i.test(di);
|
|
160
|
+
},
|
|
161
|
+
impact: 'high',
|
|
162
|
+
rating: 4,
|
|
163
|
+
category: 'devops',
|
|
164
|
+
fix: 'Add .dockerignore with node_modules, .env, and other sensitive/large files to keep images small and secure.',
|
|
165
|
+
template: null
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
dockerNoSecrets: {
|
|
169
|
+
id: 39904,
|
|
170
|
+
name: 'Dockerfile has no secrets in build args',
|
|
171
|
+
check: (ctx) => {
|
|
172
|
+
const df = findProjectFiles(ctx, /^Dockerfile$/i);
|
|
173
|
+
if (df.length === 0) return null;
|
|
174
|
+
const content = ctx.fileContent(df[0]) || '';
|
|
175
|
+
return !/ARG\s+(PASSWORD|SECRET|TOKEN|API_KEY|PRIVATE_KEY)/i.test(content);
|
|
176
|
+
},
|
|
177
|
+
impact: 'critical',
|
|
178
|
+
rating: 5,
|
|
179
|
+
category: 'devops',
|
|
180
|
+
fix: 'Never pass secrets via ARG in Dockerfile — use runtime environment variables or secret mounts instead.',
|
|
181
|
+
template: null
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
terraformFmt: {
|
|
185
|
+
id: 39705,
|
|
186
|
+
name: 'Terraform formatting configured',
|
|
187
|
+
check: (ctx) => {
|
|
188
|
+
if (!ctx.files.some(f => /\.tf$/.test(f))) return null;
|
|
189
|
+
const ci = readProjectFiles(ctx, /\.(yml|yaml)$/i, 10);
|
|
190
|
+
const makefileContent = ctx.fileContent('Makefile') || '';
|
|
191
|
+
const preCommit = ctx.fileContent('.pre-commit-config.yaml') || '';
|
|
192
|
+
return /terraform\s+fmt/i.test(ci) || /terraform\s+fmt/i.test(makefileContent) || /terraform_fmt/i.test(preCommit);
|
|
193
|
+
},
|
|
194
|
+
impact: 'medium',
|
|
195
|
+
rating: 3,
|
|
196
|
+
category: 'devops',
|
|
197
|
+
fix: 'Add `terraform fmt` to CI or pre-commit hooks to enforce consistent formatting.',
|
|
198
|
+
template: null
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
terraformDirIgnored: {
|
|
202
|
+
id: 39706,
|
|
203
|
+
name: '.terraform directory in .gitignore',
|
|
204
|
+
check: (ctx) => {
|
|
205
|
+
if (!ctx.files.some(f => /\.tf$/.test(f))) return null;
|
|
206
|
+
const gi = ctx.fileContent('.gitignore') || '';
|
|
207
|
+
return /\.terraform/i.test(gi);
|
|
208
|
+
},
|
|
209
|
+
impact: 'high',
|
|
210
|
+
rating: 4,
|
|
211
|
+
category: 'devops',
|
|
212
|
+
fix: 'Add .terraform/ to .gitignore — it contains provider binaries and should not be committed.',
|
|
213
|
+
template: null
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
terraformStateNotCommitted: {
|
|
217
|
+
id: 39707,
|
|
218
|
+
name: 'Terraform state file not committed',
|
|
219
|
+
check: (ctx) => {
|
|
220
|
+
if (!ctx.files.some(f => /\.tf$/.test(f))) return null;
|
|
221
|
+
return !ctx.files.some(f => /terraform\.tfstate$/i.test(f));
|
|
222
|
+
},
|
|
223
|
+
impact: 'critical',
|
|
224
|
+
rating: 5,
|
|
225
|
+
category: 'devops',
|
|
226
|
+
fix: 'Never commit terraform.tfstate — it may contain secrets. Use a remote backend (S3, GCS, Terraform Cloud).',
|
|
227
|
+
template: null
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
terraformBackendConfigured: {
|
|
231
|
+
id: 39708,
|
|
232
|
+
name: 'Terraform remote backend configured',
|
|
233
|
+
check: (ctx) => {
|
|
234
|
+
const tfFiles = findProjectFiles(ctx, /\.tf$/);
|
|
235
|
+
if (tfFiles.length === 0) return null;
|
|
236
|
+
const allTf = tfFiles.slice(0, 10).map(f => ctx.fileContent(f) || '').join('\n');
|
|
237
|
+
return /backend\s+"(s3|gcs|azurerm|remote|cloud|consul|http)"/i.test(allTf);
|
|
238
|
+
},
|
|
239
|
+
impact: 'high',
|
|
240
|
+
rating: 4,
|
|
241
|
+
category: 'devops',
|
|
242
|
+
fix: 'Configure a remote backend in Terraform (S3, GCS, Terraform Cloud) for team collaboration and state locking.',
|
|
243
|
+
template: null
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
githubActionsOrCI: {
|
|
247
|
+
id: 2021,
|
|
248
|
+
name: 'GitHub Actions or CI configured',
|
|
249
|
+
check: (ctx) => {
|
|
250
|
+
return ctx.hasDir('.github/workflows') || !!ctx.fileContent('.circleci/config.yml') ||
|
|
251
|
+
!!ctx.fileContent('.gitlab-ci.yml') || !!ctx.fileContent('Jenkinsfile') ||
|
|
252
|
+
!!ctx.fileContent('.travis.yml') || !!ctx.fileContent('bitbucket-pipelines.yml');
|
|
253
|
+
},
|
|
254
|
+
impact: 'medium', rating: 3, category: 'devops',
|
|
255
|
+
fix: 'Add CI pipeline for automated testing. Claude Code has a GitHub Action for audit gates.',
|
|
256
|
+
template: null
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
multipleHookTypes: {
|
|
260
|
+
id: 2024,
|
|
261
|
+
name: '2+ hook event types configured',
|
|
262
|
+
check: (ctx) => {
|
|
263
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
264
|
+
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
265
|
+
const hooks = { ...(shared.hooks || {}), ...(local.hooks || {}) };
|
|
266
|
+
return Object.keys(hooks).length >= 2;
|
|
267
|
+
},
|
|
268
|
+
impact: 'medium', rating: 3, category: 'automation',
|
|
269
|
+
fix: 'Add at least 2 hook types (e.g. PostToolUse for linting + SessionStart for initialization).',
|
|
270
|
+
template: null
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
stopFailureHook: {
|
|
274
|
+
id: 2025,
|
|
275
|
+
name: 'StopFailure hook for error tracking',
|
|
276
|
+
check: (ctx) => {
|
|
277
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
278
|
+
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
279
|
+
// StopFailure = error stop (API errors), Stop = normal completion — both useful but different
|
|
280
|
+
return !!(shared.hooks?.StopFailure || local.hooks?.StopFailure);
|
|
281
|
+
},
|
|
282
|
+
impact: 'low', rating: 3, category: 'automation',
|
|
283
|
+
fix: 'Add a StopFailure hook to log API errors and unexpected stops. Note: StopFailure (errors) is different from Stop (normal completion).',
|
|
284
|
+
template: null
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
hooksNotificationEvent: {
|
|
288
|
+
id: 2033,
|
|
289
|
+
name: 'Notification hook for alerts',
|
|
290
|
+
check: (ctx) => {
|
|
291
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
292
|
+
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
293
|
+
return !!(shared.hooks?.Notification || local.hooks?.Notification);
|
|
294
|
+
},
|
|
295
|
+
impact: 'low',
|
|
296
|
+
rating: 2,
|
|
297
|
+
category: 'automation',
|
|
298
|
+
fix: 'Add a Notification hook to capture alerts and status updates from Claude during long tasks.',
|
|
299
|
+
template: null
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
subagentStopHook: {
|
|
303
|
+
id: 2034,
|
|
304
|
+
name: 'SubagentStop hook for delegation tracking',
|
|
305
|
+
check: (ctx) => {
|
|
306
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
307
|
+
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
308
|
+
return !!(shared.hooks?.SubagentStop || local.hooks?.SubagentStop);
|
|
309
|
+
},
|
|
310
|
+
impact: 'low',
|
|
311
|
+
rating: 2,
|
|
312
|
+
category: 'automation',
|
|
313
|
+
fix: 'Add a SubagentStop hook to track when delegated subagent tasks complete.',
|
|
314
|
+
template: null
|
|
315
|
+
},
|
|
316
|
+
};
|