@metyatech/task-tracker 0.2.2 → 0.2.4
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 +15 -5
- package/dist/cli.js +130 -51
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -30,7 +30,9 @@ npm link
|
|
|
30
30
|
|
|
31
31
|
Tasks are stored in `<git-repo-root>/.tasks.jsonl` (JSONL format, one JSON object per line). All commands must be run from within a git repository. The file is created automatically on first `add`.
|
|
32
32
|
|
|
33
|
-
Each task: `{ id, description, stage, createdAt, updatedAt }`
|
|
33
|
+
Each task: `{ id, description, stage, committedEventId?, createdAt, updatedAt }`
|
|
34
|
+
|
|
35
|
+
`committedEventId` is a unique ID written when the task transitions to `committed`. It is used to locate the git commit that introduced the event into `.tasks.jsonl`, enabling accurate `pushed` detection even when `update --stage committed` is called before the actual commit.
|
|
34
36
|
|
|
35
37
|
To sync tasks across PCs, commit `.tasks.jsonl` and push/pull like any other file.
|
|
36
38
|
|
|
@@ -56,9 +58,9 @@ task-tracker list --json
|
|
|
56
58
|
### Update a task
|
|
57
59
|
|
|
58
60
|
```bash
|
|
59
|
-
task-tracker update <id> --stage
|
|
61
|
+
task-tracker update <id> --stage committed
|
|
60
62
|
task-tracker update <id> --description "Updated description"
|
|
61
|
-
task-tracker update <id> --stage
|
|
63
|
+
task-tracker update <id> --stage released --json
|
|
62
64
|
```
|
|
63
65
|
|
|
64
66
|
### Mark done
|
|
@@ -89,9 +91,17 @@ The `check` command:
|
|
|
89
91
|
|
|
90
92
|
## Lifecycle Stages
|
|
91
93
|
|
|
92
|
-
`pending` → `in-progress` → `
|
|
94
|
+
Persisted stages: `pending` → `in-progress` → `committed` → `released` → `done`
|
|
95
|
+
|
|
96
|
+
The `pushed` stage is **derived** and cannot be set manually. When a task is in `committed`
|
|
97
|
+
stage, `list` and `check` display its effective stage as `pushed` automatically once the
|
|
98
|
+
`.tasks.jsonl` commit that first introduced the task's `committedEventId` is reachable from
|
|
99
|
+
the remote upstream branch (`git merge-base --is-ancestor`). This means derivation is
|
|
100
|
+
accurate even when `update --stage committed` is called before the actual git commit.
|
|
101
|
+
|
|
102
|
+
To filter by effective `pushed` stage: `task-tracker list --stage pushed`
|
|
93
103
|
|
|
94
|
-
|
|
104
|
+
Setting `--stage pushed` on `add` or `update` is an error.
|
|
95
105
|
|
|
96
106
|
## Dev Commands
|
|
97
107
|
|
package/dist/cli.js
CHANGED
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6
|
-
import { dirname as dirname3, join as
|
|
6
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
7
7
|
import { readFileSync as readFileSync3 } from "fs";
|
|
8
8
|
|
|
9
9
|
// src/storage.ts
|
|
10
10
|
import { readFileSync, writeFileSync, existsSync, appendFileSync, mkdirSync } from "fs";
|
|
11
|
-
import { dirname, join } from "path";
|
|
11
|
+
import { dirname, join as join2 } from "path";
|
|
12
12
|
|
|
13
13
|
// src/git.ts
|
|
14
14
|
import { execSync } from "child_process";
|
|
15
|
+
import { join, relative } from "path";
|
|
15
16
|
function tryExec(cmd, cwd) {
|
|
16
17
|
try {
|
|
17
18
|
return execSync(cmd, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
@@ -55,11 +56,36 @@ function getRepoStatus(repoPath) {
|
|
|
55
56
|
}
|
|
56
57
|
return status;
|
|
57
58
|
}
|
|
59
|
+
function isCommitReachableFromUpstream(repoPath, commit) {
|
|
60
|
+
if (!/^[0-9a-f]{4,40}$/i.test(commit)) return false;
|
|
61
|
+
const result = tryExec(`git merge-base --is-ancestor ${commit} @{u}`, repoPath);
|
|
62
|
+
return result !== null;
|
|
63
|
+
}
|
|
64
|
+
function findCommitByEventId(repoPath, storageFile, eventId) {
|
|
65
|
+
if (!/^[A-Za-z0-9_-]{4,64}$/.test(eventId)) return null;
|
|
66
|
+
const relPath = relative(repoPath, storageFile).replace(/\\/g, "/");
|
|
67
|
+
const result = tryExec(`git log -S "${eventId}" --format=%H -- "${relPath}"`, repoPath);
|
|
68
|
+
if (!result) return null;
|
|
69
|
+
const hash = result.split("\n")[0].trim();
|
|
70
|
+
return hash || null;
|
|
71
|
+
}
|
|
72
|
+
function deriveEffectiveStage(task, repoPath) {
|
|
73
|
+
if (task.stage === "committed") {
|
|
74
|
+
if (task.committedEventId) {
|
|
75
|
+
const storageFile = join(repoPath, ".tasks.jsonl");
|
|
76
|
+
const commit = findCommitByEventId(repoPath, storageFile, task.committedEventId);
|
|
77
|
+
if (commit && isCommitReachableFromUpstream(repoPath, commit)) {
|
|
78
|
+
return "pushed";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return task.stage;
|
|
83
|
+
}
|
|
58
84
|
|
|
59
85
|
// src/storage.ts
|
|
60
86
|
function getStoragePath() {
|
|
61
87
|
const root = getRepoRoot();
|
|
62
|
-
return
|
|
88
|
+
return join2(root, ".tasks.jsonl");
|
|
63
89
|
}
|
|
64
90
|
function ensureStorageDir(storagePath) {
|
|
65
91
|
const dir = dirname(storagePath);
|
|
@@ -74,7 +100,15 @@ function readTasks(storagePath) {
|
|
|
74
100
|
}
|
|
75
101
|
const content = readFileSync(storagePath, "utf-8").trim();
|
|
76
102
|
if (!content) return [];
|
|
77
|
-
return content.split("\n").filter((line) => line.trim()).map((line) =>
|
|
103
|
+
return content.split("\n").filter((line) => line.trim()).map((line) => {
|
|
104
|
+
try {
|
|
105
|
+
return JSON.parse(line);
|
|
106
|
+
} catch {
|
|
107
|
+
process.stderr.write(`[task-tracker] Warning: skipping malformed line in tasks file
|
|
108
|
+
`);
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}).filter((task) => task !== null);
|
|
78
112
|
}
|
|
79
113
|
function writeTasks(storagePath, tasks) {
|
|
80
114
|
ensureStorageDir(storagePath);
|
|
@@ -90,19 +124,7 @@ function addTaskToStorage(storagePath, task) {
|
|
|
90
124
|
import { nanoid } from "nanoid";
|
|
91
125
|
|
|
92
126
|
// src/types.ts
|
|
93
|
-
var STAGES = [
|
|
94
|
-
"pending",
|
|
95
|
-
"in-progress",
|
|
96
|
-
"implemented",
|
|
97
|
-
"verified",
|
|
98
|
-
"committed",
|
|
99
|
-
"pushed",
|
|
100
|
-
"pr-created",
|
|
101
|
-
"merged",
|
|
102
|
-
"released",
|
|
103
|
-
"published",
|
|
104
|
-
"done"
|
|
105
|
-
];
|
|
127
|
+
var STAGES = ["pending", "in-progress", "committed", "released", "done"];
|
|
106
128
|
var DONE_STAGES = ["done"];
|
|
107
129
|
|
|
108
130
|
// src/tasks.ts
|
|
@@ -135,6 +157,9 @@ function updateTask(storagePath, id, updates) {
|
|
|
135
157
|
const task = tasks[idx];
|
|
136
158
|
if (updates.stage !== void 0) task.stage = updates.stage;
|
|
137
159
|
if (updates.description !== void 0) task.description = updates.description;
|
|
160
|
+
if (updates.stage === "committed" && !task.committedEventId) {
|
|
161
|
+
task.committedEventId = nanoid(16);
|
|
162
|
+
}
|
|
138
163
|
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
139
164
|
writeTasks(storagePath, tasks);
|
|
140
165
|
return task;
|
|
@@ -176,32 +201,28 @@ import chalk from "chalk";
|
|
|
176
201
|
var STAGE_COLORS = {
|
|
177
202
|
pending: (s) => chalk.gray(s),
|
|
178
203
|
"in-progress": (s) => chalk.blue(s),
|
|
179
|
-
implemented: (s) => chalk.cyan(s),
|
|
180
|
-
verified: (s) => chalk.yellow(s),
|
|
181
204
|
committed: (s) => chalk.magenta(s),
|
|
182
205
|
pushed: (s) => chalk.green(s),
|
|
183
|
-
"pr-created": (s) => chalk.greenBright(s),
|
|
184
|
-
merged: (s) => chalk.green(s),
|
|
185
206
|
released: (s) => chalk.blueBright(s),
|
|
186
|
-
published: (s) => chalk.greenBright(s),
|
|
187
207
|
done: (s) => chalk.dim(s)
|
|
188
208
|
};
|
|
189
209
|
function stageColor(stage) {
|
|
190
210
|
const fn = STAGE_COLORS[stage] ?? ((s) => chalk.white(s));
|
|
191
211
|
return fn(stage);
|
|
192
212
|
}
|
|
193
|
-
function formatTask(task) {
|
|
194
|
-
const
|
|
213
|
+
function formatTask(task, effectiveStage) {
|
|
214
|
+
const displayStage = effectiveStage ?? task.stage;
|
|
215
|
+
const stage = stageColor(displayStage);
|
|
195
216
|
const id = chalk.bold(task.id);
|
|
196
217
|
return `${id} ${stage} ${task.description}`;
|
|
197
218
|
}
|
|
198
|
-
function formatTaskTable(tasks) {
|
|
219
|
+
function formatTaskTable(tasks, getEffectiveStage) {
|
|
199
220
|
if (tasks.length === 0) {
|
|
200
221
|
return chalk.dim("No tasks found.");
|
|
201
222
|
}
|
|
202
|
-
return tasks.map(formatTask).join("\n");
|
|
223
|
+
return tasks.map((t) => formatTask(t, getEffectiveStage?.(t))).join("\n");
|
|
203
224
|
}
|
|
204
|
-
function formatCheckReport(activeTasks, repoStatus) {
|
|
225
|
+
function formatCheckReport(activeTasks, repoStatus, getEffectiveStage) {
|
|
205
226
|
const lines = [];
|
|
206
227
|
lines.push(chalk.bold("=== Task Tracker Check ==="));
|
|
207
228
|
lines.push("");
|
|
@@ -210,7 +231,7 @@ function formatCheckReport(activeTasks, repoStatus) {
|
|
|
210
231
|
lines.push(chalk.dim(" No active tasks."));
|
|
211
232
|
} else {
|
|
212
233
|
for (const t of activeTasks) {
|
|
213
|
-
lines.push(" " + formatTask(t));
|
|
234
|
+
lines.push(" " + formatTask(t, getEffectiveStage?.(t)));
|
|
214
235
|
}
|
|
215
236
|
}
|
|
216
237
|
lines.push("");
|
|
@@ -242,25 +263,25 @@ function formatCheckReport(activeTasks, repoStatus) {
|
|
|
242
263
|
// src/gui.ts
|
|
243
264
|
import { createServer } from "http";
|
|
244
265
|
import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
|
|
245
|
-
import { join as
|
|
266
|
+
import { join as join4, dirname as dirname2, resolve } from "path";
|
|
246
267
|
import { fileURLToPath } from "url";
|
|
247
268
|
import { exec } from "child_process";
|
|
248
269
|
|
|
249
270
|
// src/scanner.ts
|
|
250
271
|
import { existsSync as existsSync2, readdirSync, statSync } from "fs";
|
|
251
|
-
import { join as
|
|
272
|
+
import { join as join3, basename } from "path";
|
|
252
273
|
function scanTaskFiles(dir) {
|
|
253
|
-
const rootFile =
|
|
274
|
+
const rootFile = join3(dir, ".tasks.jsonl");
|
|
254
275
|
const root = existsSync2(rootFile) ? { path: rootFile, dir, name: basename(dir) } : null;
|
|
255
276
|
const repos = [];
|
|
256
277
|
try {
|
|
257
278
|
const entries = readdirSync(dir);
|
|
258
279
|
for (const entry of entries) {
|
|
259
280
|
if (entry.startsWith(".")) continue;
|
|
260
|
-
const subDir =
|
|
281
|
+
const subDir = join3(dir, entry);
|
|
261
282
|
try {
|
|
262
283
|
if (statSync(subDir).isDirectory()) {
|
|
263
|
-
const taskFile =
|
|
284
|
+
const taskFile = join3(subDir, ".tasks.jsonl");
|
|
264
285
|
if (existsSync2(taskFile)) {
|
|
265
286
|
repos.push({ path: taskFile, dir: subDir, name: entry });
|
|
266
287
|
}
|
|
@@ -278,8 +299,8 @@ var __filename = fileURLToPath(import.meta.url);
|
|
|
278
299
|
var __dirname = dirname2(__filename);
|
|
279
300
|
function getHtmlPath() {
|
|
280
301
|
const candidates = [
|
|
281
|
-
|
|
282
|
-
|
|
302
|
+
join4(__dirname, "public", "index.html"),
|
|
303
|
+
join4(__dirname, "..", "public", "index.html")
|
|
283
304
|
];
|
|
284
305
|
for (const p of candidates) {
|
|
285
306
|
if (existsSync3(p)) return p;
|
|
@@ -387,7 +408,7 @@ function startGui(dir, port = 3333) {
|
|
|
387
408
|
if (pathname === "/api/tasks/purge" && method === "POST") {
|
|
388
409
|
const body = await parseBody(req);
|
|
389
410
|
const targetDir = typeof body.dir === "string" ? body.dir : resolvedDir;
|
|
390
|
-
const taskFile =
|
|
411
|
+
const taskFile = join4(targetDir, ".tasks.jsonl");
|
|
391
412
|
const result = purgeTasks(taskFile);
|
|
392
413
|
sendJson(res, { count: result.count, ids: result.purged.map((t) => t.id) });
|
|
393
414
|
return;
|
|
@@ -399,7 +420,7 @@ function startGui(dir, port = 3333) {
|
|
|
399
420
|
return;
|
|
400
421
|
}
|
|
401
422
|
const targetDir = typeof body.dir === "string" ? body.dir : resolvedDir;
|
|
402
|
-
const taskFile =
|
|
423
|
+
const taskFile = join4(targetDir, ".tasks.jsonl");
|
|
403
424
|
const stage = typeof body.stage === "string" && STAGES.includes(body.stage) ? body.stage : void 0;
|
|
404
425
|
const task = createTask(taskFile, body.description, { stage });
|
|
405
426
|
sendJson(res, task, 201);
|
|
@@ -411,7 +432,7 @@ function startGui(dir, port = 3333) {
|
|
|
411
432
|
if (method === "PUT") {
|
|
412
433
|
const body = await parseBody(req);
|
|
413
434
|
const targetDir = typeof body.dir === "string" ? body.dir : resolvedDir;
|
|
414
|
-
const taskFile =
|
|
435
|
+
const taskFile = join4(targetDir, ".tasks.jsonl");
|
|
415
436
|
const updates = {};
|
|
416
437
|
if (typeof body.stage === "string" && STAGES.includes(body.stage)) {
|
|
417
438
|
updates.stage = body.stage;
|
|
@@ -429,7 +450,7 @@ function startGui(dir, port = 3333) {
|
|
|
429
450
|
}
|
|
430
451
|
if (method === "DELETE") {
|
|
431
452
|
const queryDir = url.searchParams.get("dir") ?? resolvedDir;
|
|
432
|
-
const taskFile =
|
|
453
|
+
const taskFile = join4(queryDir, ".tasks.jsonl");
|
|
433
454
|
const removed = removeTask(taskFile, id);
|
|
434
455
|
if (!removed) {
|
|
435
456
|
sendError(res, "Task not found", 404);
|
|
@@ -465,13 +486,20 @@ var __filename2 = fileURLToPath2(import.meta.url);
|
|
|
465
486
|
var __dirname2 = dirname3(__filename2);
|
|
466
487
|
var version = "0.0.0";
|
|
467
488
|
try {
|
|
468
|
-
const pkg = JSON.parse(readFileSync3(
|
|
489
|
+
const pkg = JSON.parse(readFileSync3(join5(__dirname2, "..", "package.json"), "utf-8"));
|
|
469
490
|
version = pkg.version;
|
|
470
491
|
} catch {
|
|
471
492
|
}
|
|
472
493
|
function isValidStage(s) {
|
|
473
494
|
return STAGES.includes(s);
|
|
474
495
|
}
|
|
496
|
+
function isDerivedStage(s) {
|
|
497
|
+
return s === "pushed";
|
|
498
|
+
}
|
|
499
|
+
function derivedStageError(stage) {
|
|
500
|
+
return `\`${stage}\` is a derived stage and cannot be set manually.
|
|
501
|
+
Set stage to \`committed\` instead; \`${stage}\` is derived automatically when the commit containing \`committedEventId\` is reachable from the upstream branch.`;
|
|
502
|
+
}
|
|
475
503
|
function getStorage() {
|
|
476
504
|
try {
|
|
477
505
|
return getStoragePath();
|
|
@@ -482,7 +510,15 @@ function getStorage() {
|
|
|
482
510
|
}
|
|
483
511
|
var program = new Command();
|
|
484
512
|
program.name("task-tracker").description("Persistent task lifecycle tracker for AI agent sessions").version(version, "-V, --version");
|
|
485
|
-
program.command("add <description>").description("Add a new task").option(
|
|
513
|
+
program.command("add <description>").description("Add a new task").option(
|
|
514
|
+
"--stage <stage>",
|
|
515
|
+
`Initial stage (valid: ${STAGES.join(", ")}; \`pushed\` is derived, not settable)`,
|
|
516
|
+
"pending"
|
|
517
|
+
).option("--json", "Output created task as JSON").action((description, opts) => {
|
|
518
|
+
if (isDerivedStage(opts.stage)) {
|
|
519
|
+
console.error(derivedStageError(opts.stage));
|
|
520
|
+
process2.exit(1);
|
|
521
|
+
}
|
|
486
522
|
if (!isValidStage(opts.stage)) {
|
|
487
523
|
console.error(`Invalid stage: ${opts.stage}
|
|
488
524
|
Valid stages: ${STAGES.join(", ")}`);
|
|
@@ -495,30 +531,63 @@ Valid stages: ${STAGES.join(", ")}`);
|
|
|
495
531
|
console.log("Created: " + formatTask(task));
|
|
496
532
|
}
|
|
497
533
|
});
|
|
498
|
-
program.command("list").description("List tasks").option("--all", "Include completed/done tasks").option("--stage <stage>",
|
|
499
|
-
const
|
|
500
|
-
|
|
534
|
+
program.command("list").description("List tasks").option("--all", "Include completed/done tasks").option("--stage <stage>", `Filter by stage; use \`pushed\` to filter by derived effective stage`).option("--json", "JSON output").action((opts) => {
|
|
535
|
+
const storage = getStorage();
|
|
536
|
+
const repoRoot = dirname3(storage);
|
|
537
|
+
if (opts.stage === "pushed") {
|
|
538
|
+
const committed = listTasks(storage, { all: opts.all, stage: "committed" });
|
|
539
|
+
const tasks2 = committed.filter((t) => deriveEffectiveStage(t, repoRoot) === "pushed");
|
|
540
|
+
if (opts.json) {
|
|
541
|
+
console.log(
|
|
542
|
+
JSON.stringify(
|
|
543
|
+
tasks2.map((t) => ({ ...t, effectiveStage: "pushed" })),
|
|
544
|
+
null,
|
|
545
|
+
2
|
|
546
|
+
)
|
|
547
|
+
);
|
|
548
|
+
} else {
|
|
549
|
+
console.log(formatTaskTable(tasks2, () => "pushed"));
|
|
550
|
+
}
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (opts.stage && !isValidStage(opts.stage)) {
|
|
501
554
|
console.error(`Invalid stage: ${opts.stage}
|
|
502
555
|
Valid stages: ${STAGES.join(", ")}`);
|
|
503
556
|
process2.exit(1);
|
|
504
557
|
}
|
|
505
|
-
const
|
|
558
|
+
const stage = opts.stage ? opts.stage : void 0;
|
|
559
|
+
const tasks = listTasks(storage, { all: opts.all, stage });
|
|
560
|
+
const getEffective = (t) => deriveEffectiveStage(t, repoRoot);
|
|
506
561
|
if (opts.json) {
|
|
507
|
-
console.log(
|
|
562
|
+
console.log(
|
|
563
|
+
JSON.stringify(
|
|
564
|
+
tasks.map((t) => ({ ...t, effectiveStage: getEffective(t) })),
|
|
565
|
+
null,
|
|
566
|
+
2
|
|
567
|
+
)
|
|
568
|
+
);
|
|
508
569
|
} else {
|
|
509
|
-
console.log(formatTaskTable(tasks));
|
|
570
|
+
console.log(formatTaskTable(tasks, getEffective));
|
|
510
571
|
}
|
|
511
572
|
});
|
|
512
|
-
program.command("update <id>").description("Update a task").option(
|
|
573
|
+
program.command("update <id>").description("Update a task").option(
|
|
574
|
+
"--stage <stage>",
|
|
575
|
+
`Set lifecycle stage (valid: ${STAGES.join(", ")}; \`pushed\` is derived, not settable)`
|
|
576
|
+
).option("--description <text>", "Update description").option("--json", "JSON output").action((id, opts) => {
|
|
577
|
+
if (opts.stage && isDerivedStage(opts.stage)) {
|
|
578
|
+
console.error(derivedStageError(opts.stage));
|
|
579
|
+
process2.exit(1);
|
|
580
|
+
}
|
|
513
581
|
if (opts.stage && !isValidStage(opts.stage)) {
|
|
514
582
|
console.error(`Invalid stage: ${opts.stage}
|
|
515
583
|
Valid stages: ${STAGES.join(", ")}`);
|
|
516
584
|
process2.exit(1);
|
|
517
585
|
}
|
|
586
|
+
const storage = getStorage();
|
|
518
587
|
const updates = {};
|
|
519
588
|
if (opts.stage) updates.stage = opts.stage;
|
|
520
589
|
if (opts.description) updates.description = opts.description;
|
|
521
|
-
const task = updateTask(
|
|
590
|
+
const task = updateTask(storage, id, updates);
|
|
522
591
|
if (!task) {
|
|
523
592
|
console.error(`Task not found: ${id}`);
|
|
524
593
|
process2.exit(1);
|
|
@@ -555,10 +624,20 @@ program.command("check").description("Show active tasks and git status for this
|
|
|
555
624
|
const repoRoot = dirname3(storage);
|
|
556
625
|
const activeTasks = listTasks(storage, { all: false });
|
|
557
626
|
const repoStatus = getRepoStatus(repoRoot);
|
|
627
|
+
const getEffective = (t) => deriveEffectiveStage(t, repoRoot);
|
|
558
628
|
if (opts.json) {
|
|
559
|
-
console.log(
|
|
629
|
+
console.log(
|
|
630
|
+
JSON.stringify(
|
|
631
|
+
{
|
|
632
|
+
activeTasks: activeTasks.map((t) => ({ ...t, effectiveStage: getEffective(t) })),
|
|
633
|
+
repoStatus
|
|
634
|
+
},
|
|
635
|
+
null,
|
|
636
|
+
2
|
|
637
|
+
)
|
|
638
|
+
);
|
|
560
639
|
} else {
|
|
561
|
-
console.log(formatCheckReport(activeTasks, repoStatus));
|
|
640
|
+
console.log(formatCheckReport(activeTasks, repoStatus, getEffective));
|
|
562
641
|
}
|
|
563
642
|
});
|
|
564
643
|
program.command("purge").description("Remove all done tasks from storage").option("--dry-run", "Show what would be removed without removing").option("--keep <n>", "Keep N most recent done tasks, purge the rest").option("--json", "JSON output").action((opts) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metyatech/task-tracker",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Persistent task lifecycle tracker for AI agent sessions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
|
-
"url": "https://github.com/metyatech/task-tracker.git"
|
|
20
|
+
"url": "git+https://github.com/metyatech/task-tracker.git"
|
|
21
21
|
},
|
|
22
22
|
"bugs": {
|
|
23
23
|
"url": "https://github.com/metyatech/task-tracker/issues"
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"vitest": "^4.0.0"
|
|
54
54
|
},
|
|
55
55
|
"overrides": {
|
|
56
|
-
"minimatch": "
|
|
56
|
+
"minimatch": "10.2.4"
|
|
57
57
|
},
|
|
58
58
|
"lint-staged": {
|
|
59
59
|
"src/**/*.ts": [
|