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.
Files changed (95) hide show
  1. package/.claude-plugin/marketplace.json +54 -0
  2. package/.claude-plugin/plugin.json +11 -0
  3. package/.mcp.json +8 -0
  4. package/CHANGELOG.md +261 -0
  5. package/LICENSE +21 -0
  6. package/README.md +363 -0
  7. package/SECURITY.md +101 -0
  8. package/adapters/README.md +256 -0
  9. package/adapters/codex/README.md +272 -0
  10. package/adapters/codex/install.sh +179 -0
  11. package/adapters/opencode/README.md +301 -0
  12. package/adapters/opencode/install.sh +223 -0
  13. package/lib/patterns/review-patterns.js +511 -0
  14. package/lib/patterns/slop-patterns.js +647 -0
  15. package/lib/platform/detect-platform.js +535 -0
  16. package/lib/platform/verify-tools.js +235 -0
  17. package/lib/state/workflow-state.js +635 -0
  18. package/lib/state/workflow-state.schema.json +282 -0
  19. package/lib/utils/context-optimizer.js +227 -0
  20. package/mcp-server/index.js +303 -0
  21. package/mcp-server/package.json +23 -0
  22. package/package.json +63 -0
  23. package/plugins/deslop-around/.claude-plugin/plugin.json +20 -0
  24. package/plugins/deslop-around/commands/deslop-around.md +220 -0
  25. package/plugins/deslop-around/lib/patterns/review-patterns.js +511 -0
  26. package/plugins/deslop-around/lib/patterns/slop-patterns.js +641 -0
  27. package/plugins/deslop-around/lib/platform/detect-platform.js +514 -0
  28. package/plugins/deslop-around/lib/platform/verify-tools.js +235 -0
  29. package/plugins/deslop-around/lib/state/workflow-state.js +635 -0
  30. package/plugins/deslop-around/lib/state/workflow-state.schema.json +282 -0
  31. package/plugins/deslop-around/lib/utils/context-optimizer.js +222 -0
  32. package/plugins/next-task/.claude-plugin/plugin.json +24 -0
  33. package/plugins/next-task/agents/ci-fixer.md +236 -0
  34. package/plugins/next-task/agents/ci-monitor.md +291 -0
  35. package/plugins/next-task/agents/delivery-validator.md +451 -0
  36. package/plugins/next-task/agents/deslop-work.md +272 -0
  37. package/plugins/next-task/agents/docs-updater.md +506 -0
  38. package/plugins/next-task/agents/exploration-agent.md +277 -0
  39. package/plugins/next-task/agents/implementation-agent.md +427 -0
  40. package/plugins/next-task/agents/planning-agent.md +236 -0
  41. package/plugins/next-task/agents/policy-selector.md +248 -0
  42. package/plugins/next-task/agents/review-orchestrator.md +521 -0
  43. package/plugins/next-task/agents/simple-fixer.md +136 -0
  44. package/plugins/next-task/agents/task-discoverer.md +357 -0
  45. package/plugins/next-task/agents/test-coverage-checker.md +447 -0
  46. package/plugins/next-task/agents/worktree-manager.md +419 -0
  47. package/plugins/next-task/commands/delivery-approval.md +331 -0
  48. package/plugins/next-task/commands/next-task.md +627 -0
  49. package/plugins/next-task/commands/update-docs-around.md +418 -0
  50. package/plugins/next-task/hooks/hooks.json +14 -0
  51. package/plugins/next-task/lib/patterns/review-patterns.js +511 -0
  52. package/plugins/next-task/lib/patterns/slop-patterns.js +641 -0
  53. package/plugins/next-task/lib/platform/detect-platform.js +514 -0
  54. package/plugins/next-task/lib/platform/verify-tools.js +235 -0
  55. package/plugins/next-task/lib/state/tasks-registry.schema.json +85 -0
  56. package/plugins/next-task/lib/state/workflow-state.js +635 -0
  57. package/plugins/next-task/lib/state/workflow-state.schema.json +282 -0
  58. package/plugins/next-task/lib/state/worktree-status.schema.json +219 -0
  59. package/plugins/next-task/lib/utils/context-optimizer.js +222 -0
  60. package/plugins/project-review/.claude-plugin/plugin.json +20 -0
  61. package/plugins/project-review/commands/project-review-agents.md +286 -0
  62. package/plugins/project-review/commands/project-review-github.md +142 -0
  63. package/plugins/project-review/commands/project-review.md +273 -0
  64. package/plugins/project-review/lib/patterns/review-patterns.js +511 -0
  65. package/plugins/project-review/lib/patterns/slop-patterns.js +641 -0
  66. package/plugins/project-review/lib/platform/detect-platform.js +514 -0
  67. package/plugins/project-review/lib/platform/verify-tools.js +235 -0
  68. package/plugins/project-review/lib/state/workflow-state.js +635 -0
  69. package/plugins/project-review/lib/state/workflow-state.schema.json +282 -0
  70. package/plugins/project-review/lib/utils/context-optimizer.js +222 -0
  71. package/plugins/reality-check/.claude-plugin/plugin.json +23 -0
  72. package/plugins/reality-check/README.md +156 -0
  73. package/plugins/reality-check/agents/code-explorer.md +353 -0
  74. package/plugins/reality-check/agents/doc-analyzer.md +337 -0
  75. package/plugins/reality-check/agents/issue-scanner.md +231 -0
  76. package/plugins/reality-check/agents/plan-synthesizer.md +479 -0
  77. package/plugins/reality-check/commands/scan.md +242 -0
  78. package/plugins/reality-check/commands/set.md +203 -0
  79. package/plugins/reality-check/lib/state/reality-check-state.js +509 -0
  80. package/plugins/reality-check/skills/reality-analysis/SKILL.md +317 -0
  81. package/plugins/ship/.claude-plugin/plugin.json +21 -0
  82. package/plugins/ship/commands/ship-ci-review-loop.md +443 -0
  83. package/plugins/ship/commands/ship-deployment.md +330 -0
  84. package/plugins/ship/commands/ship-error-handling.md +254 -0
  85. package/plugins/ship/commands/ship.md +370 -0
  86. package/plugins/ship/lib/patterns/review-patterns.js +511 -0
  87. package/plugins/ship/lib/patterns/slop-patterns.js +641 -0
  88. package/plugins/ship/lib/platform/detect-platform.js +514 -0
  89. package/plugins/ship/lib/platform/verify-tools.js +235 -0
  90. package/plugins/ship/lib/state/workflow-state.js +635 -0
  91. package/plugins/ship/lib/state/workflow-state.schema.json +282 -0
  92. package/plugins/ship/lib/utils/context-optimizer.js +222 -0
  93. package/scripts/install/claude.sh +50 -0
  94. package/scripts/install/codex.sh +181 -0
  95. 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
+ };