awesome-slash 2.4.2
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/marketplace.json +54 -0
- package/.claude-plugin/plugin.json +11 -0
- package/.mcp.json +8 -0
- package/CHANGELOG.md +261 -0
- package/LICENSE +21 -0
- package/README.md +363 -0
- package/SECURITY.md +101 -0
- package/adapters/README.md +256 -0
- package/adapters/codex/README.md +272 -0
- package/adapters/codex/install.sh +179 -0
- package/adapters/opencode/README.md +301 -0
- package/adapters/opencode/install.sh +223 -0
- package/lib/patterns/review-patterns.js +511 -0
- package/lib/patterns/slop-patterns.js +647 -0
- package/lib/platform/detect-platform.js +535 -0
- package/lib/platform/verify-tools.js +235 -0
- package/lib/state/workflow-state.js +635 -0
- package/lib/state/workflow-state.schema.json +282 -0
- package/lib/utils/context-optimizer.js +227 -0
- package/mcp-server/index.js +303 -0
- package/mcp-server/package.json +23 -0
- package/package.json +63 -0
- package/plugins/deslop-around/.claude-plugin/plugin.json +20 -0
- package/plugins/deslop-around/commands/deslop-around.md +220 -0
- package/plugins/deslop-around/lib/patterns/review-patterns.js +511 -0
- package/plugins/deslop-around/lib/patterns/slop-patterns.js +641 -0
- package/plugins/deslop-around/lib/platform/detect-platform.js +514 -0
- package/plugins/deslop-around/lib/platform/verify-tools.js +235 -0
- package/plugins/deslop-around/lib/state/workflow-state.js +635 -0
- package/plugins/deslop-around/lib/state/workflow-state.schema.json +282 -0
- package/plugins/deslop-around/lib/utils/context-optimizer.js +222 -0
- package/plugins/next-task/.claude-plugin/plugin.json +24 -0
- package/plugins/next-task/agents/ci-fixer.md +236 -0
- package/plugins/next-task/agents/ci-monitor.md +291 -0
- package/plugins/next-task/agents/delivery-validator.md +451 -0
- package/plugins/next-task/agents/deslop-work.md +272 -0
- package/plugins/next-task/agents/docs-updater.md +506 -0
- package/plugins/next-task/agents/exploration-agent.md +277 -0
- package/plugins/next-task/agents/implementation-agent.md +427 -0
- package/plugins/next-task/agents/planning-agent.md +236 -0
- package/plugins/next-task/agents/policy-selector.md +248 -0
- package/plugins/next-task/agents/review-orchestrator.md +521 -0
- package/plugins/next-task/agents/simple-fixer.md +136 -0
- package/plugins/next-task/agents/task-discoverer.md +357 -0
- package/plugins/next-task/agents/test-coverage-checker.md +447 -0
- package/plugins/next-task/agents/worktree-manager.md +419 -0
- package/plugins/next-task/commands/delivery-approval.md +331 -0
- package/plugins/next-task/commands/next-task.md +627 -0
- package/plugins/next-task/commands/update-docs-around.md +418 -0
- package/plugins/next-task/hooks/hooks.json +14 -0
- package/plugins/next-task/lib/patterns/review-patterns.js +511 -0
- package/plugins/next-task/lib/patterns/slop-patterns.js +641 -0
- package/plugins/next-task/lib/platform/detect-platform.js +514 -0
- package/plugins/next-task/lib/platform/verify-tools.js +235 -0
- package/plugins/next-task/lib/state/tasks-registry.schema.json +85 -0
- package/plugins/next-task/lib/state/workflow-state.js +635 -0
- package/plugins/next-task/lib/state/workflow-state.schema.json +282 -0
- package/plugins/next-task/lib/state/worktree-status.schema.json +219 -0
- package/plugins/next-task/lib/utils/context-optimizer.js +222 -0
- package/plugins/project-review/.claude-plugin/plugin.json +20 -0
- package/plugins/project-review/commands/project-review-agents.md +286 -0
- package/plugins/project-review/commands/project-review-github.md +142 -0
- package/plugins/project-review/commands/project-review.md +273 -0
- package/plugins/project-review/lib/patterns/review-patterns.js +511 -0
- package/plugins/project-review/lib/patterns/slop-patterns.js +641 -0
- package/plugins/project-review/lib/platform/detect-platform.js +514 -0
- package/plugins/project-review/lib/platform/verify-tools.js +235 -0
- package/plugins/project-review/lib/state/workflow-state.js +635 -0
- package/plugins/project-review/lib/state/workflow-state.schema.json +282 -0
- package/plugins/project-review/lib/utils/context-optimizer.js +222 -0
- package/plugins/reality-check/.claude-plugin/plugin.json +23 -0
- package/plugins/reality-check/README.md +156 -0
- package/plugins/reality-check/agents/code-explorer.md +353 -0
- package/plugins/reality-check/agents/doc-analyzer.md +337 -0
- package/plugins/reality-check/agents/issue-scanner.md +231 -0
- package/plugins/reality-check/agents/plan-synthesizer.md +479 -0
- package/plugins/reality-check/commands/scan.md +242 -0
- package/plugins/reality-check/commands/set.md +203 -0
- package/plugins/reality-check/lib/state/reality-check-state.js +509 -0
- package/plugins/reality-check/skills/reality-analysis/SKILL.md +317 -0
- package/plugins/ship/.claude-plugin/plugin.json +21 -0
- package/plugins/ship/commands/ship-ci-review-loop.md +443 -0
- package/plugins/ship/commands/ship-deployment.md +330 -0
- package/plugins/ship/commands/ship-error-handling.md +254 -0
- package/plugins/ship/commands/ship.md +370 -0
- package/plugins/ship/lib/patterns/review-patterns.js +511 -0
- package/plugins/ship/lib/patterns/slop-patterns.js +641 -0
- package/plugins/ship/lib/platform/detect-platform.js +514 -0
- package/plugins/ship/lib/platform/verify-tools.js +235 -0
- package/plugins/ship/lib/state/workflow-state.js +635 -0
- package/plugins/ship/lib/state/workflow-state.schema.json +282 -0
- package/plugins/ship/lib/utils/context-optimizer.js +222 -0
- package/scripts/install/claude.sh +50 -0
- package/scripts/install/codex.sh +181 -0
- package/scripts/install/opencode.sh +211 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Platform Detection Infrastructure
|
|
4
|
+
* Auto-detects project configuration for zero-config slash commands
|
|
5
|
+
*
|
|
6
|
+
* Usage: node lib/platform/detect-platform.js
|
|
7
|
+
* Output: JSON with detected platform information
|
|
8
|
+
*
|
|
9
|
+
* @author Avi Fenesh
|
|
10
|
+
* @license MIT
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const { execSync, exec } = require('child_process');
|
|
15
|
+
const { promisify } = require('util');
|
|
16
|
+
|
|
17
|
+
const execAsync = promisify(exec);
|
|
18
|
+
const fsPromises = fs.promises;
|
|
19
|
+
|
|
20
|
+
// Detection cache for performance (platform rarely changes during session)
|
|
21
|
+
let _cachedDetection = null;
|
|
22
|
+
let _cacheExpiry = 0;
|
|
23
|
+
const CACHE_TTL_MS = 60000; // 1 minute cache
|
|
24
|
+
|
|
25
|
+
// File read cache to avoid reading the same file multiple times (#17)
|
|
26
|
+
const _fileCache = new Map();
|
|
27
|
+
const _existsCache = new Map();
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Check if a file exists (cached)
|
|
31
|
+
* @param {string} filepath - Path to check
|
|
32
|
+
* @returns {boolean}
|
|
33
|
+
*/
|
|
34
|
+
function existsCached(filepath) {
|
|
35
|
+
if (_existsCache.has(filepath)) {
|
|
36
|
+
return _existsCache.get(filepath);
|
|
37
|
+
}
|
|
38
|
+
const exists = fs.existsSync(filepath);
|
|
39
|
+
_existsCache.set(filepath, exists);
|
|
40
|
+
return exists;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Check if a file exists (cached, async)
|
|
45
|
+
* @param {string} filepath - Path to check
|
|
46
|
+
* @returns {Promise<boolean>}
|
|
47
|
+
*/
|
|
48
|
+
async function existsCachedAsync(filepath) {
|
|
49
|
+
if (_existsCache.has(filepath)) {
|
|
50
|
+
return _existsCache.get(filepath);
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
await fsPromises.access(filepath);
|
|
54
|
+
_existsCache.set(filepath, true);
|
|
55
|
+
return true;
|
|
56
|
+
} catch {
|
|
57
|
+
_existsCache.set(filepath, false);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Read file contents (cached)
|
|
64
|
+
* @param {string} filepath - Path to read
|
|
65
|
+
* @returns {string|null}
|
|
66
|
+
*/
|
|
67
|
+
function readFileCached(filepath) {
|
|
68
|
+
if (_fileCache.has(filepath)) {
|
|
69
|
+
return _fileCache.get(filepath);
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const content = fs.readFileSync(filepath, 'utf8');
|
|
73
|
+
_fileCache.set(filepath, content);
|
|
74
|
+
return content;
|
|
75
|
+
} catch {
|
|
76
|
+
_fileCache.set(filepath, null);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Read file contents (cached, async)
|
|
83
|
+
* @param {string} filepath - Path to read
|
|
84
|
+
* @returns {Promise<string|null>}
|
|
85
|
+
*/
|
|
86
|
+
async function readFileCachedAsync(filepath) {
|
|
87
|
+
if (_fileCache.has(filepath)) {
|
|
88
|
+
return _fileCache.get(filepath);
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const content = await fsPromises.readFile(filepath, 'utf8');
|
|
92
|
+
_fileCache.set(filepath, content);
|
|
93
|
+
return content;
|
|
94
|
+
} catch {
|
|
95
|
+
_fileCache.set(filepath, null);
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Detects CI platform by scanning for configuration files
|
|
102
|
+
* @returns {string|null} CI platform name or null if not detected
|
|
103
|
+
*/
|
|
104
|
+
function detectCI() {
|
|
105
|
+
if (existsCached('.github/workflows')) return 'github-actions';
|
|
106
|
+
if (existsCached('.gitlab-ci.yml')) return 'gitlab-ci';
|
|
107
|
+
if (existsCached('.circleci/config.yml')) return 'circleci';
|
|
108
|
+
if (existsCached('Jenkinsfile')) return 'jenkins';
|
|
109
|
+
if (existsCached('.travis.yml')) return 'travis';
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Detects CI platform by scanning for configuration files (async)
|
|
115
|
+
* @returns {Promise<string|null>} CI platform name or null if not detected
|
|
116
|
+
*/
|
|
117
|
+
async function detectCIAsync() {
|
|
118
|
+
const checks = await Promise.all([
|
|
119
|
+
existsCachedAsync('.github/workflows'),
|
|
120
|
+
existsCachedAsync('.gitlab-ci.yml'),
|
|
121
|
+
existsCachedAsync('.circleci/config.yml'),
|
|
122
|
+
existsCachedAsync('Jenkinsfile'),
|
|
123
|
+
existsCachedAsync('.travis.yml')
|
|
124
|
+
]);
|
|
125
|
+
|
|
126
|
+
if (checks[0]) return 'github-actions';
|
|
127
|
+
if (checks[1]) return 'gitlab-ci';
|
|
128
|
+
if (checks[2]) return 'circleci';
|
|
129
|
+
if (checks[3]) return 'jenkins';
|
|
130
|
+
if (checks[4]) return 'travis';
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Detects deployment platform by scanning for platform-specific files
|
|
136
|
+
* @returns {string|null} Deployment platform name or null if not detected
|
|
137
|
+
*/
|
|
138
|
+
function detectDeployment() {
|
|
139
|
+
if (existsCached('railway.json') || existsCached('railway.toml')) return 'railway';
|
|
140
|
+
if (existsCached('vercel.json')) return 'vercel';
|
|
141
|
+
if (existsCached('netlify.toml') || existsCached('.netlify')) return 'netlify';
|
|
142
|
+
if (existsCached('fly.toml')) return 'fly';
|
|
143
|
+
if (existsCached('.platform.sh')) return 'platform-sh';
|
|
144
|
+
if (existsCached('render.yaml')) return 'render';
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Detects deployment platform by scanning for platform-specific files (async)
|
|
150
|
+
* @returns {Promise<string|null>} Deployment platform name or null if not detected
|
|
151
|
+
*/
|
|
152
|
+
async function detectDeploymentAsync() {
|
|
153
|
+
const checks = await Promise.all([
|
|
154
|
+
existsCachedAsync('railway.json'),
|
|
155
|
+
existsCachedAsync('railway.toml'),
|
|
156
|
+
existsCachedAsync('vercel.json'),
|
|
157
|
+
existsCachedAsync('netlify.toml'),
|
|
158
|
+
existsCachedAsync('.netlify'),
|
|
159
|
+
existsCachedAsync('fly.toml'),
|
|
160
|
+
existsCachedAsync('.platform.sh'),
|
|
161
|
+
existsCachedAsync('render.yaml')
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
if (checks[0] || checks[1]) return 'railway';
|
|
165
|
+
if (checks[2]) return 'vercel';
|
|
166
|
+
if (checks[3] || checks[4]) return 'netlify';
|
|
167
|
+
if (checks[5]) return 'fly';
|
|
168
|
+
if (checks[6]) return 'platform-sh';
|
|
169
|
+
if (checks[7]) return 'render';
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Detects project type by scanning for language-specific files
|
|
175
|
+
* @returns {string} Project type identifier
|
|
176
|
+
*/
|
|
177
|
+
function detectProjectType() {
|
|
178
|
+
if (existsCached('package.json')) return 'nodejs';
|
|
179
|
+
if (existsCached('requirements.txt') || existsCached('pyproject.toml') || existsCached('setup.py')) return 'python';
|
|
180
|
+
if (existsCached('Cargo.toml')) return 'rust';
|
|
181
|
+
if (existsCached('go.mod')) return 'go';
|
|
182
|
+
if (existsCached('pom.xml') || existsCached('build.gradle')) return 'java';
|
|
183
|
+
return 'unknown';
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Detects project type by scanning for language-specific files (async)
|
|
188
|
+
* @returns {Promise<string>} Project type identifier
|
|
189
|
+
*/
|
|
190
|
+
async function detectProjectTypeAsync() {
|
|
191
|
+
const checks = await Promise.all([
|
|
192
|
+
existsCachedAsync('package.json'),
|
|
193
|
+
existsCachedAsync('requirements.txt'),
|
|
194
|
+
existsCachedAsync('pyproject.toml'),
|
|
195
|
+
existsCachedAsync('setup.py'),
|
|
196
|
+
existsCachedAsync('Cargo.toml'),
|
|
197
|
+
existsCachedAsync('go.mod'),
|
|
198
|
+
existsCachedAsync('pom.xml'),
|
|
199
|
+
existsCachedAsync('build.gradle')
|
|
200
|
+
]);
|
|
201
|
+
|
|
202
|
+
if (checks[0]) return 'nodejs';
|
|
203
|
+
if (checks[1] || checks[2] || checks[3]) return 'python';
|
|
204
|
+
if (checks[4]) return 'rust';
|
|
205
|
+
if (checks[5]) return 'go';
|
|
206
|
+
if (checks[6] || checks[7]) return 'java';
|
|
207
|
+
return 'unknown';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Detects package manager by scanning for lockfiles
|
|
212
|
+
* @returns {string|null} Package manager name or null if not detected
|
|
213
|
+
*/
|
|
214
|
+
function detectPackageManager() {
|
|
215
|
+
if (existsCached('pnpm-lock.yaml')) return 'pnpm';
|
|
216
|
+
if (existsCached('yarn.lock')) return 'yarn';
|
|
217
|
+
if (existsCached('bun.lockb')) return 'bun';
|
|
218
|
+
if (existsCached('package-lock.json')) return 'npm';
|
|
219
|
+
if (existsCached('poetry.lock')) return 'poetry';
|
|
220
|
+
if (existsCached('Pipfile.lock')) return 'pipenv';
|
|
221
|
+
if (existsCached('Cargo.lock')) return 'cargo';
|
|
222
|
+
if (existsCached('go.sum')) return 'go';
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Detects package manager by scanning for lockfiles (async)
|
|
228
|
+
* @returns {Promise<string|null>} Package manager name or null if not detected
|
|
229
|
+
*/
|
|
230
|
+
async function detectPackageManagerAsync() {
|
|
231
|
+
const checks = await Promise.all([
|
|
232
|
+
existsCachedAsync('pnpm-lock.yaml'),
|
|
233
|
+
existsCachedAsync('yarn.lock'),
|
|
234
|
+
existsCachedAsync('bun.lockb'),
|
|
235
|
+
existsCachedAsync('package-lock.json'),
|
|
236
|
+
existsCachedAsync('poetry.lock'),
|
|
237
|
+
existsCachedAsync('Pipfile.lock'),
|
|
238
|
+
existsCachedAsync('Cargo.lock'),
|
|
239
|
+
existsCachedAsync('go.sum')
|
|
240
|
+
]);
|
|
241
|
+
|
|
242
|
+
if (checks[0]) return 'pnpm';
|
|
243
|
+
if (checks[1]) return 'yarn';
|
|
244
|
+
if (checks[2]) return 'bun';
|
|
245
|
+
if (checks[3]) return 'npm';
|
|
246
|
+
if (checks[4]) return 'poetry';
|
|
247
|
+
if (checks[5]) return 'pipenv';
|
|
248
|
+
if (checks[6]) return 'cargo';
|
|
249
|
+
if (checks[7]) return 'go';
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Detects branch strategy (single-branch vs multi-branch with dev+prod)
|
|
255
|
+
* @returns {string} 'single-branch' or 'multi-branch'
|
|
256
|
+
*/
|
|
257
|
+
function detectBranchStrategy() {
|
|
258
|
+
try {
|
|
259
|
+
// Check both local and remote branches
|
|
260
|
+
const localBranches = execSync('git branch', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
|
|
261
|
+
let remoteBranches = '';
|
|
262
|
+
try {
|
|
263
|
+
remoteBranches = execSync('git branch -r', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
|
|
264
|
+
} catch {}
|
|
265
|
+
|
|
266
|
+
const allBranches = localBranches + remoteBranches;
|
|
267
|
+
|
|
268
|
+
const hasStable = allBranches.includes('stable');
|
|
269
|
+
const hasProduction = allBranches.includes('production') || allBranches.includes('prod');
|
|
270
|
+
|
|
271
|
+
if (hasStable || hasProduction) {
|
|
272
|
+
return 'multi-branch'; // dev + prod workflow
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Check deployment configs for multi-environment setup (uses cache)
|
|
276
|
+
if (existsCached('railway.json')) {
|
|
277
|
+
try {
|
|
278
|
+
const content = readFileCached('railway.json');
|
|
279
|
+
if (content) {
|
|
280
|
+
const config = JSON.parse(content);
|
|
281
|
+
// Validate JSON structure before accessing properties
|
|
282
|
+
if (config &&
|
|
283
|
+
typeof config === 'object' &&
|
|
284
|
+
typeof config.environments === 'object' &&
|
|
285
|
+
config.environments !== null &&
|
|
286
|
+
Object.keys(config.environments).length > 1) {
|
|
287
|
+
return 'multi-branch';
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
} catch {}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return 'single-branch'; // main only
|
|
294
|
+
} catch {
|
|
295
|
+
return 'single-branch';
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Detects branch strategy (single-branch vs multi-branch with dev+prod) (async)
|
|
301
|
+
* @returns {Promise<string>} 'single-branch' or 'multi-branch'
|
|
302
|
+
*/
|
|
303
|
+
async function detectBranchStrategyAsync() {
|
|
304
|
+
try {
|
|
305
|
+
// Run git commands in parallel
|
|
306
|
+
const [localResult, remoteResult] = await Promise.all([
|
|
307
|
+
execAsync('git branch', { encoding: 'utf8' }).catch(() => ({ stdout: '' })),
|
|
308
|
+
execAsync('git branch -r', { encoding: 'utf8' }).catch(() => ({ stdout: '' }))
|
|
309
|
+
]);
|
|
310
|
+
|
|
311
|
+
const allBranches = (localResult.stdout || '') + (remoteResult.stdout || '');
|
|
312
|
+
|
|
313
|
+
const hasStable = allBranches.includes('stable');
|
|
314
|
+
const hasProduction = allBranches.includes('production') || allBranches.includes('prod');
|
|
315
|
+
|
|
316
|
+
if (hasStable || hasProduction) {
|
|
317
|
+
return 'multi-branch';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Check deployment configs for multi-environment setup (uses cache)
|
|
321
|
+
if (await existsCachedAsync('railway.json')) {
|
|
322
|
+
try {
|
|
323
|
+
const content = await readFileCachedAsync('railway.json');
|
|
324
|
+
if (content) {
|
|
325
|
+
const config = JSON.parse(content);
|
|
326
|
+
if (config &&
|
|
327
|
+
typeof config === 'object' &&
|
|
328
|
+
typeof config.environments === 'object' &&
|
|
329
|
+
config.environments !== null &&
|
|
330
|
+
Object.keys(config.environments).length > 1) {
|
|
331
|
+
return 'multi-branch';
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
} catch {}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return 'single-branch';
|
|
338
|
+
} catch {
|
|
339
|
+
return 'single-branch';
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Detects the main branch name
|
|
345
|
+
* @returns {string} Main branch name ('main' or 'master')
|
|
346
|
+
*/
|
|
347
|
+
function detectMainBranch() {
|
|
348
|
+
try {
|
|
349
|
+
const defaultBranch = execSync('git symbolic-ref refs/remotes/origin/HEAD', {
|
|
350
|
+
encoding: 'utf8',
|
|
351
|
+
stdio: ['pipe', 'pipe', 'ignore']
|
|
352
|
+
})
|
|
353
|
+
.trim()
|
|
354
|
+
.replace('refs/remotes/origin/', '');
|
|
355
|
+
return defaultBranch;
|
|
356
|
+
} catch {
|
|
357
|
+
// Fallback: check common names
|
|
358
|
+
try {
|
|
359
|
+
execSync('git rev-parse --verify main', {
|
|
360
|
+
encoding: 'utf8',
|
|
361
|
+
stdio: ['pipe', 'pipe', 'ignore']
|
|
362
|
+
});
|
|
363
|
+
return 'main';
|
|
364
|
+
} catch {
|
|
365
|
+
return 'master';
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Detects the main branch name (async)
|
|
372
|
+
* @returns {Promise<string>} Main branch name ('main' or 'master')
|
|
373
|
+
*/
|
|
374
|
+
async function detectMainBranchAsync() {
|
|
375
|
+
try {
|
|
376
|
+
const { stdout } = await execAsync('git symbolic-ref refs/remotes/origin/HEAD', { encoding: 'utf8' });
|
|
377
|
+
return stdout.trim().replace('refs/remotes/origin/', '');
|
|
378
|
+
} catch {
|
|
379
|
+
// Fallback: check common names
|
|
380
|
+
try {
|
|
381
|
+
await execAsync('git rev-parse --verify main', { encoding: 'utf8' });
|
|
382
|
+
return 'main';
|
|
383
|
+
} catch {
|
|
384
|
+
return 'master';
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Main detection function - aggregates all platform information (sync)
|
|
391
|
+
* Uses caching to avoid repeated filesystem/git operations
|
|
392
|
+
* @param {boolean} forceRefresh - Force cache refresh
|
|
393
|
+
* @returns {Object} Platform configuration object
|
|
394
|
+
*/
|
|
395
|
+
function detect(forceRefresh = false) {
|
|
396
|
+
const now = Date.now();
|
|
397
|
+
|
|
398
|
+
// Return cached result if still valid
|
|
399
|
+
if (!forceRefresh && _cachedDetection && now < _cacheExpiry) {
|
|
400
|
+
return _cachedDetection;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
_cachedDetection = {
|
|
404
|
+
ci: detectCI(),
|
|
405
|
+
deployment: detectDeployment(),
|
|
406
|
+
projectType: detectProjectType(),
|
|
407
|
+
packageManager: detectPackageManager(),
|
|
408
|
+
branchStrategy: detectBranchStrategy(),
|
|
409
|
+
mainBranch: detectMainBranch(),
|
|
410
|
+
hasPlanFile: existsCached('PLAN.md'),
|
|
411
|
+
hasTechDebtFile: existsCached('TECHNICAL_DEBT.md'),
|
|
412
|
+
timestamp: new Date(now).toISOString()
|
|
413
|
+
};
|
|
414
|
+
_cacheExpiry = now + CACHE_TTL_MS;
|
|
415
|
+
|
|
416
|
+
return _cachedDetection;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Main detection function - aggregates all platform information (async)
|
|
421
|
+
* Uses Promise.all for parallel execution and caching
|
|
422
|
+
* @param {boolean} forceRefresh - Force cache refresh
|
|
423
|
+
* @returns {Promise<Object>} Platform configuration object
|
|
424
|
+
*/
|
|
425
|
+
async function detectAsync(forceRefresh = false) {
|
|
426
|
+
const now = Date.now();
|
|
427
|
+
|
|
428
|
+
// Return cached result if still valid
|
|
429
|
+
if (!forceRefresh && _cachedDetection && now < _cacheExpiry) {
|
|
430
|
+
return _cachedDetection;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Run all detections in parallel
|
|
434
|
+
const [
|
|
435
|
+
ci,
|
|
436
|
+
deployment,
|
|
437
|
+
projectType,
|
|
438
|
+
packageManager,
|
|
439
|
+
branchStrategy,
|
|
440
|
+
mainBranch,
|
|
441
|
+
hasPlanFile,
|
|
442
|
+
hasTechDebtFile
|
|
443
|
+
] = await Promise.all([
|
|
444
|
+
detectCIAsync(),
|
|
445
|
+
detectDeploymentAsync(),
|
|
446
|
+
detectProjectTypeAsync(),
|
|
447
|
+
detectPackageManagerAsync(),
|
|
448
|
+
detectBranchStrategyAsync(),
|
|
449
|
+
detectMainBranchAsync(),
|
|
450
|
+
existsCachedAsync('PLAN.md'),
|
|
451
|
+
existsCachedAsync('TECHNICAL_DEBT.md')
|
|
452
|
+
]);
|
|
453
|
+
|
|
454
|
+
_cachedDetection = {
|
|
455
|
+
ci,
|
|
456
|
+
deployment,
|
|
457
|
+
projectType,
|
|
458
|
+
packageManager,
|
|
459
|
+
branchStrategy,
|
|
460
|
+
mainBranch,
|
|
461
|
+
hasPlanFile,
|
|
462
|
+
hasTechDebtFile,
|
|
463
|
+
timestamp: new Date(now).toISOString()
|
|
464
|
+
};
|
|
465
|
+
_cacheExpiry = now + CACHE_TTL_MS;
|
|
466
|
+
|
|
467
|
+
return _cachedDetection;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Invalidate the detection cache
|
|
472
|
+
* Call this after making changes that affect platform detection
|
|
473
|
+
*/
|
|
474
|
+
function invalidateCache() {
|
|
475
|
+
_cachedDetection = null;
|
|
476
|
+
_cacheExpiry = 0;
|
|
477
|
+
_fileCache.clear();
|
|
478
|
+
_existsCache.clear();
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// When run directly, output JSON (uses async for better performance)
|
|
482
|
+
if (require.main === module) {
|
|
483
|
+
(async () => {
|
|
484
|
+
try {
|
|
485
|
+
const result = await detectAsync();
|
|
486
|
+
console.log(JSON.stringify(result, null, 2));
|
|
487
|
+
} catch (error) {
|
|
488
|
+
console.error(JSON.stringify({
|
|
489
|
+
error: error.message,
|
|
490
|
+
timestamp: new Date().toISOString()
|
|
491
|
+
}, null, 2));
|
|
492
|
+
process.exit(1);
|
|
493
|
+
}
|
|
494
|
+
})();
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Export for use as module
|
|
498
|
+
module.exports = {
|
|
499
|
+
detect,
|
|
500
|
+
detectAsync,
|
|
501
|
+
invalidateCache,
|
|
502
|
+
detectCI,
|
|
503
|
+
detectCIAsync,
|
|
504
|
+
detectDeployment,
|
|
505
|
+
detectDeploymentAsync,
|
|
506
|
+
detectProjectType,
|
|
507
|
+
detectProjectTypeAsync,
|
|
508
|
+
detectPackageManager,
|
|
509
|
+
detectPackageManagerAsync,
|
|
510
|
+
detectBranchStrategy,
|
|
511
|
+
detectBranchStrategyAsync,
|
|
512
|
+
detectMainBranch,
|
|
513
|
+
detectMainBranchAsync
|
|
514
|
+
};
|