@harborppm/cli 0.59.0 → 0.60.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/README.md +2 -0
- package/dist/app.mjs +2337 -2114
- package/lib/cli.mjs +25 -1
- package/lib/format.mjs +23 -1
- package/lib/mcp.mjs +9 -2
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
ashoreText,
|
|
43
43
|
voyageText,
|
|
44
44
|
whyText,
|
|
45
|
+
barnaclesText,
|
|
45
46
|
handoverText,
|
|
46
47
|
fogText,
|
|
47
48
|
agentWorkText,
|
|
@@ -97,6 +98,9 @@ export const HELP = `harbor: Harbor from the terminal
|
|
|
97
98
|
changes without --apply
|
|
98
99
|
harbor why PROJECT|TASK|MILESTONE what's holding it up: the line of
|
|
99
100
|
task waits that sets its date
|
|
101
|
+
harbor barnacles PROJECT work added since the plan was set:
|
|
102
|
+
by week, by who, and the days of
|
|
103
|
+
slip it explains
|
|
100
104
|
harbor hill [--program P] where each project is on its hill
|
|
101
105
|
harbor hill PROJECT --at 0-100|top|done move it: up to 50 is figuring it
|
|
102
106
|
out, past 50 is getting it done
|
|
@@ -873,9 +877,23 @@ const DETAIL = {
|
|
|
873
877
|
" Tasks have due dates, not lengths, so a task can finish on its due date",
|
|
874
878
|
" at the soonest, never before what it waits on, and never in the past.",
|
|
875
879
|
" Each link's slack is the days it can slip before it moves the next one",
|
|
876
|
-
" (or the end); a negative one already does.
|
|
880
|
+
" (or the end); a negative one already does. Open tasks with no due date",
|
|
881
|
+
" can't hold a date, so they're left out and counted. In --json, endDate",
|
|
882
|
+
" (dueDate for a task or milestone) is the date held and daysSpare the",
|
|
883
|
+
" days before it (negative: that many over). PROJECT is a name, a key",
|
|
877
884
|
" like RR, or an id.",
|
|
878
885
|
],
|
|
886
|
+
barnacles: [
|
|
887
|
+
" harbor barnacles PROJECT reads how much the plan has grown",
|
|
888
|
+
"",
|
|
889
|
+
" Counts the tasks made after the baseline was set (without a baseline",
|
|
890
|
+
" time, after the project's first week of tasks), as a",
|
|
891
|
+
" share of what was planned, by week and by who added them. At the",
|
|
892
|
+
" crew's pace (tasks finished a week over the last six) it says how many",
|
|
893
|
+
" days of work that is, and how much of the slip against the planned end",
|
|
894
|
+
" it explains. The weekly brief names projects that grew a lot since",
|
|
895
|
+
" their baseline.",
|
|
896
|
+
],
|
|
879
897
|
voyage: [
|
|
880
898
|
" harbor voyage PROJECT reads a Done project's voyage log",
|
|
881
899
|
' harbor voyage PROJECT "YOUR LINE" writes: adds or changes your line in it',
|
|
@@ -2624,6 +2642,12 @@ export async function run(argv, io = {}) {
|
|
|
2624
2642
|
if (!of) throw new HarborError("Say what: a project, a task key like RR-42, or a milestone.");
|
|
2625
2643
|
return call("long_pole", { harbor, of }, whyText);
|
|
2626
2644
|
}
|
|
2645
|
+
case "barnacles": {
|
|
2646
|
+
const { positionals, harbor } = parse(spec());
|
|
2647
|
+
const project = positionals.join(" ").trim();
|
|
2648
|
+
if (!project) throw new HarborError("Say which project: harbor barnacles PROJECT.");
|
|
2649
|
+
return call("scope_growth", { harbor, project }, barnaclesText);
|
|
2650
|
+
}
|
|
2627
2651
|
case "voyage": {
|
|
2628
2652
|
const { positionals, harbor } = parse(spec());
|
|
2629
2653
|
const [project, ...words] = positionals;
|
package/lib/format.mjs
CHANGED
|
@@ -571,7 +571,29 @@ export function whyText(r) {
|
|
|
571
571
|
.join(", ")}.`,
|
|
572
572
|
]
|
|
573
573
|
: [];
|
|
574
|
-
|
|
574
|
+
const note = r.note ? ["", r.note] : [];
|
|
575
|
+
return [...head, "", ...via, ...lines, ...others, ...note].join("\n");
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/** A project's barnacles, as scope_growth answers it: the growth, its cost in days, by week and by who. */
|
|
579
|
+
export function barnaclesText(r) {
|
|
580
|
+
const head = [`Barnacles · ${r.project}`];
|
|
581
|
+
if (!r.added) {
|
|
582
|
+
head.push(`Nothing added since the plan was set (${r.since.slice(0, 10)}): ${r.planned} task${r.planned === 1 ? "" : "s"}, as planned.`);
|
|
583
|
+
return head.join("\n");
|
|
584
|
+
}
|
|
585
|
+
head.push(`${r.headline} (${r.planned} planned, ${r.addedThisWeek} added this week).`);
|
|
586
|
+
if (r.daysLine) head.push(r.daysLine);
|
|
587
|
+
const top = Math.max(1, ...r.byWeek.map((w) => w.added));
|
|
588
|
+
const weeks = r.byWeek.map(
|
|
589
|
+
(w) => ` w/c ${w.weekOf} ${"█".repeat(Math.max(1, Math.round((w.added / top) * 20)))} ${w.added}`,
|
|
590
|
+
);
|
|
591
|
+
const who = r.byWho.map((w) => `${w.who ?? "not recorded"} ${w.added}`).join(", ");
|
|
592
|
+
const from =
|
|
593
|
+
r.measuredFrom === "when the baseline was set"
|
|
594
|
+
? `Counted from when the baseline was set, ${r.since.slice(0, 10)}.`
|
|
595
|
+
: `Counted from a week after its first task was made, ${r.since.slice(0, 10)}; setting a baseline counts from then instead.`;
|
|
596
|
+
return [...head, "", "Added by week:", ...weeks, "", `By who: ${who}`, "", from].join("\n");
|
|
575
597
|
}
|
|
576
598
|
|
|
577
599
|
/** A position on a 21-wide track, the top of the hill marked in the middle. */
|
package/lib/mcp.mjs
CHANGED
|
@@ -53,8 +53,15 @@ export function createClient({ creds, save, fetcher = fetch, now = Date.now }) {
|
|
|
53
53
|
const msg = await res.json();
|
|
54
54
|
if (msg.error) throw new HarborError(msg.error.message);
|
|
55
55
|
const result = msg.result ?? {};
|
|
56
|
-
if (result.isError)
|
|
57
|
-
|
|
56
|
+
if (result.isError) {
|
|
57
|
+
const said = result.content?.map((c) => c.text).join("\n") || "Harbor said no.";
|
|
58
|
+
// A command newer than the site it talks to: the tool ships with the next deploy.
|
|
59
|
+
if (/^No tool named "/.test(said))
|
|
60
|
+
throw new HarborError(
|
|
61
|
+
`${new URL(current.server).host} doesn't have this yet. Try again after the next site update.`,
|
|
62
|
+
);
|
|
63
|
+
throw new HarborError(said);
|
|
64
|
+
}
|
|
58
65
|
return result.structuredContent ?? JSON.parse(result.content?.[0]?.text ?? "{}");
|
|
59
66
|
},
|
|
60
67
|
|