@nerviq/cli 1.18.0 → 1.20.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 +131 -130
- package/package.json +2 -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 +290 -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 +105 -33
- 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.js
CHANGED
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* NERVIQ Technique Database
|
|
3
|
-
* Curated from 1118 verified techniques, filtered to actionable setup recommendations.
|
|
4
|
-
* Each technique includes: what to check, how to fix, impact level.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { attachSourceUrls, buildSupplementalChecks, CLAUDE_SUPPLEMENTAL_SOURCE_URLS, STACK_CATEGORY_DETECTORS, containsEmbeddedSecret } = require('./techniques/shared');
|
|
8
|
-
const instructionTechniques = require('./techniques/instructions');
|
|
9
|
-
const qualityTechniques = require('./techniques/quality');
|
|
10
|
-
const apiTechniques = require('./techniques/api');
|
|
11
|
-
const automationTechniques = require('./techniques/automation');
|
|
12
|
-
const hygieneTechniques = require('./techniques/hygiene');
|
|
13
|
-
const observabilityTechniques = require('./techniques/observability');
|
|
14
|
-
const workflowTechniques = require('./techniques/workflow');
|
|
15
|
-
const toolTechniques = require('./techniques/tools');
|
|
16
|
-
const securityTechniques = require('./techniques/security');
|
|
17
|
-
const complianceTechniques = require('./techniques/compliance');
|
|
18
|
-
const optimizationTechniques = require('./techniques/optimization');
|
|
19
|
-
const stackTechniques = require('./techniques/stacks');
|
|
20
|
-
|
|
21
|
-
const TECHNIQUES = Object.assign({},
|
|
22
|
-
instructionTechniques,
|
|
23
|
-
qualityTechniques,
|
|
24
|
-
apiTechniques,
|
|
25
|
-
automationTechniques,
|
|
26
|
-
hygieneTechniques,
|
|
27
|
-
observabilityTechniques,
|
|
28
|
-
workflowTechniques,
|
|
29
|
-
toolTechniques,
|
|
30
|
-
securityTechniques,
|
|
31
|
-
complianceTechniques,
|
|
32
|
-
optimizationTechniques,
|
|
33
|
-
stackTechniques,
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
Object.assign(TECHNIQUES, buildSupplementalChecks({
|
|
37
|
-
idPrefix: 'CL-T',
|
|
38
|
-
urlMap: CLAUDE_SUPPLEMENTAL_SOURCE_URLS,
|
|
39
|
-
docs: (ctx) => [
|
|
40
|
-
ctx.claudeMdContent ? ctx.claudeMdContent() : (ctx.fileContent('CLAUDE.md') || ctx.fileContent('.claude/CLAUDE.md') || ''),
|
|
41
|
-
ctx.fileContent('README.md') || '',
|
|
42
|
-
].filter(Boolean).join('\n'),
|
|
43
|
-
}));
|
|
44
|
-
|
|
45
|
-
// Stack detection
|
|
46
|
-
const STACKS = {
|
|
47
|
-
react: { files: ['package.json'], content: { 'package.json': 'react' }, label: 'React' },
|
|
48
|
-
vue: { files: ['package.json'], content: { 'package.json': 'vue' }, label: 'Vue' },
|
|
49
|
-
angular: { files: ['angular.json'], content: {}, label: 'Angular' },
|
|
50
|
-
nextjs: { files: ['next.config'], content: {}, label: 'Next.js' },
|
|
51
|
-
python: { files: ['requirements.txt', 'setup.py', 'pyproject.toml', 'Pipfile'], content: {}, label: 'Python' },
|
|
52
|
-
django: { files: ['manage.py'], content: {}, label: 'Django' },
|
|
53
|
-
fastapi: { files: ['requirements.txt'], content: { 'requirements.txt': 'fastapi' }, label: 'FastAPI' },
|
|
54
|
-
node: { files: ['package.json'], content: {}, label: 'Node.js' },
|
|
55
|
-
typescript: { files: ['tsconfig.json'], content: {}, label: 'TypeScript' },
|
|
56
|
-
rust: { files: ['Cargo.toml'], content: {}, label: 'Rust' },
|
|
57
|
-
go: { files: ['go.mod'], content: {}, label: 'Go' },
|
|
58
|
-
docker: { files: ['Dockerfile', 'docker-compose.yml', 'docker-compose.yaml'], content: {}, label: 'Docker' },
|
|
59
|
-
svelte: { files: ['svelte.config.js'], content: {}, label: 'Svelte' },
|
|
60
|
-
flutter: { files: ['pubspec.yaml'], content: {}, label: 'Flutter' },
|
|
61
|
-
ruby: { files: ['Gemfile'], content: {}, label: 'Ruby' },
|
|
62
|
-
java: { files: ['pom.xml'], content: {}, label: 'Java' },
|
|
63
|
-
kotlin: { files: ['build.gradle.kts'], content: {}, label: 'Kotlin' },
|
|
64
|
-
swift: { files: ['Package.swift', '.xcodeproj'], content: {}, label: 'Swift' },
|
|
65
|
-
terraform: { files: ['main.tf', 'terraform'], content: {}, label: 'Terraform' },
|
|
66
|
-
kubernetes: { files: ['k8s', 'kubernetes', 'helm'], content: {}, label: 'Kubernetes' },
|
|
67
|
-
cpp: { files: ['CMakeLists.txt', 'Makefile', '.clang-format'], content: {}, label: 'C++' },
|
|
68
|
-
bazel: { files: ['BUILD', 'WORKSPACE', 'BUILD.bazel', 'WORKSPACE.bazel'], content: {}, label: 'Bazel' },
|
|
69
|
-
deno: { files: ['deno.json', 'deno.jsonc', 'deno.lock'], content: {}, label: 'Deno' },
|
|
70
|
-
bun: { files: ['bun.lockb', 'bunfig.toml'], content: {}, label: 'Bun' },
|
|
71
|
-
elixir: { files: ['mix.exs'], content: {}, label: 'Elixir' },
|
|
72
|
-
astro: { files: ['astro.config.mjs', 'astro.config.ts'], content: {}, label: 'Astro' },
|
|
73
|
-
remix: { files: ['remix.config.js', 'remix.config.ts'], content: {}, label: 'Remix' },
|
|
74
|
-
nestjs: { files: ['nest-cli.json'], content: {}, label: 'NestJS' },
|
|
75
|
-
laravel: { files: ['artisan'], content: {}, label: 'Laravel' },
|
|
76
|
-
dotnet: { files: ['global.json', 'Directory.Build.props'], content: {}, label: '.NET' },
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
attachSourceUrls('claude', TECHNIQUES);
|
|
80
|
-
|
|
81
|
-
module.exports = { TECHNIQUES, STACKS, STACK_CATEGORY_DETECTORS, containsEmbeddedSecret };
|
|
1
|
+
/**
|
|
2
|
+
* NERVIQ Technique Database
|
|
3
|
+
* Curated from 1118 verified techniques, filtered to actionable setup recommendations.
|
|
4
|
+
* Each technique includes: what to check, how to fix, impact level.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { attachSourceUrls, buildSupplementalChecks, CLAUDE_SUPPLEMENTAL_SOURCE_URLS, STACK_CATEGORY_DETECTORS, containsEmbeddedSecret } = require('./techniques/shared');
|
|
8
|
+
const instructionTechniques = require('./techniques/instructions');
|
|
9
|
+
const qualityTechniques = require('./techniques/quality');
|
|
10
|
+
const apiTechniques = require('./techniques/api');
|
|
11
|
+
const automationTechniques = require('./techniques/automation');
|
|
12
|
+
const hygieneTechniques = require('./techniques/hygiene');
|
|
13
|
+
const observabilityTechniques = require('./techniques/observability');
|
|
14
|
+
const workflowTechniques = require('./techniques/workflow');
|
|
15
|
+
const toolTechniques = require('./techniques/tools');
|
|
16
|
+
const securityTechniques = require('./techniques/security');
|
|
17
|
+
const complianceTechniques = require('./techniques/compliance');
|
|
18
|
+
const optimizationTechniques = require('./techniques/optimization');
|
|
19
|
+
const stackTechniques = require('./techniques/stacks');
|
|
20
|
+
|
|
21
|
+
const TECHNIQUES = Object.assign({},
|
|
22
|
+
instructionTechniques,
|
|
23
|
+
qualityTechniques,
|
|
24
|
+
apiTechniques,
|
|
25
|
+
automationTechniques,
|
|
26
|
+
hygieneTechniques,
|
|
27
|
+
observabilityTechniques,
|
|
28
|
+
workflowTechniques,
|
|
29
|
+
toolTechniques,
|
|
30
|
+
securityTechniques,
|
|
31
|
+
complianceTechniques,
|
|
32
|
+
optimizationTechniques,
|
|
33
|
+
stackTechniques,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
Object.assign(TECHNIQUES, buildSupplementalChecks({
|
|
37
|
+
idPrefix: 'CL-T',
|
|
38
|
+
urlMap: CLAUDE_SUPPLEMENTAL_SOURCE_URLS,
|
|
39
|
+
docs: (ctx) => [
|
|
40
|
+
ctx.claudeMdContent ? ctx.claudeMdContent() : (ctx.fileContent('CLAUDE.md') || ctx.fileContent('.claude/CLAUDE.md') || ''),
|
|
41
|
+
ctx.fileContent('README.md') || '',
|
|
42
|
+
].filter(Boolean).join('\n'),
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
// Stack detection
|
|
46
|
+
const STACKS = {
|
|
47
|
+
react: { files: ['package.json'], content: { 'package.json': 'react' }, label: 'React' },
|
|
48
|
+
vue: { files: ['package.json'], content: { 'package.json': 'vue' }, label: 'Vue' },
|
|
49
|
+
angular: { files: ['angular.json'], content: {}, label: 'Angular' },
|
|
50
|
+
nextjs: { files: ['next.config'], content: {}, label: 'Next.js' },
|
|
51
|
+
python: { files: ['requirements.txt', 'setup.py', 'pyproject.toml', 'Pipfile'], content: {}, label: 'Python' },
|
|
52
|
+
django: { files: ['manage.py'], content: {}, label: 'Django' },
|
|
53
|
+
fastapi: { files: ['requirements.txt'], content: { 'requirements.txt': 'fastapi' }, label: 'FastAPI' },
|
|
54
|
+
node: { files: ['package.json'], content: {}, label: 'Node.js' },
|
|
55
|
+
typescript: { files: ['tsconfig.json'], content: {}, label: 'TypeScript' },
|
|
56
|
+
rust: { files: ['Cargo.toml'], content: {}, label: 'Rust' },
|
|
57
|
+
go: { files: ['go.mod'], content: {}, label: 'Go' },
|
|
58
|
+
docker: { files: ['Dockerfile', 'docker-compose.yml', 'docker-compose.yaml'], content: {}, label: 'Docker' },
|
|
59
|
+
svelte: { files: ['svelte.config.js'], content: {}, label: 'Svelte' },
|
|
60
|
+
flutter: { files: ['pubspec.yaml'], content: {}, label: 'Flutter' },
|
|
61
|
+
ruby: { files: ['Gemfile'], content: {}, label: 'Ruby' },
|
|
62
|
+
java: { files: ['pom.xml'], content: {}, label: 'Java' },
|
|
63
|
+
kotlin: { files: ['build.gradle.kts'], content: {}, label: 'Kotlin' },
|
|
64
|
+
swift: { files: ['Package.swift', '.xcodeproj'], content: {}, label: 'Swift' },
|
|
65
|
+
terraform: { files: ['main.tf', 'terraform'], content: {}, label: 'Terraform' },
|
|
66
|
+
kubernetes: { files: ['k8s', 'kubernetes', 'helm'], content: {}, label: 'Kubernetes' },
|
|
67
|
+
cpp: { files: ['CMakeLists.txt', 'Makefile', '.clang-format'], content: {}, label: 'C++' },
|
|
68
|
+
bazel: { files: ['BUILD', 'WORKSPACE', 'BUILD.bazel', 'WORKSPACE.bazel'], content: {}, label: 'Bazel' },
|
|
69
|
+
deno: { files: ['deno.json', 'deno.jsonc', 'deno.lock'], content: {}, label: 'Deno' },
|
|
70
|
+
bun: { files: ['bun.lockb', 'bunfig.toml'], content: {}, label: 'Bun' },
|
|
71
|
+
elixir: { files: ['mix.exs'], content: {}, label: 'Elixir' },
|
|
72
|
+
astro: { files: ['astro.config.mjs', 'astro.config.ts'], content: {}, label: 'Astro' },
|
|
73
|
+
remix: { files: ['remix.config.js', 'remix.config.ts'], content: {}, label: 'Remix' },
|
|
74
|
+
nestjs: { files: ['nest-cli.json'], content: {}, label: 'NestJS' },
|
|
75
|
+
laravel: { files: ['artisan'], content: {}, label: 'Laravel' },
|
|
76
|
+
dotnet: { files: ['global.json', 'Directory.Build.props'], content: {}, label: '.NET' },
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
attachSourceUrls('claude', TECHNIQUES);
|
|
80
|
+
|
|
81
|
+
module.exports = { TECHNIQUES, STACKS, STACK_CATEGORY_DETECTORS, containsEmbeddedSecret };
|
package/src/terminology.js
CHANGED
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const TERMINOLOGY = {
|
|
4
|
-
governance: {
|
|
5
|
-
label: 'governance',
|
|
6
|
-
description: 'the rollout safety layer: permissions, hooks, profiles, and policy packs',
|
|
7
|
-
},
|
|
8
|
-
hooks: {
|
|
9
|
-
label: 'hooks',
|
|
10
|
-
description: 'auto-run checks or scripts triggered before or after agent tool actions',
|
|
11
|
-
},
|
|
12
|
-
denyRules: {
|
|
13
|
-
label: 'deny rules',
|
|
14
|
-
description: 'explicit blocks for risky reads or commands like .env access or rm -rf',
|
|
15
|
-
},
|
|
16
|
-
mcp: {
|
|
17
|
-
label: 'MCP',
|
|
18
|
-
description: 'live external tool connectors for docs, APIs, databases, and other systems',
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const TERM_ORDER = ['governance', 'hooks', 'denyRules', 'mcp'];
|
|
23
|
-
|
|
24
|
-
function normalizeTermKeys(keys = []) {
|
|
25
|
-
const seen = new Set();
|
|
26
|
-
for (const key of keys) {
|
|
27
|
-
if (!TERMINOLOGY[key]) continue;
|
|
28
|
-
seen.add(key);
|
|
29
|
-
}
|
|
30
|
-
return TERM_ORDER.filter((key) => seen.has(key));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function collectAuditTerminology(result = {}) {
|
|
34
|
-
const terms = new Set();
|
|
35
|
-
const texts = [];
|
|
36
|
-
|
|
37
|
-
for (const item of result.topNextActions || []) {
|
|
38
|
-
texts.push(item.name || '', item.fix || '', item.why || '', item.module || '', ...(item.signals || []));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
for (const item of result.results || []) {
|
|
42
|
-
if (item.passed === false) {
|
|
43
|
-
texts.push(item.key || '', item.name || '', item.fix || '', item.category || '');
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const blob = texts.join('\n');
|
|
48
|
-
if (/\bhook/i.test(blob)) terms.add('hooks');
|
|
49
|
-
if (/\bdeny rules?\b|permissions?\.deny|bypasspermissions|\.env access|rm -rf/i.test(blob)) terms.add('denyRules');
|
|
50
|
-
if (/\bmcp\b|context7|external tool/i.test(blob)) terms.add('mcp');
|
|
51
|
-
if (/\bgovernance\b|policy pack|permission profile/i.test(blob) || terms.size > 0) terms.add('governance');
|
|
52
|
-
|
|
53
|
-
return normalizeTermKeys([...terms]);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function formatTerminologyLines(keys, options = {}) {
|
|
57
|
-
const normalized = normalizeTermKeys(keys);
|
|
58
|
-
if (normalized.length === 0) return [];
|
|
59
|
-
const title = options.title || ' Terms used here:';
|
|
60
|
-
const indent = options.indent || ' ';
|
|
61
|
-
const bullet = options.bullet || '-';
|
|
62
|
-
|
|
63
|
-
return [
|
|
64
|
-
title,
|
|
65
|
-
...normalized.map((key) => `${indent}${bullet} ${TERMINOLOGY[key].label}: ${TERMINOLOGY[key].description}`),
|
|
66
|
-
];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
module.exports = {
|
|
70
|
-
TERMINOLOGY,
|
|
71
|
-
collectAuditTerminology,
|
|
72
|
-
formatTerminologyLines,
|
|
73
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const TERMINOLOGY = {
|
|
4
|
+
governance: {
|
|
5
|
+
label: 'governance',
|
|
6
|
+
description: 'the rollout safety layer: permissions, hooks, profiles, and policy packs',
|
|
7
|
+
},
|
|
8
|
+
hooks: {
|
|
9
|
+
label: 'hooks',
|
|
10
|
+
description: 'auto-run checks or scripts triggered before or after agent tool actions',
|
|
11
|
+
},
|
|
12
|
+
denyRules: {
|
|
13
|
+
label: 'deny rules',
|
|
14
|
+
description: 'explicit blocks for risky reads or commands like .env access or rm -rf',
|
|
15
|
+
},
|
|
16
|
+
mcp: {
|
|
17
|
+
label: 'MCP',
|
|
18
|
+
description: 'live external tool connectors for docs, APIs, databases, and other systems',
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const TERM_ORDER = ['governance', 'hooks', 'denyRules', 'mcp'];
|
|
23
|
+
|
|
24
|
+
function normalizeTermKeys(keys = []) {
|
|
25
|
+
const seen = new Set();
|
|
26
|
+
for (const key of keys) {
|
|
27
|
+
if (!TERMINOLOGY[key]) continue;
|
|
28
|
+
seen.add(key);
|
|
29
|
+
}
|
|
30
|
+
return TERM_ORDER.filter((key) => seen.has(key));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function collectAuditTerminology(result = {}) {
|
|
34
|
+
const terms = new Set();
|
|
35
|
+
const texts = [];
|
|
36
|
+
|
|
37
|
+
for (const item of result.topNextActions || []) {
|
|
38
|
+
texts.push(item.name || '', item.fix || '', item.why || '', item.module || '', ...(item.signals || []));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
for (const item of result.results || []) {
|
|
42
|
+
if (item.passed === false) {
|
|
43
|
+
texts.push(item.key || '', item.name || '', item.fix || '', item.category || '');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const blob = texts.join('\n');
|
|
48
|
+
if (/\bhook/i.test(blob)) terms.add('hooks');
|
|
49
|
+
if (/\bdeny rules?\b|permissions?\.deny|bypasspermissions|\.env access|rm -rf/i.test(blob)) terms.add('denyRules');
|
|
50
|
+
if (/\bmcp\b|context7|external tool/i.test(blob)) terms.add('mcp');
|
|
51
|
+
if (/\bgovernance\b|policy pack|permission profile/i.test(blob) || terms.size > 0) terms.add('governance');
|
|
52
|
+
|
|
53
|
+
return normalizeTermKeys([...terms]);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function formatTerminologyLines(keys, options = {}) {
|
|
57
|
+
const normalized = normalizeTermKeys(keys);
|
|
58
|
+
if (normalized.length === 0) return [];
|
|
59
|
+
const title = options.title || ' Terms used here:';
|
|
60
|
+
const indent = options.indent || ' ';
|
|
61
|
+
const bullet = options.bullet || '-';
|
|
62
|
+
|
|
63
|
+
return [
|
|
64
|
+
title,
|
|
65
|
+
...normalized.map((key) => `${indent}${bullet} ${TERMINOLOGY[key].label}: ${TERMINOLOGY[key].description}`),
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = {
|
|
70
|
+
TERMINOLOGY,
|
|
71
|
+
collectAuditTerminology,
|
|
72
|
+
formatTerminologyLines,
|
|
73
|
+
};
|
package/src/token-estimate.js
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
function splitIdentifierSegments(value) {
|
|
2
|
-
return String(value || '')
|
|
3
|
-
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
4
|
-
.replace(/[_./:-]+/g, ' ')
|
|
5
|
-
.split(/\s+/)
|
|
6
|
-
.filter(Boolean);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function estimateSegmentTokens(segment) {
|
|
10
|
-
const charCount = [...String(segment || '')].length;
|
|
11
|
-
return Math.max(1, Math.ceil(charCount / 4));
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function estimateTokenCount(text) {
|
|
15
|
-
if (typeof text !== 'string' || !text) return 0;
|
|
16
|
-
|
|
17
|
-
const parts = text.match(/[\p{L}\p{N}_]+|[^\s]/gu) || [];
|
|
18
|
-
let total = 0;
|
|
19
|
-
|
|
20
|
-
for (const part of parts) {
|
|
21
|
-
if (/^[\p{L}\p{N}_]+$/u.test(part)) {
|
|
22
|
-
const segments = splitIdentifierSegments(part);
|
|
23
|
-
total += segments.reduce((sum, segment) => sum + estimateSegmentTokens(segment), 0);
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
total += 1;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return total;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
module.exports = {
|
|
34
|
-
estimateTokenCount,
|
|
35
|
-
};
|
|
1
|
+
function splitIdentifierSegments(value) {
|
|
2
|
+
return String(value || '')
|
|
3
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
4
|
+
.replace(/[_./:-]+/g, ' ')
|
|
5
|
+
.split(/\s+/)
|
|
6
|
+
.filter(Boolean);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function estimateSegmentTokens(segment) {
|
|
10
|
+
const charCount = [...String(segment || '')].length;
|
|
11
|
+
return Math.max(1, Math.ceil(charCount / 4));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function estimateTokenCount(text) {
|
|
15
|
+
if (typeof text !== 'string' || !text) return 0;
|
|
16
|
+
|
|
17
|
+
const parts = text.match(/[\p{L}\p{N}_]+|[^\s]/gu) || [];
|
|
18
|
+
let total = 0;
|
|
19
|
+
|
|
20
|
+
for (const part of parts) {
|
|
21
|
+
if (/^[\p{L}\p{N}_]+$/u.test(part)) {
|
|
22
|
+
const segments = splitIdentifierSegments(part);
|
|
23
|
+
total += segments.reduce((sum, segment) => sum + estimateSegmentTokens(segment), 0);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
total += 1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return total;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
estimateTokenCount,
|
|
35
|
+
};
|
package/src/usage-patterns.js
CHANGED
|
@@ -1,99 +1,99 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { ensureProjectStateDir, resolveProjectStateReadPath } = require('./state-paths');
|
|
4
|
-
|
|
5
|
-
const PATTERNS_FILE = 'patterns.json';
|
|
6
|
-
const SUPPRESS_THRESHOLD = 3;
|
|
7
|
-
|
|
8
|
-
function patternsPath(dir, writable) {
|
|
9
|
-
if (writable) {
|
|
10
|
-
const feedbackDir = ensureProjectStateDir(dir, 'feedback');
|
|
11
|
-
return path.join(feedbackDir, PATTERNS_FILE);
|
|
12
|
-
}
|
|
13
|
-
const feedbackDir = resolveProjectStateReadPath(dir, 'feedback');
|
|
14
|
-
return path.join(feedbackDir, PATTERNS_FILE);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function loadPatterns(dir) {
|
|
18
|
-
const filePath = patternsPath(dir, false);
|
|
19
|
-
if (!fs.existsSync(filePath)) return {};
|
|
20
|
-
try {
|
|
21
|
-
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
22
|
-
} catch {
|
|
23
|
-
return {};
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function savePatterns(dir, patterns) {
|
|
28
|
-
const filePath = patternsPath(dir, true);
|
|
29
|
-
fs.writeFileSync(filePath, JSON.stringify(patterns, null, 2), 'utf8');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function recordPattern(dir, checkKey, action) {
|
|
33
|
-
if (!['accepted', 'rejected', 'skipped'].includes(action)) return;
|
|
34
|
-
const patterns = loadPatterns(dir);
|
|
35
|
-
if (!patterns[checkKey]) {
|
|
36
|
-
patterns[checkKey] = { accepted: 0, rejected: 0, skipped: 0, lastAction: null, lastDate: null };
|
|
37
|
-
}
|
|
38
|
-
patterns[checkKey][action] += 1;
|
|
39
|
-
patterns[checkKey].lastAction = action;
|
|
40
|
-
patterns[checkKey].lastDate = new Date().toISOString();
|
|
41
|
-
savePatterns(dir, patterns);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function getPriorityAdjustment(dir, checkKey) {
|
|
45
|
-
const patterns = loadPatterns(dir);
|
|
46
|
-
const entry = patterns[checkKey];
|
|
47
|
-
if (!entry) return null;
|
|
48
|
-
const total = entry.accepted + entry.rejected;
|
|
49
|
-
if (total < 2) return null;
|
|
50
|
-
if (entry.accepted > 0 && entry.rejected === 0) return 'boost';
|
|
51
|
-
if (entry.rejected >= SUPPRESS_THRESHOLD && entry.accepted === 0) return 'suppress';
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function getUsageSummary(dir) {
|
|
56
|
-
const patterns = loadPatterns(dir);
|
|
57
|
-
const keys = Object.keys(patterns);
|
|
58
|
-
const totalEvents = keys.reduce((sum, k) => {
|
|
59
|
-
const e = patterns[k];
|
|
60
|
-
return sum + e.accepted + e.rejected + e.skipped;
|
|
61
|
-
}, 0);
|
|
62
|
-
|
|
63
|
-
const withRates = keys
|
|
64
|
-
.filter(k => (patterns[k].accepted + patterns[k].rejected) > 0)
|
|
65
|
-
.map(k => {
|
|
66
|
-
const e = patterns[k];
|
|
67
|
-
const total = e.accepted + e.rejected;
|
|
68
|
-
return { key: k, accepted: e.accepted, rejected: e.rejected, total, rate: total > 0 ? e.accepted / total : 0 };
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
withRates.sort((a, b) => b.rate - a.rate || b.total - a.total);
|
|
72
|
-
const topAccepted = withRates.filter(e => e.rate > 0).slice(0, 5);
|
|
73
|
-
const topRejected = withRates.filter(e => e.rate < 1).sort((a, b) => a.rate - b.rate || b.total - a.total).slice(0, 5);
|
|
74
|
-
|
|
75
|
-
return { totalEvents, topAccepted, topRejected, patterns };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function formatUsageSummary(dir) {
|
|
79
|
-
const summary = getUsageSummary(dir);
|
|
80
|
-
if (summary.totalEvents === 0) return ' No usage patterns recorded yet.\n Patterns are tracked when you run nerviq fix.';
|
|
81
|
-
|
|
82
|
-
const lines = [` Usage Patterns (${summary.totalEvents} events recorded):`];
|
|
83
|
-
if (summary.topAccepted.length > 0) {
|
|
84
|
-
lines.push('', ' Most accepted:');
|
|
85
|
-
summary.topAccepted.forEach((e, i) => {
|
|
86
|
-
lines.push(` ${i + 1}. ${e.key.padEnd(20)} ${e.accepted}/${e.total} (${Math.round(e.rate * 100)}%)`);
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
if (summary.topRejected.length > 0) {
|
|
90
|
-
lines.push('', ' Most rejected:');
|
|
91
|
-
summary.topRejected.forEach((e, i) => {
|
|
92
|
-
const hint = e.rate === 0 && e.total >= SUPPRESS_THRESHOLD ? ' -- consider suppressing' : '';
|
|
93
|
-
lines.push(` ${i + 1}. ${e.key.padEnd(20)} ${e.accepted}/${e.total} (${Math.round(e.rate * 100)}%)${hint}`);
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
return lines.join('\n');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
module.exports = { loadPatterns, recordPattern, getPriorityAdjustment, getUsageSummary, formatUsageSummary };
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { ensureProjectStateDir, resolveProjectStateReadPath } = require('./state-paths');
|
|
4
|
+
|
|
5
|
+
const PATTERNS_FILE = 'patterns.json';
|
|
6
|
+
const SUPPRESS_THRESHOLD = 3;
|
|
7
|
+
|
|
8
|
+
function patternsPath(dir, writable) {
|
|
9
|
+
if (writable) {
|
|
10
|
+
const feedbackDir = ensureProjectStateDir(dir, 'feedback');
|
|
11
|
+
return path.join(feedbackDir, PATTERNS_FILE);
|
|
12
|
+
}
|
|
13
|
+
const feedbackDir = resolveProjectStateReadPath(dir, 'feedback');
|
|
14
|
+
return path.join(feedbackDir, PATTERNS_FILE);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function loadPatterns(dir) {
|
|
18
|
+
const filePath = patternsPath(dir, false);
|
|
19
|
+
if (!fs.existsSync(filePath)) return {};
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
22
|
+
} catch {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function savePatterns(dir, patterns) {
|
|
28
|
+
const filePath = patternsPath(dir, true);
|
|
29
|
+
fs.writeFileSync(filePath, JSON.stringify(patterns, null, 2), 'utf8');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function recordPattern(dir, checkKey, action) {
|
|
33
|
+
if (!['accepted', 'rejected', 'skipped'].includes(action)) return;
|
|
34
|
+
const patterns = loadPatterns(dir);
|
|
35
|
+
if (!patterns[checkKey]) {
|
|
36
|
+
patterns[checkKey] = { accepted: 0, rejected: 0, skipped: 0, lastAction: null, lastDate: null };
|
|
37
|
+
}
|
|
38
|
+
patterns[checkKey][action] += 1;
|
|
39
|
+
patterns[checkKey].lastAction = action;
|
|
40
|
+
patterns[checkKey].lastDate = new Date().toISOString();
|
|
41
|
+
savePatterns(dir, patterns);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getPriorityAdjustment(dir, checkKey) {
|
|
45
|
+
const patterns = loadPatterns(dir);
|
|
46
|
+
const entry = patterns[checkKey];
|
|
47
|
+
if (!entry) return null;
|
|
48
|
+
const total = entry.accepted + entry.rejected;
|
|
49
|
+
if (total < 2) return null;
|
|
50
|
+
if (entry.accepted > 0 && entry.rejected === 0) return 'boost';
|
|
51
|
+
if (entry.rejected >= SUPPRESS_THRESHOLD && entry.accepted === 0) return 'suppress';
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getUsageSummary(dir) {
|
|
56
|
+
const patterns = loadPatterns(dir);
|
|
57
|
+
const keys = Object.keys(patterns);
|
|
58
|
+
const totalEvents = keys.reduce((sum, k) => {
|
|
59
|
+
const e = patterns[k];
|
|
60
|
+
return sum + e.accepted + e.rejected + e.skipped;
|
|
61
|
+
}, 0);
|
|
62
|
+
|
|
63
|
+
const withRates = keys
|
|
64
|
+
.filter(k => (patterns[k].accepted + patterns[k].rejected) > 0)
|
|
65
|
+
.map(k => {
|
|
66
|
+
const e = patterns[k];
|
|
67
|
+
const total = e.accepted + e.rejected;
|
|
68
|
+
return { key: k, accepted: e.accepted, rejected: e.rejected, total, rate: total > 0 ? e.accepted / total : 0 };
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
withRates.sort((a, b) => b.rate - a.rate || b.total - a.total);
|
|
72
|
+
const topAccepted = withRates.filter(e => e.rate > 0).slice(0, 5);
|
|
73
|
+
const topRejected = withRates.filter(e => e.rate < 1).sort((a, b) => a.rate - b.rate || b.total - a.total).slice(0, 5);
|
|
74
|
+
|
|
75
|
+
return { totalEvents, topAccepted, topRejected, patterns };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function formatUsageSummary(dir) {
|
|
79
|
+
const summary = getUsageSummary(dir);
|
|
80
|
+
if (summary.totalEvents === 0) return ' No usage patterns recorded yet.\n Patterns are tracked when you run nerviq fix.';
|
|
81
|
+
|
|
82
|
+
const lines = [` Usage Patterns (${summary.totalEvents} events recorded):`];
|
|
83
|
+
if (summary.topAccepted.length > 0) {
|
|
84
|
+
lines.push('', ' Most accepted:');
|
|
85
|
+
summary.topAccepted.forEach((e, i) => {
|
|
86
|
+
lines.push(` ${i + 1}. ${e.key.padEnd(20)} ${e.accepted}/${e.total} (${Math.round(e.rate * 100)}%)`);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (summary.topRejected.length > 0) {
|
|
90
|
+
lines.push('', ' Most rejected:');
|
|
91
|
+
summary.topRejected.forEach((e, i) => {
|
|
92
|
+
const hint = e.rate === 0 && e.total >= SUPPRESS_THRESHOLD ? ' -- consider suppressing' : '';
|
|
93
|
+
lines.push(` ${i + 1}. ${e.key.padEnd(20)} ${e.accepted}/${e.total} (${Math.round(e.rate * 100)}%)${hint}`);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return lines.join('\n');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = { loadPatterns, recordPattern, getPriorityAdjustment, getUsageSummary, formatUsageSummary };
|