@nerviq/cli 1.18.0 → 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 +2 -2
- package/bin/cli.js +130 -130
- package/package.json +1 -1
- 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/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/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 -783
- 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
package/src/techniques/shared.js
CHANGED
|
@@ -1,443 +1,443 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared helpers for Claude technique modules.
|
|
3
|
-
* Generated mechanically from the legacy monolith during HR-09.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const { collectClaudeDenyRules } = require('../permission-rules');
|
|
9
|
-
const {
|
|
10
|
-
hasPromptInjectionDefenseGuidance,
|
|
11
|
-
hasMcpPromptInjectionDefenseGuidance,
|
|
12
|
-
hasInjectionDefenseHookConfigured,
|
|
13
|
-
} = require('../prompt-injection');
|
|
14
|
-
const {
|
|
15
|
-
getClaudeInstructionBundle,
|
|
16
|
-
getRepoInstructionBundle,
|
|
17
|
-
hasDocumentedVerificationGuidance,
|
|
18
|
-
hasDocumentedTestCommand,
|
|
19
|
-
hasDocumentedLintCommand,
|
|
20
|
-
hasDocumentedBuildCommand,
|
|
21
|
-
} = require('../instruction-surfaces');
|
|
22
|
-
|
|
23
|
-
function hasFrontendSignals(ctx) {
|
|
24
|
-
const pkg = ctx.fileContent('package.json') || '';
|
|
25
|
-
return /react|vue|angular|next|svelte|tailwind|vite|astro/i.test(pkg) ||
|
|
26
|
-
ctx.files.some(f => /tailwind\.config|vite\.config|next\.config|svelte\.config|nuxt\.config|pages\/|components\/|app\//i.test(f));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function getClaudeHookContents(ctx) {
|
|
30
|
-
const hookFiles = ctx.dirFiles('.claude/hooks').filter(f => /\.(js|cjs|mjs|ts|sh|py)$/.test(f));
|
|
31
|
-
return hookFiles.map(f => ctx.fileContent(`.claude/hooks/${f}`) || '');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function matchesPattern(value, pattern) {
|
|
35
|
-
if (pattern instanceof RegExp) {
|
|
36
|
-
pattern.lastIndex = 0;
|
|
37
|
-
return pattern.test(value);
|
|
38
|
-
}
|
|
39
|
-
return value === pattern;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function getProjectEntries(ctx) {
|
|
43
|
-
if (ctx.__nerviqProjectEntries) return ctx.__nerviqProjectEntries;
|
|
44
|
-
|
|
45
|
-
const entries = [];
|
|
46
|
-
const skippedDirs = new Set([
|
|
47
|
-
'.git',
|
|
48
|
-
'.hg',
|
|
49
|
-
'.svn',
|
|
50
|
-
'node_modules',
|
|
51
|
-
'__pycache__',
|
|
52
|
-
'.pytest_cache',
|
|
53
|
-
'.mypy_cache',
|
|
54
|
-
'.ruff_cache',
|
|
55
|
-
'.venv',
|
|
56
|
-
'venv',
|
|
57
|
-
'env',
|
|
58
|
-
'.tox',
|
|
59
|
-
'.nox',
|
|
60
|
-
'vendor',
|
|
61
|
-
'dist',
|
|
62
|
-
'build',
|
|
63
|
-
'coverage',
|
|
64
|
-
]);
|
|
65
|
-
|
|
66
|
-
const walk = (relPath = '') => {
|
|
67
|
-
const fullPath = relPath
|
|
68
|
-
? path.join(ctx.dir, ...relPath.split('/'))
|
|
69
|
-
: ctx.dir;
|
|
70
|
-
|
|
71
|
-
let dirents = [];
|
|
72
|
-
try {
|
|
73
|
-
dirents = fs.readdirSync(fullPath, { withFileTypes: true });
|
|
74
|
-
} catch {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
for (const dirent of dirents) {
|
|
79
|
-
if (dirent.name === '.DS_Store') continue;
|
|
80
|
-
|
|
81
|
-
const entryPath = relPath ? `${relPath}/${dirent.name}` : dirent.name;
|
|
82
|
-
if (dirent.isDirectory()) {
|
|
83
|
-
if (skippedDirs.has(dirent.name)) continue;
|
|
84
|
-
entries.push(`${entryPath}/`);
|
|
85
|
-
walk(entryPath);
|
|
86
|
-
} else {
|
|
87
|
-
entries.push(entryPath);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
walk();
|
|
93
|
-
ctx.__nerviqProjectEntries = entries;
|
|
94
|
-
return entries;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function getProjectFiles(ctx) {
|
|
98
|
-
if (ctx.__nerviqProjectFiles) return ctx.__nerviqProjectFiles;
|
|
99
|
-
ctx.__nerviqProjectFiles = getProjectEntries(ctx).filter(entry => !entry.endsWith('/'));
|
|
100
|
-
return ctx.__nerviqProjectFiles;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function findProjectFiles(ctx, pattern) {
|
|
104
|
-
return getProjectFiles(ctx).filter(file => matchesPattern(file, pattern));
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function hasProjectFile(ctx, pattern) {
|
|
108
|
-
return findProjectFiles(ctx, pattern).length > 0;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Check if a stack-indicator file exists at a "core" location (root, src/, lib/, app/, packages/)
|
|
113
|
-
* rather than inside examples/, docs/, test/, vendor/, or deeply nested paths.
|
|
114
|
-
* This prevents false stack detection from example/demo code.
|
|
115
|
-
*/
|
|
116
|
-
const EXCLUDED_STACK_DIRS = /^(examples?|docs?|test|tests|fixtures?|samples?|demo|vendor|third[_-]?party|\.github)\//i;
|
|
117
|
-
|
|
118
|
-
function hasCoreProjectFile(ctx, pattern) {
|
|
119
|
-
return findProjectFiles(ctx, pattern).some(file => !EXCLUDED_STACK_DIRS.test(file));
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function hasCoreRootFile(ctx, pattern) {
|
|
123
|
-
// Only match files at project root (no / in path) or one level deep (src/X, lib/X, app/X)
|
|
124
|
-
return findProjectFiles(ctx, pattern).some(file => {
|
|
125
|
-
if (EXCLUDED_STACK_DIRS.test(file)) return false;
|
|
126
|
-
const depth = (file.match(/\//g) || []).length;
|
|
127
|
-
return depth <= 1;
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function readProjectFiles(ctx, pattern, limit = 60) {
|
|
132
|
-
return findProjectFiles(ctx, pattern)
|
|
133
|
-
.slice(0, limit)
|
|
134
|
-
.map(file => ctx.fileContent(file) || '')
|
|
135
|
-
.filter(Boolean)
|
|
136
|
-
.join('\n');
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function isPythonProject(ctx) {
|
|
140
|
-
if (ctx.__nerviqIsPython !== undefined) return ctx.__nerviqIsPython;
|
|
141
|
-
// Require a Python config file (pyproject.toml, requirements.txt, setup.py) at a core location.
|
|
142
|
-
// Stray .py files in examples/ or docs/ don't make it a Python project.
|
|
143
|
-
ctx.__nerviqIsPython =
|
|
144
|
-
hasCoreRootFile(ctx, /(^|\/)(pyproject\.toml|setup\.py|Pipfile)$/i) ||
|
|
145
|
-
hasCoreRootFile(ctx, /(^|\/)requirements\.txt$/i);
|
|
146
|
-
return ctx.__nerviqIsPython;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function isGoProject(ctx) {
|
|
150
|
-
if (ctx.__nerviqIsGo !== undefined) return ctx.__nerviqIsGo;
|
|
151
|
-
ctx.__nerviqIsGo = hasCoreRootFile(ctx, /(^|\/)go\.mod$/i);
|
|
152
|
-
return ctx.__nerviqIsGo;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function isRustProject(ctx) {
|
|
156
|
-
if (ctx.__nerviqIsRust !== undefined) return ctx.__nerviqIsRust;
|
|
157
|
-
ctx.__nerviqIsRust = hasCoreRootFile(ctx, /(^|\/)Cargo\.toml$/i);
|
|
158
|
-
return ctx.__nerviqIsRust;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function isJavaProject(ctx) {
|
|
162
|
-
if (ctx.__nerviqIsJava !== undefined) return ctx.__nerviqIsJava;
|
|
163
|
-
ctx.__nerviqIsJava = hasCoreRootFile(ctx, /(^|\/)(pom\.xml|build\.gradle|build\.gradle\.kts)$/i);
|
|
164
|
-
return ctx.__nerviqIsJava;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function isFlutterProject(ctx) {
|
|
168
|
-
if (ctx.__nerviqIsFlutter !== undefined) return ctx.__nerviqIsFlutter;
|
|
169
|
-
ctx.__nerviqIsFlutter = hasCoreRootFile(ctx, /(^|\/)pubspec\.yaml$/i);
|
|
170
|
-
return ctx.__nerviqIsFlutter;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function isSwiftProject(ctx) {
|
|
174
|
-
if (ctx.__nerviqIsSwift !== undefined) return ctx.__nerviqIsSwift;
|
|
175
|
-
ctx.__nerviqIsSwift = hasCoreRootFile(ctx, /(^|\/)Package\.swift$/i) ||
|
|
176
|
-
hasCoreProjectFile(ctx, /\.xcodeproj/i);
|
|
177
|
-
return ctx.__nerviqIsSwift;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function isKotlinProject(ctx) {
|
|
181
|
-
if (ctx.__nerviqIsKotlin !== undefined) return ctx.__nerviqIsKotlin;
|
|
182
|
-
const gradle = (ctx.fileContent('build.gradle.kts') || '') + (ctx.fileContent('build.gradle') || '');
|
|
183
|
-
ctx.__nerviqIsKotlin = /kotlin/i.test(gradle);
|
|
184
|
-
return ctx.__nerviqIsKotlin;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function isRubyProject(ctx) {
|
|
188
|
-
if (ctx.__nerviqIsRuby !== undefined) return ctx.__nerviqIsRuby;
|
|
189
|
-
ctx.__nerviqIsRuby = hasCoreRootFile(ctx, /(^|\/)Gemfile$/i);
|
|
190
|
-
return ctx.__nerviqIsRuby;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function isPhpProject(ctx) {
|
|
194
|
-
if (ctx.__nerviqIsPhp !== undefined) return ctx.__nerviqIsPhp;
|
|
195
|
-
ctx.__nerviqIsPhp = hasCoreRootFile(ctx, /(^|\/)composer\.json$/i);
|
|
196
|
-
return ctx.__nerviqIsPhp;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function isDotnetProject(ctx) {
|
|
200
|
-
if (ctx.__nerviqIsDotnet !== undefined) return ctx.__nerviqIsDotnet;
|
|
201
|
-
ctx.__nerviqIsDotnet = hasCoreProjectFile(ctx, /(^|\/)(.*\.csproj|.*\.sln|global\.json)$/i);
|
|
202
|
-
return ctx.__nerviqIsDotnet;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Map category names to their project detection function.
|
|
207
|
-
* Used by the audit to skip entire categories when the stack isn't detected.
|
|
208
|
-
*/
|
|
209
|
-
const STACK_CATEGORY_DETECTORS = {
|
|
210
|
-
python: isPythonProject,
|
|
211
|
-
go: isGoProject,
|
|
212
|
-
rust: isRustProject,
|
|
213
|
-
java: isJavaProject,
|
|
214
|
-
flutter: isFlutterProject,
|
|
215
|
-
swift: isSwiftProject,
|
|
216
|
-
kotlin: isKotlinProject,
|
|
217
|
-
ruby: isRubyProject,
|
|
218
|
-
php: isPhpProject,
|
|
219
|
-
dotnet: isDotnetProject,
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
function getPythonFiles(ctx) {
|
|
223
|
-
if (ctx.__nerviqPythonFiles) return ctx.__nerviqPythonFiles;
|
|
224
|
-
ctx.__nerviqPythonFiles = findProjectFiles(ctx, /\.py$/i);
|
|
225
|
-
return ctx.__nerviqPythonFiles;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function getMainPythonFiles(ctx) {
|
|
229
|
-
if (ctx.__nerviqMainPythonFiles) return ctx.__nerviqMainPythonFiles;
|
|
230
|
-
ctx.__nerviqMainPythonFiles = getPythonFiles(ctx)
|
|
231
|
-
.filter(file => !/(^|\/)(tests?|__pycache__|migrations)\//i.test(file))
|
|
232
|
-
.filter(file => !/(^|\/)(test_[^/]+|conftest)\.py$/i.test(file))
|
|
233
|
-
.slice(0, 50);
|
|
234
|
-
return ctx.__nerviqMainPythonFiles;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function getPythonProjectText(ctx) {
|
|
238
|
-
if (ctx.__nerviqPythonProjectText) return ctx.__nerviqPythonProjectText;
|
|
239
|
-
ctx.__nerviqPythonProjectText = [
|
|
240
|
-
readProjectFiles(ctx, /(^|\/)pyproject\.toml$/i),
|
|
241
|
-
readProjectFiles(ctx, /(^|\/)requirements[^/]*\.txt$/i),
|
|
242
|
-
readProjectFiles(ctx, /(^|\/)setup\.py$/i),
|
|
243
|
-
readProjectFiles(ctx, /(^|\/)setup\.cfg$/i),
|
|
244
|
-
readProjectFiles(ctx, /(^|\/)Pipfile$/i),
|
|
245
|
-
].filter(Boolean).join('\n');
|
|
246
|
-
return ctx.__nerviqPythonProjectText;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function getGoFiles(ctx) {
|
|
250
|
-
if (ctx.__nerviqGoFiles) return ctx.__nerviqGoFiles;
|
|
251
|
-
ctx.__nerviqGoFiles = findProjectFiles(ctx, /\.go$/i);
|
|
252
|
-
return ctx.__nerviqGoFiles;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function getRustFiles(ctx) {
|
|
256
|
-
if (ctx.__nerviqRustFiles) return ctx.__nerviqRustFiles;
|
|
257
|
-
ctx.__nerviqRustFiles = findProjectFiles(ctx, /\.rs$/i);
|
|
258
|
-
return ctx.__nerviqRustFiles;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
function getMainRustFiles(ctx) {
|
|
262
|
-
if (ctx.__nerviqMainRustFiles) return ctx.__nerviqMainRustFiles;
|
|
263
|
-
ctx.__nerviqMainRustFiles = getRustFiles(ctx)
|
|
264
|
-
.filter(file => !/(^|\/)(tests|target)\//i.test(file))
|
|
265
|
-
.slice(0, 60);
|
|
266
|
-
return ctx.__nerviqMainRustFiles;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function getJavaFiles(ctx) {
|
|
270
|
-
if (ctx.__nerviqJavaFiles) return ctx.__nerviqJavaFiles;
|
|
271
|
-
ctx.__nerviqJavaFiles = findProjectFiles(ctx, /\.java$/i);
|
|
272
|
-
return ctx.__nerviqJavaFiles;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function getMainJavaFiles(ctx) {
|
|
276
|
-
if (ctx.__nerviqMainJavaFiles) return ctx.__nerviqMainJavaFiles;
|
|
277
|
-
ctx.__nerviqMainJavaFiles = getJavaFiles(ctx)
|
|
278
|
-
.filter(file => !/(^|\/)(test|tests|src\/test)\//i.test(file))
|
|
279
|
-
.slice(0, 60);
|
|
280
|
-
return ctx.__nerviqMainJavaFiles;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function getMainGoFiles(ctx) {
|
|
284
|
-
if (ctx.__nerviqMainGoFiles) return ctx.__nerviqMainGoFiles;
|
|
285
|
-
ctx.__nerviqMainGoFiles = getGoFiles(ctx).filter(file => !/_test\.go$/i.test(file)).slice(0, 60);
|
|
286
|
-
return ctx.__nerviqMainGoFiles;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function getWorkflowContent(ctx) {
|
|
290
|
-
if (ctx.__nerviqWorkflowContent !== undefined) return ctx.__nerviqWorkflowContent;
|
|
291
|
-
ctx.__nerviqWorkflowContent = readProjectFiles(ctx, /^\.github\/workflows\/.*\.ya?ml$/i);
|
|
292
|
-
return ctx.__nerviqWorkflowContent;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
function getPreCommitContent(ctx) {
|
|
296
|
-
if (ctx.__nerviqPreCommitContent !== undefined) return ctx.__nerviqPreCommitContent;
|
|
297
|
-
ctx.__nerviqPreCommitContent = readProjectFiles(ctx, /(^|\/)\.pre-commit-config\.ya?ml$/i);
|
|
298
|
-
return ctx.__nerviqPreCommitContent;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function getGoProjectText(ctx) {
|
|
302
|
-
if (ctx.__nerviqGoProjectText) return ctx.__nerviqGoProjectText;
|
|
303
|
-
ctx.__nerviqGoProjectText = [
|
|
304
|
-
readProjectFiles(ctx, /(^|\/)go\.mod$/i),
|
|
305
|
-
getWorkflowContent(ctx),
|
|
306
|
-
readProjectFiles(ctx, /(^|\/)Makefile$/),
|
|
307
|
-
getPreCommitContent(ctx),
|
|
308
|
-
getMainGoFiles(ctx).slice(0, 25).map(file => ctx.fileContent(file) || '').filter(Boolean).join('\n'),
|
|
309
|
-
].filter(Boolean).join('\n');
|
|
310
|
-
return ctx.__nerviqGoProjectText;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function getRustProjectText(ctx) {
|
|
314
|
-
if (ctx.__nerviqRustProjectText) return ctx.__nerviqRustProjectText;
|
|
315
|
-
ctx.__nerviqRustProjectText = [
|
|
316
|
-
readProjectFiles(ctx, /(^|\/)Cargo\.toml$/i),
|
|
317
|
-
readProjectFiles(ctx, /(^|\/)(clippy\.toml|\.clippy\.toml|rustfmt\.toml|\.rustfmt\.toml|build\.rs)$/i),
|
|
318
|
-
readProjectFiles(ctx, /(^|\/)\.cargo\/config\.toml$/i),
|
|
319
|
-
getWorkflowContent(ctx),
|
|
320
|
-
getMainRustFiles(ctx).slice(0, 30).map(file => ctx.fileContent(file) || '').filter(Boolean).join('\n'),
|
|
321
|
-
].filter(Boolean).join('\n');
|
|
322
|
-
return ctx.__nerviqRustProjectText;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function getJavaBuildText(ctx) {
|
|
326
|
-
if (ctx.__nerviqJavaBuildText) return ctx.__nerviqJavaBuildText;
|
|
327
|
-
ctx.__nerviqJavaBuildText = [
|
|
328
|
-
readProjectFiles(ctx, /(^|\/)pom\.xml$/i),
|
|
329
|
-
readProjectFiles(ctx, /(^|\/)build\.gradle$/i),
|
|
330
|
-
readProjectFiles(ctx, /(^|\/)build\.gradle\.kts$/i),
|
|
331
|
-
readProjectFiles(ctx, /(^|\/)settings\.gradle$/i),
|
|
332
|
-
readProjectFiles(ctx, /(^|\/)settings\.gradle\.kts$/i),
|
|
333
|
-
].filter(Boolean).join('\n');
|
|
334
|
-
return ctx.__nerviqJavaBuildText;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
function getJavaProjectText(ctx) {
|
|
338
|
-
if (ctx.__nerviqJavaProjectText) return ctx.__nerviqJavaProjectText;
|
|
339
|
-
ctx.__nerviqJavaProjectText = [
|
|
340
|
-
getJavaBuildText(ctx),
|
|
341
|
-
getWorkflowContent(ctx),
|
|
342
|
-
readProjectFiles(ctx, /(^|\/)\.editorconfig$/i),
|
|
343
|
-
readProjectFiles(ctx, /(^|\/)(application\.properties|application\.ya?ml|logback.*\.xml|log4j2?.*\.xml)$/i),
|
|
344
|
-
getMainJavaFiles(ctx).slice(0, 30).map(file => ctx.fileContent(file) || '').filter(Boolean).join('\n'),
|
|
345
|
-
].filter(Boolean).join('\n');
|
|
346
|
-
return ctx.__nerviqJavaProjectText;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
function getGoInterfaceBlocks(ctx) {
|
|
350
|
-
if (ctx.__nerviqGoInterfaces) return ctx.__nerviqGoInterfaces;
|
|
351
|
-
const blocks = [];
|
|
352
|
-
for (const file of getMainGoFiles(ctx)) {
|
|
353
|
-
const content = ctx.fileContent(file) || '';
|
|
354
|
-
for (const match of content.matchAll(/type\s+\w+\s+interface\s*\{([\s\S]*?)\}/g)) {
|
|
355
|
-
blocks.push(match[1]);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
ctx.__nerviqGoInterfaces = blocks;
|
|
359
|
-
return ctx.__nerviqGoInterfaces;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function countGoInterfaceMethods(block) {
|
|
363
|
-
return block
|
|
364
|
-
.split(/\r?\n/)
|
|
365
|
-
.map(line => line.trim())
|
|
366
|
-
.filter(line => line && !line.startsWith('//') && !line.startsWith('/*'))
|
|
367
|
-
.length;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const { containsEmbeddedSecret } = require('../secret-patterns');
|
|
371
|
-
const { attachSourceUrls } = require('../source-urls');
|
|
372
|
-
const { buildSupplementalChecks } = require('../supplemental-checks');
|
|
373
|
-
const { resolveProjectStateReadPath } = require('../state-paths');
|
|
374
|
-
|
|
375
|
-
const CLAUDE_SUPPLEMENTAL_SOURCE_URLS = {
|
|
376
|
-
'testing-strategy': 'https://code.claude.com/docs/en/common-workflows',
|
|
377
|
-
'code-quality': 'https://code.claude.com/docs/en/best-practices',
|
|
378
|
-
'api-design': 'https://code.claude.com/docs/en/best-practices',
|
|
379
|
-
database: 'https://code.claude.com/docs/en/common-workflows',
|
|
380
|
-
authentication: 'https://code.claude.com/docs/en/permissions',
|
|
381
|
-
monitoring: 'https://code.claude.com/docs/en/common-workflows',
|
|
382
|
-
'dependency-management': 'https://code.claude.com/docs/en/best-practices',
|
|
383
|
-
'cost-optimization': 'https://code.claude.com/docs/en/memory',
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
module.exports = {
|
|
387
|
-
fs,
|
|
388
|
-
path,
|
|
389
|
-
collectClaudeDenyRules,
|
|
390
|
-
hasPromptInjectionDefenseGuidance,
|
|
391
|
-
hasMcpPromptInjectionDefenseGuidance,
|
|
392
|
-
hasInjectionDefenseHookConfigured,
|
|
393
|
-
getClaudeInstructionBundle,
|
|
394
|
-
getRepoInstructionBundle,
|
|
395
|
-
hasDocumentedVerificationGuidance,
|
|
396
|
-
hasDocumentedTestCommand,
|
|
397
|
-
hasDocumentedLintCommand,
|
|
398
|
-
hasDocumentedBuildCommand,
|
|
399
|
-
hasFrontendSignals,
|
|
400
|
-
getClaudeHookContents,
|
|
401
|
-
matchesPattern,
|
|
402
|
-
getProjectEntries,
|
|
403
|
-
getProjectFiles,
|
|
404
|
-
findProjectFiles,
|
|
405
|
-
hasProjectFile,
|
|
406
|
-
EXCLUDED_STACK_DIRS,
|
|
407
|
-
hasCoreProjectFile,
|
|
408
|
-
hasCoreRootFile,
|
|
409
|
-
readProjectFiles,
|
|
410
|
-
isPythonProject,
|
|
411
|
-
isGoProject,
|
|
412
|
-
isRustProject,
|
|
413
|
-
isJavaProject,
|
|
414
|
-
isFlutterProject,
|
|
415
|
-
isSwiftProject,
|
|
416
|
-
isKotlinProject,
|
|
417
|
-
isRubyProject,
|
|
418
|
-
isPhpProject,
|
|
419
|
-
isDotnetProject,
|
|
420
|
-
STACK_CATEGORY_DETECTORS,
|
|
421
|
-
getPythonFiles,
|
|
422
|
-
getMainPythonFiles,
|
|
423
|
-
getPythonProjectText,
|
|
424
|
-
getGoFiles,
|
|
425
|
-
getRustFiles,
|
|
426
|
-
getMainRustFiles,
|
|
427
|
-
getJavaFiles,
|
|
428
|
-
getMainJavaFiles,
|
|
429
|
-
getMainGoFiles,
|
|
430
|
-
getWorkflowContent,
|
|
431
|
-
getPreCommitContent,
|
|
432
|
-
getGoProjectText,
|
|
433
|
-
getRustProjectText,
|
|
434
|
-
getJavaBuildText,
|
|
435
|
-
getJavaProjectText,
|
|
436
|
-
getGoInterfaceBlocks,
|
|
437
|
-
countGoInterfaceMethods,
|
|
438
|
-
containsEmbeddedSecret,
|
|
439
|
-
attachSourceUrls,
|
|
440
|
-
buildSupplementalChecks,
|
|
441
|
-
resolveProjectStateReadPath,
|
|
442
|
-
CLAUDE_SUPPLEMENTAL_SOURCE_URLS,
|
|
443
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for Claude technique modules.
|
|
3
|
+
* Generated mechanically from the legacy monolith during HR-09.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { collectClaudeDenyRules } = require('../permission-rules');
|
|
9
|
+
const {
|
|
10
|
+
hasPromptInjectionDefenseGuidance,
|
|
11
|
+
hasMcpPromptInjectionDefenseGuidance,
|
|
12
|
+
hasInjectionDefenseHookConfigured,
|
|
13
|
+
} = require('../prompt-injection');
|
|
14
|
+
const {
|
|
15
|
+
getClaudeInstructionBundle,
|
|
16
|
+
getRepoInstructionBundle,
|
|
17
|
+
hasDocumentedVerificationGuidance,
|
|
18
|
+
hasDocumentedTestCommand,
|
|
19
|
+
hasDocumentedLintCommand,
|
|
20
|
+
hasDocumentedBuildCommand,
|
|
21
|
+
} = require('../instruction-surfaces');
|
|
22
|
+
|
|
23
|
+
function hasFrontendSignals(ctx) {
|
|
24
|
+
const pkg = ctx.fileContent('package.json') || '';
|
|
25
|
+
return /react|vue|angular|next|svelte|tailwind|vite|astro/i.test(pkg) ||
|
|
26
|
+
ctx.files.some(f => /tailwind\.config|vite\.config|next\.config|svelte\.config|nuxt\.config|pages\/|components\/|app\//i.test(f));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getClaudeHookContents(ctx) {
|
|
30
|
+
const hookFiles = ctx.dirFiles('.claude/hooks').filter(f => /\.(js|cjs|mjs|ts|sh|py)$/.test(f));
|
|
31
|
+
return hookFiles.map(f => ctx.fileContent(`.claude/hooks/${f}`) || '');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function matchesPattern(value, pattern) {
|
|
35
|
+
if (pattern instanceof RegExp) {
|
|
36
|
+
pattern.lastIndex = 0;
|
|
37
|
+
return pattern.test(value);
|
|
38
|
+
}
|
|
39
|
+
return value === pattern;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getProjectEntries(ctx) {
|
|
43
|
+
if (ctx.__nerviqProjectEntries) return ctx.__nerviqProjectEntries;
|
|
44
|
+
|
|
45
|
+
const entries = [];
|
|
46
|
+
const skippedDirs = new Set([
|
|
47
|
+
'.git',
|
|
48
|
+
'.hg',
|
|
49
|
+
'.svn',
|
|
50
|
+
'node_modules',
|
|
51
|
+
'__pycache__',
|
|
52
|
+
'.pytest_cache',
|
|
53
|
+
'.mypy_cache',
|
|
54
|
+
'.ruff_cache',
|
|
55
|
+
'.venv',
|
|
56
|
+
'venv',
|
|
57
|
+
'env',
|
|
58
|
+
'.tox',
|
|
59
|
+
'.nox',
|
|
60
|
+
'vendor',
|
|
61
|
+
'dist',
|
|
62
|
+
'build',
|
|
63
|
+
'coverage',
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
const walk = (relPath = '') => {
|
|
67
|
+
const fullPath = relPath
|
|
68
|
+
? path.join(ctx.dir, ...relPath.split('/'))
|
|
69
|
+
: ctx.dir;
|
|
70
|
+
|
|
71
|
+
let dirents = [];
|
|
72
|
+
try {
|
|
73
|
+
dirents = fs.readdirSync(fullPath, { withFileTypes: true });
|
|
74
|
+
} catch {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (const dirent of dirents) {
|
|
79
|
+
if (dirent.name === '.DS_Store') continue;
|
|
80
|
+
|
|
81
|
+
const entryPath = relPath ? `${relPath}/${dirent.name}` : dirent.name;
|
|
82
|
+
if (dirent.isDirectory()) {
|
|
83
|
+
if (skippedDirs.has(dirent.name)) continue;
|
|
84
|
+
entries.push(`${entryPath}/`);
|
|
85
|
+
walk(entryPath);
|
|
86
|
+
} else {
|
|
87
|
+
entries.push(entryPath);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
walk();
|
|
93
|
+
ctx.__nerviqProjectEntries = entries;
|
|
94
|
+
return entries;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getProjectFiles(ctx) {
|
|
98
|
+
if (ctx.__nerviqProjectFiles) return ctx.__nerviqProjectFiles;
|
|
99
|
+
ctx.__nerviqProjectFiles = getProjectEntries(ctx).filter(entry => !entry.endsWith('/'));
|
|
100
|
+
return ctx.__nerviqProjectFiles;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function findProjectFiles(ctx, pattern) {
|
|
104
|
+
return getProjectFiles(ctx).filter(file => matchesPattern(file, pattern));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function hasProjectFile(ctx, pattern) {
|
|
108
|
+
return findProjectFiles(ctx, pattern).length > 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Check if a stack-indicator file exists at a "core" location (root, src/, lib/, app/, packages/)
|
|
113
|
+
* rather than inside examples/, docs/, test/, vendor/, or deeply nested paths.
|
|
114
|
+
* This prevents false stack detection from example/demo code.
|
|
115
|
+
*/
|
|
116
|
+
const EXCLUDED_STACK_DIRS = /^(examples?|docs?|test|tests|fixtures?|samples?|demo|vendor|third[_-]?party|\.github)\//i;
|
|
117
|
+
|
|
118
|
+
function hasCoreProjectFile(ctx, pattern) {
|
|
119
|
+
return findProjectFiles(ctx, pattern).some(file => !EXCLUDED_STACK_DIRS.test(file));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function hasCoreRootFile(ctx, pattern) {
|
|
123
|
+
// Only match files at project root (no / in path) or one level deep (src/X, lib/X, app/X)
|
|
124
|
+
return findProjectFiles(ctx, pattern).some(file => {
|
|
125
|
+
if (EXCLUDED_STACK_DIRS.test(file)) return false;
|
|
126
|
+
const depth = (file.match(/\//g) || []).length;
|
|
127
|
+
return depth <= 1;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function readProjectFiles(ctx, pattern, limit = 60) {
|
|
132
|
+
return findProjectFiles(ctx, pattern)
|
|
133
|
+
.slice(0, limit)
|
|
134
|
+
.map(file => ctx.fileContent(file) || '')
|
|
135
|
+
.filter(Boolean)
|
|
136
|
+
.join('\n');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function isPythonProject(ctx) {
|
|
140
|
+
if (ctx.__nerviqIsPython !== undefined) return ctx.__nerviqIsPython;
|
|
141
|
+
// Require a Python config file (pyproject.toml, requirements.txt, setup.py) at a core location.
|
|
142
|
+
// Stray .py files in examples/ or docs/ don't make it a Python project.
|
|
143
|
+
ctx.__nerviqIsPython =
|
|
144
|
+
hasCoreRootFile(ctx, /(^|\/)(pyproject\.toml|setup\.py|Pipfile)$/i) ||
|
|
145
|
+
hasCoreRootFile(ctx, /(^|\/)requirements\.txt$/i);
|
|
146
|
+
return ctx.__nerviqIsPython;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function isGoProject(ctx) {
|
|
150
|
+
if (ctx.__nerviqIsGo !== undefined) return ctx.__nerviqIsGo;
|
|
151
|
+
ctx.__nerviqIsGo = hasCoreRootFile(ctx, /(^|\/)go\.mod$/i);
|
|
152
|
+
return ctx.__nerviqIsGo;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function isRustProject(ctx) {
|
|
156
|
+
if (ctx.__nerviqIsRust !== undefined) return ctx.__nerviqIsRust;
|
|
157
|
+
ctx.__nerviqIsRust = hasCoreRootFile(ctx, /(^|\/)Cargo\.toml$/i);
|
|
158
|
+
return ctx.__nerviqIsRust;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function isJavaProject(ctx) {
|
|
162
|
+
if (ctx.__nerviqIsJava !== undefined) return ctx.__nerviqIsJava;
|
|
163
|
+
ctx.__nerviqIsJava = hasCoreRootFile(ctx, /(^|\/)(pom\.xml|build\.gradle|build\.gradle\.kts)$/i);
|
|
164
|
+
return ctx.__nerviqIsJava;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function isFlutterProject(ctx) {
|
|
168
|
+
if (ctx.__nerviqIsFlutter !== undefined) return ctx.__nerviqIsFlutter;
|
|
169
|
+
ctx.__nerviqIsFlutter = hasCoreRootFile(ctx, /(^|\/)pubspec\.yaml$/i);
|
|
170
|
+
return ctx.__nerviqIsFlutter;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function isSwiftProject(ctx) {
|
|
174
|
+
if (ctx.__nerviqIsSwift !== undefined) return ctx.__nerviqIsSwift;
|
|
175
|
+
ctx.__nerviqIsSwift = hasCoreRootFile(ctx, /(^|\/)Package\.swift$/i) ||
|
|
176
|
+
hasCoreProjectFile(ctx, /\.xcodeproj/i);
|
|
177
|
+
return ctx.__nerviqIsSwift;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function isKotlinProject(ctx) {
|
|
181
|
+
if (ctx.__nerviqIsKotlin !== undefined) return ctx.__nerviqIsKotlin;
|
|
182
|
+
const gradle = (ctx.fileContent('build.gradle.kts') || '') + (ctx.fileContent('build.gradle') || '');
|
|
183
|
+
ctx.__nerviqIsKotlin = /kotlin/i.test(gradle);
|
|
184
|
+
return ctx.__nerviqIsKotlin;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function isRubyProject(ctx) {
|
|
188
|
+
if (ctx.__nerviqIsRuby !== undefined) return ctx.__nerviqIsRuby;
|
|
189
|
+
ctx.__nerviqIsRuby = hasCoreRootFile(ctx, /(^|\/)Gemfile$/i);
|
|
190
|
+
return ctx.__nerviqIsRuby;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function isPhpProject(ctx) {
|
|
194
|
+
if (ctx.__nerviqIsPhp !== undefined) return ctx.__nerviqIsPhp;
|
|
195
|
+
ctx.__nerviqIsPhp = hasCoreRootFile(ctx, /(^|\/)composer\.json$/i);
|
|
196
|
+
return ctx.__nerviqIsPhp;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function isDotnetProject(ctx) {
|
|
200
|
+
if (ctx.__nerviqIsDotnet !== undefined) return ctx.__nerviqIsDotnet;
|
|
201
|
+
ctx.__nerviqIsDotnet = hasCoreProjectFile(ctx, /(^|\/)(.*\.csproj|.*\.sln|global\.json)$/i);
|
|
202
|
+
return ctx.__nerviqIsDotnet;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Map category names to their project detection function.
|
|
207
|
+
* Used by the audit to skip entire categories when the stack isn't detected.
|
|
208
|
+
*/
|
|
209
|
+
const STACK_CATEGORY_DETECTORS = {
|
|
210
|
+
python: isPythonProject,
|
|
211
|
+
go: isGoProject,
|
|
212
|
+
rust: isRustProject,
|
|
213
|
+
java: isJavaProject,
|
|
214
|
+
flutter: isFlutterProject,
|
|
215
|
+
swift: isSwiftProject,
|
|
216
|
+
kotlin: isKotlinProject,
|
|
217
|
+
ruby: isRubyProject,
|
|
218
|
+
php: isPhpProject,
|
|
219
|
+
dotnet: isDotnetProject,
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
function getPythonFiles(ctx) {
|
|
223
|
+
if (ctx.__nerviqPythonFiles) return ctx.__nerviqPythonFiles;
|
|
224
|
+
ctx.__nerviqPythonFiles = findProjectFiles(ctx, /\.py$/i);
|
|
225
|
+
return ctx.__nerviqPythonFiles;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function getMainPythonFiles(ctx) {
|
|
229
|
+
if (ctx.__nerviqMainPythonFiles) return ctx.__nerviqMainPythonFiles;
|
|
230
|
+
ctx.__nerviqMainPythonFiles = getPythonFiles(ctx)
|
|
231
|
+
.filter(file => !/(^|\/)(tests?|__pycache__|migrations)\//i.test(file))
|
|
232
|
+
.filter(file => !/(^|\/)(test_[^/]+|conftest)\.py$/i.test(file))
|
|
233
|
+
.slice(0, 50);
|
|
234
|
+
return ctx.__nerviqMainPythonFiles;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function getPythonProjectText(ctx) {
|
|
238
|
+
if (ctx.__nerviqPythonProjectText) return ctx.__nerviqPythonProjectText;
|
|
239
|
+
ctx.__nerviqPythonProjectText = [
|
|
240
|
+
readProjectFiles(ctx, /(^|\/)pyproject\.toml$/i),
|
|
241
|
+
readProjectFiles(ctx, /(^|\/)requirements[^/]*\.txt$/i),
|
|
242
|
+
readProjectFiles(ctx, /(^|\/)setup\.py$/i),
|
|
243
|
+
readProjectFiles(ctx, /(^|\/)setup\.cfg$/i),
|
|
244
|
+
readProjectFiles(ctx, /(^|\/)Pipfile$/i),
|
|
245
|
+
].filter(Boolean).join('\n');
|
|
246
|
+
return ctx.__nerviqPythonProjectText;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function getGoFiles(ctx) {
|
|
250
|
+
if (ctx.__nerviqGoFiles) return ctx.__nerviqGoFiles;
|
|
251
|
+
ctx.__nerviqGoFiles = findProjectFiles(ctx, /\.go$/i);
|
|
252
|
+
return ctx.__nerviqGoFiles;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function getRustFiles(ctx) {
|
|
256
|
+
if (ctx.__nerviqRustFiles) return ctx.__nerviqRustFiles;
|
|
257
|
+
ctx.__nerviqRustFiles = findProjectFiles(ctx, /\.rs$/i);
|
|
258
|
+
return ctx.__nerviqRustFiles;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function getMainRustFiles(ctx) {
|
|
262
|
+
if (ctx.__nerviqMainRustFiles) return ctx.__nerviqMainRustFiles;
|
|
263
|
+
ctx.__nerviqMainRustFiles = getRustFiles(ctx)
|
|
264
|
+
.filter(file => !/(^|\/)(tests|target)\//i.test(file))
|
|
265
|
+
.slice(0, 60);
|
|
266
|
+
return ctx.__nerviqMainRustFiles;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function getJavaFiles(ctx) {
|
|
270
|
+
if (ctx.__nerviqJavaFiles) return ctx.__nerviqJavaFiles;
|
|
271
|
+
ctx.__nerviqJavaFiles = findProjectFiles(ctx, /\.java$/i);
|
|
272
|
+
return ctx.__nerviqJavaFiles;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function getMainJavaFiles(ctx) {
|
|
276
|
+
if (ctx.__nerviqMainJavaFiles) return ctx.__nerviqMainJavaFiles;
|
|
277
|
+
ctx.__nerviqMainJavaFiles = getJavaFiles(ctx)
|
|
278
|
+
.filter(file => !/(^|\/)(test|tests|src\/test)\//i.test(file))
|
|
279
|
+
.slice(0, 60);
|
|
280
|
+
return ctx.__nerviqMainJavaFiles;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function getMainGoFiles(ctx) {
|
|
284
|
+
if (ctx.__nerviqMainGoFiles) return ctx.__nerviqMainGoFiles;
|
|
285
|
+
ctx.__nerviqMainGoFiles = getGoFiles(ctx).filter(file => !/_test\.go$/i.test(file)).slice(0, 60);
|
|
286
|
+
return ctx.__nerviqMainGoFiles;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function getWorkflowContent(ctx) {
|
|
290
|
+
if (ctx.__nerviqWorkflowContent !== undefined) return ctx.__nerviqWorkflowContent;
|
|
291
|
+
ctx.__nerviqWorkflowContent = readProjectFiles(ctx, /^\.github\/workflows\/.*\.ya?ml$/i);
|
|
292
|
+
return ctx.__nerviqWorkflowContent;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function getPreCommitContent(ctx) {
|
|
296
|
+
if (ctx.__nerviqPreCommitContent !== undefined) return ctx.__nerviqPreCommitContent;
|
|
297
|
+
ctx.__nerviqPreCommitContent = readProjectFiles(ctx, /(^|\/)\.pre-commit-config\.ya?ml$/i);
|
|
298
|
+
return ctx.__nerviqPreCommitContent;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function getGoProjectText(ctx) {
|
|
302
|
+
if (ctx.__nerviqGoProjectText) return ctx.__nerviqGoProjectText;
|
|
303
|
+
ctx.__nerviqGoProjectText = [
|
|
304
|
+
readProjectFiles(ctx, /(^|\/)go\.mod$/i),
|
|
305
|
+
getWorkflowContent(ctx),
|
|
306
|
+
readProjectFiles(ctx, /(^|\/)Makefile$/),
|
|
307
|
+
getPreCommitContent(ctx),
|
|
308
|
+
getMainGoFiles(ctx).slice(0, 25).map(file => ctx.fileContent(file) || '').filter(Boolean).join('\n'),
|
|
309
|
+
].filter(Boolean).join('\n');
|
|
310
|
+
return ctx.__nerviqGoProjectText;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function getRustProjectText(ctx) {
|
|
314
|
+
if (ctx.__nerviqRustProjectText) return ctx.__nerviqRustProjectText;
|
|
315
|
+
ctx.__nerviqRustProjectText = [
|
|
316
|
+
readProjectFiles(ctx, /(^|\/)Cargo\.toml$/i),
|
|
317
|
+
readProjectFiles(ctx, /(^|\/)(clippy\.toml|\.clippy\.toml|rustfmt\.toml|\.rustfmt\.toml|build\.rs)$/i),
|
|
318
|
+
readProjectFiles(ctx, /(^|\/)\.cargo\/config\.toml$/i),
|
|
319
|
+
getWorkflowContent(ctx),
|
|
320
|
+
getMainRustFiles(ctx).slice(0, 30).map(file => ctx.fileContent(file) || '').filter(Boolean).join('\n'),
|
|
321
|
+
].filter(Boolean).join('\n');
|
|
322
|
+
return ctx.__nerviqRustProjectText;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function getJavaBuildText(ctx) {
|
|
326
|
+
if (ctx.__nerviqJavaBuildText) return ctx.__nerviqJavaBuildText;
|
|
327
|
+
ctx.__nerviqJavaBuildText = [
|
|
328
|
+
readProjectFiles(ctx, /(^|\/)pom\.xml$/i),
|
|
329
|
+
readProjectFiles(ctx, /(^|\/)build\.gradle$/i),
|
|
330
|
+
readProjectFiles(ctx, /(^|\/)build\.gradle\.kts$/i),
|
|
331
|
+
readProjectFiles(ctx, /(^|\/)settings\.gradle$/i),
|
|
332
|
+
readProjectFiles(ctx, /(^|\/)settings\.gradle\.kts$/i),
|
|
333
|
+
].filter(Boolean).join('\n');
|
|
334
|
+
return ctx.__nerviqJavaBuildText;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function getJavaProjectText(ctx) {
|
|
338
|
+
if (ctx.__nerviqJavaProjectText) return ctx.__nerviqJavaProjectText;
|
|
339
|
+
ctx.__nerviqJavaProjectText = [
|
|
340
|
+
getJavaBuildText(ctx),
|
|
341
|
+
getWorkflowContent(ctx),
|
|
342
|
+
readProjectFiles(ctx, /(^|\/)\.editorconfig$/i),
|
|
343
|
+
readProjectFiles(ctx, /(^|\/)(application\.properties|application\.ya?ml|logback.*\.xml|log4j2?.*\.xml)$/i),
|
|
344
|
+
getMainJavaFiles(ctx).slice(0, 30).map(file => ctx.fileContent(file) || '').filter(Boolean).join('\n'),
|
|
345
|
+
].filter(Boolean).join('\n');
|
|
346
|
+
return ctx.__nerviqJavaProjectText;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function getGoInterfaceBlocks(ctx) {
|
|
350
|
+
if (ctx.__nerviqGoInterfaces) return ctx.__nerviqGoInterfaces;
|
|
351
|
+
const blocks = [];
|
|
352
|
+
for (const file of getMainGoFiles(ctx)) {
|
|
353
|
+
const content = ctx.fileContent(file) || '';
|
|
354
|
+
for (const match of content.matchAll(/type\s+\w+\s+interface\s*\{([\s\S]*?)\}/g)) {
|
|
355
|
+
blocks.push(match[1]);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
ctx.__nerviqGoInterfaces = blocks;
|
|
359
|
+
return ctx.__nerviqGoInterfaces;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function countGoInterfaceMethods(block) {
|
|
363
|
+
return block
|
|
364
|
+
.split(/\r?\n/)
|
|
365
|
+
.map(line => line.trim())
|
|
366
|
+
.filter(line => line && !line.startsWith('//') && !line.startsWith('/*'))
|
|
367
|
+
.length;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const { containsEmbeddedSecret } = require('../secret-patterns');
|
|
371
|
+
const { attachSourceUrls } = require('../source-urls');
|
|
372
|
+
const { buildSupplementalChecks } = require('../supplemental-checks');
|
|
373
|
+
const { resolveProjectStateReadPath } = require('../state-paths');
|
|
374
|
+
|
|
375
|
+
const CLAUDE_SUPPLEMENTAL_SOURCE_URLS = {
|
|
376
|
+
'testing-strategy': 'https://code.claude.com/docs/en/common-workflows',
|
|
377
|
+
'code-quality': 'https://code.claude.com/docs/en/best-practices',
|
|
378
|
+
'api-design': 'https://code.claude.com/docs/en/best-practices',
|
|
379
|
+
database: 'https://code.claude.com/docs/en/common-workflows',
|
|
380
|
+
authentication: 'https://code.claude.com/docs/en/permissions',
|
|
381
|
+
monitoring: 'https://code.claude.com/docs/en/common-workflows',
|
|
382
|
+
'dependency-management': 'https://code.claude.com/docs/en/best-practices',
|
|
383
|
+
'cost-optimization': 'https://code.claude.com/docs/en/memory',
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
module.exports = {
|
|
387
|
+
fs,
|
|
388
|
+
path,
|
|
389
|
+
collectClaudeDenyRules,
|
|
390
|
+
hasPromptInjectionDefenseGuidance,
|
|
391
|
+
hasMcpPromptInjectionDefenseGuidance,
|
|
392
|
+
hasInjectionDefenseHookConfigured,
|
|
393
|
+
getClaudeInstructionBundle,
|
|
394
|
+
getRepoInstructionBundle,
|
|
395
|
+
hasDocumentedVerificationGuidance,
|
|
396
|
+
hasDocumentedTestCommand,
|
|
397
|
+
hasDocumentedLintCommand,
|
|
398
|
+
hasDocumentedBuildCommand,
|
|
399
|
+
hasFrontendSignals,
|
|
400
|
+
getClaudeHookContents,
|
|
401
|
+
matchesPattern,
|
|
402
|
+
getProjectEntries,
|
|
403
|
+
getProjectFiles,
|
|
404
|
+
findProjectFiles,
|
|
405
|
+
hasProjectFile,
|
|
406
|
+
EXCLUDED_STACK_DIRS,
|
|
407
|
+
hasCoreProjectFile,
|
|
408
|
+
hasCoreRootFile,
|
|
409
|
+
readProjectFiles,
|
|
410
|
+
isPythonProject,
|
|
411
|
+
isGoProject,
|
|
412
|
+
isRustProject,
|
|
413
|
+
isJavaProject,
|
|
414
|
+
isFlutterProject,
|
|
415
|
+
isSwiftProject,
|
|
416
|
+
isKotlinProject,
|
|
417
|
+
isRubyProject,
|
|
418
|
+
isPhpProject,
|
|
419
|
+
isDotnetProject,
|
|
420
|
+
STACK_CATEGORY_DETECTORS,
|
|
421
|
+
getPythonFiles,
|
|
422
|
+
getMainPythonFiles,
|
|
423
|
+
getPythonProjectText,
|
|
424
|
+
getGoFiles,
|
|
425
|
+
getRustFiles,
|
|
426
|
+
getMainRustFiles,
|
|
427
|
+
getJavaFiles,
|
|
428
|
+
getMainJavaFiles,
|
|
429
|
+
getMainGoFiles,
|
|
430
|
+
getWorkflowContent,
|
|
431
|
+
getPreCommitContent,
|
|
432
|
+
getGoProjectText,
|
|
433
|
+
getRustProjectText,
|
|
434
|
+
getJavaBuildText,
|
|
435
|
+
getJavaProjectText,
|
|
436
|
+
getGoInterfaceBlocks,
|
|
437
|
+
countGoInterfaceMethods,
|
|
438
|
+
containsEmbeddedSecret,
|
|
439
|
+
attachSourceUrls,
|
|
440
|
+
buildSupplementalChecks,
|
|
441
|
+
resolveProjectStateReadPath,
|
|
442
|
+
CLAUDE_SUPPLEMENTAL_SOURCE_URLS,
|
|
443
|
+
};
|