@clipboard-health/groundcrew 4.26.1 → 4.27.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/lib/adapters/todo-txt/normalizer.d.ts +1 -1
- package/dist/lib/adapters/todo-txt/normalizer.d.ts.map +1 -1
- package/dist/lib/adapters/todo-txt/normalizer.js +26 -3
- package/dist/lib/adapters/todo-txt/parser.d.ts +1 -0
- package/dist/lib/adapters/todo-txt/parser.d.ts.map +1 -1
- package/dist/lib/adapters/todo-txt/parser.js +1 -1
- package/dist/lib/adapters/todo-txt/source.d.ts.map +1 -1
- package/dist/lib/adapters/todo-txt/source.js +8 -5
- package/dist/lib/adapters/todo-txt/writeback.d.ts.map +1 -1
- package/dist/lib/adapters/todo-txt/writeback.js +6 -5
- package/package.json +1 -1
|
@@ -18,5 +18,5 @@ export interface NormalizeOptions {
|
|
|
18
18
|
updatedAt: string;
|
|
19
19
|
}
|
|
20
20
|
export declare function normalizeToIssue(options: NormalizeOptions): Issue | undefined;
|
|
21
|
-
export declare function isActiveForFetch(parsed: ParsedTodoLine): boolean;
|
|
21
|
+
export declare function isActiveForFetch(parsed: ParsedTodoLine, todayIsoDate: string): boolean;
|
|
22
22
|
//# sourceMappingURL=normalizer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/todo-txt/normalizer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsC,KAAK,KAAK,EAAiB,MAAM,qBAAqB,CAAC;AACpG,OAAO,
|
|
1
|
+
{"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/todo-txt/normalizer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsC,KAAK,KAAK,EAAiB,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAkED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,CAAC,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,KAAK,GAAG,SAAS,CAqD7E;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAYtF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { AGENT_ANY } from "../../config.js";
|
|
3
3
|
import { toCanonicalId } from "../../taskSource.js";
|
|
4
|
-
import { getMetadataAll, getMetadataFirst, hashLine } from "./parser.js";
|
|
4
|
+
import { DATE_RE, getMetadataAll, getMetadataFirst, hashLine, } from "./parser.js";
|
|
5
5
|
function derivedCanonicalStatus(parsed) {
|
|
6
6
|
if (parsed.completed) {
|
|
7
7
|
return "done";
|
|
@@ -90,7 +90,7 @@ export function normalizeToIssue(options) {
|
|
|
90
90
|
sourceRef,
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
|
-
export function isActiveForFetch(parsed) {
|
|
93
|
+
export function isActiveForFetch(parsed, todayIsoDate) {
|
|
94
94
|
if (parsed.completed) {
|
|
95
95
|
return false;
|
|
96
96
|
}
|
|
@@ -98,5 +98,28 @@ export function isActiveForFetch(parsed) {
|
|
|
98
98
|
return false;
|
|
99
99
|
}
|
|
100
100
|
const statusValue = getMetadataFirst(parsed, "status");
|
|
101
|
-
|
|
101
|
+
if (statusValue === "todo") {
|
|
102
|
+
return !isDeferredByThreshold(parsed, todayIsoDate);
|
|
103
|
+
}
|
|
104
|
+
return statusValue === "in-progress" || statusValue === "in-review";
|
|
105
|
+
}
|
|
106
|
+
// Per todo.txt convention, t: (threshold) hides a task until that date.
|
|
107
|
+
// Only not-yet-started tasks defer; in-progress/in-review work must stay
|
|
108
|
+
// visible so the orchestrator keeps tracking it. Malformed t: values are
|
|
109
|
+
// surfaced by validate() and do not block dispatch.
|
|
110
|
+
function isDeferredByThreshold(parsed, todayIsoDate) {
|
|
111
|
+
const threshold = getMetadataFirst(parsed, "t");
|
|
112
|
+
if (threshold === undefined || !DATE_RE.test(threshold) || !isCalendarDate(threshold)) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
// ISO YYYY-MM-DD dates order lexicographically
|
|
116
|
+
return threshold > todayIsoDate;
|
|
117
|
+
}
|
|
118
|
+
// DATE_RE is format-only; non-calendar values like 2026-99-99 would compare
|
|
119
|
+
// greater than any real date and defer the task forever.
|
|
120
|
+
function isCalendarDate(value) {
|
|
121
|
+
const monthIndex = Number(value.slice(5, 7)) - 1;
|
|
122
|
+
const day = Number(value.slice(8, 10));
|
|
123
|
+
const date = new Date(Date.UTC(Number(value.slice(0, 4)), monthIndex, day));
|
|
124
|
+
return date.getUTCMonth() === monthIndex && date.getUTCDate() === day;
|
|
102
125
|
}
|
|
@@ -12,6 +12,7 @@ export interface ParsedTodoLine {
|
|
|
12
12
|
/** True when the final meaningful whitespace-delimited token is a `status:X` field. */
|
|
13
13
|
readonly isStatusFinalToken: boolean;
|
|
14
14
|
}
|
|
15
|
+
export declare const DATE_RE: RegExp;
|
|
15
16
|
export declare function hashLine(raw: string): string;
|
|
16
17
|
export declare function parseAllLines(fileContent: string): (ParsedTodoLine | null)[];
|
|
17
18
|
export declare function getMetadataFirst(parsed: ParsedTodoLine, key: string): string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/todo-txt/parser.ts"],"names":[],"mappings":"AAEA,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;AAEzD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,uFAAuF;IACvF,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;CACtC;
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/todo-txt/parser.ts"],"names":[],"mappings":"AAEA,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;AAEzD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,uFAAuF;IACvF,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;CACtC;AAED,eAAO,MAAM,OAAO,QAAwB,CAAC;AAM7C,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE5C;AA6ED,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,CAAC,cAAc,GAAG,IAAI,CAAC,EAAE,CAQ5E;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAExF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAE5E"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
2
|
+
export const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
3
3
|
const KEY_VALUE_RE = /^(?<key>[a-zA-Z][a-zA-Z0-9-]*):(?<value>\S+)$/;
|
|
4
4
|
const PRIORITY_RE = /^\((?<priority>[A-Z])\) /;
|
|
5
5
|
const PROJECT_RE = /^\+\S+$/;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/todo-txt/source.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,EAKL,KAAK,UAAU,EAEhB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/todo-txt/source.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,EAKL,KAAK,UAAU,EAEhB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AA6RxD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,cAAc,GACtB,UAAU,CAsJZ"}
|
|
@@ -5,9 +5,8 @@ import { AGENT_ANY } from "../../config.js";
|
|
|
5
5
|
import { toCanonicalId, } from "../../taskSource.js";
|
|
6
6
|
import { readEnvironmentVariable } from "../../util.js";
|
|
7
7
|
import { isActiveForFetch, normalizeToIssue } from "./normalizer.js";
|
|
8
|
-
import { getMetadataFirst, parseAllLines } from "./parser.js";
|
|
8
|
+
import { DATE_RE, getMetadataFirst, parseAllLines } from "./parser.js";
|
|
9
9
|
import { copyPromptFile, updateTaskStatus, validateTodoFile, withLock } from "./writeback.js";
|
|
10
|
-
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
11
10
|
const RECURRENCE_RE = /^\+?\d+[dwmy]$/;
|
|
12
11
|
function readPromptFile(promptPath) {
|
|
13
12
|
try {
|
|
@@ -95,7 +94,7 @@ function metadataToken(key, value) {
|
|
|
95
94
|
assertToken(`${key}: value`, value);
|
|
96
95
|
return `${key}:${value}`;
|
|
97
96
|
}
|
|
98
|
-
function
|
|
97
|
+
function isoDateFor(timeZone, now) {
|
|
99
98
|
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
100
99
|
timeZone,
|
|
101
100
|
year: "numeric",
|
|
@@ -109,7 +108,10 @@ function datePartFor(timeZone, now) {
|
|
|
109
108
|
if (year === undefined || month === undefined || day === undefined) {
|
|
110
109
|
throw new Error(`todo-txt: could not format date in timezone "${timeZone}"`);
|
|
111
110
|
}
|
|
112
|
-
return `${year}
|
|
111
|
+
return `${year}-${month}-${day}`;
|
|
112
|
+
}
|
|
113
|
+
function datePartFor(timeZone, now) {
|
|
114
|
+
return isoDateFor(timeZone, now).replaceAll("-", "");
|
|
113
115
|
}
|
|
114
116
|
/* v8 ignore next @preserve -- Covered in source tests; full-suite V8 coverage remaps this helper inconsistently. */
|
|
115
117
|
function nextGeneratedId(config, parsedAll) {
|
|
@@ -256,6 +258,7 @@ export function createTodoTxtTaskSource(config, context) {
|
|
|
256
258
|
]);
|
|
257
259
|
function listTasks() {
|
|
258
260
|
const updatedAt = fileUpdatedAt(todoPath);
|
|
261
|
+
const todayIsoDate = isoDateFor(config.timezone, new Date());
|
|
259
262
|
const { parsedAll } = readAndParseTodo(todoPath);
|
|
260
263
|
const issues = [];
|
|
261
264
|
for (let i = 0; i < parsedAll.length; i++) {
|
|
@@ -263,7 +266,7 @@ export function createTodoTxtTaskSource(config, context) {
|
|
|
263
266
|
if (parsed === null || parsed === undefined) {
|
|
264
267
|
continue;
|
|
265
268
|
}
|
|
266
|
-
if (!isActiveForFetch(parsed)) {
|
|
269
|
+
if (!isActiveForFetch(parsed, todayIsoDate)) {
|
|
267
270
|
continue;
|
|
268
271
|
}
|
|
269
272
|
const issue = buildIssue({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeback.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/todo-txt/writeback.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB;
|
|
1
|
+
{"version":3,"file":"writeback.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/todo-txt/writeback.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB;AAkKD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,gBAAgB,CAAC;IACtB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,wBAAsB,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAQxF;AAED,KAAK,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,MAAM,CAAC;AAgF3D,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,cAAc,GACxB,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAsElC;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAQrE;AAgGD,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAChC,MAAM,EAAE,CA0CV"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { closeSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { hashLine, parseAllLines } from "./parser.js";
|
|
4
|
-
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
3
|
+
import { DATE_RE, hashLine, parseAllLines } from "./parser.js";
|
|
5
4
|
function isoDate(date) {
|
|
6
5
|
return date.toISOString().slice(0, 10);
|
|
7
6
|
}
|
|
@@ -181,9 +180,11 @@ function buildRecurResult(parsed, parsedAll, originalLine, ref, completionDateSt
|
|
|
181
180
|
const newDue = oldDue === undefined ? undefined : advanceDate(dueBase, rec);
|
|
182
181
|
// t: always advances from its own current value by the same period
|
|
183
182
|
const newT = oldT === undefined ? undefined : advanceDate(oldT, rec);
|
|
184
|
-
// Compute new date for id advancement
|
|
185
|
-
|
|
186
|
-
const
|
|
183
|
+
// Compute new date for id advancement: prefer due:, then t:, so ids stay
|
|
184
|
+
// schedule-aligned for t:-only recurring tasks
|
|
185
|
+
const newScheduleDate = newDue ?? newT;
|
|
186
|
+
/* v8 ignore next @preserve -- rec: without due: or t: is unusual; id falls back to completion date */
|
|
187
|
+
const newDateForId = newScheduleDate === undefined ? now : new Date(`${newScheduleDate}T00:00:00Z`);
|
|
187
188
|
const baseNewId = advanceId(ref.id, newDateForId);
|
|
188
189
|
const newId = buildUniqueId(baseNewId, existingIds);
|
|
189
190
|
const newTodoLine = buildRecurringLine(originalLine, ref.id, newId, oldDue, newDue, oldT, newT);
|
package/package.json
CHANGED