taskair-cli 1.0.6 → 1.0.8

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.
Files changed (2) hide show
  1. package/dist/index.js +453 -195
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { program } from "commander";
5
5
 
6
6
  // src/commands/config.tsx
7
- import React3, { useState as useState3, useEffect as useEffect3 } from "react";
7
+ import React4, { useState as useState4, useEffect as useEffect4 } from "react";
8
8
  import { Box as Box4, Text as Text4, useInput, useApp } from "ink";
9
9
  import { hostname } from "os";
10
10
  import http from "http";
@@ -283,6 +283,7 @@ function Spinner({
283
283
  }
284
284
 
285
285
  // src/components/AsciiHeader.tsx
286
+ import { useState as useState3, useEffect as useEffect3 } from "react";
286
287
  import { Box as Box3, Text as Text3 } from "ink";
287
288
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
288
289
  var LOGO = [
@@ -293,23 +294,86 @@ var LOGO = [
293
294
  " \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551",
294
295
  " \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D"
295
296
  ];
296
- var TAGLINE = " \u2726 Space-themed \xB7 Privacy-first \xB7 AI-native task management \u2726";
297
- function AsciiHeader() {
297
+ var WAVE_COLORS = [
298
+ "#FF0080",
299
+ "#EE40FB",
300
+ "#B44FFF",
301
+ "#7B61FF",
302
+ "#5B8AFF",
303
+ "#00D4FF",
304
+ "#5B8AFF",
305
+ "#7B61FF",
306
+ "#B44FFF",
307
+ "#EE40FB"
308
+ ];
309
+ var DIVIDER_COLORS = [
310
+ "#7B61FF",
311
+ "#B44FFF",
312
+ "#FF0080",
313
+ "#B44FFF",
314
+ "#7B61FF",
315
+ "#5B8AFF",
316
+ "#00D4FF",
317
+ "#5B8AFF"
318
+ ];
319
+ var TAGLINE = "\u2726 CLI \xB7 MCP \xB7 Web \u2726";
320
+ var DIVIDER_LEN = 60;
321
+ function AnimatedDivider() {
322
+ const [frame, setFrame] = useState3(0);
323
+ useEffect3(() => {
324
+ const id = setInterval(() => {
325
+ setFrame((f) => (f + 1) % DIVIDER_COLORS.length);
326
+ }, 280);
327
+ return () => clearInterval(id);
328
+ }, []);
329
+ return /* @__PURE__ */ jsx3(Text3, { color: DIVIDER_COLORS[frame], children: "\u2500".repeat(DIVIDER_LEN) });
330
+ }
331
+ function PremiumHeader() {
332
+ const [visibleLines, setVisibleLines] = useState3(0);
333
+ const [colorFrame, setColorFrame] = useState3(0);
334
+ const [taglineChars, setTaglineChars] = useState3(0);
335
+ const [showDivider, setShowDivider] = useState3(false);
336
+ const revealed = visibleLines >= LOGO.length;
337
+ useEffect3(() => {
338
+ if (visibleLines >= LOGO.length) return;
339
+ const id = setTimeout(() => setVisibleLines((v) => v + 1), 55);
340
+ return () => clearTimeout(id);
341
+ }, [visibleLines]);
342
+ useEffect3(() => {
343
+ if (!revealed) return;
344
+ const id = setInterval(() => {
345
+ setColorFrame((f) => (f + 1) % WAVE_COLORS.length);
346
+ }, 110);
347
+ return () => clearInterval(id);
348
+ }, [revealed]);
349
+ useEffect3(() => {
350
+ if (!revealed || taglineChars >= TAGLINE.length) return;
351
+ const id = setTimeout(() => setTaglineChars((c) => c + 1), 38);
352
+ return () => clearTimeout(id);
353
+ }, [revealed, taglineChars]);
354
+ useEffect3(() => {
355
+ if (taglineChars >= TAGLINE.length && !showDivider) {
356
+ setShowDivider(true);
357
+ }
358
+ }, [taglineChars, showDivider]);
298
359
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginBottom: 1, children: [
299
- /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: LOGO.map((line, i) => /* @__PURE__ */ jsx3(Text3, { color: "magenta", bold: true, children: line }, i)) }),
300
- /* @__PURE__ */ jsx3(Text3, { color: "cyan", dimColor: true, children: TAGLINE }),
301
- /* @__PURE__ */ jsx3(Box3, { marginTop: 0, children: /* @__PURE__ */ jsx3(Text3, { color: "#7B61FF", children: "\u2500".repeat(60) }) })
360
+ /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: LOGO.slice(0, visibleLines).map((line, i) => {
361
+ const colorIdx = (i * 2 + colorFrame) % WAVE_COLORS.length;
362
+ return /* @__PURE__ */ jsx3(Text3, { color: revealed ? WAVE_COLORS[colorIdx] : "#B44FFF", bold: true, children: line }, i);
363
+ }) }),
364
+ revealed && taglineChars > 0 && /* @__PURE__ */ jsx3(Box3, { marginTop: 0, children: /* @__PURE__ */ jsxs3(Text3, { color: "#00D4FF", bold: true, children: [
365
+ " ",
366
+ TAGLINE.slice(0, taglineChars),
367
+ taglineChars < TAGLINE.length ? /* @__PURE__ */ jsx3(Text3, { color: "#7B61FF", children: "\u258C" }) : null
368
+ ] }) }),
369
+ showDivider && /* @__PURE__ */ jsx3(Box3, { marginTop: 0, children: /* @__PURE__ */ jsx3(AnimatedDivider, {}) })
302
370
  ] });
303
371
  }
372
+ function AsciiHeader() {
373
+ return /* @__PURE__ */ jsx3(PremiumHeader, {});
374
+ }
304
375
  function MiniHeader() {
305
- return /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
306
- /* @__PURE__ */ jsxs3(Text3, { color: "magenta", bold: true, children: [
307
- "\u2726",
308
- " "
309
- ] }),
310
- /* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: "TaskAir" }),
311
- /* @__PURE__ */ jsx3(Text3, { color: "#7B61FF", children: " \u2014 Space-grade task management" })
312
- ] });
376
+ return /* @__PURE__ */ jsx3(PremiumHeader, {});
313
377
  }
314
378
 
315
379
  // src/commands/config.tsx
@@ -321,12 +385,12 @@ function openBrowser(url) {
321
385
  }
322
386
  function ConfigUI({ initialApiUrl }) {
323
387
  const { exit } = useApp();
324
- const [stage, setStage] = useState3("input_api_url");
325
- const [apiUrl, setApiUrl] = useState3(initialApiUrl);
326
- const [currentInput, setCurrentInput] = useState3(initialApiUrl);
327
- const [port, setPort] = useState3(0);
328
- const [errorMsg, setErrorMsg] = useState3("");
329
- const [authDetails, setAuthDetails] = useState3(null);
388
+ const [stage, setStage] = useState4("input_api_url");
389
+ const [apiUrl, setApiUrl] = useState4(initialApiUrl);
390
+ const [currentInput, setCurrentInput] = useState4(initialApiUrl);
391
+ const [port, setPort] = useState4(0);
392
+ const [errorMsg, setErrorMsg] = useState4("");
393
+ const [authDetails, setAuthDetails] = useState4(null);
330
394
  useInput((input, key) => {
331
395
  if (stage === "input_api_url") {
332
396
  if (key.return) {
@@ -350,7 +414,7 @@ function ConfigUI({ initialApiUrl }) {
350
414
  }
351
415
  }
352
416
  });
353
- useEffect3(() => {
417
+ useEffect4(() => {
354
418
  if (stage !== "waiting_for_browser") return;
355
419
  let server = null;
356
420
  let isActive = true;
@@ -557,7 +621,7 @@ function ConfigUI({ initialApiUrl }) {
557
621
  }
558
622
  };
559
623
  }, [stage, apiUrl]);
560
- useEffect3(() => {
624
+ useEffect4(() => {
561
625
  if (stage !== "registering" || !authDetails) return;
562
626
  async function registerAndSave() {
563
627
  try {
@@ -633,7 +697,7 @@ function registerConfig(program2) {
633
697
  const existing = readCredentials();
634
698
  const { render } = await import("ink");
635
699
  render(
636
- React3.createElement(ConfigUI, {
700
+ React4.createElement(ConfigUI, {
637
701
  initialApiUrl: existing?.api_url ?? "http://localhost:3001"
638
702
  })
639
703
  );
@@ -641,19 +705,19 @@ function registerConfig(program2) {
641
705
  }
642
706
 
643
707
  // src/commands/login.tsx
644
- import React4, { useState as useState4, useEffect as useEffect4 } from "react";
708
+ import React5, { useState as useState5, useEffect as useEffect5 } from "react";
645
709
  import { Box as Box5, Text as Text5, useInput as useInput2, useApp as useApp2 } from "ink";
646
710
  import { hostname as hostname2 } from "os";
647
711
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
648
712
  function LoginUI() {
649
713
  const { exit } = useApp2();
650
714
  const creds = readCredentials();
651
- const [email, setEmail] = useState4(creds?.email ?? "");
652
- const [password, setPassword] = useState4("");
653
- const [stage, setStage] = useState4("input_email");
654
- const [currentInput, setCurrentInput] = useState4(creds?.email ?? "");
655
- const [errorMsg, setErrorMsg] = useState4("");
656
- useEffect4(() => {
715
+ const [email, setEmail] = useState5(creds?.email ?? "");
716
+ const [password, setPassword] = useState5("");
717
+ const [stage, setStage] = useState5("input_email");
718
+ const [currentInput, setCurrentInput] = useState5(creds?.email ?? "");
719
+ const [errorMsg, setErrorMsg] = useState5("");
720
+ useEffect5(() => {
657
721
  if (stage === "loading") {
658
722
  const apiUrl = creds?.api_url ?? "http://localhost:3001";
659
723
  apiLogin(apiUrl, email, password).then(async (res) => {
@@ -750,19 +814,19 @@ function LoginUI() {
750
814
  function registerLogin(program2) {
751
815
  program2.command("login").description("Authenticate with TaskAir API").action(async () => {
752
816
  const { render } = await import("ink");
753
- render(React4.createElement(LoginUI));
817
+ render(React5.createElement(LoginUI));
754
818
  });
755
819
  }
756
820
 
757
821
  // src/commands/logout.tsx
758
- import React5, { useEffect as useEffect5, useState as useState5 } from "react";
822
+ import React6, { useEffect as useEffect6, useState as useState6 } from "react";
759
823
  import { Box as Box6, useApp as useApp3 } from "ink";
760
824
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
761
825
  function LogoutUI() {
762
826
  const { exit } = useApp3();
763
- const [status, setStatus] = useState5("loading");
764
- const [message, setMessage] = useState5("");
765
- useEffect5(() => {
827
+ const [status, setStatus] = useState6("loading");
828
+ const [message, setMessage] = useState6("");
829
+ useEffect6(() => {
766
830
  const creds = readCredentials();
767
831
  if (!creds?.access_token) {
768
832
  setMessage("Not logged in.");
@@ -789,12 +853,12 @@ function LogoutUI() {
789
853
  function registerLogout(program2) {
790
854
  program2.command("logout").description("Clear local credentials and revoke session").action(async () => {
791
855
  const { render } = await import("ink");
792
- render(React5.createElement(LogoutUI));
856
+ render(React6.createElement(LogoutUI));
793
857
  });
794
858
  }
795
859
 
796
860
  // src/commands/whoami.tsx
797
- import React6 from "react";
861
+ import React7 from "react";
798
862
  import { Box as Box7, Text as Text7 } from "ink";
799
863
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
800
864
  function WhoamiUI() {
@@ -854,12 +918,12 @@ function WhoamiUI() {
854
918
  function registerWhoami(program2) {
855
919
  program2.command("whoami").description("Display the currently authenticated user").action(async () => {
856
920
  const { render } = await import("ink");
857
- render(React6.createElement(WhoamiUI));
921
+ render(React7.createElement(WhoamiUI));
858
922
  });
859
923
  }
860
924
 
861
925
  // src/commands/add.tsx
862
- import React7, { useEffect as useEffect6, useState as useState6 } from "react";
926
+ import React8, { useEffect as useEffect7, useState as useState7 } from "react";
863
927
  import { Box as Box8, Text as Text8, useApp as useApp4 } from "ink";
864
928
  import { v4 as uuidv42 } from "uuid";
865
929
 
@@ -1034,12 +1098,12 @@ function AddUI({
1034
1098
  tags
1035
1099
  }) {
1036
1100
  const { exit } = useApp4();
1037
- const [status, setStatus] = useState6("working");
1038
- const [syncStatus, setSyncStatus] = useState6("idle");
1039
- const [syncMessage, setSyncMessage] = useState6("");
1040
- const [taskId, setTaskId] = useState6("");
1041
- const [errorMsg, setErrorMsg] = useState6("");
1042
- useEffect6(() => {
1101
+ const [status, setStatus] = useState7("working");
1102
+ const [syncStatus, setSyncStatus] = useState7("idle");
1103
+ const [syncMessage, setSyncMessage] = useState7("");
1104
+ const [taskId, setTaskId] = useState7("");
1105
+ const [errorMsg, setErrorMsg] = useState7("");
1106
+ useEffect7(() => {
1043
1107
  async function createTaskAndSync() {
1044
1108
  try {
1045
1109
  const auth = requireAuth();
@@ -1156,7 +1220,7 @@ function registerAdd(program2) {
1156
1220
  const tags = options.tags ? options.tags.split(",").map((t) => t.trim()).filter(Boolean) : [];
1157
1221
  const { render } = await import("ink");
1158
1222
  render(
1159
- React7.createElement(AddUI, {
1223
+ React8.createElement(AddUI, {
1160
1224
  description,
1161
1225
  priority,
1162
1226
  due: options.due,
@@ -1167,11 +1231,11 @@ function registerAdd(program2) {
1167
1231
  }
1168
1232
 
1169
1233
  // src/commands/list.tsx
1170
- import React9, { useEffect as useEffect7, useState as useState7 } from "react";
1234
+ import React10, { useEffect as useEffect8, useState as useState8 } from "react";
1171
1235
  import { Box as Box10, Text as Text10, useApp as useApp5 } from "ink";
1172
1236
 
1173
1237
  // src/components/TaskTable.tsx
1174
- import React8 from "react";
1238
+ import React9 from "react";
1175
1239
  import { Box as Box9, Text as Text9 } from "ink";
1176
1240
  import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1177
1241
  function priorityColor(p) {
@@ -1273,7 +1337,7 @@ function TaskTable({ tasks }) {
1273
1337
  /* @__PURE__ */ jsx9(TopBorder, {}),
1274
1338
  /* @__PURE__ */ jsx9(Header, {}),
1275
1339
  /* @__PURE__ */ jsx9(Separator, {}),
1276
- tasks.map((task, i) => /* @__PURE__ */ jsxs9(React8.Fragment, { children: [
1340
+ tasks.map((task, i) => /* @__PURE__ */ jsxs9(React9.Fragment, { children: [
1277
1341
  /* @__PURE__ */ jsx9(TableRow, { task }),
1278
1342
  i < tasks.length - 1 && /* @__PURE__ */ jsx9(Separator, {})
1279
1343
  ] }, task.id)),
@@ -1330,8 +1394,8 @@ function TaskCompact({ tasks }) {
1330
1394
  import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1331
1395
  function ListUI({ filter, format }) {
1332
1396
  const { exit } = useApp5();
1333
- const [tasks, setTasks] = useState7([]);
1334
- useEffect7(() => {
1397
+ const [tasks, setTasks] = useState8([]);
1398
+ useEffect8(() => {
1335
1399
  const results = filterTasks(filter);
1336
1400
  results.sort((a, b) => {
1337
1401
  const pOrder = { high: 0, medium: 1, low: 2 };
@@ -1394,78 +1458,204 @@ function registerList(program2) {
1394
1458
  return;
1395
1459
  }
1396
1460
  const { render } = await import("ink");
1397
- render(React9.createElement(ListUI, { filter, format }));
1461
+ render(React10.createElement(ListUI, { filter, format }));
1398
1462
  });
1399
1463
  }
1400
1464
 
1401
1465
  // src/commands/done.tsx
1402
- import React10, { useEffect as useEffect8, useState as useState8 } from "react";
1466
+ import React11, { useEffect as useEffect9, useState as useState9 } from "react";
1403
1467
  import { Box as Box11, Text as Text11, useApp as useApp6 } from "ink";
1404
- import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1468
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1405
1469
  function DoneUI({ id, note }) {
1406
1470
  const { exit } = useApp6();
1407
- const [status, setStatus] = useState8("success");
1408
- const [message, setMessage] = useState8("");
1409
- const [description, setDescription] = useState8("");
1410
- useEffect8(() => {
1411
- const task = getTask(id);
1412
- if (!task) {
1413
- setMessage(`No task found with ID starting with "${id}"`);
1414
- setStatus("error");
1415
- setTimeout(() => exit(new Error("Task not found")), 1200);
1416
- return;
1417
- }
1418
- if (task.status === "completed") {
1419
- setMessage(`Task is already marked as completed.`);
1420
- setStatus("error");
1421
- setTimeout(() => exit(), 1200);
1422
- return;
1423
- }
1424
- const updated = updateTask(task.id, {
1425
- status: "completed",
1426
- completed_at: (/* @__PURE__ */ new Date()).toISOString(),
1427
- completion_note: note
1428
- });
1429
- if (updated) {
1430
- setDescription(task.description);
1431
- setMessage(`Task marked complete!`);
1432
- setStatus("success");
1433
- setTimeout(() => exit(), 1500);
1471
+ const [status, setStatus] = useState9("working");
1472
+ const [syncStatus, setSyncStatus] = useState9("idle");
1473
+ const [syncMessage, setSyncMessage] = useState9("");
1474
+ const [message, setMessage] = useState9("");
1475
+ const [description, setDescription] = useState9("");
1476
+ useEffect9(() => {
1477
+ async function completeTaskAndSync() {
1478
+ try {
1479
+ const auth = requireAuth();
1480
+ const task = getTask(id);
1481
+ if (!task) {
1482
+ setMessage(`No task found with ID starting with "${id}"`);
1483
+ setStatus("error");
1484
+ setTimeout(() => exit(new Error("Task not found")), 1200);
1485
+ return;
1486
+ }
1487
+ if (task.status === "completed") {
1488
+ setMessage(`Task is already marked as completed.`);
1489
+ setStatus("error");
1490
+ setTimeout(() => exit(), 1200);
1491
+ return;
1492
+ }
1493
+ const updated = updateTask(task.id, {
1494
+ status: "completed",
1495
+ completed_at: (/* @__PURE__ */ new Date()).toISOString(),
1496
+ completion_note: note
1497
+ });
1498
+ if (updated) {
1499
+ setDescription(task.description);
1500
+ setMessage(`Task marked complete!`);
1501
+ setStatus("success");
1502
+ setSyncStatus("syncing");
1503
+ const tasks = getAllTasks();
1504
+ const bundle = {
1505
+ tasks,
1506
+ schema_version: "1",
1507
+ exported_at: (/* @__PURE__ */ new Date()).toISOString()
1508
+ };
1509
+ const plaintext = JSON.stringify(bundle);
1510
+ const blobChecksum = checksum(plaintext);
1511
+ const syncPassword = auth.password;
1512
+ if (!syncPassword) {
1513
+ setSyncStatus("failed");
1514
+ setSyncMessage('Encryption password required. Run "taskair sync --password <pwd>".');
1515
+ setTimeout(() => exit(), 3e3);
1516
+ return;
1517
+ }
1518
+ const blob = encrypt(plaintext, syncPassword);
1519
+ const res = await apiUploadSync(
1520
+ auth.apiUrl,
1521
+ auth.accessToken,
1522
+ blob,
1523
+ blobChecksum,
1524
+ auth.deviceId
1525
+ );
1526
+ if (res.success) {
1527
+ clearSyncQueue();
1528
+ setSyncStatus("synced");
1529
+ setTimeout(() => exit(), 2e3);
1530
+ } else if (res.error?.code === "NETWORK_ERROR") {
1531
+ setSyncStatus("offline");
1532
+ setTimeout(() => exit(), 2500);
1533
+ } else {
1534
+ setSyncStatus("failed");
1535
+ setSyncMessage(res.error?.message ?? "Sync failed");
1536
+ setTimeout(() => exit(), 3500);
1537
+ }
1538
+ }
1539
+ } catch (e) {
1540
+ setMessage(e.message);
1541
+ setStatus("error");
1542
+ setTimeout(() => exit(e), 2e3);
1543
+ }
1434
1544
  }
1545
+ completeTaskAndSync();
1435
1546
  }, []);
1436
1547
  return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", padding: 1, children: [
1437
1548
  /* @__PURE__ */ jsx11(MiniHeader, {}),
1438
- /* @__PURE__ */ jsx11(Box11, { marginTop: 1, flexDirection: "column", children: status === "success" ? /* @__PURE__ */ jsxs11(Fragment2, { children: [
1549
+ status === "working" && /* @__PURE__ */ jsx11(Spinner, { label: "Updating task\u2026", type: "star", color: "green" }),
1550
+ status === "success" && /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
1439
1551
  /* @__PURE__ */ jsx11(StarBurst, { label: message, color: "green" }),
1440
- description && /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, children: [
1441
- /* @__PURE__ */ jsx11(Text11, { color: "gray", children: " " }),
1442
- /* @__PURE__ */ jsx11(Text11, { color: "green", children: "\u2713 " }),
1443
- /* @__PURE__ */ jsx11(Text11, { color: "white", children: description })
1444
- ] }),
1445
- note && /* @__PURE__ */ jsxs11(Box11, { children: [
1446
- /* @__PURE__ */ jsx11(Text11, { color: "gray", children: " Note: " }),
1447
- /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: note })
1552
+ /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, flexDirection: "column", children: [
1553
+ description && /* @__PURE__ */ jsxs11(Box11, { children: [
1554
+ /* @__PURE__ */ jsx11(Text11, { color: "gray", children: " Task: " }),
1555
+ /* @__PURE__ */ jsx11(Text11, { color: "white", children: description })
1556
+ ] }),
1557
+ note && /* @__PURE__ */ jsxs11(Box11, { children: [
1558
+ /* @__PURE__ */ jsx11(Text11, { color: "gray", children: " Note: " }),
1559
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: note })
1560
+ ] }),
1561
+ /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, children: [
1562
+ /* @__PURE__ */ jsx11(Text11, { color: "gray", children: " Sync: " }),
1563
+ syncStatus === "syncing" && /* @__PURE__ */ jsx11(Text11, { color: "magenta", children: "Syncing to cloud (E2E encrypted)..." }),
1564
+ syncStatus === "synced" && /* @__PURE__ */ jsx11(Text11, { color: "green", children: "\u2713 Synced to cloud" }),
1565
+ syncStatus === "offline" && /* @__PURE__ */ jsx11(Text11, { color: "yellow", children: "\u2601 Offline (queued locally)" }),
1566
+ syncStatus === "failed" && /* @__PURE__ */ jsxs11(Text11, { color: "red", children: [
1567
+ "\u2717 ",
1568
+ syncMessage
1569
+ ] })
1570
+ ] })
1448
1571
  ] })
1449
- ] }) : /* @__PURE__ */ jsx11(StatusBadge, { type: "error", message }) })
1572
+ ] }),
1573
+ status === "error" && /* @__PURE__ */ jsx11(StatusBadge, { type: "error", message })
1450
1574
  ] });
1451
1575
  }
1452
1576
  function registerDone(program2) {
1453
1577
  program2.command("done <id>").alias("-d").description("Mark a task as complete").option("-n, --note <note>", "Completion note").action(async (id, options) => {
1454
1578
  const { render } = await import("ink");
1455
- render(React10.createElement(DoneUI, { id, note: options.note }));
1579
+ render(React11.createElement(DoneUI, { id, note: options.note }));
1456
1580
  });
1457
1581
  }
1458
1582
 
1459
1583
  // src/commands/remove.tsx
1460
- import React11, { useEffect as useEffect9, useState as useState9 } from "react";
1584
+ import React12, { useEffect as useEffect10, useState as useState10 } from "react";
1461
1585
  import { Box as Box12, Text as Text12, useApp as useApp7, useInput as useInput3 } from "ink";
1462
1586
  import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1463
1587
  function RemoveUI({ id, force }) {
1464
1588
  const { exit } = useApp7();
1465
- const [stage, setStage] = useState9("confirm");
1466
- const [message, setMessage] = useState9("");
1589
+ const [stage, setStage] = useState10("confirm");
1590
+ const [syncStatus, setSyncStatus] = useState10("idle");
1591
+ const [syncMessage, setSyncMessage] = useState10("");
1592
+ const [message, setMessage] = useState10("");
1467
1593
  const task = getTask(id);
1468
- useEffect9(() => {
1594
+ const performDeleteAndSync = async () => {
1595
+ try {
1596
+ const auth = requireAuth();
1597
+ setStage("deleting");
1598
+ const deleted = deleteTask(task.id);
1599
+ if (deleted) {
1600
+ setMessage(`Task removed: ${task.description}`);
1601
+ setSyncStatus("syncing");
1602
+ const tasks = getAllTasks();
1603
+ const bundle = {
1604
+ tasks,
1605
+ schema_version: "1",
1606
+ exported_at: (/* @__PURE__ */ new Date()).toISOString()
1607
+ };
1608
+ const plaintext = JSON.stringify(bundle);
1609
+ const blobChecksum = checksum(plaintext);
1610
+ const syncPassword = auth.password;
1611
+ if (!syncPassword) {
1612
+ setSyncStatus("failed");
1613
+ setSyncMessage('Encryption password required. Run "taskair sync --password <pwd>".');
1614
+ setStage("success");
1615
+ setTimeout(() => exit(), 3e3);
1616
+ return;
1617
+ }
1618
+ const blob = encrypt(plaintext, syncPassword);
1619
+ const res = await apiUploadSync(
1620
+ auth.apiUrl,
1621
+ auth.accessToken,
1622
+ blob,
1623
+ blobChecksum,
1624
+ auth.deviceId
1625
+ );
1626
+ setStage("success");
1627
+ if (res.success) {
1628
+ clearSyncQueue();
1629
+ setSyncStatus("synced");
1630
+ setTimeout(() => exit(), 2e3);
1631
+ } else if (res.error?.code === "NETWORK_ERROR") {
1632
+ setSyncStatus("offline");
1633
+ setTimeout(() => exit(), 2500);
1634
+ } else {
1635
+ setSyncStatus("failed");
1636
+ setSyncMessage(res.error?.message ?? "Sync failed");
1637
+ setTimeout(() => exit(), 3500);
1638
+ }
1639
+ } else {
1640
+ setMessage("Failed to delete task.");
1641
+ setStage("error");
1642
+ setTimeout(() => exit(new Error("Delete failed")), 1200);
1643
+ }
1644
+ } catch (e) {
1645
+ setMessage(e.message);
1646
+ setStage("error");
1647
+ setTimeout(() => exit(e), 2e3);
1648
+ }
1649
+ };
1650
+ useEffect10(() => {
1651
+ try {
1652
+ requireAuth();
1653
+ } catch (e) {
1654
+ setMessage(e.message);
1655
+ setStage("error");
1656
+ setTimeout(() => exit(e), 2e3);
1657
+ return;
1658
+ }
1469
1659
  if (!task) {
1470
1660
  setMessage(`No task found with ID starting with "${id}"`);
1471
1661
  setStage("error");
@@ -1473,23 +1663,13 @@ function RemoveUI({ id, force }) {
1473
1663
  return;
1474
1664
  }
1475
1665
  if (force) {
1476
- const deleted = deleteTask(task.id);
1477
- if (deleted) {
1478
- setMessage(`Task removed: ${task.description}`);
1479
- setStage("success");
1480
- setTimeout(() => exit(), 1200);
1481
- }
1666
+ performDeleteAndSync();
1482
1667
  }
1483
1668
  }, []);
1484
1669
  useInput3((input) => {
1485
1670
  if (stage !== "confirm" || !task) return;
1486
1671
  if (input.toLowerCase() === "y") {
1487
- const deleted = deleteTask(task.id);
1488
- if (deleted) {
1489
- setMessage(`Task removed: ${task.description}`);
1490
- setStage("success");
1491
- setTimeout(() => exit(), 1200);
1492
- }
1672
+ performDeleteAndSync();
1493
1673
  } else if (input.toLowerCase() === "n" || input === "") {
1494
1674
  setStage("cancelled");
1495
1675
  setTimeout(() => exit(), 800);
@@ -1525,7 +1705,20 @@ function RemoveUI({ id, force }) {
1525
1705
  /* @__PURE__ */ jsx12(Text12, { color: "gray", children: " to cancel: " })
1526
1706
  ] })
1527
1707
  ] }),
1528
- stage === "success" && /* @__PURE__ */ jsx12(StarBurst, { label: message, color: "cyan" }),
1708
+ stage === "deleting" && /* @__PURE__ */ jsx12(Spinner, { label: "Removing task\u2026", type: "star", color: "cyan" }),
1709
+ stage === "success" && /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
1710
+ /* @__PURE__ */ jsx12(StarBurst, { label: message, color: "cyan" }),
1711
+ /* @__PURE__ */ jsx12(Box12, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsxs12(Box12, { children: [
1712
+ /* @__PURE__ */ jsx12(Text12, { color: "gray", children: " Sync: " }),
1713
+ syncStatus === "syncing" && /* @__PURE__ */ jsx12(Text12, { color: "magenta", children: "Syncing to cloud (E2E encrypted)..." }),
1714
+ syncStatus === "synced" && /* @__PURE__ */ jsx12(Text12, { color: "green", children: "\u2713 Synced to cloud" }),
1715
+ syncStatus === "offline" && /* @__PURE__ */ jsx12(Text12, { color: "yellow", children: "\u2601 Offline (queued locally)" }),
1716
+ syncStatus === "failed" && /* @__PURE__ */ jsxs12(Text12, { color: "red", children: [
1717
+ "\u2717 ",
1718
+ syncMessage
1719
+ ] })
1720
+ ] }) })
1721
+ ] }),
1529
1722
  stage === "error" && /* @__PURE__ */ jsx12(StatusBadge, { type: "error", message }),
1530
1723
  stage === "cancelled" && /* @__PURE__ */ jsx12(StatusBadge, { type: "info", message: "Deletion cancelled." })
1531
1724
  ] });
@@ -1533,92 +1726,153 @@ function RemoveUI({ id, force }) {
1533
1726
  function registerRemove(program2) {
1534
1727
  program2.command("remove <id>").alias("-r").description("Delete a task").option("--force", "Skip confirmation prompt", false).action(async (id, options) => {
1535
1728
  const { render } = await import("ink");
1536
- render(React11.createElement(RemoveUI, { id, force: options.force }));
1729
+ render(React12.createElement(RemoveUI, { id, force: options.force }));
1537
1730
  });
1538
1731
  }
1539
1732
 
1540
1733
  // src/commands/edit.tsx
1541
- import React12, { useState as useState10, useEffect as useEffect10 } from "react";
1734
+ import React13, { useState as useState11, useEffect as useEffect11 } from "react";
1542
1735
  import { Box as Box13, Text as Text13, useApp as useApp8 } from "ink";
1543
- import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1736
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1544
1737
  function EditUI({ id, updates }) {
1545
1738
  const { exit } = useApp8();
1546
- const [status, setStatus] = useState10("success");
1547
- const [message, setMessage] = useState10("");
1548
- const [changes, setChanges] = useState10([]);
1549
- useEffect10(() => {
1550
- const task = getTask(id);
1551
- if (!task) {
1552
- setMessage(`No task found with ID starting with "${id}"`);
1553
- setStatus("error");
1554
- setTimeout(() => exit(new Error("Task not found")), 1200);
1555
- return;
1556
- }
1557
- const taskUpdates = {};
1558
- const changesLog = [];
1559
- if (updates.description) {
1560
- taskUpdates.description = updates.description;
1561
- changesLog.push(`description \u2192 "${updates.description}"`);
1562
- }
1563
- if (updates.priority) {
1564
- taskUpdates.priority = updates.priority;
1565
- changesLog.push(`priority \u2192 ${updates.priority}`);
1566
- }
1567
- if (updates.status) {
1568
- taskUpdates.status = updates.status;
1569
- if (updates.status === "completed") {
1570
- taskUpdates.completed_at = (/* @__PURE__ */ new Date()).toISOString();
1739
+ const [status, setStatus] = useState11("working");
1740
+ const [syncStatus, setSyncStatus] = useState11("idle");
1741
+ const [syncMessage, setSyncMessage] = useState11("");
1742
+ const [message, setMessage] = useState11("");
1743
+ const [changes, setChanges] = useState11([]);
1744
+ useEffect11(() => {
1745
+ async function editTaskAndSync() {
1746
+ try {
1747
+ const auth = requireAuth();
1748
+ const task = getTask(id);
1749
+ if (!task) {
1750
+ setMessage(`No task found with ID starting with "${id}"`);
1751
+ setStatus("error");
1752
+ setTimeout(() => exit(new Error("Task not found")), 1200);
1753
+ return;
1754
+ }
1755
+ const taskUpdates = {};
1756
+ const changesLog = [];
1757
+ if (updates.description) {
1758
+ taskUpdates.description = updates.description;
1759
+ changesLog.push(`description \u2192 "${updates.description}"`);
1760
+ }
1761
+ if (updates.priority) {
1762
+ taskUpdates.priority = updates.priority;
1763
+ changesLog.push(`priority \u2192 ${updates.priority}`);
1764
+ }
1765
+ if (updates.status) {
1766
+ taskUpdates.status = updates.status;
1767
+ if (updates.status === "completed") {
1768
+ taskUpdates.completed_at = (/* @__PURE__ */ new Date()).toISOString();
1769
+ }
1770
+ changesLog.push(`status \u2192 ${updates.status}`);
1771
+ }
1772
+ if (updates.due) {
1773
+ taskUpdates.due_date = updates.due;
1774
+ changesLog.push(`due date \u2192 ${updates.due}`);
1775
+ }
1776
+ if (updates.tags) {
1777
+ taskUpdates.tags = updates.tags.split(",").map((t) => t.trim());
1778
+ changesLog.push(`tags \u2192 [${taskUpdates.tags.join(", ")}]`);
1779
+ }
1780
+ if (updates.note) {
1781
+ taskUpdates.completion_note = updates.note;
1782
+ changesLog.push(`note \u2192 "${updates.note}"`);
1783
+ }
1784
+ if (changesLog.length === 0) {
1785
+ setMessage("No changes specified. Use options like --description, --priority, etc.");
1786
+ setStatus("error");
1787
+ setTimeout(() => exit(), 1200);
1788
+ return;
1789
+ }
1790
+ const updated = updateTask(task.id, taskUpdates);
1791
+ if (updated) {
1792
+ setChanges(changesLog);
1793
+ setMessage(`Task ${task.id.slice(0, 8)} updated!`);
1794
+ setStatus("success");
1795
+ setSyncStatus("syncing");
1796
+ const tasks = getAllTasks();
1797
+ const bundle = {
1798
+ tasks,
1799
+ schema_version: "1",
1800
+ exported_at: (/* @__PURE__ */ new Date()).toISOString()
1801
+ };
1802
+ const plaintext = JSON.stringify(bundle);
1803
+ const blobChecksum = checksum(plaintext);
1804
+ const syncPassword = auth.password;
1805
+ if (!syncPassword) {
1806
+ setSyncStatus("failed");
1807
+ setSyncMessage('Encryption password required. Run "taskair sync --password <pwd>".');
1808
+ setTimeout(() => exit(), 3e3);
1809
+ return;
1810
+ }
1811
+ const blob = encrypt(plaintext, syncPassword);
1812
+ const res = await apiUploadSync(
1813
+ auth.apiUrl,
1814
+ auth.accessToken,
1815
+ blob,
1816
+ blobChecksum,
1817
+ auth.deviceId
1818
+ );
1819
+ if (res.success) {
1820
+ clearSyncQueue();
1821
+ setSyncStatus("synced");
1822
+ setTimeout(() => exit(), 2e3);
1823
+ } else if (res.error?.code === "NETWORK_ERROR") {
1824
+ setSyncStatus("offline");
1825
+ setTimeout(() => exit(), 2500);
1826
+ } else {
1827
+ setSyncStatus("failed");
1828
+ setSyncMessage(res.error?.message ?? "Sync failed");
1829
+ setTimeout(() => exit(), 3500);
1830
+ }
1831
+ }
1832
+ } catch (e) {
1833
+ setMessage(e.message);
1834
+ setStatus("error");
1835
+ setTimeout(() => exit(e), 2e3);
1571
1836
  }
1572
- changesLog.push(`status \u2192 ${updates.status}`);
1573
- }
1574
- if (updates.due) {
1575
- taskUpdates.due_date = updates.due;
1576
- changesLog.push(`due date \u2192 ${updates.due}`);
1577
- }
1578
- if (updates.tags) {
1579
- taskUpdates.tags = updates.tags.split(",").map((t) => t.trim());
1580
- changesLog.push(`tags \u2192 [${taskUpdates.tags.join(", ")}]`);
1581
- }
1582
- if (updates.note) {
1583
- taskUpdates.completion_note = updates.note;
1584
- changesLog.push(`note \u2192 "${updates.note}"`);
1585
- }
1586
- if (changesLog.length === 0) {
1587
- setMessage("No changes specified. Use options like --description, --priority, etc.");
1588
- setStatus("error");
1589
- setTimeout(() => exit(), 1200);
1590
- return;
1591
- }
1592
- const updated = updateTask(task.id, taskUpdates);
1593
- if (updated) {
1594
- setChanges(changesLog);
1595
- setMessage(`Task ${task.id.slice(0, 8)} updated!`);
1596
- setStatus("success");
1597
- setTimeout(() => exit(), 1500);
1598
1837
  }
1838
+ editTaskAndSync();
1599
1839
  }, []);
1600
1840
  return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", padding: 1, children: [
1601
1841
  /* @__PURE__ */ jsx13(MiniHeader, {}),
1602
- /* @__PURE__ */ jsx13(Box13, { marginTop: 1, flexDirection: "column", children: status === "success" ? /* @__PURE__ */ jsxs13(Fragment3, { children: [
1842
+ status === "working" && /* @__PURE__ */ jsx13(Spinner, { label: "Updating task\u2026", type: "star", color: "magenta" }),
1843
+ status === "success" && /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
1603
1844
  /* @__PURE__ */ jsx13(StarBurst, { label: message, color: "magenta" }),
1604
- changes.map((c) => /* @__PURE__ */ jsxs13(Box13, { marginTop: 0, children: [
1605
- /* @__PURE__ */ jsx13(Text13, { color: "gray", children: " \u21B3 " }),
1606
- /* @__PURE__ */ jsx13(Text13, { color: "white", children: c })
1607
- ] }, c))
1608
- ] }) : /* @__PURE__ */ jsx13(StatusBadge, { type: "error", message }) })
1845
+ /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, flexDirection: "column", children: [
1846
+ changes.map((c) => /* @__PURE__ */ jsxs13(Box13, { marginTop: 0, children: [
1847
+ /* @__PURE__ */ jsx13(Text13, { color: "gray", children: " \u21B3 " }),
1848
+ /* @__PURE__ */ jsx13(Text13, { color: "white", children: c })
1849
+ ] }, c)),
1850
+ /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, children: [
1851
+ /* @__PURE__ */ jsx13(Text13, { color: "gray", children: " Sync: " }),
1852
+ syncStatus === "syncing" && /* @__PURE__ */ jsx13(Text13, { color: "magenta", children: "Syncing to cloud (E2E encrypted)..." }),
1853
+ syncStatus === "synced" && /* @__PURE__ */ jsx13(Text13, { color: "green", children: "\u2713 Synced to cloud" }),
1854
+ syncStatus === "offline" && /* @__PURE__ */ jsx13(Text13, { color: "yellow", children: "\u2601 Offline (queued locally)" }),
1855
+ syncStatus === "failed" && /* @__PURE__ */ jsxs13(Text13, { color: "red", children: [
1856
+ "\u2717 ",
1857
+ syncMessage
1858
+ ] })
1859
+ ] })
1860
+ ] })
1861
+ ] }),
1862
+ status === "error" && /* @__PURE__ */ jsx13(StatusBadge, { type: "error", message })
1609
1863
  ] });
1610
1864
  }
1611
1865
  function registerEdit(program2) {
1612
1866
  program2.command("edit <id>").alias("-e").description("Update one or more fields of a task").option("--description <text>", "New description").option("--priority <level>", "New priority: high | medium | low").option("--status <status>", "New status: pending | in_progress | completed").option("--due <datetime>", "New due date").option("--tags <tags>", "New comma-separated tags").option("--note <note>", "Add a completion note").action(async (id, options) => {
1613
1867
  const { render } = await import("ink");
1614
- render(React12.createElement(EditUI, { id, updates: options }));
1868
+ render(React13.createElement(EditUI, { id, updates: options }));
1615
1869
  });
1616
1870
  }
1617
1871
 
1618
1872
  // src/commands/stats.tsx
1619
- import React13, { useEffect as useEffect11 } from "react";
1873
+ import React14, { useEffect as useEffect12 } from "react";
1620
1874
  import { Box as Box14, Text as Text14, useApp as useApp9 } from "ink";
1621
- import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
1875
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
1622
1876
  function ProgressBar({
1623
1877
  value,
1624
1878
  max,
@@ -1637,7 +1891,7 @@ function ProgressBar({
1637
1891
  function StatsUI() {
1638
1892
  const { exit } = useApp9();
1639
1893
  const stats = computeStats();
1640
- useEffect11(() => {
1894
+ useEffect12(() => {
1641
1895
  setTimeout(() => exit(), 100);
1642
1896
  }, []);
1643
1897
  const rows = [
@@ -1673,7 +1927,7 @@ function StatsUI() {
1673
1927
  row.label.padEnd(16)
1674
1928
  ] }),
1675
1929
  /* @__PURE__ */ jsx14(Text14, { color: row.color, bold: true, children: String(row.value).padStart(4) }),
1676
- stats.total > 0 && /* @__PURE__ */ jsxs14(Fragment4, { children: [
1930
+ stats.total > 0 && /* @__PURE__ */ jsxs14(Fragment2, { children: [
1677
1931
  /* @__PURE__ */ jsx14(Text14, { color: "gray", children: " " }),
1678
1932
  /* @__PURE__ */ jsx14(
1679
1933
  ProgressBar,
@@ -1695,7 +1949,7 @@ function StatsUI() {
1695
1949
  row.label.padEnd(20)
1696
1950
  ] }),
1697
1951
  /* @__PURE__ */ jsx14(Text14, { color: row.color, bold: true, children: String(row.value).padStart(4) }),
1698
- stats.total > 0 && /* @__PURE__ */ jsxs14(Fragment4, { children: [
1952
+ stats.total > 0 && /* @__PURE__ */ jsxs14(Fragment2, { children: [
1699
1953
  /* @__PURE__ */ jsx14(Text14, { color: "gray", children: " " }),
1700
1954
  /* @__PURE__ */ jsx14(
1701
1955
  ProgressBar,
@@ -1728,20 +1982,20 @@ function StatsUI() {
1728
1982
  function registerStats(program2) {
1729
1983
  program2.command("stats").description("Show task analytics and completion stats").action(async () => {
1730
1984
  const { render } = await import("ink");
1731
- render(React13.createElement(StatsUI));
1985
+ render(React14.createElement(StatsUI));
1732
1986
  });
1733
1987
  }
1734
1988
 
1735
1989
  // src/commands/sync.tsx
1736
- import React14, { useState as useState11, useEffect as useEffect12 } from "react";
1990
+ import React15, { useState as useState12, useEffect as useEffect13 } from "react";
1737
1991
  import { Box as Box15, Text as Text15, useApp as useApp10 } from "ink";
1738
- import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
1992
+ import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
1739
1993
  function SyncUI({ dryRun, password }) {
1740
1994
  const { exit } = useApp10();
1741
- const [status, setStatus] = useState11("loading");
1742
- const [message, setMessage] = useState11("");
1743
- const [details, setDetails] = useState11([]);
1744
- useEffect12(() => {
1995
+ const [status, setStatus] = useState12("loading");
1996
+ const [message, setMessage] = useState12("");
1997
+ const [details, setDetails] = useState12([]);
1998
+ useEffect13(() => {
1745
1999
  async function doSync() {
1746
2000
  try {
1747
2001
  const auth = requireAuth();
@@ -1816,7 +2070,7 @@ function SyncUI({ dryRun, password }) {
1816
2070
  /* @__PURE__ */ jsx15(MiniHeader, {}),
1817
2071
  /* @__PURE__ */ jsxs15(Box15, { marginTop: 1, flexDirection: "column", children: [
1818
2072
  status === "loading" && /* @__PURE__ */ jsx15(Spinner, { label: "Syncing to cloud (E2E encrypted)\u2026", type: "orbit", color: "magenta" }),
1819
- status === "success" && /* @__PURE__ */ jsxs15(Fragment5, { children: [
2073
+ status === "success" && /* @__PURE__ */ jsxs15(Fragment3, { children: [
1820
2074
  /* @__PURE__ */ jsx15(StarBurst, { label: message, color: "cyan" }),
1821
2075
  details.map((d) => /* @__PURE__ */ jsxs15(Box15, { children: [
1822
2076
  /* @__PURE__ */ jsx15(Text15, { color: "gray", children: " \xB7 " }),
@@ -1844,7 +2098,7 @@ function SyncUI({ dryRun, password }) {
1844
2098
  function registerSync(program2) {
1845
2099
  program2.command("sync").description("Sync tasks with cloud (E2E encrypted)").option("--dry-run", "Preview what would be synced without making changes", false).option("--password <password>", "Master password for encryption").action(async (options) => {
1846
2100
  const { render } = await import("ink");
1847
- render(React14.createElement(SyncUI, { dryRun: options.dryRun, password: options.password }));
2101
+ render(React15.createElement(SyncUI, { dryRun: options.dryRun, password: options.password }));
1848
2102
  });
1849
2103
  }
1850
2104
 
@@ -1910,12 +2164,12 @@ function registerExport(program2) {
1910
2164
  }
1911
2165
 
1912
2166
  // src/commands/upgrade.tsx
1913
- import React15, { useState as useState12, useEffect as useEffect13 } from "react";
2167
+ import React16, { useState as useState13, useEffect as useEffect14 } from "react";
1914
2168
  import { Box as Box16, Text as Text16, useApp as useApp11 } from "ink";
1915
2169
  import { exec as exec2 } from "child_process";
1916
2170
 
1917
2171
  // src/lib/version.ts
1918
- var CLI_VERSION = "1.0.6";
2172
+ var CLI_VERSION = "1.0.7";
1919
2173
 
1920
2174
  // src/commands/upgrade.tsx
1921
2175
  import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
@@ -1935,7 +2189,7 @@ function UpgradeUI({ target }) {
1935
2189
  const { exit } = useApp11();
1936
2190
  const shouldUpgradeCli = !target || target.toLowerCase() === "cli";
1937
2191
  const shouldUpgradeMcp = !target || target.toLowerCase() === "mcp";
1938
- const [cliState, setCliState] = useState12({
2192
+ const [cliState, setCliState] = useState13({
1939
2193
  name: "TaskAir CLI",
1940
2194
  pkgName: "taskair-cli",
1941
2195
  current: CLI_VERSION,
@@ -1943,7 +2197,7 @@ function UpgradeUI({ target }) {
1943
2197
  status: shouldUpgradeCli ? "checking" : "up-to-date",
1944
2198
  errorMsg: ""
1945
2199
  });
1946
- const [mcpState, setMcpState] = useState12({
2200
+ const [mcpState, setMcpState] = useState13({
1947
2201
  name: "TaskAir MCP Server",
1948
2202
  pkgName: "taskair-mcp",
1949
2203
  current: "",
@@ -1951,7 +2205,7 @@ function UpgradeUI({ target }) {
1951
2205
  status: shouldUpgradeMcp ? "checking" : "up-to-date",
1952
2206
  errorMsg: ""
1953
2207
  });
1954
- useEffect13(() => {
2208
+ useEffect14(() => {
1955
2209
  async function checkAndUpgrade() {
1956
2210
  const tasks = [];
1957
2211
  let mcpCurrent = "";
@@ -2146,13 +2400,13 @@ function UpgradeUI({ target }) {
2146
2400
  function registerUpgrade(program2) {
2147
2401
  program2.command("upgrade [target]").description('Upgrade TaskAir CLI and MCP Server. Target can be "cli", "mcp", or omitted to upgrade both.').action(async (target) => {
2148
2402
  const { render } = await import("ink");
2149
- render(React15.createElement(UpgradeUI, { target }));
2403
+ render(React16.createElement(UpgradeUI, { target }));
2150
2404
  });
2151
2405
  }
2152
2406
 
2153
2407
  // src/index.ts
2154
2408
  program.name("taskair").description(
2155
- "\u2726 Space-themed task management with E2E encryption \xB7 AI-native \xB7 Privacy-first"
2409
+ "\u2726 Privacy-first task management with E2E encryption \xB7 AI-native \xB7 CLI \xB7 MCP \xB7 Web"
2156
2410
  ).version(CLI_VERSION, "-v, --version", "Output the current version").helpOption("-h, --help", "Display help information");
2157
2411
  registerConfig(program);
2158
2412
  registerLogin(program);
@@ -2170,10 +2424,14 @@ registerExport(program);
2170
2424
  program.addHelpText(
2171
2425
  "beforeAll",
2172
2426
  `
2173
- \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
2174
- \u2551 \u2726 T A S K A I R \u2014 Space-grade task management \u2726 \u2551
2175
- \u2551 End-to-end encrypted \xB7 AI-native \xB7 Developer-first \u2551
2176
- \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
2427
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
2428
+ \u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
2429
+ \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
2430
+ \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2588\u2588\u2557 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
2431
+ \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
2432
+ \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
2433
+
2434
+ \u2726 CLI \xB7 MCP \xB7 Web \u2726
2177
2435
  `
2178
2436
  );
2179
2437
  var args = [...process.argv];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskair-cli",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Space-themed, privacy-first task management CLI with E2E encryption",
5
5
  "type": "module",
6
6
  "bin": {