@tomkapa/tayto 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -308,6 +308,17 @@ var SqliteProjectRepository = class {
308
308
  }
309
309
  };
310
310
 
311
+ // src/utils/search-parser.ts
312
+ var ID_PREFIX_RE = /^id:(.+)$/;
313
+ function parseSearchQuery(raw) {
314
+ const trimmed = raw.trim();
315
+ const match = ID_PREFIX_RE.exec(trimmed);
316
+ if (match?.[1]) {
317
+ return { kind: "id", value: match[1] };
318
+ }
319
+ return { kind: "fts", query: trimmed };
320
+ }
321
+
311
322
  // src/repository/task.repository.ts
312
323
  var TERMINAL_STATUS_ARRAY = [...TERMINAL_STATUSES];
313
324
  var TERMINAL_PLACEHOLDERS = TERMINAL_STATUS_ARRAY.map(() => "?").join(", ");
@@ -400,9 +411,15 @@ var SqliteTaskRepository = class {
400
411
  params.push(...filter.parentIds);
401
412
  }
402
413
  if (filter.search) {
403
- const ftsQuery = filter.search.trim().split(/\s+/).map((term) => `"${term.replace(/"/g, '""')}"*`).join(" ");
404
- conditions.push(`id IN (SELECT id FROM tasks_fts WHERE tasks_fts MATCH ?)`);
405
- params.push(ftsQuery);
414
+ const parsed = parseSearchQuery(filter.search);
415
+ if (parsed.kind === "id") {
416
+ conditions.push(`id LIKE ?`);
417
+ params.push(`%${parsed.value}%`);
418
+ } else {
419
+ const ftsQuery = parsed.query.split(/\s+/).map((term) => `"${term.replace(/"/g, '""')}"*`).join(" ");
420
+ conditions.push(`id IN (SELECT id FROM tasks_fts WHERE tasks_fts MATCH ?)`);
421
+ params.push(ftsQuery);
422
+ }
406
423
  }
407
424
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
408
425
  const sql = `SELECT * FROM tasks ${where} ORDER BY rank ASC`;
@@ -602,10 +619,33 @@ _${now}_
602
619
  return err(new AppError("DB_ERROR", "Failed to get ranked tasks by level", e));
603
620
  }
604
621
  }
622
+ getRankedNonTerminalTasksByLevel(projectId, level) {
623
+ try {
624
+ const types = this.getTypesForLevel(level);
625
+ const typePlaceholders = types.map(() => "?").join(", ");
626
+ const sql = `SELECT * FROM tasks WHERE project_id = ? AND ${NOT_DELETED} AND type IN (${typePlaceholders}) AND status NOT IN (${TERMINAL_PLACEHOLDERS}) ORDER BY rank ASC`;
627
+ const rows = this.db.prepare(sql).all(projectId, ...types, ...TERMINAL_STATUS_ARRAY);
628
+ return ok(rows.map(rowToTask));
629
+ } catch (e) {
630
+ return err(new AppError("DB_ERROR", "Failed to get ranked non-terminal tasks by level", e));
631
+ }
632
+ }
605
633
  search(query, projectId) {
606
634
  return logger.startSpan("TaskRepository.search", () => {
607
635
  try {
608
- const ftsQuery = query.trim().split(/\s+/).map((term) => `"${term.replace(/"/g, '""')}"*`).join(" ");
636
+ const parsed = parseSearchQuery(query);
637
+ if (parsed.kind === "id") {
638
+ const conditions = ["t.id LIKE ?", "t.deleted_at IS NULL"];
639
+ const params2 = [`%${parsed.value}%`];
640
+ if (projectId) {
641
+ conditions.push("t.project_id = ?");
642
+ params2.push(projectId);
643
+ }
644
+ const sql2 = `SELECT t.*, 0 AS fts_rank FROM tasks t WHERE ${conditions.join(" AND ")} ORDER BY t.rank ASC`;
645
+ const rows2 = this.db.prepare(sql2).all(...params2);
646
+ return ok(rows2.map((row) => ({ task: rowToTask(row), rank: row.fts_rank })));
647
+ }
648
+ const ftsQuery = parsed.query.split(/\s+/).map((term) => `"${term.replace(/"/g, '""')}"*`).join(" ");
609
649
  let sql;
610
650
  let params;
611
651
  if (projectId) {
@@ -1213,18 +1253,16 @@ var TaskServiceImpl = class {
1213
1253
  const projectResult = this.projectService.resolveProject(projectRef);
1214
1254
  if (!projectResult.ok) return projectResult;
1215
1255
  const projectId = projectResult.value.id;
1216
- const rankedResult = this.repo.getRankedTasksByLevel(
1217
- projectId,
1218
- taskLevel,
1219
- TaskStatus.Backlog
1220
- );
1256
+ const rankedResult = this.repo.getRankedNonTerminalTasksByLevel(projectId, taskLevel);
1221
1257
  if (!rankedResult.ok) return rankedResult;
1222
1258
  const ranked = rankedResult.value.filter((t) => t.id !== taskId);
1223
1259
  let newRank;
1224
1260
  if (afterId) {
1225
1261
  const anchor = ranked.find((t) => t.id === afterId);
1226
1262
  if (!anchor) {
1227
- return err(new AppError("NOT_FOUND", `Anchor task not found in backlog: ${afterId}`));
1263
+ return err(
1264
+ new AppError("NOT_FOUND", `Anchor task not found among active tasks: ${afterId}`)
1265
+ );
1228
1266
  }
1229
1267
  const anchorIndex = ranked.indexOf(anchor);
1230
1268
  const next = ranked[anchorIndex + 1];
@@ -1232,7 +1270,9 @@ var TaskServiceImpl = class {
1232
1270
  } else if (beforeId) {
1233
1271
  const anchor = ranked.find((t) => t.id === beforeId);
1234
1272
  if (!anchor) {
1235
- return err(new AppError("NOT_FOUND", `Anchor task not found in backlog: ${beforeId}`));
1273
+ return err(
1274
+ new AppError("NOT_FOUND", `Anchor task not found among active tasks: ${beforeId}`)
1275
+ );
1236
1276
  }
1237
1277
  const anchorIndex = ranked.indexOf(anchor);
1238
1278
  const prev = ranked[anchorIndex - 1];
@@ -1261,6 +1301,7 @@ var TaskServiceImpl = class {
1261
1301
  const blockersResult = depService.listBlockers(taskId);
1262
1302
  if (blockersResult.ok) {
1263
1303
  for (const blocker of blockersResult.value) {
1304
+ if (isTerminalStatus(blocker.status)) continue;
1264
1305
  if (blocker.projectId === projectId && newRank < blocker.rank) {
1265
1306
  return err(
1266
1307
  new AppError(
@@ -1274,6 +1315,7 @@ var TaskServiceImpl = class {
1274
1315
  const dependentsResult = depService.listDependents(taskId);
1275
1316
  if (dependentsResult.ok) {
1276
1317
  for (const dep of dependentsResult.value) {
1318
+ if (isTerminalStatus(dep.status)) continue;
1277
1319
  if (dep.projectId === projectId && newRank > dep.rank) {
1278
1320
  return err(
1279
1321
  new AppError(
@@ -1750,7 +1792,7 @@ var PortabilityServiceImpl = class {
1750
1792
  };
1751
1793
 
1752
1794
  // src/cli/container.ts
1753
- function createContainer(db) {
1795
+ function createContainer(db, dbPath) {
1754
1796
  const projectRepo = new SqliteProjectRepository(db);
1755
1797
  const taskRepo = new SqliteTaskRepository(db);
1756
1798
  const depRepo = new SqliteDependencyRepository(db);
@@ -1762,7 +1804,7 @@ function createContainer(db) {
1762
1804
  dependencyService,
1763
1805
  projectService
1764
1806
  );
1765
- return { projectService, taskService, dependencyService, portabilityService };
1807
+ return { dbPath, projectService, taskService, dependencyService, portabilityService };
1766
1808
  }
1767
1809
 
1768
1810
  // src/cli/index.ts
@@ -2114,7 +2156,7 @@ function buildCLI(container) {
2114
2156
  registerDepList(dep, container);
2115
2157
  registerDepGraph(dep, container);
2116
2158
  program.command("tui").description("Launch interactive terminal UI").option("-p, --project <project>", "Start with specific project").action(async (opts) => {
2117
- const { launchTUI } = await import("./tui-FTXYP3HM.js");
2159
+ const { launchTUI } = await import("./tui-5JJH67YY.js");
2118
2160
  await launchTUI(container, opts.project);
2119
2161
  });
2120
2162
  return program;
@@ -2127,10 +2169,10 @@ async function main() {
2127
2169
  initTelemetry(config);
2128
2170
  const db = createDatabase(config.dbPath);
2129
2171
  runMigrations(db);
2130
- const container = createContainer(db);
2172
+ const container = createContainer(db, config.dbPath);
2131
2173
  const args = process.argv.slice(2);
2132
2174
  if (args.length === 0) {
2133
- const { launchTUI } = await import("./tui-FTXYP3HM.js");
2175
+ const { launchTUI } = await import("./tui-5JJH67YY.js");
2134
2176
  await launchTUI(container);
2135
2177
  } else {
2136
2178
  const program = buildCLI(container);