@skyelight/mcp 0.1.2 → 0.1.4
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 +38 -5
package/package.json
CHANGED
package/src/tools.js
CHANGED
|
@@ -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("");
|
|
@@ -386,6 +410,9 @@ export function renderItem(item) {
|
|
|
386
410
|
const where = item.source.line
|
|
387
411
|
? `${item.source.file}, line ${item.source.line}`
|
|
388
412
|
: item.source.file;
|
|
413
|
+
// Named before located: "the DoseRow component" is what someone would say,
|
|
414
|
+
// and it stays true after the line has moved.
|
|
415
|
+
const rendered = item.sourcePath?.target?.component;
|
|
389
416
|
// The build too, when the page carried one. Which commit was on the
|
|
390
417
|
// screen is the difference between a live bug and a stale pin — and the
|
|
391
418
|
// branch is what makes that commit findable when the page was a preview
|
|
@@ -393,10 +420,11 @@ export function renderItem(item) {
|
|
|
393
420
|
const from = [];
|
|
394
421
|
if (item.source.build) from.push(`build ${item.source.build}`);
|
|
395
422
|
if (item.source.branch) from.push(`branch ${item.source.branch}`);
|
|
423
|
+
const by = rendered ? `the ${rendered} component, ${where}` : where;
|
|
396
424
|
lines.push(
|
|
397
425
|
from.length
|
|
398
|
-
? `Written by ${
|
|
399
|
-
: `Written by ${
|
|
426
|
+
? `Written by ${by}, in ${from.join(" on ")}.`
|
|
427
|
+
: `Written by ${by}.`,
|
|
400
428
|
);
|
|
401
429
|
// The permalink, when the project names a repository. Pinned to the
|
|
402
430
|
// commit the page carried rather than to a branch, so it opens the code
|
|
@@ -419,7 +447,11 @@ export function renderItem(item) {
|
|
|
419
447
|
// Outermost first, so a repeated file reads as one component and a
|
|
420
448
|
// change of file reads as a boundary between two.
|
|
421
449
|
for (const step of path.above) {
|
|
422
|
-
|
|
450
|
+
// The component first when there is one: it is what a person would
|
|
451
|
+
// call this part of the page, and the one identifier that survives
|
|
452
|
+
// the lines below it moving.
|
|
453
|
+
const who = step.component ? `${step.component} ` : "";
|
|
454
|
+
lines.push(` ${who}${step.file}:${step.line ?? "?"} <${step.tag}>`);
|
|
423
455
|
}
|
|
424
456
|
}
|
|
425
457
|
if (path.below.length) {
|
|
@@ -428,8 +460,9 @@ export function renderItem(item) {
|
|
|
428
460
|
for (const step of path.below) {
|
|
429
461
|
const indent = " ".repeat((step.depth ?? 0) + 1);
|
|
430
462
|
const text = step.text ? ` "${step.text}"` : "";
|
|
463
|
+
const who = step.component ? ` ${step.component}` : "";
|
|
431
464
|
lines.push(
|
|
432
|
-
`${indent}<${step.tag}
|
|
465
|
+
`${indent}<${step.tag}>${who} ${step.file}:${step.line ?? "?"}${text}`,
|
|
433
466
|
);
|
|
434
467
|
}
|
|
435
468
|
}
|