@ian2018cs/agenthub 0.1.85 → 0.1.86

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ian2018cs/agenthub",
3
- "version": "0.1.85",
3
+ "version": "0.1.86",
4
4
  "description": "A web-based UI for AI Agents",
5
5
  "type": "module",
6
6
  "main": "server/index.js",
@@ -868,28 +868,62 @@ router.post('/install', async (req, res) => {
868
868
 
869
869
  // Remove skills that existed in old version but are no longer in new version
870
870
  const newSkillNames = new Set((agent.skills || []).filter(s => s.name).map(s => s.name));
871
- for (const old of prevAgentInfo?.installedSkills || []) {
872
- if (!newSkillNames.has(old.name)) {
873
- try {
874
- await uninstallSkill(old.name, userUuid, projectDir);
875
- console.log(`[AgentInstall] Removed old skill "${old.name}" (no longer in agent)`);
876
- } catch (e) {
877
- console.warn(`[AgentInstall] Could not remove old skill "${old.name}":`, e.message);
871
+ if (prevAgentInfo?.installedSkills?.length > 0) {
872
+ // Precise diff using recorded data
873
+ for (const old of prevAgentInfo.installedSkills) {
874
+ if (!newSkillNames.has(old.name)) {
875
+ try {
876
+ await uninstallSkill(old.name, userUuid, projectDir);
877
+ console.log(`[AgentInstall] Removed old skill "${old.name}" (no longer in agent)`);
878
+ } catch (e) {
879
+ console.warn(`[AgentInstall] Could not remove old skill "${old.name}":`, e.message);
880
+ }
878
881
  }
879
882
  }
883
+ } else if (prevAgentInfo) {
884
+ // No recorded data — fallback: scan actual skills dir and remove anything not in new agent
885
+ try {
886
+ const skillsDir = path.join(projectDir, '.claude', 'skills');
887
+ const entries = await fs.readdir(skillsDir).catch(() => []);
888
+ for (const name of entries) {
889
+ if (!newSkillNames.has(name)) {
890
+ try {
891
+ await fs.unlink(path.join(skillsDir, name));
892
+ console.log(`[AgentInstall] Removed residual skill "${name}" (fallback cleanup)`);
893
+ } catch (e) {
894
+ console.warn(`[AgentInstall] Could not remove residual skill "${name}":`, e.message);
895
+ }
896
+ }
897
+ }
898
+ } catch {}
880
899
  }
881
900
 
882
901
  // Remove MCPs that existed in old version but are no longer in new version
883
902
  const newMcpNames = new Set((agent.mcps || []).filter(m => m.name).map(m => m.name));
884
- for (const old of prevAgentInfo?.installedMcps || []) {
885
- if (!newMcpNames.has(old.name)) {
886
- try {
887
- await uninstallMcp(old.keys, userUuid, projectDir);
888
- console.log(`[AgentInstall] Removed old MCP "${old.name}" (no longer in agent)`);
889
- } catch (e) {
890
- console.warn(`[AgentInstall] Could not remove old MCP "${old.name}":`, e.message);
903
+ if (prevAgentInfo?.installedMcps?.length > 0) {
904
+ // Precise diff using recorded data (includes exact server keys)
905
+ for (const old of prevAgentInfo.installedMcps) {
906
+ if (!newMcpNames.has(old.name)) {
907
+ try {
908
+ await uninstallMcp(old.keys, userUuid, projectDir);
909
+ console.log(`[AgentInstall] Removed old MCP "${old.name}" (no longer in agent)`);
910
+ } catch (e) {
911
+ console.warn(`[AgentInstall] Could not remove old MCP "${old.name}":`, e.message);
912
+ }
891
913
  }
892
914
  }
915
+ } else if (prevAgentInfo) {
916
+ // No recorded data — fallback: remove ALL project-scoped MCP keys (will be reinstalled)
917
+ try {
918
+ const userPaths = getUserPaths(userUuid);
919
+ const claudeJsonPath = path.join(userPaths.claudeDir, '.claude.json');
920
+ const claudeConfig = JSON.parse(await fs.readFile(claudeJsonPath, 'utf-8'));
921
+ const projectMcpKeys = Object.keys(claudeConfig.projects?.[projectDir]?.mcpServers || {});
922
+ if (projectMcpKeys.length > 0) {
923
+ await uninstallMcp(projectMcpKeys, userUuid, projectDir);
924
+ console.log(`[AgentInstall] Removed ${projectMcpKeys.length} residual project MCPs (fallback cleanup)`);
925
+ }
926
+ } catch {}
893
927
  }
894
928
 
895
929
  // Install skills
@@ -1853,18 +1887,37 @@ router.post('/submissions/:id/approve', async (req, res) => {
1853
1887
 
1854
1888
  // Update skill dependencies
1855
1889
  const newSkills = (publishedAgent.skills || []).filter(s => s.name && s.repo);
1856
- const oldSkills = entry.agentInfo.installedSkills || [];
1890
+ const oldSkills = entry.agentInfo.installedSkills;
1857
1891
  const newSkillNames = new Set(newSkills.map(s => s.name));
1858
1892
 
1859
- for (const old of oldSkills) {
1860
- if (!newSkillNames.has(old.name)) {
1861
- try {
1862
- await uninstallSkill(old.name, user.uuid, projectDir);
1863
- console.log(`[AgentApprove] Removed old skill "${old.name}" for user ${user.uuid}`);
1864
- } catch (e) {
1865
- console.warn(`[AgentApprove] Could not remove old skill "${old.name}" for user ${user.uuid}:`, e.message);
1893
+ if (oldSkills?.length > 0) {
1894
+ // Precise diff using recorded data
1895
+ for (const old of oldSkills) {
1896
+ if (!newSkillNames.has(old.name)) {
1897
+ try {
1898
+ await uninstallSkill(old.name, user.uuid, projectDir);
1899
+ console.log(`[AgentApprove] Removed old skill "${old.name}" for user ${user.uuid}`);
1900
+ } catch (e) {
1901
+ console.warn(`[AgentApprove] Could not remove old skill "${old.name}" for user ${user.uuid}:`, e.message);
1902
+ }
1866
1903
  }
1867
1904
  }
1905
+ } else {
1906
+ // No recorded data — fallback: scan actual skills dir
1907
+ try {
1908
+ const skillsDir = path.join(projectDir, '.claude', 'skills');
1909
+ const entries = await fs.readdir(skillsDir).catch(() => []);
1910
+ for (const name of entries) {
1911
+ if (!newSkillNames.has(name)) {
1912
+ try {
1913
+ await fs.unlink(path.join(skillsDir, name));
1914
+ console.log(`[AgentApprove] Removed residual skill "${name}" for user ${user.uuid} (fallback cleanup)`);
1915
+ } catch (e) {
1916
+ console.warn(`[AgentApprove] Could not remove residual skill "${name}" for user ${user.uuid}:`, e.message);
1917
+ }
1918
+ }
1919
+ }
1920
+ } catch {}
1868
1921
  }
1869
1922
 
1870
1923
  const newInstalledSkills = [];
@@ -1880,18 +1933,33 @@ router.post('/submissions/:id/approve', async (req, res) => {
1880
1933
 
1881
1934
  // Update MCP dependencies
1882
1935
  const newMcps = (publishedAgent.mcps || []).filter(m => m.name && m.repo);
1883
- const oldMcps = entry.agentInfo.installedMcps || [];
1936
+ const oldMcps = entry.agentInfo.installedMcps;
1884
1937
  const newMcpNames = new Set(newMcps.map(m => m.name));
1885
1938
 
1886
- for (const old of oldMcps) {
1887
- if (!newMcpNames.has(old.name)) {
1888
- try {
1889
- await uninstallMcp(old.keys, user.uuid, projectDir);
1890
- console.log(`[AgentApprove] Removed old MCP "${old.name}" for user ${user.uuid}`);
1891
- } catch (e) {
1892
- console.warn(`[AgentApprove] Could not remove old MCP "${old.name}" for user ${user.uuid}:`, e.message);
1939
+ if (oldMcps?.length > 0) {
1940
+ // Precise diff using recorded data (includes exact server keys)
1941
+ for (const old of oldMcps) {
1942
+ if (!newMcpNames.has(old.name)) {
1943
+ try {
1944
+ await uninstallMcp(old.keys, user.uuid, projectDir);
1945
+ console.log(`[AgentApprove] Removed old MCP "${old.name}" for user ${user.uuid}`);
1946
+ } catch (e) {
1947
+ console.warn(`[AgentApprove] Could not remove old MCP "${old.name}" for user ${user.uuid}:`, e.message);
1948
+ }
1893
1949
  }
1894
1950
  }
1951
+ } else {
1952
+ // No recorded data — fallback: remove ALL project-scoped MCP keys (will be reinstalled)
1953
+ try {
1954
+ const userPaths = getUserPaths(user.uuid);
1955
+ const claudeJsonPath = path.join(userPaths.claudeDir, '.claude.json');
1956
+ const claudeConfig = JSON.parse(await fs.readFile(claudeJsonPath, 'utf-8'));
1957
+ const projectMcpKeys = Object.keys(claudeConfig.projects?.[projectDir]?.mcpServers || {});
1958
+ if (projectMcpKeys.length > 0) {
1959
+ await uninstallMcp(projectMcpKeys, user.uuid, projectDir);
1960
+ console.log(`[AgentApprove] Removed ${projectMcpKeys.length} residual project MCPs for user ${user.uuid} (fallback cleanup)`);
1961
+ }
1962
+ } catch {}
1895
1963
  }
1896
1964
 
1897
1965
  const newInstalledMcps = [];