@skyelight/mcp 0.1.1 → 0.1.3
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 +1 -1
- package/src/tools.js +61 -2
package/package.json
CHANGED
package/src/tools.js
CHANGED
|
@@ -172,7 +172,7 @@ export function toolDefinitions(opts = {}) {
|
|
|
172
172
|
description:
|
|
173
173
|
"Move an item to open, deferred or resolved. Use it to close the loop after you have " +
|
|
174
174
|
"fixed something and said so \u2014 post_update first, so the person who reported it " +
|
|
175
|
-
|
|
175
|
+
'reads what changed, then set_status. "deferred" is for work that is real but not now.',
|
|
176
176
|
inputSchema: {
|
|
177
177
|
type: "object",
|
|
178
178
|
properties: {
|
|
@@ -232,6 +232,25 @@ export function renderWorkspaces(result) {
|
|
|
232
232
|
return lines.join("\n");
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Coarse relative time.
|
|
237
|
+
*
|
|
238
|
+
* Deliberately vague past a day: an agent deciding whether to look at a
|
|
239
|
+
* project needs "today" or "last month", and a precise timestamp invites it
|
|
240
|
+
* to quote a date it has no reason to trust to the minute.
|
|
241
|
+
*/
|
|
242
|
+
function ago(ts) {
|
|
243
|
+
const mins = Math.max(0, Math.round((Date.now() - ts) / 60000));
|
|
244
|
+
if (mins < 1) return "just now";
|
|
245
|
+
if (mins < 60) return `${mins}m ago`;
|
|
246
|
+
const hours = Math.round(mins / 60);
|
|
247
|
+
if (hours < 24) return `${hours}h ago`;
|
|
248
|
+
const days = Math.round(hours / 24);
|
|
249
|
+
if (days < 30) return `${days}d ago`;
|
|
250
|
+
const months = Math.round(days / 30);
|
|
251
|
+
return months < 12 ? `${months}mo ago` : `${Math.round(months / 12)}y ago`;
|
|
252
|
+
}
|
|
253
|
+
|
|
235
254
|
export function renderProjects(result) {
|
|
236
255
|
const { projects, total } = result;
|
|
237
256
|
if (total === 0) {
|
|
@@ -244,7 +263,12 @@ export function renderProjects(result) {
|
|
|
244
263
|
for (const p of projects) {
|
|
245
264
|
// The workspace is only worth naming when there is more than one in
|
|
246
265
|
// play; repeating it down a single-workspace list is noise.
|
|
247
|
-
|
|
266
|
+
// The activity date is why this list is ordered the way it is, and it
|
|
267
|
+
// was previously only in the structured payload — so an agent reading the
|
|
268
|
+
// prose could not tell a busy project from a dormant one, and one reading
|
|
269
|
+
// the raw field got `updatedAt`, which meant "renamed recently".
|
|
270
|
+
const when = p.lastActivityAt ? ` · ${ago(p.lastActivityAt)}` : "";
|
|
271
|
+
lines.push(` ${p.name}${spans ? ` (${p.workspace.name})` : ""}${when}`);
|
|
248
272
|
lines.push(` ${p.id}${p.repo ? ` — ${p.repo}` : ""}`);
|
|
249
273
|
}
|
|
250
274
|
lines.push("");
|
|
@@ -398,6 +422,41 @@ export function renderItem(item) {
|
|
|
398
422
|
? `Written by ${where}, in ${from.join(" on ")}.`
|
|
399
423
|
: `Written by ${where}.`,
|
|
400
424
|
);
|
|
425
|
+
// The permalink, when the project names a repository. Pinned to the
|
|
426
|
+
// commit the page carried rather than to a branch, so it opens the code
|
|
427
|
+
// the person was actually looking at.
|
|
428
|
+
if (item.sourceUrl) lines.push(item.sourceUrl);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// The stamps read in both directions.
|
|
432
|
+
//
|
|
433
|
+
// Separated from the one-line `Written by` above because they answer a
|
|
434
|
+
// different question. That line says where the pinned element came from;
|
|
435
|
+
// this says which component it lives in, and which of its parts a request
|
|
436
|
+
// is likely to be about. A person points at a row and means the text
|
|
437
|
+
// inside it far more often than they mean the row.
|
|
438
|
+
const path = item.sourcePath;
|
|
439
|
+
if (path && (path.above.length || path.below.length)) {
|
|
440
|
+
if (path.above.length) {
|
|
441
|
+
lines.push("");
|
|
442
|
+
lines.push("Nested inside");
|
|
443
|
+
// Outermost first, so a repeated file reads as one component and a
|
|
444
|
+
// change of file reads as a boundary between two.
|
|
445
|
+
for (const step of path.above) {
|
|
446
|
+
lines.push(` ${step.file}:${step.line ?? "?"} <${step.tag}>`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (path.below.length) {
|
|
450
|
+
lines.push("");
|
|
451
|
+
lines.push("Contains");
|
|
452
|
+
for (const step of path.below) {
|
|
453
|
+
const indent = " ".repeat((step.depth ?? 0) + 1);
|
|
454
|
+
const text = step.text ? ` "${step.text}"` : "";
|
|
455
|
+
lines.push(
|
|
456
|
+
`${indent}<${step.tag}> ${step.file}:${step.line ?? "?"}${text}`,
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
401
460
|
}
|
|
402
461
|
|
|
403
462
|
return lines.join("\n");
|