agentsys 5.2.0 → 5.2.1
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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +8 -0
- package/bin/cli.js +129 -49
- package/package.json +1 -1
- package/site/content.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentsys",
|
|
3
3
|
"description": "14 specialized plugins for AI workflow automation - task orchestration, PR workflow, slop detection, code review, drift detection, enhancement analysis, documentation sync, repo mapping, perf investigations, topic research, agent config linting, cross-tool AI consultation, and structured AI debate",
|
|
4
|
-
"version": "5.2.
|
|
4
|
+
"version": "5.2.1",
|
|
5
5
|
"owner": {
|
|
6
6
|
"name": "Avi Fenesh",
|
|
7
7
|
"url": "https://github.com/avifenesh"
|
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [5.2.1] - 2026-03-01
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **Installer marketplace source parsing** — Added compatibility for both legacy string `source` values and structured source objects (`{ source: "url", url: "..." }`) so installs no longer crash with `plugin.source.startsWith is not a function`.
|
|
17
|
+
- **Plugin fetch resilience and failure behavior** — Normalized `.git` repository URLs, added GitHub ref fallback order (`vX.Y.Z`, `X.Y.Z`, `main`, `master`), and fail-fast behavior when any plugin fetch fails.
|
|
18
|
+
- **Cross-platform install ordering** — Fixed install sequence so local install directory reset no longer wipes the fetched plugin cache before OpenCode/Codex installation.
|
|
19
|
+
|
|
12
20
|
## [5.2.0] - 2026-02-27
|
|
13
21
|
|
|
14
22
|
### Added
|
package/bin/cli.js
CHANGED
|
@@ -231,18 +231,60 @@ function loadMarketplace() {
|
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
/**
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
234
|
+
* Normalize marketplace plugin source entries.
|
|
235
|
+
*
|
|
236
|
+
* Supported formats:
|
|
237
|
+
* - string URL/path (legacy)
|
|
238
|
+
* - object: { source: "url", url: "..." } (current)
|
|
239
|
+
* - object: { source: "path", path: "..." } (local/bundled)
|
|
240
|
+
*
|
|
241
|
+
* @param {string|Object} source
|
|
242
|
+
* @returns {{type: 'remote'|'local', value: string}|null}
|
|
238
243
|
*/
|
|
239
|
-
function
|
|
240
|
-
if (
|
|
241
|
-
|
|
242
|
-
|
|
244
|
+
function resolvePluginSource(source) {
|
|
245
|
+
if (typeof source === 'string') {
|
|
246
|
+
const value = source.trim();
|
|
247
|
+
if (!value) return null;
|
|
248
|
+
if (value.startsWith('./') || value.startsWith('../')) {
|
|
249
|
+
return { type: 'local', value };
|
|
250
|
+
}
|
|
251
|
+
return { type: 'remote', value };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!source || typeof source !== 'object') return null;
|
|
255
|
+
|
|
256
|
+
const sourceType = typeof source.source === 'string' ? source.source.toLowerCase() : null;
|
|
257
|
+
|
|
258
|
+
if ((sourceType === 'path' || sourceType === 'local') && typeof source.path === 'string') {
|
|
259
|
+
return { type: 'local', value: source.path };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (sourceType === 'url' && typeof source.url === 'string') {
|
|
263
|
+
return { type: 'remote', value: source.url };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Backward/forward-compatible fallbacks
|
|
267
|
+
if (typeof source.path === 'string') {
|
|
268
|
+
return { type: 'local', value: source.path };
|
|
269
|
+
}
|
|
270
|
+
if (typeof source.url === 'string') {
|
|
271
|
+
return { type: 'remote', value: source.url };
|
|
272
|
+
}
|
|
273
|
+
|
|
243
274
|
return null;
|
|
244
275
|
}
|
|
245
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Backward-compatible helper returning only the source URL/path value.
|
|
279
|
+
*
|
|
280
|
+
* @param {string|Object} source
|
|
281
|
+
* @returns {string|null}
|
|
282
|
+
*/
|
|
283
|
+
function resolveSourceUrl(source) {
|
|
284
|
+
const normalized = resolvePluginSource(source);
|
|
285
|
+
return normalized ? normalized.value : null;
|
|
286
|
+
}
|
|
287
|
+
|
|
246
288
|
/**
|
|
247
289
|
* Resolve plugin dependencies transitively.
|
|
248
290
|
*
|
|
@@ -313,45 +355,72 @@ async function fetchPlugin(name, source, version) {
|
|
|
313
355
|
}
|
|
314
356
|
}
|
|
315
357
|
|
|
358
|
+
const parsedSource = parseGitHubSource(source, version, name);
|
|
359
|
+
const owner = parsedSource.owner;
|
|
360
|
+
const repo = parsedSource.repo;
|
|
361
|
+
|
|
362
|
+
const refCandidates = parsedSource.explicitRef
|
|
363
|
+
? [parsedSource.ref]
|
|
364
|
+
: [parsedSource.ref, version, 'main', 'master'];
|
|
365
|
+
|
|
366
|
+
let lastError = null;
|
|
367
|
+
for (const ref of [...new Set(refCandidates.filter(Boolean))]) {
|
|
368
|
+
const tarballUrl = `https://api.github.com/repos/${owner}/${repo}/tarball/${ref}`;
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
console.log(` Fetching ${name}@${version} from ${owner}/${repo} (${ref})...`);
|
|
372
|
+
|
|
373
|
+
// Clean and recreate
|
|
374
|
+
if (fs.existsSync(pluginDir)) {
|
|
375
|
+
fs.rmSync(pluginDir, { recursive: true, force: true });
|
|
376
|
+
}
|
|
377
|
+
fs.mkdirSync(pluginDir, { recursive: true });
|
|
378
|
+
|
|
379
|
+
// Download and extract tarball
|
|
380
|
+
await downloadAndExtractTarball(tarballUrl, pluginDir);
|
|
381
|
+
|
|
382
|
+
// Write version marker
|
|
383
|
+
fs.writeFileSync(versionFile, version);
|
|
384
|
+
return pluginDir;
|
|
385
|
+
} catch (err) {
|
|
386
|
+
lastError = err;
|
|
387
|
+
const isNotFound = /HTTP 404/.test(err.message);
|
|
388
|
+
if (isNotFound && !parsedSource.explicitRef) {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
throw err;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
throw new Error(
|
|
396
|
+
`Unable to fetch ${name} from ${owner}/${repo}. Tried refs: ${[...new Set(refCandidates.filter(Boolean))].join(', ')}. Last error: ${lastError ? lastError.message : 'unknown error'}`
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Parse GitHub source URL formats and normalize repo name.
|
|
402
|
+
*
|
|
403
|
+
* @param {string} source
|
|
404
|
+
* @param {string} version
|
|
405
|
+
* @param {string} [name]
|
|
406
|
+
* @returns {{owner: string, repo: string, ref: string, explicitRef: boolean}}
|
|
407
|
+
*/
|
|
408
|
+
function parseGitHubSource(source, version, name = 'plugin') {
|
|
316
409
|
// Parse source formats:
|
|
317
410
|
// "https://github.com/owner/repo" or "https://github.com/owner/repo#ref"
|
|
318
411
|
// "github:owner/repo" or "github:owner/repo#ref"
|
|
319
|
-
let owner, repo, ref;
|
|
320
412
|
const urlMatch = source.match(/github\.com\/([^/]+)\/([^/#]+)(?:#(.+))?/);
|
|
321
413
|
const shortMatch = !urlMatch && source.match(/^github:([^/]+)\/([^#]+)(?:#(.+))?$/);
|
|
322
414
|
const match = urlMatch || shortMatch;
|
|
323
415
|
if (!match) {
|
|
324
416
|
throw new Error(`Unsupported source format for ${name}: ${source}`);
|
|
325
417
|
}
|
|
326
|
-
owner = match[1];
|
|
327
|
-
repo = match[2].replace(/\.git$/, '');
|
|
328
|
-
ref = match[3] || `v${version}`;
|
|
329
|
-
|
|
330
|
-
console.log(` Fetching ${name}@${version} from ${owner}/${repo}...`);
|
|
331
|
-
|
|
332
|
-
// Clean and recreate
|
|
333
|
-
if (fs.existsSync(pluginDir)) {
|
|
334
|
-
fs.rmSync(pluginDir, { recursive: true, force: true });
|
|
335
|
-
}
|
|
336
|
-
fs.mkdirSync(pluginDir, { recursive: true });
|
|
337
|
-
|
|
338
|
-
// Download and extract tarball, falling back to main branch if version tag 404s
|
|
339
|
-
const tarballUrl = `https://api.github.com/repos/${owner}/${repo}/tarball/${ref}`;
|
|
340
|
-
try {
|
|
341
|
-
await downloadAndExtractTarball(tarballUrl, pluginDir);
|
|
342
|
-
} catch (err) {
|
|
343
|
-
if (ref !== 'main' && err.message && err.message.includes('404')) {
|
|
344
|
-
const mainUrl = `https://api.github.com/repos/${owner}/${repo}/tarball/main`;
|
|
345
|
-
await downloadAndExtractTarball(mainUrl, pluginDir);
|
|
346
|
-
} else {
|
|
347
|
-
throw err;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
418
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
419
|
+
const owner = match[1];
|
|
420
|
+
const repo = match[2].replace(/\.git$/, '');
|
|
421
|
+
const explicitRef = Boolean(match[3]);
|
|
422
|
+
const ref = match[3] || `v${version}`;
|
|
423
|
+
return { owner, repo, ref, explicitRef };
|
|
355
424
|
}
|
|
356
425
|
|
|
357
426
|
/**
|
|
@@ -470,16 +539,17 @@ async function fetchExternalPlugins(pluginNames, marketplace) {
|
|
|
470
539
|
const plugin = pluginMap[name];
|
|
471
540
|
if (!plugin) continue;
|
|
472
541
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
542
|
+
const source = resolvePluginSource(plugin.source);
|
|
543
|
+
|
|
544
|
+
// Local/bundled plugin, no external fetch needed
|
|
545
|
+
if (!source || source.type === 'local') {
|
|
476
546
|
// Bundled plugin, no fetch needed
|
|
477
547
|
fetched.push(name);
|
|
478
548
|
continue;
|
|
479
549
|
}
|
|
480
550
|
|
|
481
551
|
try {
|
|
482
|
-
await fetchPlugin(name,
|
|
552
|
+
await fetchPlugin(name, source.value, plugin.version);
|
|
483
553
|
fetched.push(name);
|
|
484
554
|
} catch (err) {
|
|
485
555
|
failed.push(name);
|
|
@@ -493,6 +563,8 @@ async function fetchExternalPlugins(pluginNames, marketplace) {
|
|
|
493
563
|
console.error(`\n [WARN] Missing dependencies: ${missingDeps.join(', ')}`);
|
|
494
564
|
console.error(` Some plugins may not work correctly without their dependencies.`);
|
|
495
565
|
}
|
|
566
|
+
|
|
567
|
+
throw new Error(`Failed to fetch ${failed.length} plugin(s): ${failed.join(', ')}`);
|
|
496
568
|
}
|
|
497
569
|
|
|
498
570
|
return fetched;
|
|
@@ -786,10 +858,13 @@ function loadComponents(pluginDir) {
|
|
|
786
858
|
if (fs.existsSync(componentsPath)) {
|
|
787
859
|
try {
|
|
788
860
|
const data = JSON.parse(fs.readFileSync(componentsPath, 'utf8'));
|
|
861
|
+
const normalize = (arr) => (arr || []).map(item =>
|
|
862
|
+
typeof item === 'string' ? { name: item } : item
|
|
863
|
+
);
|
|
789
864
|
return {
|
|
790
|
-
agents: data.agents
|
|
791
|
-
skills: data.skills
|
|
792
|
-
commands: data.commands
|
|
865
|
+
agents: normalize(data.agents),
|
|
866
|
+
skills: normalize(data.skills),
|
|
867
|
+
commands: normalize(data.commands)
|
|
793
868
|
};
|
|
794
869
|
} catch {
|
|
795
870
|
// Fall through to filesystem scan
|
|
@@ -914,12 +989,15 @@ async function installPlugin(nameWithVersion, args) {
|
|
|
914
989
|
// Fetch all
|
|
915
990
|
for (const depName of toFetch) {
|
|
916
991
|
const dep = pluginMap[depName];
|
|
917
|
-
|
|
918
|
-
|
|
992
|
+
if (!dep) continue;
|
|
993
|
+
|
|
994
|
+
const source = resolvePluginSource(dep.source);
|
|
995
|
+
if (!source || source.type === 'local') continue;
|
|
996
|
+
|
|
919
997
|
checkCoreCompat(dep);
|
|
920
998
|
const ver = depName === name && requestedVersion ? requestedVersion : dep.version;
|
|
921
999
|
try {
|
|
922
|
-
await fetchPlugin(depName,
|
|
1000
|
+
await fetchPlugin(depName, source.value, ver);
|
|
923
1001
|
} catch (err) {
|
|
924
1002
|
console.error(` [ERROR] Failed to fetch ${depName}: ${err.message}`);
|
|
925
1003
|
}
|
|
@@ -1928,8 +2006,6 @@ async function main() {
|
|
|
1928
2006
|
if (entry) checkCoreCompat(entry);
|
|
1929
2007
|
}
|
|
1930
2008
|
|
|
1931
|
-
await fetchExternalPlugins(pluginNames, marketplace);
|
|
1932
|
-
|
|
1933
2009
|
// Only copy to ~/.agentsys if OpenCode, Codex, or Cursor selected (they need local files)
|
|
1934
2010
|
const needsLocalInstall = selected.includes('opencode') || selected.includes('codex') || selected.includes('cursor');
|
|
1935
2011
|
let installDir = null;
|
|
@@ -1941,6 +2017,8 @@ async function main() {
|
|
|
1941
2017
|
installDependencies(installDir);
|
|
1942
2018
|
}
|
|
1943
2019
|
|
|
2020
|
+
await fetchExternalPlugins(pluginNames, marketplace);
|
|
2021
|
+
|
|
1944
2022
|
// Install for each platform
|
|
1945
2023
|
const failedPlatforms = [];
|
|
1946
2024
|
for (const platform of selected) {
|
|
@@ -2020,5 +2098,7 @@ module.exports = {
|
|
|
2020
2098
|
loadComponents,
|
|
2021
2099
|
resolveComponent,
|
|
2022
2100
|
buildFilterFromComponent,
|
|
2101
|
+
resolvePluginSource,
|
|
2102
|
+
parseGitHubSource,
|
|
2023
2103
|
installForCursor
|
|
2024
2104
|
};
|
package/package.json
CHANGED
package/site/content.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"url": "https://agent-sh.github.io/agentsys",
|
|
6
6
|
"repo": "https://github.com/agent-sh/agentsys",
|
|
7
7
|
"npm": "https://www.npmjs.com/package/agentsys",
|
|
8
|
-
"version": "5.2.
|
|
8
|
+
"version": "5.2.1",
|
|
9
9
|
"author": "Avi Fenesh",
|
|
10
10
|
"author_url": "https://github.com/avifenesh"
|
|
11
11
|
},
|