@claude-collective/cli 0.29.4 → 0.30.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.
Files changed (175) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/config/skills-matrix.yaml +1 -1
  3. package/config/stacks.yaml +1 -1
  4. package/dist/{chunk-XPMEGGJK.js → chunk-34E7MWJI.js} +13 -38
  5. package/dist/chunk-34E7MWJI.js.map +1 -0
  6. package/dist/{chunk-IFODQTCX.js → chunk-52XVP55K.js} +6 -3
  7. package/dist/chunk-52XVP55K.js.map +1 -0
  8. package/dist/{chunk-N73GQTCK.js → chunk-5P6RUVA7.js} +6 -6
  9. package/dist/{chunk-ZX5DM4D5.js → chunk-AGNT3FUE.js} +3 -3
  10. package/dist/{chunk-A4T4YSV4.js → chunk-AGQRRJSY.js} +2 -2
  11. package/dist/{chunk-6F3ZKDVE.js → chunk-AXND7NCM.js} +3 -3
  12. package/dist/{chunk-EP6J44I4.js → chunk-DH6F7EOJ.js} +5 -5
  13. package/dist/{chunk-AG5YGYJT.js → chunk-DTFEFLUU.js} +2 -2
  14. package/dist/chunk-EOJTMX7T.js +183 -0
  15. package/dist/chunk-EOJTMX7T.js.map +1 -0
  16. package/dist/{chunk-AJFSCLJ7.js → chunk-F23LEXMC.js} +2 -2
  17. package/dist/{chunk-G5WXKKQM.js → chunk-FFZSN5C5.js} +5 -5
  18. package/dist/chunk-FFZSN5C5.js.map +1 -0
  19. package/dist/{chunk-FY5D4KIC.js → chunk-H2NM6BDH.js} +2 -2
  20. package/dist/{chunk-56ERY7H7.js → chunk-HMZPUOWU.js} +4 -4
  21. package/dist/{chunk-RI5QEK5W.js → chunk-I6YQDA2J.js} +2 -2
  22. package/dist/{chunk-MQAYAISQ.js → chunk-JWIH7YQE.js} +2 -2
  23. package/dist/{chunk-VTUPUKFD.js → chunk-KFL72CWK.js} +2 -2
  24. package/dist/{chunk-OA5RCL2L.js → chunk-LY5HM34M.js} +2 -2
  25. package/dist/{chunk-XZKVOPCR.js → chunk-MYLRA7NI.js} +4 -4
  26. package/dist/{chunk-EUPMWSM3.js → chunk-QR2ONHDW.js} +5 -5
  27. package/dist/{chunk-SSHG7MEE.js → chunk-QUL7R35E.js} +307 -504
  28. package/dist/chunk-QUL7R35E.js.map +1 -0
  29. package/dist/{chunk-7UKQZSWT.js → chunk-RFW2RIM7.js} +2 -2
  30. package/dist/{chunk-PNXFJPXF.js → chunk-URUSBWWI.js} +2 -2
  31. package/dist/{chunk-GVVEPVR7.js → chunk-VHVRPLDY.js} +4 -4
  32. package/dist/{chunk-IRG52AN5.js → chunk-VIBOSHRL.js} +2 -2
  33. package/dist/{chunk-IQUBOWWU.js → chunk-WWLEF4MQ.js} +3 -3
  34. package/dist/{chunk-WLQUQXWO.js → chunk-WXW6SQ5K.js} +3 -3
  35. package/dist/{chunk-DUIYVKFK.js → chunk-X5AD7WWC.js} +12 -12
  36. package/dist/commands/build/marketplace.js +1 -1
  37. package/dist/commands/build/plugins.js +3 -3
  38. package/dist/commands/build/stack.js +3 -3
  39. package/dist/commands/compile.js +21 -70
  40. package/dist/commands/compile.js.map +1 -1
  41. package/dist/commands/config/get.js +2 -2
  42. package/dist/commands/config/index.js +3 -3
  43. package/dist/commands/config/path.js +2 -2
  44. package/dist/commands/config/set-project.js +2 -2
  45. package/dist/commands/config/show.js +3 -3
  46. package/dist/commands/config/unset-project.js +2 -2
  47. package/dist/commands/diff.js +2 -2
  48. package/dist/commands/doctor.js +2 -2
  49. package/dist/commands/edit.js +40 -71
  50. package/dist/commands/edit.js.map +1 -1
  51. package/dist/commands/eject.js +2 -2
  52. package/dist/commands/import/skill.js +2 -2
  53. package/dist/commands/info.js +2 -2
  54. package/dist/commands/init.js +79 -109
  55. package/dist/commands/init.js.map +1 -1
  56. package/dist/commands/list.js +2 -2
  57. package/dist/commands/new/agent.js +5 -3
  58. package/dist/commands/new/agent.js.map +1 -1
  59. package/dist/commands/new/skill.js +2 -2
  60. package/dist/commands/outdated.js +2 -2
  61. package/dist/commands/search.js +4 -4
  62. package/dist/commands/uninstall.js +41 -29
  63. package/dist/commands/uninstall.js.map +1 -1
  64. package/dist/commands/update.js +4 -6
  65. package/dist/commands/update.js.map +1 -1
  66. package/dist/commands/validate.js +2 -2
  67. package/dist/commands/version/bump.js +2 -2
  68. package/dist/commands/version/index.js +2 -2
  69. package/dist/commands/version/set.js +2 -2
  70. package/dist/commands/version/show.js +2 -2
  71. package/dist/components/skill-search/skill-search.js +3 -3
  72. package/dist/components/wizard/category-grid.js +2 -2
  73. package/dist/components/wizard/category-grid.test.js +2 -2
  74. package/dist/components/wizard/domain-selection.js +4 -4
  75. package/dist/components/wizard/help-modal.js +2 -2
  76. package/dist/components/wizard/menu-item.js +2 -2
  77. package/dist/components/wizard/search-modal.js +2 -2
  78. package/dist/components/wizard/search-modal.test.js +2 -2
  79. package/dist/components/wizard/section-progress.js +2 -2
  80. package/dist/components/wizard/section-progress.test.js +2 -2
  81. package/dist/components/wizard/source-grid.js +3 -3
  82. package/dist/components/wizard/source-grid.test.js +3 -3
  83. package/dist/components/wizard/stack-selection.js +6 -6
  84. package/dist/components/wizard/step-approach.js +6 -6
  85. package/dist/components/wizard/step-approach.test.js +6 -6
  86. package/dist/components/wizard/step-build.js +5 -5
  87. package/dist/components/wizard/step-build.test.js +5 -5
  88. package/dist/components/wizard/step-confirm.js +2 -2
  89. package/dist/components/wizard/step-confirm.test.js +4 -4
  90. package/dist/components/wizard/step-refine.js +2 -2
  91. package/dist/components/wizard/step-refine.test.js +2 -2
  92. package/dist/components/wizard/step-settings.js +4 -4
  93. package/dist/components/wizard/step-settings.test.js +8 -8
  94. package/dist/components/wizard/step-settings.test.js.map +1 -1
  95. package/dist/components/wizard/step-sources.js +7 -7
  96. package/dist/components/wizard/step-sources.test.js +7 -7
  97. package/dist/components/wizard/step-stack.js +8 -8
  98. package/dist/components/wizard/step-stack.test.js +8 -8
  99. package/dist/components/wizard/view-title.js +2 -2
  100. package/dist/components/wizard/wizard-layout.js +6 -6
  101. package/dist/components/wizard/wizard-tabs.js +2 -2
  102. package/dist/components/wizard/wizard-tabs.test.js +2 -2
  103. package/dist/components/wizard/wizard.js +21 -21
  104. package/dist/config/skills-matrix.yaml +1 -1
  105. package/dist/config/stacks.yaml +1 -1
  106. package/dist/hooks/init.js +2 -2
  107. package/dist/{source-manager-TV2YGPAN.js → source-manager-S5XTC6RW.js} +3 -3
  108. package/dist/src/agents/developer/api-developer/agent.yaml +1 -1
  109. package/dist/src/agents/developer/cli-developer/agent.yaml +1 -1
  110. package/dist/src/agents/developer/web-architecture/agent.yaml +1 -1
  111. package/dist/src/agents/developer/web-developer/agent.yaml +1 -1
  112. package/dist/src/agents/meta/agent-summoner/agent.yaml +1 -1
  113. package/dist/src/agents/meta/documentor/agent.yaml +1 -1
  114. package/dist/src/agents/meta/skill-summoner/agent.yaml +1 -1
  115. package/dist/src/agents/meta/skill-summoner/output-format.md +1 -1
  116. package/dist/src/agents/migration/cli-migrator/agent.yaml +1 -1
  117. package/dist/src/agents/pattern/pattern-scout/agent.yaml +1 -1
  118. package/dist/src/agents/pattern/web-pattern-critique/agent.yaml +1 -1
  119. package/dist/src/agents/planning/web-pm/agent.yaml +1 -1
  120. package/dist/src/agents/researcher/api-researcher/agent.yaml +1 -1
  121. package/dist/src/agents/researcher/web-researcher/agent.yaml +1 -1
  122. package/dist/src/agents/reviewer/api-reviewer/agent.yaml +1 -1
  123. package/dist/src/agents/reviewer/cli-reviewer/agent.yaml +1 -1
  124. package/dist/src/agents/reviewer/web-reviewer/agent.yaml +1 -1
  125. package/dist/src/agents/tester/cli-tester/agent.yaml +1 -1
  126. package/dist/src/agents/tester/web-tester/agent.yaml +1 -1
  127. package/dist/stores/wizard-store.js +3 -3
  128. package/dist/stores/wizard-store.test.js +3 -3
  129. package/package.json +1 -1
  130. package/src/agents/developer/api-developer/agent.yaml +1 -1
  131. package/src/agents/developer/cli-developer/agent.yaml +1 -1
  132. package/src/agents/developer/web-architecture/agent.yaml +1 -1
  133. package/src/agents/developer/web-developer/agent.yaml +1 -1
  134. package/src/agents/meta/agent-summoner/agent.yaml +1 -1
  135. package/src/agents/meta/documentor/agent.yaml +1 -1
  136. package/src/agents/meta/skill-summoner/agent.yaml +1 -1
  137. package/src/agents/meta/skill-summoner/output-format.md +1 -1
  138. package/src/agents/migration/cli-migrator/agent.yaml +1 -1
  139. package/src/agents/pattern/pattern-scout/agent.yaml +1 -1
  140. package/src/agents/pattern/web-pattern-critique/agent.yaml +1 -1
  141. package/src/agents/planning/web-pm/agent.yaml +1 -1
  142. package/src/agents/researcher/api-researcher/agent.yaml +1 -1
  143. package/src/agents/researcher/web-researcher/agent.yaml +1 -1
  144. package/src/agents/reviewer/api-reviewer/agent.yaml +1 -1
  145. package/src/agents/reviewer/cli-reviewer/agent.yaml +1 -1
  146. package/src/agents/reviewer/web-reviewer/agent.yaml +1 -1
  147. package/src/agents/tester/cli-tester/agent.yaml +1 -1
  148. package/src/agents/tester/web-tester/agent.yaml +1 -1
  149. package/dist/chunk-G5WXKKQM.js.map +0 -1
  150. package/dist/chunk-IFODQTCX.js.map +0 -1
  151. package/dist/chunk-SSHG7MEE.js.map +0 -1
  152. package/dist/chunk-XPMEGGJK.js.map +0 -1
  153. /package/dist/{chunk-N73GQTCK.js.map → chunk-5P6RUVA7.js.map} +0 -0
  154. /package/dist/{chunk-ZX5DM4D5.js.map → chunk-AGNT3FUE.js.map} +0 -0
  155. /package/dist/{chunk-A4T4YSV4.js.map → chunk-AGQRRJSY.js.map} +0 -0
  156. /package/dist/{chunk-6F3ZKDVE.js.map → chunk-AXND7NCM.js.map} +0 -0
  157. /package/dist/{chunk-EP6J44I4.js.map → chunk-DH6F7EOJ.js.map} +0 -0
  158. /package/dist/{chunk-AG5YGYJT.js.map → chunk-DTFEFLUU.js.map} +0 -0
  159. /package/dist/{chunk-AJFSCLJ7.js.map → chunk-F23LEXMC.js.map} +0 -0
  160. /package/dist/{chunk-FY5D4KIC.js.map → chunk-H2NM6BDH.js.map} +0 -0
  161. /package/dist/{chunk-56ERY7H7.js.map → chunk-HMZPUOWU.js.map} +0 -0
  162. /package/dist/{chunk-RI5QEK5W.js.map → chunk-I6YQDA2J.js.map} +0 -0
  163. /package/dist/{chunk-MQAYAISQ.js.map → chunk-JWIH7YQE.js.map} +0 -0
  164. /package/dist/{chunk-VTUPUKFD.js.map → chunk-KFL72CWK.js.map} +0 -0
  165. /package/dist/{chunk-OA5RCL2L.js.map → chunk-LY5HM34M.js.map} +0 -0
  166. /package/dist/{chunk-XZKVOPCR.js.map → chunk-MYLRA7NI.js.map} +0 -0
  167. /package/dist/{chunk-EUPMWSM3.js.map → chunk-QR2ONHDW.js.map} +0 -0
  168. /package/dist/{chunk-7UKQZSWT.js.map → chunk-RFW2RIM7.js.map} +0 -0
  169. /package/dist/{chunk-PNXFJPXF.js.map → chunk-URUSBWWI.js.map} +0 -0
  170. /package/dist/{chunk-GVVEPVR7.js.map → chunk-VHVRPLDY.js.map} +0 -0
  171. /package/dist/{chunk-IRG52AN5.js.map → chunk-VIBOSHRL.js.map} +0 -0
  172. /package/dist/{chunk-IQUBOWWU.js.map → chunk-WWLEF4MQ.js.map} +0 -0
  173. /package/dist/{chunk-WLQUQXWO.js.map → chunk-WXW6SQ5K.js.map} +0 -0
  174. /package/dist/{chunk-DUIYVKFK.js.map → chunk-X5AD7WWC.js.map} +0 -0
  175. /package/dist/{source-manager-TV2YGPAN.js.map → source-manager-S5XTC6RW.js.map} +0 -0
@@ -68,7 +68,7 @@ import {
68
68
  STANDARD_FILES,
69
69
  YAML_FORMATTING,
70
70
  yamlSchemaComment
71
- } from "./chunk-IFODQTCX.js";
71
+ } from "./chunk-52XVP55K.js";
72
72
  import {
73
73
  init_esm_shims
74
74
  } from "./chunk-AWKZ5BDL.js";
@@ -363,8 +363,8 @@ Examples:
363
363
  }
364
364
  }
365
365
  function validateLocalPath(source, flagName) {
366
- const CONTROL_CHAR_PATTERN2 = /[\x00-\x08\x0E-\x1F\x7F]/u;
367
- if (CONTROL_CHAR_PATTERN2.test(source)) {
366
+ const CONTROL_CHAR_PATTERN = /[\x00-\x08\x0E-\x1F\x7F]/u;
367
+ if (CONTROL_CHAR_PATTERN.test(source)) {
368
368
  throw new Error(
369
369
  `${flagName} contains invalid characters: "${source}"
370
370
 
@@ -682,11 +682,6 @@ function resolveSkillPath(basePath, skillPath) {
682
682
  validateSkillPath(resolved, basePath, skillPath);
683
683
  return resolved;
684
684
  }
685
- function getSkillDestPath(skill, stackDir) {
686
- const skillRelativePath = skill.path.replace(/^skills\//, "");
687
- const skillsDir = path4.join(stackDir, "skills");
688
- return resolveSkillPath(skillsDir, skillRelativePath);
689
- }
690
685
  async function generateSkillHash(skillSourcePath) {
691
686
  const skillMdPath = path4.join(skillSourcePath, STANDARD_FILES.SKILL_MD);
692
687
  return computeFileHash(skillMdPath);
@@ -695,55 +690,6 @@ function getSkillSourcePathFromSource(skill, sourceResult) {
695
690
  const srcDir = path4.join(sourceResult.sourcePath, "src");
696
691
  return resolveSkillPath(srcDir, skill.path);
697
692
  }
698
- async function copySkillFromSource(skill, stackDir, sourceResult) {
699
- const sourcePath = getSkillSourcePathFromSource(skill, sourceResult);
700
- const destPath = getSkillDestPath(skill, stackDir);
701
- const contentHash = await generateSkillHash(sourcePath);
702
- await ensureDir(path4.dirname(destPath));
703
- await copy(sourcePath, destPath);
704
- await injectForkedFromMetadata(destPath, skill.id, contentHash);
705
- return {
706
- skillId: skill.id,
707
- contentHash,
708
- sourcePath,
709
- destPath
710
- };
711
- }
712
- async function copySkillsToPluginFromSource(selectedSkillIds, pluginDir, matrix, sourceResult, sourceSelections, onProgress) {
713
- const total = selectedSkillIds.length;
714
- let completed = 0;
715
- const results = await Promise.all(
716
- selectedSkillIds.map(async (skillId) => {
717
- const skill = matrix.skills[skillId];
718
- if (!skill) {
719
- warn(`Skill not found in matrix: '${skillId}'`);
720
- completed++;
721
- onProgress?.(completed, total);
722
- return null;
723
- }
724
- const selectedSource = sourceSelections?.[skillId];
725
- const userSelectedRemote = selectedSource && selectedSource !== "local";
726
- let result;
727
- if (skill.local && skill.localPath && !userSelectedRemote) {
728
- const localSkillPath = path4.join(process.cwd(), skill.localPath);
729
- const contentHash = await generateSkillHash(localSkillPath);
730
- result = {
731
- skillId: skill.id,
732
- sourcePath: skill.localPath,
733
- destPath: skill.localPath,
734
- contentHash,
735
- local: true
736
- };
737
- } else {
738
- result = await copySkillFromSource(skill, pluginDir, sourceResult);
739
- }
740
- completed++;
741
- onProgress?.(completed, total);
742
- return result;
743
- })
744
- );
745
- return results.filter((r) => r !== null);
746
- }
747
693
  function getFlattenedSkillDestPath(skill, localSkillsDir) {
748
694
  return resolveSkillPath(localSkillsDir, skill.id);
749
695
  }
@@ -1840,106 +1786,20 @@ async function findPluginManifest(startDir) {
1840
1786
  init_esm_shims();
1841
1787
  import path9 from "path";
1842
1788
  import { last, zip } from "remeda";
1843
- var MAX_SKILL_NAME_LENGTH = 100;
1844
- function getCollectivePluginDir(projectDir) {
1845
- const dir = projectDir ?? process.cwd();
1846
- return path9.join(dir, CLAUDE_DIR, PLUGINS_SUBDIR, DEFAULT_PLUGIN_NAME);
1847
- }
1848
1789
  function getProjectPluginsDir(projectDir) {
1849
1790
  const dir = projectDir ?? process.cwd();
1850
1791
  return path9.join(dir, CLAUDE_DIR, PLUGINS_SUBDIR);
1851
1792
  }
1852
- function getPluginSkillsDir(pluginDir) {
1853
- return path9.join(pluginDir, "skills");
1854
- }
1855
1793
  function getPluginAgentsDir(pluginDir) {
1856
1794
  return path9.join(pluginDir, "agents");
1857
1795
  }
1858
1796
  function getPluginManifestPath(pluginDir) {
1859
1797
  return path9.join(pluginDir, PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE);
1860
1798
  }
1861
- async function readPluginManifest(pluginDir) {
1862
- const manifestPath = getPluginManifestPath(pluginDir);
1863
- if (!await fileExists(manifestPath)) {
1864
- verbose(` No manifest at ${manifestPath}`);
1865
- return null;
1866
- }
1867
- try {
1868
- const content = await readFileSafe(manifestPath, MAX_PLUGIN_FILE_SIZE);
1869
- const manifest = pluginManifestSchema.parse(JSON.parse(content));
1870
- if (!manifest.name || typeof manifest.name !== "string") {
1871
- verbose(` Invalid manifest at ${manifestPath}: missing name`);
1872
- return null;
1873
- }
1874
- return manifest;
1875
- } catch (error) {
1876
- verbose(` Failed to parse manifest at ${manifestPath}: ${error}`);
1877
- return null;
1878
- }
1879
- }
1880
- async function getPluginSkillIds(pluginSkillsDir, matrix) {
1881
- const skillFiles = await glob("**/SKILL.md", pluginSkillsDir);
1882
- const skillIds = [];
1883
- const aliasToId = /* @__PURE__ */ new Map();
1884
- for (const [id, skill] of typedEntries(matrix.skills)) {
1885
- if (!skill) continue;
1886
- if (skill.displayName) {
1887
- aliasToId.set(skill.displayName.toLowerCase(), id);
1888
- }
1889
- }
1890
- const dirToId = /* @__PURE__ */ new Map();
1891
- for (const [id] of typedEntries(matrix.skills)) {
1892
- const idParts = id.split("/");
1893
- const lastPart = last(idParts);
1894
- if (lastPart) {
1895
- dirToId.set(lastPart.toLowerCase(), id);
1896
- }
1897
- }
1898
- const fileContents = await Promise.all(
1899
- skillFiles.map((skillFile) => readFile(path9.join(pluginSkillsDir, skillFile)))
1900
- );
1901
- for (const [skillFile, content] of zip(skillFiles, fileContents)) {
1902
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
1903
- if (frontmatterMatch) {
1904
- const frontmatter = frontmatterMatch[1];
1905
- const nameMatch = frontmatter.match(/^name:\s*["']?(.+?)["']?\s*$/m);
1906
- if (nameMatch) {
1907
- const skillName = nameMatch[1].trim();
1908
- if (skillName.length === 0) {
1909
- warn(`Skipping plugin skill '${skillFile}': empty name in frontmatter`);
1910
- continue;
1911
- }
1912
- if (skillName.length > MAX_SKILL_NAME_LENGTH) {
1913
- warn(
1914
- `Skipping plugin skill '${skillFile}': name exceeds ${MAX_SKILL_NAME_LENGTH} characters`
1915
- );
1916
- continue;
1917
- }
1918
- if (matrix.skills[skillName]) {
1919
- skillIds.push(skillName);
1920
- continue;
1921
- }
1922
- const skillId2 = aliasToId.get(skillName.toLowerCase());
1923
- if (skillId2) {
1924
- skillIds.push(skillId2);
1925
- continue;
1926
- }
1927
- }
1928
- }
1929
- const dirPath = path9.dirname(skillFile);
1930
- const dirName = path9.basename(dirPath);
1931
- const skillId = dirToId.get(dirName.toLowerCase());
1932
- if (skillId) {
1933
- skillIds.push(skillId);
1934
- }
1935
- }
1936
- return skillIds;
1937
- }
1938
1799
 
1939
1800
  // src/cli/lib/plugins/plugin-info.ts
1940
1801
  init_esm_shims();
1941
1802
  import { readdir } from "fs/promises";
1942
- import { countBy } from "remeda";
1943
1803
 
1944
1804
  // src/cli/lib/installation/index.ts
1945
1805
  init_esm_shims();
@@ -1951,36 +1811,32 @@ async function detectInstallation(projectDir = process.cwd()) {
1951
1811
  const srcConfigPath = path10.join(projectDir, CLAUDE_SRC_DIR, STANDARD_FILES.CONFIG_YAML);
1952
1812
  const legacyConfigPath = path10.join(projectDir, CLAUDE_DIR, STANDARD_FILES.CONFIG_YAML);
1953
1813
  const localConfigPath = await fileExists(srcConfigPath) ? srcConfigPath : await fileExists(legacyConfigPath) ? legacyConfigPath : null;
1954
- if (localConfigPath) {
1955
- const loaded = await loadProjectConfig(projectDir);
1956
- const mode = loaded?.config?.installMode ?? "local";
1957
- if (mode === "local") {
1958
- return {
1959
- mode: "local",
1960
- configPath: localConfigPath,
1961
- agentsDir: path10.join(projectDir, CLAUDE_DIR, "agents"),
1962
- skillsDir: path10.join(projectDir, CLAUDE_DIR, "skills"),
1963
- projectDir
1964
- };
1965
- }
1814
+ if (!localConfigPath) {
1815
+ return null;
1966
1816
  }
1967
- const pluginDir = getCollectivePluginDir(projectDir);
1968
- const pluginConfigPath = path10.join(pluginDir, STANDARD_FILES.CONFIG_YAML);
1969
- if (await directoryExists(pluginDir)) {
1817
+ const loaded = await loadProjectConfig(projectDir);
1818
+ const mode = loaded?.config?.installMode ?? "local";
1819
+ if (mode === "local") {
1970
1820
  return {
1971
- mode: "plugin",
1972
- configPath: pluginConfigPath,
1973
- agentsDir: path10.join(pluginDir, "agents"),
1974
- skillsDir: path10.join(pluginDir, "skills"),
1821
+ mode: "local",
1822
+ configPath: localConfigPath,
1823
+ agentsDir: path10.join(projectDir, CLAUDE_DIR, "agents"),
1824
+ skillsDir: path10.join(projectDir, CLAUDE_DIR, "skills"),
1975
1825
  projectDir
1976
1826
  };
1977
1827
  }
1978
- return null;
1828
+ return {
1829
+ mode: "plugin",
1830
+ configPath: localConfigPath,
1831
+ agentsDir: path10.join(projectDir, CLAUDE_DIR, "agents"),
1832
+ skillsDir: path10.join(projectDir, CLAUDE_DIR, "plugins"),
1833
+ projectDir
1834
+ };
1979
1835
  }
1980
1836
 
1981
1837
  // src/cli/lib/installation/local-installer.ts
1982
1838
  init_esm_shims();
1983
- import path15 from "path";
1839
+ import path14 from "path";
1984
1840
  import { stringify as stringifyYaml3 } from "yaml";
1985
1841
 
1986
1842
  // src/cli/lib/stacks/index.ts
@@ -2078,11 +1934,6 @@ function getStackSkillIds(stack) {
2078
1934
  );
2079
1935
  }
2080
1936
 
2081
- // src/cli/lib/stacks/stack-installer.ts
2082
- init_esm_shims();
2083
- import os from "os";
2084
- import path14 from "path";
2085
-
2086
1937
  // src/cli/lib/stacks/stack-plugin-compiler.ts
2087
1938
  init_esm_shims();
2088
1939
  import path13 from "path";
@@ -2528,265 +2379,18 @@ Stack plugin compiled: ${result.stackName}`);
2528
2379
  }
2529
2380
  }
2530
2381
 
2531
- // src/cli/utils/exec.ts
2532
- init_esm_shims();
2533
- import { spawn } from "child_process";
2534
- var MAX_PLUGIN_PATH_LENGTH = 1024;
2535
- var MAX_GITHUB_REPO_LENGTH = 256;
2536
- var MAX_MARKETPLACE_NAME_LENGTH = 128;
2537
- var MAX_PLUGIN_NAME_LENGTH = 256;
2538
- var GITHUB_REPO_PATTERN = /^[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+$/;
2539
- var SAFE_NAME_PATTERN = /^[a-zA-Z0-9._@/-]+$/;
2540
- var SAFE_PLUGIN_PATH_PATTERN = /^[a-zA-Z0-9._@/:~-]+$/;
2541
- var CONTROL_CHAR_PATTERN = /[\x00-\x08\x0E-\x1F\x7F]/u;
2542
- function validatePluginPath(pluginPath) {
2543
- if (!pluginPath || pluginPath.trim().length === 0) {
2544
- throw new Error("Plugin path must not be empty.");
2545
- }
2546
- if (pluginPath.length > MAX_PLUGIN_PATH_LENGTH) {
2547
- throw new Error(
2548
- `Plugin path is too long (${pluginPath.length} characters, max ${MAX_PLUGIN_PATH_LENGTH}).`
2549
- );
2550
- }
2551
- if (CONTROL_CHAR_PATTERN.test(pluginPath)) {
2552
- throw new Error("Plugin path contains invalid control characters.");
2553
- }
2554
- if (!SAFE_PLUGIN_PATH_PATTERN.test(pluginPath)) {
2555
- throw new Error(
2556
- `Plugin path contains invalid characters: "${pluginPath}"
2557
- Plugin paths may only contain alphanumeric characters, dashes, underscores, dots, slashes, @, and colons.`
2558
- );
2559
- }
2560
- }
2561
- function validateGithubRepo(githubRepo) {
2562
- if (!githubRepo || githubRepo.trim().length === 0) {
2563
- throw new Error("GitHub repository must not be empty.");
2564
- }
2565
- if (githubRepo.length > MAX_GITHUB_REPO_LENGTH) {
2566
- throw new Error(
2567
- `GitHub repository is too long (${githubRepo.length} characters, max ${MAX_GITHUB_REPO_LENGTH}).`
2568
- );
2569
- }
2570
- if (CONTROL_CHAR_PATTERN.test(githubRepo)) {
2571
- throw new Error("GitHub repository contains invalid control characters.");
2572
- }
2573
- if (!GITHUB_REPO_PATTERN.test(githubRepo)) {
2574
- throw new Error(
2575
- `Invalid GitHub repository format: "${githubRepo}"
2576
- Expected format: owner/repo (e.g., 'my-org/my-skills').`
2577
- );
2578
- }
2579
- }
2580
- function validateMarketplaceName(name) {
2581
- if (!name || name.trim().length === 0) {
2582
- throw new Error("Marketplace name must not be empty.");
2583
- }
2584
- if (name.length > MAX_MARKETPLACE_NAME_LENGTH) {
2585
- throw new Error(
2586
- `Marketplace name is too long (${name.length} characters, max ${MAX_MARKETPLACE_NAME_LENGTH}).`
2587
- );
2588
- }
2589
- if (CONTROL_CHAR_PATTERN.test(name)) {
2590
- throw new Error("Marketplace name contains invalid control characters.");
2591
- }
2592
- if (!SAFE_NAME_PATTERN.test(name)) {
2593
- throw new Error(
2594
- `Marketplace name contains invalid characters: "${name}"
2595
- Names may only contain alphanumeric characters, dashes, underscores, dots, @, and slashes.`
2596
- );
2597
- }
2598
- }
2599
- function validatePluginName(pluginName) {
2600
- if (!pluginName || pluginName.trim().length === 0) {
2601
- throw new Error("Plugin name must not be empty.");
2602
- }
2603
- if (pluginName.length > MAX_PLUGIN_NAME_LENGTH) {
2604
- throw new Error(
2605
- `Plugin name is too long (${pluginName.length} characters, max ${MAX_PLUGIN_NAME_LENGTH}).`
2606
- );
2607
- }
2608
- if (CONTROL_CHAR_PATTERN.test(pluginName)) {
2609
- throw new Error("Plugin name contains invalid control characters.");
2610
- }
2611
- if (!SAFE_NAME_PATTERN.test(pluginName)) {
2612
- throw new Error(
2613
- `Plugin name contains invalid characters: "${pluginName}"
2614
- Names may only contain alphanumeric characters, dashes, underscores, dots, @, and slashes.`
2615
- );
2616
- }
2617
- }
2618
- async function execCommand(command, args, options) {
2619
- return new Promise((resolve, reject) => {
2620
- const proc = spawn(command, args, {
2621
- cwd: options?.cwd,
2622
- env: { ...process.env, ...options?.env },
2623
- stdio: ["ignore", "pipe", "pipe"]
2624
- });
2625
- let stdout = "";
2626
- let stderr = "";
2627
- proc.stdout.on("data", (data) => {
2628
- stdout += data.toString();
2629
- });
2630
- proc.stderr.on("data", (data) => {
2631
- stderr += data.toString();
2632
- });
2633
- proc.on("close", (code) => {
2634
- resolve({
2635
- stdout,
2636
- stderr,
2637
- exitCode: code ?? 1
2638
- });
2639
- });
2640
- proc.on("error", (err) => {
2641
- reject(err);
2642
- });
2643
- });
2644
- }
2645
- async function claudePluginInstall(pluginPath, scope, projectDir) {
2646
- validatePluginPath(pluginPath);
2647
- const args = ["plugin", "install", pluginPath, "--scope", scope];
2648
- const result = await execCommand("claude", args, { cwd: projectDir });
2649
- if (result.exitCode !== 0) {
2650
- const errorMessage = result.stderr || result.stdout || "Unknown error";
2651
- throw new Error(`Plugin installation failed: ${errorMessage.trim()}`);
2652
- }
2653
- }
2654
- async function isClaudeCLIAvailable() {
2655
- try {
2656
- const result = await execCommand("claude", ["--version"], {});
2657
- return result.exitCode === 0;
2658
- } catch {
2659
- return false;
2660
- }
2661
- }
2662
- async function claudePluginMarketplaceList() {
2663
- try {
2664
- const result = await execCommand("claude", ["plugin", "marketplace", "list", "--json"], {});
2665
- if (result.exitCode !== 0) {
2666
- return [];
2667
- }
2668
- let parsed;
2669
- try {
2670
- parsed = JSON.parse(result.stdout);
2671
- } catch {
2672
- warn("Failed to parse marketplace list output as JSON");
2673
- return [];
2674
- }
2675
- if (!Array.isArray(parsed)) {
2676
- warn("Unexpected marketplace list format \u2014 expected an array");
2677
- return [];
2678
- }
2679
- return parsed;
2680
- } catch {
2681
- return [];
2682
- }
2683
- }
2684
- async function claudePluginMarketplaceExists(name) {
2685
- const marketplaces = await claudePluginMarketplaceList();
2686
- return marketplaces.some((m) => m.name === name);
2687
- }
2688
- async function claudePluginMarketplaceAdd(githubRepo, name) {
2689
- validateGithubRepo(githubRepo);
2690
- validateMarketplaceName(name);
2691
- const args = ["plugin", "marketplace", "add", githubRepo, "--name", name];
2692
- let result;
2693
- try {
2694
- result = await execCommand("claude", args, {});
2695
- } catch (err) {
2696
- throw new Error(`Failed to add marketplace: ${getErrorMessage(err)}`);
2697
- }
2698
- if (result.exitCode !== 0) {
2699
- const errorMessage = result.stderr || result.stdout || "Unknown error";
2700
- if (errorMessage.includes("already installed")) {
2701
- return;
2702
- }
2703
- throw new Error(`Failed to add marketplace: ${errorMessage.trim()}`);
2704
- }
2705
- }
2706
- async function claudePluginUninstall(pluginName, scope, projectDir) {
2707
- validatePluginName(pluginName);
2708
- const args = ["plugin", "uninstall", pluginName, "--scope", scope];
2709
- const result = await execCommand("claude", args, { cwd: projectDir });
2710
- if (result.exitCode !== 0) {
2711
- const errorMessage = result.stderr || result.stdout || "Unknown error";
2712
- if (errorMessage.includes("not installed") || errorMessage.includes("not found")) {
2713
- return;
2714
- }
2715
- throw new Error(`Plugin uninstall failed: ${errorMessage.trim()}`);
2716
- }
2717
- }
2718
-
2719
- // src/cli/lib/stacks/stack-installer.ts
2720
- async function compileStackToTemp(options) {
2721
- const tempDir = path14.join(os.tmpdir(), `cc-stack-${Date.now()}`);
2722
- await ensureDir(tempDir);
2723
- const result = await compileStackPlugin({
2724
- stackId: options.stackId,
2725
- outputDir: tempDir,
2726
- projectRoot: options.projectRoot,
2727
- agentSourcePath: options.agentSourcePath
2728
- });
2729
- return {
2730
- result,
2731
- cleanup: async () => {
2732
- await remove(tempDir);
2733
- }
2734
- };
2735
- }
2736
- async function installStackAsPlugin(options) {
2737
- const { stackId, projectDir, sourcePath, agentSourcePath, marketplace } = options;
2738
- const claudeAvailable = await isClaudeCLIAvailable();
2739
- if (!claudeAvailable) {
2740
- throw new Error(
2741
- "Claude CLI not found. Please install Claude Code first: https://claude.ai/code"
2742
- );
2743
- }
2744
- if (marketplace) {
2745
- verbose(`Installing from marketplace: ${stackId}@${marketplace}`);
2746
- const pluginRef = `${stackId}@${marketplace}`;
2747
- await claudePluginInstall(pluginRef, "project", projectDir);
2748
- return {
2749
- pluginName: stackId,
2750
- stackName: stackId,
2751
- agents: [],
2752
- skills: [],
2753
- pluginPath: pluginRef,
2754
- fromMarketplace: true
2755
- };
2756
- }
2757
- verbose(`Compiling stack locally: ${stackId}`);
2758
- const { result, cleanup } = await compileStackToTemp({
2759
- stackId,
2760
- projectRoot: sourcePath,
2761
- agentSourcePath
2762
- });
2763
- try {
2764
- await claudePluginInstall(result.pluginPath, "project", projectDir);
2765
- return {
2766
- pluginName: `stack-${stackId}`,
2767
- stackName: result.stackName,
2768
- agents: result.agents,
2769
- skills: result.skillPlugins,
2770
- pluginPath: result.pluginPath,
2771
- fromMarketplace: false
2772
- };
2773
- } finally {
2774
- await cleanup();
2775
- }
2776
- }
2777
-
2778
2382
  // src/cli/lib/installation/local-installer.ts
2779
2383
  function resolveInstallPaths(projectDir) {
2780
2384
  return {
2781
- skillsDir: path15.join(projectDir, LOCAL_SKILLS_PATH),
2782
- agentsDir: path15.join(projectDir, CLAUDE_DIR, "agents"),
2783
- configPath: path15.join(projectDir, CLAUDE_SRC_DIR, STANDARD_FILES.CONFIG_YAML)
2385
+ skillsDir: path14.join(projectDir, LOCAL_SKILLS_PATH),
2386
+ agentsDir: path14.join(projectDir, CLAUDE_DIR, "agents"),
2387
+ configPath: path14.join(projectDir, CLAUDE_SRC_DIR, STANDARD_FILES.CONFIG_YAML)
2784
2388
  };
2785
2389
  }
2786
2390
  async function prepareDirectories(paths) {
2787
2391
  await ensureDir(paths.skillsDir);
2788
2392
  await ensureDir(paths.agentsDir);
2789
- await ensureDir(path15.dirname(paths.configPath));
2393
+ await ensureDir(path14.dirname(paths.configPath));
2790
2394
  }
2791
2395
  async function archiveAndCopySkills(wizardResult, sourceResult, projectDir, skillsDir) {
2792
2396
  for (const skillId of wizardResult.selectedSkills) {
@@ -2915,11 +2519,49 @@ async function compileAndWriteAgents(compileConfig, agents, localSkills, sourceR
2915
2519
  engine,
2916
2520
  installMode
2917
2521
  );
2918
- await writeFile(path15.join(agentsDir, `${name}.md`), output);
2522
+ await writeFile(path14.join(agentsDir, `${name}.md`), output);
2919
2523
  compiledAgentNames.push(name);
2920
2524
  }
2921
2525
  return compiledAgentNames;
2922
2526
  }
2527
+ async function installPluginConfig(options) {
2528
+ const { wizardResult, sourceResult, projectDir, sourceFlag } = options;
2529
+ const paths = resolveInstallPaths(projectDir);
2530
+ await ensureDir(paths.agentsDir);
2531
+ await ensureDir(path14.dirname(paths.configPath));
2532
+ const agents = await loadMergedAgents(sourceResult.sourcePath);
2533
+ const mergeResult = await buildAndMergeConfig(wizardResult, sourceResult, projectDir, sourceFlag);
2534
+ const finalConfig = mergeResult.config;
2535
+ await writeConfigFile(finalConfig, paths.configPath);
2536
+ const compileAgentsConfig = buildCompileAgents(finalConfig, agents);
2537
+ const compileConfig = {
2538
+ name: DEFAULT_PLUGIN_NAME,
2539
+ description: finalConfig.description || `Plugin setup with ${wizardResult.selectedSkills.length} skills`,
2540
+ agents: compileAgentsConfig
2541
+ };
2542
+ const stackSkillIds = finalConfig.stack ? getStackSkillIds(finalConfig.stack) : [];
2543
+ const skillsForCompilation = await loadSkillsByIds(
2544
+ stackSkillIds.map((id) => ({ id })),
2545
+ sourceResult.sourcePath
2546
+ );
2547
+ const compiledAgentNames = await compileAndWriteAgents(
2548
+ compileConfig,
2549
+ agents,
2550
+ skillsForCompilation,
2551
+ sourceResult,
2552
+ projectDir,
2553
+ paths.agentsDir,
2554
+ wizardResult.installMode
2555
+ );
2556
+ return {
2557
+ config: finalConfig,
2558
+ configPath: paths.configPath,
2559
+ compiledAgents: compiledAgentNames,
2560
+ wasMerged: mergeResult.merged,
2561
+ mergedConfigPath: mergeResult.existingConfigPath,
2562
+ agentsDir: paths.agentsDir
2563
+ };
2564
+ }
2923
2565
  async function installLocal(options) {
2924
2566
  const { wizardResult, sourceResult, projectDir, sourceFlag } = options;
2925
2567
  const paths = resolveInstallPaths(projectDir);
@@ -2962,6 +2604,174 @@ async function installLocal(options) {
2962
2604
  };
2963
2605
  }
2964
2606
 
2607
+ // src/cli/lib/plugins/plugin-discovery.ts
2608
+ init_esm_shims();
2609
+
2610
+ // src/cli/lib/plugins/plugin-settings.ts
2611
+ init_esm_shims();
2612
+ import path15 from "path";
2613
+ import os from "os";
2614
+ import { z as z2 } from "zod";
2615
+ var pluginSettingsSchema = z2.object({
2616
+ enabledPlugins: z2.record(z2.string(), z2.unknown()).optional()
2617
+ }).passthrough();
2618
+ var pluginInstallationSchema = z2.object({
2619
+ scope: z2.enum(["user", "project", "local"]),
2620
+ projectPath: z2.string().optional(),
2621
+ installPath: z2.string(),
2622
+ version: z2.string(),
2623
+ installedAt: z2.string(),
2624
+ lastUpdated: z2.string().optional(),
2625
+ gitCommitSha: z2.string().optional()
2626
+ });
2627
+ var installedPluginsSchema = z2.object({
2628
+ version: z2.number(),
2629
+ plugins: z2.record(z2.string(), z2.array(pluginInstallationSchema))
2630
+ }).passthrough();
2631
+ var SETTINGS_FILE = "settings.json";
2632
+ var INSTALLED_PLUGINS_FILE = "installed_plugins.json";
2633
+ async function getEnabledPluginKeys(projectDir) {
2634
+ const settingsPath = path15.join(projectDir, CLAUDE_DIR, SETTINGS_FILE);
2635
+ if (!await fileExists(settingsPath)) {
2636
+ verbose(`No settings.json found at '${settingsPath}'`);
2637
+ return [];
2638
+ }
2639
+ try {
2640
+ const content = await readFileSafe(settingsPath, MAX_CONFIG_FILE_SIZE);
2641
+ const raw = JSON.parse(content);
2642
+ const result = pluginSettingsSchema.safeParse(raw);
2643
+ if (!result.success) {
2644
+ verbose(`Invalid settings.json structure: ${getErrorMessage(result.error)}`);
2645
+ return [];
2646
+ }
2647
+ const settings = result.data;
2648
+ if (!settings.enabledPlugins) {
2649
+ verbose(`No enabledPlugins found in '${settingsPath}'`);
2650
+ return [];
2651
+ }
2652
+ const enabledKeys = typedEntries(settings.enabledPlugins).filter(([, enabled]) => enabled === true).map(([key]) => key);
2653
+ verbose(`Found ${enabledKeys.length} enabled plugins in settings.json`);
2654
+ return enabledKeys;
2655
+ } catch (error) {
2656
+ verbose(`Failed to read settings.json: ${getErrorMessage(error)}`);
2657
+ return [];
2658
+ }
2659
+ }
2660
+ async function resolvePluginInstallPaths(pluginKeys, projectDir) {
2661
+ if (pluginKeys.length === 0) {
2662
+ return [];
2663
+ }
2664
+ const registryPath = path15.join(os.homedir(), CLAUDE_DIR, PLUGINS_SUBDIR, INSTALLED_PLUGINS_FILE);
2665
+ if (!await fileExists(registryPath)) {
2666
+ verbose(`Plugin registry not found at '${registryPath}'`);
2667
+ return [];
2668
+ }
2669
+ try {
2670
+ const content = await readFileSafe(registryPath, MAX_CONFIG_FILE_SIZE);
2671
+ const raw = JSON.parse(content);
2672
+ const result = installedPluginsSchema.safeParse(raw);
2673
+ if (!result.success) {
2674
+ verbose(`Invalid plugin registry structure: ${getErrorMessage(result.error)}`);
2675
+ return [];
2676
+ }
2677
+ const registry = result.data;
2678
+ const resolvedPaths = [];
2679
+ for (const pluginKey of pluginKeys) {
2680
+ const installations = registry.plugins[pluginKey];
2681
+ if (!installations || installations.length === 0) {
2682
+ verbose(`Plugin '${pluginKey}' not found in registry`);
2683
+ continue;
2684
+ }
2685
+ const projectInstall = installations.find(
2686
+ (install) => install.scope === "project" && install.projectPath === projectDir
2687
+ );
2688
+ if (projectInstall) {
2689
+ resolvedPaths.push({
2690
+ pluginKey,
2691
+ installPath: projectInstall.installPath
2692
+ });
2693
+ verbose(`Resolved '${pluginKey}' to '${projectInstall.installPath}'`);
2694
+ continue;
2695
+ }
2696
+ const userInstall = installations.find((install) => install.scope === "user");
2697
+ if (userInstall) {
2698
+ resolvedPaths.push({
2699
+ pluginKey,
2700
+ installPath: userInstall.installPath
2701
+ });
2702
+ verbose(`Resolved '${pluginKey}' to '${userInstall.installPath}' (user scope)`);
2703
+ continue;
2704
+ }
2705
+ verbose(`No matching installation found for '${pluginKey}'`);
2706
+ }
2707
+ return resolvedPaths;
2708
+ } catch (error) {
2709
+ verbose(`Failed to read plugin registry: ${getErrorMessage(error)}`);
2710
+ return [];
2711
+ }
2712
+ }
2713
+ async function getVerifiedPluginInstallPaths(projectDir) {
2714
+ const enabledKeys = await getEnabledPluginKeys(projectDir);
2715
+ const resolvedPaths = await resolvePluginInstallPaths(enabledKeys, projectDir);
2716
+ const verified = [];
2717
+ for (const { pluginKey, installPath } of resolvedPaths) {
2718
+ const pluginJsonPath = path15.join(installPath, PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE);
2719
+ if (await fileExists(pluginJsonPath)) {
2720
+ verified.push({ pluginKey, installPath });
2721
+ } else {
2722
+ verbose(`Plugin '${pluginKey}' manifest does not exist at: '${pluginJsonPath}'`);
2723
+ }
2724
+ }
2725
+ verbose(`Verified ${verified.length} plugin install paths`);
2726
+ return verified;
2727
+ }
2728
+
2729
+ // src/cli/lib/plugins/plugin-discovery.ts
2730
+ async function discoverAllPluginSkills(projectDir) {
2731
+ const allSkills = {};
2732
+ try {
2733
+ const pluginPaths = await getVerifiedPluginInstallPaths(projectDir);
2734
+ if (pluginPaths.length === 0) {
2735
+ verbose(`No enabled plugins found in settings.json`);
2736
+ return allSkills;
2737
+ }
2738
+ for (const { pluginKey, installPath } of pluginPaths) {
2739
+ verbose(`Discovering skills from plugin: '${pluginKey}'`);
2740
+ try {
2741
+ const pluginSkills = await loadPluginSkills(installPath);
2742
+ for (const [id, skill] of typedEntries(pluginSkills)) {
2743
+ if (skill) {
2744
+ allSkills[id] = skill;
2745
+ }
2746
+ }
2747
+ } catch (error) {
2748
+ verbose(`Failed to load skills from '${pluginKey}': ${getErrorMessage(error)}`);
2749
+ }
2750
+ }
2751
+ } catch (error) {
2752
+ verbose(`Plugin discovery failed: ${getErrorMessage(error)}`);
2753
+ }
2754
+ return allSkills;
2755
+ }
2756
+ async function hasIndividualPlugins(projectDir) {
2757
+ try {
2758
+ const pluginPaths = await getVerifiedPluginInstallPaths(projectDir);
2759
+ return pluginPaths.length > 0;
2760
+ } catch (error) {
2761
+ verbose(`Failed to check for individual plugins: ${getErrorMessage(error)}`);
2762
+ return false;
2763
+ }
2764
+ }
2765
+ async function listPluginNames(projectDir) {
2766
+ try {
2767
+ const pluginPaths = await getVerifiedPluginInstallPaths(projectDir);
2768
+ return pluginPaths.map(({ pluginKey }) => pluginKey);
2769
+ } catch (error) {
2770
+ verbose(`Failed to list plugin names: ${getErrorMessage(error)}`);
2771
+ return [];
2772
+ }
2773
+ }
2774
+
2965
2775
  // src/cli/lib/plugins/plugin-info.ts
2966
2776
  async function getInstallationInfo() {
2967
2777
  const installation = await detectInstallation();
@@ -2972,7 +2782,13 @@ async function getInstallationInfo() {
2972
2782
  let agentCount = 0;
2973
2783
  let name = DEFAULT_PLUGIN_NAME;
2974
2784
  let version = DEFAULT_DISPLAY_VERSION;
2975
- if (await directoryExists(installation.skillsDir)) {
2785
+ if (installation.mode === "plugin") {
2786
+ try {
2787
+ const pluginSkills = await discoverAllPluginSkills(installation.projectDir);
2788
+ skillCount = Object.keys(pluginSkills).length;
2789
+ } catch {
2790
+ }
2791
+ } else if (await directoryExists(installation.skillsDir)) {
2976
2792
  try {
2977
2793
  const skills = await readdir(installation.skillsDir, {
2978
2794
  withFileTypes: true
@@ -2990,19 +2806,10 @@ async function getInstallationInfo() {
2990
2806
  } catch {
2991
2807
  }
2992
2808
  }
2993
- if (installation.mode === "local") {
2994
- const loaded = await loadProjectConfig(installation.projectDir);
2995
- if (loaded?.config) {
2996
- name = loaded.config.name || DEFAULT_PLUGIN_NAME;
2997
- version = "local";
2998
- }
2999
- } else {
3000
- const pluginDir = getCollectivePluginDir(installation.projectDir);
3001
- const manifest = await readPluginManifest(pluginDir);
3002
- if (manifest) {
3003
- name = manifest.name || DEFAULT_PLUGIN_NAME;
3004
- version = manifest.version || DEFAULT_DISPLAY_VERSION;
3005
- }
2809
+ const loaded = await loadProjectConfig(installation.projectDir);
2810
+ if (loaded?.config) {
2811
+ name = loaded.config.name || DEFAULT_PLUGIN_NAME;
2812
+ version = installation.mode === "local" ? "local" : "plugin";
3006
2813
  }
3007
2814
  return {
3008
2815
  mode: installation.mode,
@@ -3067,24 +2874,24 @@ async function getPluginVersion(pluginDir) {
3067
2874
 
3068
2875
  // src/cli/lib/plugins/plugin-validator.ts
3069
2876
  init_esm_shims();
3070
- import { z as z2 } from "zod";
2877
+ import { z as z3 } from "zod";
3071
2878
  import path17 from "path";
3072
2879
  import fg from "fast-glob";
3073
- import { countBy as countBy2 } from "remeda";
2880
+ import { countBy } from "remeda";
3074
2881
  var PLUGIN_DIR = PLUGIN_MANIFEST_DIR;
3075
2882
  var PLUGIN_MANIFEST = STANDARD_FILES.PLUGIN_JSON;
3076
2883
  var KEBAB_CASE_REGEX = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
3077
2884
  var SEMVER_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
3078
- var pluginManifestValidationSchema = z2.object({
3079
- name: z2.string(),
3080
- version: z2.string().optional(),
3081
- description: z2.string().optional(),
2885
+ var pluginManifestValidationSchema = z3.object({
2886
+ name: z3.string(),
2887
+ version: z3.string().optional(),
2888
+ description: z3.string().optional(),
3082
2889
  author: pluginAuthorSchema.optional(),
3083
- keywords: z2.array(z2.string()).optional(),
3084
- commands: z2.union([z2.string(), z2.array(z2.string())]).optional(),
3085
- agents: z2.union([z2.string(), z2.array(z2.string())]).optional(),
3086
- skills: z2.union([z2.string(), z2.array(z2.string())]).optional(),
3087
- hooks: z2.union([z2.string(), hooksRecordSchema]).optional()
2890
+ keywords: z3.array(z3.string()).optional(),
2891
+ commands: z3.union([z3.string(), z3.array(z3.string())]).optional(),
2892
+ agents: z3.union([z3.string(), z3.array(z3.string())]).optional(),
2893
+ skills: z3.union([z3.string(), z3.array(z3.string())]).optional(),
2894
+ hooks: z3.union([z3.string(), hooksRecordSchema]).optional()
3088
2895
  }).strict();
3089
2896
  function formatZodErrors2(error) {
3090
2897
  return error.issues.map((issue) => {
@@ -3370,9 +3177,9 @@ async function validateAllPlugins(pluginsDir) {
3370
3177
  }
3371
3178
  const summary = {
3372
3179
  total: results.length,
3373
- valid: countBy2(results, (r) => String(r.result.valid))["true"] ?? 0,
3374
- invalid: countBy2(results, (r) => String(r.result.valid))["false"] ?? 0,
3375
- withWarnings: countBy2(results, (r) => String(r.result.warnings.length > 0))["true"] ?? 0
3180
+ valid: countBy(results, (r) => String(r.result.valid))["true"] ?? 0,
3181
+ invalid: countBy(results, (r) => String(r.result.valid))["false"] ?? 0,
3182
+ withWarnings: countBy(results, (r) => String(r.result.warnings.length > 0))["true"] ?? 0
3376
3183
  };
3377
3184
  return {
3378
3185
  valid: summary.invalid === 0,
@@ -3437,40 +3244,37 @@ function tagLocalSkills(matrix) {
3437
3244
  verbose(`Tagged ${count} local skills with local source`);
3438
3245
  }
3439
3246
  async function tagPluginSkills(matrix, projectDir) {
3440
- const pluginDir = getCollectivePluginDir(projectDir);
3441
- if (!await directoryExists(pluginDir)) {
3442
- verbose("No plugin directory found, skipping plugin skill tagging");
3443
- return;
3444
- }
3445
- const pluginSkillsDir = getPluginSkillsDir(pluginDir);
3446
- if (!await directoryExists(pluginSkillsDir)) {
3447
- verbose("No plugin skills directory found, skipping plugin skill tagging");
3247
+ const allPluginSkillIds = await collectPluginSkillIds(matrix, projectDir);
3248
+ if (allPluginSkillIds.length === 0) {
3448
3249
  return;
3449
3250
  }
3450
- try {
3451
- const pluginSkillIds = await getPluginSkillIds(pluginSkillsDir, matrix);
3452
- for (const skillId of pluginSkillIds) {
3453
- const skill = matrix.skills[skillId];
3454
- if (!skill) continue;
3455
- skill.availableSources = skill.availableSources ?? [];
3456
- const existingSource = skill.availableSources.find((s) => s.type === "public");
3457
- if (existingSource && !existingSource.installMode) {
3458
- existingSource.installed = true;
3459
- existingSource.installMode = "plugin";
3460
- } else if (!skill.availableSources.some((s) => s.installMode === "plugin")) {
3461
- skill.availableSources.push({
3462
- name: PUBLIC_SOURCE_NAME,
3463
- type: "public",
3464
- version: skill.version,
3465
- installed: true,
3466
- installMode: "plugin"
3467
- });
3468
- }
3251
+ for (const skillId of allPluginSkillIds) {
3252
+ const skill = matrix.skills[skillId];
3253
+ if (!skill) continue;
3254
+ skill.availableSources = skill.availableSources ?? [];
3255
+ const existingSource = skill.availableSources.find((s) => s.type === "public");
3256
+ if (existingSource && !existingSource.installMode) {
3257
+ existingSource.installed = true;
3258
+ existingSource.installMode = "plugin";
3259
+ } else if (!skill.availableSources.some((s) => s.installMode === "plugin")) {
3260
+ skill.availableSources.push({
3261
+ name: PUBLIC_SOURCE_NAME,
3262
+ type: "public",
3263
+ version: skill.version,
3264
+ installed: true,
3265
+ installMode: "plugin"
3266
+ });
3469
3267
  }
3470
- verbose(`Tagged ${pluginSkillIds.length} plugin-installed skills`);
3471
- } catch (error) {
3472
- verbose(`Failed to detect plugin skills: ${error}`);
3473
3268
  }
3269
+ verbose(`Tagged ${allPluginSkillIds.length} plugin-installed skills`);
3270
+ }
3271
+ async function collectPluginSkillIds(_matrix, projectDir) {
3272
+ const pluginSkills = await discoverAllPluginSkills(projectDir);
3273
+ const skillIds = Object.keys(pluginSkills);
3274
+ if (skillIds.length === 0) {
3275
+ verbose("No plugin skills discovered from settings.json");
3276
+ }
3277
+ return skillIds;
3474
3278
  }
3475
3279
  async function tagExtraSources(matrix, projectDir) {
3476
3280
  let allSources;
@@ -3598,12 +3402,22 @@ async function loadFromRemote(source, sourceConfig, forceRefresh) {
3598
3402
  const fetchResult = await fetchFromSource(source, { forceRefresh });
3599
3403
  verbose(`Fetched to: ${fetchResult.path}`);
3600
3404
  const mergedMatrix = await loadAndMergeFromBasePath(fetchResult.path);
3405
+ let marketplace = sourceConfig.marketplace;
3406
+ if (!marketplace) {
3407
+ try {
3408
+ const marketplaceResult = await fetchMarketplace(source, { forceRefresh });
3409
+ marketplace = marketplaceResult.marketplace.name;
3410
+ verbose(`Using marketplace name from marketplace.json: ${marketplace}`);
3411
+ } catch {
3412
+ warn(`Source does not have a marketplace.json - falling back to local mode`);
3413
+ }
3414
+ }
3601
3415
  return {
3602
3416
  matrix: mergedMatrix,
3603
3417
  sourceConfig,
3604
3418
  sourcePath: fetchResult.path,
3605
3419
  isLocal: false,
3606
- marketplace: sourceConfig.marketplace
3420
+ marketplace
3607
3421
  };
3608
3422
  }
3609
3423
  async function loadAndMergeFromBasePath(basePath) {
@@ -4385,7 +4199,7 @@ async function saveSourceToProjectConfig(projectDir, source) {
4385
4199
  }
4386
4200
 
4387
4201
  // src/cli/lib/loading/source-fetcher.ts
4388
- var SAFE_NAME_PATTERN2 = /^[a-zA-Z0-9@._/ -]+$/;
4202
+ var SAFE_NAME_PATTERN = /^[a-zA-Z0-9@._/ -]+$/;
4389
4203
  var MAX_NAME_LENGTH = 200;
4390
4204
  function sanitizeSourceForCache(source) {
4391
4205
  const hash = createHash2("sha256").update(source).digest("hex").slice(0, CACHE_HASH_LENGTH);
@@ -4561,7 +4375,7 @@ Too many plugins: ${marketplace.plugins.length} (limit: ${MAX_MARKETPLACE_PLUGIN
4561
4375
  `Marketplace plugin name too long (${plugin.name.length} chars): '${plugin.name.slice(0, 50)}...'`
4562
4376
  );
4563
4377
  }
4564
- if (!SAFE_NAME_PATTERN2.test(plugin.name)) {
4378
+ if (!SAFE_NAME_PATTERN.test(plugin.name)) {
4565
4379
  warn(`Marketplace plugin name contains unsafe characters: '${plugin.name.slice(0, 50)}'`);
4566
4380
  }
4567
4381
  }
@@ -4605,7 +4419,7 @@ async function removeSource(projectDir, name) {
4605
4419
  await saveProjectConfig(projectDir, config);
4606
4420
  verbose(`Removed source "${name}"`);
4607
4421
  }
4608
- async function getSourceSummary(projectDir, matrix) {
4422
+ async function getSourceSummary(projectDir) {
4609
4423
  const config = await loadProjectSourceConfig(projectDir) ?? {};
4610
4424
  const sources = [
4611
4425
  {
@@ -4629,15 +4443,11 @@ async function getSourceSummary(projectDir, matrix) {
4629
4443
  verbose("Failed to discover local skills for source summary");
4630
4444
  }
4631
4445
  let pluginSkillCount = 0;
4632
- if (matrix) {
4633
- try {
4634
- const pluginDir = getCollectivePluginDir(projectDir);
4635
- const pluginSkillsDir = getPluginSkillsDir(pluginDir);
4636
- const skillIds = await getPluginSkillIds(pluginSkillsDir, matrix);
4637
- pluginSkillCount = skillIds.length;
4638
- } catch {
4639
- verbose("Failed to count plugin skills for source summary");
4640
- }
4446
+ try {
4447
+ const discoveredSkills = await discoverAllPluginSkills(projectDir);
4448
+ pluginSkillCount = Object.keys(discoveredSkills).length;
4449
+ } catch {
4450
+ verbose("Failed to discover plugin skills for source summary");
4641
4451
  }
4642
4452
  return { sources, localSkillCount, pluginSkillCount };
4643
4453
  }
@@ -4648,12 +4458,9 @@ export {
4648
4458
  findPluginManifest,
4649
4459
  typedEntries,
4650
4460
  typedKeys,
4651
- getCollectivePluginDir,
4652
4461
  getProjectPluginsDir,
4653
- getPluginSkillsDir,
4654
4462
  getPluginAgentsDir,
4655
4463
  getPluginManifestPath,
4656
- getPluginSkillIds,
4657
4464
  DEFAULT_SOURCE,
4658
4465
  SOURCE_ENV_VAR,
4659
4466
  getProjectConfigPath,
@@ -4674,12 +4481,10 @@ export {
4674
4481
  readForkedFromMetadata,
4675
4482
  compareLocalSkillsWithSource,
4676
4483
  injectForkedFromMetadata,
4677
- copySkillsToPluginFromSource,
4678
4484
  copySkillsToLocalFlattened,
4679
4485
  parseFrontmatter,
4680
4486
  loadAllAgents,
4681
4487
  loadProjectAgents,
4682
- loadPluginSkills,
4683
4488
  resolveAlias,
4684
4489
  validateSelection,
4685
4490
  getAvailableSkills,
@@ -4695,12 +4500,6 @@ export {
4695
4500
  compileAgentForPlugin,
4696
4501
  compileStackPlugin,
4697
4502
  printStackCompilationSummary,
4698
- claudePluginInstall,
4699
- isClaudeCLIAvailable,
4700
- claudePluginMarketplaceExists,
4701
- claudePluginMarketplaceAdd,
4702
- claudePluginUninstall,
4703
- installStackAsPlugin,
4704
4503
  loadSkillsMatrixFromSource,
4705
4504
  getMarketplaceLabel,
4706
4505
  compileSkillPlugin,
@@ -4712,10 +4511,14 @@ export {
4712
4511
  loadProjectConfig,
4713
4512
  validateProjectConfig,
4714
4513
  saveSourceToProjectConfig,
4514
+ discoverAllPluginSkills,
4515
+ hasIndividualPlugins,
4516
+ listPluginNames,
4715
4517
  addSource,
4716
4518
  removeSource,
4717
4519
  getSourceSummary,
4718
4520
  detectInstallation,
4521
+ installPluginConfig,
4719
4522
  installLocal,
4720
4523
  getInstallationInfo,
4721
4524
  formatInstallationDisplay,
@@ -4725,4 +4528,4 @@ export {
4725
4528
  validateAllPlugins,
4726
4529
  printPluginValidationResult
4727
4530
  };
4728
- //# sourceMappingURL=chunk-SSHG7MEE.js.map
4531
+ //# sourceMappingURL=chunk-QUL7R35E.js.map