pi-sdk-web 0.3.3 → 0.3.5

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/dist/server.js CHANGED
@@ -278,6 +278,7 @@ export class PiWebServer {
278
278
  cwd: this.formatCwd(this.session.sessionManager.getCwd()),
279
279
  gitBranch: this.getGitBranch(this.session.sessionManager.getCwd()),
280
280
  commands: this.getCommands(),
281
+ tools: this.getActiveTools(),
281
282
  };
282
283
  try {
283
284
  state.sessionStats = this.session.getSessionStats();
@@ -352,6 +353,19 @@ export class PiWebServer {
352
353
  return [];
353
354
  }
354
355
  }
356
+ /** Tools currently exposed to the LLM (name + description). */
357
+ getActiveTools() {
358
+ try {
359
+ const active = new Set(this.session.getActiveToolNames());
360
+ return this.session
361
+ .getAllTools()
362
+ .filter((t) => active.has(t.name))
363
+ .map((t) => ({ name: t.name, description: t.description }));
364
+ }
365
+ catch {
366
+ return [];
367
+ }
368
+ }
355
369
  handleClientMessage(ws, raw) {
356
370
  let data;
357
371
  try {
@@ -385,6 +399,10 @@ export class PiWebServer {
385
399
  case "get_stats":
386
400
  this.broadcastStats();
387
401
  break;
402
+ case "get_state":
403
+ // Used by the sidebar Tools refresh button
404
+ this.broadcastState();
405
+ break;
388
406
  case "bash": {
389
407
  const command = typeof data.command === "string" ? data.command : "";
390
408
  if (!command)
@@ -208,7 +208,7 @@ class PiWebClient {
208
208
  }
209
209
 
210
210
  // Loaded resources
211
- this.renderLoadedResources(state.commands);
211
+ this.renderLoadedResources(state.commands, state.tools);
212
212
 
213
213
  // First footer line: pwd (branch) • session name
214
214
  const pwd = state.cwd || '';
@@ -220,56 +220,90 @@ class PiWebClient {
220
220
  this.renderStats(state.sessionStats, state);
221
221
  }
222
222
 
223
- renderLoadedResources(commands) {
223
+ renderLoadedResources(commands, tools) {
224
224
  if (!this.loadedResourcesEl) return;
225
- if (!commands || commands.length === 0) {
226
- this.loadedResourcesEl.innerHTML = '';
227
- return;
228
- }
229
-
230
- const groups = { skill: [], prompt: [], extension: [] };
231
- for (const cmd of commands) {
232
- const source = cmd.source;
233
- if (source === 'skill') groups.skill.push(cmd);
234
- else if (source === 'prompt') groups.prompt.push(cmd);
235
- else groups.extension.push(cmd);
236
- }
225
+ this.loadedResourcesEl.innerHTML = '';
237
226
 
238
- const div = document.createElement('div');
239
- div.className = 'resources-block';
240
- div.dataset.expanded = 'false';
227
+ // Loaded Resources block (Skills/Prompts/Extensions)
228
+ if (commands && commands.length > 0) {
229
+ const groups = { skill: [], prompt: [], extension: [] };
230
+ for (const cmd of commands) {
231
+ const source = cmd.source;
232
+ if (source === 'skill') groups.skill.push(cmd);
233
+ else if (source === 'prompt') groups.prompt.push(cmd);
234
+ else groups.extension.push(cmd);
235
+ }
241
236
 
242
- const header = document.createElement('div');
243
- header.className = 'resources-header';
244
- header.textContent = 'Loaded Resources (click to expand)';
237
+ const div = document.createElement('div');
238
+ div.className = 'resources-block';
239
+ div.dataset.expanded = 'false';
245
240
 
246
- const body = document.createElement('div');
247
- body.className = 'resources-body';
248
- body.style.display = 'none';
241
+ const header = document.createElement('div');
242
+ header.className = 'resources-header';
243
+ header.textContent = 'Loaded Resources (click to expand)';
249
244
 
250
- const sections = [];
251
- if (groups.skill.length > 0) {
252
- sections.push(this.resourceSection('Skills', groups.skill.map((c) => c.name)));
253
- }
254
- if (groups.prompt.length > 0) {
255
- sections.push(this.resourceSection('Prompts', groups.prompt.map((c) => c.name)));
256
- }
257
- if (groups.extension.length > 0) {
258
- sections.push(this.resourceSection('Extensions', groups.extension.map((c) => c.name)));
259
- }
260
- body.innerHTML = sections.join('');
245
+ const body = document.createElement('div');
246
+ body.className = 'resources-body';
247
+ body.style.display = 'none';
261
248
 
262
- div.appendChild(header);
263
- div.appendChild(body);
264
- div.addEventListener('click', () => {
265
- const expanded = div.dataset.expanded === 'true';
266
- div.dataset.expanded = expanded ? 'false' : 'true';
267
- body.style.display = expanded ? 'none' : 'block';
268
- header.textContent = expanded ? 'Loaded Resources (click to expand)' : 'Loaded Resources (click to collapse)';
269
- });
249
+ const sections = [];
250
+ if (groups.skill.length > 0) {
251
+ sections.push(this.resourceSection('Skills', groups.skill.map((c) => c.name)));
252
+ }
253
+ if (groups.prompt.length > 0) {
254
+ sections.push(this.resourceSection('Prompts', groups.prompt.map((c) => c.name)));
255
+ }
256
+ if (groups.extension.length > 0) {
257
+ sections.push(this.resourceSection('Extensions', groups.extension.map((c) => c.name)));
258
+ }
259
+ body.innerHTML = sections.join('');
260
+
261
+ div.appendChild(header);
262
+ div.appendChild(body);
263
+ div.addEventListener('click', () => {
264
+ const expanded = div.dataset.expanded === 'true';
265
+ div.dataset.expanded = expanded ? 'false' : 'true';
266
+ body.style.display = expanded ? 'none' : 'block';
267
+ header.textContent = expanded ? 'Loaded Resources (click to expand)' : 'Loaded Resources (click to collapse)';
268
+ });
269
+ this.loadedResourcesEl.appendChild(div);
270
+ }
271
+
272
+ // Separate Tools block below Loaded Resources (web enhancement)
273
+ if (tools && tools.length > 0) {
274
+ const div = document.createElement('div');
275
+ div.className = 'resources-block';
276
+ div.dataset.expanded = 'false';
277
+ div.style.marginTop = '6px';
278
+
279
+ const header = document.createElement('div');
280
+ header.className = 'resources-header';
281
+ header.textContent = 'Tools (click to expand)';
282
+ const refreshBtn = document.createElement('span');
283
+ refreshBtn.className = 'tools-refresh';
284
+ refreshBtn.title = 'Refresh tools list';
285
+ refreshBtn.textContent = '⟳';
286
+ refreshBtn.addEventListener('click', (e) => {
287
+ e.stopPropagation();
288
+ this.send({ type: 'get_state' });
289
+ });
290
+ header.appendChild(refreshBtn);
270
291
 
271
- this.loadedResourcesEl.innerHTML = '';
272
- this.loadedResourcesEl.appendChild(div);
292
+ const body = document.createElement('div');
293
+ body.className = 'resources-body';
294
+ body.style.display = 'none';
295
+ body.innerHTML = this.resourceSection('', tools.map((t) => t.name));
296
+
297
+ div.appendChild(header);
298
+ div.appendChild(body);
299
+ div.addEventListener('click', () => {
300
+ const expanded = div.dataset.expanded === 'true';
301
+ div.dataset.expanded = expanded ? 'false' : 'true';
302
+ body.style.display = expanded ? 'none' : 'block';
303
+ header.firstChild.textContent = expanded ? 'Tools (click to expand)' : 'Tools (click to collapse)';
304
+ });
305
+ this.loadedResourcesEl.appendChild(div);
306
+ }
273
307
  }
274
308
 
275
309
  resourceSection(name, items) {
@@ -171,6 +171,17 @@ body {
171
171
  color: var(--dim);
172
172
  }
173
173
 
174
+ .tools-refresh {
175
+ float: right;
176
+ cursor: pointer;
177
+ color: var(--accent);
178
+ padding: 0 4px;
179
+ }
180
+
181
+ .tools-refresh:hover {
182
+ text-decoration: underline;
183
+ }
184
+
174
185
  .resources-body {
175
186
  margin-top: 6px;
176
187
  color: var(--muted);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-sdk-web",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Browser Web access for Pi (AI coding agent) via the Pi SDK - standalone module, zero modification to Pi itself",
5
5
  "type": "module",
6
6
  "bin": {