@tashks/core 0.1.5 → 0.3.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.
@@ -1 +1 @@
1
- {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/repository.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/repository.ts"],"names":[],"mappings":"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"}
@@ -6,8 +6,8 @@ import * as Effect from "effect/Effect";
6
6
  import * as Layer from "effect/Layer";
7
7
  import * as Schema from "effect/Schema";
8
8
  import YAML from "yaml";
9
- import { Task as TaskSchema, TaskCreateInput as TaskCreateInputSchema, TaskPatch as TaskPatchSchema, WorkLogCreateInput as WorkLogCreateInputSchema, WorkLogEntry as WorkLogEntrySchema, WorkLogPatch as WorkLogPatchSchema, } from "./schema.js";
10
- import { byUpdatedDescThenTitle, isDueBefore, isStalerThan, isUnblocked, } from "./query.js";
9
+ import { Task as TaskSchema, TaskCreateInput as TaskCreateInputSchema, TaskPatch as TaskPatchSchema, WorkLogCreateInput as WorkLogCreateInputSchema, WorkLogEntry as WorkLogEntrySchema, WorkLogPatch as WorkLogPatchSchema, Project as ProjectSchema, ProjectCreateInput as ProjectCreateInputSchema, ProjectPatch as ProjectPatchSchema, } from "./schema.js";
10
+ import { byUpdatedDescThenTitle, isDueBefore, isStalerThan, isUnblocked, listContexts as listContextsFromTasks, } from "./query.js";
11
11
  import { generateTaskId } from "./id.js";
12
12
  import { runCreateHooks, runModifyHooks, runNonMutatingHooks, } from "./hooks.js";
13
13
  import { buildCompletionRecurrenceTask, buildNextClockRecurrenceTask, isClockRecurrenceDue, } from "./recurrence.js";
@@ -22,11 +22,17 @@ const decodeWorkLogCreateInput = Schema.decodeUnknownSync(WorkLogCreateInputSche
22
22
  const decodeWorkLogEntry = Schema.decodeUnknownSync(WorkLogEntrySchema);
23
23
  const decodeWorkLogEntryEither = Schema.decodeUnknownEither(WorkLogEntrySchema);
24
24
  const decodeWorkLogPatch = Schema.decodeUnknownSync(WorkLogPatchSchema);
25
+ const decodeProject = Schema.decodeUnknownSync(ProjectSchema);
26
+ const decodeProjectEither = Schema.decodeUnknownEither(ProjectSchema);
27
+ const decodeProjectCreateInput = Schema.decodeUnknownSync(ProjectCreateInputSchema);
28
+ const decodeProjectPatch = Schema.decodeUnknownSync(ProjectPatchSchema);
25
29
  const toErrorMessage = (error) => error instanceof Error ? error.message : String(error);
26
30
  const taskFilePath = (dataDir, id) => join(dataDir, "tasks", `${id}.yaml`);
27
31
  const legacyTaskFilePath = (dataDir, id) => join(dataDir, "tasks", `${id}.yml`);
28
32
  const workLogFilePath = (dataDir, id) => join(dataDir, "work-log", `${id}.yaml`);
29
33
  const legacyWorkLogFilePath = (dataDir, id) => join(dataDir, "work-log", `${id}.yml`);
34
+ const projectFilePath = (dataDir, id) => join(dataDir, "projects", `${id}.yaml`);
35
+ const legacyProjectFilePath = (dataDir, id) => join(dataDir, "projects", `${id}.yml`);
30
36
  const dailyHighlightFilePath = (dataDir) => join(dataDir, "daily-highlight.yaml");
31
37
  const ensureTasksDir = (dataDir) => Effect.tryPromise({
32
38
  try: () => mkdir(join(dataDir, "tasks"), { recursive: true }),
@@ -36,6 +42,10 @@ const ensureWorkLogDir = (dataDir) => Effect.tryPromise({
36
42
  try: () => mkdir(join(dataDir, "work-log"), { recursive: true }),
37
43
  catch: (error) => `TaskRepository failed to create work-log directory: ${toErrorMessage(error)}`,
38
44
  });
45
+ const ensureProjectsDir = (dataDir) => Effect.tryPromise({
46
+ try: () => mkdir(join(dataDir, "projects"), { recursive: true }),
47
+ catch: (error) => `TaskRepository failed to create projects directory: ${toErrorMessage(error)}`,
48
+ });
39
49
  const writeDailyHighlightToDisk = (dataDir, id) => Effect.tryPromise({
40
50
  try: async () => {
41
51
  await mkdir(dataDir, { recursive: true });
@@ -214,6 +224,75 @@ const generateNextClockRecurrence = (dataDir, existing, generatedAt) => Effect.g
214
224
  : null;
215
225
  return { nextTask: result.nextTask, replacedId };
216
226
  });
227
+ const readProjectByIdFromDisk = (dataDir, id) => Effect.tryPromise({
228
+ try: async () => {
229
+ const candidatePaths = [
230
+ projectFilePath(dataDir, id),
231
+ legacyProjectFilePath(dataDir, id),
232
+ ];
233
+ for (const path of candidatePaths) {
234
+ const source = await readFile(path, "utf8").catch((error) => {
235
+ if (error !== null &&
236
+ typeof error === "object" &&
237
+ "code" in error &&
238
+ error.code === "ENOENT") {
239
+ return null;
240
+ }
241
+ throw error;
242
+ });
243
+ if (source === null) {
244
+ continue;
245
+ }
246
+ const parsed = YAML.parse(source);
247
+ const project = parseProjectRecord(parsed);
248
+ if (project === null) {
249
+ throw new Error(`Invalid project record in ${path}`);
250
+ }
251
+ return { path, project };
252
+ }
253
+ throw new Error(`Project not found: ${id}`);
254
+ },
255
+ catch: (error) => `TaskRepository failed to read project ${id}: ${toErrorMessage(error)}`,
256
+ });
257
+ const readProjectsFromDisk = (dataDir) => Effect.tryPromise({
258
+ try: async () => {
259
+ const projectsDir = join(dataDir, "projects");
260
+ const entries = await readdir(projectsDir, { withFileTypes: true }).catch((error) => {
261
+ if (error !== null &&
262
+ typeof error === "object" &&
263
+ "code" in error &&
264
+ error.code === "ENOENT") {
265
+ return [];
266
+ }
267
+ throw error;
268
+ });
269
+ const projectFiles = entries
270
+ .filter((entry) => entry.isFile() &&
271
+ (entry.name.endsWith(".yaml") || entry.name.endsWith(".yml")))
272
+ .map((entry) => entry.name);
273
+ const projects = [];
274
+ for (const fileName of projectFiles) {
275
+ const filePath = join(projectsDir, fileName);
276
+ const source = await readFile(filePath, "utf8");
277
+ const parsed = YAML.parse(source);
278
+ const project = parseProjectRecord(parsed);
279
+ if (project === null) {
280
+ throw new Error(`Invalid project record in ${filePath}`);
281
+ }
282
+ projects.push(project);
283
+ }
284
+ return projects;
285
+ },
286
+ catch: (error) => `TaskRepository.listProjects failed to read project files: ${toErrorMessage(error)}`,
287
+ });
288
+ const writeProjectToDisk = (path, project) => Effect.tryPromise({
289
+ try: () => writeFile(path, YAML.stringify(project), "utf8"),
290
+ catch: (error) => `TaskRepository failed to write project ${project.id}: ${toErrorMessage(error)}`,
291
+ });
292
+ const deleteProjectFromDisk = (path, id) => Effect.tryPromise({
293
+ try: () => rm(path),
294
+ catch: (error) => `TaskRepository failed to delete project ${id}: ${toErrorMessage(error)}`,
295
+ });
217
296
  const byStartedAtDescThenId = (a, b) => {
218
297
  const byStartedAtDesc = b.started_at.localeCompare(a.started_at);
219
298
  if (byStartedAtDesc !== 0) {
@@ -223,6 +302,9 @@ const byStartedAtDescThenId = (a, b) => {
223
302
  };
224
303
  export const applyListTaskFilters = (tasks, filters = {}) => {
225
304
  const dueBeforePredicate = filters.due_before !== undefined ? isDueBefore(filters.due_before) : null;
305
+ const stalePredicate = filters.stale_days !== undefined
306
+ ? isStalerThan(filters.stale_days, todayIso())
307
+ : null;
226
308
  return tasks
227
309
  .filter((task) => {
228
310
  if (filters.status !== undefined && task.status !== filters.status) {
@@ -231,7 +313,7 @@ export const applyListTaskFilters = (tasks, filters = {}) => {
231
313
  if (filters.area !== undefined && task.area !== filters.area) {
232
314
  return false;
233
315
  }
234
- if (filters.project !== undefined && task.project !== filters.project) {
316
+ if (filters.project !== undefined && !task.projects.includes(filters.project)) {
235
317
  return false;
236
318
  }
237
319
  if (filters.tags !== undefined &&
@@ -254,10 +336,68 @@ export const applyListTaskFilters = (tasks, filters = {}) => {
254
336
  if (filters.unblocked_only === true && !isUnblocked(task, tasks)) {
255
337
  return false;
256
338
  }
339
+ if (filters.duration_min !== undefined &&
340
+ (task.estimated_minutes === null || task.estimated_minutes < filters.duration_min)) {
341
+ return false;
342
+ }
343
+ if (filters.duration_max !== undefined &&
344
+ (task.estimated_minutes === null || task.estimated_minutes > filters.duration_max)) {
345
+ return false;
346
+ }
347
+ if (filters.context !== undefined &&
348
+ task.context !== filters.context) {
349
+ return false;
350
+ }
351
+ if (filters.include_templates !== true &&
352
+ task.is_template === true) {
353
+ return false;
354
+ }
355
+ if (stalePredicate !== null && !stalePredicate(task)) {
356
+ return false;
357
+ }
358
+ if (filters.priority !== undefined &&
359
+ task.priority !== filters.priority) {
360
+ return false;
361
+ }
362
+ if (filters.type !== undefined &&
363
+ task.type !== filters.type) {
364
+ return false;
365
+ }
366
+ if (filters.assignee !== undefined &&
367
+ task.assignee !== filters.assignee) {
368
+ return false;
369
+ }
370
+ if (filters.unassigned === true &&
371
+ task.assignee !== null) {
372
+ return false;
373
+ }
374
+ if (filters.parent !== undefined &&
375
+ task.parent !== filters.parent) {
376
+ return false;
377
+ }
257
378
  return true;
258
379
  })
259
380
  .sort(byUpdatedDescThenTitle);
260
381
  };
382
+ export const applyListProjectFilters = (projects, filters = {}) => {
383
+ return projects
384
+ .filter((project) => {
385
+ if (filters.status !== undefined && project.status !== filters.status) {
386
+ return false;
387
+ }
388
+ if (filters.area !== undefined && project.area !== filters.area) {
389
+ return false;
390
+ }
391
+ return true;
392
+ })
393
+ .sort((a, b) => {
394
+ const byUpdatedDesc = b.updated.localeCompare(a.updated);
395
+ if (byUpdatedDesc !== 0) {
396
+ return byUpdatedDesc;
397
+ }
398
+ return a.title.localeCompare(b.title);
399
+ });
400
+ };
261
401
  export class TaskRepository extends Context.Tag("TaskRepository")() {
262
402
  }
263
403
  const defaultDataDir = () => {
@@ -266,6 +406,57 @@ const defaultDataDir = () => {
266
406
  ? `${home}/.local/share/tashks`
267
407
  : ".local/share/tashks";
268
408
  };
409
+ export const buildInstanceFromTemplate = (template, overrides) => {
410
+ const now = new Date().toISOString().slice(0, 10);
411
+ return decodeTask({
412
+ id: generateTaskId(overrides?.title ?? template.title),
413
+ title: overrides?.title ?? template.title,
414
+ description: template.description,
415
+ status: overrides?.status ?? "backlog",
416
+ area: template.area,
417
+ projects: overrides?.projects
418
+ ? [...overrides.projects]
419
+ : [...template.projects],
420
+ tags: [...template.tags],
421
+ created: now,
422
+ updated: now,
423
+ urgency: template.urgency,
424
+ energy: template.energy,
425
+ due: overrides?.due ?? null,
426
+ context: template.context,
427
+ subtasks: template.subtasks.map((s) => ({ text: s.text, done: false })),
428
+ blocked_by: [],
429
+ estimated_minutes: template.estimated_minutes,
430
+ actual_minutes: null,
431
+ completed_at: null,
432
+ last_surfaced: null,
433
+ defer_until: overrides?.defer_until ?? null,
434
+ nudge_count: 0,
435
+ recurrence: null,
436
+ recurrence_trigger: "clock",
437
+ recurrence_strategy: "replace",
438
+ recurrence_last_generated: null,
439
+ related: [...template.related],
440
+ is_template: false,
441
+ from_template: template.id,
442
+ priority: template.priority,
443
+ type: template.type,
444
+ assignee: null,
445
+ parent: null,
446
+ close_reason: null,
447
+ comments: [],
448
+ });
449
+ };
450
+ const validateNoTemplateRefs = (relatedIds, allTasks) => {
451
+ const templateIds = allTasks
452
+ .filter((t) => t.is_template)
453
+ .map((t) => t.id);
454
+ const badRefs = relatedIds.filter((id) => templateIds.includes(id));
455
+ if (badRefs.length > 0) {
456
+ return Effect.fail(`Cannot reference template(s) in related: ${badRefs.join(", ")}`);
457
+ }
458
+ return Effect.void;
459
+ };
269
460
  const makeTaskRepositoryLive = (options = {}) => {
270
461
  const dataDir = options.dataDir ?? defaultDataDir();
271
462
  const hookRuntimeOptions = {
@@ -279,6 +470,10 @@ const makeTaskRepositoryLive = (options = {}) => {
279
470
  createTask: (input) => Effect.gen(function* () {
280
471
  yield* ensureTasksDir(dataDir);
281
472
  const created = createTaskFromInput(input);
473
+ if (created.related.length > 0) {
474
+ const allTasks = yield* readTasksFromDisk(dataDir);
475
+ yield* validateNoTemplateRefs(created.related, allTasks);
476
+ }
282
477
  const taskFromHooks = yield* runCreateHooks(created, hookRuntimeOptions);
283
478
  yield* writeTaskToDisk(taskFilePath(dataDir, taskFromHooks.id), taskFromHooks);
284
479
  return taskFromHooks;
@@ -286,6 +481,11 @@ const makeTaskRepositoryLive = (options = {}) => {
286
481
  updateTask: (id, patch) => Effect.gen(function* () {
287
482
  const existing = yield* readTaskByIdFromDisk(dataDir, id);
288
483
  const updated = applyTaskPatch(existing.task, patch);
484
+ if (patch.related !== undefined &&
485
+ updated.related.length > 0) {
486
+ const allTasks = yield* readTasksFromDisk(dataDir);
487
+ yield* validateNoTemplateRefs(updated.related, allTasks);
488
+ }
289
489
  const taskFromHooks = yield* runModifyHooks(existing.task, updated, hookRuntimeOptions);
290
490
  yield* writeTaskToDisk(existing.path, taskFromHooks);
291
491
  return taskFromHooks;
@@ -319,7 +519,8 @@ const makeTaskRepositoryLive = (options = {}) => {
319
519
  const recurringTasks = tasks.filter((task) => task.recurrence !== null &&
320
520
  task.recurrence_trigger === "clock" &&
321
521
  task.status !== "done" &&
322
- task.status !== "dropped");
522
+ task.status !== "dropped" &&
523
+ !task.is_template);
323
524
  const created = [];
324
525
  const replaced = [];
325
526
  for (const task of recurringTasks) {
@@ -347,6 +548,31 @@ const makeTaskRepositoryLive = (options = {}) => {
347
548
  yield* writeDailyHighlightToDisk(dataDir, id);
348
549
  return existing.task;
349
550
  }),
551
+ getDailyHighlight: () => Effect.gen(function* () {
552
+ const source = yield* Effect.tryPromise({
553
+ try: () => readFile(dailyHighlightFilePath(dataDir), "utf8").catch((error) => {
554
+ if (error !== null &&
555
+ typeof error === "object" &&
556
+ "code" in error &&
557
+ error.code === "ENOENT") {
558
+ return null;
559
+ }
560
+ throw error;
561
+ }),
562
+ catch: (error) => `TaskRepository failed to read daily highlight: ${toErrorMessage(error)}`,
563
+ });
564
+ if (source === null || source.trim().length === 0) {
565
+ return null;
566
+ }
567
+ const parsed = YAML.parse(source);
568
+ if (parsed === null ||
569
+ typeof parsed !== "object" ||
570
+ typeof parsed.id !== "string") {
571
+ return null;
572
+ }
573
+ const result = yield* Effect.catchAll(Effect.map(readTaskByIdFromDisk(dataDir, parsed.id), (r) => r.task), () => Effect.succeed(null));
574
+ return result;
575
+ }),
350
576
  listStale: (days) => Effect.map(readTasksFromDisk(dataDir), (tasks) => {
351
577
  const stalePredicate = isStalerThan(days, todayIso());
352
578
  return tasks
@@ -392,12 +618,91 @@ const makeTaskRepositoryLive = (options = {}) => {
392
618
  yield* writeWorkLogEntryToDisk(workLogFilePath(dataDir, entry.id), entry);
393
619
  return entry;
394
620
  }),
621
+ listProjects: (filters) => Effect.map(readProjectsFromDisk(dataDir), (projects) => applyListProjectFilters(projects, filters)),
622
+ getProject: (id) => Effect.map(readProjectByIdFromDisk(dataDir, id), (result) => result.project),
623
+ createProject: (input) => Effect.gen(function* () {
624
+ yield* ensureProjectsDir(dataDir);
625
+ const created = createProjectFromInput(input);
626
+ yield* writeProjectToDisk(projectFilePath(dataDir, created.id), created);
627
+ return created;
628
+ }),
629
+ updateProject: (id, patch) => Effect.gen(function* () {
630
+ const existing = yield* readProjectByIdFromDisk(dataDir, id);
631
+ const updated = applyProjectPatch(existing.project, patch);
632
+ yield* writeProjectToDisk(existing.path, updated);
633
+ return updated;
634
+ }),
635
+ deleteProject: (id) => Effect.gen(function* () {
636
+ const existing = yield* readProjectByIdFromDisk(dataDir, id);
637
+ yield* deleteProjectFromDisk(existing.path, id);
638
+ return { deleted: true };
639
+ }),
640
+ importProject: (project) => Effect.gen(function* () {
641
+ yield* ensureProjectsDir(dataDir);
642
+ yield* writeProjectToDisk(projectFilePath(dataDir, project.id), project);
643
+ return project;
644
+ }),
645
+ listContexts: () => Effect.map(readTasksFromDisk(dataDir), (tasks) => listContextsFromTasks(tasks)),
646
+ getRelated: (id) => Effect.gen(function* () {
647
+ const existing = yield* readTaskByIdFromDisk(dataDir, id);
648
+ const allTasks = yield* readTasksFromDisk(dataDir);
649
+ const targetRelated = new Set(existing.task.related);
650
+ return allTasks.filter((t) => t.id !== id &&
651
+ (targetRelated.has(t.id) || t.related.includes(id)));
652
+ }),
653
+ instantiateTemplate: (templateId, overrides) => Effect.gen(function* () {
654
+ const existing = yield* readTaskByIdFromDisk(dataDir, templateId);
655
+ const template = existing.task;
656
+ if (!template.is_template) {
657
+ return yield* Effect.fail(`Task ${templateId} is not a template`);
658
+ }
659
+ const instance = buildInstanceFromTemplate(template, overrides);
660
+ const taskFromHooks = yield* runCreateHooks(instance, hookRuntimeOptions);
661
+ yield* ensureTasksDir(dataDir);
662
+ yield* writeTaskToDisk(taskFilePath(dataDir, taskFromHooks.id), taskFromHooks);
663
+ return taskFromHooks;
664
+ }),
395
665
  };
396
666
  };
397
667
  export const TaskRepositoryLive = (options = {}) => Layer.succeed(TaskRepository, makeTaskRepositoryLive(options));
398
668
  export const todayIso = () => new Date().toISOString().slice(0, 10);
669
+ const migrateTaskRecord = (record) => {
670
+ if (record === null || typeof record !== "object") {
671
+ return record;
672
+ }
673
+ let rec = record;
674
+ if ("project" in rec && !("projects" in rec)) {
675
+ const { project, ...rest } = rec;
676
+ rec = {
677
+ ...rest,
678
+ projects: typeof project === "string" ? [project] : [],
679
+ };
680
+ }
681
+ if (!("related" in rec))
682
+ rec.related = [];
683
+ if (!("is_template" in rec))
684
+ rec.is_template = false;
685
+ if (!("from_template" in rec))
686
+ rec.from_template = null;
687
+ if (!("priority" in rec))
688
+ rec.priority = null;
689
+ if (!("type" in rec))
690
+ rec.type = "task";
691
+ if (!("assignee" in rec))
692
+ rec.assignee = null;
693
+ if (!("parent" in rec))
694
+ rec.parent = null;
695
+ if (!("close_reason" in rec))
696
+ rec.close_reason = null;
697
+ if (!("description" in rec))
698
+ rec.description = "";
699
+ if (!("comments" in rec))
700
+ rec.comments = [];
701
+ return rec;
702
+ };
399
703
  export const parseTaskRecord = (record) => {
400
- const result = decodeTaskEither(record);
704
+ const migrated = migrateTaskRecord(record);
705
+ const result = decodeTaskEither(migrated);
401
706
  return Either.isRight(result) ? result.right : null;
402
707
  };
403
708
  export const parseWorkLogRecord = (record) => {
@@ -414,9 +719,10 @@ export const createTaskFromInput = (input) => {
414
719
  export const applyTaskPatch = (task, patch) => {
415
720
  const normalizedTask = decodeTask(task);
416
721
  const normalizedPatch = decodeTaskPatch(patch);
722
+ const { from_template: _stripped, ...safePatch } = normalizedPatch;
417
723
  return decodeTask({
418
724
  ...normalizedTask,
419
- ...normalizedPatch,
725
+ ...safePatch,
420
726
  updated: todayIso(),
421
727
  });
422
728
  };
@@ -428,3 +734,44 @@ export const applyWorkLogPatch = (entry, patch) => {
428
734
  ...normalizedPatch,
429
735
  });
430
736
  };
737
+ export const parseProjectRecord = (record) => {
738
+ const result = decodeProjectEither(record);
739
+ return Either.isRight(result) ? result.right : null;
740
+ };
741
+ export const createProjectFromInput = (input) => {
742
+ const normalizedInput = decodeProjectCreateInput(input);
743
+ return decodeProject({
744
+ ...normalizedInput,
745
+ id: generateTaskId(normalizedInput.title),
746
+ });
747
+ };
748
+ export const promoteSubtask = (repository, taskId, subtaskIndex) => Effect.gen(function* () {
749
+ const parent = yield* repository.getTask(taskId);
750
+ if (parent.is_template) {
751
+ return yield* Effect.fail("Cannot promote subtasks on a template. Instantiate the template first.");
752
+ }
753
+ if (subtaskIndex < 0 || subtaskIndex >= parent.subtasks.length) {
754
+ return yield* Effect.fail(`Subtask index ${subtaskIndex} is out of range (0..${parent.subtasks.length - 1})`);
755
+ }
756
+ const subtask = parent.subtasks[subtaskIndex];
757
+ const newTask = yield* repository.createTask({
758
+ title: subtask.text,
759
+ projects: [...parent.projects],
760
+ area: parent.area,
761
+ tags: [...parent.tags],
762
+ status: subtask.done ? "done" : "backlog",
763
+ blocked_by: [parent.id],
764
+ });
765
+ const updatedSubtasks = parent.subtasks.filter((_, i) => i !== subtaskIndex);
766
+ yield* repository.updateTask(taskId, { subtasks: updatedSubtasks });
767
+ return newTask;
768
+ });
769
+ export const applyProjectPatch = (project, patch) => {
770
+ const normalizedProject = decodeProject(project);
771
+ const normalizedPatch = decodeProjectPatch(patch);
772
+ return decodeProject({
773
+ ...normalizedProject,
774
+ ...normalizedPatch,
775
+ updated: todayIso(),
776
+ });
777
+ };