@harborppm/cli 0.16.0 → 0.18.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 -1
- package/dist/app.mjs +29 -1
- package/lib/cli.mjs +17 -0
- package/lib/format.mjs +12 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -117,9 +117,10 @@ harbor issue "TITLE" --project P|--program P [--impact 1-5] [--owner NAME]
|
|
|
117
117
|
harbor decision "TITLE" --project P|--program P [--owner NAME] [--due DATE]
|
|
118
118
|
harbor risks [words] [--kind risk,issue,decision] [--project P] [--closed]
|
|
119
119
|
harbor premortem PROJECT ["WHAT WENT WRONG"] [--start]
|
|
120
|
+
harbor voyage PROJECT ["YOUR LINE"]
|
|
120
121
|
```
|
|
121
122
|
|
|
122
|
-
`harbor premortem` runs a pre-mortem with your crew: `--start` asks everyone "it's after the end date and this project failed: what happened?", each person adds answers alone for five minutes, and then the answers show together on the project's Risks & issues tab, where each becomes a risk in one click. On its own it prints the question and the answers you can see.
|
|
123
|
+
`harbor premortem` runs a pre-mortem with your crew: `--start` asks everyone "it's after the end date and this project failed: what happened?", each person adds answers alone for five minutes, and then the answers show together on the project's Risks & issues tab, where each becomes a risk in one click. On its own it prints the question and the answers you can see. Once a project is Done, `harbor voyage` prints its voyage log (planned against actual dates, the biggest slips, the risks that came true, who closed the most, and the crew's lines) and adds yours.
|
|
123
124
|
|
|
124
125
|
Every command takes `--json` for the raw result (a refusal then comes out as `{"error": "…"}` on standard output, still with exit code 1), and `harbor COMMAND --help` shows just that command. Statuses are backlog, todo, doing, review and done. A `DATE` is YYYY-MM-DD, `today`, `tomorrow`, or `+N` for N days from today.
|
|
125
126
|
|
package/dist/app.mjs
CHANGED
|
@@ -855,7 +855,8 @@ function normalizeRaidItem(r) {
|
|
|
855
855
|
createdAt: r.createdAt ?? now,
|
|
856
856
|
updatedAt: r.updatedAt ?? r.createdAt ?? now,
|
|
857
857
|
updates: Array.isArray(r.updates) ? r.updates : [],
|
|
858
|
-
scoreHistory: Array.isArray(r.scoreHistory) && r.scoreHistory.length ? r.scoreHistory : void 0
|
|
858
|
+
scoreHistory: Array.isArray(r.scoreHistory) && r.scoreHistory.length ? r.scoreHistory : void 0,
|
|
859
|
+
...kind === "risk" && typeof r.happened === "boolean" ? { happened: r.happened } : {}
|
|
859
860
|
};
|
|
860
861
|
}
|
|
861
862
|
/** Active first, then by score, then most recently touched. */
|
|
@@ -9336,6 +9337,33 @@ function milestonesRaidSlice({ set, get, write }) {
|
|
|
9336
9337
|
...r,
|
|
9337
9338
|
skipped
|
|
9338
9339
|
} : r) });
|
|
9340
|
+
},
|
|
9341
|
+
writeVoyageLine: (projectId, body) => {
|
|
9342
|
+
const text = body.trim().slice(0, 600);
|
|
9343
|
+
const me = get().currentUserId;
|
|
9344
|
+
const all = get().reflections ?? [];
|
|
9345
|
+
const mine = all.find((r) => r.projectId === projectId && r.kind === "voyage" && r.authorId === me);
|
|
9346
|
+
if (!text) {
|
|
9347
|
+
if (mine) write({ reflections: all.filter((r) => r.id !== mine.id) });
|
|
9348
|
+
return;
|
|
9349
|
+
}
|
|
9350
|
+
if (mine) {
|
|
9351
|
+
if (mine.body !== text) write({ reflections: all.map((r) => r.id === mine.id ? {
|
|
9352
|
+
...r,
|
|
9353
|
+
body: text
|
|
9354
|
+
} : r) });
|
|
9355
|
+
return;
|
|
9356
|
+
}
|
|
9357
|
+
if (!get().projects.some((p) => p.id === projectId)) return;
|
|
9358
|
+
const row = {
|
|
9359
|
+
id: uid("rf"),
|
|
9360
|
+
projectId,
|
|
9361
|
+
kind: "voyage",
|
|
9362
|
+
authorId: me,
|
|
9363
|
+
body: text,
|
|
9364
|
+
createdAt: stamp()
|
|
9365
|
+
};
|
|
9366
|
+
write({ reflections: [...all, row] });
|
|
9339
9367
|
}
|
|
9340
9368
|
};
|
|
9341
9369
|
}
|
package/lib/cli.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
harbors,
|
|
21
21
|
myWork,
|
|
22
22
|
premortemText,
|
|
23
|
+
voyageText,
|
|
23
24
|
raidList,
|
|
24
25
|
programTree,
|
|
25
26
|
projectList,
|
|
@@ -93,6 +94,8 @@ export const HELP = `harbor: Harbor from the terminal
|
|
|
93
94
|
harbor premortem PROJECT ["WHAT WENT WRONG"] [--start]
|
|
94
95
|
a project's pre-mortem: start,
|
|
95
96
|
answer or read it
|
|
97
|
+
harbor voyage PROJECT ["YOUR LINE"] a Done project's voyage log, and
|
|
98
|
+
your line in it
|
|
96
99
|
|
|
97
100
|
Every command takes --json, and --harbor NAME when you're in more than one
|
|
98
101
|
harbor (or set one with harbor use). harbor help COMMAND shows just that one.
|
|
@@ -1015,6 +1018,20 @@ export async function run(argv, io = {}) {
|
|
|
1015
1018
|
}
|
|
1016
1019
|
return call("get_premortem", { harbor, project }, premortemText);
|
|
1017
1020
|
}
|
|
1021
|
+
case "voyage": {
|
|
1022
|
+
const { positionals, harbor } = parse(spec());
|
|
1023
|
+
const [project, ...words] = positionals;
|
|
1024
|
+
if (!project) throw new HarborError("Give a project, and your line if you're adding one.");
|
|
1025
|
+
const line = words.join(" ").trim();
|
|
1026
|
+
if (line) {
|
|
1027
|
+
return call(
|
|
1028
|
+
"add_voyage_line",
|
|
1029
|
+
{ harbor, project, line },
|
|
1030
|
+
(r) => `Your line is in ${r.added.project}'s voyage log.`,
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
return call("get_voyage_log", { harbor, project }, voyageText);
|
|
1034
|
+
}
|
|
1018
1035
|
case "risk":
|
|
1019
1036
|
case "issue":
|
|
1020
1037
|
case "decision": {
|
package/lib/format.mjs
CHANGED
|
@@ -211,3 +211,15 @@ export function premortemText(r) {
|
|
|
211
211
|
lines.push(` Nothing yet. Add one: harbor premortem "${r.project}" "WHAT WENT WRONG"`);
|
|
212
212
|
return [...head, "", ...lines].join("\n");
|
|
213
213
|
}
|
|
214
|
+
|
|
215
|
+
/** A project's voyage log, as get_voyage_log answers it. */
|
|
216
|
+
export function voyageText(r) {
|
|
217
|
+
const head = [`Voyage log · ${r.project}${r.done ? ` · Done ${r.finished}` : ""}`];
|
|
218
|
+
if (r.note) head.push(r.note);
|
|
219
|
+
head.push((r.stats ?? []).join(" · "));
|
|
220
|
+
const story = (r.story ?? []).map((s) => ` - ${s}`);
|
|
221
|
+
const crew = (r.crew ?? []).map((c) => ` - ${c.who ?? "Someone"}: ${c.line}`);
|
|
222
|
+
return [...head, "", ...story, ...(crew.length ? ["", "From the crew:", ...crew] : [])].join(
|
|
223
|
+
"\n",
|
|
224
|
+
);
|
|
225
|
+
}
|