@nickmeriano/task 0.7.1 → 0.9.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 +73 -25
- package/dist/asks.test.d.ts +17 -0
- package/dist/asks.test.d.ts.map +1 -0
- package/dist/asks.test.js +278 -0
- package/dist/asks.test.js.map +1 -0
- package/dist/check.d.ts +43 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +403 -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 +248 -0
- package/dist/check.test.js.map +1 -0
- package/dist/claim-io.d.ts +73 -0
- package/dist/claim-io.d.ts.map +1 -0
- package/dist/claim-io.js +344 -0
- package/dist/claim-io.js.map +1 -0
- package/dist/claim.d.ts +61 -9
- package/dist/claim.d.ts.map +1 -1
- package/dist/claim.js +197 -67
- package/dist/claim.js.map +1 -1
- package/dist/claim.test.d.ts +2 -2
- package/dist/claim.test.js +235 -64
- package/dist/claim.test.js.map +1 -1
- package/dist/cli.js +724 -136
- package/dist/cli.js.map +1 -1
- package/dist/file-store.d.ts +110 -38
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +514 -238
- package/dist/file-store.js.map +1 -1
- package/dist/git-serve.d.ts +183 -0
- package/dist/git-serve.d.ts.map +1 -0
- package/dist/git-serve.js +503 -0
- package/dist/git-serve.js.map +1 -0
- package/dist/git-serve.test.d.ts +16 -0
- package/dist/git-serve.test.d.ts.map +1 -0
- package/dist/git-serve.test.js +183 -0
- package/dist/git-serve.test.js.map +1 -0
- package/dist/git.d.ts +65 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +114 -0
- package/dist/git.js.map +1 -0
- package/dist/id.d.ts +39 -0
- package/dist/id.d.ts.map +1 -0
- package/dist/id.js +67 -0
- package/dist/id.js.map +1 -0
- package/dist/inbox.d.ts +41 -0
- package/dist/inbox.d.ts.map +1 -0
- package/dist/inbox.js +56 -0
- package/dist/inbox.js.map +1 -0
- 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 +52 -0
- package/dist/overview.d.ts.map +1 -0
- package/dist/overview.js +61 -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 +48 -0
- package/dist/overview.test.js.map +1 -0
- package/dist/promote.test.d.ts +15 -0
- package/dist/promote.test.d.ts.map +1 -0
- package/dist/promote.test.js +104 -0
- package/dist/promote.test.js.map +1 -0
- package/dist/publish.d.ts +2 -17
- package/dist/publish.d.ts.map +1 -1
- package/dist/publish.js +4 -49
- package/dist/publish.js.map +1 -1
- package/dist/search.d.ts +34 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +79 -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 +228 -23
- package/dist/server.js.map +1 -1
- package/dist/store.d.ts +43 -63
- 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 +148 -106
- package/dist/store.test.js.map +1 -1
- package/dist/ticket-doc.d.ts +74 -5
- package/dist/ticket-doc.d.ts.map +1 -1
- package/dist/ticket-doc.js +229 -15
- package/dist/ticket-doc.js.map +1 -1
- package/dist/types.d.ts +115 -28
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +153 -40
- package/src/asks.test.ts +355 -0
- package/src/check.test.ts +328 -0
- package/src/check.ts +497 -0
- package/src/claim-io.ts +401 -0
- package/src/claim.test.ts +301 -71
- package/src/claim.ts +238 -81
- package/src/cli.ts +740 -131
- package/src/file-store.ts +572 -254
- package/src/git-serve.test.ts +240 -0
- package/src/git-serve.ts +595 -0
- package/src/git.ts +141 -0
- package/src/id.ts +68 -0
- package/src/inbox.ts +77 -0
- package/src/index.ts +4 -2
- package/src/overview.test.ts +52 -0
- package/src/overview.ts +105 -0
- package/src/promote.test.ts +143 -0
- package/src/publish.ts +6 -53
- package/src/search.test.ts +64 -0
- package/src/search.ts +105 -0
- package/src/server.ts +232 -21
- package/src/store.test.ts +166 -116
- package/src/store.ts +46 -444
- package/src/ticket-doc.ts +284 -21
- package/src/types.ts +120 -28
- package/ui/dist/assets/index-BjsorZOU.js +229 -0
- package/ui/dist/assets/index-CoKCUYic.css +1 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-COunM-QN.css +0 -1
- package/ui/dist/assets/index-D4homvrQ.js +0 -229
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,KAAK,QAAQ,EACb,KAAK,KAAK,GACX,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,WAAW,EACX,SAAS,EACT,WAAW,EACX,WAAW,EACX,SAAS,GACV,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,SAAS,EACd,KAAK,UAAU,GAChB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAA;AACtF,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAC9E,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,KAAK,EACL,WAAW,EACX,cAAc,EACd,cAAc,EACd,OAAO,EACP,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,aAAa,GACnB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,KAAK,MAAM,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AACtF,cAAc,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// Programmatic access to the same store the CLI and `task serve` use — for
|
|
2
2
|
// scripts that want to read or write .task/ without shelling out.
|
|
3
|
-
export {
|
|
4
|
-
export { ARCHIVE_DIR, FileStore, TICKETS_DIR, initProject,
|
|
5
|
-
export { parseTicket, serializeTicket, parseComment, serializeComment, } from "./ticket-doc.js";
|
|
3
|
+
export { boardConfig, derivePrefix, findBoards, findBoardsByPrefix, findRoot, findScopeRoot, } from "./store.js";
|
|
4
|
+
export { ARCHIVE_DIR, FileStore, TICKETS_DIR, initProject, openBoard, } from "./file-store.js";
|
|
5
|
+
export { parseTicket, parseTicketLenient, serializeTicket, parseComment, parseCommentLenient, serializeComment, } from "./ticket-doc.js";
|
|
6
|
+
export { checkBoards, checkRoot } from "./check.js";
|
|
7
|
+
export { searchStore } from "./search.js";
|
|
6
8
|
export { DEFAULT_CLAIM_PREFIX, ClaimError, claim, claimBranch, claimNamespace, claimableTasks, release, remoteClaims, } from "./claim.js";
|
|
7
9
|
export { createTaskServer } from "./server.js";
|
|
8
10
|
export { resolveAuthor, ANONYMOUS } from "./author.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,kEAAkE;AAClE,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,kEAAkE;AAClE,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,QAAQ,EACR,aAAa,GAGd,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,WAAW,EACX,SAAS,EACT,WAAW,EACX,WAAW,EACX,SAAS,GACV,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,GAGjB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,WAAW,EAAE,SAAS,EAAqC,MAAM,YAAY,CAAA;AACtF,OAAO,EAAE,WAAW,EAAuC,MAAM,aAAa,CAAA;AAC9E,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,KAAK,EACL,WAAW,EACX,cAAc,EACd,cAAc,EACd,OAAO,EACP,YAAY,GAGb,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,aAAa,EAAE,SAAS,EAAkC,MAAM,aAAa,CAAA;AACtF,cAAc,YAAY,CAAA"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `task overview` — one screen of board health (TAS-27): the numbers an agent
|
|
3
|
+
* wants before choosing work and the numbers a human wants in a standup.
|
|
4
|
+
* Nothing configurable, no charts, and nothing stored — every figure is
|
|
5
|
+
* derived from the board at read time, the same rule goals follow.
|
|
6
|
+
*/
|
|
7
|
+
import type { Store } from "./store.ts";
|
|
8
|
+
import { type Status } from "./types.ts";
|
|
9
|
+
/** Untouched this long, an open ticket is stale. Fixed on purpose. */
|
|
10
|
+
export declare const STALE_DAYS = 14;
|
|
11
|
+
export interface GoalProgress {
|
|
12
|
+
slug: string;
|
|
13
|
+
title: string;
|
|
14
|
+
done: number;
|
|
15
|
+
total: number;
|
|
16
|
+
}
|
|
17
|
+
export interface TicketRef {
|
|
18
|
+
id: string;
|
|
19
|
+
title: string;
|
|
20
|
+
}
|
|
21
|
+
export interface Overview {
|
|
22
|
+
board: string;
|
|
23
|
+
prefix: string;
|
|
24
|
+
/** Live board only — the archive is history, not health. */
|
|
25
|
+
statuses: Record<Status, number>;
|
|
26
|
+
open: number;
|
|
27
|
+
/** Open tickets a person owes something — i.e. carrying open asks. */
|
|
28
|
+
needsHuman: number;
|
|
29
|
+
/** Open asks across the live board's open tickets. */
|
|
30
|
+
openAsks: number;
|
|
31
|
+
/** The ask that's been owed longest — the standup's guilty number. */
|
|
32
|
+
oldestAsk: {
|
|
33
|
+
taskId: string;
|
|
34
|
+
text: string;
|
|
35
|
+
createdAt: string;
|
|
36
|
+
} | null;
|
|
37
|
+
/**
|
|
38
|
+
* Open tickets whose claim branch exists on origin right now; null when
|
|
39
|
+
* origin can't be asked (no remote, offline) — unknown, not zero.
|
|
40
|
+
*/
|
|
41
|
+
claimed: string[] | null;
|
|
42
|
+
goals: GoalProgress[];
|
|
43
|
+
/** Open tickets untouched for STALE_DAYS+, oldest first. */
|
|
44
|
+
stale: (TicketRef & {
|
|
45
|
+
updatedAt: string;
|
|
46
|
+
})[];
|
|
47
|
+
oldestOpen: (TicketRef & {
|
|
48
|
+
createdAt: string;
|
|
49
|
+
}) | null;
|
|
50
|
+
}
|
|
51
|
+
export declare function buildOverview(store: Store, now?: number): Overview;
|
|
52
|
+
//# sourceMappingURL=overview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overview.d.ts","sourceRoot":"","sources":["../src/overview.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAY,KAAK,MAAM,EAAE,MAAM,YAAY,CAAA;AAElD,sEAAsE;AACtE,eAAO,MAAM,UAAU,KAAK,CAAA;AAI5B,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAA;IAClB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAA;IAChB,sEAAsE;IACtE,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACrE;;;OAGG;IACH,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACxB,KAAK,EAAE,YAAY,EAAE,CAAA;IACrB,4DAA4D;IAC5D,KAAK,EAAE,CAAC,SAAS,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAA;IAC5C,UAAU,EAAE,CAAC,SAAS,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI,CAAA;CACvD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,GAAE,MAAmB,GAAG,QAAQ,CAoD9E"}
|
package/dist/overview.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `task overview` — one screen of board health (TAS-27): the numbers an agent
|
|
3
|
+
* wants before choosing work and the numbers a human wants in a standup.
|
|
4
|
+
* Nothing configurable, no charts, and nothing stored — every figure is
|
|
5
|
+
* derived from the board at read time, the same rule goals follow.
|
|
6
|
+
*/
|
|
7
|
+
import { claimBranch } from "./claim.js";
|
|
8
|
+
import { remoteClaimTips } from "./claim-io.js";
|
|
9
|
+
import { STATUSES } from "./types.js";
|
|
10
|
+
/** Untouched this long, an open ticket is stale. Fixed on purpose. */
|
|
11
|
+
export const STALE_DAYS = 14;
|
|
12
|
+
const OPEN_STATUSES = ["backlog", "todo", "in_progress"];
|
|
13
|
+
export function buildOverview(store, now = Date.now()) {
|
|
14
|
+
const tasks = store.list({ statuses: undefined });
|
|
15
|
+
const statuses = Object.fromEntries(STATUSES.map((s) => [s, 0]));
|
|
16
|
+
for (const task of tasks)
|
|
17
|
+
statuses[task.status]++;
|
|
18
|
+
const open = tasks.filter((t) => OPEN_STATUSES.includes(t.status));
|
|
19
|
+
// The bounded read — a board health check must never hang on a credential
|
|
20
|
+
// prompt; unreachable origin reads as "claimed ?" (null), not zero.
|
|
21
|
+
const tips = remoteClaimTips(store);
|
|
22
|
+
const claimed = tips
|
|
23
|
+
? open.filter((t) => tips.has(claimBranch(store.config, t.key))).map((t) => t.id)
|
|
24
|
+
: null;
|
|
25
|
+
const goals = store.goals().map((goal) => {
|
|
26
|
+
const mine = tasks.filter((t) => t.goal === goal.slug);
|
|
27
|
+
const done = mine.filter((t) => t.status === "done" || t.status === "canceled").length;
|
|
28
|
+
return { slug: goal.slug, title: goal.title, done, total: mine.length };
|
|
29
|
+
});
|
|
30
|
+
const staleBefore = now - STALE_DAYS * 24 * 60 * 60 * 1000;
|
|
31
|
+
const stale = open
|
|
32
|
+
.filter((t) => Date.parse(t.updatedAt) < staleBefore)
|
|
33
|
+
.sort((a, b) => Date.parse(a.updatedAt) - Date.parse(b.updatedAt))
|
|
34
|
+
.map((t) => ({ id: t.id, title: t.title, updatedAt: t.updatedAt }));
|
|
35
|
+
const oldest = [...open].sort((a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt))[0];
|
|
36
|
+
const openAsks = open
|
|
37
|
+
.flatMap((t) => t.asks.filter((a) => !a.resolvedAt).map((a) => ({ taskId: t.id, ask: a })))
|
|
38
|
+
.sort((a, b) => a.ask.createdAt.localeCompare(b.ask.createdAt));
|
|
39
|
+
return {
|
|
40
|
+
board: store.config.name,
|
|
41
|
+
prefix: store.config.prefix,
|
|
42
|
+
statuses,
|
|
43
|
+
open: open.length,
|
|
44
|
+
needsHuman: open.filter((t) => t.needsHuman).length,
|
|
45
|
+
openAsks: openAsks.length,
|
|
46
|
+
oldestAsk: openAsks[0]
|
|
47
|
+
? {
|
|
48
|
+
taskId: openAsks[0].taskId,
|
|
49
|
+
text: openAsks[0].ask.text,
|
|
50
|
+
createdAt: openAsks[0].ask.createdAt,
|
|
51
|
+
}
|
|
52
|
+
: null,
|
|
53
|
+
claimed,
|
|
54
|
+
goals,
|
|
55
|
+
stale,
|
|
56
|
+
oldestOpen: oldest
|
|
57
|
+
? { id: oldest.id, title: oldest.title, createdAt: oldest.createdAt }
|
|
58
|
+
: null,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=overview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overview.js","sourceRoot":"","sources":["../src/overview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `task overview` derives everything at read time — these tests pin the
|
|
3
|
+
* derivations: per-status counts, per-goal progress, needs-human, staleness
|
|
4
|
+
* against an injected clock, oldest-open, and the "unknown, not zero" answer
|
|
5
|
+
* for claims when there is no origin to ask.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=overview.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overview.test.d.ts","sourceRoot":"","sources":["../src/overview.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `task overview` derives everything at read time — these tests pin the
|
|
3
|
+
* derivations: per-status counts, per-goal progress, needs-human, staleness
|
|
4
|
+
* against an injected clock, oldest-open, and the "unknown, not zero" answer
|
|
5
|
+
* for claims when there is no origin to ask.
|
|
6
|
+
*/
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { test } from "node:test";
|
|
9
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { initProject } from "./file-store.js";
|
|
13
|
+
import { STALE_DAYS, buildOverview } from "./overview.js";
|
|
14
|
+
function tempRoot() {
|
|
15
|
+
const dir = mkdtempSync(join(tmpdir(), "task-overview-"));
|
|
16
|
+
process.on("exit", () => rmSync(dir, { recursive: true, force: true }));
|
|
17
|
+
return dir;
|
|
18
|
+
}
|
|
19
|
+
test("overview derives board health at read time", () => {
|
|
20
|
+
const root = tempRoot();
|
|
21
|
+
const store = initProject(root, { name: "health", prefix: "HLT" });
|
|
22
|
+
store.createGoal({ title: "Launch" });
|
|
23
|
+
const a = store.create({ title: "Oldest", status: "todo", goal: "launch" });
|
|
24
|
+
const b = store.create({ title: "Shipped", status: "todo", goal: "launch" });
|
|
25
|
+
store.update(b.key, { status: "done" });
|
|
26
|
+
const person = store.create({ title: "Waiting on a person", status: "todo" });
|
|
27
|
+
store.addAsk(person.key, "Sign the SOC2 form", "claude");
|
|
28
|
+
store.create({ title: "Still parked", status: "backlog" });
|
|
29
|
+
// Age the first ticket by hand — the store always writes "now".
|
|
30
|
+
const ticketPath = join(root, ".task", "tickets", a.key, "ticket.md");
|
|
31
|
+
const aged = readFileSync(ticketPath, "utf8")
|
|
32
|
+
.replace(/created: .*/, "created: 2020-01-01T00:00:00.000Z")
|
|
33
|
+
.replace(/updated: .*/, "updated: 2020-01-01T00:00:00.000Z");
|
|
34
|
+
writeFileSync(ticketPath, aged);
|
|
35
|
+
const overview = buildOverview(store, Date.parse("2020-01-10T00:00:00.000Z") + STALE_DAYS * 86_400_000);
|
|
36
|
+
assert.equal(overview.board, "health");
|
|
37
|
+
assert.equal(overview.prefix, "HLT");
|
|
38
|
+
assert.deepEqual(overview.statuses, { backlog: 1, todo: 2, in_progress: 0, done: 1, canceled: 0 });
|
|
39
|
+
assert.equal(overview.open, 3);
|
|
40
|
+
assert.equal(overview.needsHuman, 1);
|
|
41
|
+
// No origin to ask — unknown, not zero.
|
|
42
|
+
assert.equal(overview.claimed, null);
|
|
43
|
+
assert.deepEqual(overview.goals, [{ slug: "launch", title: "Launch", done: 1, total: 2 }]);
|
|
44
|
+
// Only the aged ticket is stale at that clock (the others were written just now).
|
|
45
|
+
assert.deepEqual(overview.stale.map((t) => t.id), [a.id]);
|
|
46
|
+
assert.equal(overview.oldestOpen?.id, a.id);
|
|
47
|
+
});
|
|
48
|
+
//# sourceMappingURL=overview.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overview.test.js","sourceRoot":"","sources":["../src/overview.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `task promote` — from the outside (real CLI subprocesses, exit codes are
|
|
3
|
+
* the contract):
|
|
4
|
+
*
|
|
5
|
+
* 1. Any backlog ticket promotes to todo, exit 0 — there is no quality gate;
|
|
6
|
+
* planning happens at claim time.
|
|
7
|
+
* 2. Only backlog tickets promote — todo/in_progress/done are refused.
|
|
8
|
+
* 3. Blocked tickets still promote, with the hold reported (stdout note,
|
|
9
|
+
* `holds` in --json) — blockers are claim-time gates. Needs-human isn't
|
|
10
|
+
* one at all anymore: asks gate done, not claiming.
|
|
11
|
+
* 4. `task add` with no --status lands in backlog: the dump list is the
|
|
12
|
+
* default, todo is the deliberate choice promote makes.
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=promote.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promote.test.d.ts","sourceRoot":"","sources":["../src/promote.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `task promote` — from the outside (real CLI subprocesses, exit codes are
|
|
3
|
+
* the contract):
|
|
4
|
+
*
|
|
5
|
+
* 1. Any backlog ticket promotes to todo, exit 0 — there is no quality gate;
|
|
6
|
+
* planning happens at claim time.
|
|
7
|
+
* 2. Only backlog tickets promote — todo/in_progress/done are refused.
|
|
8
|
+
* 3. Blocked tickets still promote, with the hold reported (stdout note,
|
|
9
|
+
* `holds` in --json) — blockers are claim-time gates. Needs-human isn't
|
|
10
|
+
* one at all anymore: asks gate done, not claiming.
|
|
11
|
+
* 4. `task add` with no --status lands in backlog: the dump list is the
|
|
12
|
+
* default, todo is the deliberate choice promote makes.
|
|
13
|
+
*/
|
|
14
|
+
import assert from "node:assert/strict";
|
|
15
|
+
import { test } from "node:test";
|
|
16
|
+
import { execFile } from "node:child_process";
|
|
17
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
18
|
+
import { tmpdir } from "node:os";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { fileURLToPath } from "node:url";
|
|
21
|
+
import { initProject } from "./file-store.js";
|
|
22
|
+
const CLI = fileURLToPath(new URL("./cli.ts", import.meta.url));
|
|
23
|
+
function tempDir() {
|
|
24
|
+
const dir = mkdtempSync(join(tmpdir(), "task-promote-test-"));
|
|
25
|
+
process.on("exit", () => rmSync(dir, { recursive: true, force: true }));
|
|
26
|
+
return dir;
|
|
27
|
+
}
|
|
28
|
+
/** The CLI as callers see it — a subprocess with an exit code. */
|
|
29
|
+
function cli(cwd, ...args) {
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
execFile(process.execPath, ["--experimental-strip-types", CLI, ...args], { cwd }, (error, stdout, stderr) => {
|
|
32
|
+
resolve({ code: error ? (error.code ?? 1) : 0, stdout, stderr });
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function fixture() {
|
|
37
|
+
const dir = tempDir();
|
|
38
|
+
const store = initProject(dir, { name: "promote board", prefix: "PRO" });
|
|
39
|
+
const liner = store.create({ title: "One-liner", status: "backlog" });
|
|
40
|
+
const also = store.create({ title: "Also backlog", status: "backlog" });
|
|
41
|
+
const blocked = store.create({ title: "Blocked", status: "backlog", blockedBy: [also.key] });
|
|
42
|
+
const person = store.create({ title: "For a person", status: "backlog" });
|
|
43
|
+
store.addAsk(person.key, "Pick the vendor", "claude");
|
|
44
|
+
const queued = store.create({ title: "Already queued", status: "todo" });
|
|
45
|
+
store.close();
|
|
46
|
+
return {
|
|
47
|
+
dir,
|
|
48
|
+
ids: { liner: liner.id, also: also.id, blocked: blocked.id, person: person.id, queued: queued.id },
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function show(dir, id) {
|
|
52
|
+
const result = await cli(dir, "show", id, "--json");
|
|
53
|
+
assert.equal(result.code, 0, result.stderr);
|
|
54
|
+
return JSON.parse(result.stdout).task;
|
|
55
|
+
}
|
|
56
|
+
test("any backlog ticket promotes to todo — no quality gate", async () => {
|
|
57
|
+
const { dir, ids } = fixture();
|
|
58
|
+
const result = await cli(dir, "promote", ids.liner, "--json");
|
|
59
|
+
assert.equal(result.code, 0, result.stderr);
|
|
60
|
+
const { task, holds } = JSON.parse(result.stdout);
|
|
61
|
+
assert.equal(task.status, "todo");
|
|
62
|
+
assert.deepEqual(holds, []);
|
|
63
|
+
assert.equal((await show(dir, ids.liner)).status, "todo");
|
|
64
|
+
});
|
|
65
|
+
test("only backlog tickets promote", async () => {
|
|
66
|
+
const { dir, ids } = fixture();
|
|
67
|
+
const result = await cli(dir, "promote", ids.queued);
|
|
68
|
+
assert.equal(result.code, 2);
|
|
69
|
+
assert.match(result.stderr, /is todo — only backlog tickets/);
|
|
70
|
+
});
|
|
71
|
+
test("blocked tickets promote with the hold reported; needs-human isn't a hold", async () => {
|
|
72
|
+
const { dir, ids } = fixture();
|
|
73
|
+
const blocked = await cli(dir, "promote", ids.blocked, "--json");
|
|
74
|
+
assert.equal(blocked.code, 0, blocked.stderr);
|
|
75
|
+
const parsed = JSON.parse(blocked.stdout);
|
|
76
|
+
assert.equal(parsed.task.status, "todo");
|
|
77
|
+
assert.deepEqual(parsed.holds, [`blocked by ${ids.also}`]);
|
|
78
|
+
// Asks gate done, not claiming — so a human-owing ticket promotes with
|
|
79
|
+
// nothing to report.
|
|
80
|
+
const person = await cli(dir, "promote", ids.person, "--json");
|
|
81
|
+
assert.equal(person.code, 0, person.stderr);
|
|
82
|
+
assert.deepEqual(JSON.parse(person.stdout).holds, []);
|
|
83
|
+
assert.equal((await show(dir, ids.person)).status, "todo");
|
|
84
|
+
});
|
|
85
|
+
test("task add with no --status lands in backlog", async () => {
|
|
86
|
+
const { dir } = fixture();
|
|
87
|
+
const added = await cli(dir, "add", "Dumped in passing", "--json");
|
|
88
|
+
assert.equal(added.code, 0, added.stderr);
|
|
89
|
+
const { task } = JSON.parse(added.stdout);
|
|
90
|
+
assert.equal(task.status, "backlog");
|
|
91
|
+
});
|
|
92
|
+
test("--description-file reads markdown from disk; combining both flags refuses", async () => {
|
|
93
|
+
const { dir } = fixture();
|
|
94
|
+
const body = "## Plan\n\nA description with \"quotes\", `backticks`,\nand $(subshells) that never touch a shell.\n";
|
|
95
|
+
writeFileSync(join(dir, "desc.md"), body);
|
|
96
|
+
const added = await cli(dir, "add", "From a file", "--description-file", "desc.md", "--json");
|
|
97
|
+
assert.equal(added.code, 0, added.stderr);
|
|
98
|
+
const { task } = JSON.parse(added.stdout);
|
|
99
|
+
assert.match(task.description ?? "", /"quotes", `backticks`,\nand \$\(subshells\)/);
|
|
100
|
+
const both = await cli(dir, "update", task.id, "--description", "x", "--description-file", "desc.md");
|
|
101
|
+
assert.equal(both.code, 1);
|
|
102
|
+
assert.match(both.stderr, /not both/);
|
|
103
|
+
});
|
|
104
|
+
//# sourceMappingURL=promote.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promote.test.js","sourceRoot":"","sources":["../src/promote.test.ts"],"names":[],"mappings":"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"}
|
package/dist/publish.d.ts
CHANGED
|
@@ -17,27 +17,12 @@
|
|
|
17
17
|
* commit, no token in the repo, and re-running this from a fresh clone is the
|
|
18
18
|
* same operation as re-running it here.
|
|
19
19
|
*/
|
|
20
|
+
import { detectRepo, parseRemote, type RepoRef } from "./git.ts";
|
|
21
|
+
export { detectRepo, parseRemote, type RepoRef };
|
|
20
22
|
/** Overridable so the flow can be exercised against a local `vite dev`. */
|
|
21
23
|
export declare const DEFAULT_HOST = "https://task.nickmeriano.com";
|
|
22
24
|
export type Visibility = "private" | "public";
|
|
23
25
|
export type Action = "publish" | "unpublish";
|
|
24
|
-
export interface RepoRef {
|
|
25
|
-
owner: string;
|
|
26
|
-
repo: string;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* The GitHub repo this working copy pushes to.
|
|
30
|
-
*
|
|
31
|
-
* `origin` first, then the only remote if there's exactly one — a clone with
|
|
32
|
-
* `upstream` and a fork remote should not silently publish the wrong one, so
|
|
33
|
-
* ambiguity is an error the user resolves with `--repo`.
|
|
34
|
-
*/
|
|
35
|
-
export declare function detectRepo(cwd: string): RepoRef | null;
|
|
36
|
-
/**
|
|
37
|
-
* Every shape git writes a GitHub remote in: https, ssh, `git@`-scp, with or
|
|
38
|
-
* without `.git`, and with or without credentials embedded in the host.
|
|
39
|
-
*/
|
|
40
|
-
export declare function parseRemote(url: string): RepoRef | null;
|
|
41
26
|
/** `owner/repo`, for `--repo`. */
|
|
42
27
|
export declare function parseSlug(slug: string): RepoRef | null;
|
|
43
28
|
export interface PublishOptions {
|
package/dist/publish.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../src/publish.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;
|
|
1
|
+
{"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../src/publish.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,OAAO,EAAE,MAAM,UAAU,CAAA;AAIhE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,OAAO,EAAE,CAAA;AAEhD,2EAA2E;AAC3E,eAAO,MAAM,YAAY,iCAAiC,CAAA;AAE1D,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE7C,MAAM,MAAM,MAAM,GAAG,SAAS,GAAG,WAAW,CAAA;AAE5C,kCAAkC;AAClC,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAItD;AA6BD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,UAAU,EAAE,UAAU,GAAG,IAAI,CAAA;IAC7B,iEAAiE;IACjE,MAAM,EAAE,OAAO,CAAA;IACf,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3B,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAA;CAC9B;AAKD;;;GAGG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CA4CpF;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAG5D"}
|
package/dist/publish.js
CHANGED
|
@@ -17,58 +17,13 @@
|
|
|
17
17
|
* commit, no token in the repo, and re-running this from a fresh clone is the
|
|
18
18
|
* same operation as re-running it here.
|
|
19
19
|
*/
|
|
20
|
-
import { execFileSync } from "node:child_process";
|
|
21
20
|
import process from "node:process";
|
|
21
|
+
import { detectRepo, parseRemote } from "./git.js";
|
|
22
|
+
// Re-exported so the CLI keeps one import site for "which repo is this";
|
|
23
|
+
// the implementations moved to git.ts with the rest of the git access.
|
|
24
|
+
export { detectRepo, parseRemote };
|
|
22
25
|
/** Overridable so the flow can be exercised against a local `vite dev`. */
|
|
23
26
|
export const DEFAULT_HOST = "https://task.nickmeriano.com";
|
|
24
|
-
/**
|
|
25
|
-
* The GitHub repo this working copy pushes to.
|
|
26
|
-
*
|
|
27
|
-
* `origin` first, then the only remote if there's exactly one — a clone with
|
|
28
|
-
* `upstream` and a fork remote should not silently publish the wrong one, so
|
|
29
|
-
* ambiguity is an error the user resolves with `--repo`.
|
|
30
|
-
*/
|
|
31
|
-
export function detectRepo(cwd) {
|
|
32
|
-
const remotes = gitRemotes(cwd);
|
|
33
|
-
const url = remotes.get("origin") ?? (remotes.size === 1 ? [...remotes.values()][0] : null);
|
|
34
|
-
return url ? parseRemote(url) : null;
|
|
35
|
-
}
|
|
36
|
-
function gitRemotes(cwd) {
|
|
37
|
-
const out = new Map();
|
|
38
|
-
let raw;
|
|
39
|
-
try {
|
|
40
|
-
raw = execFileSync("git", ["remote", "-v"], {
|
|
41
|
-
cwd,
|
|
42
|
-
encoding: "utf8",
|
|
43
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
return out;
|
|
48
|
-
}
|
|
49
|
-
for (const line of raw.split("\n")) {
|
|
50
|
-
const match = /^(\S+)\s+(\S+)\s+\(fetch\)$/.exec(line.trim());
|
|
51
|
-
if (match)
|
|
52
|
-
out.set(match[1], match[2]);
|
|
53
|
-
}
|
|
54
|
-
return out;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Every shape git writes a GitHub remote in: https, ssh, `git@`-scp, with or
|
|
58
|
-
* without `.git`, and with or without credentials embedded in the host.
|
|
59
|
-
*/
|
|
60
|
-
export function parseRemote(url) {
|
|
61
|
-
const cleaned = url.trim().replace(/\.git$/, "");
|
|
62
|
-
const match = /^(?:https?:\/\/|ssh:\/\/)?(?:[^@/]+@)?github\.com[:/]([^/]+)\/([^/]+?)\/?$/.exec(cleaned);
|
|
63
|
-
if (!match)
|
|
64
|
-
return null;
|
|
65
|
-
const [, owner, repo] = match;
|
|
66
|
-
if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$/.test(owner))
|
|
67
|
-
return null;
|
|
68
|
-
if (!/^[A-Za-z0-9._-]{1,100}$/.test(repo))
|
|
69
|
-
return null;
|
|
70
|
-
return { owner, repo };
|
|
71
|
-
}
|
|
72
27
|
/** `owner/repo`, for `--repo`. */
|
|
73
28
|
export function parseSlug(slug) {
|
|
74
29
|
const [owner, repo, ...rest] = slug.split("/");
|
package/dist/publish.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../src/publish.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../src/publish.ts"],"names":[],"mappings":"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"}
|
package/dist/search.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `task search` — find tickets by what they say, not just what they're tagged.
|
|
3
|
+
*
|
|
4
|
+
* Case-insensitive substring match over titles, descriptions and comments,
|
|
5
|
+
* scoped by the usual list filters. At current board sizes a straight scan of
|
|
6
|
+
* the parsed files is plenty; if boards ever grow to thousands of tickets,
|
|
7
|
+
* this is the one feature that would justify a derived FTS index — rebuilt
|
|
8
|
+
* from the files, never as state, which the text-canonical format was designed
|
|
9
|
+
* to allow.
|
|
10
|
+
*/
|
|
11
|
+
import type { Store } from "./store.ts";
|
|
12
|
+
import { type Task, type TaskFilter } from "./types.ts";
|
|
13
|
+
export interface SearchMatch {
|
|
14
|
+
field: "title" | "description" | "comment" | "ask";
|
|
15
|
+
/** The matching line, trimmed and windowed around the first hit. */
|
|
16
|
+
snippet: string;
|
|
17
|
+
/** Set on comment matches. */
|
|
18
|
+
commentId?: string;
|
|
19
|
+
author?: string;
|
|
20
|
+
/** Set on ask matches — the ordinal `task resolve` takes. */
|
|
21
|
+
askOrdinal?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface SearchResult {
|
|
24
|
+
task: Task;
|
|
25
|
+
matches: SearchMatch[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Every task matching `query`, with one snippet per matching field (and one
|
|
29
|
+
* per matching comment). `includeArchived` searches the archive *in addition
|
|
30
|
+
* to* the board — unlike `list --archived`, which shows the archive instead,
|
|
31
|
+
* a search is a question about everything ever written down.
|
|
32
|
+
*/
|
|
33
|
+
export declare function searchStore(store: Store, query: string, filter: TaskFilter, includeArchived: boolean): SearchResult[];
|
|
34
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAY,KAAK,IAAI,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAA;AAEjE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,GAAG,KAAK,CAAA;IAClD,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAA;IACf,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,IAAI,CAAA;IACV,OAAO,EAAE,WAAW,EAAE,CAAA;CACvB;AAgBD;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,UAAU,EAClB,eAAe,EAAE,OAAO,GACvB,YAAY,EAAE,CAiDhB"}
|
package/dist/search.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `task search` — find tickets by what they say, not just what they're tagged.
|
|
3
|
+
*
|
|
4
|
+
* Case-insensitive substring match over titles, descriptions and comments,
|
|
5
|
+
* scoped by the usual list filters. At current board sizes a straight scan of
|
|
6
|
+
* the parsed files is plenty; if boards ever grow to thousands of tickets,
|
|
7
|
+
* this is the one feature that would justify a derived FTS index — rebuilt
|
|
8
|
+
* from the files, never as state, which the text-canonical format was designed
|
|
9
|
+
* to allow.
|
|
10
|
+
*/
|
|
11
|
+
import { STATUSES } from "./types.js";
|
|
12
|
+
/** How much of a matching line a snippet shows. */
|
|
13
|
+
const SNIPPET_MAX = 100;
|
|
14
|
+
/** The first line containing the needle, windowed so the hit is visible. */
|
|
15
|
+
function snippetOf(text, needle) {
|
|
16
|
+
const lines = text.split("\n");
|
|
17
|
+
const line = (lines.find((l) => l.toLowerCase().includes(needle)) ?? lines[0]).trim();
|
|
18
|
+
if (line.length <= SNIPPET_MAX)
|
|
19
|
+
return line;
|
|
20
|
+
const at = line.toLowerCase().indexOf(needle);
|
|
21
|
+
const start = Math.max(0, Math.min(at - 30, line.length - SNIPPET_MAX));
|
|
22
|
+
const end = Math.min(line.length, start + SNIPPET_MAX);
|
|
23
|
+
return `${start > 0 ? "…" : ""}${line.slice(start, end).trim()}${end < line.length ? "…" : ""}`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Every task matching `query`, with one snippet per matching field (and one
|
|
27
|
+
* per matching comment). `includeArchived` searches the archive *in addition
|
|
28
|
+
* to* the board — unlike `list --archived`, which shows the archive instead,
|
|
29
|
+
* a search is a question about everything ever written down.
|
|
30
|
+
*/
|
|
31
|
+
export function searchStore(store, query, filter, includeArchived) {
|
|
32
|
+
const needle = query.toLowerCase();
|
|
33
|
+
const pool = [
|
|
34
|
+
...store.list(filter),
|
|
35
|
+
...(includeArchived ? store.list({ ...filter, archived: true }) : []),
|
|
36
|
+
];
|
|
37
|
+
const results = [];
|
|
38
|
+
for (const task of pool) {
|
|
39
|
+
const matches = [];
|
|
40
|
+
if (task.title.toLowerCase().includes(needle)) {
|
|
41
|
+
matches.push({ field: "title", snippet: snippetOf(task.title, needle) });
|
|
42
|
+
}
|
|
43
|
+
if (task.description.toLowerCase().includes(needle)) {
|
|
44
|
+
matches.push({ field: "description", snippet: snippetOf(task.description, needle) });
|
|
45
|
+
}
|
|
46
|
+
// Asks ride along on the task, so no extra read — and a hit names the
|
|
47
|
+
// ordinal, which is the handle you'd resolve it by.
|
|
48
|
+
for (const ask of task.asks) {
|
|
49
|
+
if (!ask.text.toLowerCase().includes(needle))
|
|
50
|
+
continue;
|
|
51
|
+
matches.push({
|
|
52
|
+
field: "ask",
|
|
53
|
+
snippet: snippetOf(ask.text, needle),
|
|
54
|
+
askOrdinal: ask.ordinal,
|
|
55
|
+
...(ask.author ? { author: ask.author } : {}),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
for (const comment of store.comments(task.key)) {
|
|
59
|
+
if (!comment.body.toLowerCase().includes(needle))
|
|
60
|
+
continue;
|
|
61
|
+
matches.push({
|
|
62
|
+
field: "comment",
|
|
63
|
+
snippet: snippetOf(comment.body, needle),
|
|
64
|
+
commentId: comment.id,
|
|
65
|
+
...(comment.author ? { author: comment.author } : {}),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (matches.length > 0)
|
|
69
|
+
results.push({ task, matches });
|
|
70
|
+
}
|
|
71
|
+
// Board order — grouped by status column, then position — same as `list`.
|
|
72
|
+
const order = new Map(STATUSES.map((s, i) => [s, i]));
|
|
73
|
+
results.sort((a, b) => order.get(a.task.status) - order.get(b.task.status) ||
|
|
74
|
+
a.task.position - b.task.position ||
|
|
75
|
+
a.task.createdAt.localeCompare(b.task.createdAt) ||
|
|
76
|
+
a.task.key.localeCompare(b.task.key));
|
|
77
|
+
return results;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.test.d.ts","sourceRoot":"","sources":["../src/search.test.ts"],"names":[],"mappings":""}
|