@jwilger/pi-development-system 0.57.0 → 0.58.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/package.json
CHANGED
|
@@ -32,7 +32,8 @@ const STEP = /^\s*(?:\d+[.)]|[-*])\s+(.*)$/;
|
|
|
32
32
|
const STEPS_MIN = 3;
|
|
33
33
|
const STEPS_MAX = 7;
|
|
34
34
|
/** Words that stand in for a command or an observable result. */
|
|
35
|
-
const PLACEHOLDER =
|
|
35
|
+
const PLACEHOLDER =
|
|
36
|
+
/^(?:(?:todo|tbd|tbc)\b.*|n\/?a|none|-|…|\.\.\.|works?|it works|passes|ok)\.?$/i;
|
|
36
37
|
|
|
37
38
|
const isSection = (name: string): name is Section => SECTIONS.some((s) => s === name);
|
|
38
39
|
|
|
@@ -85,7 +86,7 @@ const stepsOf = (text: string): string[] =>
|
|
|
85
86
|
|
|
86
87
|
function concrete(name: "Run" | "Expected", text: string): string | undefined {
|
|
87
88
|
const bare = stripTicks(text);
|
|
88
|
-
if (PLACEHOLDER.test(bare))
|
|
89
|
+
if (bare === "" || PLACEHOLDER.test(bare))
|
|
89
90
|
return `${name} must be a concrete ${name === "Run" ? "command" : "observable result"}, not "${bare}"`;
|
|
90
91
|
if (name === "Expected" && bare.split(/\s+/).length < 2) {
|
|
91
92
|
return "Expected must say what is observed (at least two words, e.g. a count, output or exit status)";
|
|
@@ -142,7 +143,7 @@ function locate(markdown: string, id: string | undefined): Located | ParseError
|
|
|
142
143
|
|
|
143
144
|
/** Parses one task record (the only one in the file, or the one named by `id`), reporting every problem together. */
|
|
144
145
|
export function parseTaskRecord(markdown: string, id?: string): TaskRecord | ParseError {
|
|
145
|
-
const found = locate(markdown, id);
|
|
146
|
+
const found = locate(markdown.replace(/\r\n?/g, "\n"), id);
|
|
146
147
|
if (isParseError(found)) return found;
|
|
147
148
|
const split = splitSections(found.lines);
|
|
148
149
|
const problems = problemsOf(found.lines.join("\n"), split);
|
package/src/tracker/github.ts
CHANGED
|
@@ -135,13 +135,14 @@ const createIssue = async (gh: Gh, input: NewWorkItem): Promise<TrackerResult<Wo
|
|
|
135
135
|
const labelFlags = (current: WorkItem, patch: WorkItemPatch): string[] => {
|
|
136
136
|
const wanted = patch.labels ?? current.labels;
|
|
137
137
|
const marked = (patch.status ?? current.status) === "in-progress";
|
|
138
|
+
const hadMarker = current.status === "in-progress";
|
|
138
139
|
const add = [
|
|
139
140
|
...wanted.filter((l) => !current.labels.includes(l)),
|
|
140
|
-
...(marked ? [IN_PROGRESS] : []),
|
|
141
|
+
...(marked && !hadMarker ? [IN_PROGRESS] : []),
|
|
141
142
|
];
|
|
142
143
|
const remove = [
|
|
143
144
|
...current.labels.filter((l) => !wanted.includes(l)),
|
|
144
|
-
...(marked ? [] : [
|
|
145
|
+
...(!marked && hadMarker ? [IN_PROGRESS] : []),
|
|
145
146
|
];
|
|
146
147
|
return [
|
|
147
148
|
...add.flatMap((l) => ["--add-label", l]),
|
|
@@ -175,8 +176,9 @@ const updateIssue = async (
|
|
|
175
176
|
const current = await view(gh, id);
|
|
176
177
|
if (!current.ok) return current;
|
|
177
178
|
if (patch.status === "in-progress") {
|
|
178
|
-
|
|
179
|
-
|
|
179
|
+
// No --force: that would recolour the team's existing label. "Already exists" is success.
|
|
180
|
+
const made = await gh(["label", "create", IN_PROGRESS]);
|
|
181
|
+
if (!(made.ok || /already exists/i.test(made.error.message))) return made;
|
|
180
182
|
}
|
|
181
183
|
const flags = editFlags(current.value, patch);
|
|
182
184
|
if (flags.length > 0) {
|
|
@@ -46,7 +46,9 @@ const readItem = async (root: string, id: string): Promise<TrackerResult<WorkIte
|
|
|
46
46
|
const itemIds = async (root: string): Promise<string[]> => {
|
|
47
47
|
try {
|
|
48
48
|
const files = await readdir(itemsDir(root));
|
|
49
|
-
return files.flatMap((f) =>
|
|
49
|
+
return files.flatMap((f) =>
|
|
50
|
+
f.endsWith(".md") && ID.test(f.slice(0, -3)) ? [f.slice(0, -3)] : [],
|
|
51
|
+
);
|
|
50
52
|
} catch (cause) {
|
|
51
53
|
if (isMissing(cause)) return [];
|
|
52
54
|
throw cause;
|
|
@@ -66,11 +68,14 @@ const readAll = async (root: string): Promise<TrackerResult<WorkItem[]>> => {
|
|
|
66
68
|
const save = async (root: string, item: WorkItem): Promise<TrackerResult<WorkItem>> => {
|
|
67
69
|
const problem = unwritable(item);
|
|
68
70
|
if (problem !== undefined) return trackerError(problem);
|
|
71
|
+
const others = await readAll(root);
|
|
72
|
+
if (!others.ok) return others;
|
|
69
73
|
await mkdir(itemsDir(root), { recursive: true });
|
|
70
74
|
await writeFile(fileOf(root, item.id), renderItem(item));
|
|
71
|
-
const items =
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
const items = [...others.value.filter((i) => i.id !== item.id), item].toSorted((a, b) =>
|
|
76
|
+
byCodeUnit(a.id, b.id),
|
|
77
|
+
);
|
|
78
|
+
await writeFile(join(root, "work", "backlog.md"), renderBacklog(items));
|
|
74
79
|
return ok(item);
|
|
75
80
|
};
|
|
76
81
|
|