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,163 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * pre-tool-use.js — PreToolUse hook for SCC.
6
+ *
7
+ * Reads the tool use event from stdin (JSON) and:
8
+ * 1. Warns if deprecated `sfdx` commands are used (suggest `sf` equivalent)
9
+ * 2. Warns about dangerous org operations (delete, reset, deploy to prod)
10
+ *
11
+ * Claude Code sends hook input as JSON on stdin:
12
+ * { tool_name: "Bash", tool_input: { command: "..." } }
13
+ */
14
+
15
+ const readline = require('readline');
16
+
17
+ // Map of deprecated sfdx commands to their sf equivalents
18
+ const SFDX_MIGRATION_MAP = {
19
+ 'sfdx force:apex:execute': 'sf apex run',
20
+ 'sfdx force:apex:test:run': 'sf apex run test',
21
+ 'sfdx force:apex:class:create': 'sf apex generate class',
22
+ 'sfdx force:lightning:component:create': 'sf lightning generate component',
23
+ 'sfdx force:source:deploy': 'sf project deploy start',
24
+ 'sfdx force:source:retrieve': 'sf project retrieve start',
25
+ 'sfdx force:source:push': 'sf project deploy start',
26
+ 'sfdx force:source:pull': 'sf project retrieve start',
27
+ 'sfdx force:org:create': 'sf org create scratch',
28
+ 'sfdx force:org:delete': 'sf org delete scratch',
29
+ 'sfdx force:org:list': 'sf org list',
30
+ 'sfdx force:org:open': 'sf org open',
31
+ 'sfdx force:data:query': 'sf data query',
32
+ 'sfdx force:data:record:create': 'sf data create record',
33
+ 'sfdx force:data:record:update': 'sf data update record',
34
+ 'sfdx force:data:record:delete': 'sf data delete record',
35
+ 'sfdx force:data:bulk:upsert': 'sf data upsert bulk',
36
+ 'sfdx force:package:create': 'sf package create',
37
+ 'sfdx force:package:version:create': 'sf package version create',
38
+ 'sfdx force:user:create': 'sf org create user',
39
+ 'sfdx force:user:password:generate': 'sf org generate password',
40
+ };
41
+
42
+ // Patterns for dangerous operations
43
+ const DANGEROUS_PATTERNS = [
44
+ {
45
+ pattern: /sfdx force:org:delete|sf org delete/,
46
+ warning: 'This will permanently delete a Salesforce org. Ensure you have selected the correct org.',
47
+ },
48
+ {
49
+ pattern: /--target-org\s+\S*(prod|production|prd)\S*/i,
50
+ warning: 'This command targets a PRODUCTION org. Double-check before proceeding.',
51
+ },
52
+ {
53
+ pattern: /-u\s+\S*(prod|production|prd)\S*/i,
54
+ warning: 'This command targets what appears to be a PRODUCTION org alias.',
55
+ },
56
+ {
57
+ pattern: /sf project deploy start.*--target-org\s+\S*(prod|production|prd)\S*/i,
58
+ warning: 'Deploying to PRODUCTION. Ensure all tests pass and you have approval.',
59
+ },
60
+ {
61
+ pattern: /sfdx force:data:bulk:delete|sf data delete bulk/,
62
+ warning: 'Bulk delete operation detected. This will permanently delete records.',
63
+ },
64
+ {
65
+ pattern: /sf org reset|sfdx force:org:reset/,
66
+ warning: 'Org reset will remove all source tracking history.',
67
+ },
68
+ {
69
+ pattern: /--no-track-source/,
70
+ warning: 'Deploying without source tracking. Changes may not be reflected in local source.',
71
+ },
72
+ ];
73
+
74
+ function checkSfdxDeprecation(command) {
75
+ const warnings = [];
76
+ for (const [deprecated, replacement] of Object.entries(SFDX_MIGRATION_MAP)) {
77
+ if (command.includes(deprecated)) {
78
+ warnings.push({
79
+ type: 'deprecation',
80
+ message: `Deprecated command detected: \`${deprecated}\``,
81
+ suggestion: `Use the new SF CLI equivalent: \`${replacement}\``,
82
+ docs: 'https://developer.salesforce.com/tools/salesforcecli/migration',
83
+ });
84
+ }
85
+ }
86
+ return warnings;
87
+ }
88
+
89
+ function checkDangerousOps(command) {
90
+ const warnings = [];
91
+ for (const { pattern, warning } of DANGEROUS_PATTERNS) {
92
+ if (pattern.test(command)) {
93
+ warnings.push({
94
+ type: 'danger',
95
+ message: warning,
96
+ });
97
+ }
98
+ }
99
+ return warnings;
100
+ }
101
+
102
+ function processInput(input) {
103
+ // Only process Bash tool
104
+ if (input.tool_name !== 'Bash' && input.tool_name !== 'bash') {
105
+ return null;
106
+ }
107
+
108
+ const command = (input.tool_input && input.tool_input.command) || '';
109
+ if (!command) return null;
110
+
111
+ // Skip if command doesn't involve SF/SFDX
112
+ if (!command.includes('sfdx') && !command.includes('sf ') && !command.includes('sf\n')) {
113
+ return null;
114
+ }
115
+
116
+ const deprecationWarnings = checkSfdxDeprecation(command);
117
+ const dangerWarnings = checkDangerousOps(command);
118
+ const allWarnings = [...deprecationWarnings, ...dangerWarnings];
119
+
120
+ if (allWarnings.length === 0) return null;
121
+
122
+ return allWarnings;
123
+ }
124
+
125
+ // ── Main ─────────────────────────────────────────────────────────────────────
126
+
127
+ // Read JSON from stdin
128
+ let rawInput = '';
129
+
130
+ const rl = readline.createInterface({ input: process.stdin });
131
+ rl.on('line', line => { rawInput += line + '\n'; });
132
+
133
+ rl.on('close', () => {
134
+ let input = {};
135
+ try {
136
+ input = JSON.parse(rawInput.trim() || '{}');
137
+ } catch {
138
+ // Not valid JSON — exit gracefully
139
+ process.exit(0);
140
+ }
141
+
142
+ const warnings = processInput(input);
143
+
144
+ if (!warnings || warnings.length === 0) {
145
+ process.exit(0);
146
+ }
147
+
148
+ // Print warnings to stderr (visible in Claude Code output)
149
+ console.error('\n[SCC Hook] Salesforce CLI Warnings:');
150
+ for (const w of warnings) {
151
+ if (w.type === 'deprecation') {
152
+ console.error(` DEPRECATED : ${w.message}`);
153
+ console.error(` MIGRATE TO : ${w.suggestion}`);
154
+ if (w.docs) console.error(` DOCS : ${w.docs}`);
155
+ } else if (w.type === 'danger') {
156
+ console.error(` WARNING : ${w.message}`);
157
+ }
158
+ console.error();
159
+ }
160
+
161
+ // Exit 0 to allow the tool use to proceed (we're just warning, not blocking)
162
+ process.exit(0);
163
+ });
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Backward-compatible doc warning hook entrypoint.
4
+ * Kept for consumers that still reference pre-write-doc-warn.js directly.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ require('./doc-file-warning.js');
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Quality Gate Hook
4
+ *
5
+ * Runs lightweight quality checks after file edits.
6
+ * For Apex files: checks for common anti-patterns using shared apex-analysis module.
7
+ * For LWC files: checks for common issues.
8
+ * Falls back to no-op when file type is unrecognized.
9
+ *
10
+ * Uses apex-analysis.js for:
11
+ * - Comment/string stripping (eliminates false positives)
12
+ * - Loop depth tracking (activeLoopDepths stack + globalBraceDepth)
13
+ * - Test class detection
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+
21
+ const { preprocessApex, isTestClass, trackLoopDepth } = require('../lib/apex-analysis');
22
+
23
+ const MAX_STDIN = 1024 * 1024;
24
+
25
+ function log(msg) {
26
+ process.stderr.write(`${msg}\n`);
27
+ }
28
+
29
+ /**
30
+ * Check Apex file for common anti-patterns.
31
+ */
32
+ function checkApex(filePath, content) {
33
+ const issues = [];
34
+
35
+ // Skip test classes for security/quality checks (they don't run in production)
36
+ const testClass = isTestClass(content);
37
+
38
+ // Preprocess: strip comments and string literals to avoid false positives
39
+ const processed = preprocessApex(content);
40
+ const processedLines = processed.split('\n');
41
+ const depths = trackLoopDepth(processedLines);
42
+
43
+ // Check for SOQL/DML inside loops
44
+ for (let i = 0; i < processedLines.length; i++) {
45
+ const line = processedLines[i];
46
+ if (depths[i] > 0 && /\[\s*SELECT\s/i.test(line)) {
47
+ issues.push(`Line ${i + 1}: SOQL query inside loop — potential governor limit violation`);
48
+ }
49
+ if (depths[i] > 0) {
50
+ const dmlPattern = /\b(insert|update|delete|upsert)\s+/i;
51
+ const dbPattern = /Database\.(insert|update|delete|upsert)/i;
52
+ if (dmlPattern.test(line) || dbPattern.test(line)) {
53
+ issues.push(`Line ${i + 1}: DML operation inside loop — potential governor limit violation`);
54
+ }
55
+ }
56
+ }
57
+
58
+ // Check for hardcoded IDs (use raw content — IDs could be in strings)
59
+ if (/['"][a-zA-Z0-9]{15,18}['"]/.test(content) && /00[0-9a-zA-Z]{13,16}/.test(content)) {
60
+ issues.push('Hardcoded Salesforce record ID detected — use Custom Settings, Custom Metadata, or labels instead');
61
+ }
62
+
63
+ // Check for System.debug (configurable threshold)
64
+ const debugThreshold = parseInt(process.env.SCC_DEBUG_THRESHOLD, 10) || 5;
65
+ const debugCount = (processed.match(/System\.debug/g) || []).length;
66
+ if (debugCount > debugThreshold) {
67
+ issues.push(`${debugCount} System.debug statements found — consider removing before deployment`);
68
+ }
69
+
70
+ // --- Security checks (skip for test classes) ---
71
+ if (!testClass) {
72
+ // Sharing model detection
73
+ const beforeFirstBrace = content.split('{')[0] || '';
74
+ const hasSharing = /\b(with\s+sharing|without\s+sharing|inherited\s+sharing)\b/i.test(beforeFirstBrace);
75
+ const hasSoqlOrDml = /\[\s*SELECT\s/i.test(processed) ||
76
+ /\b(insert|update|delete|upsert)\s+/i.test(processed) ||
77
+ /Database\.(insert|update|delete|upsert|query)/i.test(processed);
78
+
79
+ if (!hasSharing && hasSoqlOrDml) {
80
+ issues.push('[HIGH] No sharing declaration (with sharing/without sharing/inherited sharing) on class that performs SOQL/DML');
81
+ }
82
+
83
+ // Privilege escalation: without sharing + @AuraEnabled/@RemoteAction
84
+ if (/\bwithout\s+sharing\b/i.test(beforeFirstBrace)) {
85
+ if (/@AuraEnabled/i.test(content) || /@RemoteAction/i.test(content)) {
86
+ issues.push('[HIGH] "without sharing" class exposes @AuraEnabled/@RemoteAction methods — potential privilege escalation');
87
+ }
88
+ }
89
+
90
+ // CRUD/FLS: SOQL without WITH USER_MODE or WITH SECURITY_ENFORCED
91
+ for (let i = 0; i < processedLines.length; i++) {
92
+ const line = processedLines[i];
93
+ if (/\[\s*SELECT\s/i.test(line) && !/WITH\s+(USER_MODE|SECURITY_ENFORCED)/i.test(line)) {
94
+ issues.push(`Line ${i + 1}: [LOW] SOQL query without WITH USER_MODE or WITH SECURITY_ENFORCED`);
95
+ break; // Only warn once to avoid noise
96
+ }
97
+ }
98
+
99
+ // DML without AccessLevel.USER_MODE
100
+ for (let i = 0; i < processedLines.length; i++) {
101
+ const line = processedLines[i];
102
+ if (/Database\.(insert|update|delete|upsert)\s*\(/i.test(line) &&
103
+ !/AccessLevel\.USER_MODE/i.test(line)) {
104
+ issues.push(`Line ${i + 1}: [LOW] Database DML without AccessLevel.USER_MODE`);
105
+ break; // Only warn once
106
+ }
107
+ }
108
+
109
+ // Dynamic SOQL warning
110
+ if (/Database\.(query|countQuery)\s*\(/i.test(processed)) {
111
+ issues.push('[LOW] Dynamic SOQL detected — ensure user-supplied values use String.escapeSingleQuotes()');
112
+ }
113
+ }
114
+
115
+ return issues;
116
+ }
117
+
118
+ /**
119
+ * Check LWC JavaScript for common issues.
120
+ */
121
+ function checkLwc(filePath, content) {
122
+ const issues = [];
123
+
124
+ // Check for console.log
125
+ const consoleCount = (content.match(/console\.(log|warn|error|info)/g) || []).length;
126
+ if (consoleCount > 0) {
127
+ issues.push(`${consoleCount} console statement(s) found — remove before deployment`);
128
+ }
129
+
130
+ // Check for imperative Apex calls without error handling
131
+ if (/import\s+\w+\s+from\s+['"]@salesforce\/apex\//.test(content)) {
132
+ if (!/\.catch\(|try\s*\{|catch\s*\(/.test(content)) {
133
+ issues.push('Imperative Apex call detected without error handling — add .catch() or try/catch');
134
+ }
135
+ }
136
+
137
+ return issues;
138
+ }
139
+
140
+ /**
141
+ * Run sf scanner on a single Apex file (standard+ profile).
142
+ * Returns array of issue strings. Graceful no-op if scanner not installed.
143
+ */
144
+ function runScannerOnFile(filePath) {
145
+ const profile = (process.env.SCC_HOOK_PROFILE || 'standard').toLowerCase();
146
+ if (profile === 'minimal') return [];
147
+
148
+ const { execSync } = require('child_process');
149
+ try {
150
+ execSync('sf scanner --version', { timeout: 5000, stdio: 'pipe' });
151
+ } catch {
152
+ return []; // Scanner not installed — skip gracefully
153
+ }
154
+
155
+ try {
156
+ const output = execSync(`sf scanner run --target "${filePath}" --format json --engine pmd`, {
157
+ timeout: 30000,
158
+ stdio: 'pipe',
159
+ encoding: 'utf8',
160
+ });
161
+ const violations = JSON.parse(output);
162
+ if (!Array.isArray(violations)) return [];
163
+ return violations
164
+ .filter(v => v.severity <= 2)
165
+ .slice(0, 5)
166
+ .map(v => {
167
+ const sev = v.severity === 1 ? 'CRITICAL' : 'HIGH';
168
+ return `[${sev}] PMD: ${v.message || v.ruleName} (line ${v.line})`;
169
+ });
170
+ } catch (err) {
171
+ if (err.stdout) {
172
+ try {
173
+ const violations = JSON.parse(err.stdout);
174
+ if (!Array.isArray(violations)) return [];
175
+ return violations
176
+ .filter(v => v.severity <= 2)
177
+ .slice(0, 5)
178
+ .map(v => {
179
+ const sev = v.severity === 1 ? 'CRITICAL' : 'HIGH';
180
+ return `[${sev}] PMD: ${v.message || v.ruleName} (line ${v.line})`;
181
+ });
182
+ } catch {
183
+ return [];
184
+ }
185
+ }
186
+ return [];
187
+ }
188
+ }
189
+
190
+ function maybeRunQualityGate(filePath) {
191
+ if (!filePath || !fs.existsSync(filePath)) return;
192
+
193
+ filePath = path.resolve(filePath);
194
+ const ext = path.extname(filePath).toLowerCase();
195
+
196
+ let content;
197
+ try {
198
+ content = fs.readFileSync(filePath, 'utf8');
199
+ } catch {
200
+ return;
201
+ }
202
+
203
+ let issues = [];
204
+
205
+ if (ext === '.cls' || ext === '.trigger') {
206
+ issues = checkApex(filePath, content);
207
+ // Run sf scanner for deeper PMD analysis (standard+ profile, graceful no-op)
208
+ const scannerIssues = runScannerOnFile(filePath);
209
+ issues = issues.concat(scannerIssues);
210
+ } else if (ext === '.js' && filePath.includes('/lwc/')) {
211
+ issues = checkLwc(filePath, content);
212
+ }
213
+
214
+ if (issues.length > 0) {
215
+ log(`\n[SCC QualityGate] ${path.basename(filePath)}:`);
216
+ for (const issue of issues) {
217
+ log(` - ${issue}`);
218
+ }
219
+ if (issues.some(i => i.includes('PMD:'))) {
220
+ log(' Install Salesforce Code Analyzer for enhanced checks: sf plugins install @salesforce/sfdx-scanner');
221
+ }
222
+ log('');
223
+ }
224
+ }
225
+
226
+ function run(rawInput) {
227
+ try {
228
+ const input = JSON.parse(rawInput);
229
+ const filePath = String(input.tool_input?.file_path || '');
230
+ maybeRunQualityGate(filePath);
231
+ } catch {
232
+ // Ignore parse errors
233
+ }
234
+ return rawInput;
235
+ }
236
+
237
+ if (require.main === module) {
238
+ let raw = '';
239
+ process.stdin.setEncoding('utf8');
240
+ process.stdin.on('data', chunk => {
241
+ if (raw.length < MAX_STDIN) {
242
+ raw += chunk.substring(0, MAX_STDIN - raw.length);
243
+ }
244
+ });
245
+ process.stdin.on('end', () => {
246
+ const result = run(raw);
247
+ process.stdout.write(result);
248
+ });
249
+ }
250
+
251
+ module.exports = { run };
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ HOOK_ID="${1:-}"
5
+ REL_SCRIPT_PATH="${2:-}"
6
+ PROFILES_CSV="${3:-standard,strict}"
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ PLUGIN_ROOT="${SCC_PLUGIN_ROOT:-$(cd "${SCRIPT_DIR}/../.." && pwd)}"
9
+
10
+ # Preserve stdin for passthrough or script execution
11
+ INPUT="$(cat)"
12
+
13
+ if [[ -z "$HOOK_ID" || -z "$REL_SCRIPT_PATH" ]]; then
14
+ printf '%s' "$INPUT"
15
+ exit 0
16
+ fi
17
+
18
+ # Ask Node helper if this hook is enabled
19
+ ENABLED="$(node "${PLUGIN_ROOT}/scripts/hooks/check-hook-enabled.js" "$HOOK_ID" "$PROFILES_CSV" 2>/dev/null || echo yes)"
20
+ if [[ "$ENABLED" != "yes" ]]; then
21
+ printf '%s' "$INPUT"
22
+ exit 0
23
+ fi
24
+
25
+ SCRIPT_PATH="${PLUGIN_ROOT}/${REL_SCRIPT_PATH}"
26
+ if [[ ! -f "$SCRIPT_PATH" ]]; then
27
+ echo "[Hook] Script not found for ${HOOK_ID}: ${SCRIPT_PATH}" >&2
28
+ printf '%s' "$INPUT"
29
+ exit 0
30
+ fi
31
+
32
+ printf '%s' "$INPUT" | "$SCRIPT_PATH"
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * run-with-flags.js — Profile-based hook gating for SCC.
6
+ *
7
+ * Reads stdin, checks SCC_HOOK_PROFILE and SCC_DISABLED_HOOKS before running
8
+ * a hook script. Passes stdin through to stdout when the hook is skipped.
9
+ *
10
+ * Usage: node run-with-flags.js <hook-name> <min-profile> <script-path>
11
+ * Profiles: minimal < standard < strict
12
+ *
13
+ * Hook execution:
14
+ * - If module exports run(input): calls run(stdin) directly (no child process)
15
+ * - Otherwise: spawns 'node <script-path>' with stdin piped (legacy hooks)
16
+ */
17
+
18
+ const fs = require('fs');
19
+ const { spawnSync } = require('child_process');
20
+
21
+ const PROFILE_LEVELS = { minimal: 1, standard: 2, strict: 3 };
22
+ const MAX_STDIN = 1024 * 1024;
23
+
24
+ function readStdinRaw() {
25
+ return new Promise(resolve => {
26
+ let raw = '';
27
+ process.stdin.setEncoding('utf8');
28
+ process.stdin.on('data', chunk => {
29
+ if (raw.length < MAX_STDIN) {
30
+ const remaining = MAX_STDIN - raw.length;
31
+ raw += chunk.substring(0, remaining);
32
+ }
33
+ });
34
+ process.stdin.on('end', () => resolve(raw));
35
+ process.stdin.on('error', () => resolve(raw));
36
+ });
37
+ }
38
+
39
+ async function main() {
40
+ const hookName = process.argv[2];
41
+ const minProfile = process.argv[3] || 'standard';
42
+ const scriptPath = process.argv[4];
43
+ const timeoutMs = parseInt(process.argv[5], 10) || 30000;
44
+
45
+ const raw = await readStdinRaw();
46
+
47
+ // Guard: missing args — pass through transparently
48
+ if (!hookName || !scriptPath) {
49
+ process.stdout.write(raw);
50
+ process.exit(0);
51
+ }
52
+
53
+ const currentProfile = process.env.SCC_HOOK_PROFILE || 'standard';
54
+ const disabledHooks = (process.env.SCC_DISABLED_HOOKS || '').split(',').map(h => h.trim().toLowerCase()).filter(Boolean);
55
+
56
+ // Skip if hook is explicitly disabled
57
+ if (disabledHooks.includes(hookName.toLowerCase())) {
58
+ process.stdout.write(raw);
59
+ process.exit(0);
60
+ }
61
+
62
+ // Skip if current profile is below minimum required level
63
+ const currentLevel = PROFILE_LEVELS[currentProfile] || 2;
64
+ const minLevel = PROFILE_LEVELS[minProfile] || 2;
65
+ if (currentLevel < minLevel) {
66
+ process.stdout.write(raw);
67
+ process.exit(0);
68
+ }
69
+
70
+ // Guard: script must exist and be a .js file
71
+ if (!scriptPath.endsWith('.js') || !fs.existsSync(scriptPath)) {
72
+ process.stderr.write(`[Hook] Script not found or invalid: ${scriptPath}\n`);
73
+ process.stdout.write(raw);
74
+ process.exit(0);
75
+ }
76
+
77
+ // Source pre-check: only require() hooks that export run().
78
+ // Prevents executing legacy hooks' module-scope side effects (stdin listeners,
79
+ // process.exit calls, main() invocations) when called via require().
80
+ const src = fs.readFileSync(scriptPath, 'utf8');
81
+ const hasRunExport = /\bmodule\.exports\b/.test(src) && /\brun\b/.test(src);
82
+
83
+ let hookModule;
84
+ if (hasRunExport) {
85
+ try {
86
+ hookModule = require(scriptPath);
87
+ } catch (requireErr) {
88
+ process.stderr.write(`[Hook] require() failed for ${hookName}: ${requireErr.message}\n`);
89
+ // Fall through to spawnSync
90
+ }
91
+ }
92
+
93
+ if (hookModule && typeof hookModule.run === 'function') {
94
+ try {
95
+ const output = hookModule.run(raw);
96
+ if (output !== null && output !== undefined) process.stdout.write(output);
97
+ } catch (runErr) {
98
+ process.stderr.write(`[Hook] run() error for ${hookName}: ${runErr.message}\n`);
99
+ process.stdout.write(raw);
100
+ }
101
+ process.exit(0);
102
+ }
103
+
104
+ // Legacy path: spawn a child Node process for hooks without run() export
105
+ const result = spawnSync('node', [scriptPath], {
106
+ input: raw,
107
+ encoding: 'utf8',
108
+ env: process.env,
109
+ cwd: process.cwd(),
110
+ timeout: timeoutMs
111
+ });
112
+
113
+ if (result.error) {
114
+ process.stderr.write(`[Hook] Spawn error for ${hookName}: ${result.error.message}\n`);
115
+ process.stdout.write(raw);
116
+ process.exit(0);
117
+ }
118
+
119
+ if (result.signal) {
120
+ process.stderr.write(`[Hook] ${hookName} killed by ${result.signal} (timeout: ${timeoutMs}ms)\n`);
121
+ process.stdout.write(raw);
122
+ process.exit(0);
123
+ }
124
+
125
+ if (result.stdout) process.stdout.write(result.stdout);
126
+ if (result.stderr) process.stderr.write(result.stderr);
127
+
128
+ const code = Number.isInteger(result.status) ? result.status : 0;
129
+ process.exit(code);
130
+ }
131
+
132
+ main().catch(err => {
133
+ process.stderr.write(`[Hook] run-with-flags error: ${err.message}\n`);
134
+ process.exit(0);
135
+ });
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Session end marker hook - outputs stdin to stdout unchanged.
6
+ * Exports run() for in-process execution (avoids spawnSync issues on Windows).
7
+ */
8
+
9
+ function run(rawInput) {
10
+ return rawInput || '';
11
+ }
12
+
13
+ // Legacy CLI execution (when run directly)
14
+ if (require.main === module) {
15
+ const MAX_STDIN = 1024 * 1024;
16
+ let raw = '';
17
+ process.stdin.setEncoding('utf8');
18
+ process.stdin.on('data', chunk => {
19
+ if (raw.length < MAX_STDIN) {
20
+ const remaining = MAX_STDIN - raw.length;
21
+ raw += chunk.substring(0, remaining);
22
+ }
23
+ });
24
+ process.stdin.on('end', () => {
25
+ process.stdout.write(raw);
26
+ });
27
+ }
28
+
29
+ module.exports = { run };