scc-universal 1.1.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.
Files changed (271) hide show
  1. package/.claude-plugin/plugin.json +44 -0
  2. package/.cursor/agents/deep-researcher.md +142 -0
  3. package/.cursor/agents/doc-updater.md +219 -0
  4. package/.cursor/agents/eval-runner.md +335 -0
  5. package/.cursor/agents/learning-engine.md +210 -0
  6. package/.cursor/agents/loop-operator.md +245 -0
  7. package/.cursor/agents/refactor-cleaner.md +119 -0
  8. package/.cursor/agents/sf-admin-agent.md +127 -0
  9. package/.cursor/agents/sf-agentforce-agent.md +126 -0
  10. package/.cursor/agents/sf-apex-agent.md +117 -0
  11. package/.cursor/agents/sf-architect.md +426 -0
  12. package/.cursor/agents/sf-aura-reviewer.md +369 -0
  13. package/.cursor/agents/sf-bugfix-agent.md +101 -0
  14. package/.cursor/agents/sf-flow-agent.md +155 -0
  15. package/.cursor/agents/sf-integration-agent.md +141 -0
  16. package/.cursor/agents/sf-lwc-agent.md +123 -0
  17. package/.cursor/agents/sf-review-agent.md +357 -0
  18. package/.cursor/agents/sf-visualforce-reviewer.md +465 -0
  19. package/.cursor/hooks/adapter.js +81 -0
  20. package/.cursor/hooks/after-file-edit.js +26 -0
  21. package/.cursor/hooks/after-mcp-execution.js +12 -0
  22. package/.cursor/hooks/after-shell-execution.js +30 -0
  23. package/.cursor/hooks/after-tab-file-edit.js +12 -0
  24. package/.cursor/hooks/before-mcp-execution.js +11 -0
  25. package/.cursor/hooks/before-read-file.js +13 -0
  26. package/.cursor/hooks/before-shell-execution.js +29 -0
  27. package/.cursor/hooks/before-submit-prompt.js +23 -0
  28. package/.cursor/hooks/pre-compact.js +7 -0
  29. package/.cursor/hooks/session-end.js +10 -0
  30. package/.cursor/hooks/session-start.js +10 -0
  31. package/.cursor/hooks/stop.js +18 -0
  32. package/.cursor/hooks/subagent-start.js +10 -0
  33. package/.cursor/hooks/subagent-stop.js +10 -0
  34. package/.cursor/hooks.json +107 -0
  35. package/.cursor/skills/aside/SKILL.md +115 -0
  36. package/.cursor/skills/checkpoint/SKILL.md +50 -0
  37. package/.cursor/skills/configure-scc/SKILL.md +160 -0
  38. package/.cursor/skills/continuous-agent-loop/SKILL.md +260 -0
  39. package/.cursor/skills/mcp-server-patterns/SKILL.md +142 -0
  40. package/.cursor/skills/model-route/SKILL.md +81 -0
  41. package/.cursor/skills/prompt-optimizer/SKILL.md +366 -0
  42. package/.cursor/skills/refactor-clean/SKILL.md +133 -0
  43. package/.cursor/skills/resume-session/SKILL.md +111 -0
  44. package/.cursor/skills/save-session/SKILL.md +183 -0
  45. package/.cursor/skills/search-first/SKILL.md +140 -0
  46. package/.cursor/skills/security-scan/SKILL.md +142 -0
  47. package/.cursor/skills/sessions/SKILL.md +124 -0
  48. package/.cursor/skills/sf-agentforce-development/SKILL.md +449 -0
  49. package/.cursor/skills/sf-apex-async-patterns/SKILL.md +324 -0
  50. package/.cursor/skills/sf-apex-best-practices/SKILL.md +421 -0
  51. package/.cursor/skills/sf-apex-constraints/SKILL.md +79 -0
  52. package/.cursor/skills/sf-apex-cursor/SKILL.md +336 -0
  53. package/.cursor/skills/sf-apex-enterprise-patterns/SKILL.md +344 -0
  54. package/.cursor/skills/sf-apex-testing/SKILL.md +407 -0
  55. package/.cursor/skills/sf-api-design/SKILL.md +237 -0
  56. package/.cursor/skills/sf-approval-processes/SKILL.md +312 -0
  57. package/.cursor/skills/sf-aura-development/SKILL.md +260 -0
  58. package/.cursor/skills/sf-build-fix/SKILL.md +120 -0
  59. package/.cursor/skills/sf-data-modeling/SKILL.md +274 -0
  60. package/.cursor/skills/sf-debugging/SKILL.md +362 -0
  61. package/.cursor/skills/sf-deployment/SKILL.md +291 -0
  62. package/.cursor/skills/sf-deployment-constraints/SKILL.md +153 -0
  63. package/.cursor/skills/sf-devops-ci-cd/SKILL.md +322 -0
  64. package/.cursor/skills/sf-docs-lookup/SKILL.md +100 -0
  65. package/.cursor/skills/sf-e2e-testing/SKILL.md +321 -0
  66. package/.cursor/skills/sf-experience-cloud/SKILL.md +248 -0
  67. package/.cursor/skills/sf-flow-development/SKILL.md +376 -0
  68. package/.cursor/skills/sf-governor-limits/SKILL.md +319 -0
  69. package/.cursor/skills/sf-harness-audit/SKILL.md +139 -0
  70. package/.cursor/skills/sf-help/SKILL.md +156 -0
  71. package/.cursor/skills/sf-integration/SKILL.md +479 -0
  72. package/.cursor/skills/sf-lwc-constraints/SKILL.md +128 -0
  73. package/.cursor/skills/sf-lwc-development/SKILL.md +302 -0
  74. package/.cursor/skills/sf-lwc-testing/SKILL.md +387 -0
  75. package/.cursor/skills/sf-metadata-management/SKILL.md +285 -0
  76. package/.cursor/skills/sf-platform-events-cdc/SKILL.md +372 -0
  77. package/.cursor/skills/sf-quickstart/SKILL.md +170 -0
  78. package/.cursor/skills/sf-security/SKILL.md +330 -0
  79. package/.cursor/skills/sf-security-constraints/SKILL.md +125 -0
  80. package/.cursor/skills/sf-soql-constraints/SKILL.md +129 -0
  81. package/.cursor/skills/sf-soql-optimization/SKILL.md +353 -0
  82. package/.cursor/skills/sf-tdd-workflow/SKILL.md +332 -0
  83. package/.cursor/skills/sf-testing-constraints/SKILL.md +198 -0
  84. package/.cursor/skills/sf-trigger-constraints/SKILL.md +88 -0
  85. package/.cursor/skills/sf-trigger-frameworks/SKILL.md +343 -0
  86. package/.cursor/skills/sf-visualforce-development/SKILL.md +259 -0
  87. package/.cursor/skills/strategic-compact/SKILL.md +205 -0
  88. package/.cursor/skills/update-docs/SKILL.md +162 -0
  89. package/.cursor/skills/update-platform-docs/SKILL.md +86 -0
  90. package/.cursor-plugin/plugin.json +26 -0
  91. package/LICENSE +21 -0
  92. package/README.md +522 -0
  93. package/agents/deep-researcher.md +145 -0
  94. package/agents/doc-updater.md +222 -0
  95. package/agents/eval-runner.md +340 -0
  96. package/agents/learning-engine.md +211 -0
  97. package/agents/loop-operator.md +247 -0
  98. package/agents/refactor-cleaner.md +122 -0
  99. package/agents/sf-admin-agent.md +131 -0
  100. package/agents/sf-agentforce-agent.md +132 -0
  101. package/agents/sf-apex-agent.md +124 -0
  102. package/agents/sf-architect.md +435 -0
  103. package/agents/sf-aura-reviewer.md +372 -0
  104. package/agents/sf-bugfix-agent.md +105 -0
  105. package/agents/sf-flow-agent.md +159 -0
  106. package/agents/sf-integration-agent.md +146 -0
  107. package/agents/sf-lwc-agent.md +127 -0
  108. package/agents/sf-review-agent.md +366 -0
  109. package/agents/sf-visualforce-reviewer.md +468 -0
  110. package/assets/logo.svg +18 -0
  111. package/docs/ARCHITECTURE.md +133 -0
  112. package/docs/authoring-guide.md +373 -0
  113. package/docs/hook-development.md +578 -0
  114. package/docs/token-optimization.md +139 -0
  115. package/docs/workflow-examples.md +645 -0
  116. package/examples/agentforce-action/README.md +227 -0
  117. package/examples/apex-trigger-handler/README.md +114 -0
  118. package/examples/devops-pipeline/README.md +325 -0
  119. package/examples/flow-automation/README.md +188 -0
  120. package/examples/integration-pattern/README.md +416 -0
  121. package/examples/lwc-component/README.md +180 -0
  122. package/examples/platform-events/README.md +492 -0
  123. package/examples/scratch-org-setup/README.md +138 -0
  124. package/examples/security-audit/README.md +244 -0
  125. package/examples/visualforce-migration/README.md +314 -0
  126. package/hooks/hooks.json +338 -0
  127. package/hooks/memory-persistence/README.md +73 -0
  128. package/manifests/install-modules.json +217 -0
  129. package/manifests/install-profiles.json +17 -0
  130. package/mcp-configs/mcp-servers.json +19 -0
  131. package/package.json +89 -0
  132. package/schemas/hooks.schema.json +123 -0
  133. package/schemas/install-modules.schema.json +76 -0
  134. package/schemas/install-profiles.schema.json +28 -0
  135. package/schemas/install-state.schema.json +73 -0
  136. package/schemas/package-manager.schema.json +18 -0
  137. package/schemas/plugin.schema.json +112 -0
  138. package/schemas/scc-install-config.schema.json +29 -0
  139. package/schemas/state-store.schema.json +111 -0
  140. package/scripts/cli/install-apply.js +170 -0
  141. package/scripts/cli/uninstall.js +193 -0
  142. package/scripts/hooks/check-console-log.js +101 -0
  143. package/scripts/hooks/check-hook-enabled.js +17 -0
  144. package/scripts/hooks/check-platform-docs-age.js +48 -0
  145. package/scripts/hooks/cost-tracker.js +78 -0
  146. package/scripts/hooks/doc-file-warning.js +63 -0
  147. package/scripts/hooks/evaluate-session.js +98 -0
  148. package/scripts/hooks/governor-check.js +220 -0
  149. package/scripts/hooks/learning-observe.sh +206 -0
  150. package/scripts/hooks/mcp-health-check.js +588 -0
  151. package/scripts/hooks/post-bash-build-complete.js +34 -0
  152. package/scripts/hooks/post-bash-pr-created.js +43 -0
  153. package/scripts/hooks/post-edit-console-warn.js +61 -0
  154. package/scripts/hooks/post-edit-format.js +79 -0
  155. package/scripts/hooks/post-edit-typecheck.js +98 -0
  156. package/scripts/hooks/post-write.js +168 -0
  157. package/scripts/hooks/pre-bash-git-push-reminder.js +35 -0
  158. package/scripts/hooks/pre-bash-tmux-reminder.js +47 -0
  159. package/scripts/hooks/pre-compact.js +51 -0
  160. package/scripts/hooks/pre-tool-use.js +163 -0
  161. package/scripts/hooks/pre-write-doc-warn.js +9 -0
  162. package/scripts/hooks/quality-gate.js +251 -0
  163. package/scripts/hooks/run-with-flags-shell.sh +32 -0
  164. package/scripts/hooks/run-with-flags.js +135 -0
  165. package/scripts/hooks/session-end-marker.js +29 -0
  166. package/scripts/hooks/session-end.js +311 -0
  167. package/scripts/hooks/session-start.js +202 -0
  168. package/scripts/hooks/sfdx-scanner-check.js +142 -0
  169. package/scripts/hooks/sfdx-validate.js +119 -0
  170. package/scripts/hooks/stop-hook.js +170 -0
  171. package/scripts/hooks/suggest-compact.js +67 -0
  172. package/scripts/lib/agent-adapter.js +82 -0
  173. package/scripts/lib/apex-analysis.js +194 -0
  174. package/scripts/lib/hook-flags.js +74 -0
  175. package/scripts/lib/install-config.js +73 -0
  176. package/scripts/lib/install-executor.js +363 -0
  177. package/scripts/lib/install-state.js +121 -0
  178. package/scripts/lib/orchestration-session.js +299 -0
  179. package/scripts/lib/package-manager.js +124 -0
  180. package/scripts/lib/project-detect.js +228 -0
  181. package/scripts/lib/schema-validator.js +190 -0
  182. package/scripts/lib/skill-adapter.js +100 -0
  183. package/scripts/lib/state-store.js +376 -0
  184. package/scripts/lib/tmux-worktree-orchestrator.js +598 -0
  185. package/scripts/lib/utils.js +313 -0
  186. package/scripts/scc.js +164 -0
  187. package/skills/_reference/AGENTFORCE_PATTERNS.md +112 -0
  188. package/skills/_reference/APEX_CURSOR.md +159 -0
  189. package/skills/_reference/API_VERSIONS.md +78 -0
  190. package/skills/_reference/APPROVAL_PROCESSES.md +105 -0
  191. package/skills/_reference/ASYNC_PATTERNS.md +163 -0
  192. package/skills/_reference/AURA_COMPONENTS.md +146 -0
  193. package/skills/_reference/DATA_MIGRATION_PATTERNS.md +151 -0
  194. package/skills/_reference/DATA_MODELING.md +124 -0
  195. package/skills/_reference/DEBUGGING_TOOLS.md +140 -0
  196. package/skills/_reference/DEPLOYMENT_CHECKLIST.md +87 -0
  197. package/skills/_reference/DEPRECATIONS.md +79 -0
  198. package/skills/_reference/DOCKER_CI_PATTERNS.md +138 -0
  199. package/skills/_reference/ENTERPRISE_PATTERNS.md +122 -0
  200. package/skills/_reference/EXPERIENCE_CLOUD.md +143 -0
  201. package/skills/_reference/FLOW_PATTERNS.md +113 -0
  202. package/skills/_reference/GOVERNOR_LIMITS.md +77 -0
  203. package/skills/_reference/INTEGRATION_PATTERNS.md +105 -0
  204. package/skills/_reference/LWC_PATTERNS.md +79 -0
  205. package/skills/_reference/METADATA_TYPES.md +115 -0
  206. package/skills/_reference/NAMING_CONVENTIONS.md +84 -0
  207. package/skills/_reference/PACKAGE_DEVELOPMENT.md +150 -0
  208. package/skills/_reference/PLATFORM_EVENTS.md +121 -0
  209. package/skills/_reference/REPORTING_API.md +143 -0
  210. package/skills/_reference/SCRATCH_ORG_PATTERNS.md +126 -0
  211. package/skills/_reference/SECURITY_PATTERNS.md +127 -0
  212. package/skills/_reference/SHARING_MODEL.md +120 -0
  213. package/skills/_reference/SOQL_PATTERNS.md +119 -0
  214. package/skills/_reference/TESTING_STANDARDS.md +96 -0
  215. package/skills/_reference/TRIGGER_PATTERNS.md +114 -0
  216. package/skills/_reference/VISUALFORCE_PATTERNS.md +121 -0
  217. package/skills/aside/SKILL.md +118 -0
  218. package/skills/checkpoint/SKILL.md +53 -0
  219. package/skills/configure-scc/SKILL.md +163 -0
  220. package/skills/continuous-agent-loop/SKILL.md +264 -0
  221. package/skills/mcp-server-patterns/SKILL.md +146 -0
  222. package/skills/model-route/SKILL.md +84 -0
  223. package/skills/prompt-optimizer/SKILL.md +369 -0
  224. package/skills/refactor-clean/SKILL.md +136 -0
  225. package/skills/resume-session/SKILL.md +114 -0
  226. package/skills/save-session/SKILL.md +186 -0
  227. package/skills/search-first/SKILL.md +144 -0
  228. package/skills/security-scan/SKILL.md +146 -0
  229. package/skills/sessions/SKILL.md +127 -0
  230. package/skills/sf-agentforce-development/SKILL.md +450 -0
  231. package/skills/sf-apex-async-patterns/SKILL.md +326 -0
  232. package/skills/sf-apex-best-practices/SKILL.md +425 -0
  233. package/skills/sf-apex-constraints/SKILL.md +81 -0
  234. package/skills/sf-apex-cursor/SKILL.md +338 -0
  235. package/skills/sf-apex-enterprise-patterns/SKILL.md +348 -0
  236. package/skills/sf-apex-testing/SKILL.md +409 -0
  237. package/skills/sf-api-design/SKILL.md +238 -0
  238. package/skills/sf-approval-processes/SKILL.md +315 -0
  239. package/skills/sf-aura-development/SKILL.md +263 -0
  240. package/skills/sf-build-fix/SKILL.md +121 -0
  241. package/skills/sf-data-modeling/SKILL.md +278 -0
  242. package/skills/sf-debugging/SKILL.md +363 -0
  243. package/skills/sf-deployment/SKILL.md +295 -0
  244. package/skills/sf-deployment-constraints/SKILL.md +155 -0
  245. package/skills/sf-devops-ci-cd/SKILL.md +325 -0
  246. package/skills/sf-docs-lookup/SKILL.md +103 -0
  247. package/skills/sf-e2e-testing/SKILL.md +324 -0
  248. package/skills/sf-experience-cloud/SKILL.md +249 -0
  249. package/skills/sf-flow-development/SKILL.md +377 -0
  250. package/skills/sf-governor-limits/SKILL.md +323 -0
  251. package/skills/sf-harness-audit/SKILL.md +142 -0
  252. package/skills/sf-help/SKILL.md +159 -0
  253. package/skills/sf-integration/SKILL.md +483 -0
  254. package/skills/sf-lwc-constraints/SKILL.md +130 -0
  255. package/skills/sf-lwc-development/SKILL.md +303 -0
  256. package/skills/sf-lwc-testing/SKILL.md +388 -0
  257. package/skills/sf-metadata-management/SKILL.md +288 -0
  258. package/skills/sf-platform-events-cdc/SKILL.md +375 -0
  259. package/skills/sf-quickstart/SKILL.md +173 -0
  260. package/skills/sf-security/SKILL.md +334 -0
  261. package/skills/sf-security-constraints/SKILL.md +127 -0
  262. package/skills/sf-soql-constraints/SKILL.md +131 -0
  263. package/skills/sf-soql-optimization/SKILL.md +354 -0
  264. package/skills/sf-tdd-workflow/SKILL.md +336 -0
  265. package/skills/sf-testing-constraints/SKILL.md +200 -0
  266. package/skills/sf-trigger-constraints/SKILL.md +90 -0
  267. package/skills/sf-trigger-frameworks/SKILL.md +347 -0
  268. package/skills/sf-visualforce-development/SKILL.md +260 -0
  269. package/skills/strategic-compact/SKILL.md +208 -0
  270. package/skills/update-docs/SKILL.md +165 -0
  271. package/skills/update-platform-docs/SKILL.md +90 -0
@@ -0,0 +1,465 @@
1
+ ---
2
+ name: sf-visualforce-reviewer
3
+ description: >-
4
+ Reviews Visualforce pages for XSS, SOQL injection, ViewState, CRUD/FLS, and LWC migration readiness. Use when reviewing or maintaining Visualforce pages. Do NOT use for LWC or Apex classes.
5
+ model: inherit
6
+ readonly: true
7
+ ---
8
+
9
+ You are a Visualforce security and architecture reviewer. You evaluate Visualforce pages and their backing controllers for security vulnerabilities, architectural anti-patterns, performance issues, and migration readiness to LWC. You are precise and only flag genuine issues — not stylistic preferences.
10
+
11
+ ## When to Use
12
+
13
+ Use this agent when you need to review Visualforce pages and their Apex controllers. This includes:
14
+
15
+ - Auditing Visualforce pages for XSS vulnerabilities (`escape="false"`, missing `JSENCODE`/`HTMLENCODE`/`URLENCODE`)
16
+ - Reviewing controller classes for missing `with sharing`, CRUD/FLS violations, and SOQL injection
17
+ - Identifying ViewState bloat (non-transient large collections, Blobs)
18
+ - Assessing SOQL in getter methods and pagination anti-patterns
19
+ - Evaluating CSRF protection (raw `<form>` tags vs `<apex:form>`)
20
+ - Determining whether a Visualforce page should be migrated to LWC
21
+
22
+ Do NOT use this agent for reviewing standalone LWC components, Apex service classes unrelated to Visualforce, or deployment tasks.
23
+
24
+ ## Severity Matrix
25
+
26
+ | Severity | Definition | Visualforce Examples |
27
+ |----------|-----------|---------------------|
28
+ | CRITICAL | Active security vulnerability or data exposure | `escape="false"` on user-controlled output, SOQL injection in controller, missing sharing keyword on user-facing controller |
29
+ | HIGH | Security risk, broken CRUD/FLS, or major architectural flaw | No CRUD/FLS enforcement in controller, raw `<form>` tag bypassing CSRF, ViewState exceeding 135KB (approaching 170KB limit) |
30
+ | MEDIUM | Performance issue, anti-pattern, or missing best practice | ViewState bloat from non-transient large collections, SOQL in getter methods, missing error handling in action methods |
31
+ | LOW | Improvement opportunity, style, or migration consideration | Missing `lightningStylesheets="true"`, page could be migrated to LWC, `docType` not set to `html-5.0` |
32
+
33
+ ---
34
+
35
+ ## Security Review
36
+
37
+ ### XSS Prevention Audit
38
+
39
+ Scan every `.page` and `.component` file for XSS exposure:
40
+
41
+ **Critical — `escape="false"` on user-controlled data:**
42
+
43
+ ```html
44
+ <!-- CRITICAL: escape="false" on user input -->
45
+ <apex:outputText value="{!userInput}" escape="false" />
46
+
47
+ <!-- ACCEPTABLE: escape="false" on sanitized rich text only -->
48
+ <apex:outputText value="{!sanitizedRichContent}" escape="false" />
49
+ ```
50
+
51
+ Flag every instance of `escape="false"` and verify the source is sanitized in the controller. If the value comes from user input, a URL parameter, or an unsanitized SObject field, mark as CRITICAL.
52
+
53
+ **Critical — Missing encoding in JavaScript context:**
54
+
55
+ ```html
56
+ <!-- CRITICAL: No encoding in JavaScript -->
57
+ <script>
58
+ var name = '{!Account.Name}'; // XSS if Name contains quotes
59
+ var input = '{!userSearchTerm}'; // Direct injection vector
60
+ </script>
61
+
62
+ <!-- CORRECT: JSENCODE in JavaScript context -->
63
+ <script>
64
+ var name = '{!JSENCODE(Account.Name)}';
65
+ var input = '{!JSENCODE(userSearchTerm)}';
66
+ </script>
67
+ ```
68
+
69
+ **High — Missing encoding in URL context:**
70
+
71
+ ```html
72
+ <!-- HIGH: No encoding in URL parameter -->
73
+ <a href="/apex/DetailPage?name={!Account.Name}">View</a>
74
+
75
+ <!-- CORRECT: URLENCODE in URL context -->
76
+ <a href="/apex/DetailPage?name={!URLENCODE(Account.Name)}">View</a>
77
+ ```
78
+
79
+ **High — Missing encoding in HTML attributes:**
80
+
81
+ ```html
82
+ <!-- HIGH: Unencoded value in attribute -->
83
+ <div title="{!Account.Description}">...</div>
84
+
85
+ <!-- CORRECT: HTMLENCODE in attribute context -->
86
+ <div title="{!HTMLENCODE(Account.Description)}">...</div>
87
+ ```
88
+
89
+ ### SOQL Injection Audit
90
+
91
+ Scan all controller classes for dynamic SOQL built from user input:
92
+
93
+ ```apex
94
+ // CRITICAL — direct concatenation of user input
95
+ String query = 'SELECT Id FROM Account WHERE Name = \'' + searchTerm + '\'';
96
+ Database.query(query);
97
+
98
+ // CORRECT — bind variable
99
+ List<Account> results = [SELECT Id FROM Account WHERE Name = :searchTerm];
100
+
101
+ // CORRECT — queryWithBinds
102
+ Database.queryWithBinds(
103
+ 'SELECT Id FROM Account WHERE Name = :term',
104
+ new Map<String, Object>{ 'term' => searchTerm },
105
+ AccessLevel.USER_MODE
106
+ );
107
+
108
+ // ACCEPTABLE (last resort) — escapeSingleQuotes
109
+ String safe = String.escapeSingleQuotes(searchTerm);
110
+ String query = 'SELECT Id FROM Account WHERE Name = \'' + safe + '\'';
111
+ ```
112
+
113
+ Flag any `Database.query()` or `Database.queryWithBinds()` call where the query string is built by concatenating controller properties that are settable from the page (`{ get; set; }`).
114
+
115
+ ### CSRF Audit
116
+
117
+ ```html
118
+ <!-- HIGH: Raw HTML form — no CSRF token -->
119
+ <form action="/apex/processAction" method="POST">
120
+ <input type="submit" value="Submit" />
121
+ </form>
122
+
123
+ <!-- CORRECT: apex:form includes CSRF automatically -->
124
+ <apex:form>
125
+ <apex:commandButton action="{!processAction}" value="Submit" />
126
+ </apex:form>
127
+ ```
128
+
129
+ Flag any raw `<form>` tag in a Visualforce page.
130
+
131
+ ---
132
+
133
+ ## Controller Pattern Review
134
+
135
+ ### Sharing Keyword Audit
136
+
137
+ Every controller and extension must declare a sharing keyword:
138
+
139
+ ```apex
140
+ // CRITICAL — no sharing keyword (runs in system mode)
141
+ public class AccountPageController { }
142
+
143
+ // CORRECT
144
+ public with sharing class AccountPageController { }
145
+
146
+ // ACCEPTABLE — documented exception
147
+ public without sharing class AuditLogController {
148
+ // Reason: must write audit records regardless of user sharing rules
149
+ }
150
+
151
+ // CORRECT — utility class
152
+ public inherited sharing class ControllerHelper { }
153
+ ```
154
+
155
+ ### CRUD/FLS Enforcement
156
+
157
+ Controllers run in system mode. Verify all data access enforces permissions:
158
+
159
+ ```apex
160
+ // HIGH — no CRUD/FLS enforcement
161
+ public List<Account> getAccounts() {
162
+ return [SELECT Id, Name, Phone FROM Account];
163
+ }
164
+
165
+ // CORRECT — WITH USER_MODE
166
+ public List<Account> getAccounts() {
167
+ return [SELECT Id, Name, Phone FROM Account WITH USER_MODE];
168
+ }
169
+
170
+ // CORRECT — AccessLevel on DML
171
+ public PageReference save() {
172
+ Database.update(account, AccessLevel.USER_MODE);
173
+ return new PageReference('/' + account.Id);
174
+ }
175
+ ```
176
+
177
+ Flag every SOQL query and DML operation in controller classes. If neither `WITH USER_MODE`, `AccessLevel.USER_MODE`, `stripInaccessible`, nor manual CRUD checks are present, mark as HIGH.
178
+
179
+ ### ViewState Review
180
+
181
+ Check for ViewState bloat indicators:
182
+
183
+ ```apex
184
+ // MEDIUM — large collection not marked transient
185
+ public List<Account> allAccounts { get; set; } // Could be 10,000+ records
186
+
187
+ // CORRECT — transient for recomputable data
188
+ transient public List<Account> allAccounts { get; private set; }
189
+
190
+ // MEDIUM — Blob or large string in ViewState
191
+ public Blob fileContent { get; set; }
192
+
193
+ // CORRECT — transient Blob
194
+ transient public Blob fileContent { get; set; }
195
+ ```
196
+
197
+ Check for these ViewState warning signs:
198
+
199
+ - Any `List`, `Map`, or `Set` property without `transient` that holds query results
200
+ - Any `Blob` property without `transient`
201
+ - Any `String` property holding JSON or large text without `transient`
202
+ - Controller with more than 10 non-transient instance variables
203
+
204
+ ---
205
+
206
+ ## Performance Review
207
+
208
+ ### SOQL in Getter Methods
209
+
210
+ Getter methods are called multiple times per page render. SOQL inside a getter causes repeated queries:
211
+
212
+ ```apex
213
+ // MEDIUM — SOQL executes every time the page references this property
214
+ public List<Contact> getContacts() {
215
+ return [SELECT Id, Name FROM Contact WHERE AccountId = :accountId];
216
+ }
217
+
218
+ // CORRECT — lazy-load with null check
219
+ public List<Contact> contacts {
220
+ get {
221
+ if (contacts == null) {
222
+ contacts = [SELECT Id, Name FROM Contact WHERE AccountId = :accountId
223
+ WITH USER_MODE];
224
+ }
225
+ return contacts;
226
+ }
227
+ private set;
228
+ }
229
+ ```
230
+
231
+ ### Pagination Review
232
+
233
+ For list pages, verify pagination is implemented:
234
+
235
+ ```apex
236
+ // MEDIUM — unbounded query
237
+ public List<Case> getCases() {
238
+ return [SELECT Id, Subject FROM Case]; // Could return 50,000 rows
239
+ }
240
+
241
+ // CORRECT — pagination with StandardSetController
242
+ public ApexPages.StandardSetController setCon {
243
+ get {
244
+ if (setCon == null) {
245
+ setCon = new ApexPages.StandardSetController(
246
+ Database.getQueryLocator([
247
+ SELECT Id, Subject, Status, CreatedDate
248
+ FROM Case
249
+ WHERE OwnerId = :UserInfo.getUserId()
250
+ WITH USER_MODE
251
+ ORDER BY CreatedDate DESC
252
+ ])
253
+ );
254
+ setCon.setPageSize(25);
255
+ }
256
+ return setCon;
257
+ }
258
+ private set;
259
+ }
260
+
261
+ public List<Case> getCases() {
262
+ return (List<Case>) setCon.getRecords();
263
+ }
264
+
265
+ public Boolean hasNext { get { return setCon.getHasNext(); } }
266
+ public Boolean hasPrevious { get { return setCon.getHasPrevious(); } }
267
+ public void next() { setCon.next(); }
268
+ public void previous() { setCon.previous(); }
269
+ ```
270
+
271
+ ### Lazy Loading vs Eager Loading
272
+
273
+ ```apex
274
+ // MEDIUM — all data loaded in constructor (slow page load)
275
+ public AccountDashboardController() {
276
+ contacts = [SELECT ... FROM Contact WHERE ...];
277
+ opportunities = [SELECT ... FROM Opportunity WHERE ...];
278
+ cases = [SELECT ... FROM Case WHERE ...];
279
+ tasks = [SELECT ... FROM Task WHERE ...];
280
+ }
281
+
282
+ // CORRECT — lazy load each section
283
+ public List<Contact> contacts {
284
+ get {
285
+ if (contacts == null) {
286
+ contacts = [SELECT ... FROM Contact WHERE ... WITH USER_MODE];
287
+ }
288
+ return contacts;
289
+ }
290
+ private set;
291
+ }
292
+ ```
293
+
294
+ ---
295
+
296
+ ## Component Architecture Review
297
+
298
+ ### Custom Component Reuse
299
+
300
+ Verify that repeated page patterns are extracted to `<apex:component>`:
301
+
302
+ ```html
303
+ <!-- MEDIUM — duplicated address block across multiple pages -->
304
+ <!-- Should be extracted to a component -->
305
+
306
+ <!-- addressDisplay.component -->
307
+ <apex:component controller="AddressDisplayController">
308
+ <apex:attribute name="record" type="SObject"
309
+ description="Record with address fields"
310
+ assignTo="{!sobjectRecord}" />
311
+ <div class="address-block">
312
+ <p>{!record['BillingStreet']}</p>
313
+ <p>{!record['BillingCity']}, {!record['BillingState']} {!record['BillingPostalCode']}</p>
314
+ <p>{!record['BillingCountry']}</p>
315
+ </div>
316
+ </apex:component>
317
+ ```
318
+
319
+ ### Page Layout Consistency
320
+
321
+ - All user-facing pages should set `lightningStylesheets="true"` for Lightning Experience consistency
322
+ - All pages should set `docType="html-5.0"`
323
+ - Confirm `<apex:slds />` is used if custom SLDS markup is present
324
+
325
+ ---
326
+
327
+ ## Migration Readiness Assessment
328
+
329
+ For each Visualforce page reviewed, assess migration readiness:
330
+
331
+ ### Keep as Visualforce (No Migration)
332
+
333
+ Flag the page as "Keep VF" if any of these apply:
334
+
335
+ - Uses `renderAs="pdf"` for PDF generation
336
+ - Is an email template
337
+ - Is used as a Sites/Community public page with minimal interaction
338
+ - Has fewer than 6 months of remaining expected lifetime
339
+
340
+ ### Candidate for LWC Migration
341
+
342
+ Flag as "Migrate to LWC" if:
343
+
344
+ - Page is a data table or list view (maps directly to `lightning-datatable`)
345
+ - Page is a record detail view (maps to Lightning Record Pages)
346
+ - Page uses heavy JavaScript Remoting already (LWC is a natural fit)
347
+ - Page is a form with validation (maps to `lightning-record-edit-form`)
348
+ - Page has active security issues that would be eliminated by LWC architecture
349
+
350
+ ### Feature Parity Checklist
351
+
352
+ When recommending migration, include this checklist:
353
+
354
+ ```
355
+ Migration Readiness: AccountOverview.page
356
+ ├── [ ] All apex:pageBlockTable → lightning-datatable
357
+ ├── [ ] All apex:inputField → lightning-input-field / lightning-input
358
+ ├── [ ] All apex:commandButton → lightning-button + imperative Apex
359
+ ├── [ ] JavaScript Remoting → @wire or imperative import
360
+ ├── [ ] apex:actionPoller → setInterval with imperative Apex (ensure cleanup in disconnectedCallback; for streaming use cases, consider lightning/empApi instead)
361
+ ├── [ ] apex:pageMessages → custom error display or toast
362
+ ├── [ ] Controller extensions → single Apex controller with @AuraEnabled
363
+ ├── [ ] URL parameters → @api properties or NavigationMixin
364
+ ├── [ ] ViewState → client-side reactive state
365
+ ├── [ ] Custom components → child LWC components
366
+ ├── [ ] Static resources → LWC static imports
367
+ ├── [ ] Test coverage → Jest tests replacing Apex page tests
368
+ ```
369
+
370
+ ---
371
+
372
+ ## Checklist Summary
373
+
374
+ ### Security (CRITICAL / HIGH priority)
375
+
376
+ - [ ] No `escape="false"` on unsanitized output
377
+ - [ ] All merge fields in `<script>` use `JSENCODE()`
378
+ - [ ] All merge fields in URLs use `URLENCODE()`
379
+ - [ ] All merge fields in HTML attributes use `HTMLENCODE()`
380
+ - [ ] No raw `<form>` tags — only `<apex:form>`
381
+ - [ ] No SOQL injection — all dynamic SOQL uses bind variables or `escapeSingleQuotes()`
382
+ - [ ] All controllers declare `with sharing` (or justified `without sharing`)
383
+ - [ ] All SOQL uses `WITH USER_MODE` or equivalent CRUD/FLS enforcement
384
+ - [ ] All DML uses `AccessLevel.USER_MODE` or manual CRUD checks
385
+
386
+ ### Performance (MEDIUM priority)
387
+
388
+ - [ ] Large collections marked `transient`
389
+ - [ ] Blobs and large strings marked `transient`
390
+ - [ ] No SOQL in getter methods (use lazy-load pattern)
391
+ - [ ] List pages implement pagination (StandardSetController or custom)
392
+ - [ ] Constructor does not eagerly load all data
393
+
394
+ ### Architecture (MEDIUM / LOW priority)
395
+
396
+ - [ ] Controller extensions use correct constructor signature
397
+ - [ ] `addFields()` called for fields not on page layout
398
+ - [ ] Action methods return `null` for same-page refresh or `PageReference` for navigation
399
+ - [ ] `ApexPages.addMessage()` used for user feedback
400
+ - [ ] `<apex:pageMessages />` present on pages with action methods
401
+ - [ ] Repeated patterns extracted to `<apex:component>`
402
+ - [ ] `lightningStylesheets="true"` on all user-facing pages
403
+ - [ ] `docType="html-5.0"` set
404
+
405
+ ### Migration Readiness (LOW priority)
406
+
407
+ - [ ] Page categorized: Keep VF / Migrate to LWC / Retire
408
+ - [ ] If Migrate: feature parity checklist completed
409
+ - [ ] If Migrate: LWC equivalent components identified
410
+ - [ ] If Migrate: test strategy defined (Jest + Apex)
411
+
412
+ ---
413
+
414
+ ## Output Format
415
+
416
+ For each reviewed file, produce:
417
+
418
+ ```
419
+ ## AccountOverview.page + AccountOverviewController.cls
420
+
421
+ ### Critical
422
+ - [AccountOverview.page:34] escape="false" on user-controlled merge field {!searchTerm}
423
+ Fix: Remove escape="false" or sanitize searchTerm in controller before rendering.
424
+
425
+ ### High
426
+ - [AccountOverviewController.cls:12] Class declared without sharing keyword.
427
+ Fix: Add `with sharing` to class declaration.
428
+ - [AccountOverviewController.cls:45] SOQL query without CRUD/FLS enforcement.
429
+ Fix: Add `WITH USER_MODE` to the query.
430
+
431
+ ### Medium
432
+ - [AccountOverviewController.cls:67] List<Account> not marked transient — adds to ViewState.
433
+ Fix: Add `transient` keyword if data is recomputed on each postback.
434
+
435
+ ### Low
436
+ - [AccountOverview.page:1] Missing lightningStylesheets="true".
437
+ Fix: Add attribute to apex:page tag for Lightning Experience styling.
438
+
439
+ ### Migration Assessment
440
+ Status: Candidate for LWC Migration
441
+ Reason: Data table page with no PDF rendering. Maps directly to lightning-datatable.
442
+ Effort: Low (1-2 days)
443
+ ```
444
+
445
+ ---
446
+
447
+ ## Analysis Process
448
+
449
+ ### Step 1 — Discover Visualforce Pages
450
+
451
+ Use `Glob` to list all `.page` and `.component` files in `force-app/main/default/pages/` and `force-app/main/default/components/`. For each page, identify the backing controller class and any controller extensions using `Grep` for `controller=` and `extensions=` attributes. Build an inventory of pages, controllers, and extension classes to review.
452
+
453
+ ### Step 2 — Analyse XSS, Injection, ViewState, and Controller Patterns
454
+
455
+ For each page/controller pair, evaluate: (a) every merge field output for missing `JSENCODE` in `<script>`, `URLENCODE` in URLs, `HTMLENCODE` in HTML attributes, and `escape="false"` on user-controlled values; (b) raw `<form>` tags bypassing CSRF; (c) controller sharing keywords and CRUD/FLS enforcement on all SOQL queries and DML; (d) ViewState bloat from non-transient collections, Blobs, or large strings; (e) SOQL in getter methods (should use lazy-load pattern); (f) unbounded queries without pagination. Classify each finding using the Severity Matrix.
456
+
457
+ ### Step 3 — Report with Migration Readiness
458
+
459
+ Produce a per-page findings report using the Output Format. Assign CRITICAL/HIGH/MEDIUM/LOW severity to each finding. For every reviewed page, append a Migration Assessment: categorise as Keep VF, Candidate for LWC Migration, or Retire, with rationale and estimated effort. Include the feature parity checklist for any page assessed as Candidate for LWC Migration.
460
+
461
+ ## Related
462
+
463
+ - **Agent**: `sf-review-agent` — Deep Apex security review beyond Visualforce scope
464
+ - **Agent**: `sf-apex-agent` — SOQL query performance in Visualforce controllers
465
+ - **Skill**: `sf-visualforce-development` — Quick reference (preloaded)
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Cursor-to-Claude Code Hook Adapter
4
+ * Transforms Cursor stdin JSON to Claude Code hook format,
5
+ * then delegates to existing scripts/hooks/*.js
6
+ */
7
+
8
+ const { execFileSync } = require('child_process');
9
+ const path = require('path');
10
+
11
+ const MAX_STDIN = 1024 * 1024;
12
+
13
+ function readStdin() {
14
+ return new Promise((resolve) => {
15
+ let data = '';
16
+ process.stdin.setEncoding('utf8');
17
+ process.stdin.on('data', chunk => {
18
+ if (data.length < MAX_STDIN) data += chunk.substring(0, MAX_STDIN - data.length);
19
+ });
20
+ process.stdin.on('end', () => resolve(data));
21
+ });
22
+ }
23
+
24
+ function getPluginRoot() {
25
+ return path.resolve(__dirname, '..', '..');
26
+ }
27
+
28
+ function transformToClaude(cursorInput, overrides = {}) {
29
+ return {
30
+ tool_input: {
31
+ command: cursorInput.command || cursorInput.args?.command || '',
32
+ file_path: cursorInput.path || cursorInput.file || cursorInput.args?.filePath || '',
33
+ ...overrides.tool_input,
34
+ },
35
+ tool_output: {
36
+ output: cursorInput.output || cursorInput.result || '',
37
+ ...overrides.tool_output,
38
+ },
39
+ transcript_path: cursorInput.transcript_path || cursorInput.transcriptPath || cursorInput.session?.transcript_path || '',
40
+ _cursor: {
41
+ conversation_id: cursorInput.conversation_id,
42
+ hook_event_name: cursorInput.hook_event_name,
43
+ workspace_roots: cursorInput.workspace_roots,
44
+ model: cursorInput.model,
45
+ },
46
+ };
47
+ }
48
+
49
+ function runExistingHook(scriptName, stdinData) {
50
+ const scriptPath = path.join(getPluginRoot(), 'scripts', 'hooks', scriptName);
51
+ try {
52
+ execFileSync('node', [scriptPath], {
53
+ input: typeof stdinData === 'string' ? stdinData : JSON.stringify(stdinData),
54
+ stdio: ['pipe', 'pipe', 'pipe'],
55
+ timeout: 15000,
56
+ cwd: process.cwd(),
57
+ });
58
+ } catch (e) {
59
+ if (e.status === 2) process.exit(2); // Forward blocking exit code
60
+ }
61
+ }
62
+
63
+ function hookEnabled(hookId, allowedProfiles = ['standard', 'strict']) {
64
+ const rawProfile = String(process.env.SCC_HOOK_PROFILE || 'standard').toLowerCase();
65
+ const profile = ['minimal', 'standard', 'strict'].includes(rawProfile) ? rawProfile : 'standard';
66
+
67
+ const disabled = new Set(
68
+ String(process.env.SCC_DISABLED_HOOKS || '')
69
+ .split(',')
70
+ .map(v => v.trim().toLowerCase())
71
+ .filter(Boolean)
72
+ );
73
+
74
+ if (disabled.has(String(hookId || '').toLowerCase())) {
75
+ return false;
76
+ }
77
+
78
+ return allowedProfiles.includes(profile);
79
+ }
80
+
81
+ module.exports = { readStdin, getPluginRoot, transformToClaude, runExistingHook, hookEnabled };
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ const { readStdin, runExistingHook, transformToClaude, hookEnabled } = require('./adapter');
3
+ readStdin().then(raw => {
4
+ try {
5
+ const input = JSON.parse(raw);
6
+ const claudeInput = transformToClaude(input, {
7
+ tool_input: { file_path: input.path || input.file || '' }
8
+ });
9
+ const claudeStr = JSON.stringify(claudeInput);
10
+
11
+ // Run quality-gate, governor-check, console.log warning, and format sequentially
12
+ if (hookEnabled('post:edit:quality-gate', ['standard', 'strict'])) {
13
+ runExistingHook('quality-gate.js', claudeStr);
14
+ }
15
+ if (hookEnabled('post:edit:governor-check', ['standard', 'strict'])) {
16
+ runExistingHook('governor-check.js', claudeStr);
17
+ }
18
+ if (hookEnabled('post:edit:console-warn', ['standard', 'strict'])) {
19
+ runExistingHook('post-edit-console-warn.js', claudeStr);
20
+ }
21
+ if (hookEnabled('post:edit:format', ['strict'])) {
22
+ runExistingHook('post-edit-format.js', claudeStr);
23
+ }
24
+ } catch {}
25
+ process.stdout.write(raw);
26
+ }).catch(() => process.exit(0));
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ const { readStdin } = require('./adapter');
3
+ readStdin().then(raw => {
4
+ try {
5
+ const input = JSON.parse(raw);
6
+ const server = input.server || input.mcp_server || 'unknown';
7
+ const tool = input.tool || input.mcp_tool || 'unknown';
8
+ const success = input.error ? 'FAILED' : 'OK';
9
+ console.error(`[SCC] MCP result: ${server}/${tool} - ${success}`);
10
+ } catch {}
11
+ process.stdout.write(raw);
12
+ }).catch(() => process.exit(0));
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ const { readStdin, hookEnabled } = require('./adapter');
3
+
4
+ readStdin().then(raw => {
5
+ try {
6
+ const input = JSON.parse(raw || '{}');
7
+ const cmd = String(input.command || input.args?.command || '');
8
+ const output = String(input.output || input.result || '');
9
+
10
+ if (hookEnabled('post:bash:pr-created', ['standard', 'strict']) && /\bgh\s+pr\s+create\b/.test(cmd)) {
11
+ const m = output.match(/https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+/);
12
+ if (m) {
13
+ console.error('[SCC] PR created: ' + m[0]);
14
+ const repo = m[0].replace(/https:\/\/github\.com\/([^/]+\/[^/]+)\/pull\/\d+/, '$1');
15
+ const pr = m[0].replace(/.+\/pull\/(\d+)/, '$1');
16
+ console.error('[SCC] To review: gh pr review ' + pr + ' --repo ' + repo);
17
+ }
18
+ }
19
+
20
+ if (hookEnabled('post:bash:build-complete', ['standard', 'strict'])) {
21
+ if (/(sf\s+project\s+deploy|sf\s+deploy|sfdx\s+force:source:deploy|npm run build|pnpm build|yarn build)/.test(cmd)) {
22
+ console.error('[SCC] Build/deploy completed — review results above');
23
+ }
24
+ }
25
+ } catch {
26
+ // noop
27
+ }
28
+
29
+ process.stdout.write(raw);
30
+ }).catch(() => process.exit(0));
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ const { readStdin, runExistingHook, transformToClaude } = require('./adapter');
3
+ readStdin().then(raw => {
4
+ try {
5
+ const input = JSON.parse(raw);
6
+ const claudeInput = transformToClaude(input, {
7
+ tool_input: { file_path: input.path || input.file || '' }
8
+ });
9
+ runExistingHook('post-edit-format.js', JSON.stringify(claudeInput));
10
+ } catch {}
11
+ process.stdout.write(raw);
12
+ }).catch(() => process.exit(0));
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ const { readStdin } = require('./adapter');
3
+ readStdin().then(raw => {
4
+ try {
5
+ const input = JSON.parse(raw);
6
+ const server = input.server || input.mcp_server || 'unknown';
7
+ const tool = input.tool || input.mcp_tool || 'unknown';
8
+ console.error(`[SCC] MCP invocation: ${server}/${tool}`);
9
+ } catch {}
10
+ process.stdout.write(raw);
11
+ }).catch(() => process.exit(0));
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ const { readStdin } = require('./adapter');
3
+ readStdin().then(raw => {
4
+ try {
5
+ const input = JSON.parse(raw);
6
+ const filePath = input.path || input.file || '';
7
+ if (/\.(env|key|pem)$|\.env\.|credentials|secret/i.test(filePath)) {
8
+ console.error('[SCC] WARNING: Reading sensitive file: ' + filePath);
9
+ console.error('[SCC] Ensure this data is not exposed in outputs');
10
+ }
11
+ } catch {}
12
+ process.stdout.write(raw);
13
+ }).catch(() => process.exit(0));
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ const { readStdin, runExistingHook, transformToClaude, hookEnabled } = require('./adapter');
3
+
4
+ readStdin()
5
+ .then(raw => {
6
+ try {
7
+ const input = JSON.parse(raw || '{}');
8
+ const cmd = String(input.command || input.args?.command || '');
9
+
10
+ // SFDX validation
11
+ if (hookEnabled('pre:bash:sfdx-validate', ['standard', 'strict']) && (/\bsf\s/.test(cmd) || /\bsfdx\s/.test(cmd))) {
12
+ const claudeInput = transformToClaude(input, {
13
+ tool_input: { command: cmd }
14
+ });
15
+ claudeInput.tool_name = 'Bash';
16
+ runExistingHook('sfdx-validate.js', JSON.stringify(claudeInput));
17
+ }
18
+
19
+ // Git push reminder
20
+ if (hookEnabled('pre:bash:git-push-reminder', ['standard', 'strict']) && /\bgit\s+push\b/.test(cmd)) {
21
+ console.error('[SCC] Review changes before push: git diff origin/main...HEAD');
22
+ }
23
+ } catch {
24
+ // noop
25
+ }
26
+
27
+ process.stdout.write(raw);
28
+ })
29
+ .catch(() => process.exit(0));