coding-tool-x 3.7.11 → 3.7.12

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 (42) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +1 -1
  3. package/dist/web/assets/{Analytics-Ck_00Brj.js → Analytics-8dRv1aYW.js} +1 -1
  4. package/dist/web/assets/{ConfigTemplates-Cpw2vUa8.js → ConfigTemplates-BQRXSICO.js} +1 -1
  5. package/dist/web/assets/Home-BtzWhKIa.js +1 -0
  6. package/dist/web/assets/Home-DYVy4cGZ.css +1 -0
  7. package/dist/web/assets/{PluginManager-2RlvEJCT.js → PluginManager-BTa-Ygf6.js} +1 -1
  8. package/dist/web/assets/{ProjectList-_ecCngpl.js → ProjectList-F_cB8ldA.js} +1 -1
  9. package/dist/web/assets/{SessionList-DlWcw3hO.js → SessionList-LOrbPQvu.js} +1 -1
  10. package/dist/web/assets/SkillManager-BsX8Qpj0.css +1 -0
  11. package/dist/web/assets/SkillManager-BzI_t-yV.js +1 -0
  12. package/dist/web/assets/{WorkspaceManager-3mqghi09.js → WorkspaceManager-Db9RGiz7.js} +1 -1
  13. package/dist/web/assets/index-DIWJmyL6.js +2 -0
  14. package/dist/web/assets/{index-BceXgSSe.css → index-bJRG3RXH.css} +1 -1
  15. package/dist/web/index.html +2 -2
  16. package/docs/superpowers/plans/2026-08-01-omp-skill-scan-settings.md +759 -0
  17. package/docs/superpowers/specs/2026-08-01-omp-skill-scan-settings-design.md +224 -0
  18. package/package.json +1 -1
  19. package/src/commands/proxy-control.js +12 -12
  20. package/src/commands/toggle-proxy.js +9 -9
  21. package/src/config/paths.js +52 -29
  22. package/src/index.js +1 -1
  23. package/src/server/api/plugins.js +119 -47
  24. package/src/server/api/skills.js +63 -10
  25. package/src/server/omp-proxy-server.js +14 -1
  26. package/src/server/services/model-definition-schema.js +2 -1
  27. package/src/server/services/omp-channels.js +71 -12
  28. package/src/server/services/omp-config.js +71 -6
  29. package/src/server/services/omp-native-plugin-adapter.js +456 -0
  30. package/src/server/services/omp-settings-manager.js +3 -1
  31. package/src/server/services/omp-skill-discovery.js +358 -0
  32. package/src/server/services/omp-skill-settings-service.js +97 -0
  33. package/src/server/services/pi-omp-migration.js +273 -0
  34. package/src/server/services/platform-resolution.js +44 -0
  35. package/src/server/services/plugins-service.js +85 -561
  36. package/src/server/services/project-path-validation.js +65 -0
  37. package/src/server/services/skill-service.js +169 -77
  38. package/dist/web/assets/Home-Bft3Kztl.js +0 -1
  39. package/dist/web/assets/Home-CHZJkkjH.css +0 -1
  40. package/dist/web/assets/SkillManager-CnY7IXkX.js +0 -1
  41. package/dist/web/assets/SkillManager-D7pd-d_P.css +0 -1
  42. package/dist/web/assets/index-k5oP7c-A.js +0 -2
@@ -12,14 +12,18 @@ const path = require('path');
12
12
  const { execFileSync } = require('child_process');
13
13
  const toml = require('toml');
14
14
  const tomlStringify = require('@iarna/toml').stringify;
15
- const yaml = require('js-yaml');
16
15
  const AdmZip = require('adm-zip');
17
16
  const { listPlugins, getPlugin, updatePlugin: updatePluginRegistry } = require('../../plugins/registry');
18
17
  const { installPlugin: installPluginCore, uninstallPlugin: uninstallPluginCore } = require('../../plugins/plugin-installer');
19
18
  const { initializePlugins, shutdownPlugins } = require('../../plugins/plugin-manager');
20
19
  const { INSTALLED_DIR, CONFIG_DIR } = require('../../plugins/constants');
21
20
  const { NATIVE_PATHS, PATHS } = require('../../config/paths');
22
- const { getOmpCommand } = require('./omp-config');
21
+ const { OmpNativePluginAdapter } = require('./omp-native-plugin-adapter');
22
+ const {
23
+ importLegacyPluginRepos,
24
+ migratePiStorage
25
+ } = require('./pi-omp-migration');
26
+ const { resolveManagedPlatform } = require('./platform-resolution');
23
27
  const { maskToken } = require('./oauth-utils');
24
28
  const {
25
29
  assertInsideAllowedRoots,
@@ -48,7 +52,6 @@ const DEFAULT_REPOS_BY_PLATFORM = {
48
52
  opencode: [],
49
53
  omp: []
50
54
  };
51
- const SUPPORTED_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode', 'omp'];
52
55
  const PLATFORM_CAPABILITIES = {
53
56
  claude: {
54
57
  platform: 'claude',
@@ -109,8 +112,11 @@ const PLATFORM_CAPABILITIES = {
109
112
  toggle: true,
110
113
  config: true,
111
114
  import: false,
112
- syncRepos: false,
113
- pluginKindLabel: 'packages/extensions'
115
+ syncRepos: true,
116
+ pluginKindLabel: 'plugins/extensions',
117
+ repositoryMode: 'native-marketplace',
118
+ repositoryToggle: false,
119
+ repositoryAuth: false
114
120
  }
115
121
  };
116
122
 
@@ -194,58 +200,6 @@ function splitPluginMarketplaceKey(key = '') {
194
200
  };
195
201
  }
196
202
 
197
- function normalizeOmpPackageEntry(item = null) {
198
- if (typeof item === 'string') {
199
- const name = item.trim();
200
- return name ? {
201
- name,
202
- source: name,
203
- directory: name,
204
- installSource: name,
205
- resourceTypes: []
206
- } : null;
207
- }
208
- if (!item || typeof item !== 'object') return null;
209
-
210
- const rawName = item.name || item.package || item.id || item.source || item.path || '';
211
- const name = String(rawName || '').trim();
212
- if (!name) return null;
213
-
214
- const directory = String(item.directory || item.path || name).trim() || name;
215
- const installSource = String(item.installSource || item.source || item.url || item.packageSource || name).trim() || name;
216
- return {
217
- ...item,
218
- name,
219
- source: item.source || installSource,
220
- directory,
221
- installSource,
222
- resourceTypes: normalizeOmpResourceTypes(item.resourceTypes || item.resources || item.provides || item.capabilities)
223
- };
224
- }
225
-
226
- function getOmpPackageIdentity(item = null) {
227
- const normalized = normalizeOmpPackageEntry(item);
228
- return normalized?.name || '';
229
- }
230
-
231
- function ompPackageSettingValue(item = null) {
232
- const normalized = normalizeOmpPackageEntry(item);
233
- if (!normalized) return null;
234
- if (typeof item === 'string') return normalized.name;
235
-
236
- const next = { ...item };
237
- next.name = normalized.name;
238
- next.directory = normalized.directory;
239
- next.source = normalized.installSource;
240
- const resourceTypes = normalizeOmpResourceTypes(next.resourceTypes || next.resources || normalized.resourceTypes);
241
- if (resourceTypes.length > 0) {
242
- next.resourceTypes = resourceTypes;
243
- } else {
244
- delete next.resourceTypes;
245
- }
246
- return next;
247
- }
248
-
249
203
  function normalizeOmpResourceType(value = '') {
250
204
  const normalized = String(value || '').trim();
251
205
  if (!normalized) return '';
@@ -304,17 +258,6 @@ function normalizeOmpResourceTypes(input = []) {
304
258
  return values;
305
259
  }
306
260
 
307
- function isOmpPackageInstallSource(value = '') {
308
- const source = String(value || '').trim();
309
- if (!source) return false;
310
- if (isLikelyLocalPath(source)) return true;
311
- if (/^(npm|git):/i.test(source)) return true;
312
- if (/^https?:\/\//i.test(source)) return true;
313
- if (/^ssh:\/\//i.test(source)) return true;
314
- if (/^git@[^:]+:.+/i.test(source)) return true;
315
- return !source.startsWith('-');
316
- }
317
-
318
261
  function normalizeMarketplaceSkillPaths(skills = []) {
319
262
  if (!Array.isArray(skills)) return [];
320
263
  return skills
@@ -493,7 +436,12 @@ function stripJsonComments(input = '') {
493
436
 
494
437
  class PluginsService {
495
438
  constructor(platform = 'claude') {
496
- this.platform = SUPPORTED_PLATFORMS.includes(platform) ? platform : 'claude';
439
+ this.platform = resolveManagedPlatform(platform).platform;
440
+ this._ompMigrationWarnings = [];
441
+ if (this.platform === 'omp') {
442
+ const migration = migratePiStorage(PATHS);
443
+ this._ompMigrationWarnings.push(...migration.warnings);
444
+ }
497
445
  this.configDir = PATHS.config || path.join((PATHS.base || process.env.HOME || os.homedir()), 'config');
498
446
  this.ccToolConfigDir = path.dirname(PATHS.pluginRepos.claude);
499
447
  this.opencodePluginsDir = path.join(OPENCODE_CONFIG_DIR, 'plugins');
@@ -501,12 +449,19 @@ class PluginsService {
501
449
  this.codexPluginsCacheDir = CODEX_PLUGINS_CACHE_DIR;
502
450
  this.marketCachePath = PATHS.pluginMarketCache[this.platform] || PATHS.pluginMarketCache.claude;
503
451
  this._marketCache = null;
452
+ this.ompNativeAdapter = this.platform === 'omp'
453
+ ? new OmpNativePluginAdapter()
454
+ : null;
504
455
  }
505
456
 
506
457
  getCapabilities() {
507
458
  return { ...(PLATFORM_CAPABILITIES[this.platform] || PLATFORM_CAPABILITIES.claude) };
508
459
  }
509
460
 
461
+ getMigrationWarnings() {
462
+ return [...this._ompMigrationWarnings];
463
+ }
464
+
510
465
  _pluginsSupported() {
511
466
  return this.getCapabilities().supportsPlugins !== false;
512
467
  }
@@ -748,195 +703,6 @@ class PluginsService {
748
703
  return json;
749
704
  }
750
705
 
751
- _readOmpSettings() {
752
- const filePath = NATIVE_PATHS.omp.settings;
753
- if (!fs.existsSync(filePath)) {
754
- const legacyPath = NATIVE_PATHS.omp.settingsJsonLegacy;
755
- if (legacyPath && fs.existsSync(legacyPath)) {
756
- try {
757
- const raw = fs.readFileSync(legacyPath, 'utf8');
758
- return raw.trim() ? JSON.parse(raw) : {};
759
- } catch (err) {
760
- console.error('[PluginsService] Failed to read legacy OMP settings:', err.message);
761
- }
762
- }
763
- return {};
764
- }
765
-
766
- try {
767
- const raw = fs.readFileSync(filePath, 'utf8');
768
- const parsed = raw.trim() ? yaml.load(raw) : {};
769
- return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
770
- } catch (err) {
771
- console.error('[PluginsService] Failed to read OMP settings:', err.message);
772
- return {};
773
- }
774
- }
775
-
776
- _writeOmpSettings(settings = {}) {
777
- this._ensureDir(path.dirname(NATIVE_PATHS.omp.settings));
778
- fs.writeFileSync(NATIVE_PATHS.omp.settings, yaml.dump(settings, {
779
- lineWidth: 120,
780
- noRefs: true,
781
- sortKeys: false
782
- }), 'utf8');
783
- }
784
-
785
- _listOmpPackages() {
786
- const settings = this._readOmpSettings();
787
- return Array.isArray(settings.packages)
788
- ? settings.packages.map(normalizeOmpPackageEntry).filter(Boolean)
789
- : [];
790
- }
791
-
792
- _setOmpPackages(packages = []) {
793
- const settings = this._readOmpSettings();
794
- const seen = new Set();
795
- settings.packages = packages
796
- .map(item => ompPackageSettingValue(item))
797
- .filter((item) => {
798
- const name = getOmpPackageIdentity(item);
799
- if (!name || seen.has(name)) return false;
800
- seen.add(name);
801
- return true;
802
- });
803
- this._writeOmpSettings(settings);
804
- }
805
-
806
- _listOmpDisabledPackageEntries() {
807
- const settings = this._readOmpSettings();
808
- return Array.isArray(settings.disabledPackages)
809
- ? settings.disabledPackages.map(normalizeOmpPackageEntry).filter(Boolean)
810
- : [];
811
- }
812
-
813
- _listOmpDisabledPackages() {
814
- return this._listOmpDisabledPackageEntries().map(item => item.name).filter(Boolean);
815
- }
816
-
817
- _setOmpDisabledPackages(packages = []) {
818
- const settings = this._readOmpSettings();
819
- const seen = new Set();
820
- settings.disabledPackages = packages
821
- .map(item => ompPackageSettingValue(item))
822
- .filter((item) => {
823
- const name = getOmpPackageIdentity(item);
824
- if (!name || seen.has(name)) return false;
825
- seen.add(name);
826
- return true;
827
- });
828
- this._writeOmpSettings(settings);
829
- }
830
-
831
- _runOmpPackageCommand(args = []) {
832
- execFileSync(getOmpCommand(), args, {
833
- cwd: process.cwd(),
834
- encoding: 'utf8',
835
- stdio: 'pipe',
836
- timeout: 120000,
837
- windowsHide: true
838
- });
839
- }
840
-
841
- _registerOmpPackageReference(packageName, metadata = {}) {
842
- const packages = this._listOmpPackages();
843
- const installSource = String(metadata.installSource || metadata.source || packageName || '').trim();
844
- const displayName = String(metadata.name || packageName || installSource).trim();
845
- if (!packages.some(pkg => pkg.name === displayName || pkg.installSource === installSource)) {
846
- const resourceTypes = normalizeOmpResourceTypes(metadata.resourceTypes || metadata.resources);
847
- const entry = installSource && installSource !== displayName
848
- ? {
849
- name: displayName,
850
- source: installSource,
851
- ...(metadata.version ? { version: metadata.version } : {}),
852
- ...(metadata.description ? { description: metadata.description } : {}),
853
- ...(resourceTypes.length > 0 ? { resourceTypes } : {})
854
- }
855
- : displayName;
856
- packages.push(entry);
857
- this._setOmpPackages(packages);
858
- }
859
- }
860
-
861
- _buildOmpPackagePlugin(packageName, metadata = {}) {
862
- const installSource = String(metadata.installSource || metadata.source || packageName || '').trim();
863
- const name = String(metadata.name || packageName || installSource).trim();
864
- const resourceTypes = normalizeOmpResourceTypes(metadata.resourceTypes || metadata.resources);
865
- return {
866
- name,
867
- directory: name,
868
- installSource: installSource || name,
869
- version: metadata.version || 'latest',
870
- description: metadata.description || '',
871
- pluginKind: 'package',
872
- pluginType: 'package',
873
- ...(resourceTypes.length > 0 ? { resourceTypes } : {})
874
- };
875
- }
876
-
877
- _installOmpPackage(packageName, metadata = {}) {
878
- const installSource = String(metadata.installSource || metadata.source || packageName || '').trim();
879
- const plugin = this._buildOmpPackagePlugin(packageName, { ...metadata, installSource });
880
- try {
881
- this._runOmpPackageCommand(['install', installSource || packageName]);
882
- return {
883
- success: true,
884
- plugin
885
- };
886
- } catch (err) {
887
- this._registerOmpPackageReference(packageName, { ...metadata, installSource, name: plugin.name });
888
- return {
889
- success: true,
890
- registeredOnly: true,
891
- warning: `Failed to run "${getOmpCommand()} install"; saved the package reference so OMP can resolve it later. ${err.message}`,
892
- plugin
893
- };
894
- }
895
- }
896
-
897
- _removeOmpPackage(packageName) {
898
- try {
899
- this._runOmpPackageCommand(['remove', packageName]);
900
- return true;
901
- } catch (err) {
902
- return false;
903
- }
904
- }
905
-
906
- _listOmpLocalExtensions() {
907
- const plugins = [];
908
- const extensionsDir = NATIVE_PATHS.omp.extensions;
909
- if (!fs.existsSync(extensionsDir)) return plugins;
910
-
911
- try {
912
- const disabled = new Set(this._listOmpDisabledPackages());
913
- const entries = fs.readdirSync(extensionsDir, { withFileTypes: true });
914
- for (const entry of entries) {
915
- if (entry.name.startsWith('.')) continue;
916
- const fullPath = path.join(extensionsDir, entry.name);
917
- const ext = path.extname(entry.name).toLowerCase();
918
- if (!entry.isDirectory() && !['.js', '.mjs', '.cjs', '.ts'].includes(ext)) continue;
919
- const name = entry.isDirectory() ? entry.name : entry.name.slice(0, -ext.length);
920
- plugins.push({
921
- name,
922
- directory: entry.name,
923
- installPath: fullPath,
924
- source: 'omp-extension',
925
- version: 'local',
926
- description: '',
927
- installed: true,
928
- enabled: !disabled.has(name) && !disabled.has(entry.name),
929
- pluginType: entry.isDirectory() ? 'extension-directory' : 'extension-file',
930
- pluginKind: 'extension'
931
- });
932
- }
933
- } catch (err) {
934
- console.error('[PluginsService] Failed to list OMP extensions:', err.message);
935
- }
936
-
937
- return plugins;
938
- }
939
-
940
706
  _readOpenCodeConfig() {
941
707
  const filePath = this._getOpenCodeConfigPath();
942
708
  if (!fs.existsSync(filePath)) return { filePath, config: {} };
@@ -1415,7 +1181,7 @@ class PluginsService {
1415
1181
  * Reads from Claude Code's native installed_plugins.json
1416
1182
  * @returns {Object} { plugins: Array }
1417
1183
  */
1418
- listPlugins() {
1184
+ listPlugins(options = {}) {
1419
1185
  if (!this._pluginsSupported()) {
1420
1186
  return { plugins: [] };
1421
1187
  }
@@ -1454,38 +1220,7 @@ class PluginsService {
1454
1220
  }
1455
1221
 
1456
1222
  if (this._isOmp()) {
1457
- const disabledEntries = this._listOmpDisabledPackageEntries();
1458
- const disabled = new Set(disabledEntries.flatMap(item => [item.name, item.installSource].filter(Boolean)));
1459
- const plugins = [];
1460
- const seen = new Set();
1461
-
1462
- for (const pkg of this._listOmpPackages()) {
1463
- const seenKey = pkg.installSource || pkg.name;
1464
- if (seen.has(seenKey)) continue;
1465
- seen.add(seenKey);
1466
- plugins.push({
1467
- ...pkg,
1468
- name: pkg.name,
1469
- directory: pkg.directory || pkg.name,
1470
- installSource: pkg.installSource || pkg.source || pkg.name,
1471
- resourceTypes: normalizeOmpResourceTypes(pkg.resourceTypes || pkg.resources),
1472
- source: 'omp-settings',
1473
- version: pkg.version || 'latest',
1474
- description: pkg.description || '',
1475
- installed: true,
1476
- enabled: !disabled.has(pkg.name) && !disabled.has(pkg.installSource),
1477
- pluginType: 'package',
1478
- pluginKind: 'package'
1479
- });
1480
- }
1481
-
1482
- for (const extension of this._listOmpLocalExtensions()) {
1483
- if (seen.has(extension.name)) continue;
1484
- seen.add(extension.name);
1485
- plugins.push(extension);
1486
- }
1487
-
1488
- return { plugins };
1223
+ return this.ompNativeAdapter.listPlugins(options);
1489
1224
  }
1490
1225
 
1491
1226
  const plugins = [];
@@ -1604,7 +1339,7 @@ class PluginsService {
1604
1339
  * @param {string} name - Plugin name
1605
1340
  * @returns {Object|null} Plugin details or null
1606
1341
  */
1607
- getPlugin(name) {
1342
+ getPlugin(name, options = {}) {
1608
1343
  if (this._isCodex()) {
1609
1344
  const plugin = this.listPlugins().plugins.find(p => p.name === name || `${p.name}@${p.marketplace}` === name);
1610
1345
  if (!plugin) return null;
@@ -1628,7 +1363,9 @@ class PluginsService {
1628
1363
  }
1629
1364
 
1630
1365
  if (this._isOmp()) {
1631
- const plugin = this.listPlugins().plugins.find(p => p.name === name || p.directory === name);
1366
+ const plugin = this.listPlugins(options).plugins.find(p =>
1367
+ p.pluginId === name || p.id === name || p.name === name
1368
+ );
1632
1369
  if (!plugin) return null;
1633
1370
  return plugin;
1634
1371
  }
@@ -1667,7 +1404,7 @@ class PluginsService {
1667
1404
  * @param {Object} repoInfo - Optional repo info { owner, name, branch, directory }
1668
1405
  * @returns {Promise<Object>} Installation result
1669
1406
  */
1670
- async installPlugin(source, repoInfo = null) {
1407
+ async installPlugin(source, repoInfo = null, options = {}) {
1671
1408
  if (this._isCodex()) {
1672
1409
  if (hasRepoInstallInfo(repoInfo)) {
1673
1410
  return this._installFromRepoDirectory(repoInfo);
@@ -1697,48 +1434,15 @@ class PluginsService {
1697
1434
  }
1698
1435
 
1699
1436
  if (this._isOmp()) {
1700
- const packageMetadata = repoInfo && typeof repoInfo === 'object'
1701
- ? {
1702
- name: repoInfo.name || repoInfo.packageName || repoInfo.displayName,
1703
- version: repoInfo.version,
1704
- description: repoInfo.description,
1705
- resourceTypes: repoInfo.resourceTypes || repoInfo.resources,
1706
- installSource: repoInfo.installSource || repoInfo.source
1707
- }
1708
- : {};
1709
- const explicitPackageSource = String(source || packageMetadata.installSource || '').trim();
1710
-
1711
- if (
1712
- repoInfo?.pluginKind === 'package' ||
1713
- repoInfo?.pluginType === 'package' ||
1714
- (explicitPackageSource && isOmpPackageInstallSource(explicitPackageSource) && !hasRepoInstallInfo(repoInfo))
1715
- ) {
1716
- return this._installOmpPackage(explicitPackageSource, packageMetadata);
1717
- }
1718
-
1719
- if (hasRepoInstallInfo(repoInfo)) {
1720
- return this._installFromRepoDirectory(repoInfo, { installRoot: NATIVE_PATHS.omp.extensions });
1721
- }
1722
-
1723
- const parsedSource = this.parseRepoTreeSource(source);
1724
- if (parsedSource) {
1725
- return this._installOmpPackage(source);
1726
- }
1727
-
1728
- const parsedRepo = this._repoFromGitUrl(source, 'main');
1729
- if (parsedRepo) {
1730
- return this._installOmpPackage(source);
1731
- }
1732
-
1733
- const packageName = String(source || '').trim();
1734
- if (packageName && isOmpPackageInstallSource(packageName)) {
1735
- return this._installOmpPackage(packageName);
1736
- }
1737
-
1738
- return {
1739
- success: false,
1740
- error: 'OMP plugin install expects package name, repository metadata, a GitHub/GitLab tree URL, or a Git repository URL'
1741
- };
1437
+ const metadata = repoInfo && typeof repoInfo === 'object' ? repoInfo : {};
1438
+ const target = String(
1439
+ source
1440
+ || metadata.pluginId
1441
+ || metadata.installTarget
1442
+ || metadata.installSource
1443
+ || ''
1444
+ ).trim();
1445
+ return this.ompNativeAdapter.installPlugin(target, metadata, options);
1742
1446
  }
1743
1447
 
1744
1448
  if (this._isOpenCode()) {
@@ -1979,7 +1683,7 @@ class PluginsService {
1979
1683
  } catch (e) {
1980
1684
  console.error('[PluginsService] Failed to update native installed_plugins.json:', e.message);
1981
1685
  }
1982
- } else if (!this._isOpenCode() && !this._isOmp()) {
1686
+ } else if (!this._isOpenCode()) {
1983
1687
  const installTimestamp = new Date().toISOString();
1984
1688
  const sourceUrl = this.buildRepoBrowserUrl(normalizedRepo, directory) || buildRepoUrl(normalizedRepo);
1985
1689
  const repoSourceMeta = {
@@ -2009,21 +1713,6 @@ class PluginsService {
2009
1713
  }
2010
1714
 
2011
1715
  this.writeRepoSourceMeta(pluginDir, repoSourceMeta);
2012
- } else if (this._isOmp()) {
2013
- const sourceUrl = this.buildRepoBrowserUrl(normalizedRepo, directory) || buildRepoUrl(normalizedRepo);
2014
- this.writeRepoSourceMeta(pluginDir, {
2015
- repoId: normalizedRepo.id,
2016
- repoProvider: normalizedRepo.provider,
2017
- repoOwner: normalizedRepo.owner || '',
2018
- repoName: normalizedRepo.name || '',
2019
- repoBranch: normalizedRepo.branch,
2020
- repoDirectory: directory,
2021
- repoHost: normalizedRepo.host || '',
2022
- repoProjectPath: normalizedRepo.projectPath || '',
2023
- repoLocalPath: normalizedRepo.localPath || '',
2024
- repoUrl: normalizedRepo.repoUrl || buildRepoUrl(normalizedRepo),
2025
- source: sourceUrl
2026
- });
2027
1716
  }
2028
1717
 
2029
1718
  return {
@@ -2049,9 +1738,6 @@ class PluginsService {
2049
1738
  if (this._isOpenCode()) {
2050
1739
  return ['package.json', 'plugin.json'];
2051
1740
  }
2052
- if (this._isOmp()) {
2053
- return ['omp.json', 'extension.json', 'plugin.json', 'package.json'];
2054
- }
2055
1741
  return ['.claude-plugin/plugin.json', 'plugin.json', 'package.json'];
2056
1742
  }
2057
1743
 
@@ -2140,7 +1826,7 @@ class PluginsService {
2140
1826
  * @param {string} name - Plugin name
2141
1827
  * @returns {Object} Uninstallation result
2142
1828
  */
2143
- uninstallPlugin(name) {
1829
+ uninstallPlugin(name, options = {}) {
2144
1830
  if (this._isCodex()) {
2145
1831
  const plugins = this.listPlugins().plugins;
2146
1832
  const target = plugins.find(plugin => plugin.name === name || `${plugin.name}@${plugin.marketplace}` === name);
@@ -2184,68 +1870,7 @@ class PluginsService {
2184
1870
  }
2185
1871
 
2186
1872
  if (this._isOmp()) {
2187
- const targetName = String(name || '').trim();
2188
- let safeName = '';
2189
- try {
2190
- safeName = normalizePluginPathName(targetName, 'plugin name');
2191
- } catch {
2192
- safeName = '';
2193
- }
2194
- const packages = this._listOmpPackages();
2195
- let removed = false;
2196
- const packageToRemove = packages.find(pkg =>
2197
- pkg.name === targetName ||
2198
- pkg.name === safeName ||
2199
- pkg.installSource === targetName ||
2200
- pkg.installSource === safeName
2201
- );
2202
- if (packageToRemove) {
2203
- this._removeOmpPackage(packageToRemove.installSource || packageToRemove.name);
2204
- }
2205
- const nextPackages = packages.filter(pkg =>
2206
- pkg.name !== safeName &&
2207
- pkg.name !== targetName &&
2208
- pkg.installSource !== safeName &&
2209
- pkg.installSource !== targetName
2210
- );
2211
- if (nextPackages.length !== packages.length) {
2212
- this._setOmpPackages(nextPackages);
2213
- const disabledEntries = this._listOmpDisabledPackageEntries().filter(pkg =>
2214
- pkg.name !== safeName &&
2215
- pkg.name !== targetName &&
2216
- pkg.installSource !== safeName &&
2217
- pkg.installSource !== targetName &&
2218
- pkg.name !== packageToRemove?.name &&
2219
- pkg.installSource !== packageToRemove?.installSource
2220
- );
2221
- this._setOmpDisabledPackages(disabledEntries);
2222
- removed = true;
2223
- }
2224
-
2225
- const extensionsDir = NATIVE_PATHS.omp.extensions;
2226
- if (safeName && fs.existsSync(extensionsDir)) {
2227
- const directPath = resolveInsideRoot(extensionsDir, safeName, 'OMP extension path');
2228
- if (fs.existsSync(directPath)) {
2229
- fs.rmSync(directPath, { recursive: true, force: true });
2230
- removed = true;
2231
- } else {
2232
- const entries = fs.readdirSync(extensionsDir, { withFileTypes: true });
2233
- for (const entry of entries) {
2234
- const baseName = entry.name.replace(path.extname(entry.name), '');
2235
- if (entry.name === safeName || baseName === safeName) {
2236
- const entryPath = resolveInsideRoot(extensionsDir, entry.name, 'OMP extension path');
2237
- fs.rmSync(entryPath, { recursive: true, force: true });
2238
- removed = true;
2239
- break;
2240
- }
2241
- }
2242
- }
2243
- }
2244
-
2245
- return {
2246
- success: true,
2247
- message: removed ? 'Plugin removed successfully' : 'Plugin not found'
2248
- };
1873
+ return this.ompNativeAdapter.uninstallPlugin(name, options);
2249
1874
  }
2250
1875
 
2251
1876
  if (this._isOpenCode()) {
@@ -2361,7 +1986,7 @@ class PluginsService {
2361
1986
  * @param {boolean} enabled - Enable or disable
2362
1987
  * @returns {Object} Updated plugin info
2363
1988
  */
2364
- togglePlugin(name, enabled) {
1989
+ togglePlugin(name, enabled, options = {}) {
2365
1990
  if (this._isCodex()) {
2366
1991
  const plugins = this.listPlugins().plugins;
2367
1992
  const target = plugins.find(plugin => plugin.name === name || `${plugin.name}@${plugin.marketplace}` === name);
@@ -2381,26 +2006,7 @@ class PluginsService {
2381
2006
  }
2382
2007
 
2383
2008
  if (this._isOmp()) {
2384
- const targetName = String(name || '').trim();
2385
- const packages = this._listOmpPackages();
2386
- const targetPackage = packages.find(pkg => pkg.name === targetName || pkg.installSource === targetName);
2387
- const disabledEntries = this._listOmpDisabledPackageEntries();
2388
- const disabled = new Set(disabledEntries.flatMap(item => [item.name, item.installSource].filter(Boolean)));
2389
- if (enabled) {
2390
- disabled.delete(targetName);
2391
- if (targetPackage) {
2392
- disabled.delete(targetPackage.name);
2393
- disabled.delete(targetPackage.installSource);
2394
- }
2395
- } else {
2396
- disabled.add(targetPackage || targetName);
2397
- }
2398
- this._setOmpDisabledPackages(Array.from(disabled));
2399
- return {
2400
- name: targetName,
2401
- enabled,
2402
- success: true
2403
- };
2009
+ return this.ompNativeAdapter.togglePlugin(name, enabled, options);
2404
2010
  }
2405
2011
 
2406
2012
  if (this._isOpenCode()) {
@@ -2487,7 +2093,7 @@ class PluginsService {
2487
2093
  * @param {Object} config - Configuration object
2488
2094
  * @returns {Object} Result
2489
2095
  */
2490
- updatePluginConfig(name, config) {
2096
+ updatePluginConfig(name, config, options = {}) {
2491
2097
  if (this._isCodex()) {
2492
2098
  throw new Error('Codex cached plugins do not support config updates');
2493
2099
  }
@@ -2497,16 +2103,7 @@ class PluginsService {
2497
2103
  }
2498
2104
 
2499
2105
  if (this._isOmp()) {
2500
- const settings = this._readOmpSettings();
2501
- settings.packageConfig = settings.packageConfig && typeof settings.packageConfig === 'object' && !Array.isArray(settings.packageConfig)
2502
- ? settings.packageConfig
2503
- : {};
2504
- settings.packageConfig[name] = config;
2505
- this._writeOmpSettings(settings);
2506
- return {
2507
- success: true,
2508
- message: `Configuration updated for plugin "${name}"`
2509
- };
2106
+ return this.ompNativeAdapter.updatePluginConfig(name, config, options);
2510
2107
  }
2511
2108
 
2512
2109
  if (this._isOpenCode()) {
@@ -2591,10 +2188,15 @@ class PluginsService {
2591
2188
  * Reads from both our config and Claude Code's native marketplace config
2592
2189
  * @returns {Array} Repos list
2593
2190
  */
2594
- getRepos() {
2191
+ getRepos(options = {}) {
2595
2192
  if (!this.getCapabilities().repositories) {
2596
2193
  return [];
2597
2194
  }
2195
+ if (this._isOmp()) {
2196
+ const migration = importLegacyPluginRepos(this.ompNativeAdapter, PATHS, options);
2197
+ this._ompMigrationWarnings = migration.warnings;
2198
+ return this.ompNativeAdapter.listMarketplaces(options);
2199
+ }
2598
2200
 
2599
2201
  const repos = [];
2600
2202
  const seenRepos = new Set();
@@ -2667,7 +2269,12 @@ class PluginsService {
2667
2269
  * @param {Object} repo - Repository info { url, owner, name, branch, enabled }
2668
2270
  * @returns {Array} Updated repos list
2669
2271
  */
2670
- addRepo(repo) {
2272
+ addRepo(repo, options = {}) {
2273
+ if (this._isOmp()) {
2274
+ const source = repo.source || repo.sourceUri || repo.repoUrl || repo.url
2275
+ || repo.localPath || [repo.owner, repo.name].filter(Boolean).join('/');
2276
+ return this.ompNativeAdapter.addMarketplace(source, options);
2277
+ }
2671
2278
  const config = this.loadReposConfig();
2672
2279
  const normalizedRepo = this.normalizeRepoConfig({
2673
2280
  ...repo,
@@ -2693,7 +2300,10 @@ class PluginsService {
2693
2300
  * @param {string} name - Repository name
2694
2301
  * @returns {Array} Updated repos list
2695
2302
  */
2696
- removeRepo(owner, name, repoId = '') {
2303
+ removeRepo(owner, name, repoId = '', options = {}) {
2304
+ if (this._isOmp()) {
2305
+ return this.ompNativeAdapter.removeMarketplace(repoId || name, options);
2306
+ }
2697
2307
  const config = this.loadReposConfig();
2698
2308
  config.repos = config.repos.filter(r => {
2699
2309
  if (repoId) {
@@ -2713,7 +2323,10 @@ class PluginsService {
2713
2323
  * @param {boolean} enabled - Enable or disable
2714
2324
  * @returns {Array} Updated repos list
2715
2325
  */
2716
- toggleRepo(owner, name, enabled, repoId = '') {
2326
+ toggleRepo(owner, name, enabled, repoId = '', options = {}) {
2327
+ if (this._isOmp()) {
2328
+ throw new Error('OMP native marketplaces do not support repository toggles');
2329
+ }
2717
2330
  const config = this.loadReposConfig();
2718
2331
  const repo = config.repos.find(r => {
2719
2332
  if (repoId) return r.id === repoId;
@@ -2729,6 +2342,9 @@ class PluginsService {
2729
2342
  }
2730
2343
 
2731
2344
  updateRepoAuth(owner, name, token = '', clearToken = false, repoId = '') {
2345
+ if (this._isOmp()) {
2346
+ throw new Error('OMP native marketplaces do not support per-repository authentication');
2347
+ }
2732
2348
  const config = this.loadReposConfig();
2733
2349
  const repo = config.repos.find(r => {
2734
2350
  if (repoId) return r.id === repoId;
@@ -3178,7 +2794,11 @@ class PluginsService {
3178
2794
  * Sync repositories to Claude Code marketplace
3179
2795
  * @returns {Promise<Object>} Sync results
3180
2796
  */
3181
- async syncRepos() {
2797
+ async syncRepos(options = {}) {
2798
+ if (this._isOmp()) {
2799
+ const repos = this.ompNativeAdapter.updateMarketplaces('', options);
2800
+ return { success: true, repos, results: [] };
2801
+ }
3182
2802
  if (!this.getCapabilities().syncRepos) {
3183
2803
  return { success: true, results: [] };
3184
2804
  }
@@ -3213,8 +2833,8 @@ class PluginsService {
3213
2833
  * Sync plugins from Claude Code
3214
2834
  * @returns {Promise<Object>} Updated plugins list
3215
2835
  */
3216
- async syncPlugins() {
3217
- return this.listPlugins();
2836
+ async syncPlugins(options = {}) {
2837
+ return this.listPlugins(options);
3218
2838
  }
3219
2839
 
3220
2840
  /**
@@ -3599,104 +3219,20 @@ class PluginsService {
3599
3219
  return results;
3600
3220
  }
3601
3221
 
3602
- _normalizeOmpCatalogEntries(catalog = null) {
3603
- if (Array.isArray(catalog)) return catalog;
3604
- if (!catalog || typeof catalog !== 'object') return [];
3605
- for (const key of ['packages', 'plugins', 'extensions']) {
3606
- if (Array.isArray(catalog[key])) return catalog[key];
3607
- }
3608
- return [];
3609
- }
3610
-
3611
- _normalizeOmpCatalogPackage(repo, entry = {}, catalog = {}, defaults = {}) {
3612
- if (!entry || typeof entry !== 'object') return null;
3613
- const installSource = String(
3614
- entry.installSource ||
3615
- entry.source ||
3616
- entry.package ||
3617
- entry.url ||
3618
- entry.repo ||
3619
- entry.repository ||
3620
- entry.name ||
3621
- ''
3622
- ).trim();
3623
- const name = String(entry.name || entry.id || entry.package || installSource || '').trim();
3624
- if (!name || !installSource) return null;
3625
-
3626
- const resourceTypes = normalizeOmpResourceTypes(
3627
- entry.resourceTypes ||
3628
- entry.resources ||
3629
- entry.provides ||
3630
- entry.omp?.resources ||
3631
- defaults.resourceTypes
3632
- );
3633
- return this.buildMarketPluginItem(repo, {
3634
- name,
3635
- displayName: entry.displayName || entry.title || '',
3636
- description: entry.description || '',
3637
- author: entry.author || catalog.author || repo.owner,
3638
- version: entry.version || 'latest',
3639
- category: entry.category || 'general',
3640
- directory: entry.directory || name,
3641
- marketplace: entry.marketplace || repo.marketplace || catalog.name || defaults.marketplace || '',
3642
- installSource,
3643
- marketplaceFormat: defaults.marketplaceFormat || 'omp-package-catalog',
3644
- pluginKind: 'package',
3645
- pluginType: 'package',
3646
- resourceTypes,
3647
- resources: entry.resources || entry.provides || null,
3648
- repoUrl: entry.repoUrl || entry.repositoryUrl || entry.repository || repo.repoUrl || buildRepoUrl(repo)
3649
- });
3650
- }
3651
-
3652
- async _fetchOmpPackageCatalogPlugins(repo, fileMap, readJson) {
3653
- if (!this._isOmp()) return [];
3654
- const catalogFiles = ['omp-packages.json', '.omp/packages.json', 'packages.json'];
3655
- for (const catalogPath of catalogFiles) {
3656
- if (!fileMap.has(catalogPath)) continue;
3657
- const catalog = await readJson(catalogPath);
3658
- const entries = this._normalizeOmpCatalogEntries(catalog);
3659
- const plugins = entries
3660
- .map(entry => this._normalizeOmpCatalogPackage(repo, entry, catalog, {
3661
- marketplaceFormat: 'omp-package-catalog'
3662
- }))
3663
- .filter(Boolean);
3664
- if (plugins.length > 0) return plugins;
3665
- }
3666
-
3667
- const manifestPath = fileMap.has('omp.json') ? 'omp.json' : (fileMap.has('package.json') ? 'package.json' : '');
3668
- if (!manifestPath) return [];
3669
- const manifest = await readJson(manifestPath);
3670
- const ompMeta = manifest.omp && typeof manifest.omp === 'object' ? manifest.omp: {};
3671
- const installSource = ompMeta.installSource || manifest.installSource || manifest.name;
3672
- const resourceTypes = normalizeOmpResourceTypes(
3673
- ompMeta.resourceTypes ||
3674
- ompMeta.resources ||
3675
- manifest.resourceTypes ||
3676
- manifest.resources
3677
- );
3678
- if (!installSource || resourceTypes.length === 0) return [];
3679
- const plugin = this._normalizeOmpCatalogPackage(repo, {
3680
- name: manifest.name || repo.name,
3681
- installSource,
3682
- version: manifest.version,
3683
- description: manifest.description,
3684
- author: manifest.author,
3685
- resourceTypes
3686
- }, { name: repo.marketplace || repo.name }, {
3687
- marketplaceFormat: manifestPath === 'omp.json' ? 'omp-manifest' : 'omp-package-manifest'
3688
- });
3689
- return plugin ? [plugin] : [];
3690
- }
3691
-
3692
3222
  /**
3693
3223
  * Get market plugins from configured repositories
3694
3224
  * @returns {Promise<Array>} List of available market plugins
3695
3225
  */
3696
- async getMarketPlugins(forceRefresh = false) {
3226
+ async getMarketPlugins(forceRefresh = false, options = {}) {
3697
3227
  if (!this.getCapabilities().market) {
3698
3228
  return [];
3699
3229
  }
3230
+ if (this._isOmp()) {
3231
+ if (forceRefresh) {
3232
+ this.ompNativeAdapter.updateMarketplaces('', options);
3233
+ }
3234
+ return this.ompNativeAdapter.discover(options);
3235
+ }
3700
3236
 
3701
3237
  if (forceRefresh) {
3702
3238
  this.clearMarketCache({ removeFile: false });
@@ -3765,15 +3301,6 @@ class PluginsService {
3765
3301
  }
3766
3302
  }
3767
3303
 
3768
- // OMP package catalog format; legacy catalog filenames may still include omp.
3769
- if (this._isOmp()) {
3770
- const ompPackagePlugins = await this._fetchOmpPackageCatalogPlugins(repo, fileMap, readJson);
3771
- if (ompPackagePlugins.length > 0) {
3772
- marketPlugins.push(...ompPackagePlugins);
3773
- continue;
3774
- }
3775
- }
3776
-
3777
3304
  const nestedManifestFiles = files.filter(file => {
3778
3305
  if (this._isCodex()) {
3779
3306
  return file.path === '.codex-plugin/plugin.json' || file.path.endsWith('/.codex-plugin/plugin.json');
@@ -3819,9 +3346,7 @@ class PluginsService {
3819
3346
 
3820
3347
  for (const dir of pluginDirs) {
3821
3348
  try {
3822
- const manifest = this._isOmp()
3823
- ? await this._readManifestFromRepo(repo, dir, ['omp.json', 'extension.json', 'plugin.json', 'package.json'])
3824
- : await readJson(`${dir}/plugin.json`);
3349
+ const manifest = await readJson(`${dir}/plugin.json`);
3825
3350
  if (!manifest) {
3826
3351
  throw new Error(`File not found: ${dir}/plugin.json`);
3827
3352
  }
@@ -3832,7 +3357,6 @@ class PluginsService {
3832
3357
  author: manifest.author || repo.owner,
3833
3358
  version: manifest.version || '1.0.0',
3834
3359
  directory: dir,
3835
- pluginKind: this._isOmp() ? 'extension' : undefined,
3836
3360
  commands: manifest.commands || [],
3837
3361
  hooks: manifest.hooks || []
3838
3362
  }));