awesome-slash 2.4.4 → 2.5.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/marketplace.json +6 -6
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +88 -1
- package/README.md +173 -161
- package/SECURITY.md +25 -81
- package/adapters/codex/install.sh +58 -16
- package/adapters/opencode/install.sh +92 -23
- package/lib/index.js +47 -4
- package/lib/patterns/review-patterns.js +58 -11
- package/lib/patterns/slop-patterns.js +154 -147
- package/lib/platform/detect-platform.js +99 -350
- package/lib/platform/detection-configs.js +93 -0
- package/lib/platform/verify-tools.js +10 -78
- package/lib/schemas/README.md +195 -0
- package/lib/schemas/validator.js +247 -0
- package/lib/sources/custom-handler.js +199 -0
- package/lib/sources/policy-questions.js +239 -0
- package/lib/sources/source-cache.js +149 -0
- package/lib/state/workflow-state.js +363 -665
- package/lib/types/README.md +292 -0
- package/lib/types/agent-frontmatter.d.ts +134 -0
- package/lib/types/command-frontmatter.d.ts +107 -0
- package/lib/types/hook-frontmatter.d.ts +115 -0
- package/lib/types/index.d.ts +84 -0
- package/lib/types/plugin-manifest.d.ts +102 -0
- package/lib/types/skill-frontmatter.d.ts +89 -0
- package/lib/utils/cache-manager.js +154 -0
- package/lib/utils/context-optimizer.js +5 -36
- package/lib/utils/deprecation.js +37 -0
- package/lib/utils/shell-escape.js +88 -0
- package/mcp-server/index.js +513 -18
- package/package.json +6 -2
- package/plugins/deslop-around/.claude-plugin/plugin.json +1 -1
- package/plugins/deslop-around/lib/index.js +170 -0
- package/plugins/deslop-around/lib/patterns/review-patterns.js +58 -11
- package/plugins/deslop-around/lib/patterns/slop-patterns.js +170 -129
- package/plugins/deslop-around/lib/platform/detect-platform.js +212 -123
- package/plugins/deslop-around/lib/platform/detection-configs.js +93 -0
- package/plugins/deslop-around/lib/platform/verify-tools.js +10 -1
- package/plugins/deslop-around/lib/schemas/README.md +195 -0
- package/plugins/deslop-around/lib/schemas/validator.js +205 -0
- package/plugins/deslop-around/lib/sources/custom-handler.js +199 -0
- package/plugins/deslop-around/lib/sources/policy-questions.js +239 -0
- package/plugins/deslop-around/lib/sources/source-cache.js +149 -0
- package/plugins/deslop-around/lib/state/workflow-state.js +382 -484
- package/plugins/deslop-around/lib/types/README.md +292 -0
- package/plugins/deslop-around/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/deslop-around/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/deslop-around/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/deslop-around/lib/types/index.d.ts +84 -0
- package/plugins/deslop-around/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/deslop-around/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/deslop-around/lib/utils/cache-manager.js +154 -0
- package/plugins/deslop-around/lib/utils/context-optimizer.js +115 -37
- package/plugins/deslop-around/lib/utils/deprecation.js +37 -0
- package/plugins/deslop-around/lib/utils/shell-escape.js +88 -0
- package/plugins/next-task/.claude-plugin/plugin.json +1 -1
- package/plugins/next-task/agents/delivery-validator.md +2 -2
- package/plugins/next-task/agents/implementation-agent.md +3 -4
- package/plugins/next-task/agents/planning-agent.md +77 -19
- package/plugins/next-task/agents/review-orchestrator.md +21 -122
- package/plugins/next-task/agents/task-discoverer.md +164 -23
- package/plugins/next-task/commands/next-task.md +180 -14
- package/plugins/next-task/lib/index.js +170 -0
- package/plugins/next-task/lib/patterns/review-patterns.js +58 -11
- package/plugins/next-task/lib/patterns/slop-patterns.js +170 -129
- package/plugins/next-task/lib/platform/detect-platform.js +212 -123
- package/plugins/next-task/lib/platform/detection-configs.js +93 -0
- package/plugins/next-task/lib/platform/verify-tools.js +10 -1
- package/plugins/next-task/lib/schemas/README.md +195 -0
- package/plugins/next-task/lib/schemas/validator.js +205 -0
- package/plugins/next-task/lib/sources/custom-handler.js +199 -0
- package/plugins/next-task/lib/sources/policy-questions.js +239 -0
- package/plugins/next-task/lib/sources/source-cache.js +149 -0
- package/plugins/next-task/lib/state/workflow-state.js +382 -484
- package/plugins/next-task/lib/types/README.md +292 -0
- package/plugins/next-task/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/next-task/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/next-task/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/next-task/lib/types/index.d.ts +84 -0
- package/plugins/next-task/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/next-task/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/next-task/lib/utils/cache-manager.js +154 -0
- package/plugins/next-task/lib/utils/context-optimizer.js +115 -37
- package/plugins/next-task/lib/utils/deprecation.js +37 -0
- package/plugins/next-task/lib/utils/shell-escape.js +88 -0
- package/plugins/project-review/.claude-plugin/plugin.json +1 -1
- package/plugins/project-review/lib/index.js +170 -0
- package/plugins/project-review/lib/patterns/review-patterns.js +58 -11
- package/plugins/project-review/lib/patterns/slop-patterns.js +170 -129
- package/plugins/project-review/lib/platform/detect-platform.js +212 -123
- package/plugins/project-review/lib/platform/detection-configs.js +93 -0
- package/plugins/project-review/lib/platform/verify-tools.js +10 -1
- package/plugins/project-review/lib/schemas/README.md +195 -0
- package/plugins/project-review/lib/schemas/validator.js +205 -0
- package/plugins/project-review/lib/sources/custom-handler.js +199 -0
- package/plugins/project-review/lib/sources/policy-questions.js +239 -0
- package/plugins/project-review/lib/sources/source-cache.js +149 -0
- package/plugins/project-review/lib/state/workflow-state.js +382 -484
- package/plugins/project-review/lib/types/README.md +292 -0
- package/plugins/project-review/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/project-review/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/project-review/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/project-review/lib/types/index.d.ts +84 -0
- package/plugins/project-review/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/project-review/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/project-review/lib/utils/cache-manager.js +154 -0
- package/plugins/project-review/lib/utils/context-optimizer.js +115 -37
- package/plugins/project-review/lib/utils/deprecation.js +37 -0
- package/plugins/project-review/lib/utils/shell-escape.js +88 -0
- package/plugins/reality-check/.claude-plugin/plugin.json +1 -1
- package/plugins/reality-check/agents/code-explorer.md +1 -1
- package/plugins/ship/.claude-plugin/plugin.json +1 -1
- package/plugins/ship/lib/index.js +170 -0
- package/plugins/ship/lib/patterns/review-patterns.js +58 -11
- package/plugins/ship/lib/patterns/slop-patterns.js +170 -129
- package/plugins/ship/lib/platform/detect-platform.js +212 -123
- package/plugins/ship/lib/platform/detection-configs.js +93 -0
- package/plugins/ship/lib/platform/verify-tools.js +10 -1
- package/plugins/ship/lib/schemas/README.md +195 -0
- package/plugins/ship/lib/schemas/validator.js +205 -0
- package/plugins/ship/lib/sources/custom-handler.js +199 -0
- package/plugins/ship/lib/sources/policy-questions.js +239 -0
- package/plugins/ship/lib/sources/source-cache.js +149 -0
- package/plugins/ship/lib/state/workflow-state.js +382 -484
- package/plugins/ship/lib/types/README.md +292 -0
- package/plugins/ship/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/ship/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/ship/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/ship/lib/types/index.d.ts +84 -0
- package/plugins/ship/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/ship/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/ship/lib/utils/cache-manager.js +154 -0
- package/plugins/ship/lib/utils/context-optimizer.js +115 -37
- package/plugins/ship/lib/utils/deprecation.js +37 -0
- package/plugins/ship/lib/utils/shell-escape.js +88 -0
- package/lib/state/workflow-state.schema.json +0 -282
- package/plugins/deslop-around/lib/state/workflow-state.schema.json +0 -282
- package/plugins/next-task/agents/policy-selector.md +0 -248
- package/plugins/next-task/lib/state/tasks-registry.schema.json +0 -85
- package/plugins/next-task/lib/state/workflow-state.schema.json +0 -282
- package/plugins/next-task/lib/state/worktree-status.schema.json +0 -219
- package/plugins/project-review/lib/state/workflow-state.schema.json +0 -282
- package/plugins/ship/lib/state/workflow-state.schema.json +0 -282
|
@@ -11,12 +11,21 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
const fs = require('fs');
|
|
14
|
-
const
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { exec } = require('child_process');
|
|
15
16
|
const { promisify } = require('util');
|
|
16
17
|
|
|
17
18
|
const execAsync = promisify(exec);
|
|
18
19
|
const fsPromises = fs.promises;
|
|
19
20
|
|
|
21
|
+
// Import shared utilities
|
|
22
|
+
const { CacheManager } = require('../utils/cache-manager');
|
|
23
|
+
const {
|
|
24
|
+
CI_CONFIGS,
|
|
25
|
+
DEPLOYMENT_CONFIGS,
|
|
26
|
+
PACKAGE_MANAGER_CONFIGS
|
|
27
|
+
} = require('./detection-configs');
|
|
28
|
+
|
|
20
29
|
/**
|
|
21
30
|
* Default timeout for async operations (5 seconds)
|
|
22
31
|
*/
|
|
@@ -38,7 +47,6 @@ function safeJSONParse(content, filename = 'unknown') {
|
|
|
38
47
|
return null;
|
|
39
48
|
}
|
|
40
49
|
if (content.length > MAX_JSON_SIZE_BYTES) {
|
|
41
|
-
// File too large - skip parsing to prevent DoS
|
|
42
50
|
return null;
|
|
43
51
|
}
|
|
44
52
|
try {
|
|
@@ -75,69 +83,53 @@ function withTimeout(promise, timeoutMs = DEFAULT_ASYNC_TIMEOUT_MS, operation =
|
|
|
75
83
|
* @param {number} timeoutMs - Timeout in milliseconds
|
|
76
84
|
* @returns {Promise<{stdout: string, stderr: string}>}
|
|
77
85
|
*/
|
|
78
|
-
async function
|
|
86
|
+
async function execWithTimeout(cmd, options = {}, timeoutMs = DEFAULT_ASYNC_TIMEOUT_MS) {
|
|
79
87
|
return withTimeout(execAsync(cmd, options), timeoutMs, `exec: ${cmd.substring(0, 50)}`);
|
|
80
88
|
}
|
|
81
89
|
|
|
82
|
-
//
|
|
83
|
-
let _cachedDetection = null;
|
|
84
|
-
let _cacheExpiry = 0;
|
|
85
|
-
const CACHE_TTL_MS = 60000; // 1 minute cache
|
|
86
|
-
|
|
87
|
-
// File read cache to avoid reading the same file multiple times (#17)
|
|
88
|
-
// Limited to prevent unbounded memory growth in long-running processes
|
|
89
|
-
const MAX_CACHE_SIZE = 100;
|
|
90
|
+
// Maximum cached file size constant
|
|
90
91
|
const MAX_CACHED_FILE_SIZE = 64 * 1024; // 64KB max per cached file
|
|
91
|
-
const _fileCache = new Map();
|
|
92
|
-
const _existsCache = new Map();
|
|
93
92
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
* @param {number} maxSize - Maximum entries
|
|
99
|
-
*/
|
|
100
|
-
function enforceMaxCacheSize(cache, maxSize = MAX_CACHE_SIZE) {
|
|
101
|
-
// O(1) eviction: delete oldest entries one at a time
|
|
102
|
-
// Map maintains insertion order, so first key is oldest
|
|
103
|
-
while (cache.size > maxSize) {
|
|
104
|
-
const firstKey = cache.keys().next().value;
|
|
105
|
-
cache.delete(firstKey);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
93
|
+
// Cache instances using CacheManager abstraction
|
|
94
|
+
const _detectionCache = new CacheManager({ maxSize: 1, ttl: 60000 });
|
|
95
|
+
const _fileCache = new CacheManager({ maxSize: 100, ttl: 60000, maxValueSize: MAX_CACHED_FILE_SIZE });
|
|
96
|
+
const _existsCache = new CacheManager({ maxSize: 100, ttl: 60000 });
|
|
108
97
|
|
|
109
98
|
/**
|
|
110
|
-
*
|
|
111
|
-
* @param {
|
|
112
|
-
* @
|
|
99
|
+
* Generic file-based detector
|
|
100
|
+
* @param {Array} configs - Array of {file, platform} objects
|
|
101
|
+
* @param {Function} existsChecker - Async function to check file existence
|
|
102
|
+
* @returns {Promise<string|null>} Detected platform or null
|
|
113
103
|
*/
|
|
114
|
-
function
|
|
115
|
-
|
|
116
|
-
|
|
104
|
+
async function detectFromFiles(configs, existsChecker) {
|
|
105
|
+
const checks = await Promise.all(
|
|
106
|
+
configs.map(({ file }) => existsChecker(file))
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
for (let i = 0; i < checks.length; i++) {
|
|
110
|
+
if (checks[i]) {
|
|
111
|
+
return configs[i].platform;
|
|
112
|
+
}
|
|
117
113
|
}
|
|
118
|
-
|
|
119
|
-
_existsCache.set(filepath, exists);
|
|
120
|
-
enforceMaxCacheSize(_existsCache);
|
|
121
|
-
return exists;
|
|
114
|
+
return null;
|
|
122
115
|
}
|
|
123
116
|
|
|
124
117
|
/**
|
|
125
|
-
* Check if a file exists (cached
|
|
118
|
+
* Check if a file exists (cached)
|
|
126
119
|
* @param {string} filepath - Path to check
|
|
127
120
|
* @returns {Promise<boolean>}
|
|
128
121
|
*/
|
|
129
|
-
async function
|
|
130
|
-
|
|
131
|
-
|
|
122
|
+
async function existsCached(filepath) {
|
|
123
|
+
const cached = _existsCache.get(filepath);
|
|
124
|
+
if (cached !== undefined) {
|
|
125
|
+
return cached;
|
|
132
126
|
}
|
|
133
127
|
try {
|
|
134
128
|
await fsPromises.access(filepath);
|
|
135
129
|
_existsCache.set(filepath, true);
|
|
136
|
-
enforceMaxCacheSize(_existsCache);
|
|
137
130
|
return true;
|
|
138
131
|
} catch {
|
|
139
132
|
_existsCache.set(filepath, false);
|
|
140
|
-
enforceMaxCacheSize(_existsCache);
|
|
141
133
|
return false;
|
|
142
134
|
}
|
|
143
135
|
}
|
|
@@ -145,155 +137,57 @@ async function existsCachedAsync(filepath) {
|
|
|
145
137
|
/**
|
|
146
138
|
* Read file contents (cached)
|
|
147
139
|
* Only caches files smaller than MAX_CACHED_FILE_SIZE to prevent memory bloat
|
|
148
|
-
*
|
|
149
|
-
* @returns {string|null}
|
|
150
|
-
*/
|
|
151
|
-
function readFileCached(filepath) {
|
|
152
|
-
if (_fileCache.has(filepath)) {
|
|
153
|
-
return _fileCache.get(filepath);
|
|
154
|
-
}
|
|
155
|
-
try {
|
|
156
|
-
const content = fs.readFileSync(filepath, 'utf8');
|
|
157
|
-
// Only cache small files to prevent memory bloat
|
|
158
|
-
if (content.length <= MAX_CACHED_FILE_SIZE) {
|
|
159
|
-
_fileCache.set(filepath, content);
|
|
160
|
-
enforceMaxCacheSize(_fileCache);
|
|
161
|
-
}
|
|
162
|
-
return content;
|
|
163
|
-
} catch {
|
|
164
|
-
// Cache null for missing files (small memory footprint)
|
|
165
|
-
_fileCache.set(filepath, null);
|
|
166
|
-
enforceMaxCacheSize(_fileCache);
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Read file contents (cached, async)
|
|
173
|
-
* Only caches files smaller than MAX_CACHED_FILE_SIZE to prevent memory bloat
|
|
140
|
+
* Optimized: normalizes filepath to prevent cache pollution from variant paths
|
|
174
141
|
* @param {string} filepath - Path to read
|
|
175
142
|
* @returns {Promise<string|null>}
|
|
176
143
|
*/
|
|
177
|
-
async function
|
|
178
|
-
|
|
179
|
-
|
|
144
|
+
async function readFileCached(filepath) {
|
|
145
|
+
const normalizedPath = path.resolve(filepath);
|
|
146
|
+
|
|
147
|
+
const cached = _fileCache.get(normalizedPath);
|
|
148
|
+
if (cached !== undefined) {
|
|
149
|
+
return cached;
|
|
180
150
|
}
|
|
181
151
|
try {
|
|
182
|
-
const content = await fsPromises.readFile(
|
|
183
|
-
|
|
184
|
-
if (content.length <= MAX_CACHED_FILE_SIZE) {
|
|
185
|
-
_fileCache.set(filepath, content);
|
|
186
|
-
enforceMaxCacheSize(_fileCache);
|
|
187
|
-
}
|
|
152
|
+
const content = await fsPromises.readFile(normalizedPath, 'utf8');
|
|
153
|
+
_fileCache.set(normalizedPath, content);
|
|
188
154
|
return content;
|
|
189
155
|
} catch {
|
|
190
|
-
|
|
191
|
-
_fileCache.set(filepath, null);
|
|
192
|
-
enforceMaxCacheSize(_fileCache);
|
|
156
|
+
_fileCache.set(normalizedPath, null);
|
|
193
157
|
return null;
|
|
194
158
|
}
|
|
195
159
|
}
|
|
196
160
|
|
|
197
161
|
/**
|
|
198
162
|
* Detects CI platform by scanning for configuration files
|
|
199
|
-
* @returns {string|null} CI platform name or null if not detected
|
|
200
|
-
*/
|
|
201
|
-
function detectCI() {
|
|
202
|
-
if (existsCached('.github/workflows')) return 'github-actions';
|
|
203
|
-
if (existsCached('.gitlab-ci.yml')) return 'gitlab-ci';
|
|
204
|
-
if (existsCached('.circleci/config.yml')) return 'circleci';
|
|
205
|
-
if (existsCached('Jenkinsfile')) return 'jenkins';
|
|
206
|
-
if (existsCached('.travis.yml')) return 'travis';
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Detects CI platform by scanning for configuration files (async)
|
|
212
163
|
* @returns {Promise<string|null>} CI platform name or null if not detected
|
|
213
164
|
*/
|
|
214
|
-
async function
|
|
215
|
-
|
|
216
|
-
existsCachedAsync('.github/workflows'),
|
|
217
|
-
existsCachedAsync('.gitlab-ci.yml'),
|
|
218
|
-
existsCachedAsync('.circleci/config.yml'),
|
|
219
|
-
existsCachedAsync('Jenkinsfile'),
|
|
220
|
-
existsCachedAsync('.travis.yml')
|
|
221
|
-
]);
|
|
222
|
-
|
|
223
|
-
if (checks[0]) return 'github-actions';
|
|
224
|
-
if (checks[1]) return 'gitlab-ci';
|
|
225
|
-
if (checks[2]) return 'circleci';
|
|
226
|
-
if (checks[3]) return 'jenkins';
|
|
227
|
-
if (checks[4]) return 'travis';
|
|
228
|
-
return null;
|
|
165
|
+
async function detectCI() {
|
|
166
|
+
return detectFromFiles(CI_CONFIGS, existsCached);
|
|
229
167
|
}
|
|
230
168
|
|
|
231
169
|
/**
|
|
232
170
|
* Detects deployment platform by scanning for platform-specific files
|
|
233
|
-
* @returns {string|null} Deployment platform name or null if not detected
|
|
234
|
-
*/
|
|
235
|
-
function detectDeployment() {
|
|
236
|
-
if (existsCached('railway.json') || existsCached('railway.toml')) return 'railway';
|
|
237
|
-
if (existsCached('vercel.json')) return 'vercel';
|
|
238
|
-
if (existsCached('netlify.toml') || existsCached('.netlify')) return 'netlify';
|
|
239
|
-
if (existsCached('fly.toml')) return 'fly';
|
|
240
|
-
if (existsCached('.platform.sh')) return 'platform-sh';
|
|
241
|
-
if (existsCached('render.yaml')) return 'render';
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Detects deployment platform by scanning for platform-specific files (async)
|
|
247
171
|
* @returns {Promise<string|null>} Deployment platform name or null if not detected
|
|
248
172
|
*/
|
|
249
|
-
async function
|
|
250
|
-
|
|
251
|
-
existsCachedAsync('railway.json'),
|
|
252
|
-
existsCachedAsync('railway.toml'),
|
|
253
|
-
existsCachedAsync('vercel.json'),
|
|
254
|
-
existsCachedAsync('netlify.toml'),
|
|
255
|
-
existsCachedAsync('.netlify'),
|
|
256
|
-
existsCachedAsync('fly.toml'),
|
|
257
|
-
existsCachedAsync('.platform.sh'),
|
|
258
|
-
existsCachedAsync('render.yaml')
|
|
259
|
-
]);
|
|
260
|
-
|
|
261
|
-
if (checks[0] || checks[1]) return 'railway';
|
|
262
|
-
if (checks[2]) return 'vercel';
|
|
263
|
-
if (checks[3] || checks[4]) return 'netlify';
|
|
264
|
-
if (checks[5]) return 'fly';
|
|
265
|
-
if (checks[6]) return 'platform-sh';
|
|
266
|
-
if (checks[7]) return 'render';
|
|
267
|
-
return null;
|
|
173
|
+
async function detectDeployment() {
|
|
174
|
+
return detectFromFiles(DEPLOYMENT_CONFIGS, existsCached);
|
|
268
175
|
}
|
|
269
176
|
|
|
270
177
|
/**
|
|
271
178
|
* Detects project type by scanning for language-specific files
|
|
272
|
-
* @returns {string} Project type identifier
|
|
273
|
-
*/
|
|
274
|
-
function detectProjectType() {
|
|
275
|
-
if (existsCached('package.json')) return 'nodejs';
|
|
276
|
-
if (existsCached('requirements.txt') || existsCached('pyproject.toml') || existsCached('setup.py')) return 'python';
|
|
277
|
-
if (existsCached('Cargo.toml')) return 'rust';
|
|
278
|
-
if (existsCached('go.mod')) return 'go';
|
|
279
|
-
if (existsCached('pom.xml') || existsCached('build.gradle')) return 'java';
|
|
280
|
-
return 'unknown';
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Detects project type by scanning for language-specific files (async)
|
|
285
179
|
* @returns {Promise<string>} Project type identifier
|
|
286
180
|
*/
|
|
287
|
-
async function
|
|
181
|
+
async function detectProjectType() {
|
|
288
182
|
const checks = await Promise.all([
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
183
|
+
existsCached('package.json'),
|
|
184
|
+
existsCached('requirements.txt'),
|
|
185
|
+
existsCached('pyproject.toml'),
|
|
186
|
+
existsCached('setup.py'),
|
|
187
|
+
existsCached('Cargo.toml'),
|
|
188
|
+
existsCached('go.mod'),
|
|
189
|
+
existsCached('pom.xml'),
|
|
190
|
+
existsCached('build.gradle')
|
|
297
191
|
]);
|
|
298
192
|
|
|
299
193
|
if (checks[0]) return 'nodejs';
|
|
@@ -306,103 +200,24 @@ async function detectProjectTypeAsync() {
|
|
|
306
200
|
|
|
307
201
|
/**
|
|
308
202
|
* Detects package manager by scanning for lockfiles
|
|
309
|
-
* @returns {string|null} Package manager name or null if not detected
|
|
310
|
-
*/
|
|
311
|
-
function detectPackageManager() {
|
|
312
|
-
if (existsCached('pnpm-lock.yaml')) return 'pnpm';
|
|
313
|
-
if (existsCached('yarn.lock')) return 'yarn';
|
|
314
|
-
if (existsCached('bun.lockb')) return 'bun';
|
|
315
|
-
if (existsCached('package-lock.json')) return 'npm';
|
|
316
|
-
if (existsCached('poetry.lock')) return 'poetry';
|
|
317
|
-
if (existsCached('Pipfile.lock')) return 'pipenv';
|
|
318
|
-
if (existsCached('Cargo.lock')) return 'cargo';
|
|
319
|
-
if (existsCached('go.sum')) return 'go';
|
|
320
|
-
return null;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Detects package manager by scanning for lockfiles (async)
|
|
325
203
|
* @returns {Promise<string|null>} Package manager name or null if not detected
|
|
326
204
|
*/
|
|
327
|
-
async function
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
existsCachedAsync('package-lock.json'),
|
|
333
|
-
existsCachedAsync('poetry.lock'),
|
|
334
|
-
existsCachedAsync('Pipfile.lock'),
|
|
335
|
-
existsCachedAsync('Cargo.lock'),
|
|
336
|
-
existsCachedAsync('go.sum')
|
|
337
|
-
]);
|
|
338
|
-
|
|
339
|
-
if (checks[0]) return 'pnpm';
|
|
340
|
-
if (checks[1]) return 'yarn';
|
|
341
|
-
if (checks[2]) return 'bun';
|
|
342
|
-
if (checks[3]) return 'npm';
|
|
343
|
-
if (checks[4]) return 'poetry';
|
|
344
|
-
if (checks[5]) return 'pipenv';
|
|
345
|
-
if (checks[6]) return 'cargo';
|
|
346
|
-
if (checks[7]) return 'go';
|
|
347
|
-
return null;
|
|
205
|
+
async function detectPackageManager() {
|
|
206
|
+
return detectFromFiles(
|
|
207
|
+
PACKAGE_MANAGER_CONFIGS.map(({ file, manager }) => ({ file, platform: manager })),
|
|
208
|
+
existsCached
|
|
209
|
+
);
|
|
348
210
|
}
|
|
349
211
|
|
|
350
212
|
/**
|
|
351
213
|
* Detects branch strategy (single-branch vs multi-branch with dev+prod)
|
|
352
|
-
* @returns {string} 'single-branch' or 'multi-branch'
|
|
353
|
-
*/
|
|
354
|
-
function detectBranchStrategy() {
|
|
355
|
-
try {
|
|
356
|
-
// Check both local and remote branches
|
|
357
|
-
const localBranches = execSync('git branch', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
|
|
358
|
-
let remoteBranches = '';
|
|
359
|
-
try {
|
|
360
|
-
remoteBranches = execSync('git branch -r', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
|
|
361
|
-
} catch {}
|
|
362
|
-
|
|
363
|
-
const allBranches = localBranches + remoteBranches;
|
|
364
|
-
|
|
365
|
-
const hasStable = allBranches.includes('stable');
|
|
366
|
-
const hasProduction = allBranches.includes('production') || allBranches.includes('prod');
|
|
367
|
-
|
|
368
|
-
if (hasStable || hasProduction) {
|
|
369
|
-
return 'multi-branch'; // dev + prod workflow
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// Check deployment configs for multi-environment setup (uses cache)
|
|
373
|
-
if (existsCached('railway.json')) {
|
|
374
|
-
try {
|
|
375
|
-
const content = readFileCached('railway.json');
|
|
376
|
-
if (content) {
|
|
377
|
-
const config = safeJSONParse(content, 'railway.json');
|
|
378
|
-
// Validate JSON structure before accessing properties
|
|
379
|
-
if (config &&
|
|
380
|
-
typeof config === 'object' &&
|
|
381
|
-
typeof config.environments === 'object' &&
|
|
382
|
-
config.environments !== null &&
|
|
383
|
-
Object.keys(config.environments).length > 1) {
|
|
384
|
-
return 'multi-branch';
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
} catch {}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return 'single-branch'; // main only
|
|
391
|
-
} catch {
|
|
392
|
-
return 'single-branch';
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Detects branch strategy (single-branch vs multi-branch with dev+prod) (async)
|
|
398
214
|
* @returns {Promise<string>} 'single-branch' or 'multi-branch'
|
|
399
215
|
*/
|
|
400
|
-
async function
|
|
216
|
+
async function detectBranchStrategy() {
|
|
401
217
|
try {
|
|
402
|
-
// Run git commands in parallel with timeout protection
|
|
403
218
|
const [localResult, remoteResult] = await Promise.all([
|
|
404
|
-
|
|
405
|
-
|
|
219
|
+
execWithTimeout('git branch', { encoding: 'utf8' }).catch(() => ({ stdout: '' })),
|
|
220
|
+
execWithTimeout('git branch -r', { encoding: 'utf8' }).catch(() => ({ stdout: '' }))
|
|
406
221
|
]);
|
|
407
222
|
|
|
408
223
|
const allBranches = (localResult.stdout || '') + (remoteResult.stdout || '');
|
|
@@ -414,10 +229,9 @@ async function detectBranchStrategyAsync() {
|
|
|
414
229
|
return 'multi-branch';
|
|
415
230
|
}
|
|
416
231
|
|
|
417
|
-
|
|
418
|
-
if (await existsCachedAsync('railway.json')) {
|
|
232
|
+
if (await existsCached('railway.json')) {
|
|
419
233
|
try {
|
|
420
|
-
const content = await
|
|
234
|
+
const content = await readFileCached('railway.json');
|
|
421
235
|
if (content) {
|
|
422
236
|
const config = safeJSONParse(content, 'railway.json');
|
|
423
237
|
if (config &&
|
|
@@ -439,43 +253,15 @@ async function detectBranchStrategyAsync() {
|
|
|
439
253
|
|
|
440
254
|
/**
|
|
441
255
|
* Detects the main branch name
|
|
442
|
-
* @returns {string} Main branch name ('main' or 'master')
|
|
443
|
-
*/
|
|
444
|
-
function detectMainBranch() {
|
|
445
|
-
try {
|
|
446
|
-
const defaultBranch = execSync('git symbolic-ref refs/remotes/origin/HEAD', {
|
|
447
|
-
encoding: 'utf8',
|
|
448
|
-
stdio: ['pipe', 'pipe', 'ignore']
|
|
449
|
-
})
|
|
450
|
-
.trim()
|
|
451
|
-
.replace('refs/remotes/origin/', '');
|
|
452
|
-
return defaultBranch;
|
|
453
|
-
} catch {
|
|
454
|
-
// Fallback: check common names
|
|
455
|
-
try {
|
|
456
|
-
execSync('git rev-parse --verify main', {
|
|
457
|
-
encoding: 'utf8',
|
|
458
|
-
stdio: ['pipe', 'pipe', 'ignore']
|
|
459
|
-
});
|
|
460
|
-
return 'main';
|
|
461
|
-
} catch {
|
|
462
|
-
return 'master';
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Detects the main branch name (async)
|
|
469
256
|
* @returns {Promise<string>} Main branch name ('main' or 'master')
|
|
470
257
|
*/
|
|
471
|
-
async function
|
|
258
|
+
async function detectMainBranch() {
|
|
472
259
|
try {
|
|
473
|
-
const { stdout } = await
|
|
260
|
+
const { stdout } = await execWithTimeout('git symbolic-ref refs/remotes/origin/HEAD', { encoding: 'utf8' });
|
|
474
261
|
return stdout.trim().replace('refs/remotes/origin/', '');
|
|
475
262
|
} catch {
|
|
476
|
-
// Fallback: check common names
|
|
477
263
|
try {
|
|
478
|
-
await
|
|
264
|
+
await execWithTimeout('git rev-parse --verify main', { encoding: 'utf8' });
|
|
479
265
|
return 'main';
|
|
480
266
|
} catch {
|
|
481
267
|
return 'master';
|
|
@@ -484,50 +270,19 @@ async function detectMainBranchAsync() {
|
|
|
484
270
|
}
|
|
485
271
|
|
|
486
272
|
/**
|
|
487
|
-
* Main detection function - aggregates all platform information
|
|
488
|
-
* Uses caching to avoid repeated filesystem/git operations
|
|
489
|
-
* @param {boolean} forceRefresh - Force cache refresh
|
|
490
|
-
* @returns {Object} Platform configuration object
|
|
491
|
-
*/
|
|
492
|
-
function detect(forceRefresh = false) {
|
|
493
|
-
const now = Date.now();
|
|
494
|
-
|
|
495
|
-
// Return cached result if still valid
|
|
496
|
-
if (!forceRefresh && _cachedDetection && now < _cacheExpiry) {
|
|
497
|
-
return _cachedDetection;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
_cachedDetection = {
|
|
501
|
-
ci: detectCI(),
|
|
502
|
-
deployment: detectDeployment(),
|
|
503
|
-
projectType: detectProjectType(),
|
|
504
|
-
packageManager: detectPackageManager(),
|
|
505
|
-
branchStrategy: detectBranchStrategy(),
|
|
506
|
-
mainBranch: detectMainBranch(),
|
|
507
|
-
hasPlanFile: existsCached('PLAN.md'),
|
|
508
|
-
hasTechDebtFile: existsCached('TECHNICAL_DEBT.md'),
|
|
509
|
-
timestamp: new Date(now).toISOString()
|
|
510
|
-
};
|
|
511
|
-
_cacheExpiry = now + CACHE_TTL_MS;
|
|
512
|
-
|
|
513
|
-
return _cachedDetection;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* Main detection function - aggregates all platform information (async)
|
|
273
|
+
* Main detection function - aggregates all platform information
|
|
518
274
|
* Uses Promise.all for parallel execution and caching
|
|
519
275
|
* @param {boolean} forceRefresh - Force cache refresh
|
|
520
276
|
* @returns {Promise<Object>} Platform configuration object
|
|
521
277
|
*/
|
|
522
|
-
async function
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
278
|
+
async function detect(forceRefresh = false) {
|
|
279
|
+
if (!forceRefresh) {
|
|
280
|
+
const cached = _detectionCache.get('detection');
|
|
281
|
+
if (cached !== undefined) {
|
|
282
|
+
return cached;
|
|
283
|
+
}
|
|
528
284
|
}
|
|
529
285
|
|
|
530
|
-
// Run all detections in parallel
|
|
531
286
|
const [
|
|
532
287
|
ci,
|
|
533
288
|
deployment,
|
|
@@ -538,17 +293,17 @@ async function detectAsync(forceRefresh = false) {
|
|
|
538
293
|
hasPlanFile,
|
|
539
294
|
hasTechDebtFile
|
|
540
295
|
] = await Promise.all([
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
296
|
+
detectCI(),
|
|
297
|
+
detectDeployment(),
|
|
298
|
+
detectProjectType(),
|
|
299
|
+
detectPackageManager(),
|
|
300
|
+
detectBranchStrategy(),
|
|
301
|
+
detectMainBranch(),
|
|
302
|
+
existsCached('PLAN.md'),
|
|
303
|
+
existsCached('TECHNICAL_DEBT.md')
|
|
549
304
|
]);
|
|
550
305
|
|
|
551
|
-
|
|
306
|
+
const detection = {
|
|
552
307
|
ci,
|
|
553
308
|
deployment,
|
|
554
309
|
projectType,
|
|
@@ -557,35 +312,36 @@ async function detectAsync(forceRefresh = false) {
|
|
|
557
312
|
mainBranch,
|
|
558
313
|
hasPlanFile,
|
|
559
314
|
hasTechDebtFile,
|
|
560
|
-
timestamp: new Date(
|
|
315
|
+
timestamp: new Date().toISOString()
|
|
561
316
|
};
|
|
562
|
-
_cacheExpiry = now + CACHE_TTL_MS;
|
|
563
317
|
|
|
564
|
-
|
|
318
|
+
_detectionCache.set('detection', detection);
|
|
319
|
+
return detection;
|
|
565
320
|
}
|
|
566
321
|
|
|
567
322
|
/**
|
|
568
|
-
* Invalidate
|
|
323
|
+
* Invalidate all detection caches
|
|
569
324
|
* Call this after making changes that affect platform detection
|
|
570
325
|
*/
|
|
571
326
|
function invalidateCache() {
|
|
572
|
-
|
|
573
|
-
_cacheExpiry = 0;
|
|
327
|
+
_detectionCache.clear();
|
|
574
328
|
_fileCache.clear();
|
|
575
329
|
_existsCache.clear();
|
|
576
330
|
}
|
|
577
331
|
|
|
578
|
-
// When run directly, output JSON
|
|
332
|
+
// When run directly, output JSON
|
|
579
333
|
if (require.main === module) {
|
|
580
334
|
(async () => {
|
|
581
335
|
try {
|
|
582
|
-
const result = await
|
|
583
|
-
|
|
336
|
+
const result = await detect();
|
|
337
|
+
const indent = process.stdout.isTTY ? 2 : 0;
|
|
338
|
+
console.log(JSON.stringify(result, null, indent));
|
|
584
339
|
} catch (error) {
|
|
340
|
+
const indent = process.stderr.isTTY ? 2 : 0;
|
|
585
341
|
console.error(JSON.stringify({
|
|
586
342
|
error: error.message,
|
|
587
343
|
timestamp: new Date().toISOString()
|
|
588
|
-
}, null,
|
|
344
|
+
}, null, indent));
|
|
589
345
|
process.exit(1);
|
|
590
346
|
}
|
|
591
347
|
})();
|
|
@@ -594,18 +350,11 @@ if (require.main === module) {
|
|
|
594
350
|
// Export for use as module
|
|
595
351
|
module.exports = {
|
|
596
352
|
detect,
|
|
597
|
-
detectAsync,
|
|
598
353
|
invalidateCache,
|
|
599
354
|
detectCI,
|
|
600
|
-
detectCIAsync,
|
|
601
355
|
detectDeployment,
|
|
602
|
-
detectDeploymentAsync,
|
|
603
356
|
detectProjectType,
|
|
604
|
-
detectProjectTypeAsync,
|
|
605
357
|
detectPackageManager,
|
|
606
|
-
detectPackageManagerAsync,
|
|
607
358
|
detectBranchStrategy,
|
|
608
|
-
|
|
609
|
-
detectMainBranch,
|
|
610
|
-
detectMainBranchAsync
|
|
359
|
+
detectMainBranch
|
|
611
360
|
};
|