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,119 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SFDX Validate Hook
4
+ *
5
+ * PreToolUse hook that validates Salesforce CLI commands before execution.
6
+ * Checks for:
7
+ * - Commands that should use --dry-run first
8
+ * - Destructive operations that need confirmation
9
+ * - Common parameter mistakes
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const readline = require('readline');
15
+
16
+ const MAX_STDIN = 1024 * 1024;
17
+
18
+ /**
19
+ * Validation rules for SF CLI commands.
20
+ */
21
+ const VALIDATION_RULES = [
22
+ {
23
+ pattern: /sf project deploy start(?!.*--dry-run)(?!.*--validate)/,
24
+ check: (cmd) => !cmd.includes('--test-level') && !cmd.includes('--dry-run'),
25
+ message: 'Deployment without --test-level specified. Consider adding --test-level RunLocalTests',
26
+ severity: 'warning',
27
+ },
28
+ {
29
+ pattern: /sf data delete bulk/,
30
+ message: 'Bulk delete operation — this permanently removes records. Use --dry-run first.',
31
+ severity: 'warning',
32
+ },
33
+ {
34
+ pattern: /sf org delete scratch/,
35
+ check: (cmd) => !cmd.includes('--no-prompt'),
36
+ message: 'Scratch org deletion — ensure you have pushed all source changes before deleting.',
37
+ severity: 'info',
38
+ },
39
+ {
40
+ pattern: /sf project deploy start.*--ignore-conflicts/,
41
+ message: 'Deploying with --ignore-conflicts may overwrite changes made directly in the org.',
42
+ severity: 'warning',
43
+ },
44
+ {
45
+ pattern: /sf data import tree/,
46
+ check: (cmd) => !cmd.includes('--plan'),
47
+ message: 'Data import without --plan. Consider using a plan file for repeatable imports.',
48
+ severity: 'info',
49
+ },
50
+ {
51
+ pattern: /sf package version create(?!.*--skip-validation)/,
52
+ check: (cmd) => !cmd.includes('--code-coverage'),
53
+ message: 'Package version creation — remember that managed packages require 75% code coverage.',
54
+ severity: 'info',
55
+ },
56
+ {
57
+ pattern: /sf project deploy start.*--source-dir.*destructiveChanges/i,
58
+ message: 'Destructive changes deployment detected. Ensure you have verified the manifest.',
59
+ severity: 'warning',
60
+ },
61
+ ];
62
+
63
+ function validateCommand(command) {
64
+ if (!command) return [];
65
+
66
+ // Only check SF CLI commands
67
+ if (!command.includes('sf ') && !command.includes('sfdx ')) return [];
68
+
69
+ const warnings = [];
70
+ for (const rule of VALIDATION_RULES) {
71
+ if (rule.pattern.test(command)) {
72
+ if (rule.check && !rule.check(command)) continue;
73
+ warnings.push({
74
+ severity: rule.severity,
75
+ message: rule.message,
76
+ });
77
+ }
78
+ }
79
+ return warnings;
80
+ }
81
+
82
+ // Read JSON from stdin
83
+ let rawInput = '';
84
+ const rl = readline.createInterface({ input: process.stdin });
85
+ rl.on('line', line => {
86
+ if (rawInput.length < MAX_STDIN) {
87
+ rawInput += line + '\n';
88
+ }
89
+ });
90
+
91
+ rl.on('close', () => {
92
+ let input = {};
93
+ try {
94
+ input = JSON.parse(rawInput.trim() || '{}');
95
+ } catch {
96
+ process.exit(0);
97
+ }
98
+
99
+ if (input.tool_name !== 'Bash' && input.tool_name !== 'bash') {
100
+ process.exit(0);
101
+ }
102
+
103
+ const command = (input.tool_input && input.tool_input.command) || '';
104
+ const warnings = validateCommand(command);
105
+
106
+ if (warnings.length === 0) {
107
+ process.exit(0);
108
+ }
109
+
110
+ console.error('\n[SCC Validate] SF CLI Command Check:');
111
+ for (const w of warnings) {
112
+ const prefix = w.severity === 'warning' ? 'WARNING' : 'INFO';
113
+ console.error(` ${prefix}: ${w.message}`);
114
+ }
115
+ console.error();
116
+
117
+ // Exit 0 — we warn but don't block
118
+ process.exit(0);
119
+ });
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * stop-hook.js — Stop hook for SCC.
6
+ *
7
+ * Runs when Claude Code is about to stop/complete a session.
8
+ * Checks for uncommitted Apex/LWC changes and reminds about:
9
+ * - Running tests before committing
10
+ * - Deploying to a scratch org to verify
11
+ * - Checking test coverage
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const { spawnSync } = require('child_process');
17
+
18
+ const CWD = process.cwd();
19
+
20
+ /**
21
+ * Run a command and return { status, stdout, stderr }.
22
+ */
23
+ function run(cmd, args, cwd) {
24
+ const result = spawnSync(cmd, args, {
25
+ encoding: 'utf8',
26
+ timeout: 10000,
27
+ cwd: cwd || CWD,
28
+ env: process.env,
29
+ });
30
+ return {
31
+ status: result.status,
32
+ stdout: result.stdout || '',
33
+ stderr: result.stderr || '',
34
+ error: result.error,
35
+ };
36
+ }
37
+
38
+ /**
39
+ * Get uncommitted files via git status --porcelain.
40
+ */
41
+ function getUncommittedFiles() {
42
+ const result = run('git', ['status', '--porcelain']);
43
+ if (result.status !== 0 || result.error) return null;
44
+
45
+ return result.stdout
46
+ .split('\n')
47
+ .filter(line => line.trim().length > 0)
48
+ .map(line => {
49
+ const statusCode = line.slice(0, 2).trim();
50
+ const filePath = line.slice(3).trim();
51
+ return { statusCode, filePath };
52
+ });
53
+ }
54
+
55
+ /**
56
+ * Check if this is a Salesforce project.
57
+ */
58
+ function isSalesforceProject(dir) {
59
+ let d = dir;
60
+ for (let i = 0; i < 5; i++) {
61
+ if (fs.existsSync(path.join(d, 'sfdx-project.json'))) return true;
62
+ const parent = path.dirname(d);
63
+ if (parent === d) break;
64
+ d = parent;
65
+ }
66
+ return false;
67
+ }
68
+
69
+ /**
70
+ * Classify Salesforce file types.
71
+ */
72
+ function classifyFiles(files) {
73
+ const classified = { apex: [], lwc: [], aura: [], other: [] };
74
+ for (const f of files) {
75
+ const fp = f.filePath;
76
+ const ext = path.extname(fp).toLowerCase();
77
+ const parts = fp.split('/');
78
+
79
+ if (ext === '.cls' || ext === '.trigger') {
80
+ classified.apex.push(f);
81
+ } else if (parts.includes('lwc')) {
82
+ classified.lwc.push(f);
83
+ } else if (parts.includes('aura')) {
84
+ classified.aura.push(f);
85
+ } else {
86
+ classified.other.push(f);
87
+ }
88
+ }
89
+ return classified;
90
+ }
91
+
92
+ // ── Main ─────────────────────────────────────────────────────────────────────
93
+
94
+ // Only run in Salesforce projects
95
+ if (!isSalesforceProject(CWD)) {
96
+ process.exit(0);
97
+ }
98
+
99
+ const uncommittedFiles = getUncommittedFiles();
100
+
101
+ // Can't check git or no changes
102
+ if (uncommittedFiles === null || uncommittedFiles.length === 0) {
103
+ process.exit(0);
104
+ }
105
+
106
+ const classified = classifyFiles(uncommittedFiles);
107
+ const hasSfChanges = classified.apex.length > 0 || classified.lwc.length > 0 || classified.aura.length > 0;
108
+
109
+ if (!hasSfChanges) {
110
+ process.exit(0);
111
+ }
112
+
113
+ // Print a summary reminder
114
+ console.log('\n── SCC: Uncommitted Salesforce Changes ────────────────');
115
+
116
+ if (classified.apex.length > 0) {
117
+ console.log(`\nApex changes (${classified.apex.length} file(s)):`);
118
+ for (const f of classified.apex.slice(0, 5)) {
119
+ console.log(` ${f.statusCode} ${f.filePath}`);
120
+ }
121
+ if (classified.apex.length > 5) console.log(` ... and ${classified.apex.length - 5} more`);
122
+
123
+ console.log('\nApex reminders:');
124
+ console.log(' • Run Apex tests before committing:');
125
+ console.log(' sf apex run test --result-format human --code-coverage');
126
+ console.log(' • Ensure 75% code coverage across your org');
127
+ console.log(' • Deploy to scratch org to verify: sf project deploy start');
128
+ }
129
+
130
+ if (classified.lwc.length > 0) {
131
+ console.log(`\nLWC changes (${classified.lwc.length} file(s)):`);
132
+ for (const f of classified.lwc.slice(0, 5)) {
133
+ console.log(` ${f.statusCode} ${f.filePath}`);
134
+ }
135
+ if (classified.lwc.length > 5) console.log(` ... and ${classified.lwc.length - 5} more`);
136
+
137
+ console.log('\nLWC reminders:');
138
+ console.log(' • Run Jest unit tests: npm run test:unit');
139
+ console.log(' • Validate component: sf project deploy start --dry-run');
140
+ console.log(' • Test in browser: sf org open');
141
+ }
142
+
143
+ if (classified.aura.length > 0) {
144
+ console.log(`\nAura changes (${classified.aura.length} file(s)):`);
145
+ for (const f of classified.aura.slice(0, 3)) {
146
+ console.log(` ${f.statusCode} ${f.filePath}`);
147
+ }
148
+ }
149
+
150
+ // Check if there's a package.json with test scripts (for LWC Jest)
151
+ const packageJsonPath = path.join(CWD, 'package.json');
152
+ let hasJestScript = false;
153
+ if (fs.existsSync(packageJsonPath)) {
154
+ try {
155
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
156
+ hasJestScript = !!(pkg.scripts && (pkg.scripts['test:unit'] || pkg.scripts['jest'] || pkg.scripts['test']));
157
+ } catch { /* ignore */ }
158
+ }
159
+
160
+ console.log('\nNext steps:');
161
+ if (classified.apex.length > 0) {
162
+ console.log(' 1. sf apex run test --result-format human --code-coverage');
163
+ }
164
+ if (classified.lwc.length > 0 && hasJestScript) {
165
+ console.log(' 2. npm run test:unit');
166
+ }
167
+ console.log(` ${classified.apex.length > 0 ? '3' : '1'}. git add . && git commit -m "feat: <your message>"`);
168
+ console.log('────────────────────────────────────────────────────────\n');
169
+
170
+ process.exit(0);
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Strategic Compact Suggester
4
+ *
5
+ * Tracks tool call count and suggests manual /compact at logical intervals.
6
+ * Runs on PreToolUse to count invocations.
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const os = require('os');
14
+
15
+ function getTempDir() {
16
+ return os.tmpdir();
17
+ }
18
+
19
+ function log(msg) {
20
+ process.stderr.write(`${msg}\n`);
21
+ }
22
+
23
+ async function main() {
24
+ const sessionId = (process.env.CLAUDE_SESSION_ID || 'default').replace(/[^a-zA-Z0-9_-]/g, '') || 'default';
25
+ const counterFile = path.join(getTempDir(), `scc-tool-count-${sessionId}`);
26
+ const rawThreshold = parseInt(process.env.COMPACT_THRESHOLD || '50', 10);
27
+ const threshold = Number.isFinite(rawThreshold) && rawThreshold > 0 && rawThreshold <= 10000
28
+ ? rawThreshold
29
+ : 50;
30
+
31
+ let count = 1;
32
+
33
+ try {
34
+ const fd = fs.openSync(counterFile, 'a+');
35
+ try {
36
+ const buf = Buffer.alloc(64);
37
+ const bytesRead = fs.readSync(fd, buf, 0, 64, 0);
38
+ if (bytesRead > 0) {
39
+ const parsed = parseInt(buf.toString('utf8', 0, bytesRead).trim(), 10);
40
+ count = (Number.isFinite(parsed) && parsed > 0 && parsed <= 1000000)
41
+ ? parsed + 1
42
+ : 1;
43
+ }
44
+ fs.ftruncateSync(fd, 0);
45
+ fs.writeSync(fd, String(count), 0);
46
+ } finally {
47
+ fs.closeSync(fd);
48
+ }
49
+ } catch {
50
+ try { fs.writeFileSync(counterFile, String(count)); } catch { /* ignore */ }
51
+ }
52
+
53
+ if (count === threshold) {
54
+ log(`[SCC Compact] ${threshold} tool calls reached — consider /compact if transitioning phases`);
55
+ }
56
+
57
+ if (count > threshold && (count - threshold) % 25 === 0) {
58
+ log(`[SCC Compact] ${count} tool calls — good checkpoint for /compact if context is stale`);
59
+ }
60
+
61
+ process.exit(0);
62
+ }
63
+
64
+ main().catch(err => {
65
+ console.error('[SCC Compact] Error:', err.message);
66
+ process.exit(0);
67
+ });
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * agent-adapter.js — Transforms SCC agents from Claude Code format to Cursor format.
5
+ *
6
+ * Claude Code agents use: name, description, tools, model (sonnet/opus/haiku), origin,
7
+ * disallowedTools, permissionMode, maxTurns, skills, mcpServers, hooks, memory, etc.
8
+ * Cursor agents use: name, description, model (fast/inherit/specific ID), readonly, is_background.
9
+ *
10
+ * This adapter strips Claude-only fields, maps model aliases, and outputs
11
+ * clean Cursor-compatible agent .md files.
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const { parseFrontmatter, serializeFrontmatter, ensureDir } = require('./utils');
17
+
18
+ // Fields that Cursor recognizes in agent frontmatter
19
+ const CURSOR_ALLOWED_FIELDS = new Set([
20
+ 'name',
21
+ 'description',
22
+ 'model',
23
+ 'readonly',
24
+ 'is_background',
25
+ ]);
26
+
27
+ // Map Claude Code model aliases to Cursor equivalents
28
+ const MODEL_MAP = {
29
+ sonnet: 'inherit',
30
+ opus: 'inherit',
31
+ haiku: 'fast',
32
+ 'claude-sonnet': 'inherit',
33
+ 'claude-opus': 'inherit',
34
+ 'claude-haiku': 'fast',
35
+ };
36
+
37
+ /**
38
+ * Transform a single agent .md content string from Claude Code to Cursor format.
39
+ * @param {string} content - raw agent .md content
40
+ * @returns {string} - transformed agent .md content for Cursor
41
+ */
42
+ function transformAgent(content) {
43
+ const { frontmatter, body } = parseFrontmatter(content);
44
+
45
+ const cursorFm = {};
46
+
47
+ // Copy allowed fields
48
+ for (const key of Object.keys(frontmatter)) {
49
+ if (CURSOR_ALLOWED_FIELDS.has(key)) {
50
+ cursorFm[key] = frontmatter[key];
51
+ }
52
+ }
53
+
54
+ // Map model aliases to Cursor equivalents
55
+ if (cursorFm.model) {
56
+ const mapped = MODEL_MAP[String(cursorFm.model).toLowerCase()];
57
+ if (mapped) {
58
+ cursorFm.model = mapped;
59
+ }
60
+ // Full model IDs (e.g. claude-sonnet-4-6) pass through unchanged
61
+ }
62
+
63
+ return serializeFrontmatter(cursorFm, body);
64
+ }
65
+
66
+ /**
67
+ * Transform and write a single agent file.
68
+ * @param {string} srcPath - source agent .md file
69
+ * @param {string} destPath - destination file path
70
+ */
71
+ function transformAgentFile(srcPath, destPath) {
72
+ if (!fs.existsSync(srcPath)) {
73
+ throw new Error(`Source agent file not found: ${srcPath}`);
74
+ }
75
+
76
+ const content = fs.readFileSync(srcPath, 'utf8');
77
+ const transformed = transformAgent(content);
78
+ ensureDir(path.dirname(destPath));
79
+ fs.writeFileSync(destPath, transformed, 'utf8');
80
+ }
81
+
82
+ module.exports = { transformAgent, transformAgentFile, CURSOR_ALLOWED_FIELDS, MODEL_MAP };
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * apex-analysis.js — Shared Apex static analysis utilities
6
+ *
7
+ * Provides preprocessing (comment/string stripping), loop depth tracking,
8
+ * and test class detection for use by governor-check.js and quality-gate.js.
9
+ *
10
+ * Architecture:
11
+ * - trackLoopDepth uses an activeLoopDepths stack + globalBraceDepth counter
12
+ * to correctly handle if/try/catch blocks inside loops (their closing braces
13
+ * don't prematurely end the enclosing loop).
14
+ * - Linear 6-step pipeline per line — no continue statements that could skip
15
+ * brace tracking and desync globalBraceDepth.
16
+ *
17
+ * Known limitations:
18
+ * - do { } while(cond) — the 'do' keyword isn't followed by '(', so the loop
19
+ * regex doesn't match it. Brace tracking still manages depth correctly, but
20
+ * 'do' itself isn't recognized as a loop start.
21
+ * - Class-level static initializer blocks may affect globalBraceDepth.
22
+ * - Anonymous inner classes with { } may affect brace counting.
23
+ */
24
+
25
+ /**
26
+ * Strip comments and string literals from Apex code.
27
+ * Replaces removed content with whitespace to preserve line alignment.
28
+ *
29
+ * @param {string} code - Raw Apex source code
30
+ * @returns {string} Code with comments and strings replaced by spaces
31
+ */
32
+ function preprocessApex(code) {
33
+ let result = code;
34
+ // Remove block comments (preserve newlines for line alignment)
35
+ result = result.replace(/\/\*[\s\S]*?\*\//g, m => m.replace(/[^\n]/g, ' '));
36
+ // Remove single-line comments
37
+ result = result.replace(/\/\/.*$/gm, m => ' '.repeat(m.length));
38
+ // Remove string literals (handle escaped quotes)
39
+ result = result.replace(/'(?:[^'\\]|\\.)*'/g, m => ' '.repeat(m.length));
40
+ result = result.replace(/"(?:[^"\\]|\\.)*"/g, m => ' '.repeat(m.length));
41
+ return result;
42
+ }
43
+
44
+ /**
45
+ * Detect whether an Apex source file is a test class.
46
+ * Test classes don't run in production, so governor limit scanning
47
+ * on them creates noise.
48
+ *
49
+ * @param {string} code - Raw or preprocessed Apex source code
50
+ * @returns {boolean} True if the class has @IsTest annotation at class level
51
+ */
52
+ function isTestClass(code) {
53
+ // Match @IsTest before the class keyword (class-level annotation)
54
+ const beforeFirstBrace = code.split('{')[0] || '';
55
+ return /@[Ii]s[Tt]est\b/.test(beforeFirstBrace);
56
+ }
57
+
58
+ /**
59
+ * Track loop depth for each line of preprocessed Apex code.
60
+ *
61
+ * Returns an array of integers where depths[i] is the loop nesting depth
62
+ * at line i. depths[i] > 0 means line i is inside a loop.
63
+ *
64
+ * Uses an activeLoopDepths stack bound to globalBraceDepth to correctly
65
+ * handle if/try/catch blocks inside loops. Uses unbracedStack counter
66
+ * for nested unbraced loops. Uses waitingForBody state for Allman brace style.
67
+ *
68
+ * CRITICAL: Linear 6-step pipeline — every non-empty line flows through all
69
+ * steps. No continue statements that could skip brace tracking.
70
+ *
71
+ * @param {string[]} processedLines - Lines from preprocessApex(code).split('\n')
72
+ * @returns {number[]} Array of loop depth per line
73
+ */
74
+ function trackLoopDepth(processedLines) {
75
+ const depths = [];
76
+ let globalBraceDepth = 0; // total brace depth in file
77
+ const activeLoopDepths = []; // stack: globalBraceDepth where each braced loop started
78
+ let unbracedStack = 0; // counter for nested unbraced loops
79
+ let parenDepth = 0; // for multi-line for/while declarations
80
+ let pendingLoop = false; // SCANNING_DECLARATION state
81
+ let waitingForBody = false; // WAITING_FOR_BODY state
82
+
83
+ for (let i = 0; i < processedLines.length; i++) {
84
+ const line = processedLines[i];
85
+ const trimmed = line.trim();
86
+ let singleLineLoopBonus = 0;
87
+ let skipLoopDetection = false;
88
+
89
+ // Empty lines: no braces, safe to skip
90
+ if (!trimmed) {
91
+ depths.push(activeLoopDepths.length + unbracedStack);
92
+ continue;
93
+ }
94
+
95
+ // ═══════════ STEP 1: Resolve pending multi-line declarations ═══════════
96
+ if (pendingLoop) {
97
+ let closingIdx = -1;
98
+ for (let j = 0; j < trimmed.length; j++) {
99
+ if (trimmed[j] === '(') parenDepth++;
100
+ if (trimmed[j] === ')') {
101
+ parenDepth--;
102
+ if (parenDepth === 0) { closingIdx = j; break; }
103
+ }
104
+ }
105
+ if (closingIdx === -1) {
106
+ // Still reading declaration — inside parens, no block braces matter
107
+ depths.push(activeLoopDepths.length + unbracedStack);
108
+ continue; // safe: inside parens
109
+ }
110
+ pendingLoop = false;
111
+ const afterClose = trimmed.substring(closingIdx + 1).trim();
112
+ if (afterClose.startsWith('{') || afterClose.includes('{')) {
113
+ activeLoopDepths.push(globalBraceDepth);
114
+ } else if (/\S/.test(afterClose) && /;\s*$/.test(afterClose)) {
115
+ singleLineLoopBonus = 1;
116
+ } else if (/\S/.test(afterClose)) {
117
+ unbracedStack++;
118
+ } else {
119
+ waitingForBody = true;
120
+ }
121
+ skipLoopDetection = true;
122
+ }
123
+
124
+ // ═══════════ STEP 2: Resolve WAITING_FOR_BODY ═══════════
125
+ if (waitingForBody) {
126
+ waitingForBody = false;
127
+ if (trimmed.startsWith('{')) {
128
+ // Allman braced loop
129
+ activeLoopDepths.push(globalBraceDepth);
130
+ } else {
131
+ // Unbraced loop — this line IS the body (or start of it)
132
+ unbracedStack++;
133
+ }
134
+ }
135
+
136
+ // ═══════════ STEP 3: Detect new loop starts ═══════════
137
+ const loopMatch = !skipLoopDetection && trimmed.match(/\b(for|while|do)\s*\(/);
138
+ if (loopMatch) {
139
+ // Token-aware paren scan from match point (NOT lastIndexOf)
140
+ let localParenDepth = 0;
141
+ let closingIdx = -1;
142
+ for (let j = loopMatch.index; j < trimmed.length; j++) {
143
+ if (trimmed[j] === '(') localParenDepth++;
144
+ if (trimmed[j] === ')') {
145
+ localParenDepth--;
146
+ if (localParenDepth === 0) { closingIdx = j; break; }
147
+ }
148
+ }
149
+
150
+ if (closingIdx === -1) {
151
+ // Multi-line declaration — parens still open
152
+ parenDepth = localParenDepth;
153
+ pendingLoop = true;
154
+ } else {
155
+ // CRITICAL: use closingIdx from paren scan, NOT lastIndexOf(')')
156
+ const afterClose = trimmed.substring(closingIdx + 1).trim();
157
+ if (afterClose.startsWith('{') || afterClose.includes('{')) {
158
+ activeLoopDepths.push(globalBraceDepth);
159
+ } else if (/\S/.test(afterClose) && /;\s*$/.test(afterClose)) {
160
+ singleLineLoopBonus = 1;
161
+ } else if (/\S/.test(afterClose)) {
162
+ unbracedStack++;
163
+ } else {
164
+ waitingForBody = true;
165
+ }
166
+ }
167
+ }
168
+
169
+ // ═══════════ STEP 4: Record current line depth ═══════════
170
+ // Single-line loop bonus adds +1 for the current line only
171
+ depths.push(activeLoopDepths.length + unbracedStack + singleLineLoopBonus);
172
+
173
+ // ═══════════ STEP 5: Update globalBraceDepth + reconcile ═══════════
174
+ const opens = (trimmed.match(/\{/g) || []).length;
175
+ const closes = (trimmed.match(/\}/g) || []).length;
176
+ globalBraceDepth += (opens - closes);
177
+
178
+ // If brace depth dropped to/below where a loop started, that loop closed
179
+ while (
180
+ activeLoopDepths.length > 0 &&
181
+ globalBraceDepth <= activeLoopDepths[activeLoopDepths.length - 1]
182
+ ) {
183
+ activeLoopDepths.pop();
184
+ }
185
+
186
+ // ═══════════ STEP 6: Drain unbraced stack (LAST — after braces counted) ═══════════
187
+ if (unbracedStack > 0 && /;\s*$/.test(trimmed) && singleLineLoopBonus === 0) {
188
+ unbracedStack = 0;
189
+ }
190
+ }
191
+ return depths;
192
+ }
193
+
194
+ module.exports = { preprocessApex, isTestClass, trackLoopDepth };