@tomkapa/tayto 0.6.0 → 0.8.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,21 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ APP_VERSION,
3
4
  DependencyType,
4
5
  TaskLevel,
5
6
  TaskStatus,
6
7
  TaskType,
7
8
  UIDependencyType,
9
+ define_CHANGELOG_ENTRIES_default,
8
10
  detectGitRemote,
11
+ isNewerVersion,
9
12
  isTerminalStatus,
10
13
  logger
11
- } from "./chunk-74Q55TOV.js";
14
+ } from "./chunk-XD24XQNF.js";
12
15
 
13
16
  // src/tui/index.tsx
14
17
  import { render } from "ink";
15
18
 
16
19
  // src/tui/components/App.tsx
17
- import { useReducer, useEffect as useEffect2, useCallback as useCallback2, useMemo as useMemo2, useState as useState7 } from "react";
18
- import { Box as Box18, Text as Text18, useInput as useInput7, useApp, useStdout as useStdout4 } from "ink";
20
+ import { useReducer, useEffect as useEffect5, useCallback as useCallback2, useMemo as useMemo3, useState as useState8, useRef as useRef5 } from "react";
21
+ import { Box as Box21, Text as Text21, useInput as useInput7, useApp, useStdout as useStdout4 } from "ink";
19
22
 
20
23
  // src/tui/types.ts
21
24
  var ViewType = {
@@ -25,6 +28,7 @@ var ViewType = {
25
28
  TaskEdit: "task-edit",
26
29
  ProjectSelector: "project-selector",
27
30
  ProjectCreate: "project-create",
31
+ ProjectEdit: "project-edit",
28
32
  DependencyList: "dependency-list",
29
33
  EpicPicker: "epic-picker",
30
34
  ProjectLink: "project-link",
@@ -177,8 +181,13 @@ var initialState = {
177
181
  epicSelectedIndex: 0,
178
182
  selectedEpicIds: /* @__PURE__ */ new Set(),
179
183
  linkingProject: null,
184
+ editingProject: null,
180
185
  isEpicReordering: false,
181
- epicReorderSnapshot: null
186
+ epicReorderSnapshot: null,
187
+ detectedGitRemote: null,
188
+ changelogEntries: null,
189
+ changelogIndex: 0,
190
+ changelogDialogOpen: false
182
191
  };
183
192
  function appReducer(state, action) {
184
193
  switch (action.type) {
@@ -200,6 +209,8 @@ function appReducer(state, action) {
200
209
  isSearchActive: false,
201
210
  formData: null,
202
211
  linkingProject: null,
212
+ editingProject: null,
213
+ detectedGitRemote: null,
203
214
  focusedPanel: "list"
204
215
  };
205
216
  }
@@ -384,11 +395,29 @@ function appReducer(state, action) {
384
395
  }
385
396
  case "SET_LINKING_PROJECT":
386
397
  return { ...state, linkingProject: action.project };
398
+ case "SET_EDITING_PROJECT":
399
+ return { ...state, editingProject: action.project };
400
+ case "SET_DETECTED_GIT_REMOTE":
401
+ return { ...state, detectedGitRemote: action.remote };
402
+ case "SET_CHANGELOG":
403
+ return { ...state, changelogEntries: action.entries, changelogIndex: 0 };
404
+ case "CHANGELOG_NAVIGATE": {
405
+ if (!state.changelogEntries) return state;
406
+ const max = Math.max(0, state.changelogEntries.length - 1);
407
+ const newIdx = action.direction === "up" ? Math.max(0, state.changelogIndex - 1) : Math.min(max, state.changelogIndex + 1);
408
+ return { ...state, changelogIndex: newIdx };
409
+ }
410
+ case "DISMISS_CHANGELOG":
411
+ return { ...state, changelogEntries: null, changelogIndex: 0, changelogDialogOpen: false };
412
+ case "OPEN_CHANGELOG_DIALOG":
413
+ return { ...state, changelogDialogOpen: true, changelogIndex: 0 };
414
+ case "CLOSE_CHANGELOG_DIALOG":
415
+ return { ...state, changelogDialogOpen: false };
387
416
  }
388
417
  }
389
418
 
390
419
  // src/tui/components/Header.tsx
391
- import { Box as Box2, Text as Text2 } from "ink";
420
+ import { Box as Box3, Text as Text3 } from "ink";
392
421
 
393
422
  // src/tui/components/Logo.tsx
394
423
  import React from "react";
@@ -423,10 +452,71 @@ var Logo = React.memo(function Logo2() {
423
452
  return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: lines });
424
453
  });
425
454
 
426
- // src/tui/components/Header.tsx
455
+ // src/tui/components/ChangelogTicker.tsx
456
+ import { useState, useEffect, useMemo } from "react";
457
+ import { Box as Box2, Text as Text2 } from "ink";
427
458
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
459
+ var INTERVAL_MS = 4e3;
460
+ var WIDTH = 98;
461
+ function ChangelogTicker({ entries }) {
462
+ const items = useMemo(
463
+ () => entries.flatMap(
464
+ (entry) => entry.sections.flatMap(
465
+ (section) => section.items.map((item) => ({
466
+ version: entry.version,
467
+ section: section.heading,
468
+ text: item
469
+ }))
470
+ )
471
+ ),
472
+ [entries]
473
+ );
474
+ const [index, setIndex] = useState(0);
475
+ useEffect(() => {
476
+ if (items.length <= 1) return;
477
+ const timer = setInterval(() => {
478
+ setIndex((i) => (i + 1) % items.length);
479
+ }, INTERVAL_MS);
480
+ return () => {
481
+ clearInterval(timer);
482
+ };
483
+ }, [items.length]);
484
+ const current = items[index % Math.max(1, items.length)];
485
+ if (!current) return null;
486
+ return /* @__PURE__ */ jsxs(
487
+ Box2,
488
+ {
489
+ borderStyle: "bold",
490
+ borderColor: theme.border,
491
+ paddingX: 2,
492
+ width: WIDTH,
493
+ flexShrink: 0,
494
+ flexDirection: "column",
495
+ children: [
496
+ /* @__PURE__ */ jsxs(Box2, { gap: 1, children: [
497
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dialog.label, bold: true, children: `v${current.version}` }),
498
+ /* @__PURE__ */ jsx2(Text2, { color: theme.border, children: "\xB7" }),
499
+ /* @__PURE__ */ jsx2(Text2, { color: theme.table.headerFg, children: current.section })
500
+ ] }),
501
+ /* @__PURE__ */ jsxs(Box2, { flexDirection: "row", gap: 1, children: [
502
+ /* @__PURE__ */ jsx2(Box2, { flexGrow: 1, children: /* @__PURE__ */ jsx2(Text2, { color: theme.fg, wrap: "truncate", children: current.text }) }),
503
+ /* @__PURE__ */ jsx2(Box2, { flexShrink: 0, children: /* @__PURE__ */ jsx2(Text2, { color: theme.menu.key, bold: true, children: "<W>" }) })
504
+ ] })
505
+ ]
506
+ }
507
+ );
508
+ }
509
+
510
+ // src/tui/components/Header.tsx
511
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
428
512
  function getKeyHints(state) {
429
513
  const { activeView, isSearchActive, isReordering, isEpicReordering, isAddingDep, focusedPanel } = state;
514
+ if (state.changelogDialogOpen) {
515
+ return [
516
+ { key: "\u2191/\u2193", desc: "navigate" },
517
+ { key: "esc", desc: "close" }
518
+ ];
519
+ }
430
520
  if (isAddingDep) {
431
521
  return [
432
522
  { key: "type", desc: "search" },
@@ -513,7 +603,8 @@ function getKeyHints(state) {
513
603
  }
514
604
  if (activeView === ViewType.TaskCreate || activeView === ViewType.TaskEdit) {
515
605
  return [
516
- { key: "tab", desc: "next field" },
606
+ { key: "\u2191\u2193/tab", desc: "navigate" },
607
+ { key: "\u2190\u2192", desc: "cursor" },
517
608
  { key: "ctrl+s", desc: "save" },
518
609
  { key: "esc", desc: "cancel" }
519
610
  ];
@@ -522,6 +613,7 @@ function getKeyHints(state) {
522
613
  return [
523
614
  { key: "j/k", desc: "nav" },
524
615
  { key: "enter", desc: "select" },
616
+ { key: "e", desc: "edit" },
525
617
  { key: "c", desc: "create" },
526
618
  { key: "l", desc: "link" },
527
619
  { key: "d", desc: "default" },
@@ -555,40 +647,47 @@ function Header({ state, latestVersion }) {
555
647
  const hints = getKeyHints(state);
556
648
  const hintCols = hints.length <= 7 ? 2 : hints.length <= 12 ? 3 : 4;
557
649
  const columns = chunkHints(hints, hintCols);
558
- return /* @__PURE__ */ jsxs(Box2, { flexDirection: "row", gap: 1, children: [
559
- /* @__PURE__ */ jsx2(Box2, { flexShrink: 0, children: /* @__PURE__ */ jsx2(Logo, {}) }),
560
- /* @__PURE__ */ jsxs(Box2, { flexDirection: "column", justifyContent: "center", flexShrink: 0, paddingLeft: 1, children: [
561
- /* @__PURE__ */ jsx2(Text2, { color: theme.logo, bold: true, children: "tayto" }),
562
- /* @__PURE__ */ jsxs(Box2, { gap: 1, children: [
563
- /* @__PURE__ */ jsx2(Text2, { color: theme.fg, children: "Project:" }),
564
- /* @__PURE__ */ jsx2(Text2, { color: theme.titleCounter, bold: true, children: projectName })
650
+ const hasTicker = state.changelogEntries !== null;
651
+ return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "row", gap: 1, children: [
652
+ /* @__PURE__ */ jsx3(Box3, { flexShrink: 0, children: /* @__PURE__ */ jsx3(Logo, {}) }),
653
+ /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", justifyContent: "center", flexShrink: 0, paddingLeft: 1, children: [
654
+ /* @__PURE__ */ jsx3(Text3, { color: theme.logo, bold: true, children: "tayto" }),
655
+ /* @__PURE__ */ jsxs2(Box3, { gap: 1, children: [
656
+ /* @__PURE__ */ jsx3(Text3, { color: theme.fg, children: "Project:" }),
657
+ /* @__PURE__ */ jsx3(Text3, { color: theme.titleCounter, bold: true, children: projectName })
565
658
  ] }),
566
- /* @__PURE__ */ jsxs(Box2, { gap: 1, children: [
567
- /* @__PURE__ */ jsx2(Text2, { color: theme.fg, children: "Tasks:" }),
568
- /* @__PURE__ */ jsx2(Text2, { color: theme.titleCounter, bold: true, children: taskCount })
659
+ /* @__PURE__ */ jsxs2(Box3, { gap: 1, children: [
660
+ /* @__PURE__ */ jsx3(Text3, { color: theme.fg, children: "Tasks:" }),
661
+ /* @__PURE__ */ jsx3(Text3, { color: theme.titleCounter, bold: true, children: taskCount })
569
662
  ] }),
570
- latestVersion && /* @__PURE__ */ jsxs(Box2, { gap: 1, children: [
571
- /* @__PURE__ */ jsxs(Text2, { color: theme.flash.warn, children: [
663
+ latestVersion && /* @__PURE__ */ jsxs2(Box3, { gap: 1, children: [
664
+ /* @__PURE__ */ jsxs2(Text3, { color: theme.flash.warn, children: [
572
665
  "Update ",
573
666
  latestVersion
574
667
  ] }),
575
- /* @__PURE__ */ jsx2(Text2, { color: theme.fg, children: "\u2014 tayto upgrade" })
668
+ /* @__PURE__ */ jsx3(Text3, { color: theme.fg, children: "\u2014 tayto upgrade" })
576
669
  ] })
577
670
  ] }),
578
- /* @__PURE__ */ jsx2(Box2, { flexGrow: 1, justifyContent: "flex-end", children: /* @__PURE__ */ jsx2(Box2, { flexDirection: "row", gap: 2, children: columns.map((col, ci) => /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: col.map((h) => /* @__PURE__ */ jsxs(Box2, { children: [
579
- /* @__PURE__ */ jsxs(Text2, { color: theme.menu.key, bold: true, children: [
580
- "<",
581
- h.key,
582
- ">"
583
- ] }),
584
- /* @__PURE__ */ jsx2(Text2, { color: theme.menu.desc, children: h.desc })
585
- ] }, h.key)) }, col[0]?.key ?? String(ci))) }) })
671
+ hasTicker && state.changelogEntries ? /* @__PURE__ */ jsxs2(Fragment, { children: [
672
+ /* @__PURE__ */ jsx3(Box3, { flexGrow: 1, justifyContent: "center", children: /* @__PURE__ */ jsx3(ChangelogTicker, { entries: state.changelogEntries }) }),
673
+ /* @__PURE__ */ jsx3(Box3, { flexShrink: 0, children: /* @__PURE__ */ jsx3(HintGrid, { columns }) })
674
+ ] }) : /* @__PURE__ */ jsx3(Box3, { flexGrow: 1, justifyContent: "flex-end", children: /* @__PURE__ */ jsx3(HintGrid, { columns }) })
586
675
  ] });
587
676
  }
677
+ function HintGrid({ columns }) {
678
+ return /* @__PURE__ */ jsx3(Box3, { flexDirection: "row", gap: 2, children: columns.map((col, ci) => /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: col.map((h) => /* @__PURE__ */ jsxs2(Box3, { children: [
679
+ /* @__PURE__ */ jsxs2(Text3, { color: theme.menu.key, bold: true, children: [
680
+ "<",
681
+ h.key,
682
+ ">"
683
+ ] }),
684
+ /* @__PURE__ */ jsx3(Text3, { color: theme.menu.desc, children: h.desc })
685
+ ] }, h.key)) }, col[0]?.key ?? String(ci))) });
686
+ }
588
687
 
589
688
  // src/tui/components/Crumbs.tsx
590
- import { Box as Box3, Text as Text3 } from "ink";
591
- import { jsx as jsx3 } from "react/jsx-runtime";
689
+ import { Box as Box4, Text as Text4 } from "ink";
690
+ import { jsx as jsx4 } from "react/jsx-runtime";
592
691
  var VIEW_LABELS = {
593
692
  "task-list": "tasks",
594
693
  "task-detail": "detail",
@@ -598,11 +697,11 @@ var VIEW_LABELS = {
598
697
  help: "help"
599
698
  };
600
699
  function Crumbs({ breadcrumbs }) {
601
- return /* @__PURE__ */ jsx3(Box3, { flexDirection: "row", gap: 0, width: "100%", children: breadcrumbs.map((crumb, i) => {
700
+ return /* @__PURE__ */ jsx4(Box4, { flexDirection: "row", gap: 0, width: "100%", children: breadcrumbs.map((crumb, i) => {
602
701
  const isActive = i === breadcrumbs.length - 1;
603
702
  const label = ` ${VIEW_LABELS[crumb] ?? crumb} `;
604
- return /* @__PURE__ */ jsx3(
605
- Text3,
703
+ return /* @__PURE__ */ jsx4(
704
+ Text4,
606
705
  {
607
706
  color: theme.crumb.fg,
608
707
  backgroundColor: isActive ? theme.crumb.activeBg : theme.crumb.bg,
@@ -615,20 +714,20 @@ function Crumbs({ breadcrumbs }) {
615
714
  }
616
715
 
617
716
  // src/tui/components/FlashMessage.tsx
618
- import { Box as Box4, Text as Text4 } from "ink";
619
- import { jsx as jsx4 } from "react/jsx-runtime";
717
+ import { Box as Box5, Text as Text5 } from "ink";
718
+ import { jsx as jsx5 } from "react/jsx-runtime";
620
719
  var LEVEL_COLOR = {
621
720
  info: theme.flash.info,
622
721
  warn: theme.flash.warn,
623
722
  error: theme.flash.error
624
723
  };
625
724
  function FlashMessage({ message, level }) {
626
- return /* @__PURE__ */ jsx4(Box4, { justifyContent: "center", width: "100%", children: /* @__PURE__ */ jsx4(Text4, { color: LEVEL_COLOR[level], bold: level === "error", children: message }) });
725
+ return /* @__PURE__ */ jsx5(Box5, { justifyContent: "center", width: "100%", children: /* @__PURE__ */ jsx5(Text5, { color: LEVEL_COLOR[level], bold: level === "error", children: message }) });
627
726
  }
628
727
 
629
728
  // src/tui/components/TaskList.tsx
630
- import { Box as Box5, Text as Text5 } from "ink";
631
- import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
729
+ import { Box as Box6, Text as Text6 } from "ink";
730
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
632
731
  var COL = {
633
732
  rank: 5,
634
733
  type: 12,
@@ -656,50 +755,50 @@ function TaskList({
656
755
  if (filter.type) filterParts.push(`type:${filter.type}`);
657
756
  if (filter.search) filterParts.push(filter.search);
658
757
  const filterText = filterParts.length > 0 ? filterParts.join(" ") : "";
659
- return /* @__PURE__ */ jsxs2(
660
- Box5,
758
+ return /* @__PURE__ */ jsxs3(
759
+ Box6,
661
760
  {
662
761
  flexDirection: "column",
663
762
  flexGrow: 1,
664
763
  borderStyle: "bold",
665
764
  borderColor: isFocused ? theme.borderFocus : theme.border,
666
765
  children: [
667
- /* @__PURE__ */ jsxs2(Box5, { children: [
668
- /* @__PURE__ */ jsxs2(Text5, { color: theme.title, bold: true, children: [
766
+ /* @__PURE__ */ jsxs3(Box6, { children: [
767
+ /* @__PURE__ */ jsxs3(Text6, { color: theme.title, bold: true, children: [
669
768
  " ",
670
769
  "tasks"
671
770
  ] }),
672
- /* @__PURE__ */ jsx5(Text5, { color: theme.fg, children: "(" }),
673
- /* @__PURE__ */ jsx5(Text5, { color: theme.titleHighlight, bold: true, children: activeProjectName }),
674
- /* @__PURE__ */ jsx5(Text5, { color: theme.fg, children: ")" }),
675
- /* @__PURE__ */ jsxs2(Text5, { color: theme.titleCounter, bold: true, children: [
771
+ /* @__PURE__ */ jsx6(Text6, { color: theme.fg, children: "(" }),
772
+ /* @__PURE__ */ jsx6(Text6, { color: theme.titleHighlight, bold: true, children: activeProjectName }),
773
+ /* @__PURE__ */ jsx6(Text6, { color: theme.fg, children: ")" }),
774
+ /* @__PURE__ */ jsxs3(Text6, { color: theme.titleCounter, bold: true, children: [
676
775
  "[",
677
776
  tasks.length,
678
777
  "]"
679
778
  ] }),
680
- isReordering && /* @__PURE__ */ jsxs2(Text5, { color: theme.flash.warn, bold: true, children: [
779
+ isReordering && /* @__PURE__ */ jsxs3(Text6, { color: theme.flash.warn, bold: true, children: [
681
780
  " ",
682
781
  "REORDER"
683
782
  ] }),
684
- epicFilterActive && /* @__PURE__ */ jsx5(Text5, { color: theme.titleHighlight, children: " [epic]" }),
685
- filterText && /* @__PURE__ */ jsxs2(Text5, { color: theme.titleFilter, children: [
783
+ epicFilterActive && /* @__PURE__ */ jsx6(Text6, { color: theme.titleHighlight, children: " [epic]" }),
784
+ filterText && /* @__PURE__ */ jsxs3(Text6, { color: theme.titleFilter, children: [
686
785
  " /",
687
786
  filterText
688
787
  ] })
689
788
  ] }),
690
- isSearchActive && /* @__PURE__ */ jsxs2(Box5, { borderStyle: "round", borderColor: theme.prompt, paddingX: 1, children: [
691
- /* @__PURE__ */ jsx5(Text5, { color: theme.prompt, children: "/" }),
692
- /* @__PURE__ */ jsx5(Text5, { color: theme.prompt, children: searchQuery }),
693
- /* @__PURE__ */ jsx5(Text5, { color: theme.promptSuggest, children: "_" })
789
+ isSearchActive && /* @__PURE__ */ jsxs3(Box6, { borderStyle: "round", borderColor: theme.prompt, paddingX: 1, children: [
790
+ /* @__PURE__ */ jsx6(Text6, { color: theme.prompt, children: "/" }),
791
+ /* @__PURE__ */ jsx6(Text6, { color: theme.prompt, children: searchQuery }),
792
+ /* @__PURE__ */ jsx6(Text6, { color: theme.promptSuggest, children: "_" })
694
793
  ] }),
695
- /* @__PURE__ */ jsxs2(Box5, { children: [
696
- /* @__PURE__ */ jsx5(Text5, { color: theme.table.headerFg, bold: true, children: " " }),
697
- /* @__PURE__ */ jsx5(Text5, { color: theme.table.headerFg, bold: true, children: "#".padEnd(COL.rank) }),
698
- /* @__PURE__ */ jsx5(Text5, { color: theme.table.headerFg, bold: true, children: "TYPE".padEnd(COL.type) }),
699
- /* @__PURE__ */ jsx5(Text5, { color: theme.table.headerFg, bold: true, children: "STATUS".padEnd(COL.status) }),
700
- /* @__PURE__ */ jsx5(Text5, { color: theme.table.headerFg, bold: true, children: "NAME" })
794
+ /* @__PURE__ */ jsxs3(Box6, { children: [
795
+ /* @__PURE__ */ jsx6(Text6, { color: theme.table.headerFg, bold: true, children: " " }),
796
+ /* @__PURE__ */ jsx6(Text6, { color: theme.table.headerFg, bold: true, children: "#".padEnd(COL.rank) }),
797
+ /* @__PURE__ */ jsx6(Text6, { color: theme.table.headerFg, bold: true, children: "TYPE".padEnd(COL.type) }),
798
+ /* @__PURE__ */ jsx6(Text6, { color: theme.table.headerFg, bold: true, children: "STATUS".padEnd(COL.status) }),
799
+ /* @__PURE__ */ jsx6(Text6, { color: theme.table.headerFg, bold: true, children: "NAME" })
701
800
  ] }),
702
- /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: tasks.length === 0 ? /* @__PURE__ */ jsx5(Box5, { paddingX: 2, paddingY: 1, children: /* @__PURE__ */ jsx5(Text5, { color: theme.fg, children: "No tasks found. Press 'c' to create one." }) }) : visibleTasks.map((task, i) => {
801
+ /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: tasks.length === 0 ? /* @__PURE__ */ jsx6(Box6, { paddingX: 2, paddingY: 1, children: /* @__PURE__ */ jsx6(Text6, { color: theme.fg, children: "No tasks found. Press 'c' to create one." }) }) : visibleTasks.map((task, i) => {
703
802
  const actualIndex = viewStart + i;
704
803
  const isSelected = actualIndex === selectedIndex;
705
804
  const isNonTerminalBlocker = nonTerminalBlockerIds.has(task.id);
@@ -709,7 +808,7 @@ function TaskList({
709
808
  const depMarker = isNonTerminalBlocker ? "\u25B2 " : isNonTerminalDependent ? "\u25BC " : " ";
710
809
  if (isSelected) {
711
810
  const cursorBg = isReordering ? theme.flash.warn : isSelectedBlocked ? theme.table.blockedCursorBg : theme.table.cursorBg;
712
- return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsxs2(Text5, { backgroundColor: cursorBg, color: theme.table.cursorFg, bold: true, children: [
811
+ return /* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsxs3(Text6, { backgroundColor: cursorBg, color: theme.table.cursorFg, bold: true, children: [
713
812
  isReordering ? "~ " : "> ",
714
813
  rowNum.padEnd(COL.rank),
715
814
  task.type.padEnd(COL.type),
@@ -718,7 +817,7 @@ function TaskList({
718
817
  ] }) }, task.id);
719
818
  }
720
819
  if (isNonTerminalBlocker || isNonTerminalDependent) {
721
- return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsxs2(Text5, { backgroundColor: theme.table.depHighlightBg, color: theme.table.fg, bold: true, children: [
820
+ return /* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsxs3(Text6, { backgroundColor: theme.table.depHighlightBg, color: theme.table.fg, bold: true, children: [
722
821
  depMarker,
723
822
  rowNum.padEnd(COL.rank),
724
823
  task.type.padEnd(COL.type),
@@ -726,15 +825,15 @@ function TaskList({
726
825
  task.name
727
826
  ] }) }, task.id);
728
827
  }
729
- return /* @__PURE__ */ jsxs2(Box5, { children: [
730
- /* @__PURE__ */ jsx5(Text5, { children: " " }),
731
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: rowNum.padEnd(COL.rank) }),
732
- /* @__PURE__ */ jsx5(Text5, { color: TYPE_COLOR[task.type] ?? rowColor, children: task.type.padEnd(COL.type) }),
733
- /* @__PURE__ */ jsx5(Text5, { color: STATUS_COLOR[task.status] ?? rowColor, children: task.status.padEnd(COL.status) }),
734
- /* @__PURE__ */ jsx5(Text5, { color: rowColor, children: task.name })
828
+ return /* @__PURE__ */ jsxs3(Box6, { children: [
829
+ /* @__PURE__ */ jsx6(Text6, { children: " " }),
830
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: rowNum.padEnd(COL.rank) }),
831
+ /* @__PURE__ */ jsx6(Text6, { color: TYPE_COLOR[task.type] ?? rowColor, children: task.type.padEnd(COL.type) }),
832
+ /* @__PURE__ */ jsx6(Text6, { color: STATUS_COLOR[task.status] ?? rowColor, children: task.status.padEnd(COL.status) }),
833
+ /* @__PURE__ */ jsx6(Text6, { color: rowColor, children: task.name })
735
834
  ] }, task.id);
736
835
  }) }),
737
- tasks.length > PAGE_SIZE && /* @__PURE__ */ jsx5(Box5, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs2(Text5, { dimColor: true, children: [
836
+ tasks.length > PAGE_SIZE && /* @__PURE__ */ jsx6(Box6, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs3(Text6, { dimColor: true, children: [
738
837
  "[",
739
838
  viewStart + 1,
740
839
  "-",
@@ -749,12 +848,12 @@ function TaskList({
749
848
  }
750
849
 
751
850
  // src/tui/components/TaskDetail.tsx
752
- import { useMemo } from "react";
753
- import { Box as Box7, Text as Text7, useStdout } from "ink";
851
+ import { useMemo as useMemo2 } from "react";
852
+ import { Box as Box8, Text as Text8, useStdout } from "ink";
754
853
  import chalk2 from "chalk";
755
854
 
756
855
  // src/tui/components/Markdown.tsx
757
- import { Text as Text6, Box as Box6 } from "ink";
856
+ import { Text as Text7, Box as Box7 } from "ink";
758
857
  import { Marked } from "marked";
759
858
  import { markedTerminal } from "marked-terminal";
760
859
  import chalk from "chalk";
@@ -790,7 +889,7 @@ mermaid.initialize({ startOnLoad: true, theme: 'dark' });
790
889
  }
791
890
 
792
891
  // src/tui/components/Markdown.tsx
793
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
892
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
794
893
  function extractMermaidBlocks(content) {
795
894
  const lexer = new Marked();
796
895
  const tokens = lexer.lexer(content);
@@ -851,12 +950,12 @@ function renderMarkdown(content) {
851
950
  function MermaidHint({ content }) {
852
951
  const blocks = extractMermaidBlocks(content);
853
952
  if (blocks.length === 0) return null;
854
- return /* @__PURE__ */ jsxs3(Box6, { children: [
855
- /* @__PURE__ */ jsxs3(Text6, { color: theme.menu.key, bold: true, children: [
953
+ return /* @__PURE__ */ jsxs4(Box7, { children: [
954
+ /* @__PURE__ */ jsxs4(Text7, { color: theme.menu.key, bold: true, children: [
856
955
  "<m>",
857
956
  " "
858
957
  ] }),
859
- /* @__PURE__ */ jsxs3(Text6, { dimColor: true, children: [
958
+ /* @__PURE__ */ jsxs4(Text7, { dimColor: true, children: [
860
959
  "Open ",
861
960
  blocks.length,
862
961
  " mermaid diagram",
@@ -874,7 +973,7 @@ function openAllMermaidDiagrams(content) {
874
973
  }
875
974
 
876
975
  // src/tui/components/TaskDetail.tsx
877
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
976
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
878
977
  var PAD = " ";
879
978
  function field(label, value) {
880
979
  return PAD + chalk2.hex(theme.yaml.key).bold(label) + chalk2.hex(theme.yaml.colon)(": ") + chalk2.hex(theme.yaml.value)(value);
@@ -950,44 +1049,44 @@ function TaskDetail({
950
1049
  const allText = `${task.description}
951
1050
  ${task.technicalNotes}
952
1051
  ${task.additionalRequirements}`;
953
- const contentLines = useMemo(
1052
+ const contentLines = useMemo2(
954
1053
  () => buildContentLines(task, blockers, dependents, related, duplicates),
955
1054
  [task, blockers, dependents, related, duplicates]
956
1055
  );
957
1056
  const viewportHeight = Math.max(1, (stdout.rows > 0 ? stdout.rows : 24) - CHROME_LINES);
958
1057
  const visibleLines = contentLines.slice(scrollOffset, scrollOffset + viewportHeight);
959
- return /* @__PURE__ */ jsxs4(
960
- Box7,
1058
+ return /* @__PURE__ */ jsxs5(
1059
+ Box8,
961
1060
  {
962
1061
  flexDirection: "column",
963
1062
  flexGrow: 1,
964
1063
  borderStyle: "bold",
965
1064
  borderColor: isFocused ? theme.borderFocus : theme.border,
966
1065
  children: [
967
- /* @__PURE__ */ jsxs4(Box7, { gap: 0, children: [
968
- /* @__PURE__ */ jsxs4(Text7, { color: theme.title, bold: true, children: [
1066
+ /* @__PURE__ */ jsxs5(Box8, { gap: 0, children: [
1067
+ /* @__PURE__ */ jsxs5(Text8, { color: theme.title, bold: true, children: [
969
1068
  " ",
970
1069
  "detail"
971
1070
  ] }),
972
- /* @__PURE__ */ jsx7(Text7, { color: theme.fg, children: "(" }),
973
- /* @__PURE__ */ jsx7(Text7, { color: theme.titleHighlight, bold: true, children: task.name }),
974
- /* @__PURE__ */ jsx7(Text7, { color: theme.fg, children: ")" }),
975
- scrollOffset > 0 && /* @__PURE__ */ jsxs4(Text7, { dimColor: true, children: [
1071
+ /* @__PURE__ */ jsx8(Text8, { color: theme.fg, children: "(" }),
1072
+ /* @__PURE__ */ jsx8(Text8, { color: theme.titleHighlight, bold: true, children: task.name }),
1073
+ /* @__PURE__ */ jsx8(Text8, { color: theme.fg, children: ")" }),
1074
+ scrollOffset > 0 && /* @__PURE__ */ jsxs5(Text8, { dimColor: true, children: [
976
1075
  " \u2191",
977
1076
  scrollOffset
978
1077
  ] })
979
1078
  ] }),
980
- /* @__PURE__ */ jsx7(Text7, { children: visibleLines.join("\n") }),
981
- /* @__PURE__ */ jsx7(Box7, { flexGrow: 1 }),
982
- /* @__PURE__ */ jsx7(Box7, { paddingX: 1, children: /* @__PURE__ */ jsx7(MermaidHint, { content: allText }) })
1079
+ /* @__PURE__ */ jsx8(Text8, { children: visibleLines.join("\n") }),
1080
+ /* @__PURE__ */ jsx8(Box8, { flexGrow: 1 }),
1081
+ /* @__PURE__ */ jsx8(Box8, { paddingX: 1, children: /* @__PURE__ */ jsx8(MermaidHint, { content: allText }) })
983
1082
  ]
984
1083
  }
985
1084
  );
986
1085
  }
987
1086
 
988
1087
  // src/tui/components/TaskForm.tsx
989
- import { useState as useState2, useCallback } from "react";
990
- import { Box as Box9, Text as Text9, useInput as useInput2 } from "ink";
1088
+ import { useState as useState3, useCallback, useEffect as useEffect2, useRef } from "react";
1089
+ import { Box as Box10, Text as Text10, useInput as useInput2, useStdin } from "ink";
991
1090
 
992
1091
  // src/tui/editor.ts
993
1092
  import { writeFileSync as writeFileSync2, readFileSync, unlinkSync, mkdtempSync as mkdtempSync2 } from "fs";
@@ -997,15 +1096,17 @@ import { spawnSync } from "child_process";
997
1096
  function getEditor() {
998
1097
  return process.env["EDITOR"] ?? process.env["VISUAL"] ?? "vi";
999
1098
  }
1000
- function openInEditor(content, filename) {
1099
+ function openInEditor(content, filename, options) {
1001
1100
  const dir = mkdtempSync2(join2(tmpdir2(), "task-"));
1002
1101
  const filepath = join2(dir, filename);
1003
1102
  writeFileSync2(filepath, content, "utf-8");
1004
1103
  const editor = getEditor();
1104
+ options?.beforeOpen?.();
1005
1105
  const result = spawnSync(editor, [filepath], {
1006
1106
  stdio: "inherit",
1007
1107
  shell: true
1008
1108
  });
1109
+ options?.afterOpen?.();
1009
1110
  if (result.status !== 0) {
1010
1111
  try {
1011
1112
  unlinkSync(filepath);
@@ -1023,9 +1124,9 @@ function openInEditor(content, filename) {
1023
1124
  }
1024
1125
 
1025
1126
  // src/tui/components/TaskPicker.tsx
1026
- import { useState } from "react";
1027
- import { Box as Box8, Text as Text8, useInput, useStdout as useStdout2 } from "ink";
1028
- import { Fragment, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1127
+ import { useState as useState2 } from "react";
1128
+ import { Box as Box9, Text as Text9, useInput, useStdout as useStdout2 } from "ink";
1129
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1029
1130
  var DEP_TYPE_VALUES = Object.values(UIDependencyType);
1030
1131
  var DEP_TYPE_COLOR = {
1031
1132
  [DependencyType.Blocks]: theme.status.error,
@@ -1037,10 +1138,10 @@ function TaskPicker({ tasks, excludeIds, initialSelection, onConfirm, onCancel }
1037
1138
  const { stdout } = useStdout2();
1038
1139
  const termHeight = stdout.rows > 0 ? stdout.rows : 24;
1039
1140
  const maxVisible = Math.max(3, termHeight - 12);
1040
- const [searchQuery, setSearchQuery] = useState("");
1041
- const [isSearching, setIsSearching] = useState(false);
1042
- const [cursorIndex, setCursorIndex] = useState(0);
1043
- const [selected, setSelected] = useState(() => {
1141
+ const [searchQuery, setSearchQuery] = useState2("");
1142
+ const [isSearching, setIsSearching] = useState2(false);
1143
+ const [cursorIndex, setCursorIndex] = useState2(0);
1144
+ const [selected, setSelected] = useState2(() => {
1044
1145
  const map = /* @__PURE__ */ new Map();
1045
1146
  if (initialSelection) {
1046
1147
  for (const dep of initialSelection) {
@@ -1139,28 +1240,28 @@ function TaskPicker({ tasks, excludeIds, initialSelection, onConfirm, onCancel }
1139
1240
  return;
1140
1241
  }
1141
1242
  });
1142
- return /* @__PURE__ */ jsxs5(Box8, { flexDirection: "column", borderStyle: "bold", borderColor: theme.borderFocus, flexGrow: 1, children: [
1143
- /* @__PURE__ */ jsxs5(Box8, { gap: 0, children: [
1144
- /* @__PURE__ */ jsxs5(Text8, { color: theme.title, bold: true, children: [
1243
+ return /* @__PURE__ */ jsxs6(Box9, { flexDirection: "column", borderStyle: "bold", borderColor: theme.borderFocus, flexGrow: 1, children: [
1244
+ /* @__PURE__ */ jsxs6(Box9, { gap: 0, children: [
1245
+ /* @__PURE__ */ jsxs6(Text9, { color: theme.title, bold: true, children: [
1145
1246
  " ",
1146
1247
  "select dependencies"
1147
1248
  ] }),
1148
- /* @__PURE__ */ jsxs5(Text8, { color: theme.titleCounter, bold: true, children: [
1249
+ /* @__PURE__ */ jsxs6(Text9, { color: theme.titleCounter, bold: true, children: [
1149
1250
  " ",
1150
1251
  "[",
1151
1252
  selected.size,
1152
1253
  " selected]"
1153
1254
  ] })
1154
1255
  ] }),
1155
- isSearching ? /* @__PURE__ */ jsxs5(Box8, { borderStyle: "round", borderColor: theme.prompt, paddingX: 1, children: [
1156
- /* @__PURE__ */ jsx8(Text8, { color: theme.prompt, children: "/" }),
1157
- /* @__PURE__ */ jsx8(Text8, { color: theme.prompt, children: searchQuery }),
1158
- /* @__PURE__ */ jsx8(Text8, { color: theme.promptSuggest, children: "_" })
1159
- ] }) : searchQuery ? /* @__PURE__ */ jsx8(Box8, { paddingX: 1, children: /* @__PURE__ */ jsxs5(Text8, { color: theme.titleFilter, children: [
1256
+ isSearching ? /* @__PURE__ */ jsxs6(Box9, { borderStyle: "round", borderColor: theme.prompt, paddingX: 1, children: [
1257
+ /* @__PURE__ */ jsx9(Text9, { color: theme.prompt, children: "/" }),
1258
+ /* @__PURE__ */ jsx9(Text9, { color: theme.prompt, children: searchQuery }),
1259
+ /* @__PURE__ */ jsx9(Text9, { color: theme.promptSuggest, children: "_" })
1260
+ ] }) : searchQuery ? /* @__PURE__ */ jsx9(Box9, { paddingX: 1, children: /* @__PURE__ */ jsxs6(Text9, { color: theme.titleFilter, children: [
1160
1261
  "/",
1161
1262
  searchQuery
1162
1263
  ] }) }) : null,
1163
- /* @__PURE__ */ jsx8(Box8, { paddingX: 1, children: /* @__PURE__ */ jsxs5(Text8, { color: theme.table.headerFg, bold: true, children: [
1264
+ /* @__PURE__ */ jsx9(Box9, { paddingX: 1, children: /* @__PURE__ */ jsxs6(Text9, { color: theme.table.headerFg, bold: true, children: [
1164
1265
  " ",
1165
1266
  "SEL".padEnd(5),
1166
1267
  "ID".padEnd(14),
@@ -1168,7 +1269,7 @@ function TaskPicker({ tasks, excludeIds, initialSelection, onConfirm, onCancel }
1168
1269
  "STATUS".padEnd(14),
1169
1270
  "NAME"
1170
1271
  ] }) }),
1171
- available.length === 0 ? /* @__PURE__ */ jsx8(Box8, { paddingX: 2, paddingY: 1, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "No tasks match the filter" }) }) : visible.map((task, i) => {
1272
+ available.length === 0 ? /* @__PURE__ */ jsx9(Box9, { paddingX: 2, paddingY: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "No tasks match the filter" }) }) : visible.map((task, i) => {
1172
1273
  const actualIndex = viewStart + i;
1173
1274
  const isCursor = actualIndex === cursorIndex;
1174
1275
  const entry = selected.get(task.id);
@@ -1176,24 +1277,24 @@ function TaskPicker({ tasks, excludeIds, initialSelection, onConfirm, onCancel }
1176
1277
  const checkMark = isChecked ? "[x]" : "[ ]";
1177
1278
  const depType = entry ? DEP_TYPE_LABEL[entry.type] ?? entry.type : "";
1178
1279
  const depColor = entry ? DEP_TYPE_COLOR[entry.type] ?? theme.table.fg : theme.table.fg;
1179
- return /* @__PURE__ */ jsx8(Box8, { paddingX: 1, children: isCursor ? /* @__PURE__ */ jsxs5(Text8, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
1280
+ return /* @__PURE__ */ jsx9(Box9, { paddingX: 1, children: isCursor ? /* @__PURE__ */ jsxs6(Text9, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
1180
1281
  "> ",
1181
1282
  checkMark.padEnd(5),
1182
1283
  task.id.padEnd(14),
1183
1284
  depType.padEnd(12),
1184
1285
  task.status.padEnd(14),
1185
1286
  task.name
1186
- ] }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
1187
- /* @__PURE__ */ jsx8(Text8, { children: " " }),
1188
- /* @__PURE__ */ jsx8(Text8, { color: isChecked ? theme.status.added : theme.table.fg, children: checkMark.padEnd(5) }),
1189
- /* @__PURE__ */ jsx8(Text8, { color: theme.yaml.value, children: task.id.padEnd(14) }),
1190
- /* @__PURE__ */ jsx8(Text8, { color: depColor, children: depType.padEnd(12) }),
1191
- /* @__PURE__ */ jsx8(Text8, { color: theme.status.completed, children: task.status.padEnd(14) }),
1192
- /* @__PURE__ */ jsx8(Text8, { color: theme.table.fg, children: task.name })
1287
+ ] }) : /* @__PURE__ */ jsxs6(Fragment2, { children: [
1288
+ /* @__PURE__ */ jsx9(Text9, { children: " " }),
1289
+ /* @__PURE__ */ jsx9(Text9, { color: isChecked ? theme.status.added : theme.table.fg, children: checkMark.padEnd(5) }),
1290
+ /* @__PURE__ */ jsx9(Text9, { color: theme.yaml.value, children: task.id.padEnd(14) }),
1291
+ /* @__PURE__ */ jsx9(Text9, { color: depColor, children: depType.padEnd(12) }),
1292
+ /* @__PURE__ */ jsx9(Text9, { color: theme.status.completed, children: task.status.padEnd(14) }),
1293
+ /* @__PURE__ */ jsx9(Text9, { color: theme.table.fg, children: task.name })
1193
1294
  ] }) }, task.id);
1194
1295
  }),
1195
- /* @__PURE__ */ jsx8(Box8, { flexGrow: 1 }),
1196
- available.length > maxVisible && /* @__PURE__ */ jsx8(Box8, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs5(Text8, { dimColor: true, children: [
1296
+ /* @__PURE__ */ jsx9(Box9, { flexGrow: 1 }),
1297
+ available.length > maxVisible && /* @__PURE__ */ jsx9(Box9, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs6(Text9, { dimColor: true, children: [
1197
1298
  "[",
1198
1299
  viewStart + 1,
1199
1300
  "-",
@@ -1202,11 +1303,11 @@ function TaskPicker({ tasks, excludeIds, initialSelection, onConfirm, onCancel }
1202
1303
  available.length,
1203
1304
  "]"
1204
1305
  ] }) }),
1205
- selected.size > 0 && /* @__PURE__ */ jsxs5(Box8, { paddingX: 1, flexDirection: "column", children: [
1206
- /* @__PURE__ */ jsx8(Text8, { color: theme.table.headerFg, bold: true, children: "Selected:" }),
1207
- /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsx8(Text8, { children: Array.from(selected.values()).map((d) => `${d.id} (${DEP_TYPE_LABEL[d.type] ?? d.type})`).join(", ") }) })
1306
+ selected.size > 0 && /* @__PURE__ */ jsxs6(Box9, { paddingX: 1, flexDirection: "column", children: [
1307
+ /* @__PURE__ */ jsx9(Text9, { color: theme.table.headerFg, bold: true, children: "Selected:" }),
1308
+ /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Text9, { children: Array.from(selected.values()).map((d) => `${d.id} (${DEP_TYPE_LABEL[d.type] ?? d.type})`).join(", ") }) })
1208
1309
  ] }),
1209
- /* @__PURE__ */ jsx8(Box8, { paddingX: 1, children: /* @__PURE__ */ jsxs5(Text8, { dimColor: true, children: [
1310
+ /* @__PURE__ */ jsx9(Box9, { paddingX: 1, children: /* @__PURE__ */ jsxs6(Text9, { dimColor: true, children: [
1210
1311
  "space/t: select & cycle type (blocks ",
1211
1312
  "->",
1212
1313
  " relates-to ",
@@ -1222,7 +1323,7 @@ function TaskPicker({ tasks, excludeIds, initialSelection, onConfirm, onCancel }
1222
1323
  }
1223
1324
 
1224
1325
  // src/tui/components/TaskForm.tsx
1225
- import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1326
+ import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1226
1327
  var FIELDS = [
1227
1328
  { label: "Name", key: "name", type: "inline" },
1228
1329
  { label: "Type", key: "type", type: "select", options: TYPE_VALUES },
@@ -1243,8 +1344,8 @@ var FIELDS = [
1243
1344
  }
1244
1345
  ];
1245
1346
  function TaskForm({ editingTask, allTasks, initialDeps, onSave, onCancel }) {
1246
- const [focusIndex, setFocusIndex] = useState2(0);
1247
- const [values, setValues] = useState2({
1347
+ const [focusIndex, setFocusIndex] = useState3(0);
1348
+ const [values, setValues] = useState3({
1248
1349
  name: editingTask?.name ?? "",
1249
1350
  type: editingTask?.type ?? TaskType.Story,
1250
1351
  status: editingTask?.status ?? TaskStatus.Backlog,
@@ -1252,23 +1353,42 @@ function TaskForm({ editingTask, allTasks, initialDeps, onSave, onCancel }) {
1252
1353
  technicalNotes: editingTask?.technicalNotes ?? "",
1253
1354
  additionalRequirements: editingTask?.additionalRequirements ?? ""
1254
1355
  });
1255
- const [editorActive, setEditorActive] = useState2(false);
1256
- const [pickerActive, setPickerActive] = useState2(false);
1257
- const [pickedDeps, setPickedDeps] = useState2(initialDeps ?? []);
1356
+ const [cursorPos, setCursorPos] = useState3(() => (editingTask?.name ?? "").length);
1357
+ const cursorRef = useRef(cursorPos);
1358
+ cursorRef.current = cursorPos;
1359
+ const [editorActive, setEditorActive] = useState3(false);
1360
+ const [pickerActive, setPickerActive] = useState3(false);
1361
+ const [pickedDeps, setPickedDeps] = useState3(initialDeps ?? []);
1362
+ const { setRawMode } = useStdin();
1363
+ useEffect2(() => {
1364
+ const field2 = FIELDS[focusIndex];
1365
+ if (field2?.type === "inline") {
1366
+ const pos = values[field2.key]?.length ?? 0;
1367
+ setCursorPos(pos);
1368
+ cursorRef.current = pos;
1369
+ }
1370
+ }, [focusIndex]);
1258
1371
  const currentField = FIELDS[focusIndex];
1259
1372
  const launchEditor = useCallback(
1260
1373
  (field2) => {
1261
1374
  setEditorActive(true);
1262
1375
  setTimeout(() => {
1263
1376
  const content = values[field2.key] ?? "";
1264
- const result = openInEditor(content, field2.editorFilename ?? `${field2.key}.md`);
1377
+ const result = openInEditor(content, field2.editorFilename ?? `${field2.key}.md`, {
1378
+ beforeOpen: () => {
1379
+ setRawMode(false);
1380
+ },
1381
+ afterOpen: () => {
1382
+ setRawMode(true);
1383
+ }
1384
+ });
1265
1385
  if (result !== null) {
1266
1386
  setValues((v) => ({ ...v, [field2.key]: result }));
1267
1387
  }
1268
1388
  setEditorActive(false);
1269
1389
  }, 50);
1270
1390
  },
1271
- [values]
1391
+ [values, setRawMode]
1272
1392
  );
1273
1393
  const handlePickerConfirm = useCallback((selected) => {
1274
1394
  setPickedDeps(selected);
@@ -1308,32 +1428,49 @@ function TaskForm({ editingTask, allTasks, initialDeps, onSave, onCancel }) {
1308
1428
  }
1309
1429
  return;
1310
1430
  }
1431
+ if (key.upArrow) {
1432
+ setFocusIndex((i) => Math.max(0, i - 1));
1433
+ return;
1434
+ }
1435
+ if (key.downArrow) {
1436
+ setFocusIndex((i) => Math.min(FIELDS.length - 1, i + 1));
1437
+ return;
1438
+ }
1311
1439
  if (currentField.type === "inline") {
1312
- const currentValue = values[currentField.key] ?? "";
1313
- if (key.backspace || key.delete) {
1314
- setValues((v) => ({ ...v, [currentField.key]: currentValue.slice(0, -1) }));
1440
+ if (key.leftArrow) {
1441
+ setCursorPos((p) => Math.max(0, p - 1));
1442
+ } else if (key.rightArrow) {
1443
+ setCursorPos((p) => Math.min((values[currentField.key] ?? "").length, p + 1));
1444
+ } else if (key.backspace || key.delete) {
1445
+ const pos = cursorRef.current;
1446
+ if (pos > 0) {
1447
+ setValues((v) => {
1448
+ const cur = v[currentField.key] ?? "";
1449
+ return { ...v, [currentField.key]: cur.slice(0, pos - 1) + cur.slice(pos) };
1450
+ });
1451
+ cursorRef.current = pos - 1;
1452
+ setCursorPos(pos - 1);
1453
+ }
1315
1454
  } else if (key.return) {
1316
1455
  setFocusIndex((i) => Math.min(FIELDS.length - 1, i + 1));
1317
1456
  } else if (input && !key.ctrl && !key.meta) {
1318
- setValues((v) => ({ ...v, [currentField.key]: currentValue + input }));
1457
+ const pos = cursorRef.current;
1458
+ setValues((v) => {
1459
+ const cur = v[currentField.key] ?? "";
1460
+ return { ...v, [currentField.key]: cur.slice(0, pos) + input + cur.slice(pos) };
1461
+ });
1462
+ cursorRef.current = pos + input.length;
1463
+ setCursorPos(pos + input.length);
1319
1464
  }
1320
1465
  }
1321
1466
  if (currentField.type === "picker") {
1322
1467
  if (key.return) {
1323
1468
  setPickerActive(true);
1324
- } else if (key.downArrow) {
1325
- setFocusIndex((i) => Math.min(FIELDS.length - 1, i + 1));
1326
- } else if (key.upArrow) {
1327
- setFocusIndex((i) => Math.max(0, i - 1));
1328
1469
  }
1329
1470
  }
1330
1471
  if (currentField.type === "editor") {
1331
1472
  if (key.return) {
1332
1473
  launchEditor(currentField);
1333
- } else if (key.downArrow) {
1334
- setFocusIndex((i) => Math.min(FIELDS.length - 1, i + 1));
1335
- } else if (key.upArrow) {
1336
- setFocusIndex((i) => Math.max(0, i - 1));
1337
1474
  }
1338
1475
  }
1339
1476
  if (currentField.type === "select") {
@@ -1353,7 +1490,7 @@ function TaskForm({ editingTask, allTasks, initialDeps, onSave, onCancel }) {
1353
1490
  );
1354
1491
  if (pickerActive) {
1355
1492
  const pickerExclude = editingTask ? { excludeIds: /* @__PURE__ */ new Set([editingTask.id]) } : {};
1356
- return /* @__PURE__ */ jsx9(
1493
+ return /* @__PURE__ */ jsx10(
1357
1494
  TaskPicker,
1358
1495
  {
1359
1496
  tasks: allTasks,
@@ -1366,63 +1503,64 @@ function TaskForm({ editingTask, allTasks, initialDeps, onSave, onCancel }) {
1366
1503
  }
1367
1504
  const isEdit = editingTask !== null;
1368
1505
  const depSummary = pickedDeps.length > 0 ? pickedDeps.map((d) => `${d.id} (${DEP_TYPE_LABEL[d.type] ?? d.type})`).join(", ") : "";
1369
- return /* @__PURE__ */ jsxs6(Box9, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
1370
- /* @__PURE__ */ jsx9(Box9, { gap: 0, children: /* @__PURE__ */ jsxs6(Text9, { color: theme.title, bold: true, children: [
1506
+ return /* @__PURE__ */ jsxs7(Box10, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
1507
+ /* @__PURE__ */ jsx10(Box10, { gap: 0, children: /* @__PURE__ */ jsxs7(Text10, { color: theme.title, bold: true, children: [
1371
1508
  " ",
1372
1509
  isEdit ? "edit" : "create"
1373
1510
  ] }) }),
1374
- /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", paddingX: 1, paddingY: 0, children: FIELDS.map((field2, i) => {
1511
+ /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", paddingX: 1, paddingY: 0, children: FIELDS.map((field2, i) => {
1375
1512
  const isFocused = i === focusIndex;
1376
1513
  const value = field2.key === "dependsOn" ? depSummary : values[field2.key] ?? "";
1377
- const displayValue = value;
1378
- return /* @__PURE__ */ jsxs6(Box9, { gap: 1, children: [
1379
- /* @__PURE__ */ jsxs6(Text9, { color: isFocused ? theme.dialog.label : theme.yaml.key, bold: isFocused, children: [
1514
+ return /* @__PURE__ */ jsxs7(Box10, { gap: 1, children: [
1515
+ /* @__PURE__ */ jsxs7(Text10, { color: isFocused ? theme.dialog.label : theme.yaml.key, bold: isFocused, children: [
1380
1516
  isFocused ? ">" : " ",
1381
1517
  " ",
1382
1518
  field2.label.padEnd(14)
1383
1519
  ] }),
1384
- field2.type === "inline" && /* @__PURE__ */ jsxs6(Text9, { color: isFocused ? theme.yaml.value : theme.table.fg, children: [
1385
- displayValue,
1386
- isFocused ? /* @__PURE__ */ jsx9(Text9, { color: theme.titleHighlight, children: "_" }) : ""
1387
- ] }),
1388
- field2.type === "picker" && /* @__PURE__ */ jsxs6(Text9, { children: [
1389
- displayValue ? /* @__PURE__ */ jsx9(Text9, { color: theme.status.added, children: displayValue.length > 60 ? displayValue.slice(0, 60) + "..." : displayValue }) : /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: isFocused ? "press enter to select" : "none" }),
1390
- isFocused && /* @__PURE__ */ jsx9(Text9, { color: theme.menu.key, children: " [enter: open picker]" })
1520
+ field2.type === "inline" && /* @__PURE__ */ jsx10(Text10, { color: isFocused ? theme.yaml.value : theme.table.fg, children: isFocused ? /* @__PURE__ */ jsxs7(Fragment3, { children: [
1521
+ value.slice(0, cursorPos),
1522
+ /* @__PURE__ */ jsx10(Text10, { color: theme.titleHighlight, children: "_" }),
1523
+ value.slice(cursorPos)
1524
+ ] }) : value }),
1525
+ field2.type === "picker" && /* @__PURE__ */ jsxs7(Text10, { children: [
1526
+ value ? /* @__PURE__ */ jsx10(Text10, { color: theme.status.added, children: value.length > 60 ? value.slice(0, 60) + "..." : value }) : /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: isFocused ? "press enter to select" : "none" }),
1527
+ isFocused && /* @__PURE__ */ jsx10(Text10, { color: theme.menu.key, children: " [enter: open picker]" })
1391
1528
  ] }),
1392
- field2.type === "editor" && /* @__PURE__ */ jsxs6(Text9, { children: [
1393
- displayValue ? /* @__PURE__ */ jsxs6(Text9, { color: theme.status.added, children: [
1394
- displayValue.split("\n")[0]?.slice(0, 50),
1395
- displayValue.length > 50 || displayValue.includes("\n") ? "..." : ""
1396
- ] }) : /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: isFocused ? "press enter" : "empty" }),
1397
- isFocused && /* @__PURE__ */ jsx9(Text9, { color: theme.menu.key, children: " [enter: $EDITOR]" })
1529
+ field2.type === "editor" && /* @__PURE__ */ jsxs7(Text10, { children: [
1530
+ value ? /* @__PURE__ */ jsxs7(Text10, { color: theme.status.added, children: [
1531
+ value.split("\n")[0]?.slice(0, 50),
1532
+ value.length > 50 || value.includes("\n") ? "..." : ""
1533
+ ] }) : /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: isFocused ? "press enter" : "empty" }),
1534
+ isFocused && /* @__PURE__ */ jsx10(Text10, { color: theme.menu.key, children: " [enter: $EDITOR]" })
1398
1535
  ] }),
1399
- field2.type === "select" && /* @__PURE__ */ jsxs6(Text9, { color: isFocused ? theme.yaml.value : theme.table.fg, children: [
1536
+ field2.type === "select" && /* @__PURE__ */ jsxs7(Text10, { color: isFocused ? theme.yaml.value : theme.table.fg, children: [
1400
1537
  isFocused ? "< " : " ",
1401
- displayValue,
1538
+ value,
1402
1539
  isFocused ? " >" : ""
1403
1540
  ] })
1404
1541
  ] }, field2.key);
1405
1542
  }) }),
1406
- /* @__PURE__ */ jsx9(Box9, { flexGrow: 1 }),
1407
- /* @__PURE__ */ jsx9(Box9, { paddingX: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "tab: next | shift+tab: prev | ctrl+s: save | esc: cancel" }) }),
1408
- editorActive && /* @__PURE__ */ jsx9(Box9, { paddingX: 1, children: /* @__PURE__ */ jsx9(Text9, { color: theme.flash.warn, bold: true, children: "Editor open... save and close to return" }) })
1543
+ /* @__PURE__ */ jsx10(Box10, { flexGrow: 1 }),
1544
+ /* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\u2191\u2193/tab: navigate | \u2190\u2192: cursor | ctrl+s: save | esc: cancel" }) }),
1545
+ editorActive && /* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text10, { color: theme.flash.warn, bold: true, children: "Editor open... save and close to return" }) })
1409
1546
  ] });
1410
1547
  }
1411
1548
 
1412
1549
  // src/tui/components/ProjectSelector.tsx
1413
- import { useState as useState3 } from "react";
1414
- import { Box as Box10, Text as Text10, useInput as useInput3 } from "ink";
1415
- import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1550
+ import { useState as useState4 } from "react";
1551
+ import { Box as Box11, Text as Text11, useInput as useInput3 } from "ink";
1552
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1416
1553
  function ProjectSelector({
1417
1554
  projects,
1418
1555
  activeProject,
1419
1556
  onSelect,
1420
1557
  onCreate,
1558
+ onEdit,
1421
1559
  onSetDefault,
1422
1560
  onLink,
1423
1561
  onCancel
1424
1562
  }) {
1425
- const [selectedIndex, setSelectedIndex] = useState3(() => {
1563
+ const [selectedIndex, setSelectedIndex] = useState4(() => {
1426
1564
  if (!activeProject) return 0;
1427
1565
  const idx = projects.findIndex((p) => p.id === activeProject.id);
1428
1566
  return idx >= 0 ? idx : 0;
@@ -1443,6 +1581,13 @@ function ProjectSelector({
1443
1581
  onCreate();
1444
1582
  return;
1445
1583
  }
1584
+ if (input === "e") {
1585
+ const project = projects[selectedIndex];
1586
+ if (project) {
1587
+ onEdit(project);
1588
+ }
1589
+ return;
1590
+ }
1446
1591
  if (input === "d") {
1447
1592
  const project = projects[selectedIndex];
1448
1593
  if (project) {
@@ -1464,32 +1609,32 @@ function ProjectSelector({
1464
1609
  setSelectedIndex((i) => Math.min(projects.length - 1, i + 1));
1465
1610
  }
1466
1611
  });
1467
- return /* @__PURE__ */ jsxs7(Box10, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
1468
- /* @__PURE__ */ jsxs7(Box10, { gap: 0, children: [
1469
- /* @__PURE__ */ jsxs7(Text10, { color: theme.title, bold: true, children: [
1612
+ return /* @__PURE__ */ jsxs8(Box11, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
1613
+ /* @__PURE__ */ jsxs8(Box11, { gap: 0, children: [
1614
+ /* @__PURE__ */ jsxs8(Text11, { color: theme.title, bold: true, children: [
1470
1615
  " ",
1471
1616
  "projects"
1472
1617
  ] }),
1473
- /* @__PURE__ */ jsxs7(Text10, { color: theme.titleCounter, bold: true, children: [
1618
+ /* @__PURE__ */ jsxs8(Text11, { color: theme.titleCounter, bold: true, children: [
1474
1619
  "[",
1475
1620
  projects.length,
1476
1621
  "]"
1477
1622
  ] })
1478
1623
  ] }),
1479
- /* @__PURE__ */ jsxs7(Box10, { paddingX: 1, children: [
1480
- /* @__PURE__ */ jsx10(Text10, { color: theme.table.headerFg, bold: true, children: " NAME".padEnd(30) }),
1481
- /* @__PURE__ */ jsx10(Text10, { color: theme.table.headerFg, bold: true, children: "GIT REMOTE".padEnd(40) }),
1482
- /* @__PURE__ */ jsx10(Text10, { color: theme.table.headerFg, bold: true, children: "DESCRIPTION" })
1624
+ /* @__PURE__ */ jsxs8(Box11, { paddingX: 1, children: [
1625
+ /* @__PURE__ */ jsx11(Text11, { color: theme.table.headerFg, bold: true, children: " NAME".padEnd(30) }),
1626
+ /* @__PURE__ */ jsx11(Text11, { color: theme.table.headerFg, bold: true, children: "GIT REMOTE".padEnd(40) }),
1627
+ /* @__PURE__ */ jsx11(Text11, { color: theme.table.headerFg, bold: true, children: "DESCRIPTION" })
1483
1628
  ] }),
1484
- projects.length === 0 ? /* @__PURE__ */ jsx10(Box10, { paddingX: 1, paddingY: 1, children: /* @__PURE__ */ jsx10(Text10, { color: theme.fg, children: "No projects. Press 'c' to create one." }) }) : projects.map((project, i) => {
1629
+ projects.length === 0 ? /* @__PURE__ */ jsx11(Box11, { paddingX: 1, paddingY: 1, children: /* @__PURE__ */ jsx11(Text11, { color: theme.fg, children: "No projects. Press 'c' to create one." }) }) : projects.map((project, i) => {
1485
1630
  const isSelected = i === selectedIndex;
1486
1631
  const isActive = project.id === activeProject?.id;
1487
1632
  const activeMarker = isActive ? "*" : " ";
1488
1633
  const defaultMarker = project.isDefault ? "D" : " ";
1489
1634
  const marker = `${activeMarker}${defaultMarker}`;
1490
- const remoteDisplay = (project.gitRemote ?? "").slice(0, 38).padEnd(40);
1635
+ const remoteDisplay = (project.gitRemote?.value ?? "").slice(0, 38).padEnd(40);
1491
1636
  if (isSelected) {
1492
- return /* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsxs7(Text10, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
1637
+ return /* @__PURE__ */ jsx11(Box11, { paddingX: 1, children: /* @__PURE__ */ jsxs8(Text11, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
1493
1638
  marker,
1494
1639
  " ",
1495
1640
  project.name.padEnd(27),
@@ -1497,39 +1642,64 @@ function ProjectSelector({
1497
1642
  project.description
1498
1643
  ] }) }, project.id);
1499
1644
  }
1500
- return /* @__PURE__ */ jsxs7(Box10, { paddingX: 1, children: [
1501
- /* @__PURE__ */ jsxs7(Text10, { color: isActive ? theme.status.modified : theme.table.fg, children: [
1645
+ return /* @__PURE__ */ jsxs8(Box11, { paddingX: 1, children: [
1646
+ /* @__PURE__ */ jsxs8(Text11, { color: isActive ? theme.status.modified : theme.table.fg, children: [
1502
1647
  marker,
1503
1648
  " ",
1504
1649
  project.name.padEnd(27)
1505
1650
  ] }),
1506
- /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: remoteDisplay }),
1507
- /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: project.description })
1651
+ /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: remoteDisplay }),
1652
+ /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: project.description })
1508
1653
  ] }, project.id);
1509
1654
  }),
1510
- /* @__PURE__ */ jsx10(Box10, { flexGrow: 1 }),
1511
- /* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "enter: select | d: set default | l: link git | c: create | esc: back" }) })
1655
+ /* @__PURE__ */ jsx11(Box11, { flexGrow: 1 }),
1656
+ /* @__PURE__ */ jsx11(Box11, { paddingX: 1, children: /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "enter: select | e: edit | d: set default | l: link git | c: create | esc: back" }) })
1512
1657
  ] });
1513
1658
  }
1514
1659
 
1515
1660
  // src/tui/components/ProjectForm.tsx
1516
- import { useState as useState4 } from "react";
1517
- import { Box as Box11, Text as Text11, useInput as useInput4 } from "ink";
1518
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1661
+ import { useState as useState5, useEffect as useEffect3, useRef as useRef2 } from "react";
1662
+ import { Box as Box12, Text as Text12, useInput as useInput4 } from "ink";
1663
+ import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
1519
1664
  var FIELDS2 = [
1520
1665
  { label: "Name", key: "name", type: "inline" },
1521
1666
  { label: "Key", key: "key", type: "inline" },
1522
1667
  { label: "Description", key: "description", type: "inline" },
1668
+ { label: "Git Remote", key: "gitRemote", type: "inline" },
1523
1669
  { label: "Default", key: "isDefault", type: "toggle" }
1524
1670
  ];
1525
- function ProjectForm({ onSave, onCancel }) {
1526
- const [focusIndex, setFocusIndex] = useState4(0);
1527
- const [values, setValues] = useState4({
1528
- name: "",
1529
- key: "",
1530
- description: "",
1531
- isDefault: "no"
1671
+ function ProjectForm({ editingProject, initialGitRemote, onSave, onCancel }) {
1672
+ const isEditing = !!editingProject;
1673
+ const [focusIndex, setFocusIndex] = useState5(0);
1674
+ const [values, setValues] = useState5(() => {
1675
+ if (editingProject) {
1676
+ return {
1677
+ name: editingProject.name,
1678
+ key: editingProject.key,
1679
+ description: editingProject.description,
1680
+ gitRemote: editingProject.gitRemote?.value ?? "",
1681
+ isDefault: editingProject.isDefault ? "yes" : "no"
1682
+ };
1683
+ }
1684
+ return {
1685
+ name: "",
1686
+ key: "",
1687
+ description: "",
1688
+ gitRemote: initialGitRemote?.value ?? "",
1689
+ isDefault: "no"
1690
+ };
1532
1691
  });
1692
+ const [cursorPos, setCursorPos] = useState5(0);
1693
+ const cursorRef = useRef2(cursorPos);
1694
+ cursorRef.current = cursorPos;
1695
+ useEffect3(() => {
1696
+ const field2 = FIELDS2[focusIndex];
1697
+ if (field2?.type === "inline") {
1698
+ const pos = values[field2.key]?.length ?? 0;
1699
+ setCursorPos(pos);
1700
+ cursorRef.current = pos;
1701
+ }
1702
+ }, [focusIndex]);
1533
1703
  const currentField = FIELDS2[focusIndex];
1534
1704
  useInput4((input, key) => {
1535
1705
  if (key.escape) {
@@ -1552,19 +1722,46 @@ function ProjectForm({ onSave, onCancel }) {
1552
1722
  name: nameVal,
1553
1723
  key: values["key"] ?? "",
1554
1724
  description: values["description"] ?? "",
1725
+ gitRemote: values["gitRemote"] ?? "",
1555
1726
  isDefault: values["isDefault"] === "yes"
1556
1727
  });
1557
1728
  }
1558
1729
  return;
1559
1730
  }
1560
- if (currentField.type === "inline") {
1561
- const currentValue = values[currentField.key] ?? "";
1562
- if (key.backspace || key.delete) {
1563
- setValues((v) => ({ ...v, [currentField.key]: currentValue.slice(0, -1) }));
1731
+ if (key.upArrow) {
1732
+ setFocusIndex((i) => Math.max(0, i - 1));
1733
+ return;
1734
+ }
1735
+ if (key.downArrow) {
1736
+ setFocusIndex((i) => Math.min(FIELDS2.length - 1, i + 1));
1737
+ return;
1738
+ }
1739
+ const isReadOnly = isEditing && currentField.key === "key";
1740
+ if (currentField.type === "inline" && !isReadOnly) {
1741
+ if (key.leftArrow) {
1742
+ setCursorPos((p) => Math.max(0, p - 1));
1743
+ } else if (key.rightArrow) {
1744
+ setCursorPos((p) => Math.min((values[currentField.key] ?? "").length, p + 1));
1745
+ } else if (key.backspace || key.delete) {
1746
+ const pos = cursorRef.current;
1747
+ if (pos > 0) {
1748
+ setValues((v) => {
1749
+ const cur = v[currentField.key] ?? "";
1750
+ return { ...v, [currentField.key]: cur.slice(0, pos - 1) + cur.slice(pos) };
1751
+ });
1752
+ cursorRef.current = pos - 1;
1753
+ setCursorPos(pos - 1);
1754
+ }
1564
1755
  } else if (key.return) {
1565
1756
  setFocusIndex((i) => Math.min(FIELDS2.length - 1, i + 1));
1566
1757
  } else if (input && !key.ctrl && !key.meta) {
1567
- setValues((v) => ({ ...v, [currentField.key]: currentValue + input }));
1758
+ const pos = cursorRef.current;
1759
+ setValues((v) => {
1760
+ const cur = v[currentField.key] ?? "";
1761
+ return { ...v, [currentField.key]: cur.slice(0, pos) + input + cur.slice(pos) };
1762
+ });
1763
+ cursorRef.current = pos + input.length;
1764
+ setCursorPos(pos + input.length);
1568
1765
  }
1569
1766
  }
1570
1767
  if (currentField.type === "toggle") {
@@ -1576,43 +1773,50 @@ function ProjectForm({ onSave, onCancel }) {
1576
1773
  }
1577
1774
  }
1578
1775
  });
1579
- return /* @__PURE__ */ jsxs8(Box11, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
1580
- /* @__PURE__ */ jsx11(Box11, { gap: 0, children: /* @__PURE__ */ jsxs8(Text11, { color: theme.title, bold: true, children: [
1776
+ return /* @__PURE__ */ jsxs9(Box12, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
1777
+ /* @__PURE__ */ jsx12(Box12, { gap: 0, children: /* @__PURE__ */ jsxs9(Text12, { color: theme.title, bold: true, children: [
1581
1778
  " ",
1582
- "new project"
1779
+ isEditing ? "edit project" : "new project"
1583
1780
  ] }) }),
1584
- /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", paddingX: 1, paddingY: 0, children: FIELDS2.map((field2, i) => {
1781
+ /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", paddingX: 1, paddingY: 0, children: FIELDS2.map((field2, i) => {
1585
1782
  const isFocused = i === focusIndex;
1586
1783
  const value = values[field2.key] ?? "";
1587
- return /* @__PURE__ */ jsxs8(Box11, { gap: 1, children: [
1588
- /* @__PURE__ */ jsxs8(Text11, { color: isFocused ? theme.dialog.label : theme.yaml.key, bold: isFocused, children: [
1784
+ return /* @__PURE__ */ jsxs9(Box12, { gap: 1, children: [
1785
+ /* @__PURE__ */ jsxs9(Text12, { color: isFocused ? theme.dialog.label : theme.yaml.key, bold: isFocused, children: [
1589
1786
  isFocused ? ">" : " ",
1590
1787
  " ",
1591
1788
  field2.label.padEnd(14)
1592
1789
  ] }),
1593
- field2.type === "inline" && /* @__PURE__ */ jsxs8(Text11, { color: isFocused ? theme.yaml.value : theme.table.fg, children: [
1594
- value,
1595
- isFocused ? /* @__PURE__ */ jsx11(Text11, { color: theme.titleHighlight, children: "_" }) : "",
1596
- field2.key === "key" && !value && /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: isFocused ? " (auto from name)" : "" })
1790
+ field2.type === "inline" && /* @__PURE__ */ jsxs9(Text12, { color: isFocused ? theme.yaml.value : theme.table.fg, children: [
1791
+ isFocused ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
1792
+ value.slice(0, cursorPos),
1793
+ /* @__PURE__ */ jsx12(Text12, { color: theme.titleHighlight, children: "_" }),
1794
+ value.slice(cursorPos)
1795
+ ] }) : value,
1796
+ field2.key === "key" && !value && !isEditing && /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: isFocused ? " (auto from name)" : "" }),
1797
+ field2.key === "key" && isEditing && isFocused && /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: " (read-only)" })
1597
1798
  ] }),
1598
- field2.type === "toggle" && /* @__PURE__ */ jsxs8(Text11, { color: isFocused ? theme.yaml.value : theme.table.fg, children: [
1799
+ field2.type === "toggle" && /* @__PURE__ */ jsxs9(Text12, { color: isFocused ? theme.yaml.value : theme.table.fg, children: [
1599
1800
  isFocused ? "< " : " ",
1600
1801
  value,
1601
1802
  isFocused ? " >" : ""
1602
1803
  ] })
1603
1804
  ] }, field2.key);
1604
1805
  }) }),
1605
- /* @__PURE__ */ jsx11(Box11, { flexGrow: 1 }),
1606
- /* @__PURE__ */ jsx11(Box11, { paddingX: 1, children: /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "tab: next | shift+tab: prev | ctrl+s: save | esc: cancel" }) })
1806
+ /* @__PURE__ */ jsx12(Box12, { flexGrow: 1 }),
1807
+ /* @__PURE__ */ jsx12(Box12, { paddingX: 1, children: /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "\u2191\u2193/tab: navigate | \u2190\u2192: cursor | ctrl+s: save | esc: cancel" }) })
1607
1808
  ] });
1608
1809
  }
1609
1810
 
1610
1811
  // src/tui/components/ProjectLinkForm.tsx
1611
- import { useState as useState5 } from "react";
1612
- import { Box as Box12, Text as Text12, useInput as useInput5 } from "ink";
1613
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
1812
+ import { useState as useState6, useRef as useRef3 } from "react";
1813
+ import { Box as Box13, Text as Text13, useInput as useInput5 } from "ink";
1814
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1614
1815
  function ProjectLinkForm({ project, onSave, onUnlink, onDetect, onCancel }) {
1615
- const [remoteUrl, setRemoteUrl] = useState5(project.gitRemote ?? "");
1816
+ const [remoteUrl, setRemoteUrl] = useState6(project.gitRemote?.value ?? "");
1817
+ const [cursorPos, setCursorPos] = useState6(() => (project.gitRemote?.value ?? "").length);
1818
+ const cursorRef = useRef3(cursorPos);
1819
+ cursorRef.current = cursorPos;
1616
1820
  useInput5((input, key) => {
1617
1821
  if (key.escape) {
1618
1822
  onCancel();
@@ -1629,6 +1833,8 @@ function ProjectLinkForm({ project, onSave, onUnlink, onDetect, onCancel }) {
1629
1833
  const detected = onDetect();
1630
1834
  if (detected) {
1631
1835
  setRemoteUrl(detected);
1836
+ cursorRef.current = detected.length;
1837
+ setCursorPos(detected.length);
1632
1838
  }
1633
1839
  return;
1634
1840
  }
@@ -1638,55 +1844,107 @@ function ProjectLinkForm({ project, onSave, onUnlink, onDetect, onCancel }) {
1638
1844
  }
1639
1845
  return;
1640
1846
  }
1847
+ if (key.leftArrow) {
1848
+ setCursorPos((p) => Math.max(0, p - 1));
1849
+ return;
1850
+ }
1851
+ if (key.rightArrow) {
1852
+ setCursorPos((p) => Math.min(remoteUrl.length, p + 1));
1853
+ return;
1854
+ }
1641
1855
  if (key.backspace || key.delete) {
1642
- setRemoteUrl((v) => v.slice(0, -1));
1856
+ const pos = cursorRef.current;
1857
+ if (pos > 0) {
1858
+ setRemoteUrl((v) => v.slice(0, pos - 1) + v.slice(pos));
1859
+ cursorRef.current = pos - 1;
1860
+ setCursorPos(pos - 1);
1861
+ }
1643
1862
  return;
1644
1863
  }
1645
1864
  if (input && !key.ctrl && !key.meta) {
1646
- setRemoteUrl((v) => v + input);
1865
+ const pos = cursorRef.current;
1866
+ setRemoteUrl((v) => v.slice(0, pos) + input + v.slice(pos));
1867
+ cursorRef.current = pos + input.length;
1868
+ setCursorPos(pos + input.length);
1647
1869
  }
1648
1870
  });
1649
- return /* @__PURE__ */ jsxs9(Box12, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
1650
- /* @__PURE__ */ jsxs9(Box12, { gap: 0, children: [
1651
- /* @__PURE__ */ jsxs9(Text12, { color: theme.title, bold: true, children: [
1871
+ return /* @__PURE__ */ jsxs10(Box13, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
1872
+ /* @__PURE__ */ jsxs10(Box13, { gap: 0, children: [
1873
+ /* @__PURE__ */ jsxs10(Text13, { color: theme.title, bold: true, children: [
1652
1874
  " ",
1653
1875
  "link git remote"
1654
1876
  ] }),
1655
- /* @__PURE__ */ jsxs9(Text12, { color: theme.titleCounter, bold: true, children: [
1877
+ /* @__PURE__ */ jsxs10(Text13, { color: theme.titleCounter, bold: true, children: [
1656
1878
  " ",
1657
1879
  "[",
1658
1880
  project.name,
1659
1881
  "]"
1660
1882
  ] })
1661
1883
  ] }),
1662
- /* @__PURE__ */ jsxs9(Box12, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [
1663
- /* @__PURE__ */ jsxs9(Box12, { gap: 1, children: [
1664
- /* @__PURE__ */ jsx12(Text12, { color: theme.dialog.label, bold: true, children: "Current:" }),
1665
- /* @__PURE__ */ jsx12(
1666
- Text12,
1884
+ /* @__PURE__ */ jsxs10(Box13, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [
1885
+ /* @__PURE__ */ jsxs10(Box13, { gap: 1, children: [
1886
+ /* @__PURE__ */ jsx13(Text13, { color: theme.dialog.label, bold: true, children: "Current:" }),
1887
+ /* @__PURE__ */ jsx13(
1888
+ Text13,
1667
1889
  {
1668
1890
  color: project.gitRemote ? theme.yaml.value : theme.table.fg,
1669
1891
  dimColor: !project.gitRemote,
1670
- children: project.gitRemote ?? "(none)"
1892
+ children: project.gitRemote?.value ?? "(none)"
1671
1893
  }
1672
1894
  )
1673
1895
  ] }),
1674
- /* @__PURE__ */ jsxs9(Box12, { gap: 1, marginTop: 1, children: [
1675
- /* @__PURE__ */ jsx12(Text12, { color: theme.dialog.label, bold: true, children: "Remote URL: " }),
1676
- /* @__PURE__ */ jsxs9(Text12, { color: theme.yaml.value, children: [
1677
- remoteUrl,
1678
- /* @__PURE__ */ jsx12(Text12, { color: theme.titleHighlight, children: "_" })
1896
+ /* @__PURE__ */ jsxs10(Box13, { gap: 1, marginTop: 1, children: [
1897
+ /* @__PURE__ */ jsx13(Text13, { color: theme.dialog.label, bold: true, children: "Remote URL: " }),
1898
+ /* @__PURE__ */ jsxs10(Text13, { color: theme.yaml.value, children: [
1899
+ remoteUrl.slice(0, cursorPos),
1900
+ /* @__PURE__ */ jsx13(Text13, { color: theme.titleHighlight, children: "_" }),
1901
+ remoteUrl.slice(cursorPos)
1679
1902
  ] })
1680
1903
  ] })
1681
1904
  ] }),
1682
- /* @__PURE__ */ jsx12(Box12, { flexGrow: 1 }),
1683
- /* @__PURE__ */ jsx12(Box12, { paddingX: 1, children: /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "ctrl+s: save | ctrl+d: detect from cwd | ctrl+u: unlink | esc: cancel" }) })
1905
+ /* @__PURE__ */ jsx13(Box13, { flexGrow: 1 }),
1906
+ /* @__PURE__ */ jsx13(Box13, { paddingX: 1, children: /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "ctrl+s: save | ctrl+d: detect from cwd | ctrl+u: unlink | esc: cancel" }) })
1684
1907
  ] });
1685
1908
  }
1686
1909
 
1910
+ // src/utils/dismissed-remotes.ts
1911
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
1912
+ function isDismissedRemote(filePath, remote) {
1913
+ try {
1914
+ const content = readFileSync2(filePath, "utf-8");
1915
+ const list = JSON.parse(content);
1916
+ if (!Array.isArray(list)) return false;
1917
+ return list.includes(remote.value);
1918
+ } catch {
1919
+ return false;
1920
+ }
1921
+ }
1922
+ function dismissRemote(filePath, remote) {
1923
+ let list = [];
1924
+ try {
1925
+ const content = readFileSync2(filePath, "utf-8");
1926
+ const parsed = JSON.parse(content);
1927
+ if (Array.isArray(parsed)) {
1928
+ list = parsed.filter((v) => typeof v === "string");
1929
+ }
1930
+ } catch {
1931
+ }
1932
+ if (list.includes(remote.value)) return;
1933
+ list.push(remote.value);
1934
+ try {
1935
+ writeFileSync3(filePath, JSON.stringify(list), "utf-8");
1936
+ logger.info(`dismissRemote: persisted ${remote.value} to ${filePath}`);
1937
+ } catch (e) {
1938
+ logger.error(
1939
+ `dismissRemote: failed to write ${filePath}`,
1940
+ e instanceof Error ? e : new Error(String(e))
1941
+ );
1942
+ }
1943
+ }
1944
+
1687
1945
  // src/tui/components/HelpOverlay.tsx
1688
- import { Box as Box13, Text as Text13 } from "ink";
1689
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1946
+ import { Box as Box14, Text as Text14 } from "ink";
1947
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1690
1948
  var ROW1 = [
1691
1949
  {
1692
1950
  title: "NAVIGATION",
@@ -1755,10 +2013,10 @@ var ROW2 = [
1755
2013
  {
1756
2014
  title: "FORMS",
1757
2015
  keys: [
1758
- ["tab", "Next field"],
1759
- ["shift+tab", "Prev field"],
2016
+ ["\u2191\u2193/tab", "Navigate fields"],
2017
+ ["\u2190\u2192", "Move cursor"],
1760
2018
  ["ctrl+s", "Save"],
1761
- ["enter", "Open editor"],
2019
+ ["enter", "Open editor / next"],
1762
2020
  ["esc", "Cancel"]
1763
2021
  ]
1764
2022
  },
@@ -1772,21 +2030,21 @@ var ROW2 = [
1772
2030
  }
1773
2031
  ];
1774
2032
  function SectionRow({ sections }) {
1775
- return /* @__PURE__ */ jsx13(Box13, { flexDirection: "row", gap: 4, children: sections.map((section) => /* @__PURE__ */ jsxs10(Box13, { flexDirection: "column", children: [
1776
- /* @__PURE__ */ jsx13(Text13, { color: theme.table.headerFg, bold: true, children: section.title }),
1777
- section.keys.map(([key, desc]) => /* @__PURE__ */ jsxs10(Box13, { gap: 1, children: [
1778
- /* @__PURE__ */ jsxs10(Text13, { color: theme.menu.key, bold: true, children: [
2033
+ return /* @__PURE__ */ jsx14(Box14, { flexDirection: "row", gap: 4, children: sections.map((section) => /* @__PURE__ */ jsxs11(Box14, { flexDirection: "column", children: [
2034
+ /* @__PURE__ */ jsx14(Text14, { color: theme.table.headerFg, bold: true, children: section.title }),
2035
+ section.keys.map(([key, desc]) => /* @__PURE__ */ jsxs11(Box14, { gap: 1, children: [
2036
+ /* @__PURE__ */ jsxs11(Text14, { color: theme.menu.key, bold: true, children: [
1779
2037
  "<",
1780
2038
  key.padEnd(5),
1781
2039
  ">"
1782
2040
  ] }),
1783
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: desc })
2041
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: desc })
1784
2042
  ] }, key))
1785
2043
  ] }, section.title)) });
1786
2044
  }
1787
2045
  function HelpOverlay() {
1788
- return /* @__PURE__ */ jsxs10(
1789
- Box13,
2046
+ return /* @__PURE__ */ jsxs11(
2047
+ Box14,
1790
2048
  {
1791
2049
  flexDirection: "column",
1792
2050
  borderStyle: "bold",
@@ -1794,28 +2052,28 @@ function HelpOverlay() {
1794
2052
  paddingX: 2,
1795
2053
  paddingY: 1,
1796
2054
  children: [
1797
- /* @__PURE__ */ jsxs10(Text13, { color: theme.title, bold: true, children: [
2055
+ /* @__PURE__ */ jsxs11(Text14, { color: theme.title, bold: true, children: [
1798
2056
  " ",
1799
2057
  "Help"
1800
2058
  ] }),
1801
- /* @__PURE__ */ jsx13(Text13, { children: " " }),
1802
- /* @__PURE__ */ jsxs10(Box13, { flexDirection: "column", gap: 1, children: [
1803
- /* @__PURE__ */ jsx13(SectionRow, { sections: ROW1 }),
1804
- /* @__PURE__ */ jsx13(SectionRow, { sections: ROW2 })
2059
+ /* @__PURE__ */ jsx14(Text14, { children: " " }),
2060
+ /* @__PURE__ */ jsxs11(Box14, { flexDirection: "column", gap: 1, children: [
2061
+ /* @__PURE__ */ jsx14(SectionRow, { sections: ROW1 }),
2062
+ /* @__PURE__ */ jsx14(SectionRow, { sections: ROW2 })
1805
2063
  ] }),
1806
- /* @__PURE__ */ jsx13(Text13, { children: " " }),
1807
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Press any key to close" })
2064
+ /* @__PURE__ */ jsx14(Text14, { children: " " }),
2065
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Press any key to close" })
1808
2066
  ]
1809
2067
  }
1810
2068
  );
1811
2069
  }
1812
2070
 
1813
2071
  // src/tui/components/ConfirmDialog.tsx
1814
- import { Box as Box14, Text as Text14 } from "ink";
1815
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
2072
+ import { Box as Box15, Text as Text15 } from "ink";
2073
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
1816
2074
  function ConfirmDialog({ task }) {
1817
- return /* @__PURE__ */ jsxs11(
1818
- Box14,
2075
+ return /* @__PURE__ */ jsxs12(
2076
+ Box15,
1819
2077
  {
1820
2078
  flexDirection: "column",
1821
2079
  borderStyle: "bold",
@@ -1824,17 +2082,17 @@ function ConfirmDialog({ task }) {
1824
2082
  paddingY: 1,
1825
2083
  alignSelf: "center",
1826
2084
  children: [
1827
- /* @__PURE__ */ jsx14(Text14, { color: theme.dialog.label, bold: true, children: "<Delete>" }),
1828
- /* @__PURE__ */ jsx14(Text14, { children: " " }),
1829
- /* @__PURE__ */ jsxs11(Text14, { color: theme.dialog.fg, children: [
2085
+ /* @__PURE__ */ jsx15(Text15, { color: theme.dialog.label, bold: true, children: "<Delete>" }),
2086
+ /* @__PURE__ */ jsx15(Text15, { children: " " }),
2087
+ /* @__PURE__ */ jsxs12(Text15, { color: theme.dialog.fg, children: [
1830
2088
  'Delete task "',
1831
2089
  task.name,
1832
2090
  '"?'
1833
2091
  ] }),
1834
- /* @__PURE__ */ jsx14(Text14, { children: " " }),
1835
- /* @__PURE__ */ jsxs11(Box14, { gap: 3, children: [
1836
- /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsx14(
1837
- Text14,
2092
+ /* @__PURE__ */ jsx15(Text15, { children: " " }),
2093
+ /* @__PURE__ */ jsxs12(Box15, { gap: 3, children: [
2094
+ /* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsx15(
2095
+ Text15,
1838
2096
  {
1839
2097
  backgroundColor: theme.dialog.buttonFocusBg,
1840
2098
  color: theme.dialog.buttonFocusFg,
@@ -1842,7 +2100,39 @@ function ConfirmDialog({ task }) {
1842
2100
  children: " y: OK "
1843
2101
  }
1844
2102
  ) }),
1845
- /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsx14(Text14, { backgroundColor: theme.dialog.buttonBg, color: theme.dialog.buttonFg, children: " n: Cancel " }) })
2103
+ /* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsx15(Text15, { backgroundColor: theme.dialog.buttonBg, color: theme.dialog.buttonFg, children: " n: Cancel " }) })
2104
+ ] })
2105
+ ]
2106
+ }
2107
+ );
2108
+ }
2109
+
2110
+ // src/tui/components/DetectedProjectDialog.tsx
2111
+ import { Box as Box16, Text as Text16 } from "ink";
2112
+ import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
2113
+ function DetectedProjectDialog({ remote }) {
2114
+ return /* @__PURE__ */ jsxs13(
2115
+ Box16,
2116
+ {
2117
+ flexDirection: "column",
2118
+ borderStyle: "bold",
2119
+ borderColor: theme.borderFocus,
2120
+ paddingX: 3,
2121
+ paddingY: 1,
2122
+ alignSelf: "center",
2123
+ children: [
2124
+ /* @__PURE__ */ jsx16(Text16, { color: theme.dialog.label, bold: true, children: "<New Repo Detected>" }),
2125
+ /* @__PURE__ */ jsx16(Text16, { children: " " }),
2126
+ /* @__PURE__ */ jsxs13(Text16, { color: theme.dialog.fg, children: [
2127
+ "Git remote: ",
2128
+ /* @__PURE__ */ jsx16(Text16, { bold: true, children: remote.value })
2129
+ ] }),
2130
+ /* @__PURE__ */ jsx16(Text16, { color: theme.dialog.fg, children: "No project is linked to this repo yet." }),
2131
+ /* @__PURE__ */ jsx16(Text16, { color: theme.dialog.fg, children: "Would you like to create one and link it?" }),
2132
+ /* @__PURE__ */ jsx16(Text16, { children: " " }),
2133
+ /* @__PURE__ */ jsxs13(Box16, { gap: 3, children: [
2134
+ /* @__PURE__ */ jsx16(Text16, { backgroundColor: theme.dialog.buttonFocusBg, color: theme.dialog.buttonFocusFg, bold: true, children: " y: Create " }),
2135
+ /* @__PURE__ */ jsx16(Text16, { backgroundColor: theme.dialog.buttonBg, color: theme.dialog.buttonFg, children: " n: Skip " })
1846
2136
  ] })
1847
2137
  ]
1848
2138
  }
@@ -1850,8 +2140,8 @@ function ConfirmDialog({ task }) {
1850
2140
  }
1851
2141
 
1852
2142
  // src/tui/components/DependencyList.tsx
1853
- import { Box as Box15, Text as Text15 } from "ink";
1854
- import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
2143
+ import { Box as Box17, Text as Text17 } from "ink";
2144
+ import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
1855
2145
  function TaskRow({
1856
2146
  task,
1857
2147
  globalIndex,
@@ -1859,16 +2149,16 @@ function TaskRow({
1859
2149
  }) {
1860
2150
  const isSelected = globalIndex === selectedIndex;
1861
2151
  const statusColor = STATUS_COLOR[task.status] ?? theme.table.fg;
1862
- return /* @__PURE__ */ jsx15(Box15, { children: isSelected ? /* @__PURE__ */ jsxs12(Text15, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
2152
+ return /* @__PURE__ */ jsx17(Box17, { children: isSelected ? /* @__PURE__ */ jsxs14(Text17, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
1863
2153
  "> ",
1864
2154
  task.id.padEnd(12),
1865
2155
  task.status.padEnd(14),
1866
2156
  task.name
1867
- ] }) : /* @__PURE__ */ jsxs12(Fragment2, { children: [
1868
- /* @__PURE__ */ jsx15(Text15, { children: " " }),
1869
- /* @__PURE__ */ jsx15(Text15, { color: theme.yaml.value, children: task.id.padEnd(12) }),
1870
- /* @__PURE__ */ jsx15(Text15, { color: statusColor, children: task.status.padEnd(14) }),
1871
- /* @__PURE__ */ jsx15(Text15, { color: theme.table.fg, children: task.name })
2157
+ ] }) : /* @__PURE__ */ jsxs14(Fragment5, { children: [
2158
+ /* @__PURE__ */ jsx17(Text17, { children: " " }),
2159
+ /* @__PURE__ */ jsx17(Text17, { color: theme.yaml.value, children: task.id.padEnd(12) }),
2160
+ /* @__PURE__ */ jsx17(Text17, { color: statusColor, children: task.status.padEnd(14) }),
2161
+ /* @__PURE__ */ jsx17(Text17, { color: theme.table.fg, children: task.name })
1872
2162
  ] }) }, task.id);
1873
2163
  }
1874
2164
  function DependencyList({
@@ -1889,23 +2179,23 @@ function DependencyList({
1889
2179
  const relatedOffset = offset;
1890
2180
  offset += related.length;
1891
2181
  const duplicatesOffset = offset;
1892
- return /* @__PURE__ */ jsxs12(Box15, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
1893
- /* @__PURE__ */ jsxs12(Box15, { gap: 0, children: [
1894
- /* @__PURE__ */ jsxs12(Text15, { color: theme.title, bold: true, children: [
2182
+ return /* @__PURE__ */ jsxs14(Box17, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
2183
+ /* @__PURE__ */ jsxs14(Box17, { gap: 0, children: [
2184
+ /* @__PURE__ */ jsxs14(Text17, { color: theme.title, bold: true, children: [
1895
2185
  " ",
1896
2186
  "dependencies"
1897
2187
  ] }),
1898
- /* @__PURE__ */ jsx15(Text15, { color: theme.fg, children: "(" }),
1899
- /* @__PURE__ */ jsx15(Text15, { color: theme.titleHighlight, bold: true, children: task.name }),
1900
- /* @__PURE__ */ jsx15(Text15, { color: theme.fg, children: ")" })
2188
+ /* @__PURE__ */ jsx17(Text17, { color: theme.fg, children: "(" }),
2189
+ /* @__PURE__ */ jsx17(Text17, { color: theme.titleHighlight, bold: true, children: task.name }),
2190
+ /* @__PURE__ */ jsx17(Text17, { color: theme.fg, children: ")" })
1901
2191
  ] }),
1902
- /* @__PURE__ */ jsxs12(Box15, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
1903
- /* @__PURE__ */ jsxs12(Text15, { color: theme.table.headerFg, bold: true, children: [
2192
+ /* @__PURE__ */ jsxs14(Box17, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
2193
+ /* @__PURE__ */ jsxs14(Text17, { color: theme.table.headerFg, bold: true, children: [
1904
2194
  "BLOCKED BY (",
1905
2195
  blockers.length,
1906
2196
  ")"
1907
2197
  ] }),
1908
- blockers.length === 0 ? /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: " No blockers" }) : blockers.map((t, i) => /* @__PURE__ */ jsx15(
2198
+ blockers.length === 0 ? /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: " No blockers" }) : blockers.map((t, i) => /* @__PURE__ */ jsx17(
1909
2199
  TaskRow,
1910
2200
  {
1911
2201
  task: t,
@@ -1915,13 +2205,13 @@ function DependencyList({
1915
2205
  t.id
1916
2206
  ))
1917
2207
  ] }),
1918
- /* @__PURE__ */ jsxs12(Box15, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
1919
- /* @__PURE__ */ jsxs12(Text15, { color: theme.table.headerFg, bold: true, children: [
2208
+ /* @__PURE__ */ jsxs14(Box17, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
2209
+ /* @__PURE__ */ jsxs14(Text17, { color: theme.table.headerFg, bold: true, children: [
1920
2210
  "BLOCKS (",
1921
2211
  dependents.length,
1922
2212
  ")"
1923
2213
  ] }),
1924
- dependents.length === 0 ? /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: " No dependents" }) : dependents.map((t, i) => /* @__PURE__ */ jsx15(
2214
+ dependents.length === 0 ? /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: " No dependents" }) : dependents.map((t, i) => /* @__PURE__ */ jsx17(
1925
2215
  TaskRow,
1926
2216
  {
1927
2217
  task: t,
@@ -1931,13 +2221,13 @@ function DependencyList({
1931
2221
  t.id
1932
2222
  ))
1933
2223
  ] }),
1934
- /* @__PURE__ */ jsxs12(Box15, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
1935
- /* @__PURE__ */ jsxs12(Text15, { color: theme.table.headerFg, bold: true, children: [
2224
+ /* @__PURE__ */ jsxs14(Box17, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
2225
+ /* @__PURE__ */ jsxs14(Text17, { color: theme.table.headerFg, bold: true, children: [
1936
2226
  "RELATES TO (",
1937
2227
  related.length,
1938
2228
  ")"
1939
2229
  ] }),
1940
- related.length === 0 ? /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: " No related tasks" }) : related.map((t, i) => /* @__PURE__ */ jsx15(
2230
+ related.length === 0 ? /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: " No related tasks" }) : related.map((t, i) => /* @__PURE__ */ jsx17(
1941
2231
  TaskRow,
1942
2232
  {
1943
2233
  task: t,
@@ -1947,13 +2237,13 @@ function DependencyList({
1947
2237
  t.id
1948
2238
  ))
1949
2239
  ] }),
1950
- /* @__PURE__ */ jsxs12(Box15, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
1951
- /* @__PURE__ */ jsxs12(Text15, { color: theme.table.headerFg, bold: true, children: [
2240
+ /* @__PURE__ */ jsxs14(Box17, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
2241
+ /* @__PURE__ */ jsxs14(Text17, { color: theme.table.headerFg, bold: true, children: [
1952
2242
  "DUPLICATES (",
1953
2243
  duplicates.length,
1954
2244
  ")"
1955
2245
  ] }),
1956
- duplicates.length === 0 ? /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: " No duplicate tasks" }) : duplicates.map((t, i) => /* @__PURE__ */ jsx15(
2246
+ duplicates.length === 0 ? /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: " No duplicate tasks" }) : duplicates.map((t, i) => /* @__PURE__ */ jsx17(
1957
2247
  TaskRow,
1958
2248
  {
1959
2249
  task: t,
@@ -1963,19 +2253,19 @@ function DependencyList({
1963
2253
  t.id
1964
2254
  ))
1965
2255
  ] }),
1966
- /* @__PURE__ */ jsx15(Box15, { flexGrow: 1 }),
1967
- isAddingDep && /* @__PURE__ */ jsxs12(Box15, { borderStyle: "round", borderColor: theme.prompt, paddingX: 1, children: [
1968
- /* @__PURE__ */ jsx15(Text15, { color: theme.prompt, children: "depends on (id or id:type): " }),
1969
- /* @__PURE__ */ jsx15(Text15, { color: theme.prompt, children: addDepInput }),
1970
- /* @__PURE__ */ jsx15(Text15, { color: theme.promptSuggest, children: "_" })
2256
+ /* @__PURE__ */ jsx17(Box17, { flexGrow: 1 }),
2257
+ isAddingDep && /* @__PURE__ */ jsxs14(Box17, { borderStyle: "round", borderColor: theme.prompt, paddingX: 1, children: [
2258
+ /* @__PURE__ */ jsx17(Text17, { color: theme.prompt, children: "depends on (id or id:type): " }),
2259
+ /* @__PURE__ */ jsx17(Text17, { color: theme.prompt, children: addDepInput }),
2260
+ /* @__PURE__ */ jsx17(Text17, { color: theme.promptSuggest, children: "_" })
1971
2261
  ] }),
1972
- /* @__PURE__ */ jsx15(Box15, { paddingX: 1, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "a: add dep (id or id:relates-to) | x: remove selected | enter: go to task | esc: back" }) })
2262
+ /* @__PURE__ */ jsx17(Box17, { paddingX: 1, children: /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "a: add dep (id or id:relates-to) | x: remove selected | enter: go to task | esc: back" }) })
1973
2263
  ] });
1974
2264
  }
1975
2265
 
1976
2266
  // src/tui/components/EpicPanel.tsx
1977
- import { Box as Box16, Text as Text16 } from "ink";
1978
- import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
2267
+ import { Box as Box18, Text as Text18 } from "ink";
2268
+ import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
1979
2269
  var PAGE_SIZE2 = 20;
1980
2270
  function EpicPanel({
1981
2271
  epics,
@@ -1988,34 +2278,34 @@ function EpicPanel({
1988
2278
  const currentPage = Math.floor(selectedIndex / PAGE_SIZE2);
1989
2279
  const viewStart = currentPage * PAGE_SIZE2;
1990
2280
  const visibleEpics = epics.slice(viewStart, viewStart + PAGE_SIZE2);
1991
- return /* @__PURE__ */ jsxs13(
1992
- Box16,
2281
+ return /* @__PURE__ */ jsxs15(
2282
+ Box18,
1993
2283
  {
1994
2284
  flexDirection: "column",
1995
2285
  width: 48,
1996
2286
  borderStyle: "bold",
1997
2287
  borderColor: isFocused ? theme.borderFocus : theme.border,
1998
2288
  children: [
1999
- /* @__PURE__ */ jsxs13(Box16, { children: [
2000
- /* @__PURE__ */ jsxs13(Text16, { color: theme.title, bold: true, children: [
2289
+ /* @__PURE__ */ jsxs15(Box18, { children: [
2290
+ /* @__PURE__ */ jsxs15(Text18, { color: theme.title, bold: true, children: [
2001
2291
  " ",
2002
2292
  "epics"
2003
2293
  ] }),
2004
- /* @__PURE__ */ jsxs13(Text16, { color: theme.titleCounter, bold: true, children: [
2294
+ /* @__PURE__ */ jsxs15(Text18, { color: theme.titleCounter, bold: true, children: [
2005
2295
  "[",
2006
2296
  epics.length,
2007
2297
  "]"
2008
2298
  ] }),
2009
- isReordering && /* @__PURE__ */ jsxs13(Text16, { color: theme.flash.warn, bold: true, children: [
2299
+ isReordering && /* @__PURE__ */ jsxs15(Text18, { color: theme.flash.warn, bold: true, children: [
2010
2300
  " ",
2011
2301
  "REORDER"
2012
2302
  ] }),
2013
- filterActive && /* @__PURE__ */ jsxs13(Text16, { color: theme.titleFilter, children: [
2303
+ filterActive && /* @__PURE__ */ jsxs15(Text18, { color: theme.titleFilter, children: [
2014
2304
  " *",
2015
2305
  selectedEpicIds.size
2016
2306
  ] })
2017
2307
  ] }),
2018
- /* @__PURE__ */ jsx16(Box16, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: epics.length === 0 ? /* @__PURE__ */ jsx16(Box16, { paddingX: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "No epics" }) }) : visibleEpics.map((epic, i) => {
2308
+ /* @__PURE__ */ jsx18(Box18, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: epics.length === 0 ? /* @__PURE__ */ jsx18(Box18, { paddingX: 1, children: /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "No epics" }) }) : visibleEpics.map((epic, i) => {
2019
2309
  const actualIndex = viewStart + i;
2020
2310
  const isSelected = actualIndex === selectedIndex && isFocused;
2021
2311
  const isChecked = selectedEpicIds.has(epic.id);
@@ -2023,21 +2313,21 @@ function EpicPanel({
2023
2313
  const statusColor = STATUS_COLOR[epic.status] ?? theme.table.fg;
2024
2314
  if (isSelected) {
2025
2315
  const cursorBg = isReordering ? theme.flash.warn : theme.table.cursorBg;
2026
- return /* @__PURE__ */ jsx16(Box16, { children: /* @__PURE__ */ jsxs13(Text16, { backgroundColor: cursorBg, color: theme.table.cursorFg, bold: true, children: [
2316
+ return /* @__PURE__ */ jsx18(Box18, { children: /* @__PURE__ */ jsxs15(Text18, { backgroundColor: cursorBg, color: theme.table.cursorFg, bold: true, children: [
2027
2317
  isReordering ? "~ " : " ",
2028
2318
  marker,
2029
2319
  " ",
2030
2320
  epic.name
2031
2321
  ] }) }, epic.id);
2032
2322
  }
2033
- return /* @__PURE__ */ jsx16(Box16, { children: /* @__PURE__ */ jsxs13(Text16, { color: isChecked ? theme.titleHighlight : statusColor, children: [
2323
+ return /* @__PURE__ */ jsx18(Box18, { children: /* @__PURE__ */ jsxs15(Text18, { color: isChecked ? theme.titleHighlight : statusColor, children: [
2034
2324
  " ",
2035
2325
  marker,
2036
2326
  " ",
2037
2327
  epic.name
2038
2328
  ] }) }, epic.id);
2039
2329
  }) }),
2040
- epics.length > PAGE_SIZE2 && /* @__PURE__ */ jsx16(Box16, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs13(Text16, { dimColor: true, children: [
2330
+ epics.length > PAGE_SIZE2 && /* @__PURE__ */ jsx18(Box18, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs15(Text18, { dimColor: true, children: [
2041
2331
  "[",
2042
2332
  viewStart + 1,
2043
2333
  "-",
@@ -2052,16 +2342,16 @@ function EpicPanel({
2052
2342
  }
2053
2343
 
2054
2344
  // src/tui/components/EpicPicker.tsx
2055
- import { useState as useState6 } from "react";
2056
- import { Box as Box17, Text as Text17, useInput as useInput6, useStdout as useStdout3 } from "ink";
2057
- import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
2345
+ import { useState as useState7 } from "react";
2346
+ import { Box as Box19, Text as Text19, useInput as useInput6, useStdout as useStdout3 } from "ink";
2347
+ import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
2058
2348
  function EpicPicker({ epics, currentEpicId, onSelect, onCancel }) {
2059
2349
  const { stdout } = useStdout3();
2060
2350
  const termHeight = stdout.rows > 0 ? stdout.rows : 24;
2061
2351
  const maxVisible = Math.max(3, termHeight - 10);
2062
- const [searchQuery, setSearchQuery] = useState6("");
2063
- const [isSearching, setIsSearching] = useState6(false);
2064
- const [cursorIndex, setCursorIndex] = useState6(0);
2352
+ const [searchQuery, setSearchQuery] = useState7("");
2353
+ const [isSearching, setIsSearching] = useState7(false);
2354
+ const [cursorIndex, setCursorIndex] = useState7(0);
2065
2355
  const filtered = epics.filter((e) => {
2066
2356
  if (!searchQuery.trim()) return true;
2067
2357
  const q = searchQuery.toLowerCase();
@@ -2127,53 +2417,53 @@ function EpicPicker({ epics, currentEpicId, onSelect, onCancel }) {
2127
2417
  return;
2128
2418
  }
2129
2419
  });
2130
- return /* @__PURE__ */ jsxs14(Box17, { flexDirection: "column", borderStyle: "bold", borderColor: theme.borderFocus, flexGrow: 1, children: [
2131
- /* @__PURE__ */ jsxs14(Box17, { gap: 0, children: [
2132
- /* @__PURE__ */ jsxs14(Text17, { color: theme.title, bold: true, children: [
2420
+ return /* @__PURE__ */ jsxs16(Box19, { flexDirection: "column", borderStyle: "bold", borderColor: theme.borderFocus, flexGrow: 1, children: [
2421
+ /* @__PURE__ */ jsxs16(Box19, { gap: 0, children: [
2422
+ /* @__PURE__ */ jsxs16(Text19, { color: theme.title, bold: true, children: [
2133
2423
  " ",
2134
2424
  "assign to epic"
2135
2425
  ] }),
2136
- /* @__PURE__ */ jsxs14(Text17, { color: theme.titleCounter, bold: true, children: [
2426
+ /* @__PURE__ */ jsxs16(Text19, { color: theme.titleCounter, bold: true, children: [
2137
2427
  " ",
2138
2428
  "[",
2139
2429
  epics.length,
2140
2430
  "]"
2141
2431
  ] })
2142
2432
  ] }),
2143
- isSearching ? /* @__PURE__ */ jsxs14(Box17, { borderStyle: "round", borderColor: theme.prompt, paddingX: 1, children: [
2144
- /* @__PURE__ */ jsx17(Text17, { color: theme.prompt, children: "/" }),
2145
- /* @__PURE__ */ jsx17(Text17, { color: theme.prompt, children: searchQuery }),
2146
- /* @__PURE__ */ jsx17(Text17, { color: theme.promptSuggest, children: "_" })
2147
- ] }) : searchQuery ? /* @__PURE__ */ jsx17(Box17, { paddingX: 1, children: /* @__PURE__ */ jsxs14(Text17, { color: theme.titleFilter, children: [
2433
+ isSearching ? /* @__PURE__ */ jsxs16(Box19, { borderStyle: "round", borderColor: theme.prompt, paddingX: 1, children: [
2434
+ /* @__PURE__ */ jsx19(Text19, { color: theme.prompt, children: "/" }),
2435
+ /* @__PURE__ */ jsx19(Text19, { color: theme.prompt, children: searchQuery }),
2436
+ /* @__PURE__ */ jsx19(Text19, { color: theme.promptSuggest, children: "_" })
2437
+ ] }) : searchQuery ? /* @__PURE__ */ jsx19(Box19, { paddingX: 1, children: /* @__PURE__ */ jsxs16(Text19, { color: theme.titleFilter, children: [
2148
2438
  "/",
2149
2439
  searchQuery
2150
2440
  ] }) }) : null,
2151
- /* @__PURE__ */ jsx17(Box17, { paddingX: 1, children: /* @__PURE__ */ jsxs14(Text17, { color: theme.table.headerFg, bold: true, children: [
2441
+ /* @__PURE__ */ jsx19(Box19, { paddingX: 1, children: /* @__PURE__ */ jsxs16(Text19, { color: theme.table.headerFg, bold: true, children: [
2152
2442
  " ",
2153
2443
  "ID".padEnd(14),
2154
2444
  "STATUS".padEnd(14),
2155
2445
  "NAME"
2156
2446
  ] }) }),
2157
- filtered.length === 0 ? /* @__PURE__ */ jsx17(Box17, { paddingX: 2, paddingY: 1, children: /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "No epics match the filter" }) }) : visible.map((epic, i) => {
2447
+ filtered.length === 0 ? /* @__PURE__ */ jsx19(Box19, { paddingX: 2, paddingY: 1, children: /* @__PURE__ */ jsx19(Text19, { dimColor: true, children: "No epics match the filter" }) }) : visible.map((epic, i) => {
2158
2448
  const actualIndex = viewStart + i;
2159
2449
  const isCursor = actualIndex === cursorIndex;
2160
2450
  const isCurrent = epic.id === currentEpicId;
2161
2451
  const marker = isCurrent ? "* " : " ";
2162
2452
  const statusColor = STATUS_COLOR[epic.status] ?? theme.table.fg;
2163
- return /* @__PURE__ */ jsx17(Box17, { paddingX: 1, children: isCursor ? /* @__PURE__ */ jsxs14(Text17, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
2453
+ return /* @__PURE__ */ jsx19(Box19, { paddingX: 1, children: isCursor ? /* @__PURE__ */ jsxs16(Text19, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
2164
2454
  "> ",
2165
2455
  epic.id.padEnd(14),
2166
2456
  epic.status.padEnd(14),
2167
2457
  epic.name
2168
- ] }) : /* @__PURE__ */ jsxs14(Fragment3, { children: [
2169
- /* @__PURE__ */ jsx17(Text17, { color: isCurrent ? theme.titleHighlight : theme.table.fg, children: marker }),
2170
- /* @__PURE__ */ jsx17(Text17, { color: theme.yaml.value, children: epic.id.padEnd(14) }),
2171
- /* @__PURE__ */ jsx17(Text17, { color: statusColor, children: epic.status.padEnd(14) }),
2172
- /* @__PURE__ */ jsx17(Text17, { color: isCurrent ? theme.titleHighlight : theme.table.fg, children: epic.name })
2458
+ ] }) : /* @__PURE__ */ jsxs16(Fragment6, { children: [
2459
+ /* @__PURE__ */ jsx19(Text19, { color: isCurrent ? theme.titleHighlight : theme.table.fg, children: marker }),
2460
+ /* @__PURE__ */ jsx19(Text19, { color: theme.yaml.value, children: epic.id.padEnd(14) }),
2461
+ /* @__PURE__ */ jsx19(Text19, { color: statusColor, children: epic.status.padEnd(14) }),
2462
+ /* @__PURE__ */ jsx19(Text19, { color: isCurrent ? theme.titleHighlight : theme.table.fg, children: epic.name })
2173
2463
  ] }) }, epic.id);
2174
2464
  }),
2175
- /* @__PURE__ */ jsx17(Box17, { flexGrow: 1 }),
2176
- filtered.length > maxVisible && /* @__PURE__ */ jsx17(Box17, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs14(Text17, { dimColor: true, children: [
2465
+ /* @__PURE__ */ jsx19(Box19, { flexGrow: 1 }),
2466
+ filtered.length > maxVisible && /* @__PURE__ */ jsx19(Box19, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs16(Text19, { dimColor: true, children: [
2177
2467
  "[",
2178
2468
  viewStart + 1,
2179
2469
  "-",
@@ -2182,19 +2472,52 @@ function EpicPicker({ epics, currentEpicId, onSelect, onCancel }) {
2182
2472
  filtered.length,
2183
2473
  "]"
2184
2474
  ] }) }),
2185
- /* @__PURE__ */ jsx17(Box17, { paddingX: 1, children: /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "enter: assign | x: unassign | /: search | esc: cancel" }) })
2475
+ /* @__PURE__ */ jsx19(Box19, { paddingX: 1, children: /* @__PURE__ */ jsx19(Text19, { dimColor: true, children: "enter: assign | x: unassign | /: search | esc: cancel" }) })
2186
2476
  ] });
2187
2477
  }
2188
2478
 
2479
+ // src/tui/components/ChangelogBanner.tsx
2480
+ import { Box as Box20, Text as Text20 } from "ink";
2481
+ import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
2482
+ function ChangelogBanner({ entries, currentIndex }) {
2483
+ const entry = entries[currentIndex];
2484
+ if (!entry) return null;
2485
+ const total = entries.length;
2486
+ return /* @__PURE__ */ jsxs17(
2487
+ Box20,
2488
+ {
2489
+ flexDirection: "column",
2490
+ borderStyle: "bold",
2491
+ borderColor: theme.borderFocus,
2492
+ paddingX: 3,
2493
+ paddingY: 1,
2494
+ gap: 1,
2495
+ alignSelf: "center",
2496
+ children: [
2497
+ /* @__PURE__ */ jsx20(Text20, { color: theme.dialog.label, bold: true, children: `<What's New \u2014 v${entry.version}${entry.date ? ` (${entry.date})` : ""}>` }),
2498
+ entry.sections.map((section) => /* @__PURE__ */ jsxs17(Box20, { flexDirection: "column", children: [
2499
+ /* @__PURE__ */ jsx20(Text20, { color: theme.table.headerFg, bold: true, children: `### ${section.heading}` }),
2500
+ section.items.map((item, i) => /* @__PURE__ */ jsx20(Text20, { color: theme.dialog.fg, children: `- ${item}` }, `${section.heading}-${i}`))
2501
+ ] }, section.heading)),
2502
+ /* @__PURE__ */ jsxs17(Box20, { gap: 2, children: [
2503
+ /* @__PURE__ */ jsx20(Text20, { color: theme.fg, children: `[${currentIndex + 1}/${total}]` }),
2504
+ /* @__PURE__ */ jsx20(Text20, { color: theme.menu.desc, children: "\u2191/\u2193: navigate" }),
2505
+ /* @__PURE__ */ jsx20(Text20, { color: theme.menu.desc, children: "esc: close" })
2506
+ ] })
2507
+ ]
2508
+ }
2509
+ );
2510
+ }
2511
+
2189
2512
  // src/tui/useAutoRefetch.ts
2190
- import { useEffect, useRef } from "react";
2513
+ import { useEffect as useEffect4, useRef as useRef4 } from "react";
2191
2514
  import { watchFile, unwatchFile } from "fs";
2192
2515
  var POLL_INTERVAL_MS = 1e3;
2193
2516
  var DEBOUNCE_MS = 200;
2194
2517
  function useAutoRefetch(dbPath, onRefetch) {
2195
- const callbackRef = useRef(onRefetch);
2518
+ const callbackRef = useRef4(onRefetch);
2196
2519
  callbackRef.current = onRefetch;
2197
- useEffect(() => {
2520
+ useEffect4(() => {
2198
2521
  let debounceTimer = null;
2199
2522
  const handleChange = () => {
2200
2523
  if (debounceTimer) clearTimeout(debounceTimer);
@@ -2221,8 +2544,47 @@ function useAutoRefetch(dbPath, onRefetch) {
2221
2544
  }, [dbPath]);
2222
2545
  }
2223
2546
 
2547
+ // src/changelog.ts
2548
+ var CHANGELOG_ENTRIES = typeof define_CHANGELOG_ENTRIES_default !== "undefined" ? define_CHANGELOG_ENTRIES_default : [];
2549
+
2550
+ // src/utils/changelog-seen.ts
2551
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
2552
+ function isValidSeenCache(value) {
2553
+ if (typeof value !== "object" || value === null) return false;
2554
+ return typeof value["changelogSeenVersion"] === "string";
2555
+ }
2556
+ function readSeenVersion(cachePath) {
2557
+ try {
2558
+ const raw = readFileSync3(cachePath, "utf-8");
2559
+ const parsed = JSON.parse(raw);
2560
+ if (isValidSeenCache(parsed)) return parsed.changelogSeenVersion;
2561
+ return null;
2562
+ } catch {
2563
+ logger.info("changelog-seen: cache not available", { path: cachePath });
2564
+ return null;
2565
+ }
2566
+ }
2567
+ function writeSeenVersion(cachePath, version) {
2568
+ try {
2569
+ let existing = {};
2570
+ try {
2571
+ const raw = readFileSync3(cachePath, "utf-8");
2572
+ const parsed = JSON.parse(raw);
2573
+ if (typeof parsed === "object" && parsed !== null) {
2574
+ existing = parsed;
2575
+ }
2576
+ } catch {
2577
+ }
2578
+ existing["changelogSeenVersion"] = version;
2579
+ writeFileSync4(cachePath, JSON.stringify(existing), "utf-8");
2580
+ logger.info("changelog-seen: persisted seen version", { version, path: cachePath });
2581
+ } catch {
2582
+ logger.warn("changelog-seen: failed to write seen version", { path: cachePath });
2583
+ }
2584
+ }
2585
+
2224
2586
  // src/tui/components/App.tsx
2225
- import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
2587
+ import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
2226
2588
  var STATUS_CYCLE = [
2227
2589
  TaskStatus.Backlog,
2228
2590
  TaskStatus.Todo,
@@ -2235,8 +2597,8 @@ function App({ container, initialProject, latestVersion }) {
2235
2597
  const { exit } = useApp();
2236
2598
  const { stdout } = useStdout4();
2237
2599
  const [state, dispatch] = useReducer(appReducer, initialState);
2238
- const [, setResizeTick] = useState7(0);
2239
- useEffect2(() => {
2600
+ const [, setResizeTick] = useState8(0);
2601
+ useEffect5(() => {
2240
2602
  const onResize = () => {
2241
2603
  setResizeTick((n) => n + 1);
2242
2604
  };
@@ -2401,10 +2763,10 @@ function App({ container, initialProject, latestVersion }) {
2401
2763
  loadEpics();
2402
2764
  }, [loadProjects, loadTasks, loadEpics]);
2403
2765
  useAutoRefetch(container.dbPath, refetchAll);
2404
- useEffect2(() => {
2766
+ useEffect5(() => {
2405
2767
  loadProjects();
2406
2768
  }, [loadProjects]);
2407
- useEffect2(() => {
2769
+ useEffect5(() => {
2408
2770
  if (state.projects.length > 0 && !state.activeProject) {
2409
2771
  logger.info(`TUI.resolveProject: resolving initialProject=${initialProject ?? "(default)"}`);
2410
2772
  const result = container.projectService.resolveProject(initialProject);
@@ -2421,13 +2783,47 @@ function App({ container, initialProject, latestVersion }) {
2421
2783
  }
2422
2784
  }
2423
2785
  }, [state.projects, state.activeProject, initialProject, container]);
2424
- useEffect2(() => {
2786
+ const gitRemoteCheckedRef = useRef5(false);
2787
+ useEffect5(() => {
2788
+ if (state.projects.length > 0 && !gitRemoteCheckedRef.current && !initialProject) {
2789
+ gitRemoteCheckedRef.current = true;
2790
+ const remoteResult = detectGitRemote();
2791
+ if (remoteResult.ok && remoteResult.value) {
2792
+ const remote = remoteResult.value;
2793
+ const alreadyLinked = state.projects.some((p) => p.gitRemote?.equals(remote));
2794
+ const dismissed = isDismissedRemote(container.dismissedGitRemotesPath, remote);
2795
+ if (!alreadyLinked && !dismissed) {
2796
+ logger.info(`TUI.detectGitRemote: unlinked remote detected: ${remote.value}`);
2797
+ dispatch({ type: "SET_DETECTED_GIT_REMOTE", remote });
2798
+ }
2799
+ }
2800
+ }
2801
+ }, [state.projects, initialProject]);
2802
+ const changelogCheckedRef = useRef5(false);
2803
+ useEffect5(() => {
2804
+ if (changelogCheckedRef.current) return;
2805
+ changelogCheckedRef.current = true;
2806
+ const seenVersion = readSeenVersion(container.updateCachePath);
2807
+ if (seenVersion !== APP_VERSION) {
2808
+ const newEntries = CHANGELOG_ENTRIES.filter(
2809
+ (e) => isNewerVersion(e.version, seenVersion ?? "0.0.0") && !isNewerVersion(e.version, APP_VERSION)
2810
+ );
2811
+ if (newEntries.length > 0) {
2812
+ logger.info(
2813
+ `TUI.changelog: showing ticker for [${newEntries.map((e) => e.version).join(", ")}] (last seen: ${seenVersion ?? "none"})`
2814
+ );
2815
+ dispatch({ type: "SET_CHANGELOG", entries: newEntries });
2816
+ writeSeenVersion(container.updateCachePath, APP_VERSION);
2817
+ }
2818
+ }
2819
+ }, [container.updateCachePath]);
2820
+ useEffect5(() => {
2425
2821
  if (state.activeProject) {
2426
2822
  loadTasks();
2427
2823
  loadEpics();
2428
2824
  }
2429
2825
  }, [state.activeProject, state.filter, loadTasks, loadEpics]);
2430
- useEffect2(() => {
2826
+ useEffect5(() => {
2431
2827
  if (state.flash) {
2432
2828
  const timer = setTimeout(() => {
2433
2829
  dispatch({ type: "CLEAR_FLASH" });
@@ -2439,6 +2835,21 @@ function App({ container, initialProject, latestVersion }) {
2439
2835
  return void 0;
2440
2836
  }, [state.flash]);
2441
2837
  useInput7((input, key) => {
2838
+ if (state.changelogDialogOpen) {
2839
+ if (key.upArrow || input === "k") {
2840
+ dispatch({ type: "CHANGELOG_NAVIGATE", direction: "up" });
2841
+ return;
2842
+ }
2843
+ if (key.downArrow || input === "j") {
2844
+ dispatch({ type: "CHANGELOG_NAVIGATE", direction: "down" });
2845
+ return;
2846
+ }
2847
+ if (key.escape || input === "q" || input === "W") {
2848
+ dispatch({ type: "CLOSE_CHANGELOG_DIALOG" });
2849
+ return;
2850
+ }
2851
+ return;
2852
+ }
2442
2853
  if (state.confirmDelete) {
2443
2854
  if (input === "y") {
2444
2855
  const result = container.taskService.deleteTask(state.confirmDelete.id);
@@ -2459,11 +2870,20 @@ function App({ container, initialProject, latestVersion }) {
2459
2870
  }
2460
2871
  return;
2461
2872
  }
2873
+ if (state.detectedGitRemote && state.activeView === ViewType.TaskList) {
2874
+ if (input === "y") {
2875
+ dispatch({ type: "NAVIGATE_TO", view: ViewType.ProjectCreate });
2876
+ } else if (input === "n" || key.escape) {
2877
+ dismissRemote(container.dismissedGitRemotesPath, state.detectedGitRemote);
2878
+ dispatch({ type: "SET_DETECTED_GIT_REMOTE", remote: null });
2879
+ }
2880
+ return;
2881
+ }
2462
2882
  if (state.activeView === ViewType.Help) {
2463
2883
  dispatch({ type: "GO_BACK" });
2464
2884
  return;
2465
2885
  }
2466
- 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) {
2886
+ if (state.activeView === ViewType.TaskCreate || state.activeView === ViewType.TaskEdit || state.activeView === ViewType.ProjectSelector || state.activeView === ViewType.ProjectCreate || state.activeView === ViewType.ProjectEdit || state.activeView === ViewType.ProjectLink || state.activeView === ViewType.EpicPicker) {
2467
2887
  return;
2468
2888
  }
2469
2889
  if (state.activeView === ViewType.DependencyList && state.isAddingDep) {
@@ -2791,6 +3211,10 @@ function App({ container, initialProject, latestVersion }) {
2791
3211
  dispatch({ type: "CLEAR_FILTER" });
2792
3212
  return;
2793
3213
  }
3214
+ if (input === "W" && state.changelogEntries) {
3215
+ dispatch({ type: "OPEN_CHANGELOG_DIALOG" });
3216
+ return;
3217
+ }
2794
3218
  }
2795
3219
  if (state.activeView === ViewType.TaskList && state.focusedPanel === "detail" && previewTask) {
2796
3220
  if (key.upArrow || input === "k") {
@@ -3037,35 +3461,57 @@ ${state.selectedTask.additionalRequirements}`;
3037
3461
  const handleProjectCreate = useCallback2(() => {
3038
3462
  dispatch({ type: "NAVIGATE_TO", view: ViewType.ProjectCreate });
3039
3463
  }, []);
3464
+ const handleProjectEdit = useCallback2((project) => {
3465
+ dispatch({ type: "SET_EDITING_PROJECT", project });
3466
+ dispatch({ type: "NAVIGATE_TO", view: ViewType.ProjectEdit });
3467
+ }, []);
3040
3468
  const handleProjectFormSave = useCallback2(
3041
3469
  (data) => {
3042
- const result = container.projectService.createProject({
3470
+ const editing = state.activeView === ViewType.ProjectEdit ? state.editingProject : null;
3471
+ const result = editing ? container.projectService.updateProject(editing.id, {
3472
+ name: data.name,
3473
+ description: data.description,
3474
+ isDefault: data.isDefault,
3475
+ gitRemote: data.gitRemote || null
3476
+ }) : container.projectService.createProject({
3043
3477
  name: data.name,
3044
3478
  key: data.key || void 0,
3045
3479
  description: data.description || void 0,
3046
- isDefault: data.isDefault
3480
+ isDefault: data.isDefault,
3481
+ gitRemote: data.gitRemote || void 0
3047
3482
  });
3048
3483
  if (result.ok) {
3049
- logger.info(`TUI.createProject: created key=${result.value.key} name=${result.value.name}`);
3484
+ const verb = editing ? "updated" : "created";
3485
+ logger.info(
3486
+ `TUI.${editing ? "editProject" : "createProject"}: ${verb} key=${result.value.key} name=${result.value.name}`
3487
+ );
3050
3488
  dispatch({
3051
3489
  type: "FLASH",
3052
- message: `Project created: ${result.value.name}`,
3490
+ message: `Project ${verb}: ${result.value.name}`,
3053
3491
  level: "info"
3054
3492
  });
3055
- dispatch({ type: "SET_ACTIVE_PROJECT", project: result.value });
3056
- dispatch({ type: "GO_BACK" });
3493
+ if (editing && state.activeProject?.id === result.value.id) {
3494
+ dispatch({ type: "SET_ACTIVE_PROJECT", project: result.value });
3495
+ }
3496
+ if (!editing) {
3497
+ dispatch({ type: "SET_ACTIVE_PROJECT", project: result.value });
3498
+ dispatch({ type: "GO_BACK" });
3499
+ }
3057
3500
  dispatch({ type: "GO_BACK" });
3058
3501
  loadProjects();
3059
3502
  } else {
3060
- logger.error("TUI.createProject: failed", result.error);
3503
+ logger.error(`TUI.${editing ? "editProject" : "createProject"}: failed`, result.error);
3061
3504
  dispatch({ type: "FLASH", message: result.error.message, level: "error" });
3062
3505
  }
3063
3506
  },
3064
- [container, loadProjects]
3507
+ [container, state.activeView, state.editingProject, state.activeProject, loadProjects]
3065
3508
  );
3066
3509
  const handleProjectFormCancel = useCallback2(() => {
3510
+ if (state.activeView === ViewType.ProjectCreate && state.detectedGitRemote) {
3511
+ dismissRemote(container.dismissedGitRemotesPath, state.detectedGitRemote);
3512
+ }
3067
3513
  dispatch({ type: "GO_BACK" });
3068
- }, []);
3514
+ }, [state.activeView, state.detectedGitRemote, container]);
3069
3515
  const handleProjectLink = useCallback2((project) => {
3070
3516
  dispatch({ type: "SET_LINKING_PROJECT", project });
3071
3517
  dispatch({ type: "NAVIGATE_TO", view: ViewType.ProjectLink });
@@ -3076,11 +3522,11 @@ ${state.selectedTask.additionalRequirements}`;
3076
3522
  const result = container.projectService.linkGitRemote(state.linkingProject.id, remote);
3077
3523
  if (result.ok) {
3078
3524
  logger.info(
3079
- `TUI.linkGitRemote: linked project=${state.linkingProject.id} remote=${result.value.gitRemote}`
3525
+ `TUI.linkGitRemote: linked project=${state.linkingProject.id} remote=${result.value.gitRemote?.value}`
3080
3526
  );
3081
3527
  dispatch({
3082
3528
  type: "FLASH",
3083
- message: `Linked to: ${result.value.gitRemote}`,
3529
+ message: `Linked to: ${result.value.gitRemote?.value}`,
3084
3530
  level: "info"
3085
3531
  });
3086
3532
  dispatch({ type: "GO_BACK" });
@@ -3108,8 +3554,8 @@ ${state.selectedTask.additionalRequirements}`;
3108
3554
  const handleLinkDetect = useCallback2(() => {
3109
3555
  const result = detectGitRemote();
3110
3556
  if (result.ok && result.value) {
3111
- dispatch({ type: "FLASH", message: `Detected: ${result.value}`, level: "info" });
3112
- return result.value;
3557
+ dispatch({ type: "FLASH", message: `Detected: ${result.value.value}`, level: "info" });
3558
+ return result.value.value;
3113
3559
  }
3114
3560
  dispatch({ type: "FLASH", message: "No git remote detected in cwd", level: "warn" });
3115
3561
  return null;
@@ -3121,7 +3567,7 @@ ${state.selectedTask.additionalRequirements}`;
3121
3567
  dispatch({ type: "GO_BACK" });
3122
3568
  }, []);
3123
3569
  const previewTask = state.tasks[state.selectedIndex] ?? null;
3124
- const initialDepsForEdit = useMemo2(() => {
3570
+ const initialDepsForEdit = useMemo3(() => {
3125
3571
  return [
3126
3572
  ...state.depBlockers.map((t) => ({ id: t.id, name: t.name, type: DependencyType.Blocks })),
3127
3573
  ...state.depRelated.map((t) => ({ id: t.id, name: t.name, type: DependencyType.RelatesTo })),
@@ -3132,23 +3578,24 @@ ${state.selectedTask.additionalRequirements}`;
3132
3578
  }))
3133
3579
  ];
3134
3580
  }, [state.depBlockers, state.depRelated, state.depDuplicates]);
3135
- const allProjectTasks = useMemo2(() => {
3581
+ const allProjectTasks = useMemo3(() => {
3136
3582
  if (!state.activeProject) return [];
3137
3583
  const result = container.taskService.listTasks(state.activeProject, {});
3138
3584
  return result.ok ? result.value : [];
3139
3585
  }, [container, state.activeProject, state.tasks]);
3140
3586
  const previewTaskId = previewTask?.id ?? null;
3141
- useEffect2(() => {
3587
+ useEffect5(() => {
3142
3588
  if (state.activeView === ViewType.TaskList && previewTaskId) {
3143
3589
  loadDeps(previewTaskId);
3144
3590
  }
3145
3591
  }, [state.activeView, previewTaskId, loadDeps]);
3146
- return /* @__PURE__ */ jsxs15(Box18, { flexDirection: "column", height: stdout.rows, children: [
3147
- /* @__PURE__ */ jsx18(Header, { state, latestVersion }),
3148
- /* @__PURE__ */ jsxs15(Box18, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: [
3149
- state.confirmDelete && /* @__PURE__ */ jsx18(ConfirmDialog, { task: state.confirmDelete }),
3150
- !state.confirmDelete && state.activeView === ViewType.TaskList && /* @__PURE__ */ jsxs15(Box18, { flexDirection: "row", flexGrow: 1, children: [
3151
- /* @__PURE__ */ jsx18(
3592
+ return /* @__PURE__ */ jsxs18(Box21, { flexDirection: "column", height: stdout.rows, children: [
3593
+ /* @__PURE__ */ jsx21(Header, { state, latestVersion }),
3594
+ /* @__PURE__ */ jsxs18(Box21, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: [
3595
+ state.confirmDelete && /* @__PURE__ */ jsx21(ConfirmDialog, { task: state.confirmDelete }),
3596
+ !state.confirmDelete && state.changelogEntries && state.changelogDialogOpen && state.activeView === ViewType.TaskList && /* @__PURE__ */ jsx21(ChangelogBanner, { entries: state.changelogEntries, currentIndex: state.changelogIndex }),
3597
+ !state.confirmDelete && !state.changelogDialogOpen && state.activeView === ViewType.TaskList && (state.detectedGitRemote ? /* @__PURE__ */ jsx21(DetectedProjectDialog, { remote: state.detectedGitRemote }) : /* @__PURE__ */ jsxs18(Box21, { flexDirection: "row", flexGrow: 1, children: [
3598
+ /* @__PURE__ */ jsx21(
3152
3599
  EpicPanel,
3153
3600
  {
3154
3601
  epics: state.epics,
@@ -3158,7 +3605,7 @@ ${state.selectedTask.additionalRequirements}`;
3158
3605
  isReordering: state.isEpicReordering
3159
3606
  }
3160
3607
  ),
3161
- /* @__PURE__ */ jsx18(Box18, { width: taskListWidth, children: /* @__PURE__ */ jsx18(
3608
+ /* @__PURE__ */ jsx21(Box21, { width: taskListWidth, children: /* @__PURE__ */ jsx21(
3162
3609
  TaskList,
3163
3610
  {
3164
3611
  tasks: state.tasks,
@@ -3179,7 +3626,7 @@ ${state.selectedTask.additionalRequirements}`;
3179
3626
  epicFilterActive: state.selectedEpicIds.size > 0
3180
3627
  }
3181
3628
  ) }),
3182
- /* @__PURE__ */ jsx18(Box18, { width: taskDetailWidth, children: previewTask ? /* @__PURE__ */ jsx18(
3629
+ /* @__PURE__ */ jsx21(Box21, { width: taskDetailWidth, children: previewTask ? /* @__PURE__ */ jsx21(
3183
3630
  TaskDetail,
3184
3631
  {
3185
3632
  task: previewTask,
@@ -3190,24 +3637,24 @@ ${state.selectedTask.additionalRequirements}`;
3190
3637
  isFocused: state.focusedPanel === "detail",
3191
3638
  scrollOffset: state.detailScrollOffset
3192
3639
  }
3193
- ) : /* @__PURE__ */ jsxs15(
3194
- Box18,
3640
+ ) : /* @__PURE__ */ jsxs18(
3641
+ Box21,
3195
3642
  {
3196
3643
  flexDirection: "column",
3197
3644
  flexGrow: 1,
3198
3645
  borderStyle: "bold",
3199
3646
  borderColor: theme.border,
3200
3647
  children: [
3201
- /* @__PURE__ */ jsx18(Box18, { children: /* @__PURE__ */ jsxs15(Text18, { color: theme.title, bold: true, children: [
3648
+ /* @__PURE__ */ jsx21(Box21, { children: /* @__PURE__ */ jsxs18(Text21, { color: theme.title, bold: true, children: [
3202
3649
  " ",
3203
3650
  "detail"
3204
3651
  ] }) }),
3205
- /* @__PURE__ */ jsx18(Box18, { flexGrow: 1, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "No task selected" }) })
3652
+ /* @__PURE__ */ jsx21(Box21, { flexGrow: 1, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx21(Text21, { dimColor: true, children: "No task selected" }) })
3206
3653
  ]
3207
3654
  }
3208
3655
  ) })
3209
- ] }),
3210
- !state.confirmDelete && state.activeView === ViewType.TaskDetail && state.selectedTask && /* @__PURE__ */ jsx18(
3656
+ ] })),
3657
+ !state.confirmDelete && state.activeView === ViewType.TaskDetail && state.selectedTask && /* @__PURE__ */ jsx21(
3211
3658
  TaskDetail,
3212
3659
  {
3213
3660
  task: state.selectedTask,
@@ -3218,7 +3665,7 @@ ${state.selectedTask.additionalRequirements}`;
3218
3665
  scrollOffset: state.detailScrollOffset
3219
3666
  }
3220
3667
  ),
3221
- !state.confirmDelete && state.activeView === ViewType.DependencyList && state.selectedTask && /* @__PURE__ */ jsx18(
3668
+ !state.confirmDelete && state.activeView === ViewType.DependencyList && state.selectedTask && /* @__PURE__ */ jsx21(
3222
3669
  DependencyList,
3223
3670
  {
3224
3671
  task: state.selectedTask,
@@ -3231,7 +3678,7 @@ ${state.selectedTask.additionalRequirements}`;
3231
3678
  addDepInput: state.addDepInput
3232
3679
  }
3233
3680
  ),
3234
- !state.confirmDelete && (state.activeView === ViewType.TaskCreate || state.activeView === ViewType.TaskEdit) && /* @__PURE__ */ jsx18(
3681
+ !state.confirmDelete && (state.activeView === ViewType.TaskCreate || state.activeView === ViewType.TaskEdit) && /* @__PURE__ */ jsx21(
3235
3682
  TaskForm,
3236
3683
  {
3237
3684
  editingTask: state.activeView === ViewType.TaskEdit ? state.selectedTask : null,
@@ -3241,7 +3688,7 @@ ${state.selectedTask.additionalRequirements}`;
3241
3688
  onCancel: handleFormCancel
3242
3689
  }
3243
3690
  ),
3244
- !state.confirmDelete && state.activeView === ViewType.EpicPicker && state.selectedTask && /* @__PURE__ */ jsx18(
3691
+ !state.confirmDelete && state.activeView === ViewType.EpicPicker && state.selectedTask && /* @__PURE__ */ jsx21(
3245
3692
  EpicPicker,
3246
3693
  {
3247
3694
  epics: state.epics,
@@ -3250,20 +3697,36 @@ ${state.selectedTask.additionalRequirements}`;
3250
3697
  onCancel: handleEpicPickerCancel
3251
3698
  }
3252
3699
  ),
3253
- !state.confirmDelete && state.activeView === ViewType.ProjectSelector && /* @__PURE__ */ jsx18(
3700
+ !state.confirmDelete && state.activeView === ViewType.ProjectSelector && /* @__PURE__ */ jsx21(
3254
3701
  ProjectSelector,
3255
3702
  {
3256
3703
  projects: state.projects,
3257
3704
  activeProject: state.activeProject,
3258
3705
  onSelect: handleProjectSelect,
3259
3706
  onCreate: handleProjectCreate,
3707
+ onEdit: handleProjectEdit,
3260
3708
  onSetDefault: handleSetDefault,
3261
3709
  onLink: handleProjectLink,
3262
3710
  onCancel: handleProjectCancel
3263
3711
  }
3264
3712
  ),
3265
- !state.confirmDelete && state.activeView === ViewType.ProjectCreate && /* @__PURE__ */ jsx18(ProjectForm, { onSave: handleProjectFormSave, onCancel: handleProjectFormCancel }),
3266
- !state.confirmDelete && state.activeView === ViewType.ProjectLink && state.linkingProject && /* @__PURE__ */ jsx18(
3713
+ !state.confirmDelete && state.activeView === ViewType.ProjectCreate && /* @__PURE__ */ jsx21(
3714
+ ProjectForm,
3715
+ {
3716
+ initialGitRemote: state.detectedGitRemote ?? void 0,
3717
+ onSave: handleProjectFormSave,
3718
+ onCancel: handleProjectFormCancel
3719
+ }
3720
+ ),
3721
+ !state.confirmDelete && state.activeView === ViewType.ProjectEdit && state.editingProject && /* @__PURE__ */ jsx21(
3722
+ ProjectForm,
3723
+ {
3724
+ editingProject: state.editingProject,
3725
+ onSave: handleProjectFormSave,
3726
+ onCancel: handleProjectFormCancel
3727
+ }
3728
+ ),
3729
+ !state.confirmDelete && state.activeView === ViewType.ProjectLink && state.linkingProject && /* @__PURE__ */ jsx21(
3267
3730
  ProjectLinkForm,
3268
3731
  {
3269
3732
  project: state.linkingProject,
@@ -3273,18 +3736,18 @@ ${state.selectedTask.additionalRequirements}`;
3273
3736
  onCancel: handleLinkCancel
3274
3737
  }
3275
3738
  ),
3276
- !state.confirmDelete && state.activeView === ViewType.Help && /* @__PURE__ */ jsx18(HelpOverlay, {})
3739
+ !state.confirmDelete && state.activeView === ViewType.Help && /* @__PURE__ */ jsx21(HelpOverlay, {})
3277
3740
  ] }),
3278
- /* @__PURE__ */ jsx18(Crumbs, { breadcrumbs: state.breadcrumbs }),
3279
- state.flash && /* @__PURE__ */ jsx18(FlashMessage, { message: state.flash.message, level: state.flash.level })
3741
+ /* @__PURE__ */ jsx21(Crumbs, { breadcrumbs: state.breadcrumbs }),
3742
+ state.flash && /* @__PURE__ */ jsx21(FlashMessage, { message: state.flash.message, level: state.flash.level })
3280
3743
  ] });
3281
3744
  }
3282
3745
 
3283
3746
  // src/tui/index.tsx
3284
- import { jsx as jsx19 } from "react/jsx-runtime";
3747
+ import { jsx as jsx22 } from "react/jsx-runtime";
3285
3748
  async function launchTUI(container, initialProject, latestVersion) {
3286
3749
  const instance = render(
3287
- /* @__PURE__ */ jsx19(App, { container, initialProject, latestVersion }),
3750
+ /* @__PURE__ */ jsx22(App, { container, initialProject, latestVersion }),
3288
3751
  {
3289
3752
  exitOnCtrlC: true
3290
3753
  }
@@ -3294,4 +3757,4 @@ async function launchTUI(container, initialProject, latestVersion) {
3294
3757
  export {
3295
3758
  launchTUI
3296
3759
  };
3297
- //# sourceMappingURL=tui-WMESKCRD.js.map
3760
+ //# sourceMappingURL=tui-NKAKDHTY.js.map