@jamesaphoenix/tx-cli 0.4.3 → 0.4.5

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 (89) hide show
  1. package/dist/cli.d.ts +1 -1
  2. package/dist/cli.js +2 -7
  3. package/dist/commands/attempt.js +5 -4
  4. package/dist/commands/bulk.js +8 -7
  5. package/dist/commands/claim.js +4 -3
  6. package/dist/commands/cycle.js +2 -1
  7. package/dist/commands/dep.js +3 -2
  8. package/dist/commands/doc.js +13 -12
  9. package/dist/commands/hierarchy.js +3 -2
  10. package/dist/commands/invariant.js +2 -1
  11. package/dist/commands/sync-platform.js +4 -3
  12. package/dist/commands/sync.js +3 -2
  13. package/dist/commands/task.js +8 -7
  14. package/dist/commands/trace.js +14 -13
  15. package/dist/commands/validate.js +2 -1
  16. package/package.json +10 -3
  17. package/dist/cli-exit.d.ts.map +0 -1
  18. package/dist/cli-exit.js.map +0 -1
  19. package/dist/cli.d.ts.map +0 -1
  20. package/dist/cli.js.map +0 -1
  21. package/dist/commands/attempt.d.ts.map +0 -1
  22. package/dist/commands/attempt.js.map +0 -1
  23. package/dist/commands/bulk.d.ts.map +0 -1
  24. package/dist/commands/bulk.js.map +0 -1
  25. package/dist/commands/claim.d.ts.map +0 -1
  26. package/dist/commands/claim.js.map +0 -1
  27. package/dist/commands/compact.d.ts.map +0 -1
  28. package/dist/commands/compact.js.map +0 -1
  29. package/dist/commands/coordinator.d.ts.map +0 -1
  30. package/dist/commands/coordinator.js.map +0 -1
  31. package/dist/commands/cycle.d.ts.map +0 -1
  32. package/dist/commands/cycle.js.map +0 -1
  33. package/dist/commands/daemon.d.ts.map +0 -1
  34. package/dist/commands/daemon.js.map +0 -1
  35. package/dist/commands/dashboard.d.ts.map +0 -1
  36. package/dist/commands/dashboard.js.map +0 -1
  37. package/dist/commands/dashboard.test.d.ts +0 -2
  38. package/dist/commands/dashboard.test.d.ts.map +0 -1
  39. package/dist/commands/dashboard.test.js +0 -99
  40. package/dist/commands/dashboard.test.js.map +0 -1
  41. package/dist/commands/dep.d.ts.map +0 -1
  42. package/dist/commands/dep.js.map +0 -1
  43. package/dist/commands/doc.d.ts.map +0 -1
  44. package/dist/commands/doc.js.map +0 -1
  45. package/dist/commands/doctor.d.ts.map +0 -1
  46. package/dist/commands/doctor.js.map +0 -1
  47. package/dist/commands/graph.d.ts.map +0 -1
  48. package/dist/commands/graph.js.map +0 -1
  49. package/dist/commands/hierarchy.d.ts.map +0 -1
  50. package/dist/commands/hierarchy.js.map +0 -1
  51. package/dist/commands/hooks.d.ts.map +0 -1
  52. package/dist/commands/hooks.js.map +0 -1
  53. package/dist/commands/invariant.d.ts.map +0 -1
  54. package/dist/commands/invariant.js.map +0 -1
  55. package/dist/commands/learning.d.ts.map +0 -1
  56. package/dist/commands/learning.js.map +0 -1
  57. package/dist/commands/migrate.d.ts.map +0 -1
  58. package/dist/commands/migrate.js.map +0 -1
  59. package/dist/commands/orchestrator.d.ts.map +0 -1
  60. package/dist/commands/orchestrator.js.map +0 -1
  61. package/dist/commands/stats.d.ts.map +0 -1
  62. package/dist/commands/stats.js.map +0 -1
  63. package/dist/commands/sync-platform.d.ts.map +0 -1
  64. package/dist/commands/sync-platform.js.map +0 -1
  65. package/dist/commands/sync.d.ts.map +0 -1
  66. package/dist/commands/sync.js.map +0 -1
  67. package/dist/commands/task.d.ts.map +0 -1
  68. package/dist/commands/task.js.map +0 -1
  69. package/dist/commands/test.d.ts.map +0 -1
  70. package/dist/commands/test.js.map +0 -1
  71. package/dist/commands/trace.d.ts.map +0 -1
  72. package/dist/commands/trace.js.map +0 -1
  73. package/dist/commands/validate.d.ts.map +0 -1
  74. package/dist/commands/validate.js.map +0 -1
  75. package/dist/commands/worker.d.ts.map +0 -1
  76. package/dist/commands/worker.js.map +0 -1
  77. package/dist/help.d.ts.map +0 -1
  78. package/dist/help.js.map +0 -1
  79. package/dist/output.d.ts.map +0 -1
  80. package/dist/output.js.map +0 -1
  81. package/dist/tx +0 -0
  82. package/dist/utils/parse.d.ts.map +0 -1
  83. package/dist/utils/parse.js.map +0 -1
  84. package/dist/utils/parse.test.d.ts +0 -2
  85. package/dist/utils/parse.test.d.ts.map +0 -1
  86. package/dist/utils/parse.test.js +0 -140
  87. package/dist/utils/parse.test.js.map +0 -1
  88. package/dist/version.d.ts.map +0 -1
  89. package/dist/version.js.map +0 -1
package/dist/cli.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
  /**
3
3
  * TX CLI - Task management for AI agents and humans
4
4
  *
package/dist/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
  /**
3
3
  * TX CLI - Task management for AI agents and humans
4
4
  *
@@ -291,6 +291,7 @@ const errorExitCodes = {
291
291
  ClaimNotFoundError: 2,
292
292
  DocNotFoundError: 2,
293
293
  InvariantNotFoundError: 2,
294
+ HasChildrenError: 1,
294
295
  ValidationError: 1,
295
296
  CircularDependencyError: 1,
296
297
  DatabaseError: 1,
@@ -315,12 +316,6 @@ Effect.catchAll((error) => {
315
316
  _exitCode = errorExitCodes[tag] ?? 1;
316
317
  return Effect.void;
317
318
  }
318
- if (tag === "HasChildrenError") {
319
- console.error(err.message ?? `Cannot delete task with children`);
320
- console.error("Hint: use --cascade to delete with all children, or delete/move children first.");
321
- _exitCode = 1;
322
- return Effect.void;
323
- }
324
319
  console.error(`Error: ${err.message ?? String(error)}`);
325
320
  _exitCode = 1;
326
321
  return Effect.void;
@@ -5,12 +5,13 @@ import { Effect } from "effect";
5
5
  import { AttemptService, TaskService } from "@jamesaphoenix/tx-core";
6
6
  import { toJson } from "../output.js";
7
7
  import { flag, parseTaskId } from "../utils/parse.js";
8
+ import { CliExitError } from "../cli-exit.js";
8
9
  export const tryAttempt = (pos, flags) => Effect.gen(function* () {
9
10
  const rawTaskId = pos[0];
10
11
  const approach = pos[1];
11
12
  if (!rawTaskId || !approach) {
12
13
  console.error("Usage: tx try <task-id> <approach> --failed|--succeeded [reason]");
13
- process.exit(1);
14
+ throw new CliExitError(1);
14
15
  }
15
16
  const taskId = parseTaskId(rawTaskId);
16
17
  // Check for --failed and --succeeded flags
@@ -23,11 +24,11 @@ export const tryAttempt = (pos, flags) => Effect.gen(function* () {
23
24
  // Validate mutually exclusive flags
24
25
  if (hasFailedFlag && hasSucceededFlag) {
25
26
  console.error("Error: --failed and --succeeded are mutually exclusive");
26
- process.exit(1);
27
+ throw new CliExitError(1);
27
28
  }
28
29
  if (!hasFailedFlag && !hasSucceededFlag) {
29
30
  console.error("Error: Must specify either --failed or --succeeded");
30
- process.exit(1);
31
+ throw new CliExitError(1);
31
32
  }
32
33
  const outcome = hasFailedFlag ? "failed" : "succeeded";
33
34
  // Reason can be the value of the flag (if string) or positional arg
@@ -61,7 +62,7 @@ export const attempts = (pos, flags) => Effect.gen(function* () {
61
62
  const rawTaskId = pos[0];
62
63
  if (!rawTaskId) {
63
64
  console.error("Usage: tx attempts <task-id> [--json]");
64
- process.exit(1);
65
+ throw new CliExitError(1);
65
66
  }
66
67
  const taskId = parseTaskId(rawTaskId);
67
68
  const attemptSvc = yield* AttemptService;
@@ -5,6 +5,7 @@ import { Effect } from "effect";
5
5
  import { TaskService, ReadyService } from "@jamesaphoenix/tx-core";
6
6
  import { flag, parseTaskId } from "../utils/parse.js";
7
7
  import { toJson } from "../output.js";
8
+ import { CliExitError } from "../cli-exit.js";
8
9
  /** Extract error message safely without unsafe 'as' casts. */
9
10
  function extractErrorMessage(error) {
10
11
  if (error instanceof Error)
@@ -30,7 +31,7 @@ export const bulk = (pos, flags) => Effect.gen(function* () {
30
31
  if (!subcommand) {
31
32
  console.error("Usage: tx bulk <done|score|reset|delete> <id...> [options]");
32
33
  console.error("Run 'tx bulk --help' for more information");
33
- process.exit(1);
34
+ throw new CliExitError(1);
34
35
  }
35
36
  switch (subcommand) {
36
37
  case "done":
@@ -44,13 +45,13 @@ export const bulk = (pos, flags) => Effect.gen(function* () {
44
45
  default:
45
46
  console.error(`Unknown bulk subcommand: ${subcommand}`);
46
47
  console.error("Valid subcommands: done, score, reset, delete");
47
- process.exit(1);
48
+ throw new CliExitError(1);
48
49
  }
49
50
  });
50
51
  const bulkDone = (pos, flags) => Effect.gen(function* () {
51
52
  if (pos.length === 0) {
52
53
  console.error("Usage: tx bulk done <id> [id...] [--json]");
53
- process.exit(1);
54
+ throw new CliExitError(1);
54
55
  }
55
56
  const taskSvc = yield* TaskService;
56
57
  const readySvc = yield* ReadyService;
@@ -90,12 +91,12 @@ const bulkDone = (pos, flags) => Effect.gen(function* () {
90
91
  const bulkScore = (pos, flags) => Effect.gen(function* () {
91
92
  if (pos.length < 2) {
92
93
  console.error("Usage: tx bulk score <score> <id> [id...] [--json]");
93
- process.exit(1);
94
+ throw new CliExitError(1);
94
95
  }
95
96
  const scoreVal = parseInt(pos[0], 10);
96
97
  if (Number.isNaN(scoreVal)) {
97
98
  console.error(`Invalid score: "${pos[0]}" is not a valid number`);
98
- process.exit(1);
99
+ throw new CliExitError(1);
99
100
  }
100
101
  const ids = pos.slice(1);
101
102
  const taskSvc = yield* TaskService;
@@ -120,7 +121,7 @@ const bulkScore = (pos, flags) => Effect.gen(function* () {
120
121
  const bulkReset = (pos, flags) => Effect.gen(function* () {
121
122
  if (pos.length === 0) {
122
123
  console.error("Usage: tx bulk reset <id> [id...] [--json]");
123
- process.exit(1);
124
+ throw new CliExitError(1);
124
125
  }
125
126
  const taskSvc = yield* TaskService;
126
127
  const result = { succeeded: [], failed: [] };
@@ -144,7 +145,7 @@ const bulkReset = (pos, flags) => Effect.gen(function* () {
144
145
  const bulkDelete = (pos, flags) => Effect.gen(function* () {
145
146
  if (pos.length === 0) {
146
147
  console.error("Usage: tx bulk delete <id> [id...] [--json]");
147
- process.exit(1);
148
+ throw new CliExitError(1);
148
149
  }
149
150
  const taskSvc = yield* TaskService;
150
151
  const result = { succeeded: [], failed: [] };
@@ -9,6 +9,7 @@ import { Effect } from "effect";
9
9
  import { ClaimService } from "@jamesaphoenix/tx-core";
10
10
  import { toJson } from "../output.js";
11
11
  import { flag, parseIntOpt, parseTaskId } from "../utils/parse.js";
12
+ import { CliExitError } from "../cli-exit.js";
12
13
  /**
13
14
  * Claim a task for a worker with a lease.
14
15
  *
@@ -27,7 +28,7 @@ export const claim = (pos, flags) => Effect.gen(function* () {
27
28
  console.error("Options:");
28
29
  console.error(" --lease <m> Lease duration in minutes (default: 30)");
29
30
  console.error(" --json Output in JSON format");
30
- process.exit(1);
31
+ throw new CliExitError(1);
31
32
  }
32
33
  const taskId = parseTaskId(rawTaskId);
33
34
  const leaseMinutes = parseIntOpt(flags, "lease", "lease");
@@ -54,7 +55,7 @@ export const claimRelease = (pos, flags) => Effect.gen(function* () {
54
55
  const workerId = pos[1];
55
56
  if (!rawTaskId || !workerId) {
56
57
  console.error("Usage: tx claim:release <task-id> <worker-id> [--json]");
57
- process.exit(1);
58
+ throw new CliExitError(1);
58
59
  }
59
60
  const taskId = parseTaskId(rawTaskId);
60
61
  const svc = yield* ClaimService;
@@ -79,7 +80,7 @@ export const claimRenew = (pos, flags) => Effect.gen(function* () {
79
80
  const workerId = pos[1];
80
81
  if (!rawTaskId || !workerId) {
81
82
  console.error("Usage: tx claim:renew <task-id> <worker-id> [--json]");
82
- process.exit(1);
83
+ throw new CliExitError(1);
83
84
  }
84
85
  const taskId = parseTaskId(rawTaskId);
85
86
  const svc = yield* ClaimService;
@@ -8,6 +8,7 @@ import { Effect } from "effect";
8
8
  import { CycleScanService } from "@jamesaphoenix/tx-core";
9
9
  import { toJson } from "../output.js";
10
10
  import { flag, opt, parseIntOpt } from "../utils/parse.js";
11
+ import { CliExitError } from "../cli-exit.js";
11
12
  /** Dispatch cycle command. */
12
13
  export const cycle = (_pos, flags) => {
13
14
  const taskPrompt = opt(flags, "task-prompt");
@@ -32,7 +33,7 @@ export const cycle = (_pos, flags) => {
32
33
  console.error(" --dry-run Report only, no DB writes");
33
34
  console.error(" --score <N> Base score for new tasks (default: 500)");
34
35
  console.error(" --json Output as JSON");
35
- process.exit(1);
36
+ throw new CliExitError(1);
36
37
  });
37
38
  }
38
39
  return Effect.gen(function* () {
@@ -5,12 +5,13 @@ import { Effect } from "effect";
5
5
  import { TaskService, DependencyService } from "@jamesaphoenix/tx-core";
6
6
  import { toJson } from "../output.js";
7
7
  import { flag, parseTaskId } from "../utils/parse.js";
8
+ import { CliExitError } from "../cli-exit.js";
8
9
  export const block = (pos, flags) => Effect.gen(function* () {
9
10
  const rawId = pos[0];
10
11
  const rawBlocker = pos[1];
11
12
  if (!rawId || !rawBlocker) {
12
13
  console.error("Usage: tx block <task-id> <blocker-id> [--json]");
13
- process.exit(1);
14
+ throw new CliExitError(1);
14
15
  }
15
16
  const id = parseTaskId(rawId);
16
17
  const blocker = parseTaskId(rawBlocker);
@@ -31,7 +32,7 @@ export const unblock = (pos, flags) => Effect.gen(function* () {
31
32
  const rawBlocker = pos[1];
32
33
  if (!rawId || !rawBlocker) {
33
34
  console.error("Usage: tx unblock <task-id> <blocker-id> [--json]");
34
- process.exit(1);
35
+ throw new CliExitError(1);
35
36
  }
36
37
  const id = parseTaskId(rawId);
37
38
  const blocker = parseTaskId(rawBlocker);
@@ -9,6 +9,7 @@ import { DocService } from "@jamesaphoenix/tx-core";
9
9
  import { DOC_KINDS } from "@jamesaphoenix/tx-types";
10
10
  import { toJson } from "../output.js";
11
11
  import { flag, opt } from "../utils/parse.js";
12
+ import { CliExitError } from "../cli-exit.js";
12
13
  const docKindStrings = DOC_KINDS;
13
14
  /** Dispatch doc subcommands. */
14
15
  export const doc = (pos, flags) => {
@@ -31,7 +32,7 @@ export const doc = (pos, flags) => {
31
32
  return Effect.sync(() => {
32
33
  console.error(`Unknown doc subcommand: ${sub ?? "(none)"}`);
33
34
  console.error("Run 'tx doc --help' for usage information");
34
- process.exit(1);
35
+ throw new CliExitError(1);
35
36
  });
36
37
  }
37
38
  };
@@ -41,11 +42,11 @@ const docAdd = (pos, flags) => Effect.gen(function* () {
41
42
  if (!kind || !name) {
42
43
  console.error("Usage: tx doc add <kind> <name> [--title <title>]");
43
44
  console.error(" Kinds: overview, prd, design");
44
- process.exit(1);
45
+ throw new CliExitError(1);
45
46
  }
46
47
  if (!docKindStrings.includes(kind)) {
47
48
  console.error(`Invalid kind: ${kind}. Must be one of: ${DOC_KINDS.join(", ")}`);
48
- process.exit(1);
49
+ throw new CliExitError(1);
49
50
  }
50
51
  const title = opt(flags, "title", "t") ?? name;
51
52
  // Generate template YAML
@@ -70,7 +71,7 @@ const docEdit = (pos, _flags) => Effect.gen(function* () {
70
71
  const name = pos[0];
71
72
  if (!name) {
72
73
  console.error("Usage: tx doc edit <name>");
73
- process.exit(1);
74
+ throw new CliExitError(1);
74
75
  }
75
76
  const svc = yield* DocService;
76
77
  const doc = yield* svc.get(name);
@@ -81,14 +82,14 @@ const docEdit = (pos, _flags) => Effect.gen(function* () {
81
82
  }
82
83
  catch {
83
84
  console.error(`Failed to open editor: ${editor}`);
84
- process.exit(1);
85
+ throw new CliExitError(1);
85
86
  }
86
87
  });
87
88
  const docShow = (pos, flags) => Effect.gen(function* () {
88
89
  const name = pos[0];
89
90
  if (!name) {
90
91
  console.error("Usage: tx doc show <name> [--md] [--json]");
91
- process.exit(1);
92
+ throw new CliExitError(1);
92
93
  }
93
94
  const svc = yield* DocService;
94
95
  const doc = yield* svc.get(name);
@@ -162,7 +163,7 @@ const docLock = (pos, flags) => Effect.gen(function* () {
162
163
  const name = pos[0];
163
164
  if (!name) {
164
165
  console.error("Usage: tx doc lock <name>");
165
- process.exit(1);
166
+ throw new CliExitError(1);
166
167
  }
167
168
  const svc = yield* DocService;
168
169
  const doc = yield* svc.lock(name);
@@ -182,7 +183,7 @@ const docVersion = (pos, flags) => Effect.gen(function* () {
182
183
  const name = pos[0];
183
184
  if (!name) {
184
185
  console.error("Usage: tx doc version <name>");
185
- process.exit(1);
186
+ throw new CliExitError(1);
186
187
  }
187
188
  const svc = yield* DocService;
188
189
  const doc = yield* svc.createVersion(name);
@@ -200,7 +201,7 @@ const docLink = (pos, flags) => Effect.gen(function* () {
200
201
  const to = pos[1];
201
202
  if (!from || !to) {
202
203
  console.error("Usage: tx doc link <from-name> <to-name> [--type <link-type>]");
203
- process.exit(1);
204
+ throw new CliExitError(1);
204
205
  }
205
206
  const linkType = opt(flags, "type");
206
207
  const svc = yield* DocService;
@@ -217,7 +218,7 @@ const docAttach = (pos, flags) => Effect.gen(function* () {
217
218
  const docName = pos[1];
218
219
  if (!taskId || !docName) {
219
220
  console.error("Usage: tx doc attach <task-id> <doc-name> [--type implements|references]");
220
- process.exit(1);
221
+ throw new CliExitError(1);
221
222
  }
222
223
  const linkType = (opt(flags, "type") ?? "implements");
223
224
  const svc = yield* DocService;
@@ -234,7 +235,7 @@ const docPatch = (pos, flags) => Effect.gen(function* () {
234
235
  const patchName = pos[1];
235
236
  if (!designName || !patchName) {
236
237
  console.error("Usage: tx doc patch <design-name> <patch-name> [--title <title>]");
237
- process.exit(1);
238
+ throw new CliExitError(1);
238
239
  }
239
240
  const title = opt(flags, "title", "t") ?? patchName;
240
241
  const svc = yield* DocService;
@@ -270,7 +271,7 @@ const docDrift = (pos, flags) => Effect.gen(function* () {
270
271
  const name = pos[0];
271
272
  if (!name) {
272
273
  console.error("Usage: tx doc drift <name>");
273
- process.exit(1);
274
+ throw new CliExitError(1);
274
275
  }
275
276
  const svc = yield* DocService;
276
277
  const warnings = yield* svc.detectDrift(name);
@@ -5,11 +5,12 @@ import { Effect } from "effect";
5
5
  import { TaskService } from "@jamesaphoenix/tx-core";
6
6
  import { toJson, formatTaskLine } from "../output.js";
7
7
  import { flag, parseTaskId } from "../utils/parse.js";
8
+ import { CliExitError } from "../cli-exit.js";
8
9
  export const children = (pos, flags) => Effect.gen(function* () {
9
10
  const raw = pos[0];
10
11
  if (!raw) {
11
12
  console.error("Usage: tx children <id> [--json]");
12
- process.exit(1);
13
+ throw new CliExitError(1);
13
14
  }
14
15
  const id = parseTaskId(raw);
15
16
  const svc = yield* TaskService;
@@ -34,7 +35,7 @@ export const tree = (pos, flags) => Effect.gen(function* () {
34
35
  const raw = pos[0];
35
36
  if (!raw) {
36
37
  console.error("Usage: tx tree <id> [--json]");
37
- process.exit(1);
38
+ throw new CliExitError(1);
38
39
  }
39
40
  const id = parseTaskId(raw);
40
41
  const svc = yield* TaskService;
@@ -5,6 +5,7 @@ import { Effect } from "effect";
5
5
  import { DocService } from "@jamesaphoenix/tx-core";
6
6
  import { toJson } from "../output.js";
7
7
  import { flag, opt } from "../utils/parse.js";
8
+ import { CliExitError } from "../cli-exit.js";
8
9
  /** Dispatch invariant subcommands. */
9
10
  export const invariant = (pos, flags) => {
10
11
  const sub = pos[0];
@@ -18,7 +19,7 @@ export const invariant = (pos, flags) => {
18
19
  return Effect.sync(() => {
19
20
  console.error(`Unknown invariant subcommand: ${sub ?? "(none)"}`);
20
21
  console.error("Run 'tx invariant --help' for usage information");
21
- process.exit(1);
22
+ throw new CliExitError(1);
22
23
  });
23
24
  }
24
25
  };
@@ -11,6 +11,7 @@ import { homedir } from "node:os";
11
11
  import { TaskService, buildClaudeTaskFiles } from "@jamesaphoenix/tx-core";
12
12
  import { toJson } from "../output.js";
13
13
  import { flag, opt } from "../utils/parse.js";
14
+ import { CliExitError } from "../cli-exit.js";
14
15
  export const syncClaude = (_pos, flags) => Effect.gen(function* () {
15
16
  const taskSvc = yield* TaskService;
16
17
  // Resolve target directory
@@ -21,7 +22,7 @@ export const syncClaude = (_pos, flags) => Effect.gen(function* () {
21
22
  // Validate team name to prevent path traversal (e.g. --team ../../.ssh)
22
23
  if (!/^[a-zA-Z0-9_-]+$/.test(teamName)) {
23
24
  console.error("Invalid team name: must contain only alphanumeric characters, hyphens, and underscores");
24
- process.exit(1);
25
+ throw new CliExitError(1);
25
26
  }
26
27
  targetDir = join(homedir(), ".claude", "tasks", teamName);
27
28
  }
@@ -30,7 +31,7 @@ export const syncClaude = (_pos, flags) => Effect.gen(function* () {
30
31
  }
31
32
  else {
32
33
  console.error("Either --team <name> or --dir <path> is required");
33
- process.exit(1);
34
+ throw new CliExitError(1);
34
35
  }
35
36
  // Create directory if it doesn't exist
36
37
  if (!existsSync(targetDir)) {
@@ -69,6 +70,6 @@ export const syncClaude = (_pos, flags) => Effect.gen(function* () {
69
70
  });
70
71
  export const syncCodex = (_pos, _flags) => Effect.sync(() => {
71
72
  console.error("Codex sync is not yet implemented. Use 'tx sync claude' for Claude Code.");
72
- process.exit(1);
73
+ throw new CliExitError(1);
73
74
  });
74
75
  //# sourceMappingURL=sync-platform.js.map
@@ -6,6 +6,7 @@ import { SyncService } from "@jamesaphoenix/tx-core";
6
6
  import { toJson } from "../output.js";
7
7
  import { commandHelp } from "../help.js";
8
8
  import { flag, opt } from "../utils/parse.js";
9
+ import { CliExitError } from "../cli-exit.js";
9
10
  import { syncClaude, syncCodex } from "./sync-platform.js";
10
11
  export const sync = (pos, flags) => Effect.gen(function* () {
11
12
  const subcommand = pos[0];
@@ -79,7 +80,7 @@ export const sync = (pos, flags) => Effect.gen(function* () {
79
80
  const disableFlag = flag(flags, "disable");
80
81
  if (enableFlag && disableFlag) {
81
82
  console.error("Cannot specify both --enable and --disable");
82
- process.exit(1);
83
+ throw new CliExitError(1);
83
84
  }
84
85
  if (enableFlag) {
85
86
  yield* syncSvc.enableAutoSync();
@@ -122,7 +123,7 @@ export const sync = (pos, flags) => Effect.gen(function* () {
122
123
  else {
123
124
  console.error(`Unknown sync subcommand: ${subcommand}`);
124
125
  console.error(`Run 'tx sync --help' for usage information`);
125
- process.exit(1);
126
+ throw new CliExitError(1);
126
127
  }
127
128
  });
128
129
  //# sourceMappingURL=sync.js.map
@@ -6,11 +6,12 @@ import { TaskService, ReadyService, AttemptService } from "@jamesaphoenix/tx-cor
6
6
  import { assertTaskStatus, TASK_STATUSES } from "@jamesaphoenix/tx-types";
7
7
  import { toJson, formatTaskWithDeps, formatTaskLine, formatReadyTaskLine } from "../output.js";
8
8
  import { flag, opt, parseIntOpt, parseTaskId } from "../utils/parse.js";
9
+ import { CliExitError } from "../cli-exit.js";
9
10
  export const add = (pos, flags) => Effect.gen(function* () {
10
11
  const title = pos[0];
11
12
  if (!title) {
12
13
  console.error("Usage: tx add <title> [--parent/-p <id>] [--score/-s <n>] [--description/-d <text>] [--json]");
13
- process.exit(1);
14
+ throw new CliExitError(1);
14
15
  }
15
16
  const svc = yield* TaskService;
16
17
  const task = yield* svc.create({
@@ -44,7 +45,7 @@ export const list = (_pos, flags) => Effect.gen(function* () {
44
45
  }
45
46
  catch {
46
47
  console.error(`Invalid status filter. Valid statuses: ${TASK_STATUSES.join(", ")}`);
47
- process.exit(1);
48
+ throw new CliExitError(1);
48
49
  }
49
50
  }
50
51
  const tasks = yield* svc.listWithDeps({
@@ -100,7 +101,7 @@ export const show = (pos, flags) => Effect.gen(function* () {
100
101
  const raw = pos[0];
101
102
  if (!raw) {
102
103
  console.error("Usage: tx show <id> [--json]");
103
- process.exit(1);
104
+ throw new CliExitError(1);
104
105
  }
105
106
  const id = parseTaskId(raw);
106
107
  const svc = yield* TaskService;
@@ -133,7 +134,7 @@ export const update = (pos, flags) => Effect.gen(function* () {
133
134
  const raw = pos[0];
134
135
  if (!raw) {
135
136
  console.error("Usage: tx update <id> [--status <s>] [--title <t>] [--score <n>] [--description <d>] [--parent <p>] [--json]");
136
- process.exit(1);
137
+ throw new CliExitError(1);
137
138
  }
138
139
  const id = parseTaskId(raw);
139
140
  const svc = yield* TaskService;
@@ -164,7 +165,7 @@ export const done = (pos, flags) => Effect.gen(function* () {
164
165
  const raw = pos[0];
165
166
  if (!raw) {
166
167
  console.error("Usage: tx done <id> [--json]");
167
- process.exit(1);
168
+ throw new CliExitError(1);
168
169
  }
169
170
  const id = parseTaskId(raw);
170
171
  const taskSvc = yield* TaskService;
@@ -194,7 +195,7 @@ export const deleteTask = (pos, flags) => Effect.gen(function* () {
194
195
  const raw = pos[0];
195
196
  if (!raw) {
196
197
  console.error("Usage: tx delete <id> [--cascade] [--json]");
197
- process.exit(1);
198
+ throw new CliExitError(1);
198
199
  }
199
200
  const id = parseTaskId(raw);
200
201
  const cascade = flag(flags, "cascade");
@@ -212,7 +213,7 @@ export const reset = (pos, flags) => Effect.gen(function* () {
212
213
  const raw = pos[0];
213
214
  if (!raw) {
214
215
  console.error("Usage: tx reset <id> [--json]");
215
- process.exit(1);
216
+ throw new CliExitError(1);
216
217
  }
217
218
  const id = parseTaskId(raw);
218
219
  const taskSvc = yield* TaskService;
@@ -10,6 +10,7 @@ import { RunRepository, SqliteClient, getAdapter } from "@jamesaphoenix/tx-core"
10
10
  import { toJson, truncate } from "../output.js";
11
11
  import { commandHelp } from "../help.js";
12
12
  import { flag, parseIntOpt } from "../utils/parse.js";
13
+ import { CliExitError } from "../cli-exit.js";
13
14
  /**
14
15
  * Calculate relative time string (e.g., "2h ago", "3d ago").
15
16
  */
@@ -191,19 +192,19 @@ export const traceTranscript = (pos, _flags) => Effect.gen(function* () {
191
192
  if (!runId) {
192
193
  console.error("Error: run-id is required");
193
194
  console.error("Usage: tx trace transcript <run-id>");
194
- process.exit(1);
195
+ throw new CliExitError(1);
195
196
  }
196
197
  const runRepo = yield* RunRepository;
197
198
  // Get run details
198
199
  const run = yield* runRepo.findById(runId);
199
200
  if (!run) {
200
201
  console.error(`Error: Run not found: ${runId}`);
201
- process.exit(1);
202
+ throw new CliExitError(1);
202
203
  }
203
204
  // Check if transcript path exists
204
205
  if (!run.transcriptPath) {
205
206
  console.error(`Error: No transcript recorded for run: ${runId}`);
206
- process.exit(1);
207
+ throw new CliExitError(1);
207
208
  }
208
209
  // Resolve transcript path relative to .tx directory
209
210
  const txDir = join(process.cwd(), ".tx");
@@ -212,7 +213,7 @@ export const traceTranscript = (pos, _flags) => Effect.gen(function* () {
212
213
  : resolve(txDir, run.transcriptPath);
213
214
  if (!existsSync(fullPath)) {
214
215
  console.error(`Error: Transcript file not found: ${fullPath}`);
215
- process.exit(1);
216
+ throw new CliExitError(1);
216
217
  }
217
218
  // Read and output raw content
218
219
  try {
@@ -222,7 +223,7 @@ export const traceTranscript = (pos, _flags) => Effect.gen(function* () {
222
223
  catch (err) {
223
224
  const message = err instanceof Error ? err.message : String(err);
224
225
  console.error(`Error: Failed to read transcript file: ${message}`);
225
- process.exit(1);
226
+ throw new CliExitError(1);
226
227
  }
227
228
  });
228
229
  /**
@@ -236,19 +237,19 @@ export const traceStderr = (pos, _flags) => Effect.gen(function* () {
236
237
  if (!runId) {
237
238
  console.error("Error: run-id is required");
238
239
  console.error("Usage: tx trace stderr <run-id>");
239
- process.exit(1);
240
+ throw new CliExitError(1);
240
241
  }
241
242
  const runRepo = yield* RunRepository;
242
243
  // Get run details
243
244
  const run = yield* runRepo.findById(runId);
244
245
  if (!run) {
245
246
  console.error(`Error: Run not found: ${runId}`);
246
- process.exit(1);
247
+ throw new CliExitError(1);
247
248
  }
248
249
  // Check if stderr path exists
249
250
  if (!run.stderrPath) {
250
251
  console.error(`Error: No stderr recorded for run: ${runId}`);
251
- process.exit(1);
252
+ throw new CliExitError(1);
252
253
  }
253
254
  // Resolve stderr path relative to .tx directory
254
255
  const txDir = join(process.cwd(), ".tx");
@@ -257,7 +258,7 @@ export const traceStderr = (pos, _flags) => Effect.gen(function* () {
257
258
  : resolve(txDir, run.stderrPath);
258
259
  if (!existsSync(fullPath)) {
259
260
  console.error(`Error: Stderr file not found: ${fullPath}`);
260
- process.exit(1);
261
+ throw new CliExitError(1);
261
262
  }
262
263
  // Read and output raw content
263
264
  try {
@@ -267,7 +268,7 @@ export const traceStderr = (pos, _flags) => Effect.gen(function* () {
267
268
  catch (err) {
268
269
  const message = err instanceof Error ? err.message : String(err);
269
270
  console.error(`Error: Failed to read stderr file: ${message}`);
270
- process.exit(1);
271
+ throw new CliExitError(1);
271
272
  }
272
273
  });
273
274
  /**
@@ -278,7 +279,7 @@ export const traceShow = (pos, flags) => Effect.gen(function* () {
278
279
  if (!runId) {
279
280
  console.error("Error: run-id is required");
280
281
  console.error("Usage: tx trace show <run-id> [--full] [--json]");
281
- process.exit(1);
282
+ throw new CliExitError(1);
282
283
  }
283
284
  const runRepo = yield* RunRepository;
284
285
  const db = yield* SqliteClient;
@@ -286,7 +287,7 @@ export const traceShow = (pos, flags) => Effect.gen(function* () {
286
287
  const run = yield* runRepo.findById(runId);
287
288
  if (!run) {
288
289
  console.error(`Error: Run not found: ${runId}`);
289
- process.exit(1);
290
+ throw new CliExitError(1);
290
291
  }
291
292
  // Get events for this run
292
293
  const eventRows = getEventsForRun(db, runId);
@@ -614,7 +615,7 @@ Options:
614
615
  else {
615
616
  console.error(`Unknown trace subcommand: ${subcommand}`);
616
617
  console.error(`Run 'tx trace --help' for usage information`);
617
- process.exit(1);
618
+ throw new CliExitError(1);
618
619
  }
619
620
  });
620
621
  //# sourceMappingURL=trace.js.map
@@ -4,6 +4,7 @@
4
4
  import { Effect } from "effect";
5
5
  import { ValidationService } from "@jamesaphoenix/tx-core";
6
6
  import { toJson } from "../output.js";
7
+ import { CliExitError } from "../cli-exit.js";
7
8
  function flag(flags, ...names) {
8
9
  return names.some(n => flags[n] === true);
9
10
  }
@@ -88,7 +89,7 @@ export const validate = (_pos, flags) => Effect.gen(function* () {
88
89
  }
89
90
  // Exit with code 1 if validation failed (errors found)
90
91
  if (!result.valid) {
91
- process.exit(1);
92
+ throw new CliExitError(1);
92
93
  }
93
94
  });
94
95
  //# sourceMappingURL=validate.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jamesaphoenix/tx-cli",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "TX command line tool - task management for AI agents and humans",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",
@@ -15,7 +15,15 @@
15
15
  }
16
16
  },
17
17
  "files": [
18
- "dist"
18
+ "dist",
19
+ "!dist/tx",
20
+ "!dist/tx-*",
21
+ "!dist/**/*.test.*",
22
+ "!dist/**/*.test.d.ts",
23
+ "!dist/**/*.test.d.ts.map",
24
+ "!dist/**/*.js.map",
25
+ "!dist/**/*.d.ts.map",
26
+ "README.md"
19
27
  ],
20
28
  "scripts": {
21
29
  "build": "tsc -b",
@@ -31,7 +39,6 @@
31
39
  },
32
40
  "dependencies": {
33
41
  "@jamesaphoenix/tx-core": "*",
34
- "@jamesaphoenix/tx-test-utils": "*",
35
42
  "@jamesaphoenix/tx-types": "*",
36
43
  "effect": "^3.19.15"
37
44
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli-exit.d.ts","sourceRoot":"","sources":["../src/cli-exit.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAG,cAAc,CAAS;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;gBAET,IAAI,EAAE,MAAM;CAIzB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli-exit.js","sourceRoot":"","sources":["../src/cli-exit.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IAC5B,IAAI,GAAG,cAAuB,CAAA;IAC9B,IAAI,CAAQ;IAErB,YAAY,IAAY;QACtB,KAAK,EAAE,CAAA;QACP,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;CACF"}
package/dist/cli.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;GAIG"}