@harborppm/cli 0.87.0 → 0.88.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/dist/app.mjs +81 -2
- package/lib/format.mjs +6 -0
- package/package.json +1 -1
package/dist/app.mjs
CHANGED
|
@@ -5066,6 +5066,22 @@ function isVoyageProject(projectId) {
|
|
|
5066
5066
|
function ownProjects(projects) {
|
|
5067
5067
|
return projects.filter((p) => !isVoyageProject(p.id));
|
|
5068
5068
|
}
|
|
5069
|
+
/**
|
|
5070
|
+
* The harbor as anyone outside it may see it: First voyage is practice, so
|
|
5071
|
+
* share links, their embeds and the Monday letter to stakeholders leave it
|
|
5072
|
+
* out, with its tasks, risks and spend, from what they show and count.
|
|
5073
|
+
*/
|
|
5074
|
+
function withoutPractice(data) {
|
|
5075
|
+
if (!data.projects.some((p) => isVoyageProject(p.id))) return data;
|
|
5076
|
+
const out = {
|
|
5077
|
+
...data,
|
|
5078
|
+
projects: ownProjects(data.projects),
|
|
5079
|
+
tasks: data.tasks.filter((t) => !isVoyageProject(t.projectId)),
|
|
5080
|
+
raidItems: data.raidItems.filter((r) => !r.projectId || !isVoyageProject(r.projectId))
|
|
5081
|
+
};
|
|
5082
|
+
if (data.spendEntries) out.spendEntries = data.spendEntries.filter((e) => !isVoyageProject(e.projectId));
|
|
5083
|
+
return out;
|
|
5084
|
+
}
|
|
5069
5085
|
var VOYAGE_PROJECT_ID, STEPS, voyageTaskId, VOYAGE_NO_FORECAST, SEEDED;
|
|
5070
5086
|
var init_first_voyage = __esmMin((() => {
|
|
5071
5087
|
VOYAGE_PROJECT_ID = "voyage";
|
|
@@ -8205,6 +8221,7 @@ function list$2(names) {
|
|
|
8205
8221
|
* document only; nothing here changes it.
|
|
8206
8222
|
*/
|
|
8207
8223
|
function shippingForecast(data, opts) {
|
|
8224
|
+
data = withoutPractice(data);
|
|
8208
8225
|
const { now } = opts;
|
|
8209
8226
|
const today = todayISO(now);
|
|
8210
8227
|
const programs = data.programs.filter((p) => !opts.portfolioId || p.portfolioId === opts.portfolioId);
|
|
@@ -8291,6 +8308,7 @@ var init_shipping_forecast = __esmMin((() => {
|
|
|
8291
8308
|
init_date_text();
|
|
8292
8309
|
init_format();
|
|
8293
8310
|
init_indexes();
|
|
8311
|
+
init_first_voyage();
|
|
8294
8312
|
FORECAST_AREAS = 10;
|
|
8295
8313
|
WORDS = [
|
|
8296
8314
|
"no",
|
|
@@ -10022,7 +10040,9 @@ const KIND_RANK = {
|
|
|
10022
10040
|
sounding: 3,
|
|
10023
10041
|
assigned: 4,
|
|
10024
10042
|
idle: 5,
|
|
10025
|
-
mention: 6
|
|
10043
|
+
mention: 6,
|
|
10044
|
+
reply: 7,
|
|
10045
|
+
finished: 8
|
|
10026
10046
|
};
|
|
10027
10047
|
const PING_KIND_LABEL = {
|
|
10028
10048
|
harbor: "Your harbor",
|
|
@@ -10032,7 +10052,9 @@ const PING_KIND_LABEL = {
|
|
|
10032
10052
|
sounding: "Soundings",
|
|
10033
10053
|
assigned: "Assigned to you",
|
|
10034
10054
|
idle: "Quiet blockers",
|
|
10035
|
-
mention: "Mentions"
|
|
10055
|
+
mention: "Mentions",
|
|
10056
|
+
reply: "Replies",
|
|
10057
|
+
finished: "Finished"
|
|
10036
10058
|
};
|
|
10037
10059
|
function firstName$3(name) {
|
|
10038
10060
|
return name.split(/\s+/)[0] ?? name;
|
|
@@ -10240,6 +10262,63 @@ function inboxOf(data, personId, now = clockNow()) {
|
|
|
10240
10262
|
href: `/projects/${task.projectId}`
|
|
10241
10263
|
});
|
|
10242
10264
|
}
|
|
10265
|
+
const recentByTask = /* @__PURE__ */ new Map();
|
|
10266
|
+
for (const c of data.comments) {
|
|
10267
|
+
const list = recentByTask.get(c.taskId);
|
|
10268
|
+
if (list) list.push(c);
|
|
10269
|
+
else recentByTask.set(c.taskId, [c]);
|
|
10270
|
+
}
|
|
10271
|
+
for (const [taskId, thread] of recentByTask) {
|
|
10272
|
+
const task = byId(data.tasks).get(taskId);
|
|
10273
|
+
if (!task) continue;
|
|
10274
|
+
const ordered = [...thread].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
10275
|
+
let joined = task.assignedBy === personId && task.assigneeId !== personId;
|
|
10276
|
+
const replies = [];
|
|
10277
|
+
for (const c of ordered) {
|
|
10278
|
+
if (c.authorId === personId) {
|
|
10279
|
+
joined = true;
|
|
10280
|
+
replies.length = 0;
|
|
10281
|
+
continue;
|
|
10282
|
+
}
|
|
10283
|
+
if (!joined || c.createdAt < since) continue;
|
|
10284
|
+
if ((c.mentionedIds ?? []).includes(personId)) continue;
|
|
10285
|
+
replies.push(c);
|
|
10286
|
+
}
|
|
10287
|
+
const latest = replies.at(-1);
|
|
10288
|
+
if (!latest) continue;
|
|
10289
|
+
const authors = [...new Set(replies.map((c) => {
|
|
10290
|
+
const who = data.people.find((p) => p.id === c.authorId);
|
|
10291
|
+
return who ? firstName$3(who.name) : "Someone";
|
|
10292
|
+
}))];
|
|
10293
|
+
const project = projectOf(data, task);
|
|
10294
|
+
const snippet = excerpt(latest.body);
|
|
10295
|
+
pings.push({
|
|
10296
|
+
id: `reply:${latest.id}`,
|
|
10297
|
+
kind: "reply",
|
|
10298
|
+
title: `${listNames(authors)} replied`,
|
|
10299
|
+
detail: snippet ? `on “${task.title}”${project ? ` · ${project.name}` : ""} — ${snippet}` : `on “${task.title}”${project ? ` · ${project.name}` : ""}`,
|
|
10300
|
+
at: latest.createdAt,
|
|
10301
|
+
taskId: task.id,
|
|
10302
|
+
personId: latest.authorId,
|
|
10303
|
+
href: `/projects/${task.projectId}`
|
|
10304
|
+
});
|
|
10305
|
+
}
|
|
10306
|
+
for (const task of data.tasks) {
|
|
10307
|
+
if (task.assignedBy !== personId || !task.assigneeId || task.assigneeId === personId) continue;
|
|
10308
|
+
if (task.status !== "done" || !task.completedAt || task.completedAt < since) continue;
|
|
10309
|
+
const who = data.people.find((p) => p.id === task.assigneeId);
|
|
10310
|
+
const project = projectOf(data, task);
|
|
10311
|
+
pings.push({
|
|
10312
|
+
id: `finished:${task.id}:${task.completedAt}`,
|
|
10313
|
+
kind: "finished",
|
|
10314
|
+
title: `${who ? firstName$3(who.name) : "Someone"} finished “${task.title}”`,
|
|
10315
|
+
detail: project ? `A task you gave them · ${project.name}` : "A task you gave them",
|
|
10316
|
+
at: task.completedAt,
|
|
10317
|
+
taskId: task.id,
|
|
10318
|
+
personId: task.assigneeId,
|
|
10319
|
+
href: `/projects/${task.projectId}`
|
|
10320
|
+
});
|
|
10321
|
+
}
|
|
10243
10322
|
return pings.sort((a, b) => {
|
|
10244
10323
|
const rank = KIND_RANK[a.kind] - KIND_RANK[b.kind];
|
|
10245
10324
|
if (rank !== 0) return rank;
|
package/lib/format.mjs
CHANGED
|
@@ -769,6 +769,12 @@ export function importText(r) {
|
|
|
769
769
|
if (r.alreadyThere) lines.push(`${n(r.alreadyThere, "task")} already there, left alone.`);
|
|
770
770
|
if (r.leftOut) lines.push(`${n(r.leftOut, "row")} left out (canceled, won't do or duplicate).`);
|
|
771
771
|
if (r.waitsOnLinks) lines.push(`${n(r.waitsOnLinks, "link")} between tasks became "waits on".`);
|
|
772
|
+
if (r.subtasks)
|
|
773
|
+
lines.push(
|
|
774
|
+
r.subtasks === 1
|
|
775
|
+
? "1 row came in as a subtask of its task."
|
|
776
|
+
: `${r.subtasks} rows came in as subtasks of their tasks.`,
|
|
777
|
+
);
|
|
772
778
|
for (const p of r.problems ?? [])
|
|
773
779
|
lines.push(`${p.card ? `Card “${p.card}”` : `Row ${p.row}`}: ${p.message}`);
|
|
774
780
|
return lines.join("\n");
|