@jamesaphoenix/tx-cli 0.4.3

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 (138) hide show
  1. package/dist/cli-exit.d.ts +15 -0
  2. package/dist/cli-exit.d.ts.map +1 -0
  3. package/dist/cli-exit.js +18 -0
  4. package/dist/cli-exit.js.map +1 -0
  5. package/dist/cli.d.ts +8 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +349 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/commands/attempt.d.ts +9 -0
  10. package/dist/commands/attempt.d.ts.map +1 -0
  11. package/dist/commands/attempt.js +93 -0
  12. package/dist/commands/attempt.js.map +1 -0
  13. package/dist/commands/bulk.d.ts +8 -0
  14. package/dist/commands/bulk.d.ts.map +1 -0
  15. package/dist/commands/bulk.js +168 -0
  16. package/dist/commands/bulk.js.map +1 -0
  17. package/dist/commands/claim.d.ts +39 -0
  18. package/dist/commands/claim.d.ts.map +1 -0
  19. package/dist/commands/claim.js +96 -0
  20. package/dist/commands/claim.js.map +1 -0
  21. package/dist/commands/compact.d.ts +17 -0
  22. package/dist/commands/compact.d.ts.map +1 -0
  23. package/dist/commands/compact.js +167 -0
  24. package/dist/commands/compact.js.map +1 -0
  25. package/dist/commands/coordinator.d.ts +12 -0
  26. package/dist/commands/coordinator.d.ts.map +1 -0
  27. package/dist/commands/coordinator.js +124 -0
  28. package/dist/commands/coordinator.js.map +1 -0
  29. package/dist/commands/cycle.d.ts +12 -0
  30. package/dist/commands/cycle.d.ts.map +1 -0
  31. package/dist/commands/cycle.js +109 -0
  32. package/dist/commands/cycle.js.map +1 -0
  33. package/dist/commands/daemon.d.ts +16 -0
  34. package/dist/commands/daemon.d.ts.map +1 -0
  35. package/dist/commands/daemon.js +635 -0
  36. package/dist/commands/daemon.js.map +1 -0
  37. package/dist/commands/dashboard.d.ts +12 -0
  38. package/dist/commands/dashboard.d.ts.map +1 -0
  39. package/dist/commands/dashboard.js +105 -0
  40. package/dist/commands/dashboard.js.map +1 -0
  41. package/dist/commands/dashboard.test.d.ts +2 -0
  42. package/dist/commands/dashboard.test.d.ts.map +1 -0
  43. package/dist/commands/dashboard.test.js +99 -0
  44. package/dist/commands/dashboard.test.js.map +1 -0
  45. package/dist/commands/dep.d.ts +9 -0
  46. package/dist/commands/dep.d.ts.map +1 -0
  47. package/dist/commands/dep.js +50 -0
  48. package/dist/commands/dep.js.map +1 -0
  49. package/dist/commands/doc.d.ts +10 -0
  50. package/dist/commands/doc.d.ts.map +1 -0
  51. package/dist/commands/doc.js +397 -0
  52. package/dist/commands/doc.js.map +1 -0
  53. package/dist/commands/doctor.d.ts +9 -0
  54. package/dist/commands/doctor.d.ts.map +1 -0
  55. package/dist/commands/doctor.js +168 -0
  56. package/dist/commands/doctor.js.map +1 -0
  57. package/dist/commands/graph.d.ts +58 -0
  58. package/dist/commands/graph.d.ts.map +1 -0
  59. package/dist/commands/graph.js +441 -0
  60. package/dist/commands/graph.js.map +1 -0
  61. package/dist/commands/hierarchy.d.ts +9 -0
  62. package/dist/commands/hierarchy.d.ts.map +1 -0
  63. package/dist/commands/hierarchy.js +68 -0
  64. package/dist/commands/hierarchy.js.map +1 -0
  65. package/dist/commands/hooks.d.ts +56 -0
  66. package/dist/commands/hooks.d.ts.map +1 -0
  67. package/dist/commands/hooks.js +365 -0
  68. package/dist/commands/hooks.js.map +1 -0
  69. package/dist/commands/invariant.d.ts +9 -0
  70. package/dist/commands/invariant.d.ts.map +1 -0
  71. package/dist/commands/invariant.js +126 -0
  72. package/dist/commands/invariant.js.map +1 -0
  73. package/dist/commands/learning.d.ts +16 -0
  74. package/dist/commands/learning.d.ts.map +1 -0
  75. package/dist/commands/learning.js +362 -0
  76. package/dist/commands/learning.js.map +1 -0
  77. package/dist/commands/migrate.d.ts +9 -0
  78. package/dist/commands/migrate.d.ts.map +1 -0
  79. package/dist/commands/migrate.js +56 -0
  80. package/dist/commands/migrate.js.map +1 -0
  81. package/dist/commands/orchestrator.d.ts +11 -0
  82. package/dist/commands/orchestrator.d.ts.map +1 -0
  83. package/dist/commands/orchestrator.js +129 -0
  84. package/dist/commands/orchestrator.js.map +1 -0
  85. package/dist/commands/stats.d.ts +8 -0
  86. package/dist/commands/stats.d.ts.map +1 -0
  87. package/dist/commands/stats.js +128 -0
  88. package/dist/commands/stats.js.map +1 -0
  89. package/dist/commands/sync-platform.d.ts +12 -0
  90. package/dist/commands/sync-platform.d.ts.map +1 -0
  91. package/dist/commands/sync-platform.js +74 -0
  92. package/dist/commands/sync-platform.js.map +1 -0
  93. package/dist/commands/sync.d.ts +8 -0
  94. package/dist/commands/sync.d.ts.map +1 -0
  95. package/dist/commands/sync.js +128 -0
  96. package/dist/commands/sync.js.map +1 -0
  97. package/dist/commands/task.d.ts +15 -0
  98. package/dist/commands/task.d.ts.map +1 -0
  99. package/dist/commands/task.js +233 -0
  100. package/dist/commands/task.js.map +1 -0
  101. package/dist/commands/test.d.ts +16 -0
  102. package/dist/commands/test.d.ts.map +1 -0
  103. package/dist/commands/test.js +112 -0
  104. package/dist/commands/test.js.map +1 -0
  105. package/dist/commands/trace.d.ts +39 -0
  106. package/dist/commands/trace.d.ts.map +1 -0
  107. package/dist/commands/trace.js +620 -0
  108. package/dist/commands/trace.js.map +1 -0
  109. package/dist/commands/validate.d.ts +9 -0
  110. package/dist/commands/validate.d.ts.map +1 -0
  111. package/dist/commands/validate.js +94 -0
  112. package/dist/commands/validate.js.map +1 -0
  113. package/dist/commands/worker.d.ts +10 -0
  114. package/dist/commands/worker.d.ts.map +1 -0
  115. package/dist/commands/worker.js +204 -0
  116. package/dist/commands/worker.js.map +1 -0
  117. package/dist/help.d.ts +3 -0
  118. package/dist/help.d.ts.map +1 -0
  119. package/dist/help.js +1366 -0
  120. package/dist/help.js.map +1 -0
  121. package/dist/output.d.ts +12 -0
  122. package/dist/output.d.ts.map +1 -0
  123. package/dist/output.js +76 -0
  124. package/dist/output.js.map +1 -0
  125. package/dist/tx +0 -0
  126. package/dist/utils/parse.d.ts +45 -0
  127. package/dist/utils/parse.d.ts.map +1 -0
  128. package/dist/utils/parse.js +79 -0
  129. package/dist/utils/parse.js.map +1 -0
  130. package/dist/utils/parse.test.d.ts +2 -0
  131. package/dist/utils/parse.test.d.ts.map +1 -0
  132. package/dist/utils/parse.test.js +140 -0
  133. package/dist/utils/parse.test.js.map +1 -0
  134. package/dist/version.d.ts +2 -0
  135. package/dist/version.d.ts.map +1 -0
  136. package/dist/version.js +10 -0
  137. package/dist/version.js.map +1 -0
  138. package/package.json +58 -0
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Stats command: Queue metrics and health overview
3
+ */
4
+ import { Effect } from "effect";
5
+ import { SqliteClient } from "@jamesaphoenix/tx-core";
6
+ import { toJson } from "../output.js";
7
+ import { flag } from "../utils/parse.js";
8
+ export const stats = (_pos, flags) => Effect.gen(function* () {
9
+ const db = yield* SqliteClient;
10
+ // 1. Status counts via GROUP BY (single query)
11
+ const statusRows = db.prepare("SELECT status, COUNT(*) as count FROM tasks GROUP BY status").all();
12
+ const byStatus = {};
13
+ let total = 0;
14
+ for (const row of statusRows) {
15
+ byStatus[row.status] = row.count;
16
+ total += row.count;
17
+ }
18
+ // 2+3. Ready count + priority breakdown via SQL aggregation (single query)
19
+ // A task is "ready" when status is workable AND all blockers are done
20
+ const priorityRows = db.prepare(`
21
+ SELECT
22
+ CASE
23
+ WHEN t.score >= 900 THEN 'critical'
24
+ WHEN t.score >= 700 THEN 'high'
25
+ WHEN t.score >= 500 THEN 'medium'
26
+ ELSE 'low'
27
+ END as bucket,
28
+ COUNT(*) as count
29
+ FROM tasks t
30
+ WHERE t.status IN ('backlog', 'ready', 'planning')
31
+ AND NOT EXISTS (
32
+ SELECT 1 FROM task_dependencies d
33
+ JOIN tasks blocker ON blocker.id = d.blocker_id
34
+ WHERE d.blocked_id = t.id
35
+ AND blocker.status != 'done'
36
+ )
37
+ GROUP BY bucket
38
+ `).all();
39
+ let readyCount = 0;
40
+ let critical = 0;
41
+ let high = 0;
42
+ let medium = 0;
43
+ let low = 0;
44
+ for (const row of priorityRows) {
45
+ readyCount += row.count;
46
+ if (row.bucket === "critical")
47
+ critical = row.count;
48
+ else if (row.bucket === "high")
49
+ high = row.count;
50
+ else if (row.bucket === "medium")
51
+ medium = row.count;
52
+ else
53
+ low = row.count;
54
+ }
55
+ // 4. Activity: completed tasks in time windows
56
+ const last24hRow = db.prepare("SELECT COUNT(*) as count FROM tasks WHERE status = 'done' AND completed_at > datetime('now', '-1 day')").get();
57
+ const last7dRow = db.prepare("SELECT COUNT(*) as count FROM tasks WHERE status = 'done' AND completed_at > datetime('now', '-7 days')").get();
58
+ const last24h = last24hRow?.count ?? 0;
59
+ const last7d = last7dRow?.count ?? 0;
60
+ // Avg completion rate: tasks per day over the full history
61
+ const dateRangeRow = db.prepare("SELECT MIN(completed_at) as earliest, COUNT(*) as total_done FROM tasks WHERE status = 'done' AND completed_at IS NOT NULL").get();
62
+ let avgPerDay = null;
63
+ if (dateRangeRow?.earliest && dateRangeRow.total_done > 0) {
64
+ const earliest = new Date(dateRangeRow.earliest);
65
+ const now = new Date();
66
+ const daysDiff = (now.getTime() - earliest.getTime()) / (1000 * 60 * 60 * 24);
67
+ if (daysDiff >= 1) {
68
+ avgPerDay = Math.round((dateRangeRow.total_done / daysDiff) * 10) / 10;
69
+ }
70
+ }
71
+ // 5. Claim stats
72
+ const activeClaimsRow = db.prepare("SELECT COUNT(*) as count FROM task_claims WHERE status = 'active' AND datetime(lease_expires_at) >= datetime('now')").get();
73
+ const expiredClaimsRow = db.prepare("SELECT COUNT(*) as count FROM task_claims WHERE status = 'active' AND datetime(lease_expires_at) < datetime('now')").get();
74
+ const result = {
75
+ total,
76
+ byStatus,
77
+ readyCount,
78
+ byPriority: { critical, high, medium, low },
79
+ activity: { last24h, last7d, avgPerDay },
80
+ claims: {
81
+ active: activeClaimsRow?.count ?? 0,
82
+ expired: expiredClaimsRow?.count ?? 0,
83
+ },
84
+ };
85
+ if (flag(flags, "json")) {
86
+ console.log(toJson(result));
87
+ }
88
+ else {
89
+ console.log(formatStats(result));
90
+ }
91
+ });
92
+ function pct(n, total) {
93
+ if (total === 0)
94
+ return "0%";
95
+ return `${Math.round((n / total) * 100)}%`;
96
+ }
97
+ function formatStats(s) {
98
+ const lines = [];
99
+ // Queue Status
100
+ lines.push("Queue Status:");
101
+ lines.push(` Total: ${s.total} tasks`);
102
+ lines.push(` Ready: ${s.readyCount} (${pct(s.readyCount, s.total)})`);
103
+ lines.push(` Active: ${s.byStatus.active ?? 0} (${pct(s.byStatus.active ?? 0, s.total)})`);
104
+ lines.push(` Blocked: ${s.byStatus.blocked ?? 0} (${pct(s.byStatus.blocked ?? 0, s.total)})`);
105
+ lines.push(` Done: ${s.byStatus.done ?? 0} (${pct(s.byStatus.done ?? 0, s.total)})`);
106
+ // By Priority (ready tasks only)
107
+ lines.push("");
108
+ lines.push("By Priority:");
109
+ lines.push(` Critical (900+): ${s.byPriority.critical} ready`);
110
+ lines.push(` High (700-899): ${s.byPriority.high} ready`);
111
+ lines.push(` Medium (500-699): ${s.byPriority.medium} ready`);
112
+ lines.push(` Low (<500): ${s.byPriority.low} ready`);
113
+ // Activity
114
+ lines.push("");
115
+ lines.push("Activity:");
116
+ lines.push(` Last 24h: ${s.activity.last24h} completed`);
117
+ lines.push(` Last 7d: ${s.activity.last7d} completed`);
118
+ if (s.activity.avgPerDay !== null) {
119
+ lines.push(` Avg completion: ${s.activity.avgPerDay} tasks/day`);
120
+ }
121
+ // Claims
122
+ lines.push("");
123
+ lines.push("Claims:");
124
+ lines.push(` Active: ${s.claims.active}`);
125
+ lines.push(` Expired: ${s.claims.expired}`);
126
+ return lines.join("\n");
127
+ }
128
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/commands/stats.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Platform sync commands: claude, codex
3
+ *
4
+ * One-way push of tx tasks to external agent task systems.
5
+ * Writes directly to the target platform's on-disk task format.
6
+ */
7
+ import { Effect } from "effect";
8
+ import { TaskService } from "@jamesaphoenix/tx-core";
9
+ import { type Flags } from "../utils/parse.js";
10
+ export declare const syncClaude: (_pos: string[], flags: Flags) => Effect.Effect<void, import("@jamesaphoenix/tx-core").DatabaseError, TaskService>;
11
+ export declare const syncCodex: (_pos: string[], _flags: Flags) => Effect.Effect<never, never, never>;
12
+ //# sourceMappingURL=sync-platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-platform.d.ts","sourceRoot":"","sources":["../../src/commands/sync-platform.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAI/B,OAAO,EAAE,WAAW,EAAwB,MAAM,wBAAwB,CAAA;AAE1E,OAAO,EAAE,KAAK,KAAK,EAAa,MAAM,mBAAmB,CAAA;AAEzD,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,EAAE,EAAE,OAAO,KAAK,qFA8DnD,CAAA;AAEJ,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,EAAE,EAAE,QAAQ,KAAK,uCAInD,CAAA"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Platform sync commands: claude, codex
3
+ *
4
+ * One-way push of tx tasks to external agent task systems.
5
+ * Writes directly to the target platform's on-disk task format.
6
+ */
7
+ import { Effect } from "effect";
8
+ import { join, resolve } from "node:path";
9
+ import { existsSync, writeFileSync, mkdirSync, readdirSync, unlinkSync } from "node:fs";
10
+ import { homedir } from "node:os";
11
+ import { TaskService, buildClaudeTaskFiles } from "@jamesaphoenix/tx-core";
12
+ import { toJson } from "../output.js";
13
+ import { flag, opt } from "../utils/parse.js";
14
+ export const syncClaude = (_pos, flags) => Effect.gen(function* () {
15
+ const taskSvc = yield* TaskService;
16
+ // Resolve target directory
17
+ const teamName = opt(flags, "team");
18
+ const dirOverride = opt(flags, "dir");
19
+ let targetDir;
20
+ if (teamName) {
21
+ // Validate team name to prevent path traversal (e.g. --team ../../.ssh)
22
+ if (!/^[a-zA-Z0-9_-]+$/.test(teamName)) {
23
+ console.error("Invalid team name: must contain only alphanumeric characters, hyphens, and underscores");
24
+ process.exit(1);
25
+ }
26
+ targetDir = join(homedir(), ".claude", "tasks", teamName);
27
+ }
28
+ else if (dirOverride) {
29
+ targetDir = resolve(dirOverride);
30
+ }
31
+ else {
32
+ console.error("Either --team <name> or --dir <path> is required");
33
+ process.exit(1);
34
+ }
35
+ // Create directory if it doesn't exist
36
+ if (!existsSync(targetDir)) {
37
+ mkdirSync(targetDir, { recursive: true });
38
+ }
39
+ const allTasks = yield* taskSvc.listWithDeps();
40
+ const { files, highwatermark } = buildClaudeTaskFiles(allTasks);
41
+ // Remove stale task files from previous syncs
42
+ const newIds = new Set(files.map(f => `${f.id}.json`));
43
+ const existing = readdirSync(targetDir).filter(f => /^\d+\.json$/.test(f));
44
+ for (const stale of existing) {
45
+ if (!newIds.has(stale)) {
46
+ unlinkSync(join(targetDir, stale));
47
+ }
48
+ }
49
+ // Write individual task JSON files
50
+ for (const file of files) {
51
+ writeFileSync(join(targetDir, `${file.id}.json`), JSON.stringify(file, null, 2));
52
+ }
53
+ // Write highwatermark
54
+ writeFileSync(join(targetDir, ".highwatermark"), String(highwatermark));
55
+ // Ensure .lock file exists
56
+ const lockPath = join(targetDir, ".lock");
57
+ if (!existsSync(lockPath)) {
58
+ writeFileSync(lockPath, "");
59
+ }
60
+ if (flag(flags, "json")) {
61
+ console.log(toJson({ tasksWritten: files.length, dir: targetDir, highwatermark }));
62
+ }
63
+ else {
64
+ console.log(`Wrote ${files.length} task(s) to ${targetDir}`);
65
+ const readyCount = files.filter(f => f.blockedBy.length === 0 && f.status === "pending").length;
66
+ const inProgressCount = files.filter(f => f.status === "in_progress").length;
67
+ console.log(` Ready: ${readyCount}, In-progress: ${inProgressCount}, Blocked: ${files.length - readyCount - inProgressCount}`);
68
+ }
69
+ });
70
+ export const syncCodex = (_pos, _flags) => Effect.sync(() => {
71
+ console.error("Codex sync is not yet implemented. Use 'tx sync claude' for Claude Code.");
72
+ process.exit(1);
73
+ });
74
+ //# sourceMappingURL=sync-platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-platform.js","sourceRoot":"","sources":["../../src/commands/sync-platform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Sync commands: export, import, status, compact, auto
3
+ */
4
+ import { Effect } from "effect";
5
+ import { SyncService } from "@jamesaphoenix/tx-core";
6
+ import { type Flags } from "../utils/parse.js";
7
+ export declare const sync: (pos: string[], flags: Flags) => Effect.Effect<void, import("@jamesaphoenix/tx-core").ValidationError | import("@jamesaphoenix/tx-core").DatabaseError | import("@jamesaphoenix/tx-core").TaskNotFoundError, import("@jamesaphoenix/tx-core").TaskService | SyncService>;
8
+ //# sourceMappingURL=sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAGpD,OAAO,EAAE,KAAK,KAAK,EAAa,MAAM,mBAAmB,CAAA;AAGzD,eAAO,MAAM,IAAI,GAAI,KAAK,MAAM,EAAE,EAAE,OAAO,KAAK,4OAoH5C,CAAA"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Sync commands: export, import, status, compact, auto
3
+ */
4
+ import { Effect } from "effect";
5
+ import { SyncService } from "@jamesaphoenix/tx-core";
6
+ import { toJson } from "../output.js";
7
+ import { commandHelp } from "../help.js";
8
+ import { flag, opt } from "../utils/parse.js";
9
+ import { syncClaude, syncCodex } from "./sync-platform.js";
10
+ export const sync = (pos, flags) => Effect.gen(function* () {
11
+ const subcommand = pos[0];
12
+ if (!subcommand || subcommand === "help") {
13
+ console.log(commandHelp["sync"]);
14
+ return;
15
+ }
16
+ // Check for --help on subcommand
17
+ if (flag(flags, "help", "h")) {
18
+ const helpKey = `sync ${subcommand}`;
19
+ if (commandHelp[helpKey]) {
20
+ console.log(commandHelp[helpKey]);
21
+ return;
22
+ }
23
+ }
24
+ // Platform sync subcommands (don't need SyncService)
25
+ if (subcommand === "claude") {
26
+ return yield* syncClaude(pos.slice(1), flags);
27
+ }
28
+ else if (subcommand === "codex") {
29
+ return yield* syncCodex(pos.slice(1), flags);
30
+ }
31
+ const syncSvc = yield* SyncService;
32
+ if (subcommand === "export") {
33
+ const path = opt(flags, "path");
34
+ // Export tasks
35
+ const result = yield* syncSvc.export(path);
36
+ if (flag(flags, "json")) {
37
+ console.log(toJson(result));
38
+ }
39
+ else {
40
+ console.log(`Exported ${result.opCount} operation(s) to ${result.path}`);
41
+ }
42
+ }
43
+ else if (subcommand === "import") {
44
+ const path = opt(flags, "path");
45
+ // Import tasks
46
+ const result = yield* syncSvc.import(path);
47
+ if (flag(flags, "json")) {
48
+ console.log(toJson(result));
49
+ }
50
+ else {
51
+ console.log(`Tasks: imported=${result.imported}, skipped=${result.skipped}, conflicts=${result.conflicts}`);
52
+ const deps = result.dependencies;
53
+ console.log(`Dependencies: added=${deps.added}, removed=${deps.removed}, skipped=${deps.skipped}, failures=${deps.failures.length}`);
54
+ if (deps.failures.length > 0) {
55
+ console.log(`\nDependency failures:`);
56
+ for (const f of deps.failures) {
57
+ console.log(` ${f.blockerId} -> ${f.blockedId}: ${f.error}`);
58
+ }
59
+ }
60
+ }
61
+ }
62
+ else if (subcommand === "status") {
63
+ const status = yield* syncSvc.status();
64
+ if (flag(flags, "json")) {
65
+ console.log(toJson(status));
66
+ }
67
+ else {
68
+ console.log(`Sync Status:`);
69
+ console.log(` Tasks in database: ${status.dbTaskCount}`);
70
+ console.log(` Operations in JSONL: ${status.jsonlOpCount}`);
71
+ console.log(` Last export: ${status.lastExport ? status.lastExport.toISOString() : "(never)"}`);
72
+ console.log(` Last import: ${status.lastImport ? status.lastImport.toISOString() : "(never)"}`);
73
+ console.log(` Dirty (unexported changes): ${status.isDirty ? "yes" : "no"}`);
74
+ console.log(` Auto-sync: ${status.autoSyncEnabled ? "enabled" : "disabled"}`);
75
+ }
76
+ }
77
+ else if (subcommand === "auto") {
78
+ const enableFlag = flag(flags, "enable");
79
+ const disableFlag = flag(flags, "disable");
80
+ if (enableFlag && disableFlag) {
81
+ console.error("Cannot specify both --enable and --disable");
82
+ process.exit(1);
83
+ }
84
+ if (enableFlag) {
85
+ yield* syncSvc.enableAutoSync();
86
+ if (flag(flags, "json")) {
87
+ console.log(toJson({ autoSync: true }));
88
+ }
89
+ else {
90
+ console.log("Auto-sync enabled");
91
+ }
92
+ }
93
+ else if (disableFlag) {
94
+ yield* syncSvc.disableAutoSync();
95
+ if (flag(flags, "json")) {
96
+ console.log(toJson({ autoSync: false }));
97
+ }
98
+ else {
99
+ console.log("Auto-sync disabled");
100
+ }
101
+ }
102
+ else {
103
+ const enabled = yield* syncSvc.isAutoSyncEnabled();
104
+ if (flag(flags, "json")) {
105
+ console.log(toJson({ autoSync: enabled }));
106
+ }
107
+ else {
108
+ console.log(`Auto-sync: ${enabled ? "enabled" : "disabled"}`);
109
+ }
110
+ }
111
+ }
112
+ else if (subcommand === "compact") {
113
+ const path = opt(flags, "path");
114
+ const result = yield* syncSvc.compact(path);
115
+ if (flag(flags, "json")) {
116
+ console.log(toJson(result));
117
+ }
118
+ else {
119
+ console.log(`Compacted: ${result.before} → ${result.after} operations`);
120
+ }
121
+ }
122
+ else {
123
+ console.error(`Unknown sync subcommand: ${subcommand}`);
124
+ console.error(`Run 'tx sync --help' for usage information`);
125
+ process.exit(1);
126
+ }
127
+ });
128
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Task commands: add, list, show, update, done, delete, reset
3
+ */
4
+ import { Effect } from "effect";
5
+ import { TaskService, ReadyService, AttemptService } from "@jamesaphoenix/tx-core";
6
+ import { type Flags } from "../utils/parse.js";
7
+ export declare const add: (pos: string[], flags: Flags) => Effect.Effect<void, import("@jamesaphoenix/tx-core").ValidationError | import("@jamesaphoenix/tx-core").DatabaseError | import("@jamesaphoenix/tx-core").TaskNotFoundError, TaskService>;
8
+ export declare const list: (_pos: string[], flags: Flags) => Effect.Effect<void, import("@jamesaphoenix/tx-core").DatabaseError, TaskService>;
9
+ export declare const ready: (_pos: string[], flags: Flags) => Effect.Effect<void, import("@jamesaphoenix/tx-core").DatabaseError, ReadyService | AttemptService>;
10
+ export declare const show: (pos: string[], flags: Flags) => Effect.Effect<void, import("@jamesaphoenix/tx-core").DatabaseError | import("@jamesaphoenix/tx-core").TaskNotFoundError, TaskService | AttemptService>;
11
+ export declare const update: (pos: string[], flags: Flags) => Effect.Effect<void, import("@jamesaphoenix/tx-core").ValidationError | import("@jamesaphoenix/tx-core").DatabaseError | import("@jamesaphoenix/tx-core").TaskNotFoundError | import("@jamesaphoenix/tx-core").StaleDataError, TaskService>;
12
+ export declare const done: (pos: string[], flags: Flags) => Effect.Effect<void, import("@jamesaphoenix/tx-core").ValidationError | import("@jamesaphoenix/tx-core").DatabaseError | import("@jamesaphoenix/tx-core").TaskNotFoundError | import("@jamesaphoenix/tx-core").StaleDataError, TaskService | ReadyService>;
13
+ export declare const deleteTask: (pos: string[], flags: Flags) => Effect.Effect<void, import("@jamesaphoenix/tx-core").DatabaseError | import("@jamesaphoenix/tx-core").TaskNotFoundError | import("@jamesaphoenix/tx-core").HasChildrenError, TaskService>;
14
+ export declare const reset: (pos: string[], flags: Flags) => Effect.Effect<void, import("@jamesaphoenix/tx-core").ValidationError | import("@jamesaphoenix/tx-core").DatabaseError | import("@jamesaphoenix/tx-core").TaskNotFoundError | import("@jamesaphoenix/tx-core").StaleDataError, TaskService>;
15
+ //# sourceMappingURL=task.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/commands/task.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAGlF,OAAO,EAAE,KAAK,KAAK,EAAuC,MAAM,mBAAmB,CAAA;AAEnF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,EAAE,OAAO,KAAK,6LA0B3C,CAAA;AAEJ,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,EAAE,EAAE,OAAO,KAAK,qFAkC7C,CAAA;AAEJ,eAAO,MAAM,KAAK,GAAI,MAAM,MAAM,EAAE,EAAE,OAAO,KAAK,uGA8B9C,CAAA;AAEJ,eAAO,MAAM,IAAI,GAAI,KAAK,MAAM,EAAE,EAAE,OAAO,KAAK,2JAmC5C,CAAA;AAEJ,eAAO,MAAM,MAAM,GAAI,KAAK,MAAM,EAAE,EAAE,OAAO,KAAK,+OA4B9C,CAAA;AAEJ,eAAO,MAAM,IAAI,GAAI,KAAK,MAAM,EAAE,EAAE,OAAO,KAAK,8PAkC5C,CAAA;AAEJ,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,EAAE,EAAE,OAAO,KAAK,8LAmBlD,CAAA;AAEJ,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,EAAE,EAAE,OAAO,KAAK,+OAyB7C,CAAA"}
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Task commands: add, list, show, update, done, delete, reset
3
+ */
4
+ import { Effect } from "effect";
5
+ import { TaskService, ReadyService, AttemptService } from "@jamesaphoenix/tx-core";
6
+ import { assertTaskStatus, TASK_STATUSES } from "@jamesaphoenix/tx-types";
7
+ import { toJson, formatTaskWithDeps, formatTaskLine, formatReadyTaskLine } from "../output.js";
8
+ import { flag, opt, parseIntOpt, parseTaskId } from "../utils/parse.js";
9
+ export const add = (pos, flags) => Effect.gen(function* () {
10
+ const title = pos[0];
11
+ if (!title) {
12
+ console.error("Usage: tx add <title> [--parent/-p <id>] [--score/-s <n>] [--description/-d <text>] [--json]");
13
+ process.exit(1);
14
+ }
15
+ const svc = yield* TaskService;
16
+ const task = yield* svc.create({
17
+ title,
18
+ description: opt(flags, "description", "d"),
19
+ parentId: opt(flags, "parent", "p"),
20
+ score: parseIntOpt(flags, "score", "score", "s"),
21
+ metadata: {}
22
+ });
23
+ if (flag(flags, "json")) {
24
+ const full = yield* svc.getWithDeps(task.id);
25
+ console.log(toJson(full));
26
+ }
27
+ else {
28
+ console.log(`Created task: ${task.id}`);
29
+ console.log(` Title: ${task.title}`);
30
+ console.log(` Score: ${task.score}`);
31
+ if (task.parentId)
32
+ console.log(` Parent: ${task.parentId}`);
33
+ }
34
+ });
35
+ export const list = (_pos, flags) => Effect.gen(function* () {
36
+ const svc = yield* TaskService;
37
+ const statusFilter = opt(flags, "status");
38
+ const limit = parseIntOpt(flags, "limit", "limit", "n");
39
+ // Validate status values if provided
40
+ let validatedStatuses;
41
+ if (statusFilter) {
42
+ try {
43
+ validatedStatuses = statusFilter.split(",").map(s => assertTaskStatus(s.trim()));
44
+ }
45
+ catch {
46
+ console.error(`Invalid status filter. Valid statuses: ${TASK_STATUSES.join(", ")}`);
47
+ process.exit(1);
48
+ }
49
+ }
50
+ const tasks = yield* svc.listWithDeps({
51
+ status: validatedStatuses,
52
+ limit
53
+ });
54
+ if (flag(flags, "json")) {
55
+ console.log(toJson(tasks));
56
+ }
57
+ else {
58
+ if (tasks.length === 0) {
59
+ console.log("No tasks found");
60
+ }
61
+ else {
62
+ console.log(`${tasks.length} task(s):`);
63
+ for (const t of tasks) {
64
+ console.log(formatTaskLine(t));
65
+ }
66
+ }
67
+ }
68
+ });
69
+ export const ready = (_pos, flags) => Effect.gen(function* () {
70
+ const svc = yield* ReadyService;
71
+ const attemptSvc = yield* AttemptService;
72
+ const limit = parseIntOpt(flags, "limit", "limit", "n") ?? 10;
73
+ const tasks = yield* svc.getReady(limit);
74
+ // Get failed attempt counts for all tasks in a single query
75
+ const taskIds = tasks.map(t => t.id);
76
+ const failedCounts = yield* attemptSvc.getFailedCountsForTasks(taskIds);
77
+ if (flag(flags, "json")) {
78
+ // Add failedAttemptCount to each task in JSON output
79
+ const tasksWithCounts = tasks.map(t => ({
80
+ ...t,
81
+ failedAttemptCount: failedCounts.get(t.id) ?? 0
82
+ }));
83
+ console.log(toJson(tasksWithCounts));
84
+ }
85
+ else {
86
+ if (tasks.length === 0) {
87
+ console.log("No ready tasks");
88
+ }
89
+ else {
90
+ console.log(`${tasks.length} ready task(s):`);
91
+ for (const t of tasks) {
92
+ const failedCount = failedCounts.get(t.id) ?? 0;
93
+ const failedWarning = failedCount >= 2 ? ` \u26A0 ${failedCount} failed attempts` : "";
94
+ console.log(formatReadyTaskLine(t) + failedWarning);
95
+ }
96
+ }
97
+ }
98
+ });
99
+ export const show = (pos, flags) => Effect.gen(function* () {
100
+ const raw = pos[0];
101
+ if (!raw) {
102
+ console.error("Usage: tx show <id> [--json]");
103
+ process.exit(1);
104
+ }
105
+ const id = parseTaskId(raw);
106
+ const svc = yield* TaskService;
107
+ const attemptSvc = yield* AttemptService;
108
+ const task = yield* svc.getWithDeps(id);
109
+ // Get up to 10 most recent attempts
110
+ const allAttempts = yield* attemptSvc.listForTask(id);
111
+ const attempts = allAttempts.slice(0, 10);
112
+ if (flag(flags, "json")) {
113
+ console.log(toJson({ ...task, attempts }));
114
+ }
115
+ else {
116
+ console.log(formatTaskWithDeps(task));
117
+ // Show attempt history if there are any
118
+ if (attempts.length > 0) {
119
+ console.log("");
120
+ console.log("Previous Attempts:");
121
+ for (const a of attempts) {
122
+ const outcomeSymbol = a.outcome === "succeeded" ? "\u2713" : "\u2717";
123
+ console.log(` ${outcomeSymbol} ${a.approach}`);
124
+ if (a.reason) {
125
+ console.log(` Reason: ${a.reason}`);
126
+ }
127
+ console.log(` ${a.createdAt.toISOString()}`);
128
+ }
129
+ }
130
+ }
131
+ });
132
+ export const update = (pos, flags) => Effect.gen(function* () {
133
+ const raw = pos[0];
134
+ if (!raw) {
135
+ console.error("Usage: tx update <id> [--status <s>] [--title <t>] [--score <n>] [--description <d>] [--parent <p>] [--json]");
136
+ process.exit(1);
137
+ }
138
+ const id = parseTaskId(raw);
139
+ const svc = yield* TaskService;
140
+ const input = {};
141
+ if (opt(flags, "status"))
142
+ input.status = opt(flags, "status");
143
+ if (opt(flags, "title"))
144
+ input.title = opt(flags, "title");
145
+ const scoreVal = parseIntOpt(flags, "score", "score");
146
+ if (scoreVal !== undefined)
147
+ input.score = scoreVal;
148
+ if (opt(flags, "description", "d"))
149
+ input.description = opt(flags, "description", "d");
150
+ if (opt(flags, "parent", "p"))
151
+ input.parentId = opt(flags, "parent", "p");
152
+ yield* svc.update(id, input);
153
+ const task = yield* svc.getWithDeps(id);
154
+ if (flag(flags, "json")) {
155
+ console.log(toJson(task));
156
+ }
157
+ else {
158
+ console.log(`Updated: ${task.id}`);
159
+ console.log(` Status: ${task.status}`);
160
+ console.log(` Score: ${task.score}`);
161
+ }
162
+ });
163
+ export const done = (pos, flags) => Effect.gen(function* () {
164
+ const raw = pos[0];
165
+ if (!raw) {
166
+ console.error("Usage: tx done <id> [--json]");
167
+ process.exit(1);
168
+ }
169
+ const id = parseTaskId(raw);
170
+ const taskSvc = yield* TaskService;
171
+ const readySvc = yield* ReadyService;
172
+ // Get tasks blocked by this one BEFORE marking complete
173
+ const blocking = yield* readySvc.getBlocking(id);
174
+ yield* taskSvc.update(id, { status: "done" });
175
+ const task = yield* taskSvc.getWithDeps(id);
176
+ // Find newly unblocked tasks using batch query
177
+ // Filter to workable statuses and get their full deps info in one batch
178
+ const candidateIds = blocking
179
+ .filter(t => ["backlog", "ready", "planning"].includes(t.status))
180
+ .map(t => t.id);
181
+ const candidatesWithDeps = yield* taskSvc.getWithDepsBatch(candidateIds);
182
+ const nowReady = candidatesWithDeps.filter(t => t.isReady).map(t => t.id);
183
+ if (flag(flags, "json")) {
184
+ console.log(toJson({ task, nowReady }));
185
+ }
186
+ else {
187
+ console.log(`Completed: ${task.id} - ${task.title}`);
188
+ if (nowReady.length > 0) {
189
+ console.log(`Now unblocked: ${nowReady.join(", ")}`);
190
+ }
191
+ }
192
+ });
193
+ export const deleteTask = (pos, flags) => Effect.gen(function* () {
194
+ const raw = pos[0];
195
+ if (!raw) {
196
+ console.error("Usage: tx delete <id> [--cascade] [--json]");
197
+ process.exit(1);
198
+ }
199
+ const id = parseTaskId(raw);
200
+ const cascade = flag(flags, "cascade");
201
+ const svc = yield* TaskService;
202
+ const task = yield* svc.getWithDeps(id);
203
+ yield* svc.remove(id, { cascade });
204
+ if (flag(flags, "json")) {
205
+ console.log(toJson({ success: true, message: `Deleted task ${task.id}`, data: { id: task.id, title: task.title, cascade } }));
206
+ }
207
+ else {
208
+ console.log(`Deleted: ${task.id} - ${task.title}${cascade ? " (with children)" : ""}`);
209
+ }
210
+ });
211
+ export const reset = (pos, flags) => Effect.gen(function* () {
212
+ const raw = pos[0];
213
+ if (!raw) {
214
+ console.error("Usage: tx reset <id> [--json]");
215
+ process.exit(1);
216
+ }
217
+ const id = parseTaskId(raw);
218
+ const taskSvc = yield* TaskService;
219
+ // Get current task to show what we're resetting from
220
+ const before = yield* taskSvc.getWithDeps(id);
221
+ const oldStatus = before.status;
222
+ // Force update to ready status (bypass normal validation)
223
+ yield* taskSvc.forceStatus(id, "ready");
224
+ const task = yield* taskSvc.getWithDeps(id);
225
+ if (flag(flags, "json")) {
226
+ console.log(toJson({ task, oldStatus }));
227
+ }
228
+ else {
229
+ console.log(`Reset: ${task.id} - ${task.title}`);
230
+ console.log(` ${oldStatus} -> ready`);
231
+ }
232
+ });
233
+ //# sourceMappingURL=task.js.map