@bretwardjames/tw-bridge 0.4.0 → 0.5.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.
package/dist/cli.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import fs3 from "fs";
5
- import path4 from "path";
6
- import os3 from "os";
4
+ import fs4 from "fs";
5
+ import path5 from "path";
6
+ import os4 from "os";
7
7
 
8
8
  // src/config.ts
9
9
  import fs from "fs";
@@ -241,8 +241,8 @@ async function oauthLogin(clientId, clientSecret, rl) {
241
241
  saveTokens(tokens);
242
242
  return tokens;
243
243
  }
244
- async function asanaFetch(path5, token, options = {}) {
245
- const url = `${API_BASE}${path5}`;
244
+ async function asanaFetch(path6, token, options = {}) {
245
+ const url = `${API_BASE}${path6}`;
246
246
  const res = await fetch(url, {
247
247
  ...options,
248
248
  headers: {
@@ -258,9 +258,9 @@ async function asanaFetch(path5, token, options = {}) {
258
258
  const json = await res.json();
259
259
  return json.data;
260
260
  }
261
- async function asanaFetchAll(path5, token) {
261
+ async function asanaFetchAll(path6, token) {
262
262
  const results = [];
263
- let nextPage = path5;
263
+ let nextPage = path6;
264
264
  while (nextPage) {
265
265
  const url = `${API_BASE}${nextPage}`;
266
266
  const res = await fetch(url, {
@@ -707,12 +707,65 @@ function updateTaskDescription(existing, newDescription) {
707
707
  return result.status === 0;
708
708
  }
709
709
 
710
+ // src/tracking.ts
711
+ import fs3 from "fs";
712
+ import path4 from "path";
713
+ import os3 from "os";
714
+ import { spawnSync as spawnSync3 } from "child_process";
715
+ var TRACKING_FILE = path4.join(os3.homedir(), ".config", "tw-bridge", "tracking.json");
716
+ function loadTracking() {
717
+ try {
718
+ return JSON.parse(fs3.readFileSync(TRACKING_FILE, "utf-8"));
719
+ } catch {
720
+ return {};
721
+ }
722
+ }
723
+ function saveTracking(state) {
724
+ fs3.mkdirSync(path4.dirname(TRACKING_FILE), { recursive: true });
725
+ fs3.writeFileSync(TRACKING_FILE, JSON.stringify(state));
726
+ }
727
+ function mergedTags(state) {
728
+ const all = /* @__PURE__ */ new Set();
729
+ for (const tags of Object.values(state)) {
730
+ for (const tag of tags) all.add(tag);
731
+ }
732
+ return [...all];
733
+ }
734
+ function startParallel(key, tags) {
735
+ const tracking = loadTracking();
736
+ tracking[key] = tags;
737
+ saveTracking(tracking);
738
+ const merged = mergedTags(tracking);
739
+ spawnSync3("timew", ["stop"], { stdio: "pipe" });
740
+ spawnSync3("timew", ["start", ...merged], { stdio: "pipe" });
741
+ }
742
+ function startSwitch(key, tags) {
743
+ saveTracking({ [key]: tags });
744
+ spawnSync3("timew", ["stop"], { stdio: "pipe" });
745
+ spawnSync3("timew", ["start", ...tags], { stdio: "pipe" });
746
+ }
747
+ function stopEntry(key) {
748
+ const tracking = loadTracking();
749
+ delete tracking[key];
750
+ saveTracking(tracking);
751
+ const remaining = mergedTags(tracking);
752
+ spawnSync3("timew", ["stop"], { stdio: "pipe" });
753
+ if (remaining.length > 0) {
754
+ spawnSync3("timew", ["start", ...remaining], { stdio: "pipe" });
755
+ }
756
+ }
757
+ function getActiveMeetings() {
758
+ const tracking = loadTracking();
759
+ return Object.entries(tracking).filter(([key]) => key.startsWith("meeting:")).map(([key, tags]) => ({ key, tags }));
760
+ }
761
+
710
762
  // src/cli.ts
711
- var HOOKS_DIR = path4.join(os3.homedir(), ".task", "hooks");
763
+ var HOOKS_DIR = path5.join(os4.homedir(), ".task", "hooks");
712
764
  var commands = {
713
765
  add: addBackend,
714
766
  install,
715
767
  sync,
768
+ meeting: meetingCmd,
716
769
  timewarrior: timewarriorCmd,
717
770
  which,
718
771
  config: showConfig
@@ -722,9 +775,10 @@ async function main() {
722
775
  if (!command || command === "--help") {
723
776
  console.log("Usage: tw-bridge <command>\n");
724
777
  console.log("Commands:");
725
- console.log(" add Add a new backend instance");
726
- console.log(" install Install Taskwarrior hooks and shell integration");
778
+ console.log(" add Add a new backend instance");
779
+ console.log(" install Install Taskwarrior hooks and shell integration");
727
780
  console.log(" sync Pull tasks from all backends");
781
+ console.log(" meeting Track meetings in Timewarrior (no task created)");
728
782
  console.log(" timewarrior Manage Timewarrior integration");
729
783
  console.log(" which Print the context for the current directory");
730
784
  console.log(" config Show current configuration");
@@ -800,18 +854,18 @@ Added backend "${name}" (adapter: ${adapterType})`);
800
854
  Use 'task context ${matchTag}' to switch to this project.`);
801
855
  }
802
856
  async function install() {
803
- fs3.mkdirSync(HOOKS_DIR, { recursive: true });
804
- const hookSource = path4.resolve(
805
- path4.dirname(new URL(import.meta.url).pathname),
857
+ fs4.mkdirSync(HOOKS_DIR, { recursive: true });
858
+ const hookSource = path5.resolve(
859
+ path5.dirname(new URL(import.meta.url).pathname),
806
860
  "hooks",
807
861
  "on-modify.js"
808
862
  );
809
- const hookTarget = path4.join(HOOKS_DIR, "on-modify.tw-bridge");
810
- if (fs3.existsSync(hookTarget)) {
811
- fs3.unlinkSync(hookTarget);
863
+ const hookTarget = path5.join(HOOKS_DIR, "on-modify.tw-bridge");
864
+ if (fs4.existsSync(hookTarget)) {
865
+ fs4.unlinkSync(hookTarget);
812
866
  }
813
- fs3.symlinkSync(hookSource, hookTarget);
814
- fs3.chmodSync(hookSource, 493);
867
+ fs4.symlinkSync(hookSource, hookTarget);
868
+ fs4.chmodSync(hookSource, 493);
815
869
  console.log(`Installed hook: ${hookTarget} -> ${hookSource}`);
816
870
  console.log("\nAdd these to your .taskrc:\n");
817
871
  console.log("# --- tw-bridge UDAs ---");
@@ -828,31 +882,133 @@ async function install() {
828
882
  console.log("urgency.user.tag.ready_for_beta.coefficient=-4.0");
829
883
  console.log("urgency.user.tag.in_beta.coefficient=-6.0");
830
884
  installTimewExtension();
831
- if (fs3.existsSync(STANDARD_TIMEW_HOOK)) {
885
+ if (fs4.existsSync(STANDARD_TIMEW_HOOK)) {
832
886
  console.log("\nTimewarrior hook detected. To enable tw-bridge time tracking:");
833
887
  console.log(" tw-bridge timewarrior enable");
834
888
  }
835
889
  installShellFunction();
836
890
  }
837
- var TIMEW_EXT_DIR = path4.join(os3.homedir(), ".timewarrior", "extensions");
891
+ var TIMEW_EXT_DIR = path5.join(os4.homedir(), ".timewarrior", "extensions");
838
892
  function installTimewExtension() {
839
- fs3.mkdirSync(TIMEW_EXT_DIR, { recursive: true });
840
- const extSource = path4.resolve(
841
- path4.dirname(new URL(import.meta.url).pathname),
893
+ fs4.mkdirSync(TIMEW_EXT_DIR, { recursive: true });
894
+ const extSource = path5.resolve(
895
+ path5.dirname(new URL(import.meta.url).pathname),
842
896
  "extensions",
843
897
  "bridge.js"
844
898
  );
845
- const extTarget = path4.join(TIMEW_EXT_DIR, "bridge");
846
- if (fs3.existsSync(extTarget)) {
847
- fs3.unlinkSync(extTarget);
899
+ const extTarget = path5.join(TIMEW_EXT_DIR, "bridge");
900
+ if (fs4.existsSync(extTarget)) {
901
+ fs4.unlinkSync(extTarget);
848
902
  }
849
- fs3.symlinkSync(extSource, extTarget);
850
- fs3.chmodSync(extSource, 493);
903
+ fs4.symlinkSync(extSource, extTarget);
904
+ fs4.chmodSync(extSource, 493);
851
905
  console.log(`
852
906
  Installed Timewarrior extension: ${extTarget} -> ${extSource}`);
853
907
  console.log(" Usage: timew bridge [task-time|wall-time] [project-filter]");
854
908
  }
855
- var STANDARD_TIMEW_HOOK = path4.join(HOOKS_DIR, "on-modify.timewarrior");
909
+ function sanitizeMeetingName(name) {
910
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
911
+ }
912
+ function detectProjectContext() {
913
+ const cwd = process.cwd();
914
+ const config = loadConfig();
915
+ for (const [, backend] of Object.entries(config.backends)) {
916
+ const backendCwd = backend.config?.cwd;
917
+ if (!backendCwd) continue;
918
+ const resolved = path5.resolve(backendCwd);
919
+ if (cwd === resolved || cwd.startsWith(resolved + path5.sep)) {
920
+ return backend.match.tags?.[0] ?? null;
921
+ }
922
+ }
923
+ return null;
924
+ }
925
+ async function meetingCmd() {
926
+ const sub = process.argv[3];
927
+ if (!sub || sub === "--help") {
928
+ console.log("Usage: tw-bridge meeting <subcommand>\n");
929
+ console.log("Subcommands:");
930
+ console.log(" start <name> [--switch] Start tracking a meeting");
931
+ console.log(" stop [name] Stop a meeting (or all if no name)");
932
+ console.log(" list Show active meetings");
933
+ console.log("\nMeetings are tracked in Timewarrior only \u2014 no Taskwarrior task is created.");
934
+ console.log("By default, meetings run in parallel with active tasks.");
935
+ console.log("Use --switch to pause the current task instead.");
936
+ return;
937
+ }
938
+ const config = loadConfig();
939
+ if (!config.timewarrior?.enabled) {
940
+ console.error("Timewarrior is not enabled. Run: tw-bridge timewarrior enable");
941
+ process.exit(1);
942
+ }
943
+ if (sub === "start") {
944
+ const nameArg = process.argv.slice(4).filter((a) => !a.startsWith("--")).join(" ");
945
+ if (!nameArg) {
946
+ console.error("Usage: tw-bridge meeting start <name>");
947
+ process.exit(1);
948
+ }
949
+ const switchMode = process.argv.includes("--switch") || process.argv.includes("-s");
950
+ const tag = sanitizeMeetingName(nameArg);
951
+ const key = `meeting:${tag}`;
952
+ const tags = ["meeting", tag];
953
+ const project = detectProjectContext();
954
+ if (project) tags.push(project);
955
+ if (switchMode) {
956
+ startSwitch(key, tags);
957
+ } else {
958
+ startParallel(key, tags);
959
+ }
960
+ console.log(`Meeting started: ${nameArg}`);
961
+ console.log(` Tags: ${tags.join(" ")}`);
962
+ if (!switchMode) {
963
+ console.log(" Mode: parallel (active tasks continue tracking)");
964
+ } else {
965
+ console.log(" Mode: switch (active tasks paused)");
966
+ }
967
+ return;
968
+ }
969
+ if (sub === "stop") {
970
+ const nameArg = process.argv.slice(4).join(" ").trim();
971
+ const active = getActiveMeetings();
972
+ if (active.length === 0) {
973
+ console.log("No active meetings.");
974
+ return;
975
+ }
976
+ if (nameArg) {
977
+ const tag = sanitizeMeetingName(nameArg);
978
+ const key = `meeting:${tag}`;
979
+ const match = active.find((m) => m.key === key);
980
+ if (!match) {
981
+ console.error(`No active meeting matching "${nameArg}".`);
982
+ console.error(`Active meetings: ${active.map((m) => m.key.replace("meeting:", "")).join(", ")}`);
983
+ process.exit(1);
984
+ }
985
+ stopEntry(key);
986
+ console.log(`Meeting stopped: ${nameArg}`);
987
+ } else {
988
+ for (const m of active) {
989
+ stopEntry(m.key);
990
+ }
991
+ console.log(`Stopped ${active.length} meeting(s): ${active.map((m) => m.key.replace("meeting:", "")).join(", ")}`);
992
+ }
993
+ return;
994
+ }
995
+ if (sub === "list") {
996
+ const active = getActiveMeetings();
997
+ if (active.length === 0) {
998
+ console.log("No active meetings.");
999
+ return;
1000
+ }
1001
+ console.log("Active meetings:");
1002
+ for (const m of active) {
1003
+ const name = m.key.replace("meeting:", "");
1004
+ console.log(` ${name} (${m.tags.join(" ")})`);
1005
+ }
1006
+ return;
1007
+ }
1008
+ console.error(`Unknown subcommand: ${sub}`);
1009
+ process.exit(1);
1010
+ }
1011
+ var STANDARD_TIMEW_HOOK = path5.join(HOOKS_DIR, "on-modify.timewarrior");
856
1012
  async function timewarriorCmd() {
857
1013
  const sub = process.argv[3];
858
1014
  if (!sub || sub === "--help") {
@@ -875,8 +1031,8 @@ async function timewarriorCmd() {
875
1031
  console.log("Timewarrior: enabled");
876
1032
  console.log(" Parallel tracking: use `task start <id> --parallel`");
877
1033
  }
878
- const hookExists = fs3.existsSync(STANDARD_TIMEW_HOOK);
879
- const hookDisabled = fs3.existsSync(STANDARD_TIMEW_HOOK + ".disabled");
1034
+ const hookExists = fs4.existsSync(STANDARD_TIMEW_HOOK);
1035
+ const hookDisabled = fs4.existsSync(STANDARD_TIMEW_HOOK + ".disabled");
880
1036
  if (hookExists) {
881
1037
  console.log(`Standard hook: active (${STANDARD_TIMEW_HOOK})`);
882
1038
  if (tw?.enabled) {
@@ -894,9 +1050,9 @@ async function timewarriorCmd() {
894
1050
  const configPath = saveConfig(config);
895
1051
  console.log("Timewarrior tracking enabled");
896
1052
  console.log(`Config: ${configPath}`);
897
- if (fs3.existsSync(STANDARD_TIMEW_HOOK)) {
1053
+ if (fs4.existsSync(STANDARD_TIMEW_HOOK)) {
898
1054
  const disabled = STANDARD_TIMEW_HOOK + ".disabled";
899
- fs3.renameSync(STANDARD_TIMEW_HOOK, disabled);
1055
+ fs4.renameSync(STANDARD_TIMEW_HOOK, disabled);
900
1056
  console.log(`
901
1057
  Disabled standard hook: ${STANDARD_TIMEW_HOOK} -> .disabled`);
902
1058
  console.log("tw-bridge will handle Timewarrior tracking directly.");
@@ -909,8 +1065,8 @@ Disabled standard hook: ${STANDARD_TIMEW_HOOK} -> .disabled`);
909
1065
  console.log("Timewarrior tracking disabled");
910
1066
  console.log(`Config: ${configPath}`);
911
1067
  const disabled = STANDARD_TIMEW_HOOK + ".disabled";
912
- if (fs3.existsSync(disabled)) {
913
- fs3.renameSync(disabled, STANDARD_TIMEW_HOOK);
1068
+ if (fs4.existsSync(disabled)) {
1069
+ fs4.renameSync(disabled, STANDARD_TIMEW_HOOK);
914
1070
  console.log(`
915
1071
  Restored standard hook: ${STANDARD_TIMEW_HOOK}`);
916
1072
  }
@@ -940,28 +1096,28 @@ task() {
940
1096
  var SHELL_MARKER = "# tw-bridge: auto-context task wrapper";
941
1097
  function installShellFunction() {
942
1098
  const shell = process.env.SHELL ?? "/bin/bash";
943
- const home = os3.homedir();
1099
+ const home = os4.homedir();
944
1100
  let rcFile;
945
1101
  if (shell.endsWith("zsh")) {
946
- rcFile = path4.join(home, ".zshrc");
1102
+ rcFile = path5.join(home, ".zshrc");
947
1103
  } else {
948
- rcFile = path4.join(home, ".bashrc");
1104
+ rcFile = path5.join(home, ".bashrc");
949
1105
  }
950
- const existing = fs3.existsSync(rcFile) ? fs3.readFileSync(rcFile, "utf-8") : "";
1106
+ const existing = fs4.existsSync(rcFile) ? fs4.readFileSync(rcFile, "utf-8") : "";
951
1107
  if (existing.includes(SHELL_MARKER)) {
952
1108
  console.log(`
953
1109
  Shell integration already installed in ${rcFile}`);
954
1110
  return;
955
1111
  }
956
- fs3.appendFileSync(rcFile, "\n" + SHELL_FUNCTION + "\n");
1112
+ fs4.appendFileSync(rcFile, "\n" + SHELL_FUNCTION + "\n");
957
1113
  console.log(`
958
1114
  Shell integration installed in ${rcFile}`);
959
1115
  console.log("Restart your shell or run: source " + rcFile);
960
1116
  }
961
- var SEEN_FILE = path4.join(os3.homedir(), ".config", "tw-bridge", ".seen-dirs");
1117
+ var SEEN_FILE = path5.join(os4.homedir(), ".config", "tw-bridge", ".seen-dirs");
962
1118
  function loadSeenDirs() {
963
1119
  try {
964
- const raw = fs3.readFileSync(SEEN_FILE, "utf-8");
1120
+ const raw = fs4.readFileSync(SEEN_FILE, "utf-8");
965
1121
  return new Set(raw.split("\n").filter(Boolean));
966
1122
  } catch {
967
1123
  return /* @__PURE__ */ new Set();
@@ -971,15 +1127,15 @@ function markDirSeen(dir) {
971
1127
  const seen = loadSeenDirs();
972
1128
  if (seen.has(dir)) return;
973
1129
  seen.add(dir);
974
- fs3.mkdirSync(path4.dirname(SEEN_FILE), { recursive: true });
975
- fs3.writeFileSync(SEEN_FILE, [...seen].join("\n") + "\n");
1130
+ fs4.mkdirSync(path5.dirname(SEEN_FILE), { recursive: true });
1131
+ fs4.writeFileSync(SEEN_FILE, [...seen].join("\n") + "\n");
976
1132
  }
977
1133
  function isGitRepo(dir) {
978
1134
  try {
979
1135
  let current = dir;
980
- while (current !== path4.dirname(current)) {
981
- if (fs3.existsSync(path4.join(current, ".git"))) return true;
982
- current = path4.dirname(current);
1136
+ while (current !== path5.dirname(current)) {
1137
+ if (fs4.existsSync(path5.join(current, ".git"))) return true;
1138
+ current = path5.dirname(current);
983
1139
  }
984
1140
  return false;
985
1141
  } catch {
@@ -992,8 +1148,8 @@ async function which() {
992
1148
  for (const [_name, backend] of Object.entries(config.backends)) {
993
1149
  const backendCwd = backend.config?.cwd;
994
1150
  if (!backendCwd) continue;
995
- const resolved = path4.resolve(backendCwd);
996
- if (cwd === resolved || cwd.startsWith(resolved + path4.sep)) {
1151
+ const resolved = path5.resolve(backendCwd);
1152
+ if (cwd === resolved || cwd.startsWith(resolved + path5.sep)) {
997
1153
  const contextTag = backend.match.tags?.[0];
998
1154
  if (contextTag) {
999
1155
  process.stdout.write(contextTag);
@@ -1009,15 +1165,15 @@ async function which() {
1009
1165
  const seen = loadSeenDirs();
1010
1166
  let gitRoot = cwd;
1011
1167
  let current = cwd;
1012
- while (current !== path4.dirname(current)) {
1013
- if (fs3.existsSync(path4.join(current, ".git"))) {
1168
+ while (current !== path5.dirname(current)) {
1169
+ if (fs4.existsSync(path5.join(current, ".git"))) {
1014
1170
  gitRoot = current;
1015
1171
  break;
1016
1172
  }
1017
- current = path4.dirname(current);
1173
+ current = path5.dirname(current);
1018
1174
  }
1019
1175
  if (!seen.has(gitRoot)) {
1020
- const dirName = path4.basename(gitRoot);
1176
+ const dirName = path5.basename(gitRoot);
1021
1177
  process.stderr.write(
1022
1178
  `tw-bridge: unconfigured project "${dirName}". Run: tw-bridge add ${dirName} --adapter ghp
1023
1179
  `
@@ -1,10 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/hooks/on-modify.ts
4
- import fs3 from "fs";
5
- import path4 from "path";
6
- import os3 from "os";
7
- import { spawnSync as spawnSync2 } from "child_process";
4
+ import fs4 from "fs";
5
+ import { spawnSync as spawnSync3 } from "child_process";
8
6
 
9
7
  // src/config.ts
10
8
  import fs from "fs";
@@ -601,7 +599,11 @@ async function resolveAdapter(task, config) {
601
599
  return adapter;
602
600
  }
603
601
 
604
- // src/hooks/on-modify.ts
602
+ // src/tracking.ts
603
+ import fs3 from "fs";
604
+ import path4 from "path";
605
+ import os3 from "os";
606
+ import { spawnSync as spawnSync2 } from "child_process";
605
607
  var TRACKING_FILE = path4.join(os3.homedir(), ".config", "tw-bridge", "tracking.json");
606
608
  function loadTracking() {
607
609
  try {
@@ -621,19 +623,6 @@ function mergedTags(state) {
621
623
  }
622
624
  return [...all];
623
625
  }
624
- function timewTags(task) {
625
- const tags = [];
626
- if (task.backend) tags.push(task.backend);
627
- if (task.backend_id) tags.push(`#${task.backend_id}`);
628
- if (task.project) tags.push(task.project);
629
- for (const tag of task.tags ?? []) {
630
- if (!tag.includes("_")) tags.push(tag);
631
- }
632
- return [...new Set(tags)];
633
- }
634
- function taskKey(task) {
635
- return task.backend_id ? `#${task.backend_id}` : task.uuid;
636
- }
637
626
  function getCurrentInterval() {
638
627
  const result = spawnSync2("timew", ["export"], {
639
628
  encoding: "utf-8",
@@ -647,6 +636,21 @@ function getCurrentInterval() {
647
636
  return null;
648
637
  }
649
638
  }
639
+
640
+ // src/hooks/on-modify.ts
641
+ function timewTags(task) {
642
+ const tags = [];
643
+ if (task.backend) tags.push(task.backend);
644
+ if (task.backend_id) tags.push(`#${task.backend_id}`);
645
+ if (task.project) tags.push(task.project);
646
+ for (const tag of task.tags ?? []) {
647
+ if (!tag.includes("_")) tags.push(tag);
648
+ }
649
+ return [...new Set(tags)];
650
+ }
651
+ function taskKey(task) {
652
+ return task.backend_id ? `#${task.backend_id}` : task.uuid;
653
+ }
650
654
  function formatDuration(isoStart) {
651
655
  const start = new Date(
652
656
  isoStart.replace(
@@ -685,10 +689,10 @@ tw-bridge: Currently tracking: ${tags} (${duration})`
685
689
  lines.push(" [s] Switch \u2014 stop current, start new task (default)");
686
690
  lines.push(" [p] Parallel \u2014 add new task to current tracking");
687
691
  lines.push("");
688
- fs3.writeSync(ttyFd, lines.join("\n"));
689
- fs3.writeSync(ttyFd, " > ");
692
+ fs4.writeSync(ttyFd, lines.join("\n"));
693
+ fs4.writeSync(ttyFd, " > ");
690
694
  const buf = Buffer.alloc(64);
691
- const bytesRead = fs3.readSync(ttyFd, buf, 0, 64, null);
695
+ const bytesRead = fs4.readSync(ttyFd, buf, 0, 64, null);
692
696
  const answer = buf.toString("utf-8", 0, bytesRead).trim().toLowerCase();
693
697
  return answer.startsWith("p") ? "parallel" : "switch";
694
698
  }
@@ -713,12 +717,12 @@ function handleTimewarriorStart(config, newTask, ttyFd) {
713
717
  tracking[key] = newTags;
714
718
  saveTracking(tracking);
715
719
  const merged = mergedTags(tracking);
716
- spawnSync2("timew", ["stop"], { stdio: "pipe" });
717
- spawnSync2("timew", ["start", ...merged], { stdio: "pipe" });
720
+ spawnSync3("timew", ["stop"], { stdio: "pipe" });
721
+ spawnSync3("timew", ["start", ...merged], { stdio: "pipe" });
718
722
  } else {
719
723
  saveTracking({ [key]: newTags });
720
- spawnSync2("timew", ["stop"], { stdio: "pipe" });
721
- spawnSync2("timew", ["start", ...newTags], { stdio: "pipe" });
724
+ spawnSync3("timew", ["stop"], { stdio: "pipe" });
725
+ spawnSync3("timew", ["start", ...newTags], { stdio: "pipe" });
722
726
  }
723
727
  }
724
728
  function handleTimewarriorStop(config, task) {
@@ -728,13 +732,13 @@ function handleTimewarriorStop(config, task) {
728
732
  delete tracking[key];
729
733
  saveTracking(tracking);
730
734
  const remaining = mergedTags(tracking);
731
- spawnSync2("timew", ["stop"], { stdio: "pipe" });
735
+ spawnSync3("timew", ["stop"], { stdio: "pipe" });
732
736
  if (remaining.length > 0) {
733
- spawnSync2("timew", ["start", ...remaining], { stdio: "pipe" });
737
+ spawnSync3("timew", ["start", ...remaining], { stdio: "pipe" });
734
738
  }
735
739
  }
736
740
  async function main() {
737
- const input = fs3.readFileSync("/dev/stdin", "utf-8").trim().split("\n");
741
+ const input = fs4.readFileSync("/dev/stdin", "utf-8").trim().split("\n");
738
742
  const oldTask = JSON.parse(input[0]);
739
743
  const newTask = JSON.parse(input[1]);
740
744
  process.stdout.write(JSON.stringify(newTask) + "\n");
@@ -745,7 +749,7 @@ async function main() {
745
749
  let ttyFd = null;
746
750
  if (wasStarted) {
747
751
  try {
748
- ttyFd = fs3.openSync("/dev/tty", "r+");
752
+ ttyFd = fs4.openSync("/dev/tty", "r+");
749
753
  } catch {
750
754
  }
751
755
  }
@@ -773,7 +777,7 @@ async function main() {
773
777
  process.stderr.write(`tw-bridge: ${msg}
774
778
  `);
775
779
  } finally {
776
- if (ttyFd !== null) fs3.closeSync(ttyFd);
780
+ if (ttyFd !== null) fs4.closeSync(ttyFd);
777
781
  }
778
782
  }
779
783
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bretwardjames/tw-bridge",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Taskwarrior backend bridge — unified sync and hooks for multiple task management platforms",
5
5
  "type": "module",
6
6
  "license": "MIT",