@nickmeriano/task 0.7.0 → 0.8.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 +12 -7
- package/dist/check.d.ts +42 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +364 -0
- package/dist/check.js.map +1 -0
- package/dist/check.test.d.ts +9 -0
- package/dist/check.test.d.ts.map +1 -0
- package/dist/check.test.js +209 -0
- package/dist/check.test.js.map +1 -0
- package/dist/claim.d.ts +56 -3
- package/dist/claim.d.ts.map +1 -1
- package/dist/claim.js +164 -9
- package/dist/claim.js.map +1 -1
- package/dist/claim.test.js +168 -14
- package/dist/claim.test.js.map +1 -1
- package/dist/cli.js +504 -93
- package/dist/cli.js.map +1 -1
- package/dist/file-store.d.ts +41 -15
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +197 -99
- package/dist/file-store.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/overview.d.ts +43 -0
- package/dist/overview.d.ts.map +1 -0
- package/dist/overview.js +53 -0
- package/dist/overview.js.map +1 -0
- package/dist/overview.test.d.ts +8 -0
- package/dist/overview.test.d.ts.map +1 -0
- package/dist/overview.test.js +47 -0
- package/dist/overview.test.js.map +1 -0
- package/dist/promote.test.d.ts +14 -0
- package/dist/promote.test.d.ts.map +1 -0
- package/dist/promote.test.js +106 -0
- package/dist/promote.test.js.map +1 -0
- package/dist/search.d.ts +32 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +66 -0
- package/dist/search.js.map +1 -0
- package/dist/search.test.d.ts +2 -0
- package/dist/search.test.d.ts.map +1 -0
- package/dist/search.test.js +53 -0
- package/dist/search.test.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +10 -2
- package/dist/server.js.map +1 -1
- package/dist/store.d.ts +16 -50
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +0 -368
- package/dist/store.js.map +1 -1
- package/dist/store.test.d.ts +1 -2
- package/dist/store.test.d.ts.map +1 -1
- package/dist/store.test.js +77 -48
- package/dist/store.test.js.map +1 -1
- package/dist/ticket-doc.d.ts +55 -2
- package/dist/ticket-doc.d.ts.map +1 -1
- package/dist/ticket-doc.js +177 -7
- package/dist/ticket-doc.js.map +1 -1
- package/dist/types.d.ts +67 -14
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +97 -31
- package/src/check.test.ts +271 -0
- package/src/check.ts +454 -0
- package/src/claim.test.ts +202 -14
- package/src/claim.ts +194 -10
- package/src/cli.ts +510 -89
- package/src/file-store.ts +217 -111
- package/src/index.ts +4 -2
- package/src/overview.test.ts +51 -0
- package/src/overview.ts +90 -0
- package/src/promote.test.ts +131 -0
- package/src/search.test.ts +64 -0
- package/src/search.ts +91 -0
- package/src/server.ts +11 -2
- package/src/store.test.ts +89 -57
- package/src/store.ts +18 -431
- package/src/ticket-doc.ts +210 -10
- package/src/types.ts +71 -14
- package/ui/dist/assets/{index-oJzomUDL.js → index-BJmOsOdR.js} +76 -76
- package/ui/dist/assets/index-BoqQlqSU.css +1 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-CXW8uT5f.css +0 -1
package/README.md
CHANGED
|
@@ -43,13 +43,18 @@ nothing reads — which is exactly what happened the first time this tried.
|
|
|
43
43
|
task add "Wire up webhook retries" --tags api --milestone launch
|
|
44
44
|
task list # open tasks, board order
|
|
45
45
|
task list --needs-human # what's blocked on a person
|
|
46
|
-
task
|
|
47
|
-
|
|
46
|
+
task search "webhook" # find tickets by what they say — titles,
|
|
47
|
+
# descriptions and comments, with snippets
|
|
48
|
+
task move 1 in_progress # status changes all go through move
|
|
49
|
+
task promote 1 # backlog → todo, the human "build this" call;
|
|
50
|
+
# blocked / needs-human promote with the hold
|
|
51
|
+
# reported
|
|
48
52
|
task link 2 --blocked-by 1 # dependency, visible from both tasks
|
|
49
53
|
task update 1 --pr https://github.com/you/repo/pull/42
|
|
50
54
|
task comment 1 "shipped in #42" --author claude
|
|
51
55
|
task boards # every board in the repo, with prefixes
|
|
52
56
|
task archive --all # move finished tickets to .task/archive/
|
|
57
|
+
task check # lint the board files; --fix repairs the mechanical part
|
|
53
58
|
task serve # opens the kanban + table UI, live
|
|
54
59
|
task publish # same board, at a URL you can open on a phone
|
|
55
60
|
```
|
|
@@ -83,7 +88,7 @@ task claim --release TAS-21 # abandon a claim cleanly
|
|
|
83
88
|
`1` already claimed (pick the next ticket), `2` not claimable — not `todo`,
|
|
84
89
|
blocked, `--needs-human`, or a dirty working tree.
|
|
85
90
|
- **The lifecycle rides the branch.** Claim = `in_progress`; before the PR is
|
|
86
|
-
marked ready, `task done` + `task update --pr` on the branch; the merge lands
|
|
91
|
+
marked ready, `task move <id> done` + `task update --pr` on the branch; the merge lands
|
|
87
92
|
code and `done` together, and the branch auto-deletes. Between claim and
|
|
88
93
|
merge the default branch still says `todo` — the branch is the truth about
|
|
89
94
|
in-flight work, and `git ls-remote origin 'task/claim/*'` (or your
|
|
@@ -205,8 +210,9 @@ jobs:
|
|
|
205
210
|
in the header. Run it inside a package and you get just that board. Every
|
|
206
211
|
other command stays scoped to the nearest `.task/`.
|
|
207
212
|
- Boards from before 0.6 stored their state as a committed SQLite database;
|
|
208
|
-
|
|
209
|
-
(the database stays on disk as an ignored backup)
|
|
213
|
+
run `task migrate` once on `@nickmeriano/task@0.6` to move one onto text
|
|
214
|
+
files (the database stays on disk as an ignored backup) — newer versions
|
|
215
|
+
read only the text format.
|
|
210
216
|
|
|
211
217
|
## How it's built
|
|
212
218
|
|
|
@@ -214,8 +220,7 @@ jobs:
|
|
|
214
220
|
block — reviewable in a PR, mergeable by git, editable by hand. Comments are
|
|
215
221
|
one file each (append-only merges cleanly), and the `blocks`/`blocked-by`
|
|
216
222
|
relation is stored on one side only, so its two views can't disagree.
|
|
217
|
-
|
|
218
|
-
nothing to compile, so `npx` starts in about a second.
|
|
223
|
+
Nothing to compile, so `npx` starts in about a second.
|
|
219
224
|
- **Realtime is a file watch.** `task serve` is one `node:http` process: the
|
|
220
225
|
prebuilt UI, a JSON API, and a server-sent-events stream that pings whenever
|
|
221
226
|
anything writes to `.task/` — this UI, another terminal, an agent mid-run.
|
package/dist/check.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `task check` — lint the board files.
|
|
3
|
+
*
|
|
4
|
+
* A database enforced its constraints at write time; a text store has two
|
|
5
|
+
* extra write paths no CLI code ever sees — git merges and hand-edits — so
|
|
6
|
+
* validation has to be a lint you can run. This strictly parses every ticket
|
|
7
|
+
* comment and goal file on every board at or below the check root (the same
|
|
8
|
+
* walk `task serve` does) and reports what would degrade or throw at read
|
|
9
|
+
* time: files that don't parse, invalid timestamps, dangling `blocked_by`
|
|
10
|
+
* references (one branch deleted a ticket another branch linked to), `goal:`
|
|
11
|
+
* refs naming a goal that doesn't exist, tickets duplicated between the board
|
|
12
|
+
* and the archive, unknown frontmatter keys, and formatting drift from the
|
|
13
|
+
* canonical serialization (including the pre-0.8 `milestone:` key, whose
|
|
14
|
+
* rewrite to `goal:` is the migration).
|
|
15
|
+
*
|
|
16
|
+
* The mechanical repairs — pruning dangling or self references, deduping and
|
|
17
|
+
* sorting `blocked_by`, rewriting a file into canonical form — are applied by
|
|
18
|
+
* `--fix`. Anything that needs a decision (a status that isn't a status, a
|
|
19
|
+
* missing title) is reported and left alone. Cheap enough for pre-commit and
|
|
20
|
+
* CI, which is where merge-introduced drift gets caught.
|
|
21
|
+
*/
|
|
22
|
+
export interface CheckIssue {
|
|
23
|
+
/** Board id relative to the check root — "." for the root board. */
|
|
24
|
+
board: string;
|
|
25
|
+
/** File (or directory) the problem is in, relative to the check root. */
|
|
26
|
+
file: string;
|
|
27
|
+
message: string;
|
|
28
|
+
/** `task check --fix` repairs this mechanically. */
|
|
29
|
+
fixable: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface CheckResult {
|
|
32
|
+
boards: number;
|
|
33
|
+
/** Ticket and comment files inspected. */
|
|
34
|
+
files: number;
|
|
35
|
+
issues: CheckIssue[];
|
|
36
|
+
/** Files rewritten, relative to the check root. Empty unless fixing. */
|
|
37
|
+
fixed: string[];
|
|
38
|
+
}
|
|
39
|
+
/** The boards `task check` covers from `cwd` — the same set `task serve` has. */
|
|
40
|
+
export declare function checkRoot(cwd: string): string;
|
|
41
|
+
export declare function checkBoards(root: string, fix: boolean): CheckResult;
|
|
42
|
+
//# sourceMappingURL=check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAqCH,MAAM,WAAW,UAAU;IACzB,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,oDAAoD;IACpD,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,wEAAwE;IACxE,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C;AA2BD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,WAAW,CAwBnE"}
|
package/dist/check.js
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `task check` — lint the board files.
|
|
3
|
+
*
|
|
4
|
+
* A database enforced its constraints at write time; a text store has two
|
|
5
|
+
* extra write paths no CLI code ever sees — git merges and hand-edits — so
|
|
6
|
+
* validation has to be a lint you can run. This strictly parses every ticket
|
|
7
|
+
* comment and goal file on every board at or below the check root (the same
|
|
8
|
+
* walk `task serve` does) and reports what would degrade or throw at read
|
|
9
|
+
* time: files that don't parse, invalid timestamps, dangling `blocked_by`
|
|
10
|
+
* references (one branch deleted a ticket another branch linked to), `goal:`
|
|
11
|
+
* refs naming a goal that doesn't exist, tickets duplicated between the board
|
|
12
|
+
* and the archive, unknown frontmatter keys, and formatting drift from the
|
|
13
|
+
* canonical serialization (including the pre-0.8 `milestone:` key, whose
|
|
14
|
+
* rewrite to `goal:` is the migration).
|
|
15
|
+
*
|
|
16
|
+
* The mechanical repairs — pruning dangling or self references, deduping and
|
|
17
|
+
* sorting `blocked_by`, rewriting a file into canonical form — are applied by
|
|
18
|
+
* `--fix`. Anything that needs a decision (a status that isn't a status, a
|
|
19
|
+
* missing title) is reported and left alone. Cheap enough for pre-commit and
|
|
20
|
+
* CI, which is where merge-introduced drift gets caught.
|
|
21
|
+
*/
|
|
22
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import { ARCHIVE_DIR, COMMENTS_DIR, GOALS_ARCHIVE_DIR, GOALS_DIR, TICKETS_DIR, TICKET_FILE, } from "./file-store.js";
|
|
25
|
+
import { COMMENT_FIELDS, TicketParseError, frontmatterKeys, isGoalSlug, parseComment, parseGoal, parseTicket, serializeComment, serializeGoal, serializeTicket, } from "./ticket-doc.js";
|
|
26
|
+
import { CONFIG_FILE, TASK_DIR, boardConfig, findBoards, findRoot, findScopeRoot, } from "./store.js";
|
|
27
|
+
import { claimNamespace } from "./claim.js";
|
|
28
|
+
/** The boards `task check` covers from `cwd` — the same set `task serve` has. */
|
|
29
|
+
export function checkRoot(cwd) {
|
|
30
|
+
return findRoot(cwd) ?? cwd;
|
|
31
|
+
}
|
|
32
|
+
function posixJoin(...parts) {
|
|
33
|
+
return parts.filter((p) => p !== "" && p !== ".").join("/");
|
|
34
|
+
}
|
|
35
|
+
/** Strip the `where: ` prefix strict parsers put in messages — we report the file. */
|
|
36
|
+
function reason(error, where) {
|
|
37
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
38
|
+
return message.startsWith(`${where}: `) ? message.slice(where.length + 2) : message;
|
|
39
|
+
}
|
|
40
|
+
function isValidTimestamp(value) {
|
|
41
|
+
return value !== "" && !Number.isNaN(Date.parse(value));
|
|
42
|
+
}
|
|
43
|
+
export function checkBoards(root, fix) {
|
|
44
|
+
const boards = findBoards(root);
|
|
45
|
+
// The repo's root board is the reference for everything repo-scoped: the
|
|
46
|
+
// `boards` key may live only there, and every board's claim namespace must
|
|
47
|
+
// match its. It can sit *above* the check root (check run inside a nested
|
|
48
|
+
// board), so it's resolved by walking up, not taken from the walk down.
|
|
49
|
+
const scopeRoot = findScopeRoot(root);
|
|
50
|
+
// Normalized through claimNamespace so "claude/task" and "claude/task/"
|
|
51
|
+
// compare equal; an unreadable or malformed root config means "no
|
|
52
|
+
// reference" (the root board's own check reports the problem).
|
|
53
|
+
let rootBranchPrefix = null;
|
|
54
|
+
try {
|
|
55
|
+
rootBranchPrefix = claimNamespace(boardConfig(scopeRoot));
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
rootBranchPrefix = null;
|
|
59
|
+
}
|
|
60
|
+
const issues = [];
|
|
61
|
+
const fixed = [];
|
|
62
|
+
let files = 0;
|
|
63
|
+
for (const board of boards) {
|
|
64
|
+
files += checkBoard(board, scopeRoot, rootBranchPrefix, fix, issues, fixed);
|
|
65
|
+
}
|
|
66
|
+
return { boards: boards.length, files, issues, fixed };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Config lints (TAS-27): the pre-0.8 `claimPrefix` spelling (fixable — the
|
|
70
|
+
* migration to `claims.branchPrefix` is mechanical), a `boards` list on a
|
|
71
|
+
* non-root board (root-only by design, so vendored boards can't opt into a
|
|
72
|
+
* repo's automation), a malformed one, an unusable branch prefix, and a
|
|
73
|
+
* branch prefix that disagrees with the root board's (workflows match claim
|
|
74
|
+
* branches by pattern — one repo, one namespace).
|
|
75
|
+
*/
|
|
76
|
+
function checkConfig(board, scopeRoot, rootBranchPrefix, fix, push, fixed) {
|
|
77
|
+
const absolute = join(board.root, TASK_DIR, CONFIG_FILE);
|
|
78
|
+
const file = posixJoin(board.id, TASK_DIR, CONFIG_FILE);
|
|
79
|
+
let raw;
|
|
80
|
+
try {
|
|
81
|
+
raw = JSON.parse(readFileSync(absolute, "utf8"));
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
push(file, reason(error, ""));
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const config = raw;
|
|
88
|
+
if (raw.claimPrefix !== undefined) {
|
|
89
|
+
push(file, "`claimPrefix` is now `claims.branchPrefix` — `task check --fix` rewrites it", true);
|
|
90
|
+
if (fix) {
|
|
91
|
+
// In-place key replacement so the migration diff is one line where
|
|
92
|
+
// possible; an existing claims.branchPrefix wins over the legacy key.
|
|
93
|
+
const claims = {
|
|
94
|
+
...(typeof raw.claims === "object" && raw.claims !== null
|
|
95
|
+
? raw.claims
|
|
96
|
+
: {}),
|
|
97
|
+
};
|
|
98
|
+
claims.branchPrefix ??= raw.claimPrefix;
|
|
99
|
+
const migrated = {};
|
|
100
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
101
|
+
if (key === "claimPrefix") {
|
|
102
|
+
if (raw.claims === undefined)
|
|
103
|
+
migrated.claims = claims;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
migrated[key] = key === "claims" ? claims : value;
|
|
107
|
+
}
|
|
108
|
+
writeFileSync(absolute, `${JSON.stringify(migrated, null, 2)}\n`);
|
|
109
|
+
fixed.push(file);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (raw.boards !== undefined) {
|
|
113
|
+
if (board.root !== scopeRoot) {
|
|
114
|
+
push(file, '"boards" is root-only — automation reads it from the repo\'s root board; remove it here');
|
|
115
|
+
}
|
|
116
|
+
else if (!Array.isArray(raw.boards) ||
|
|
117
|
+
raw.boards.length === 0 ||
|
|
118
|
+
raw.boards.some((b) => typeof b !== "string")) {
|
|
119
|
+
push(file, '"boards" must be a non-empty array of board prefixes, e.g. ["NIC", "TAS"]');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
let branchPrefix = null;
|
|
123
|
+
try {
|
|
124
|
+
branchPrefix = claimNamespace(config);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
push(file, reason(error, ""));
|
|
128
|
+
}
|
|
129
|
+
if (branchPrefix !== null &&
|
|
130
|
+
rootBranchPrefix !== null &&
|
|
131
|
+
board.root !== scopeRoot &&
|
|
132
|
+
branchPrefix !== rootBranchPrefix) {
|
|
133
|
+
push(file, `claims.branchPrefix ${JSON.stringify(branchPrefix)} disagrees with the root board's ${JSON.stringify(rootBranchPrefix)} — workflows match claim branches by pattern, keep one namespace per repo`);
|
|
134
|
+
}
|
|
135
|
+
return config;
|
|
136
|
+
}
|
|
137
|
+
function checkBoard(board, scopeRoot, rootBranchPrefix, fix, issues, fixed) {
|
|
138
|
+
const taskDir = join(board.root, TASK_DIR);
|
|
139
|
+
const relTaskDir = posixJoin(board.id, TASK_DIR);
|
|
140
|
+
const push = (file, message, fixable = false) => issues.push({ board: board.id, file, message, fixable });
|
|
141
|
+
const config = checkConfig(board, scopeRoot, rootBranchPrefix, fix, push, fixed);
|
|
142
|
+
const prefix = config?.prefix ?? "TASK";
|
|
143
|
+
const displayId = (n) => `${prefix}-${n}`;
|
|
144
|
+
// ── Collect every ticket, live and archived ────────────────────────────────
|
|
145
|
+
const tickets = [];
|
|
146
|
+
let files = 0;
|
|
147
|
+
for (const [dir, archived] of [
|
|
148
|
+
[TICKETS_DIR, false],
|
|
149
|
+
[ARCHIVE_DIR, true],
|
|
150
|
+
]) {
|
|
151
|
+
const home = join(taskDir, dir);
|
|
152
|
+
if (!existsSync(home))
|
|
153
|
+
continue;
|
|
154
|
+
for (const entry of readdirSync(home, { withFileTypes: true })) {
|
|
155
|
+
if (entry.name.startsWith("."))
|
|
156
|
+
continue;
|
|
157
|
+
const relEntry = posixJoin(relTaskDir, dir, entry.name);
|
|
158
|
+
if (!entry.isDirectory() || !/^\d+$/.test(entry.name)) {
|
|
159
|
+
push(relEntry, "unexpected entry — the board only reads numeric ticket directories");
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const absolute = join(home, entry.name, TICKET_FILE);
|
|
163
|
+
const file = posixJoin(relEntry, TICKET_FILE);
|
|
164
|
+
if (!existsSync(absolute)) {
|
|
165
|
+
push(file, "ticket directory without a ticket.md");
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
files++;
|
|
169
|
+
const text = readFileSync(absolute, "utf8");
|
|
170
|
+
let doc = null;
|
|
171
|
+
try {
|
|
172
|
+
doc = parseTicket(text, file);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
if (!(error instanceof TicketParseError))
|
|
176
|
+
throw error;
|
|
177
|
+
push(file, reason(error, file));
|
|
178
|
+
}
|
|
179
|
+
tickets.push({ number: Number(entry.name), file, absolute, archived, text, doc });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// A ticket present in both tickets/ and archive/ is a merge artifact: one
|
|
183
|
+
// branch archived it while another kept editing it. Which copy wins is a
|
|
184
|
+
// human call, so it's reported, never auto-fixed.
|
|
185
|
+
const live = new Set(tickets.filter((t) => !t.archived).map((t) => t.number));
|
|
186
|
+
const valid = new Set(tickets.map((t) => t.number));
|
|
187
|
+
for (const ticket of tickets) {
|
|
188
|
+
if (ticket.archived && live.has(ticket.number)) {
|
|
189
|
+
push(ticket.file, `${displayId(ticket.number)} also exists in ${TICKETS_DIR}/ — a merge kept both copies; delete one`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// ── Goals ──────────────────────────────────────────────────────────────────
|
|
193
|
+
// Same strict pass as tickets: parse, timestamps, unknown keys, canonical
|
|
194
|
+
// form. `goalSlugs` (live and archived together) is what ticket refs are
|
|
195
|
+
// checked against below — an archived goal still resolves.
|
|
196
|
+
const goalSlugs = new Set();
|
|
197
|
+
const liveGoalSlugs = new Set();
|
|
198
|
+
for (const [dir, goalArchived] of [
|
|
199
|
+
[GOALS_DIR, false],
|
|
200
|
+
[`${GOALS_DIR}/${GOALS_ARCHIVE_DIR}`, true],
|
|
201
|
+
]) {
|
|
202
|
+
const home = join(taskDir, dir);
|
|
203
|
+
if (!existsSync(home))
|
|
204
|
+
continue;
|
|
205
|
+
for (const entry of readdirSync(home, { withFileTypes: true })) {
|
|
206
|
+
if (entry.name.startsWith("."))
|
|
207
|
+
continue;
|
|
208
|
+
if (entry.isDirectory() && !goalArchived && entry.name === GOALS_ARCHIVE_DIR)
|
|
209
|
+
continue;
|
|
210
|
+
const file = posixJoin(relTaskDir, dir, entry.name);
|
|
211
|
+
if (entry.isDirectory() || !entry.name.endsWith(".md")) {
|
|
212
|
+
push(file, "unexpected entry — goals/ holds one <slug>.md per goal");
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
files++;
|
|
216
|
+
const slug = entry.name.slice(0, -3);
|
|
217
|
+
const text = readFileSync(join(home, entry.name), "utf8");
|
|
218
|
+
let doc;
|
|
219
|
+
try {
|
|
220
|
+
doc = parseGoal(text, file);
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
if (!(error instanceof TicketParseError))
|
|
224
|
+
throw error;
|
|
225
|
+
push(file, reason(error, file));
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (goalArchived && liveGoalSlugs.has(slug)) {
|
|
229
|
+
push(file, `goal "${slug}" also exists in ${GOALS_DIR}/ — a merge kept both copies; delete one`);
|
|
230
|
+
}
|
|
231
|
+
goalSlugs.add(slug);
|
|
232
|
+
if (!goalArchived)
|
|
233
|
+
liveGoalSlugs.add(slug);
|
|
234
|
+
if (!isGoalSlug(slug)) {
|
|
235
|
+
push(file, `"${slug}" is not a valid goal slug — lowercase letters, digits and dashes`);
|
|
236
|
+
}
|
|
237
|
+
for (const [key, at] of [
|
|
238
|
+
[doc.createdAt, "created"],
|
|
239
|
+
[doc.updatedAt, "updated"],
|
|
240
|
+
]) {
|
|
241
|
+
if (!isValidTimestamp(key))
|
|
242
|
+
push(file, `${at} is not a valid timestamp`);
|
|
243
|
+
}
|
|
244
|
+
for (const [key] of doc.extras ?? []) {
|
|
245
|
+
push(file, `unknown frontmatter key "${key}" — preserved on rewrite, but nothing reads it`);
|
|
246
|
+
}
|
|
247
|
+
const canonical = serializeGoal(doc);
|
|
248
|
+
if (canonical !== text) {
|
|
249
|
+
push(file, "not in canonical form — `task check --fix` rewrites it", true);
|
|
250
|
+
if (fix) {
|
|
251
|
+
writeFileSync(join(home, entry.name), canonical);
|
|
252
|
+
fixed.push(file);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// ── Per-ticket content checks ──────────────────────────────────────────────
|
|
258
|
+
for (const ticket of tickets) {
|
|
259
|
+
const { doc, file, number } = ticket;
|
|
260
|
+
if (!doc)
|
|
261
|
+
continue;
|
|
262
|
+
for (const [key, at] of [
|
|
263
|
+
[doc.createdAt, "created"],
|
|
264
|
+
[doc.updatedAt, "updated"],
|
|
265
|
+
]) {
|
|
266
|
+
if (!isValidTimestamp(key))
|
|
267
|
+
push(file, `${at} is not a valid timestamp`);
|
|
268
|
+
}
|
|
269
|
+
for (const [key] of doc.extras ?? []) {
|
|
270
|
+
push(file, `unknown frontmatter key "${key}" — preserved on rewrite, but nothing reads it`);
|
|
271
|
+
}
|
|
272
|
+
if (ticket.archived && doc.status !== "done" && doc.status !== "canceled") {
|
|
273
|
+
push(file, `archived but still ${doc.status} — only done or canceled tickets belong in the archive`);
|
|
274
|
+
}
|
|
275
|
+
// A dangling goal ref is deliberately NOT fixable: unlike a blocked_by
|
|
276
|
+
// pointing at a deleted ticket (pure mechanics), a goal ref is
|
|
277
|
+
// organization — whether to create the missing goal or clear the ref is a
|
|
278
|
+
// human call, so the lint states both options and touches nothing.
|
|
279
|
+
if (doc.goal && !goalSlugs.has(doc.goal)) {
|
|
280
|
+
push(file, `goal "${doc.goal}" doesn't exist — create it with \`task goal add\`, or clear the ref with \`--goal ""\``);
|
|
281
|
+
}
|
|
282
|
+
// The repaired relation list: no self references, no dangling targets, no
|
|
283
|
+
// duplicates, sorted. Each divergence is its own issue; all are mechanical.
|
|
284
|
+
const pruned = [...new Set(doc.blockedBy)]
|
|
285
|
+
.filter((n) => n !== number && valid.has(n))
|
|
286
|
+
.sort((a, b) => a - b);
|
|
287
|
+
if (doc.blockedBy.includes(number)) {
|
|
288
|
+
push(file, `${displayId(number)} is blocked by itself`, true);
|
|
289
|
+
}
|
|
290
|
+
for (const target of new Set(doc.blockedBy)) {
|
|
291
|
+
if (target !== number && !valid.has(target)) {
|
|
292
|
+
push(file, `blocked_by references ${displayId(target)}, which doesn't exist`, true);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// Everything else — duplicate or unsorted blocked_by, hand-edited field
|
|
296
|
+
// order or quoting, stray whitespace — is formatting drift: the parse is
|
|
297
|
+
// fine, the bytes just aren't what the CLI would write. `normalized` keeps
|
|
298
|
+
// the dangling refs so drift isn't double-reported on top of them.
|
|
299
|
+
//
|
|
300
|
+
// One drift gets its own words: a pre-0.8 `milestone:` key. It already
|
|
301
|
+
// parses as the goal, and the canonical rewrite is the whole migration —
|
|
302
|
+
// but "not in canonical form" would undersell what the fix is about to do.
|
|
303
|
+
const normalized = serializeTicket({
|
|
304
|
+
...doc,
|
|
305
|
+
blockedBy: [...new Set(doc.blockedBy)].sort((a, b) => a - b),
|
|
306
|
+
});
|
|
307
|
+
if (frontmatterKeys(ticket.text)?.includes("milestone")) {
|
|
308
|
+
push(file, "`milestone:` is now `goal:` — `task check --fix` rewrites it", true);
|
|
309
|
+
}
|
|
310
|
+
else if (normalized !== ticket.text) {
|
|
311
|
+
push(file, "not in canonical form — `task check --fix` rewrites it", true);
|
|
312
|
+
}
|
|
313
|
+
const canonical = serializeTicket({ ...doc, blockedBy: pruned });
|
|
314
|
+
if (fix && canonical !== ticket.text) {
|
|
315
|
+
writeFileSync(ticket.absolute, canonical);
|
|
316
|
+
fixed.push(file);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
// ── Comments ───────────────────────────────────────────────────────────────
|
|
320
|
+
const knownCommentKeys = new Set(COMMENT_FIELDS);
|
|
321
|
+
for (const ticket of tickets) {
|
|
322
|
+
const dir = join(ticket.absolute, "..", COMMENTS_DIR);
|
|
323
|
+
if (!existsSync(dir))
|
|
324
|
+
continue;
|
|
325
|
+
for (const entry of readdirSync(dir)) {
|
|
326
|
+
if (!entry.endsWith(".md") || entry.startsWith("."))
|
|
327
|
+
continue;
|
|
328
|
+
files++;
|
|
329
|
+
const file = posixJoin(ticket.file.slice(0, -TICKET_FILE.length - 1), COMMENTS_DIR, entry);
|
|
330
|
+
const text = readFileSync(join(dir, entry), "utf8");
|
|
331
|
+
let parsed;
|
|
332
|
+
try {
|
|
333
|
+
parsed = parseComment(text, file);
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
if (!(error instanceof TicketParseError))
|
|
337
|
+
throw error;
|
|
338
|
+
push(file, reason(error, file));
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
if (!isValidTimestamp(parsed.createdAt))
|
|
342
|
+
push(file, "created is not a valid timestamp");
|
|
343
|
+
let unknownKeys = false;
|
|
344
|
+
for (const key of frontmatterKeys(text) ?? []) {
|
|
345
|
+
if (knownCommentKeys.has(key))
|
|
346
|
+
continue;
|
|
347
|
+
unknownKeys = true;
|
|
348
|
+
push(file, `unknown frontmatter key "${key}"`);
|
|
349
|
+
}
|
|
350
|
+
// A comment's serialization carries only the known fields, so rewriting
|
|
351
|
+
// one with unknown keys would delete them — skip the drift fix there.
|
|
352
|
+
const canonical = serializeComment(parsed);
|
|
353
|
+
if (canonical !== text && !unknownKeys) {
|
|
354
|
+
push(file, "not in canonical form — `task check --fix` rewrites it", true);
|
|
355
|
+
if (fix) {
|
|
356
|
+
writeFileSync(join(dir, entry), canonical);
|
|
357
|
+
fixed.push(file);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return files;
|
|
363
|
+
}
|
|
364
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `task check` exists for the write paths the CLI never sees — git merges and
|
|
3
|
+
* hand-edits — so its tests are those: files written slightly wrong, links
|
|
4
|
+
* whose target one branch deleted, tickets a merge duplicated into the board
|
|
5
|
+
* *and* the archive. Plus the sharp edge fixed alongside it: unknown
|
|
6
|
+
* frontmatter keys now survive a rewrite instead of being dropped.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=check.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.test.d.ts","sourceRoot":"","sources":["../src/check.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|