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.
- package/.claude-plugin/plugin.json +44 -0
- package/.cursor/agents/deep-researcher.md +142 -0
- package/.cursor/agents/doc-updater.md +219 -0
- package/.cursor/agents/eval-runner.md +335 -0
- package/.cursor/agents/learning-engine.md +210 -0
- package/.cursor/agents/loop-operator.md +245 -0
- package/.cursor/agents/refactor-cleaner.md +119 -0
- package/.cursor/agents/sf-admin-agent.md +127 -0
- package/.cursor/agents/sf-agentforce-agent.md +126 -0
- package/.cursor/agents/sf-apex-agent.md +117 -0
- package/.cursor/agents/sf-architect.md +426 -0
- package/.cursor/agents/sf-aura-reviewer.md +369 -0
- package/.cursor/agents/sf-bugfix-agent.md +101 -0
- package/.cursor/agents/sf-flow-agent.md +155 -0
- package/.cursor/agents/sf-integration-agent.md +141 -0
- package/.cursor/agents/sf-lwc-agent.md +123 -0
- package/.cursor/agents/sf-review-agent.md +357 -0
- package/.cursor/agents/sf-visualforce-reviewer.md +465 -0
- package/.cursor/hooks/adapter.js +81 -0
- package/.cursor/hooks/after-file-edit.js +26 -0
- package/.cursor/hooks/after-mcp-execution.js +12 -0
- package/.cursor/hooks/after-shell-execution.js +30 -0
- package/.cursor/hooks/after-tab-file-edit.js +12 -0
- package/.cursor/hooks/before-mcp-execution.js +11 -0
- package/.cursor/hooks/before-read-file.js +13 -0
- package/.cursor/hooks/before-shell-execution.js +29 -0
- package/.cursor/hooks/before-submit-prompt.js +23 -0
- package/.cursor/hooks/pre-compact.js +7 -0
- package/.cursor/hooks/session-end.js +10 -0
- package/.cursor/hooks/session-start.js +10 -0
- package/.cursor/hooks/stop.js +18 -0
- package/.cursor/hooks/subagent-start.js +10 -0
- package/.cursor/hooks/subagent-stop.js +10 -0
- package/.cursor/hooks.json +107 -0
- package/.cursor/skills/aside/SKILL.md +115 -0
- package/.cursor/skills/checkpoint/SKILL.md +50 -0
- package/.cursor/skills/configure-scc/SKILL.md +160 -0
- package/.cursor/skills/continuous-agent-loop/SKILL.md +260 -0
- package/.cursor/skills/mcp-server-patterns/SKILL.md +142 -0
- package/.cursor/skills/model-route/SKILL.md +81 -0
- package/.cursor/skills/prompt-optimizer/SKILL.md +366 -0
- package/.cursor/skills/refactor-clean/SKILL.md +133 -0
- package/.cursor/skills/resume-session/SKILL.md +111 -0
- package/.cursor/skills/save-session/SKILL.md +183 -0
- package/.cursor/skills/search-first/SKILL.md +140 -0
- package/.cursor/skills/security-scan/SKILL.md +142 -0
- package/.cursor/skills/sessions/SKILL.md +124 -0
- package/.cursor/skills/sf-agentforce-development/SKILL.md +449 -0
- package/.cursor/skills/sf-apex-async-patterns/SKILL.md +324 -0
- package/.cursor/skills/sf-apex-best-practices/SKILL.md +421 -0
- package/.cursor/skills/sf-apex-constraints/SKILL.md +79 -0
- package/.cursor/skills/sf-apex-cursor/SKILL.md +336 -0
- package/.cursor/skills/sf-apex-enterprise-patterns/SKILL.md +344 -0
- package/.cursor/skills/sf-apex-testing/SKILL.md +407 -0
- package/.cursor/skills/sf-api-design/SKILL.md +237 -0
- package/.cursor/skills/sf-approval-processes/SKILL.md +312 -0
- package/.cursor/skills/sf-aura-development/SKILL.md +260 -0
- package/.cursor/skills/sf-build-fix/SKILL.md +120 -0
- package/.cursor/skills/sf-data-modeling/SKILL.md +274 -0
- package/.cursor/skills/sf-debugging/SKILL.md +362 -0
- package/.cursor/skills/sf-deployment/SKILL.md +291 -0
- package/.cursor/skills/sf-deployment-constraints/SKILL.md +153 -0
- package/.cursor/skills/sf-devops-ci-cd/SKILL.md +322 -0
- package/.cursor/skills/sf-docs-lookup/SKILL.md +100 -0
- package/.cursor/skills/sf-e2e-testing/SKILL.md +321 -0
- package/.cursor/skills/sf-experience-cloud/SKILL.md +248 -0
- package/.cursor/skills/sf-flow-development/SKILL.md +376 -0
- package/.cursor/skills/sf-governor-limits/SKILL.md +319 -0
- package/.cursor/skills/sf-harness-audit/SKILL.md +139 -0
- package/.cursor/skills/sf-help/SKILL.md +156 -0
- package/.cursor/skills/sf-integration/SKILL.md +479 -0
- package/.cursor/skills/sf-lwc-constraints/SKILL.md +128 -0
- package/.cursor/skills/sf-lwc-development/SKILL.md +302 -0
- package/.cursor/skills/sf-lwc-testing/SKILL.md +387 -0
- package/.cursor/skills/sf-metadata-management/SKILL.md +285 -0
- package/.cursor/skills/sf-platform-events-cdc/SKILL.md +372 -0
- package/.cursor/skills/sf-quickstart/SKILL.md +170 -0
- package/.cursor/skills/sf-security/SKILL.md +330 -0
- package/.cursor/skills/sf-security-constraints/SKILL.md +125 -0
- package/.cursor/skills/sf-soql-constraints/SKILL.md +129 -0
- package/.cursor/skills/sf-soql-optimization/SKILL.md +353 -0
- package/.cursor/skills/sf-tdd-workflow/SKILL.md +332 -0
- package/.cursor/skills/sf-testing-constraints/SKILL.md +198 -0
- package/.cursor/skills/sf-trigger-constraints/SKILL.md +88 -0
- package/.cursor/skills/sf-trigger-frameworks/SKILL.md +343 -0
- package/.cursor/skills/sf-visualforce-development/SKILL.md +259 -0
- package/.cursor/skills/strategic-compact/SKILL.md +205 -0
- package/.cursor/skills/update-docs/SKILL.md +162 -0
- package/.cursor/skills/update-platform-docs/SKILL.md +86 -0
- package/.cursor-plugin/plugin.json +26 -0
- package/LICENSE +21 -0
- package/README.md +522 -0
- package/agents/deep-researcher.md +145 -0
- package/agents/doc-updater.md +222 -0
- package/agents/eval-runner.md +340 -0
- package/agents/learning-engine.md +211 -0
- package/agents/loop-operator.md +247 -0
- package/agents/refactor-cleaner.md +122 -0
- package/agents/sf-admin-agent.md +131 -0
- package/agents/sf-agentforce-agent.md +132 -0
- package/agents/sf-apex-agent.md +124 -0
- package/agents/sf-architect.md +435 -0
- package/agents/sf-aura-reviewer.md +372 -0
- package/agents/sf-bugfix-agent.md +105 -0
- package/agents/sf-flow-agent.md +159 -0
- package/agents/sf-integration-agent.md +146 -0
- package/agents/sf-lwc-agent.md +127 -0
- package/agents/sf-review-agent.md +366 -0
- package/agents/sf-visualforce-reviewer.md +468 -0
- package/assets/logo.svg +18 -0
- package/docs/ARCHITECTURE.md +133 -0
- package/docs/authoring-guide.md +373 -0
- package/docs/hook-development.md +578 -0
- package/docs/token-optimization.md +139 -0
- package/docs/workflow-examples.md +645 -0
- package/examples/agentforce-action/README.md +227 -0
- package/examples/apex-trigger-handler/README.md +114 -0
- package/examples/devops-pipeline/README.md +325 -0
- package/examples/flow-automation/README.md +188 -0
- package/examples/integration-pattern/README.md +416 -0
- package/examples/lwc-component/README.md +180 -0
- package/examples/platform-events/README.md +492 -0
- package/examples/scratch-org-setup/README.md +138 -0
- package/examples/security-audit/README.md +244 -0
- package/examples/visualforce-migration/README.md +314 -0
- package/hooks/hooks.json +338 -0
- package/hooks/memory-persistence/README.md +73 -0
- package/manifests/install-modules.json +217 -0
- package/manifests/install-profiles.json +17 -0
- package/mcp-configs/mcp-servers.json +19 -0
- package/package.json +89 -0
- package/schemas/hooks.schema.json +123 -0
- package/schemas/install-modules.schema.json +76 -0
- package/schemas/install-profiles.schema.json +28 -0
- package/schemas/install-state.schema.json +73 -0
- package/schemas/package-manager.schema.json +18 -0
- package/schemas/plugin.schema.json +112 -0
- package/schemas/scc-install-config.schema.json +29 -0
- package/schemas/state-store.schema.json +111 -0
- package/scripts/cli/install-apply.js +170 -0
- package/scripts/cli/uninstall.js +193 -0
- package/scripts/hooks/check-console-log.js +101 -0
- package/scripts/hooks/check-hook-enabled.js +17 -0
- package/scripts/hooks/check-platform-docs-age.js +48 -0
- package/scripts/hooks/cost-tracker.js +78 -0
- package/scripts/hooks/doc-file-warning.js +63 -0
- package/scripts/hooks/evaluate-session.js +98 -0
- package/scripts/hooks/governor-check.js +220 -0
- package/scripts/hooks/learning-observe.sh +206 -0
- package/scripts/hooks/mcp-health-check.js +588 -0
- package/scripts/hooks/post-bash-build-complete.js +34 -0
- package/scripts/hooks/post-bash-pr-created.js +43 -0
- package/scripts/hooks/post-edit-console-warn.js +61 -0
- package/scripts/hooks/post-edit-format.js +79 -0
- package/scripts/hooks/post-edit-typecheck.js +98 -0
- package/scripts/hooks/post-write.js +168 -0
- package/scripts/hooks/pre-bash-git-push-reminder.js +35 -0
- package/scripts/hooks/pre-bash-tmux-reminder.js +47 -0
- package/scripts/hooks/pre-compact.js +51 -0
- package/scripts/hooks/pre-tool-use.js +163 -0
- package/scripts/hooks/pre-write-doc-warn.js +9 -0
- package/scripts/hooks/quality-gate.js +251 -0
- package/scripts/hooks/run-with-flags-shell.sh +32 -0
- package/scripts/hooks/run-with-flags.js +135 -0
- package/scripts/hooks/session-end-marker.js +29 -0
- package/scripts/hooks/session-end.js +311 -0
- package/scripts/hooks/session-start.js +202 -0
- package/scripts/hooks/sfdx-scanner-check.js +142 -0
- package/scripts/hooks/sfdx-validate.js +119 -0
- package/scripts/hooks/stop-hook.js +170 -0
- package/scripts/hooks/suggest-compact.js +67 -0
- package/scripts/lib/agent-adapter.js +82 -0
- package/scripts/lib/apex-analysis.js +194 -0
- package/scripts/lib/hook-flags.js +74 -0
- package/scripts/lib/install-config.js +73 -0
- package/scripts/lib/install-executor.js +363 -0
- package/scripts/lib/install-state.js +121 -0
- package/scripts/lib/orchestration-session.js +299 -0
- package/scripts/lib/package-manager.js +124 -0
- package/scripts/lib/project-detect.js +228 -0
- package/scripts/lib/schema-validator.js +190 -0
- package/scripts/lib/skill-adapter.js +100 -0
- package/scripts/lib/state-store.js +376 -0
- package/scripts/lib/tmux-worktree-orchestrator.js +598 -0
- package/scripts/lib/utils.js +313 -0
- package/scripts/scc.js +164 -0
- package/skills/_reference/AGENTFORCE_PATTERNS.md +112 -0
- package/skills/_reference/APEX_CURSOR.md +159 -0
- package/skills/_reference/API_VERSIONS.md +78 -0
- package/skills/_reference/APPROVAL_PROCESSES.md +105 -0
- package/skills/_reference/ASYNC_PATTERNS.md +163 -0
- package/skills/_reference/AURA_COMPONENTS.md +146 -0
- package/skills/_reference/DATA_MIGRATION_PATTERNS.md +151 -0
- package/skills/_reference/DATA_MODELING.md +124 -0
- package/skills/_reference/DEBUGGING_TOOLS.md +140 -0
- package/skills/_reference/DEPLOYMENT_CHECKLIST.md +87 -0
- package/skills/_reference/DEPRECATIONS.md +79 -0
- package/skills/_reference/DOCKER_CI_PATTERNS.md +138 -0
- package/skills/_reference/ENTERPRISE_PATTERNS.md +122 -0
- package/skills/_reference/EXPERIENCE_CLOUD.md +143 -0
- package/skills/_reference/FLOW_PATTERNS.md +113 -0
- package/skills/_reference/GOVERNOR_LIMITS.md +77 -0
- package/skills/_reference/INTEGRATION_PATTERNS.md +105 -0
- package/skills/_reference/LWC_PATTERNS.md +79 -0
- package/skills/_reference/METADATA_TYPES.md +115 -0
- package/skills/_reference/NAMING_CONVENTIONS.md +84 -0
- package/skills/_reference/PACKAGE_DEVELOPMENT.md +150 -0
- package/skills/_reference/PLATFORM_EVENTS.md +121 -0
- package/skills/_reference/REPORTING_API.md +143 -0
- package/skills/_reference/SCRATCH_ORG_PATTERNS.md +126 -0
- package/skills/_reference/SECURITY_PATTERNS.md +127 -0
- package/skills/_reference/SHARING_MODEL.md +120 -0
- package/skills/_reference/SOQL_PATTERNS.md +119 -0
- package/skills/_reference/TESTING_STANDARDS.md +96 -0
- package/skills/_reference/TRIGGER_PATTERNS.md +114 -0
- package/skills/_reference/VISUALFORCE_PATTERNS.md +121 -0
- package/skills/aside/SKILL.md +118 -0
- package/skills/checkpoint/SKILL.md +53 -0
- package/skills/configure-scc/SKILL.md +163 -0
- package/skills/continuous-agent-loop/SKILL.md +264 -0
- package/skills/mcp-server-patterns/SKILL.md +146 -0
- package/skills/model-route/SKILL.md +84 -0
- package/skills/prompt-optimizer/SKILL.md +369 -0
- package/skills/refactor-clean/SKILL.md +136 -0
- package/skills/resume-session/SKILL.md +114 -0
- package/skills/save-session/SKILL.md +186 -0
- package/skills/search-first/SKILL.md +144 -0
- package/skills/security-scan/SKILL.md +146 -0
- package/skills/sessions/SKILL.md +127 -0
- package/skills/sf-agentforce-development/SKILL.md +450 -0
- package/skills/sf-apex-async-patterns/SKILL.md +326 -0
- package/skills/sf-apex-best-practices/SKILL.md +425 -0
- package/skills/sf-apex-constraints/SKILL.md +81 -0
- package/skills/sf-apex-cursor/SKILL.md +338 -0
- package/skills/sf-apex-enterprise-patterns/SKILL.md +348 -0
- package/skills/sf-apex-testing/SKILL.md +409 -0
- package/skills/sf-api-design/SKILL.md +238 -0
- package/skills/sf-approval-processes/SKILL.md +315 -0
- package/skills/sf-aura-development/SKILL.md +263 -0
- package/skills/sf-build-fix/SKILL.md +121 -0
- package/skills/sf-data-modeling/SKILL.md +278 -0
- package/skills/sf-debugging/SKILL.md +363 -0
- package/skills/sf-deployment/SKILL.md +295 -0
- package/skills/sf-deployment-constraints/SKILL.md +155 -0
- package/skills/sf-devops-ci-cd/SKILL.md +325 -0
- package/skills/sf-docs-lookup/SKILL.md +103 -0
- package/skills/sf-e2e-testing/SKILL.md +324 -0
- package/skills/sf-experience-cloud/SKILL.md +249 -0
- package/skills/sf-flow-development/SKILL.md +377 -0
- package/skills/sf-governor-limits/SKILL.md +323 -0
- package/skills/sf-harness-audit/SKILL.md +142 -0
- package/skills/sf-help/SKILL.md +159 -0
- package/skills/sf-integration/SKILL.md +483 -0
- package/skills/sf-lwc-constraints/SKILL.md +130 -0
- package/skills/sf-lwc-development/SKILL.md +303 -0
- package/skills/sf-lwc-testing/SKILL.md +388 -0
- package/skills/sf-metadata-management/SKILL.md +288 -0
- package/skills/sf-platform-events-cdc/SKILL.md +375 -0
- package/skills/sf-quickstart/SKILL.md +173 -0
- package/skills/sf-security/SKILL.md +334 -0
- package/skills/sf-security-constraints/SKILL.md +127 -0
- package/skills/sf-soql-constraints/SKILL.md +131 -0
- package/skills/sf-soql-optimization/SKILL.md +354 -0
- package/skills/sf-tdd-workflow/SKILL.md +336 -0
- package/skills/sf-testing-constraints/SKILL.md +200 -0
- package/skills/sf-trigger-constraints/SKILL.md +90 -0
- package/skills/sf-trigger-frameworks/SKILL.md +347 -0
- package/skills/sf-visualforce-development/SKILL.md +260 -0
- package/skills/strategic-compact/SKILL.md +208 -0
- package/skills/update-docs/SKILL.md +165 -0
- package/skills/update-platform-docs/SKILL.md +90 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawnSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
function stripCodeTicks(value) {
|
|
8
|
+
if (typeof value !== 'string') {
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const trimmed = value.trim();
|
|
13
|
+
if (trimmed.startsWith('`') && trimmed.endsWith('`') && trimmed.length >= 2) {
|
|
14
|
+
return trimmed.slice(1, -1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return trimmed;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function parseSection(content, heading) {
|
|
21
|
+
if (typeof content !== 'string' || content.length === 0) {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const lines = content.split('\n');
|
|
26
|
+
const headingLines = new Set([`## ${heading}`, `**${heading}**`]);
|
|
27
|
+
const startIndex = lines.findIndex(line => headingLines.has(line.trim()));
|
|
28
|
+
|
|
29
|
+
if (startIndex === -1) {
|
|
30
|
+
return '';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const collected = [];
|
|
34
|
+
for (let index = startIndex + 1; index < lines.length; index += 1) {
|
|
35
|
+
const line = lines[index];
|
|
36
|
+
const trimmed = line.trim();
|
|
37
|
+
if (trimmed.startsWith('## ') || (/^\*\*.+\*\*$/.test(trimmed) && !headingLines.has(trimmed))) {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
collected.push(line);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return collected.join('\n').trim();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function parseBullets(section) {
|
|
47
|
+
if (!section) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return section
|
|
52
|
+
.split('\n')
|
|
53
|
+
.map(line => line.trim())
|
|
54
|
+
.filter(line => line.startsWith('- '))
|
|
55
|
+
.map(line => stripCodeTicks(line.replace(/^- /, '').trim()));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function parseWorkerStatus(content) {
|
|
59
|
+
const status = {
|
|
60
|
+
state: null,
|
|
61
|
+
updated: null,
|
|
62
|
+
branch: null,
|
|
63
|
+
worktree: null,
|
|
64
|
+
taskFile: null,
|
|
65
|
+
handoffFile: null
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (typeof content !== 'string' || content.length === 0) {
|
|
69
|
+
return status;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const line of content.split('\n')) {
|
|
73
|
+
const match = line.match(/^- ([A-Za-z ]+):\s*(.+)$/);
|
|
74
|
+
if (!match) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const key = match[1].trim().toLowerCase().replace(/\s+/g, '');
|
|
79
|
+
const value = stripCodeTicks(match[2]);
|
|
80
|
+
|
|
81
|
+
if (key === 'state') status.state = value;
|
|
82
|
+
if (key === 'updated') status.updated = value;
|
|
83
|
+
if (key === 'branch') status.branch = value;
|
|
84
|
+
if (key === 'worktree') status.worktree = value;
|
|
85
|
+
if (key === 'taskfile') status.taskFile = value;
|
|
86
|
+
if (key === 'handofffile') status.handoffFile = value;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return status;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function parseWorkerTask(content) {
|
|
93
|
+
return {
|
|
94
|
+
objective: parseSection(content, 'Objective'),
|
|
95
|
+
seedPaths: parseBullets(parseSection(content, 'Seeded Local Overlays'))
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function parseWorkerHandoff(content) {
|
|
100
|
+
return {
|
|
101
|
+
summary: parseBullets(parseSection(content, 'Summary')),
|
|
102
|
+
validation: parseBullets(parseSection(content, 'Validation')),
|
|
103
|
+
remainingRisks: parseBullets(parseSection(content, 'Remaining Risks'))
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function readTextIfExists(filePath) {
|
|
108
|
+
if (!filePath || !fs.existsSync(filePath)) {
|
|
109
|
+
return '';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function listWorkerDirectories(coordinationDir) {
|
|
116
|
+
if (!coordinationDir || !fs.existsSync(coordinationDir)) {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return fs.readdirSync(coordinationDir, { withFileTypes: true })
|
|
121
|
+
.filter(entry => entry.isDirectory())
|
|
122
|
+
.filter(entry => {
|
|
123
|
+
const workerDir = path.join(coordinationDir, entry.name);
|
|
124
|
+
return ['status.md', 'task.md', 'handoff.md']
|
|
125
|
+
.some(filename => fs.existsSync(path.join(workerDir, filename)));
|
|
126
|
+
})
|
|
127
|
+
.map(entry => entry.name)
|
|
128
|
+
.sort();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function loadWorkerSnapshots(coordinationDir) {
|
|
132
|
+
return listWorkerDirectories(coordinationDir).map(workerSlug => {
|
|
133
|
+
const workerDir = path.join(coordinationDir, workerSlug);
|
|
134
|
+
const statusPath = path.join(workerDir, 'status.md');
|
|
135
|
+
const taskPath = path.join(workerDir, 'task.md');
|
|
136
|
+
const handoffPath = path.join(workerDir, 'handoff.md');
|
|
137
|
+
|
|
138
|
+
const status = parseWorkerStatus(readTextIfExists(statusPath));
|
|
139
|
+
const task = parseWorkerTask(readTextIfExists(taskPath));
|
|
140
|
+
const handoff = parseWorkerHandoff(readTextIfExists(handoffPath));
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
workerSlug,
|
|
144
|
+
workerDir,
|
|
145
|
+
status,
|
|
146
|
+
task,
|
|
147
|
+
handoff,
|
|
148
|
+
files: {
|
|
149
|
+
status: statusPath,
|
|
150
|
+
task: taskPath,
|
|
151
|
+
handoff: handoffPath
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function listTmuxPanes(sessionName, options = {}) {
|
|
158
|
+
const { spawnSyncImpl = spawnSync } = options;
|
|
159
|
+
const format = [
|
|
160
|
+
'#{pane_id}',
|
|
161
|
+
'#{window_index}',
|
|
162
|
+
'#{pane_index}',
|
|
163
|
+
'#{pane_title}',
|
|
164
|
+
'#{pane_current_command}',
|
|
165
|
+
'#{pane_current_path}',
|
|
166
|
+
'#{pane_active}',
|
|
167
|
+
'#{pane_dead}',
|
|
168
|
+
'#{pane_pid}'
|
|
169
|
+
].join('\t');
|
|
170
|
+
|
|
171
|
+
const result = spawnSyncImpl('tmux', ['list-panes', '-t', sessionName, '-F', format], {
|
|
172
|
+
encoding: 'utf8',
|
|
173
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
if (result.error) {
|
|
177
|
+
if (result.error.code === 'ENOENT') {
|
|
178
|
+
return [];
|
|
179
|
+
}
|
|
180
|
+
throw result.error;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (result.status !== 0) {
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return (result.stdout || '')
|
|
188
|
+
.split('\n')
|
|
189
|
+
.map(line => line.trim())
|
|
190
|
+
.filter(Boolean)
|
|
191
|
+
.map(line => {
|
|
192
|
+
const [
|
|
193
|
+
paneId,
|
|
194
|
+
windowIndex,
|
|
195
|
+
paneIndex,
|
|
196
|
+
title,
|
|
197
|
+
currentCommand,
|
|
198
|
+
currentPath,
|
|
199
|
+
active,
|
|
200
|
+
dead,
|
|
201
|
+
pid
|
|
202
|
+
] = line.split('\t');
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
paneId,
|
|
206
|
+
windowIndex: Number(windowIndex),
|
|
207
|
+
paneIndex: Number(paneIndex),
|
|
208
|
+
title,
|
|
209
|
+
currentCommand,
|
|
210
|
+
currentPath,
|
|
211
|
+
active: active === '1',
|
|
212
|
+
dead: dead === '1',
|
|
213
|
+
pid: pid ? Number(pid) : null
|
|
214
|
+
};
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function summarizeWorkerStates(workers) {
|
|
219
|
+
return workers.reduce((counts, worker) => {
|
|
220
|
+
const state = worker.status.state || 'unknown';
|
|
221
|
+
counts[state] = (counts[state] || 0) + 1;
|
|
222
|
+
return counts;
|
|
223
|
+
}, {});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function buildSessionSnapshot({ sessionName, coordinationDir, panes }) {
|
|
227
|
+
const workerSnapshots = loadWorkerSnapshots(coordinationDir);
|
|
228
|
+
const paneMap = new Map(panes.map(pane => [pane.title, pane]));
|
|
229
|
+
|
|
230
|
+
const workers = workerSnapshots.map(worker => ({
|
|
231
|
+
...worker,
|
|
232
|
+
pane: paneMap.get(worker.workerSlug) || null
|
|
233
|
+
}));
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
sessionName,
|
|
237
|
+
coordinationDir,
|
|
238
|
+
sessionActive: panes.length > 0,
|
|
239
|
+
paneCount: panes.length,
|
|
240
|
+
workerCount: workers.length,
|
|
241
|
+
workerStates: summarizeWorkerStates(workers),
|
|
242
|
+
panes,
|
|
243
|
+
workers
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function resolveSnapshotTarget(targetPath, cwd = process.cwd()) {
|
|
248
|
+
const absoluteTarget = path.resolve(cwd, targetPath);
|
|
249
|
+
|
|
250
|
+
if (fs.existsSync(absoluteTarget) && fs.statSync(absoluteTarget).isFile()) {
|
|
251
|
+
const config = JSON.parse(fs.readFileSync(absoluteTarget, 'utf8'));
|
|
252
|
+
const repoRoot = path.resolve(config.repoRoot || cwd);
|
|
253
|
+
const coordinationRoot = path.resolve(
|
|
254
|
+
config.coordinationRoot || path.join(repoRoot, '.orchestration')
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
sessionName: config.sessionName,
|
|
259
|
+
coordinationDir: path.join(coordinationRoot, config.sessionName),
|
|
260
|
+
repoRoot,
|
|
261
|
+
targetType: 'plan'
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
sessionName: targetPath,
|
|
267
|
+
coordinationDir: path.join(cwd, '.claude', 'orchestration', targetPath),
|
|
268
|
+
repoRoot: cwd,
|
|
269
|
+
targetType: 'session'
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function collectSessionSnapshot(targetPath, cwd = process.cwd()) {
|
|
274
|
+
const target = resolveSnapshotTarget(targetPath, cwd);
|
|
275
|
+
const panes = listTmuxPanes(target.sessionName);
|
|
276
|
+
const snapshot = buildSessionSnapshot({
|
|
277
|
+
sessionName: target.sessionName,
|
|
278
|
+
coordinationDir: target.coordinationDir,
|
|
279
|
+
panes
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
...snapshot,
|
|
284
|
+
repoRoot: target.repoRoot,
|
|
285
|
+
targetType: target.targetType
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
module.exports = {
|
|
290
|
+
buildSessionSnapshot,
|
|
291
|
+
collectSessionSnapshot,
|
|
292
|
+
listTmuxPanes,
|
|
293
|
+
loadWorkerSnapshots,
|
|
294
|
+
normalizeText: stripCodeTicks,
|
|
295
|
+
parseWorkerHandoff,
|
|
296
|
+
parseWorkerStatus,
|
|
297
|
+
parseWorkerTask,
|
|
298
|
+
resolveSnapshotTarget
|
|
299
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* package-manager.js — Detect the active package manager (npm/pnpm/yarn/bun).
|
|
5
|
+
*
|
|
6
|
+
* Detection order:
|
|
7
|
+
* 1. CLAUDE_PACKAGE_MANAGER env var override
|
|
8
|
+
* 2. Presence of lock files in cwd
|
|
9
|
+
* 3. npm_execpath env var set by npm/yarn
|
|
10
|
+
* 4. Fallback: npm
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { spawnSync } = require('child_process');
|
|
16
|
+
|
|
17
|
+
const LOCK_FILES = [
|
|
18
|
+
{ file: 'bun.lockb', manager: 'bun' },
|
|
19
|
+
{ file: 'yarn.lock', manager: 'yarn' },
|
|
20
|
+
{ file: 'pnpm-lock.yaml', manager: 'pnpm' },
|
|
21
|
+
{ file: 'package-lock.json', manager: 'npm' },
|
|
22
|
+
{ file: 'npm-shrinkwrap.json', manager: 'npm' },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Detect which package manager is in use.
|
|
27
|
+
* @param {string} [cwd] - Directory to check for lock files (defaults to process.cwd())
|
|
28
|
+
* @returns {'npm' | 'pnpm' | 'yarn' | 'bun'}
|
|
29
|
+
*/
|
|
30
|
+
function detectPackageManager(cwd) {
|
|
31
|
+
// 1. Environment override
|
|
32
|
+
const override = process.env.CLAUDE_PACKAGE_MANAGER || process.env.SCC_PACKAGE_MANAGER;
|
|
33
|
+
if (override && ['npm', 'pnpm', 'yarn', 'bun'].includes(override.toLowerCase())) {
|
|
34
|
+
return override.toLowerCase();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const dir = cwd || process.cwd();
|
|
38
|
+
|
|
39
|
+
// 2. Lock file detection
|
|
40
|
+
for (const { file, manager } of LOCK_FILES) {
|
|
41
|
+
if (fs.existsSync(path.join(dir, file))) {
|
|
42
|
+
return manager;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 3. npm_execpath (set by npm/yarn when running scripts)
|
|
47
|
+
const execPath = process.env.npm_execpath || '';
|
|
48
|
+
if (execPath.includes('yarn')) return 'yarn';
|
|
49
|
+
if (execPath.includes('pnpm')) return 'pnpm';
|
|
50
|
+
if (execPath.includes('bun')) return 'bun';
|
|
51
|
+
|
|
52
|
+
// 4. Try to detect installed package managers by running them
|
|
53
|
+
for (const pm of ['bun', 'pnpm', 'yarn']) {
|
|
54
|
+
const result = spawnSync(pm, ['--version'], { encoding: 'utf8', timeout: 2000 });
|
|
55
|
+
if (result.status === 0) return pm;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return 'npm';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get the install command for a given package.
|
|
63
|
+
* @param {string} pkg - Package name (e.g., 'sql.js')
|
|
64
|
+
* @param {string} [manager] - Override package manager
|
|
65
|
+
* @returns {string} Full install command string
|
|
66
|
+
*/
|
|
67
|
+
function getInstallCommand(pkg, manager) {
|
|
68
|
+
const pm = manager || detectPackageManager();
|
|
69
|
+
switch (pm) {
|
|
70
|
+
case 'yarn':
|
|
71
|
+
return `yarn add ${pkg}`;
|
|
72
|
+
case 'pnpm':
|
|
73
|
+
return `pnpm add ${pkg}`;
|
|
74
|
+
case 'bun':
|
|
75
|
+
return `bun add ${pkg}`;
|
|
76
|
+
case 'npm':
|
|
77
|
+
default:
|
|
78
|
+
return `npm install ${pkg}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get the run command for a script.
|
|
84
|
+
* @param {string} script - Script name
|
|
85
|
+
* @param {string} [manager] - Override package manager
|
|
86
|
+
* @returns {string}
|
|
87
|
+
*/
|
|
88
|
+
function getRunCommand(script, manager) {
|
|
89
|
+
const pm = manager || detectPackageManager();
|
|
90
|
+
switch (pm) {
|
|
91
|
+
case 'yarn':
|
|
92
|
+
return `yarn ${script}`;
|
|
93
|
+
case 'pnpm':
|
|
94
|
+
return `pnpm run ${script}`;
|
|
95
|
+
case 'bun':
|
|
96
|
+
return `bun run ${script}`;
|
|
97
|
+
case 'npm':
|
|
98
|
+
default:
|
|
99
|
+
return `npm run ${script}`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get the exec/dlx command (equivalent of npx).
|
|
105
|
+
* @param {string} bin - Binary to exec
|
|
106
|
+
* @param {string} [manager] - Override package manager
|
|
107
|
+
* @returns {string}
|
|
108
|
+
*/
|
|
109
|
+
function getExecCommand(bin, manager) {
|
|
110
|
+
const pm = manager || detectPackageManager();
|
|
111
|
+
switch (pm) {
|
|
112
|
+
case 'yarn':
|
|
113
|
+
return `yarn dlx ${bin}`;
|
|
114
|
+
case 'pnpm':
|
|
115
|
+
return `pnpm dlx ${bin}`;
|
|
116
|
+
case 'bun':
|
|
117
|
+
return `bunx ${bin}`;
|
|
118
|
+
case 'npm':
|
|
119
|
+
default:
|
|
120
|
+
return `npx ${bin}`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = { detectPackageManager, getInstallCommand, getRunCommand, getExecCommand };
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Salesforce project type and framework detection
|
|
3
|
+
*
|
|
4
|
+
* Cross-platform (Windows, macOS, Linux) project type detection
|
|
5
|
+
* by inspecting files in the working directory. Tailored for Salesforce
|
|
6
|
+
* projects with support for common web technologies used alongside SF.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Salesforce project detection rules.
|
|
14
|
+
* Each rule checks for marker files specific to Salesforce project types.
|
|
15
|
+
*/
|
|
16
|
+
const SF_PROJECT_RULES = [
|
|
17
|
+
{
|
|
18
|
+
type: 'sfdx',
|
|
19
|
+
markers: ['sfdx-project.json', 'sf-project.json'],
|
|
20
|
+
description: 'Salesforce DX Project'
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
type: 'lwc',
|
|
24
|
+
markers: ['force-app/main/default/lwc'],
|
|
25
|
+
description: 'Lightning Web Components'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: 'apex',
|
|
29
|
+
markers: ['force-app/main/default/classes'],
|
|
30
|
+
description: 'Apex Classes'
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: 'aura',
|
|
34
|
+
markers: ['force-app/main/default/aura'],
|
|
35
|
+
description: 'Aura Components (Legacy)'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: 'flow',
|
|
39
|
+
markers: ['force-app/main/default/flows'],
|
|
40
|
+
description: 'Salesforce Flows'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'trigger',
|
|
44
|
+
markers: ['force-app/main/default/triggers'],
|
|
45
|
+
description: 'Apex Triggers'
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Language detection rules for non-Salesforce languages.
|
|
51
|
+
*/
|
|
52
|
+
const LANGUAGE_RULES = [
|
|
53
|
+
{
|
|
54
|
+
type: 'typescript',
|
|
55
|
+
markers: ['tsconfig.json', 'tsconfig.build.json'],
|
|
56
|
+
extensions: ['.ts', '.tsx']
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
type: 'javascript',
|
|
60
|
+
markers: ['package.json', 'jsconfig.json'],
|
|
61
|
+
extensions: ['.js', '.jsx', '.mjs']
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
type: 'java',
|
|
65
|
+
markers: ['pom.xml', 'build.gradle', 'build.gradle.kts'],
|
|
66
|
+
extensions: ['.java']
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: 'python',
|
|
70
|
+
markers: ['requirements.txt', 'pyproject.toml', 'setup.py'],
|
|
71
|
+
extensions: ['.py']
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Framework detection rules relevant to Salesforce ecosystems.
|
|
77
|
+
*/
|
|
78
|
+
const FRAMEWORK_RULES = [
|
|
79
|
+
// Salesforce frameworks
|
|
80
|
+
{ framework: 'fflib', language: 'apex', markers: [], packageKeys: ['fflib'] },
|
|
81
|
+
|
|
82
|
+
// JavaScript/TypeScript frameworks (for LWC/Heroku)
|
|
83
|
+
{ framework: 'react', language: 'typescript', markers: [], packageKeys: ['react'] },
|
|
84
|
+
{ framework: 'express', language: 'javascript', markers: [], packageKeys: ['express'] },
|
|
85
|
+
{ framework: 'nextjs', language: 'typescript', markers: ['next.config.js', 'next.config.mjs', 'next.config.ts'], packageKeys: ['next'] },
|
|
86
|
+
|
|
87
|
+
// Java frameworks (for SF Java SDK)
|
|
88
|
+
{ framework: 'spring', language: 'java', markers: [], packageKeys: ['spring-boot', 'org.springframework'] }
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Check if a file or directory exists relative to the project directory
|
|
93
|
+
*/
|
|
94
|
+
function fileExists(projectDir, filePath) {
|
|
95
|
+
try {
|
|
96
|
+
return fs.existsSync(path.join(projectDir, filePath));
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Check if any file with given extension exists in the project root
|
|
104
|
+
*/
|
|
105
|
+
function hasFileWithExtension(projectDir, extensions) {
|
|
106
|
+
try {
|
|
107
|
+
const entries = fs.readdirSync(projectDir, { withFileTypes: true });
|
|
108
|
+
return entries.some(entry => {
|
|
109
|
+
if (!entry.isFile()) return false;
|
|
110
|
+
const ext = path.extname(entry.name);
|
|
111
|
+
return extensions.includes(ext);
|
|
112
|
+
});
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Read and parse package.json dependencies
|
|
120
|
+
*/
|
|
121
|
+
function getPackageJsonDeps(projectDir) {
|
|
122
|
+
try {
|
|
123
|
+
const pkgPath = path.join(projectDir, 'package.json');
|
|
124
|
+
if (!fs.existsSync(pkgPath)) return [];
|
|
125
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
126
|
+
return [...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.devDependencies || {})];
|
|
127
|
+
} catch {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Read sfdx-project.json for Salesforce project configuration
|
|
134
|
+
*/
|
|
135
|
+
function getSfdxProjectConfig(projectDir) {
|
|
136
|
+
try {
|
|
137
|
+
const sfdxPath = path.join(projectDir, 'sfdx-project.json');
|
|
138
|
+
if (!fs.existsSync(sfdxPath)) {
|
|
139
|
+
const sfPath = path.join(projectDir, 'sf-project.json');
|
|
140
|
+
if (!fs.existsSync(sfPath)) return null;
|
|
141
|
+
return JSON.parse(fs.readFileSync(sfPath, 'utf8'));
|
|
142
|
+
}
|
|
143
|
+
return JSON.parse(fs.readFileSync(sfdxPath, 'utf8'));
|
|
144
|
+
} catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Detect Salesforce project type, languages, and frameworks
|
|
151
|
+
* @param {string} [projectDir] - Project directory (defaults to cwd)
|
|
152
|
+
* @returns {{ sfTypes: string[], languages: string[], frameworks: string[], primary: string, projectDir: string, sfdxConfig: Object|null }}
|
|
153
|
+
*/
|
|
154
|
+
function detectProjectType(projectDir) {
|
|
155
|
+
projectDir = projectDir || process.cwd();
|
|
156
|
+
const sfTypes = [];
|
|
157
|
+
const languages = [];
|
|
158
|
+
const frameworks = [];
|
|
159
|
+
|
|
160
|
+
// Step 1: Detect Salesforce project types
|
|
161
|
+
for (const rule of SF_PROJECT_RULES) {
|
|
162
|
+
const hasMarker = rule.markers.some(m => fileExists(projectDir, m));
|
|
163
|
+
if (hasMarker) {
|
|
164
|
+
sfTypes.push(rule.type);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Step 2: Detect additional languages
|
|
169
|
+
for (const rule of LANGUAGE_RULES) {
|
|
170
|
+
const hasMarker = rule.markers.some(m => fileExists(projectDir, m));
|
|
171
|
+
const hasExt = rule.extensions.length > 0 && hasFileWithExtension(projectDir, rule.extensions);
|
|
172
|
+
if (hasMarker || hasExt) {
|
|
173
|
+
languages.push(rule.type);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Deduplicate: if both typescript and javascript detected, keep typescript
|
|
178
|
+
if (languages.includes('typescript') && languages.includes('javascript')) {
|
|
179
|
+
const idx = languages.indexOf('javascript');
|
|
180
|
+
if (idx !== -1) languages.splice(idx, 1);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Step 3: Detect frameworks
|
|
184
|
+
const npmDeps = getPackageJsonDeps(projectDir);
|
|
185
|
+
|
|
186
|
+
for (const rule of FRAMEWORK_RULES) {
|
|
187
|
+
const hasMarker = rule.markers.some(m => fileExists(projectDir, m));
|
|
188
|
+
let hasDep = false;
|
|
189
|
+
if (rule.packageKeys.length > 0 && (rule.language === 'typescript' || rule.language === 'javascript')) {
|
|
190
|
+
hasDep = rule.packageKeys.some(key => npmDeps.some(dep => dep.toLowerCase().includes(key.toLowerCase())));
|
|
191
|
+
}
|
|
192
|
+
if (hasMarker || hasDep) {
|
|
193
|
+
frameworks.push(rule.framework);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Step 4: Determine primary type
|
|
198
|
+
let primary = 'unknown';
|
|
199
|
+
if (sfTypes.length > 0) {
|
|
200
|
+
primary = 'salesforce';
|
|
201
|
+
} else if (frameworks.length > 0) {
|
|
202
|
+
primary = frameworks[0];
|
|
203
|
+
} else if (languages.length > 0) {
|
|
204
|
+
primary = languages[0];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Step 5: Read SFDX project config
|
|
208
|
+
const sfdxConfig = getSfdxProjectConfig(projectDir);
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
sfTypes,
|
|
212
|
+
languages,
|
|
213
|
+
frameworks,
|
|
214
|
+
primary,
|
|
215
|
+
projectDir,
|
|
216
|
+
sfdxConfig
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
module.exports = {
|
|
221
|
+
detectProjectType,
|
|
222
|
+
SF_PROJECT_RULES,
|
|
223
|
+
LANGUAGE_RULES,
|
|
224
|
+
FRAMEWORK_RULES,
|
|
225
|
+
// Exported for testing
|
|
226
|
+
getPackageJsonDeps,
|
|
227
|
+
getSfdxProjectConfig
|
|
228
|
+
};
|