@tomkapa/tayto 0.3.2 → 0.4.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.
@@ -5,16 +5,17 @@ import {
5
5
  TaskStatus,
6
6
  TaskType,
7
7
  UIDependencyType,
8
+ detectGitRemote,
8
9
  isTerminalStatus,
9
10
  logger
10
- } from "./chunk-STYT4TGJ.js";
11
+ } from "./chunk-74Q55TOV.js";
11
12
 
12
13
  // src/tui/index.tsx
13
14
  import { render } from "ink";
14
15
 
15
16
  // src/tui/components/App.tsx
16
- import { useReducer, useEffect as useEffect2, useCallback as useCallback2, useMemo as useMemo2, useState as useState6 } from "react";
17
- import { Box as Box16, Text as Text16, useInput as useInput6, useApp, useStdout as useStdout4 } from "ink";
17
+ import { useReducer, useEffect as useEffect2, useCallback as useCallback2, useMemo as useMemo2, useState as useState7 } from "react";
18
+ import { Box as Box17, Text as Text17, useInput as useInput7, useApp, useStdout as useStdout4 } from "ink";
18
19
 
19
20
  // src/tui/types.ts
20
21
  var ViewType = {
@@ -26,6 +27,7 @@ var ViewType = {
26
27
  ProjectCreate: "project-create",
27
28
  DependencyList: "dependency-list",
28
29
  EpicPicker: "epic-picker",
30
+ ProjectLink: "project-link",
29
31
  Help: "help"
30
32
  };
31
33
 
@@ -192,6 +194,7 @@ var initialState = {
192
194
  epics: [],
193
195
  epicSelectedIndex: 0,
194
196
  selectedEpicIds: /* @__PURE__ */ new Set(),
197
+ linkingProject: null,
195
198
  isEpicReordering: false,
196
199
  epicReorderSnapshot: null
197
200
  };
@@ -214,6 +217,7 @@ function appReducer(state, action) {
214
217
  confirmDelete: null,
215
218
  isSearchActive: false,
216
219
  formData: null,
220
+ linkingProject: null,
217
221
  focusedPanel: "list"
218
222
  };
219
223
  }
@@ -396,6 +400,8 @@ function appReducer(state, action) {
396
400
  epicReorderSnapshot: null
397
401
  };
398
402
  }
403
+ case "SET_LINKING_PROJECT":
404
+ return { ...state, linkingProject: action.project };
399
405
  }
400
406
  }
401
407
 
@@ -1314,6 +1320,7 @@ function ProjectSelector({
1314
1320
  onSelect,
1315
1321
  onCreate,
1316
1322
  onSetDefault,
1323
+ onLink,
1317
1324
  onCancel
1318
1325
  }) {
1319
1326
  const [selectedIndex, setSelectedIndex] = useState3(() => {
@@ -1344,6 +1351,13 @@ function ProjectSelector({
1344
1351
  }
1345
1352
  return;
1346
1353
  }
1354
+ if (input === "l") {
1355
+ const project = projects[selectedIndex];
1356
+ if (project) {
1357
+ onLink(project);
1358
+ }
1359
+ return;
1360
+ }
1347
1361
  if (key.upArrow || input === "k") {
1348
1362
  setSelectedIndex((i) => Math.max(0, i - 1));
1349
1363
  }
@@ -1365,6 +1379,7 @@ function ProjectSelector({
1365
1379
  ] }),
1366
1380
  /* @__PURE__ */ jsxs7(Box9, { paddingX: 1, children: [
1367
1381
  /* @__PURE__ */ jsx9(Text9, { color: theme.table.headerFg, bold: true, children: " NAME".padEnd(30) }),
1382
+ /* @__PURE__ */ jsx9(Text9, { color: theme.table.headerFg, bold: true, children: "GIT REMOTE".padEnd(40) }),
1368
1383
  /* @__PURE__ */ jsx9(Text9, { color: theme.table.headerFg, bold: true, children: "DESCRIPTION" })
1369
1384
  ] }),
1370
1385
  projects.length === 0 ? /* @__PURE__ */ jsx9(Box9, { paddingX: 1, paddingY: 1, children: /* @__PURE__ */ jsx9(Text9, { color: theme.fg, children: "No projects. Press 'c' to create one." }) }) : projects.map((project, i) => {
@@ -1373,11 +1388,13 @@ function ProjectSelector({
1373
1388
  const activeMarker = isActive ? "*" : " ";
1374
1389
  const defaultMarker = project.isDefault ? "D" : " ";
1375
1390
  const marker = `${activeMarker}${defaultMarker}`;
1391
+ const remoteDisplay = (project.gitRemote ?? "").slice(0, 38).padEnd(40);
1376
1392
  if (isSelected) {
1377
1393
  return /* @__PURE__ */ jsx9(Box9, { paddingX: 1, children: /* @__PURE__ */ jsxs7(Text9, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
1378
1394
  marker,
1379
1395
  " ",
1380
1396
  project.name.padEnd(27),
1397
+ remoteDisplay,
1381
1398
  project.description
1382
1399
  ] }) }, project.id);
1383
1400
  }
@@ -1387,11 +1404,12 @@ function ProjectSelector({
1387
1404
  " ",
1388
1405
  project.name.padEnd(27)
1389
1406
  ] }),
1407
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: remoteDisplay }),
1390
1408
  /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: project.description })
1391
1409
  ] }, project.id);
1392
1410
  }),
1393
1411
  /* @__PURE__ */ jsx9(Box9, { flexGrow: 1 }),
1394
- /* @__PURE__ */ jsx9(Box9, { paddingX: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "enter: select | d: set default | c: create | esc: back" }) })
1412
+ /* @__PURE__ */ jsx9(Box9, { paddingX: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "enter: select | d: set default | l: link git | c: create | esc: back" }) })
1395
1413
  ] });
1396
1414
  }
1397
1415
 
@@ -1490,9 +1508,86 @@ function ProjectForm({ onSave, onCancel }) {
1490
1508
  ] });
1491
1509
  }
1492
1510
 
1493
- // src/tui/components/HelpOverlay.tsx
1494
- import { Box as Box11, Text as Text11 } from "ink";
1511
+ // src/tui/components/ProjectLinkForm.tsx
1512
+ import { useState as useState5 } from "react";
1513
+ import { Box as Box11, Text as Text11, useInput as useInput5 } from "ink";
1495
1514
  import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1515
+ function ProjectLinkForm({ project, onSave, onUnlink, onDetect, onCancel }) {
1516
+ const [remoteUrl, setRemoteUrl] = useState5(project.gitRemote ?? "");
1517
+ useInput5((input, key) => {
1518
+ if (key.escape) {
1519
+ onCancel();
1520
+ return;
1521
+ }
1522
+ if (input === "s" && key.ctrl) {
1523
+ const trimmed = remoteUrl.trim();
1524
+ if (trimmed) {
1525
+ onSave(trimmed);
1526
+ }
1527
+ return;
1528
+ }
1529
+ if (input === "d" && key.ctrl) {
1530
+ const detected = onDetect();
1531
+ if (detected) {
1532
+ setRemoteUrl(detected);
1533
+ }
1534
+ return;
1535
+ }
1536
+ if (input === "u" && key.ctrl) {
1537
+ if (project.gitRemote) {
1538
+ onUnlink();
1539
+ }
1540
+ return;
1541
+ }
1542
+ if (key.backspace || key.delete) {
1543
+ setRemoteUrl((v) => v.slice(0, -1));
1544
+ return;
1545
+ }
1546
+ if (input && !key.ctrl && !key.meta) {
1547
+ setRemoteUrl((v) => v + input);
1548
+ }
1549
+ });
1550
+ return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
1551
+ /* @__PURE__ */ jsxs9(Box11, { gap: 0, children: [
1552
+ /* @__PURE__ */ jsxs9(Text11, { color: theme.title, bold: true, children: [
1553
+ " ",
1554
+ "link git remote"
1555
+ ] }),
1556
+ /* @__PURE__ */ jsxs9(Text11, { color: theme.titleCounter, bold: true, children: [
1557
+ " ",
1558
+ "[",
1559
+ project.name,
1560
+ "]"
1561
+ ] })
1562
+ ] }),
1563
+ /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [
1564
+ /* @__PURE__ */ jsxs9(Box11, { gap: 1, children: [
1565
+ /* @__PURE__ */ jsx11(Text11, { color: theme.dialog.label, bold: true, children: "Current:" }),
1566
+ /* @__PURE__ */ jsx11(
1567
+ Text11,
1568
+ {
1569
+ color: project.gitRemote ? theme.yaml.value : theme.table.fg,
1570
+ dimColor: !project.gitRemote,
1571
+ children: project.gitRemote ?? "(none)"
1572
+ }
1573
+ )
1574
+ ] }),
1575
+ /* @__PURE__ */ jsxs9(Box11, { gap: 1, marginTop: 1, children: [
1576
+ /* @__PURE__ */ jsx11(Text11, { color: theme.dialog.label, bold: true, children: "Remote URL: " }),
1577
+ /* @__PURE__ */ jsxs9(Text11, { color: theme.yaml.value, children: [
1578
+ remoteUrl,
1579
+ /* @__PURE__ */ jsx11(Text11, { color: theme.titleHighlight, children: "_" })
1580
+ ] })
1581
+ ] })
1582
+ ] }),
1583
+ /* @__PURE__ */ jsx11(Box11, { flexGrow: 1 }),
1584
+ /* @__PURE__ */ jsx11(Box11, { paddingX: 1, children: /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "ctrl+s: save | ctrl+d: detect from cwd | ctrl+u: unlink | esc: cancel" }) })
1585
+ ] });
1586
+ }
1587
+
1588
+ // src/tui/components/HelpOverlay.tsx
1589
+ import { Box as Box12, Text as Text12 } from "ink";
1590
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1496
1591
  var SECTIONS = [
1497
1592
  {
1498
1593
  title: "NAVIGATION",
@@ -1554,8 +1649,8 @@ var SECTIONS = [
1554
1649
  }
1555
1650
  ];
1556
1651
  function HelpOverlay() {
1557
- return /* @__PURE__ */ jsxs9(
1558
- Box11,
1652
+ return /* @__PURE__ */ jsxs10(
1653
+ Box12,
1559
1654
  {
1560
1655
  flexDirection: "column",
1561
1656
  borderStyle: "bold",
@@ -1563,35 +1658,35 @@ function HelpOverlay() {
1563
1658
  paddingX: 2,
1564
1659
  paddingY: 1,
1565
1660
  children: [
1566
- /* @__PURE__ */ jsxs9(Text11, { color: theme.title, bold: true, children: [
1661
+ /* @__PURE__ */ jsxs10(Text12, { color: theme.title, bold: true, children: [
1567
1662
  " ",
1568
1663
  "Help"
1569
1664
  ] }),
1570
- /* @__PURE__ */ jsx11(Text11, { children: " " }),
1571
- /* @__PURE__ */ jsx11(Box11, { flexDirection: "row", gap: 4, children: SECTIONS.map((section) => /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", children: [
1572
- /* @__PURE__ */ jsx11(Text11, { color: theme.table.headerFg, bold: true, children: section.title }),
1573
- section.keys.map(([key, desc]) => /* @__PURE__ */ jsxs9(Box11, { gap: 1, children: [
1574
- /* @__PURE__ */ jsxs9(Text11, { color: theme.menu.key, bold: true, children: [
1665
+ /* @__PURE__ */ jsx12(Text12, { children: " " }),
1666
+ /* @__PURE__ */ jsx12(Box12, { flexDirection: "row", gap: 4, children: SECTIONS.map((section) => /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", children: [
1667
+ /* @__PURE__ */ jsx12(Text12, { color: theme.table.headerFg, bold: true, children: section.title }),
1668
+ section.keys.map(([key, desc]) => /* @__PURE__ */ jsxs10(Box12, { gap: 1, children: [
1669
+ /* @__PURE__ */ jsxs10(Text12, { color: theme.menu.key, bold: true, children: [
1575
1670
  "<",
1576
1671
  (key ?? "").padEnd(5),
1577
1672
  ">"
1578
1673
  ] }),
1579
- /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: desc })
1674
+ /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: desc })
1580
1675
  ] }, key))
1581
1676
  ] }, section.title)) }),
1582
- /* @__PURE__ */ jsx11(Text11, { children: " " }),
1583
- /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "Press any key to close" })
1677
+ /* @__PURE__ */ jsx12(Text12, { children: " " }),
1678
+ /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Press any key to close" })
1584
1679
  ]
1585
1680
  }
1586
1681
  );
1587
1682
  }
1588
1683
 
1589
1684
  // src/tui/components/ConfirmDialog.tsx
1590
- import { Box as Box12, Text as Text12 } from "ink";
1591
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1685
+ import { Box as Box13, Text as Text13 } from "ink";
1686
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1592
1687
  function ConfirmDialog({ task }) {
1593
- return /* @__PURE__ */ jsxs10(
1594
- Box12,
1688
+ return /* @__PURE__ */ jsxs11(
1689
+ Box13,
1595
1690
  {
1596
1691
  flexDirection: "column",
1597
1692
  borderStyle: "bold",
@@ -1600,17 +1695,17 @@ function ConfirmDialog({ task }) {
1600
1695
  paddingY: 1,
1601
1696
  alignSelf: "center",
1602
1697
  children: [
1603
- /* @__PURE__ */ jsx12(Text12, { color: theme.dialog.label, bold: true, children: "<Delete>" }),
1604
- /* @__PURE__ */ jsx12(Text12, { children: " " }),
1605
- /* @__PURE__ */ jsxs10(Text12, { color: theme.dialog.fg, children: [
1698
+ /* @__PURE__ */ jsx13(Text13, { color: theme.dialog.label, bold: true, children: "<Delete>" }),
1699
+ /* @__PURE__ */ jsx13(Text13, { children: " " }),
1700
+ /* @__PURE__ */ jsxs11(Text13, { color: theme.dialog.fg, children: [
1606
1701
  'Delete task "',
1607
1702
  task.name,
1608
1703
  '"?'
1609
1704
  ] }),
1610
- /* @__PURE__ */ jsx12(Text12, { children: " " }),
1611
- /* @__PURE__ */ jsxs10(Box12, { gap: 3, children: [
1612
- /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(
1613
- Text12,
1705
+ /* @__PURE__ */ jsx13(Text13, { children: " " }),
1706
+ /* @__PURE__ */ jsxs11(Box13, { gap: 3, children: [
1707
+ /* @__PURE__ */ jsx13(Box13, { children: /* @__PURE__ */ jsx13(
1708
+ Text13,
1614
1709
  {
1615
1710
  backgroundColor: theme.dialog.buttonFocusBg,
1616
1711
  color: theme.dialog.buttonFocusFg,
@@ -1618,7 +1713,7 @@ function ConfirmDialog({ task }) {
1618
1713
  children: " y: OK "
1619
1714
  }
1620
1715
  ) }),
1621
- /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(Text12, { backgroundColor: theme.dialog.buttonBg, color: theme.dialog.buttonFg, children: " n: Cancel " }) })
1716
+ /* @__PURE__ */ jsx13(Box13, { children: /* @__PURE__ */ jsx13(Text13, { backgroundColor: theme.dialog.buttonBg, color: theme.dialog.buttonFg, children: " n: Cancel " }) })
1622
1717
  ] })
1623
1718
  ]
1624
1719
  }
@@ -1626,8 +1721,8 @@ function ConfirmDialog({ task }) {
1626
1721
  }
1627
1722
 
1628
1723
  // src/tui/components/DependencyList.tsx
1629
- import { Box as Box13, Text as Text13 } from "ink";
1630
- import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1724
+ import { Box as Box14, Text as Text14 } from "ink";
1725
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1631
1726
  function TaskRow({
1632
1727
  task,
1633
1728
  globalIndex,
@@ -1635,16 +1730,16 @@ function TaskRow({
1635
1730
  }) {
1636
1731
  const isSelected = globalIndex === selectedIndex;
1637
1732
  const statusColor = STATUS_COLOR[task.status] ?? theme.table.fg;
1638
- return /* @__PURE__ */ jsx13(Box13, { children: isSelected ? /* @__PURE__ */ jsxs11(Text13, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
1733
+ return /* @__PURE__ */ jsx14(Box14, { children: isSelected ? /* @__PURE__ */ jsxs12(Text14, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
1639
1734
  "> ",
1640
1735
  task.id.padEnd(12),
1641
1736
  task.status.padEnd(14),
1642
1737
  task.name
1643
- ] }) : /* @__PURE__ */ jsxs11(Fragment2, { children: [
1644
- /* @__PURE__ */ jsx13(Text13, { children: " " }),
1645
- /* @__PURE__ */ jsx13(Text13, { color: theme.yaml.value, children: task.id.padEnd(12) }),
1646
- /* @__PURE__ */ jsx13(Text13, { color: statusColor, children: task.status.padEnd(14) }),
1647
- /* @__PURE__ */ jsx13(Text13, { color: theme.table.fg, children: task.name })
1738
+ ] }) : /* @__PURE__ */ jsxs12(Fragment2, { children: [
1739
+ /* @__PURE__ */ jsx14(Text14, { children: " " }),
1740
+ /* @__PURE__ */ jsx14(Text14, { color: theme.yaml.value, children: task.id.padEnd(12) }),
1741
+ /* @__PURE__ */ jsx14(Text14, { color: statusColor, children: task.status.padEnd(14) }),
1742
+ /* @__PURE__ */ jsx14(Text14, { color: theme.table.fg, children: task.name })
1648
1743
  ] }) }, task.id);
1649
1744
  }
1650
1745
  function DependencyList({
@@ -1665,23 +1760,23 @@ function DependencyList({
1665
1760
  const relatedOffset = offset;
1666
1761
  offset += related.length;
1667
1762
  const duplicatesOffset = offset;
1668
- return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
1669
- /* @__PURE__ */ jsxs11(Box13, { gap: 0, children: [
1670
- /* @__PURE__ */ jsxs11(Text13, { color: theme.title, bold: true, children: [
1763
+ return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
1764
+ /* @__PURE__ */ jsxs12(Box14, { gap: 0, children: [
1765
+ /* @__PURE__ */ jsxs12(Text14, { color: theme.title, bold: true, children: [
1671
1766
  " ",
1672
1767
  "dependencies"
1673
1768
  ] }),
1674
- /* @__PURE__ */ jsx13(Text13, { color: theme.fg, children: "(" }),
1675
- /* @__PURE__ */ jsx13(Text13, { color: theme.titleHighlight, bold: true, children: task.name }),
1676
- /* @__PURE__ */ jsx13(Text13, { color: theme.fg, children: ")" })
1769
+ /* @__PURE__ */ jsx14(Text14, { color: theme.fg, children: "(" }),
1770
+ /* @__PURE__ */ jsx14(Text14, { color: theme.titleHighlight, bold: true, children: task.name }),
1771
+ /* @__PURE__ */ jsx14(Text14, { color: theme.fg, children: ")" })
1677
1772
  ] }),
1678
- /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
1679
- /* @__PURE__ */ jsxs11(Text13, { color: theme.table.headerFg, bold: true, children: [
1773
+ /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
1774
+ /* @__PURE__ */ jsxs12(Text14, { color: theme.table.headerFg, bold: true, children: [
1680
1775
  "BLOCKED BY (",
1681
1776
  blockers.length,
1682
1777
  ")"
1683
1778
  ] }),
1684
- blockers.length === 0 ? /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: " No blockers" }) : blockers.map((t, i) => /* @__PURE__ */ jsx13(
1779
+ blockers.length === 0 ? /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: " No blockers" }) : blockers.map((t, i) => /* @__PURE__ */ jsx14(
1685
1780
  TaskRow,
1686
1781
  {
1687
1782
  task: t,
@@ -1691,13 +1786,13 @@ function DependencyList({
1691
1786
  t.id
1692
1787
  ))
1693
1788
  ] }),
1694
- /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
1695
- /* @__PURE__ */ jsxs11(Text13, { color: theme.table.headerFg, bold: true, children: [
1789
+ /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
1790
+ /* @__PURE__ */ jsxs12(Text14, { color: theme.table.headerFg, bold: true, children: [
1696
1791
  "BLOCKS (",
1697
1792
  dependents.length,
1698
1793
  ")"
1699
1794
  ] }),
1700
- dependents.length === 0 ? /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: " No dependents" }) : dependents.map((t, i) => /* @__PURE__ */ jsx13(
1795
+ dependents.length === 0 ? /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: " No dependents" }) : dependents.map((t, i) => /* @__PURE__ */ jsx14(
1701
1796
  TaskRow,
1702
1797
  {
1703
1798
  task: t,
@@ -1707,13 +1802,13 @@ function DependencyList({
1707
1802
  t.id
1708
1803
  ))
1709
1804
  ] }),
1710
- /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
1711
- /* @__PURE__ */ jsxs11(Text13, { color: theme.table.headerFg, bold: true, children: [
1805
+ /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
1806
+ /* @__PURE__ */ jsxs12(Text14, { color: theme.table.headerFg, bold: true, children: [
1712
1807
  "RELATES TO (",
1713
1808
  related.length,
1714
1809
  ")"
1715
1810
  ] }),
1716
- related.length === 0 ? /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: " No related tasks" }) : related.map((t, i) => /* @__PURE__ */ jsx13(
1811
+ related.length === 0 ? /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: " No related tasks" }) : related.map((t, i) => /* @__PURE__ */ jsx14(
1717
1812
  TaskRow,
1718
1813
  {
1719
1814
  task: t,
@@ -1723,13 +1818,13 @@ function DependencyList({
1723
1818
  t.id
1724
1819
  ))
1725
1820
  ] }),
1726
- /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
1727
- /* @__PURE__ */ jsxs11(Text13, { color: theme.table.headerFg, bold: true, children: [
1821
+ /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
1822
+ /* @__PURE__ */ jsxs12(Text14, { color: theme.table.headerFg, bold: true, children: [
1728
1823
  "DUPLICATES (",
1729
1824
  duplicates.length,
1730
1825
  ")"
1731
1826
  ] }),
1732
- duplicates.length === 0 ? /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: " No duplicate tasks" }) : duplicates.map((t, i) => /* @__PURE__ */ jsx13(
1827
+ duplicates.length === 0 ? /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: " No duplicate tasks" }) : duplicates.map((t, i) => /* @__PURE__ */ jsx14(
1733
1828
  TaskRow,
1734
1829
  {
1735
1830
  task: t,
@@ -1739,19 +1834,19 @@ function DependencyList({
1739
1834
  t.id
1740
1835
  ))
1741
1836
  ] }),
1742
- /* @__PURE__ */ jsx13(Box13, { flexGrow: 1 }),
1743
- isAddingDep && /* @__PURE__ */ jsxs11(Box13, { borderStyle: "round", borderColor: theme.prompt, paddingX: 1, children: [
1744
- /* @__PURE__ */ jsx13(Text13, { color: theme.prompt, children: "depends on (id or id:type): " }),
1745
- /* @__PURE__ */ jsx13(Text13, { color: theme.prompt, children: addDepInput }),
1746
- /* @__PURE__ */ jsx13(Text13, { color: theme.promptSuggest, children: "_" })
1837
+ /* @__PURE__ */ jsx14(Box14, { flexGrow: 1 }),
1838
+ isAddingDep && /* @__PURE__ */ jsxs12(Box14, { borderStyle: "round", borderColor: theme.prompt, paddingX: 1, children: [
1839
+ /* @__PURE__ */ jsx14(Text14, { color: theme.prompt, children: "depends on (id or id:type): " }),
1840
+ /* @__PURE__ */ jsx14(Text14, { color: theme.prompt, children: addDepInput }),
1841
+ /* @__PURE__ */ jsx14(Text14, { color: theme.promptSuggest, children: "_" })
1747
1842
  ] }),
1748
- /* @__PURE__ */ jsx13(Box13, { paddingX: 1, children: /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "a: add dep (id or id:relates-to) | x: remove selected | enter: go to task | esc: back" }) })
1843
+ /* @__PURE__ */ jsx14(Box14, { paddingX: 1, children: /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "a: add dep (id or id:relates-to) | x: remove selected | enter: go to task | esc: back" }) })
1749
1844
  ] });
1750
1845
  }
1751
1846
 
1752
1847
  // src/tui/components/EpicPanel.tsx
1753
- import { Box as Box14, Text as Text14 } from "ink";
1754
- import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1848
+ import { Box as Box15, Text as Text15 } from "ink";
1849
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1755
1850
  var PAGE_SIZE2 = 20;
1756
1851
  function EpicPanel({
1757
1852
  epics,
@@ -1764,34 +1859,34 @@ function EpicPanel({
1764
1859
  const currentPage = Math.floor(selectedIndex / PAGE_SIZE2);
1765
1860
  const viewStart = currentPage * PAGE_SIZE2;
1766
1861
  const visibleEpics = epics.slice(viewStart, viewStart + PAGE_SIZE2);
1767
- return /* @__PURE__ */ jsxs12(
1768
- Box14,
1862
+ return /* @__PURE__ */ jsxs13(
1863
+ Box15,
1769
1864
  {
1770
1865
  flexDirection: "column",
1771
1866
  width: 48,
1772
1867
  borderStyle: "bold",
1773
1868
  borderColor: isFocused ? theme.borderFocus : theme.border,
1774
1869
  children: [
1775
- /* @__PURE__ */ jsxs12(Box14, { children: [
1776
- /* @__PURE__ */ jsxs12(Text14, { color: theme.title, bold: true, children: [
1870
+ /* @__PURE__ */ jsxs13(Box15, { children: [
1871
+ /* @__PURE__ */ jsxs13(Text15, { color: theme.title, bold: true, children: [
1777
1872
  " ",
1778
1873
  "epics"
1779
1874
  ] }),
1780
- /* @__PURE__ */ jsxs12(Text14, { color: theme.titleCounter, bold: true, children: [
1875
+ /* @__PURE__ */ jsxs13(Text15, { color: theme.titleCounter, bold: true, children: [
1781
1876
  "[",
1782
1877
  epics.length,
1783
1878
  "]"
1784
1879
  ] }),
1785
- isReordering && /* @__PURE__ */ jsxs12(Text14, { color: theme.flash.warn, bold: true, children: [
1880
+ isReordering && /* @__PURE__ */ jsxs13(Text15, { color: theme.flash.warn, bold: true, children: [
1786
1881
  " ",
1787
1882
  "REORDER"
1788
1883
  ] }),
1789
- filterActive && /* @__PURE__ */ jsxs12(Text14, { color: theme.titleFilter, children: [
1884
+ filterActive && /* @__PURE__ */ jsxs13(Text15, { color: theme.titleFilter, children: [
1790
1885
  " *",
1791
1886
  selectedEpicIds.size
1792
1887
  ] })
1793
1888
  ] }),
1794
- /* @__PURE__ */ jsx14(Box14, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: epics.length === 0 ? /* @__PURE__ */ jsx14(Box14, { paddingX: 1, children: /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "No epics" }) }) : visibleEpics.map((epic, i) => {
1889
+ /* @__PURE__ */ jsx15(Box15, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: epics.length === 0 ? /* @__PURE__ */ jsx15(Box15, { paddingX: 1, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "No epics" }) }) : visibleEpics.map((epic, i) => {
1795
1890
  const actualIndex = viewStart + i;
1796
1891
  const isSelected = actualIndex === selectedIndex && isFocused;
1797
1892
  const isChecked = selectedEpicIds.has(epic.id);
@@ -1799,21 +1894,21 @@ function EpicPanel({
1799
1894
  const statusColor = STATUS_COLOR[epic.status] ?? theme.table.fg;
1800
1895
  if (isSelected) {
1801
1896
  const cursorBg = isReordering ? theme.flash.warn : theme.table.cursorBg;
1802
- return /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsxs12(Text14, { backgroundColor: cursorBg, color: theme.table.cursorFg, bold: true, children: [
1897
+ return /* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsxs13(Text15, { backgroundColor: cursorBg, color: theme.table.cursorFg, bold: true, children: [
1803
1898
  isReordering ? "~ " : " ",
1804
1899
  marker,
1805
1900
  " ",
1806
1901
  epic.name
1807
1902
  ] }) }, epic.id);
1808
1903
  }
1809
- return /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsxs12(Text14, { color: isChecked ? theme.titleHighlight : statusColor, children: [
1904
+ return /* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsxs13(Text15, { color: isChecked ? theme.titleHighlight : statusColor, children: [
1810
1905
  " ",
1811
1906
  marker,
1812
1907
  " ",
1813
1908
  epic.name
1814
1909
  ] }) }, epic.id);
1815
1910
  }) }),
1816
- epics.length > PAGE_SIZE2 && /* @__PURE__ */ jsx14(Box14, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs12(Text14, { dimColor: true, children: [
1911
+ epics.length > PAGE_SIZE2 && /* @__PURE__ */ jsx15(Box15, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs13(Text15, { dimColor: true, children: [
1817
1912
  "[",
1818
1913
  viewStart + 1,
1819
1914
  "-",
@@ -1828,16 +1923,16 @@ function EpicPanel({
1828
1923
  }
1829
1924
 
1830
1925
  // src/tui/components/EpicPicker.tsx
1831
- import { useState as useState5 } from "react";
1832
- import { Box as Box15, Text as Text15, useInput as useInput5, useStdout as useStdout3 } from "ink";
1833
- import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1926
+ import { useState as useState6 } from "react";
1927
+ import { Box as Box16, Text as Text16, useInput as useInput6, useStdout as useStdout3 } from "ink";
1928
+ import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
1834
1929
  function EpicPicker({ epics, currentEpicId, onSelect, onCancel }) {
1835
1930
  const { stdout } = useStdout3();
1836
1931
  const termHeight = stdout.rows > 0 ? stdout.rows : 24;
1837
1932
  const maxVisible = Math.max(3, termHeight - 10);
1838
- const [searchQuery, setSearchQuery] = useState5("");
1839
- const [isSearching, setIsSearching] = useState5(false);
1840
- const [cursorIndex, setCursorIndex] = useState5(0);
1933
+ const [searchQuery, setSearchQuery] = useState6("");
1934
+ const [isSearching, setIsSearching] = useState6(false);
1935
+ const [cursorIndex, setCursorIndex] = useState6(0);
1841
1936
  const filtered = epics.filter((e) => {
1842
1937
  if (!searchQuery.trim()) return true;
1843
1938
  const q = searchQuery.toLowerCase();
@@ -1851,7 +1946,7 @@ function EpicPicker({ epics, currentEpicId, onSelect, onCancel }) {
1851
1946
  viewStart = cursorIndex;
1852
1947
  }
1853
1948
  const visible = filtered.slice(viewStart, viewStart + maxVisible);
1854
- useInput5((input, key) => {
1949
+ useInput6((input, key) => {
1855
1950
  if (isSearching) {
1856
1951
  if (key.escape) {
1857
1952
  setIsSearching(false);
@@ -1903,53 +1998,53 @@ function EpicPicker({ epics, currentEpicId, onSelect, onCancel }) {
1903
1998
  return;
1904
1999
  }
1905
2000
  });
1906
- return /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", borderStyle: "bold", borderColor: theme.borderFocus, flexGrow: 1, children: [
1907
- /* @__PURE__ */ jsxs13(Box15, { gap: 0, children: [
1908
- /* @__PURE__ */ jsxs13(Text15, { color: theme.title, bold: true, children: [
2001
+ return /* @__PURE__ */ jsxs14(Box16, { flexDirection: "column", borderStyle: "bold", borderColor: theme.borderFocus, flexGrow: 1, children: [
2002
+ /* @__PURE__ */ jsxs14(Box16, { gap: 0, children: [
2003
+ /* @__PURE__ */ jsxs14(Text16, { color: theme.title, bold: true, children: [
1909
2004
  " ",
1910
2005
  "assign to epic"
1911
2006
  ] }),
1912
- /* @__PURE__ */ jsxs13(Text15, { color: theme.titleCounter, bold: true, children: [
2007
+ /* @__PURE__ */ jsxs14(Text16, { color: theme.titleCounter, bold: true, children: [
1913
2008
  " ",
1914
2009
  "[",
1915
2010
  epics.length,
1916
2011
  "]"
1917
2012
  ] })
1918
2013
  ] }),
1919
- isSearching ? /* @__PURE__ */ jsxs13(Box15, { borderStyle: "round", borderColor: theme.prompt, paddingX: 1, children: [
1920
- /* @__PURE__ */ jsx15(Text15, { color: theme.prompt, children: "/" }),
1921
- /* @__PURE__ */ jsx15(Text15, { color: theme.prompt, children: searchQuery }),
1922
- /* @__PURE__ */ jsx15(Text15, { color: theme.promptSuggest, children: "_" })
1923
- ] }) : searchQuery ? /* @__PURE__ */ jsx15(Box15, { paddingX: 1, children: /* @__PURE__ */ jsxs13(Text15, { color: theme.titleFilter, children: [
2014
+ isSearching ? /* @__PURE__ */ jsxs14(Box16, { borderStyle: "round", borderColor: theme.prompt, paddingX: 1, children: [
2015
+ /* @__PURE__ */ jsx16(Text16, { color: theme.prompt, children: "/" }),
2016
+ /* @__PURE__ */ jsx16(Text16, { color: theme.prompt, children: searchQuery }),
2017
+ /* @__PURE__ */ jsx16(Text16, { color: theme.promptSuggest, children: "_" })
2018
+ ] }) : searchQuery ? /* @__PURE__ */ jsx16(Box16, { paddingX: 1, children: /* @__PURE__ */ jsxs14(Text16, { color: theme.titleFilter, children: [
1924
2019
  "/",
1925
2020
  searchQuery
1926
2021
  ] }) }) : null,
1927
- /* @__PURE__ */ jsx15(Box15, { paddingX: 1, children: /* @__PURE__ */ jsxs13(Text15, { color: theme.table.headerFg, bold: true, children: [
2022
+ /* @__PURE__ */ jsx16(Box16, { paddingX: 1, children: /* @__PURE__ */ jsxs14(Text16, { color: theme.table.headerFg, bold: true, children: [
1928
2023
  " ",
1929
2024
  "ID".padEnd(14),
1930
2025
  "STATUS".padEnd(14),
1931
2026
  "NAME"
1932
2027
  ] }) }),
1933
- filtered.length === 0 ? /* @__PURE__ */ jsx15(Box15, { paddingX: 2, paddingY: 1, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "No epics match the filter" }) }) : visible.map((epic, i) => {
2028
+ filtered.length === 0 ? /* @__PURE__ */ jsx16(Box16, { paddingX: 2, paddingY: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "No epics match the filter" }) }) : visible.map((epic, i) => {
1934
2029
  const actualIndex = viewStart + i;
1935
2030
  const isCursor = actualIndex === cursorIndex;
1936
2031
  const isCurrent = epic.id === currentEpicId;
1937
2032
  const marker = isCurrent ? "* " : " ";
1938
2033
  const statusColor = STATUS_COLOR[epic.status] ?? theme.table.fg;
1939
- return /* @__PURE__ */ jsx15(Box15, { paddingX: 1, children: isCursor ? /* @__PURE__ */ jsxs13(Text15, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
2034
+ return /* @__PURE__ */ jsx16(Box16, { paddingX: 1, children: isCursor ? /* @__PURE__ */ jsxs14(Text16, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
1940
2035
  "> ",
1941
2036
  epic.id.padEnd(14),
1942
2037
  epic.status.padEnd(14),
1943
2038
  epic.name
1944
- ] }) : /* @__PURE__ */ jsxs13(Fragment3, { children: [
1945
- /* @__PURE__ */ jsx15(Text15, { color: isCurrent ? theme.titleHighlight : theme.table.fg, children: marker }),
1946
- /* @__PURE__ */ jsx15(Text15, { color: theme.yaml.value, children: epic.id.padEnd(14) }),
1947
- /* @__PURE__ */ jsx15(Text15, { color: statusColor, children: epic.status.padEnd(14) }),
1948
- /* @__PURE__ */ jsx15(Text15, { color: isCurrent ? theme.titleHighlight : theme.table.fg, children: epic.name })
2039
+ ] }) : /* @__PURE__ */ jsxs14(Fragment3, { children: [
2040
+ /* @__PURE__ */ jsx16(Text16, { color: isCurrent ? theme.titleHighlight : theme.table.fg, children: marker }),
2041
+ /* @__PURE__ */ jsx16(Text16, { color: theme.yaml.value, children: epic.id.padEnd(14) }),
2042
+ /* @__PURE__ */ jsx16(Text16, { color: statusColor, children: epic.status.padEnd(14) }),
2043
+ /* @__PURE__ */ jsx16(Text16, { color: isCurrent ? theme.titleHighlight : theme.table.fg, children: epic.name })
1949
2044
  ] }) }, epic.id);
1950
2045
  }),
1951
- /* @__PURE__ */ jsx15(Box15, { flexGrow: 1 }),
1952
- filtered.length > maxVisible && /* @__PURE__ */ jsx15(Box15, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs13(Text15, { dimColor: true, children: [
2046
+ /* @__PURE__ */ jsx16(Box16, { flexGrow: 1 }),
2047
+ filtered.length > maxVisible && /* @__PURE__ */ jsx16(Box16, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs14(Text16, { dimColor: true, children: [
1953
2048
  "[",
1954
2049
  viewStart + 1,
1955
2050
  "-",
@@ -1958,7 +2053,7 @@ function EpicPicker({ epics, currentEpicId, onSelect, onCancel }) {
1958
2053
  filtered.length,
1959
2054
  "]"
1960
2055
  ] }) }),
1961
- /* @__PURE__ */ jsx15(Box15, { paddingX: 1, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "enter: assign | x: unassign | /: search | esc: cancel" }) })
2056
+ /* @__PURE__ */ jsx16(Box16, { paddingX: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "enter: assign | x: unassign | /: search | esc: cancel" }) })
1962
2057
  ] });
1963
2058
  }
1964
2059
 
@@ -1998,7 +2093,7 @@ function useAutoRefetch(dbPath, onRefetch) {
1998
2093
  }
1999
2094
 
2000
2095
  // src/tui/components/App.tsx
2001
- import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
2096
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
2002
2097
  var STATUS_CYCLE = [
2003
2098
  TaskStatus.Backlog,
2004
2099
  TaskStatus.Todo,
@@ -2011,7 +2106,7 @@ function App({ container, initialProject }) {
2011
2106
  const { exit } = useApp();
2012
2107
  const { stdout } = useStdout4();
2013
2108
  const [state, dispatch] = useReducer(appReducer, initialState);
2014
- const [, setResizeTick] = useState6(0);
2109
+ const [, setResizeTick] = useState7(0);
2015
2110
  useEffect2(() => {
2016
2111
  const onResize = () => {
2017
2112
  setResizeTick((n) => n + 1);
@@ -2036,15 +2131,14 @@ function App({ container, initialProject }) {
2036
2131
  }
2037
2132
  }, [container]);
2038
2133
  const loadTasks = useCallback2(() => {
2134
+ const activeProject = state.activeProject;
2135
+ if (!activeProject) return;
2039
2136
  logger.startSpan("TUI.loadTasks", () => {
2040
2137
  const filter = { ...state.filter, level: TaskLevel.Work };
2041
- if (state.activeProject) {
2042
- filter.projectId = state.activeProject.id;
2043
- }
2044
2138
  if (state.selectedEpicIds.size > 0) {
2045
2139
  filter.parentIds = [...state.selectedEpicIds];
2046
2140
  }
2047
- const result = container.taskService.listTasks(filter);
2141
+ const result = container.taskService.listTasks(activeProject, filter);
2048
2142
  if (result.ok) {
2049
2143
  logger.info(`TUI.loadTasks: loaded ${result.value.length} tasks`);
2050
2144
  dispatch({ type: "SET_TASKS", tasks: result.value });
@@ -2056,8 +2150,7 @@ function App({ container, initialProject }) {
2056
2150
  }, [container, state.filter, state.activeProject, state.selectedEpicIds]);
2057
2151
  const loadEpics = useCallback2(() => {
2058
2152
  if (!state.activeProject) return;
2059
- const result = container.taskService.listTasks({
2060
- projectId: state.activeProject.id,
2153
+ const result = container.taskService.listTasks(state.activeProject, {
2061
2154
  level: TaskLevel.Epic
2062
2155
  });
2063
2156
  if (result.ok) {
@@ -2102,13 +2195,15 @@ function App({ container, initialProject }) {
2102
2195
  [container, state.selectedTask, loadTasks, loadEpics]
2103
2196
  );
2104
2197
  const saveReorder = useCallback2(() => {
2198
+ const activeProject = state.activeProject;
2199
+ if (!activeProject) return;
2105
2200
  const tasks = state.tasks;
2106
2201
  const idx = state.selectedIndex;
2107
2202
  const task = tasks[idx];
2108
2203
  if (!task) return;
2109
2204
  const prev = tasks[idx - 1];
2110
2205
  const next = tasks[idx + 1];
2111
- const result = prev ? container.taskService.rerankTask({ taskId: task.id, afterId: prev.id }) : next ? container.taskService.rerankTask({ taskId: task.id, beforeId: next.id }) : container.taskService.rerankTask({ taskId: task.id, position: 1 });
2206
+ const result = prev ? container.taskService.rerankTask({ taskId: task.id, afterId: prev.id }, activeProject) : next ? container.taskService.rerankTask({ taskId: task.id, beforeId: next.id }, activeProject) : container.taskService.rerankTask({ taskId: task.id, position: 1 }, activeProject);
2112
2207
  dispatch({ type: "EXIT_REORDER", save: result.ok });
2113
2208
  dispatch({
2114
2209
  type: "FLASH",
@@ -2118,13 +2213,15 @@ function App({ container, initialProject }) {
2118
2213
  loadTasks();
2119
2214
  }, [container, state.tasks, state.selectedIndex, loadTasks]);
2120
2215
  const saveEpicReorder = useCallback2(() => {
2216
+ const activeProject = state.activeProject;
2217
+ if (!activeProject) return;
2121
2218
  const epics = state.epics;
2122
2219
  const idx = state.epicSelectedIndex;
2123
2220
  const epic = epics[idx];
2124
2221
  if (!epic) return;
2125
2222
  const prev = epics[idx - 1];
2126
2223
  const next = epics[idx + 1];
2127
- const result = prev ? container.taskService.rerankTask({ taskId: epic.id, afterId: prev.id }) : next ? container.taskService.rerankTask({ taskId: epic.id, beforeId: next.id }) : container.taskService.rerankTask({ taskId: epic.id, position: 1 });
2224
+ const result = prev ? container.taskService.rerankTask({ taskId: epic.id, afterId: prev.id }, activeProject) : next ? container.taskService.rerankTask({ taskId: epic.id, beforeId: next.id }, activeProject) : container.taskService.rerankTask({ taskId: epic.id, position: 1 }, activeProject);
2128
2225
  dispatch({ type: "EXIT_EPIC_REORDER", save: result.ok });
2129
2226
  dispatch({
2130
2227
  type: "FLASH",
@@ -2135,13 +2232,18 @@ function App({ container, initialProject }) {
2135
2232
  }, [container, state.epics, state.epicSelectedIndex, loadEpics]);
2136
2233
  const rerankSelectedToEdge = useCallback2(
2137
2234
  (kind, edge) => {
2235
+ const activeProject = state.activeProject;
2236
+ if (!activeProject) return;
2138
2237
  const isEpic = kind === "epic";
2139
2238
  const item = isEpic ? state.epics[state.epicSelectedIndex] : state.tasks[state.selectedIndex];
2140
2239
  if (!item) return;
2141
- const result = container.taskService.rerankTask({
2142
- taskId: item.id,
2143
- ...edge === "top" ? { top: true } : { bottom: true }
2144
- });
2240
+ const result = container.taskService.rerankTask(
2241
+ {
2242
+ taskId: item.id,
2243
+ ...edge === "top" ? { top: true } : { bottom: true }
2244
+ },
2245
+ activeProject
2246
+ );
2145
2247
  dispatch({
2146
2248
  type: isEpic ? "EXIT_EPIC_REORDER" : "EXIT_REORDER",
2147
2249
  save: result.ok
@@ -2207,7 +2309,7 @@ function App({ container, initialProject }) {
2207
2309
  }
2208
2310
  return void 0;
2209
2311
  }, [state.flash]);
2210
- useInput6((input, key) => {
2312
+ useInput7((input, key) => {
2211
2313
  if (state.confirmDelete) {
2212
2314
  if (input === "y") {
2213
2315
  const result = container.taskService.deleteTask(state.confirmDelete.id);
@@ -2232,7 +2334,7 @@ function App({ container, initialProject }) {
2232
2334
  dispatch({ type: "GO_BACK" });
2233
2335
  return;
2234
2336
  }
2235
- if (state.activeView === ViewType.TaskCreate || state.activeView === ViewType.TaskEdit || state.activeView === ViewType.ProjectSelector || state.activeView === ViewType.ProjectCreate || state.activeView === ViewType.EpicPicker) {
2337
+ if (state.activeView === ViewType.TaskCreate || state.activeView === ViewType.TaskEdit || state.activeView === ViewType.ProjectSelector || state.activeView === ViewType.ProjectCreate || state.activeView === ViewType.ProjectLink || state.activeView === ViewType.EpicPicker) {
2236
2338
  return;
2237
2339
  }
2238
2340
  if (state.activeView === ViewType.DependencyList && state.isAddingDep) {
@@ -2733,7 +2835,8 @@ ${state.selectedTask.additionalRequirements}`;
2733
2835
  loadTasks();
2734
2836
  loadEpics();
2735
2837
  } else {
2736
- const result = container.taskService.createTask(data, state.activeProject?.id);
2838
+ if (!state.activeProject) return;
2839
+ const result = container.taskService.createTask(data, state.activeProject);
2737
2840
  if (result.ok) {
2738
2841
  dispatch({ type: "FLASH", message: "Task created", level: "info" });
2739
2842
  dispatch({ type: "GO_BACK" });
@@ -2833,6 +2936,57 @@ ${state.selectedTask.additionalRequirements}`;
2833
2936
  const handleProjectFormCancel = useCallback2(() => {
2834
2937
  dispatch({ type: "GO_BACK" });
2835
2938
  }, []);
2939
+ const handleProjectLink = useCallback2((project) => {
2940
+ dispatch({ type: "SET_LINKING_PROJECT", project });
2941
+ dispatch({ type: "NAVIGATE_TO", view: ViewType.ProjectLink });
2942
+ }, []);
2943
+ const handleLinkSave = useCallback2(
2944
+ (remote) => {
2945
+ if (!state.linkingProject) return;
2946
+ const result = container.projectService.linkGitRemote(state.linkingProject.id, remote);
2947
+ if (result.ok) {
2948
+ logger.info(
2949
+ `TUI.linkGitRemote: linked project=${state.linkingProject.id} remote=${result.value.gitRemote}`
2950
+ );
2951
+ dispatch({
2952
+ type: "FLASH",
2953
+ message: `Linked to: ${result.value.gitRemote}`,
2954
+ level: "info"
2955
+ });
2956
+ dispatch({ type: "GO_BACK" });
2957
+ loadProjects();
2958
+ } else {
2959
+ logger.error("TUI.linkGitRemote: failed", result.error);
2960
+ dispatch({ type: "FLASH", message: result.error.message, level: "error" });
2961
+ }
2962
+ },
2963
+ [container, state.linkingProject, loadProjects]
2964
+ );
2965
+ const handleLinkUnlink = useCallback2(() => {
2966
+ if (!state.linkingProject) return;
2967
+ const result = container.projectService.unlinkGitRemote(state.linkingProject.id);
2968
+ if (result.ok) {
2969
+ logger.info(`TUI.unlinkGitRemote: unlinked project=${state.linkingProject.id}`);
2970
+ dispatch({ type: "FLASH", message: "Git remote unlinked", level: "info" });
2971
+ dispatch({ type: "GO_BACK" });
2972
+ loadProjects();
2973
+ } else {
2974
+ logger.error("TUI.unlinkGitRemote: failed", result.error);
2975
+ dispatch({ type: "FLASH", message: result.error.message, level: "error" });
2976
+ }
2977
+ }, [container, state.linkingProject, loadProjects]);
2978
+ const handleLinkDetect = useCallback2(() => {
2979
+ const result = detectGitRemote();
2980
+ if (result.ok && result.value) {
2981
+ dispatch({ type: "FLASH", message: `Detected: ${result.value}`, level: "info" });
2982
+ return result.value;
2983
+ }
2984
+ dispatch({ type: "FLASH", message: "No git remote detected in cwd", level: "warn" });
2985
+ return null;
2986
+ }, []);
2987
+ const handleLinkCancel = useCallback2(() => {
2988
+ dispatch({ type: "GO_BACK" });
2989
+ }, []);
2836
2990
  const handleProjectCancel = useCallback2(() => {
2837
2991
  dispatch({ type: "GO_BACK" });
2838
2992
  }, []);
@@ -2850,7 +3004,7 @@ ${state.selectedTask.additionalRequirements}`;
2850
3004
  }, [state.depBlockers, state.depRelated, state.depDuplicates]);
2851
3005
  const allProjectTasks = useMemo2(() => {
2852
3006
  if (!state.activeProject) return [];
2853
- const result = container.taskService.listTasks({ projectId: state.activeProject.id });
3007
+ const result = container.taskService.listTasks(state.activeProject, {});
2854
3008
  return result.ok ? result.value : [];
2855
3009
  }, [container, state.activeProject, state.tasks]);
2856
3010
  const previewTaskId = previewTask?.id ?? null;
@@ -2859,12 +3013,12 @@ ${state.selectedTask.additionalRequirements}`;
2859
3013
  loadDeps(previewTaskId);
2860
3014
  }
2861
3015
  }, [state.activeView, previewTaskId, loadDeps]);
2862
- return /* @__PURE__ */ jsxs14(Box16, { flexDirection: "column", height: stdout.rows, children: [
2863
- /* @__PURE__ */ jsx16(Header, { state }),
2864
- /* @__PURE__ */ jsxs14(Box16, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: [
2865
- state.confirmDelete && /* @__PURE__ */ jsx16(ConfirmDialog, { task: state.confirmDelete }),
2866
- !state.confirmDelete && state.activeView === ViewType.TaskList && /* @__PURE__ */ jsxs14(Box16, { flexDirection: "row", flexGrow: 1, children: [
2867
- /* @__PURE__ */ jsx16(
3016
+ return /* @__PURE__ */ jsxs15(Box17, { flexDirection: "column", height: stdout.rows, children: [
3017
+ /* @__PURE__ */ jsx17(Header, { state }),
3018
+ /* @__PURE__ */ jsxs15(Box17, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: [
3019
+ state.confirmDelete && /* @__PURE__ */ jsx17(ConfirmDialog, { task: state.confirmDelete }),
3020
+ !state.confirmDelete && state.activeView === ViewType.TaskList && /* @__PURE__ */ jsxs15(Box17, { flexDirection: "row", flexGrow: 1, children: [
3021
+ /* @__PURE__ */ jsx17(
2868
3022
  EpicPanel,
2869
3023
  {
2870
3024
  epics: state.epics,
@@ -2874,7 +3028,7 @@ ${state.selectedTask.additionalRequirements}`;
2874
3028
  isReordering: state.isEpicReordering
2875
3029
  }
2876
3030
  ),
2877
- /* @__PURE__ */ jsx16(Box16, { width: taskListWidth, children: /* @__PURE__ */ jsx16(
3031
+ /* @__PURE__ */ jsx17(Box17, { width: taskListWidth, children: /* @__PURE__ */ jsx17(
2878
3032
  TaskList,
2879
3033
  {
2880
3034
  tasks: state.tasks,
@@ -2895,7 +3049,7 @@ ${state.selectedTask.additionalRequirements}`;
2895
3049
  epicFilterActive: state.selectedEpicIds.size > 0
2896
3050
  }
2897
3051
  ) }),
2898
- /* @__PURE__ */ jsx16(Box16, { width: taskDetailWidth, children: previewTask ? /* @__PURE__ */ jsx16(
3052
+ /* @__PURE__ */ jsx17(Box17, { width: taskDetailWidth, children: previewTask ? /* @__PURE__ */ jsx17(
2899
3053
  TaskDetail,
2900
3054
  {
2901
3055
  task: previewTask,
@@ -2906,24 +3060,24 @@ ${state.selectedTask.additionalRequirements}`;
2906
3060
  isFocused: state.focusedPanel === "detail",
2907
3061
  scrollOffset: state.detailScrollOffset
2908
3062
  }
2909
- ) : /* @__PURE__ */ jsxs14(
2910
- Box16,
3063
+ ) : /* @__PURE__ */ jsxs15(
3064
+ Box17,
2911
3065
  {
2912
3066
  flexDirection: "column",
2913
3067
  flexGrow: 1,
2914
3068
  borderStyle: "bold",
2915
3069
  borderColor: theme.border,
2916
3070
  children: [
2917
- /* @__PURE__ */ jsx16(Box16, { children: /* @__PURE__ */ jsxs14(Text16, { color: theme.title, bold: true, children: [
3071
+ /* @__PURE__ */ jsx17(Box17, { children: /* @__PURE__ */ jsxs15(Text17, { color: theme.title, bold: true, children: [
2918
3072
  " ",
2919
3073
  "detail"
2920
3074
  ] }) }),
2921
- /* @__PURE__ */ jsx16(Box16, { flexGrow: 1, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "No task selected" }) })
3075
+ /* @__PURE__ */ jsx17(Box17, { flexGrow: 1, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "No task selected" }) })
2922
3076
  ]
2923
3077
  }
2924
3078
  ) })
2925
3079
  ] }),
2926
- !state.confirmDelete && state.activeView === ViewType.TaskDetail && state.selectedTask && /* @__PURE__ */ jsx16(
3080
+ !state.confirmDelete && state.activeView === ViewType.TaskDetail && state.selectedTask && /* @__PURE__ */ jsx17(
2927
3081
  TaskDetail,
2928
3082
  {
2929
3083
  task: state.selectedTask,
@@ -2934,7 +3088,7 @@ ${state.selectedTask.additionalRequirements}`;
2934
3088
  scrollOffset: state.detailScrollOffset
2935
3089
  }
2936
3090
  ),
2937
- !state.confirmDelete && state.activeView === ViewType.DependencyList && state.selectedTask && /* @__PURE__ */ jsx16(
3091
+ !state.confirmDelete && state.activeView === ViewType.DependencyList && state.selectedTask && /* @__PURE__ */ jsx17(
2938
3092
  DependencyList,
2939
3093
  {
2940
3094
  task: state.selectedTask,
@@ -2947,7 +3101,7 @@ ${state.selectedTask.additionalRequirements}`;
2947
3101
  addDepInput: state.addDepInput
2948
3102
  }
2949
3103
  ),
2950
- !state.confirmDelete && (state.activeView === ViewType.TaskCreate || state.activeView === ViewType.TaskEdit) && /* @__PURE__ */ jsx16(
3104
+ !state.confirmDelete && (state.activeView === ViewType.TaskCreate || state.activeView === ViewType.TaskEdit) && /* @__PURE__ */ jsx17(
2951
3105
  TaskForm,
2952
3106
  {
2953
3107
  editingTask: state.activeView === ViewType.TaskEdit ? state.selectedTask : null,
@@ -2957,7 +3111,7 @@ ${state.selectedTask.additionalRequirements}`;
2957
3111
  onCancel: handleFormCancel
2958
3112
  }
2959
3113
  ),
2960
- !state.confirmDelete && state.activeView === ViewType.EpicPicker && state.selectedTask && /* @__PURE__ */ jsx16(
3114
+ !state.confirmDelete && state.activeView === ViewType.EpicPicker && state.selectedTask && /* @__PURE__ */ jsx17(
2961
3115
  EpicPicker,
2962
3116
  {
2963
3117
  epics: state.epics,
@@ -2966,7 +3120,7 @@ ${state.selectedTask.additionalRequirements}`;
2966
3120
  onCancel: handleEpicPickerCancel
2967
3121
  }
2968
3122
  ),
2969
- !state.confirmDelete && state.activeView === ViewType.ProjectSelector && /* @__PURE__ */ jsx16(
3123
+ !state.confirmDelete && state.activeView === ViewType.ProjectSelector && /* @__PURE__ */ jsx17(
2970
3124
  ProjectSelector,
2971
3125
  {
2972
3126
  projects: state.projects,
@@ -2974,21 +3128,32 @@ ${state.selectedTask.additionalRequirements}`;
2974
3128
  onSelect: handleProjectSelect,
2975
3129
  onCreate: handleProjectCreate,
2976
3130
  onSetDefault: handleSetDefault,
3131
+ onLink: handleProjectLink,
2977
3132
  onCancel: handleProjectCancel
2978
3133
  }
2979
3134
  ),
2980
- !state.confirmDelete && state.activeView === ViewType.ProjectCreate && /* @__PURE__ */ jsx16(ProjectForm, { onSave: handleProjectFormSave, onCancel: handleProjectFormCancel }),
2981
- !state.confirmDelete && state.activeView === ViewType.Help && /* @__PURE__ */ jsx16(HelpOverlay, {})
3135
+ !state.confirmDelete && state.activeView === ViewType.ProjectCreate && /* @__PURE__ */ jsx17(ProjectForm, { onSave: handleProjectFormSave, onCancel: handleProjectFormCancel }),
3136
+ !state.confirmDelete && state.activeView === ViewType.ProjectLink && state.linkingProject && /* @__PURE__ */ jsx17(
3137
+ ProjectLinkForm,
3138
+ {
3139
+ project: state.linkingProject,
3140
+ onSave: handleLinkSave,
3141
+ onUnlink: handleLinkUnlink,
3142
+ onDetect: handleLinkDetect,
3143
+ onCancel: handleLinkCancel
3144
+ }
3145
+ ),
3146
+ !state.confirmDelete && state.activeView === ViewType.Help && /* @__PURE__ */ jsx17(HelpOverlay, {})
2982
3147
  ] }),
2983
- /* @__PURE__ */ jsx16(Crumbs, { breadcrumbs: state.breadcrumbs }),
2984
- state.flash && /* @__PURE__ */ jsx16(FlashMessage, { message: state.flash.message, level: state.flash.level })
3148
+ /* @__PURE__ */ jsx17(Crumbs, { breadcrumbs: state.breadcrumbs }),
3149
+ state.flash && /* @__PURE__ */ jsx17(FlashMessage, { message: state.flash.message, level: state.flash.level })
2985
3150
  ] });
2986
3151
  }
2987
3152
 
2988
3153
  // src/tui/index.tsx
2989
- import { jsx as jsx17 } from "react/jsx-runtime";
3154
+ import { jsx as jsx18 } from "react/jsx-runtime";
2990
3155
  async function launchTUI(container, initialProject) {
2991
- const instance = render(/* @__PURE__ */ jsx17(App, { container, initialProject }), {
3156
+ const instance = render(/* @__PURE__ */ jsx18(App, { container, initialProject }), {
2992
3157
  exitOnCtrlC: true
2993
3158
  });
2994
3159
  await instance.waitUntilExit();
@@ -2996,4 +3161,4 @@ async function launchTUI(container, initialProject) {
2996
3161
  export {
2997
3162
  launchTUI
2998
3163
  };
2999
- //# sourceMappingURL=tui-JEP3F4JS.js.map
3164
+ //# sourceMappingURL=tui-IXZGQMWN.js.map