claude-home 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-home",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "Web dashboard for Claude Code — browse sessions, manage skills, hooks, commands, and agents",
5
5
  "main": "server.js",
6
6
  "bin": {
package/public/index.html CHANGED
@@ -2766,13 +2766,42 @@
2766
2766
  <div>
2767
2767
  <div style="padding:8px 10px;border-bottom:1px solid var(--rule);display:flex;flex-direction:column;gap:6px">
2768
2768
  <input class="search-input" type="text" placeholder="Buscar skills…" x-model="marketplaceSearch" style="width:100%;font-size:12px;padding:4px 8px">
2769
- <template x-if="marketplaceSources.length>1">
2770
- <div style="display:flex;gap:4px;flex-wrap:wrap">
2771
- <button class="tools-tab" :class="{active:marketplaceSourceFilter==='all'}" style="font-size:11px;padding:2px 8px" @click="marketplaceSourceFilter='all'">Todos</button>
2769
+ <div style="display:flex;align-items:center;gap:4px;flex-wrap:wrap">
2770
+ <div style="display:flex;gap:4px;flex-wrap:wrap;flex:1">
2771
+ <template x-if="marketplaceSources.length>1">
2772
+ <button class="tools-tab" :class="{active:marketplaceSourceFilter==='all'}" style="font-size:11px;padding:2px 8px" @click="marketplaceSourceFilter='all'">All</button>
2773
+ </template>
2772
2774
  <template x-for="src in marketplaceSources" :key="src.id">
2773
- <button class="tools-tab" :class="{active:marketplaceSourceFilter===src.id}" style="font-size:11px;padding:2px 8px" @click="marketplaceSourceFilter=src.id" x-text="src.name"></button>
2775
+ <span style="display:inline-flex;align-items:center;gap:2px">
2776
+ <button class="tools-tab" :class="{active:marketplaceSourceFilter===src.id||marketplaceSources.length===1}" style="font-size:11px;padding:2px 8px" @click="marketplaceSourceFilter=src.id" x-text="src.name"></button>
2777
+ <button style="font-size:10px;padding:1px 4px;border:none;background:none;color:var(--ink-3);cursor:pointer;line-height:1" title="Remove source" @click.stop="removeMarketplaceSource(src.id)">✕</button>
2778
+ </span>
2774
2779
  </template>
2775
2780
  </div>
2781
+ <button class="btn btn-outline btn-sm" style="font-size:11px;padding:2px 8px;flex-shrink:0" @click="marketplaceSourceForm.open=!marketplaceSourceForm.open;marketplaceSourceForm.msg=''">+ Add source</button>
2782
+ </div>
2783
+ <!-- Add source form -->
2784
+ <template x-if="marketplaceSourceForm.open">
2785
+ <div style="display:flex;flex-direction:column;gap:6px;padding:10px;border:1px solid var(--rule-2);border-radius:6px;background:var(--canvas)">
2786
+ <div style="font-size:11px;font-weight:600;color:var(--ink)">New source</div>
2787
+ <input type="text" x-model="marketplaceSourceForm.name" placeholder="Name (e.g. My Org Skills)" style="font-size:12px;padding:4px 8px;border:1px solid var(--rule-2);border-radius:4px;background:var(--white)">
2788
+ <div style="display:flex;gap:6px">
2789
+ <input type="text" x-model="marketplaceSourceForm.owner" placeholder="GitHub owner / org" style="font-size:12px;padding:4px 8px;border:1px solid var(--rule-2);border-radius:4px;background:var(--white);flex:1">
2790
+ <input type="text" x-model="marketplaceSourceForm.repo" placeholder="repo name" style="font-size:12px;padding:4px 8px;border:1px solid var(--rule-2);border-radius:4px;background:var(--white);flex:1">
2791
+ </div>
2792
+ <div style="display:flex;gap:6px">
2793
+ <input type="text" x-model="marketplaceSourceForm.branch" placeholder="branch (default: main)" style="font-size:12px;padding:4px 8px;border:1px solid var(--rule-2);border-radius:4px;background:var(--white);flex:1">
2794
+ <input type="text" x-model="marketplaceSourceForm.skillsPath" placeholder="skills path (leave empty for root)" style="font-size:12px;padding:4px 8px;border:1px solid var(--rule-2);border-radius:4px;background:var(--white);flex:1">
2795
+ </div>
2796
+ <input type="password" x-model="marketplaceSourceForm.token" placeholder="GitHub token (optional, for private repos)" style="font-size:12px;padding:4px 8px;border:1px solid var(--rule-2);border-radius:4px;background:var(--white)">
2797
+ <div style="display:flex;gap:6px;align-items:center">
2798
+ <button class="btn btn-primary btn-sm" :disabled="marketplaceSourceForm.saving||!marketplaceSourceForm.name||!marketplaceSourceForm.owner||!marketplaceSourceForm.repo" @click="addMarketplaceSource()">
2799
+ <span x-text="marketplaceSourceForm.saving ? 'Adding…' : 'Add'"></span>
2800
+ </button>
2801
+ <button class="btn btn-outline btn-sm" @click="marketplaceSourceForm.open=false">Cancel</button>
2802
+ <span x-show="marketplaceSourceForm.msg" :style="marketplaceSourceForm.msg.startsWith('Error')?'color:var(--red)':'color:var(--green)'" style="font-size:11px" x-text="marketplaceSourceForm.msg"></span>
2803
+ </div>
2804
+ </div>
2776
2805
  </template>
2777
2806
  </div>
2778
2807
  <template x-for="s in filteredMarketplace()" :key="s.slug">
@@ -2781,7 +2810,7 @@
2781
2810
  <div style="display:flex;align-items:center;gap:5px;flex-wrap:wrap">
2782
2811
  <div class="tool-row-name" x-text="s.name"></div>
2783
2812
  <template x-if="marketplaceInstalled[s.slug]"><span style="font-size:9px;padding:1px 5px;border-radius:3px;background:var(--green);color:#fff;font-weight:600">installed</span></template>
2784
- <template x-if="marketplaceSources.length>1"><span style="font-size:9px;padding:1px 5px;border-radius:3px;background:var(--canvas);border:1px solid var(--rule-2);color:var(--ink-3)" x-text="s._sourceName"></span></template>
2813
+ <span style="font-size:9px;padding:1px 5px;border-radius:3px;background:var(--canvas);border:1px solid var(--rule-2);color:var(--ink-3)" x-text="s._sourceName"></span>
2785
2814
  </div>
2786
2815
  <div class="tool-row-desc" x-text="s.description"></div>
2787
2816
  <template x-if="s.tags?.length>0">
@@ -3829,6 +3858,7 @@
3829
3858
  marketplace: [],
3830
3859
  marketplaceSources: [],
3831
3860
  marketplaceSourceFilter: 'all',
3861
+ marketplaceSourceForm: { open: false, name: '', owner: '', repo: '', branch: 'main', skillsPath: '', token: '', tokenEnv: '', saving: false, msg: '' },
3832
3862
  marketplaceLoading: false,
3833
3863
  marketplaceErrors: [],
3834
3864
  marketplaceSearch: '',
@@ -4972,6 +5002,32 @@
4972
5002
  );
4973
5003
  },
4974
5004
 
5005
+ async addMarketplaceSource() {
5006
+ const f = this.marketplaceSourceForm;
5007
+ f.saving = true; f.msg = '';
5008
+ try {
5009
+ const r = await fetch('/api/marketplace/sources', {
5010
+ method: 'POST',
5011
+ headers: { 'Content-Type': 'application/json' },
5012
+ body: JSON.stringify({ name: f.name, owner: f.owner, repo: f.repo, branch: f.branch || 'main', skillsPath: f.skillsPath || '', token: f.token || '' }),
5013
+ });
5014
+ const data = await r.json();
5015
+ if (!r.ok) { f.msg = 'Error: ' + data.error; return; }
5016
+ f.msg = 'Source added!';
5017
+ f.open = false;
5018
+ Object.assign(f, { name: '', owner: '', repo: '', branch: 'main', skillsPath: '', token: '' });
5019
+ this.marketplace = [];
5020
+ await this.loadMarketplace();
5021
+ } catch (e) { f.msg = 'Error: ' + e.message; }
5022
+ finally { f.saving = false; }
5023
+ },
5024
+
5025
+ async removeMarketplaceSource(id) {
5026
+ await fetch(`/api/marketplace/sources/${encodeURIComponent(id)}`, { method: 'DELETE' });
5027
+ this.marketplace = [];
5028
+ await this.loadMarketplace();
5029
+ },
5030
+
4975
5031
  async loadMarketplace() {
4976
5032
  if (this.marketplace.length > 0) return;
4977
5033
  this.marketplaceLoading = true;
package/server.js CHANGED
@@ -1138,10 +1138,35 @@ app.get('/api/status', (req, res) => {
1138
1138
  app.get('/api/marketplace/sources', (req, res) => {
1139
1139
  const sources = getMarketplaceSources()
1140
1140
  .filter(s => s.owner && s.repo)
1141
- .map(({ id, name, owner, repo }) => ({ id, name, owner, repo }));
1141
+ .map(({ id, name, owner, repo, branch, skillsPath }) => ({ id, name, owner, repo, branch, skillsPath }));
1142
1142
  res.json(sources);
1143
1143
  });
1144
1144
 
1145
+ // POST /api/marketplace/sources — add a new source
1146
+ app.post('/api/marketplace/sources', (req, res) => {
1147
+ const { name, owner, repo, branch, skillsPath, token, tokenEnv } = req.body || {};
1148
+ if (!name || !owner || !repo) return res.status(400).json({ error: 'name, owner and repo are required' });
1149
+ const config = JSON.parse(fs.readFileSync(MARKETPLACE_CONFIG_PATH, 'utf8'));
1150
+ const id = owner.toLowerCase() + '-' + repo.toLowerCase().replace(/[^a-z0-9]/g, '-');
1151
+ if (config.sources.some(s => s.id === id)) return res.status(409).json({ error: 'Source already exists' });
1152
+ config.sources.push({ id, name, owner, repo, branch: branch || 'main', skillsPath: skillsPath || '', token: token || '', tokenEnv: tokenEnv || '' });
1153
+ fs.writeFileSync(MARKETPLACE_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');
1154
+ sourceCache.delete(id);
1155
+ res.json({ id });
1156
+ });
1157
+
1158
+ // DELETE /api/marketplace/sources/:id — remove a source
1159
+ app.delete('/api/marketplace/sources/:id', (req, res) => {
1160
+ const { id } = req.params;
1161
+ const config = JSON.parse(fs.readFileSync(MARKETPLACE_CONFIG_PATH, 'utf8'));
1162
+ const idx = config.sources.findIndex(s => s.id === id);
1163
+ if (idx === -1) return res.status(404).json({ error: 'Source not found' });
1164
+ config.sources.splice(idx, 1);
1165
+ fs.writeFileSync(MARKETPLACE_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');
1166
+ sourceCache.delete(id);
1167
+ res.json({ ok: true });
1168
+ });
1169
+
1145
1170
  // GET /api/marketplace/registry — discovers skills from all configured sources
1146
1171
  app.get('/api/marketplace/registry', async (req, res) => {
1147
1172
  const sources = getMarketplaceSources().filter(s => s.owner && s.repo);