@ulpi/cli 0.1.4 → 0.1.6

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 (112) hide show
  1. package/LICENSE +21 -0
  2. package/dist/{auth-PN7TMQHV-2W4ICG64.js → auth-FWM7MM4Q-VZC3U2XZ.js} +1 -1
  3. package/dist/{auth-ECQ3IB4E.js → auth-HDK7ECJL.js} +2 -1
  4. package/dist/{chunk-3SBPZRB5.js → chunk-3BCW6ABU.js} +402 -142
  5. package/dist/{chunk-JGBXM5NC.js → chunk-3WB5CXH4.js} +180 -5
  6. package/dist/{chunk-2HEE5OKX.js → chunk-4UCJIAOU.js} +2 -2
  7. package/dist/chunk-4XTHZVDS.js +109 -0
  8. package/dist/chunk-4ZPOZULQ.js +6522 -0
  9. package/dist/{chunk-SIAQVRKG.js → chunk-5MI5GIXM.js} +48 -2
  10. package/dist/{chunk-KLEASXUR.js → chunk-6ZL6NXMV.js} +1 -1
  11. package/dist/chunk-76D3BYJD.js +221 -0
  12. package/dist/{chunk-ZLYRPD7I.js → chunk-AWOSRA5F.js} +1 -1
  13. package/dist/{chunk-PDR55ZNW.js → chunk-BFEKZZHM.js} +274 -57
  14. package/dist/chunk-C7CLUQI6.js +1286 -0
  15. package/dist/{chunk-7AL4DOEJ.js → chunk-E3B5NROU.js} +7 -7
  16. package/dist/chunk-EJ7TW77N.js +1418 -0
  17. package/dist/{chunk-5J6NLQUN.js → chunk-IV6MWETF.js} +383 -168
  18. package/dist/chunk-IZPJHSPX.js +1478 -0
  19. package/dist/chunk-JLHNLM3C.js +228 -0
  20. package/dist/{chunk-BZL5H4YQ.js → chunk-KYYI23AQ.js} +2 -2
  21. package/dist/{chunk-2CLNOKPA.js → chunk-RSFJ6QSR.js} +18 -0
  22. package/dist/chunk-S6ANCSYO.js +1271 -0
  23. package/dist/chunk-SEU7WWNQ.js +1251 -0
  24. package/dist/chunk-SNQ7NAIS.js +453 -0
  25. package/dist/{ulpi-RMMCUAGP-JCJ273T6.js → chunk-TSLDGT5O.js} +73 -35
  26. package/dist/{chunk-SPOI23SB.js → chunk-UXHCHOWQ.js} +83 -62
  27. package/dist/chunk-V2H5D6Y3.js +146 -0
  28. package/dist/{chunk-QJ5GSMEC.js → chunk-VVEDXI7E.js} +2 -1
  29. package/dist/chunk-VXH5Y4FO.js +6761 -0
  30. package/dist/chunk-WED4LM5N.js +322 -0
  31. package/dist/{chunk-74WVVWJ4.js → chunk-YOKL7RB5.js} +184 -15
  32. package/dist/chunk-Z53CAR7G.js +298 -0
  33. package/dist/ci-X3U2W4HC.js +854 -0
  34. package/dist/cloud-2F3NLVHN.js +274 -0
  35. package/dist/{codemap-RKSD4MIE.js → codemap-XNGMAF3F.js} +37 -37
  36. package/dist/codex-MB5YTMRT.js +132 -0
  37. package/dist/{config-EGAXXCGL.js → config-OOELBYTH.js} +1 -1
  38. package/dist/dist-2BJYR5EI.js +59 -0
  39. package/dist/dist-2K7IEVTA.js +43 -0
  40. package/dist/dist-3EIQTZHT.js +1380 -0
  41. package/dist/{dist-YA2BWZB2.js → dist-4U5L2X2C.js} +2 -2
  42. package/dist/{dist-UKMCJBB2.js → dist-54KAMNLO.js} +16 -15
  43. package/dist/dist-6M4MZWZW.js +58 -0
  44. package/dist/dist-6X576SU2.js +27 -0
  45. package/dist/dist-7QOEYLFX.js +103 -0
  46. package/dist/dist-AYBGHEDY.js +2541 -0
  47. package/dist/dist-EK45QNEM.js +45 -0
  48. package/dist/{dist-CS2VKNYS.js → dist-FKFEJRPX.js} +16 -15
  49. package/dist/dist-GTEJUBBT.js +66 -0
  50. package/dist/dist-HA74OKJZ.js +40 -0
  51. package/dist/dist-HU5RZAON.js +48 -0
  52. package/dist/dist-IYE3OBRB.js +374 -0
  53. package/dist/{dist-GJYT2OQV.js → dist-JLU26AB6.js} +12 -9
  54. package/dist/{dist-6G7JC2RA.js → dist-KUCI6JFE.js} +49 -9
  55. package/dist/dist-NUEMFZFL.js +33 -0
  56. package/dist/{dist-RKOGLK7R.js → dist-NUXMDXZ3.js} +31 -3
  57. package/dist/{dist-QAU3LGJN.js → dist-YCNWHSLN.js} +15 -5
  58. package/dist/{dist-CB5D5LMO.js → dist-YFFG2ZD6.js} +9 -16
  59. package/dist/dist-ZG4OKCSR.js +15 -0
  60. package/dist/doctor-SI4LLLDZ.js +345 -0
  61. package/dist/{export-import-4A5MWLIA.js → export-import-JFQH4KSJ.js} +1 -1
  62. package/dist/{history-3MOBX4MA.js → history-5NE46ZAH.js} +7 -7
  63. package/dist/hooks-installer-UN5JZLDQ.js +19 -0
  64. package/dist/index.js +395 -619
  65. package/dist/{init-6CH4HV5T.js → init-5FK3VKRT.js} +79 -13
  66. package/dist/job-HIDMAFW2.js +376 -0
  67. package/dist/jobs.memory-PLMMSFHB-VBECCTHN.js +33 -0
  68. package/dist/kiro-VMUHDFGK.js +153 -0
  69. package/dist/{launchd-LF2QMSKZ.js → launchd-6AWT54HR.js} +9 -17
  70. package/dist/mcp-PDUD7SGP.js +249 -0
  71. package/dist/mcp-installer-PQU3XOGO.js +259 -0
  72. package/dist/mcp-setup-OA7IB3H3.js +263 -0
  73. package/dist/{memory-Y6OZTXJ2.js → memory-ZNAEAK3B.js} +17 -17
  74. package/dist/{ollama-3XCUZMZT-FYKHW4TZ.js → ollama-3XCUZMZT-4JMH6B7P.js} +1 -1
  75. package/dist/{openai-E7G2YAHU-UYY4ZWON.js → openai-E7G2YAHU-T3HMBPH7.js} +2 -2
  76. package/dist/portal-JYWVHXDU.js +210 -0
  77. package/dist/prd-Q4J5NVAR.js +408 -0
  78. package/dist/repos-WWZXNN3P.js +271 -0
  79. package/dist/review-integration-5WHEJU2A.js +14 -0
  80. package/dist/{rules-E427DKYJ.js → rules-Y4VSOY5Y.js} +3 -3
  81. package/dist/run-VPNXEIBY.js +687 -0
  82. package/dist/server-COL4AXKU-P7S7NNF6.js +11 -0
  83. package/dist/server-KKSETHDV-XSSLEENT.js +20 -0
  84. package/dist/{skills-CX73O3IV.js → skills-QEYU2N27.js} +4 -2
  85. package/dist/start-JYOEL7AJ.js +303 -0
  86. package/dist/{status-4DFHDJMN.js → status-BHQYYGAL.js} +2 -2
  87. package/dist/{templates-U7T6MARD.js → templates-CBRUJ66V.js} +4 -3
  88. package/dist/tui-DP7736EX.js +61 -0
  89. package/dist/ulpi-5EN6JCAS-LFE3WSL4.js +10 -0
  90. package/dist/{uninstall-6SW35IK4.js → uninstall-ICUV6DDV.js} +3 -3
  91. package/dist/{update-M6IBJNYP.js → update-7ZMAYRBH.js} +3 -3
  92. package/dist/{version-checker-Q6YTYAGP.js → version-checker-4ZFMZA7Y.js} +2 -2
  93. package/package.json +39 -31
  94. package/dist/chunk-2MZER6ND.js +0 -415
  95. package/dist/chunk-2VYFVYJL.js +0 -4273
  96. package/dist/chunk-6OCEY7JY.js +0 -422
  97. package/dist/chunk-7LXY5UVC.js +0 -330
  98. package/dist/chunk-B55DDP24.js +0 -136
  99. package/dist/chunk-JWUUVXIV.js +0 -13694
  100. package/dist/chunk-MIAQVCFW.js +0 -39
  101. package/dist/chunk-YM2HV4IA.js +0 -505
  102. package/dist/ci-STSL2LSP.js +0 -370
  103. package/dist/mcp-installer-NQCGKQ23.js +0 -124
  104. package/dist/projects-ATHDD3D6.js +0 -271
  105. package/dist/review-ADUPV3PN.js +0 -152
  106. package/dist/server-USLHY6GH-AEOJC5ST.js +0 -18
  107. package/dist/server-X5P6WH2M-7K2RY34N.js +0 -11
  108. package/dist/skills/ulpi-generate-guardian/SKILL.md +0 -750
  109. package/dist/skills/ulpi-generate-guardian/references/framework-rules.md +0 -849
  110. package/dist/skills/ulpi-generate-guardian/references/language-rules.md +0 -591
  111. package/dist/ui-OWXZ3YSR.js +0 -167
  112. package/dist/ui.html +0 -698
@@ -0,0 +1,1271 @@
1
+ import {
2
+ extractDescription,
3
+ githubFetch
4
+ } from "./chunk-76D3BYJD.js";
5
+
6
+ // ../../packages/tracker-engine/dist/index.js
7
+ import { readFile, writeFile, access, constants } from "fs/promises";
8
+ import { resolve } from "path";
9
+ import { readFile as readFile2, writeFile as writeFile2, access as access2, constants as constants2 } from "fs/promises";
10
+ import { resolve as resolve2 } from "path";
11
+ var BaseTrackerPlugin = class {
12
+ config = {};
13
+ ready = false;
14
+ async initialize(config) {
15
+ this.config = config;
16
+ this.ready = true;
17
+ }
18
+ async isReady() {
19
+ return this.ready;
20
+ }
21
+ async getTask(id) {
22
+ const tasks = await this.getTasks();
23
+ return tasks.find((t) => t.id === id);
24
+ }
25
+ /**
26
+ * Get the next task to work on.
27
+ * 1. Gets all open/in_progress tasks
28
+ * 2. Filters to ready tasks (no unresolved dependencies)
29
+ * 3. Sorts by priority (lowest number = highest priority)
30
+ * 4. Prefers in_progress tasks over open tasks
31
+ */
32
+ async getNextTask(filter) {
33
+ const mergedFilter = {
34
+ ...filter,
35
+ status: ["open", "in_progress"],
36
+ ready: true
37
+ };
38
+ const tasks = await this.getTasks(mergedFilter);
39
+ if (tasks.length === 0) return void 0;
40
+ tasks.sort((a, b) => a.priority - b.priority);
41
+ const inProgress = tasks.find((t) => t.status === "in_progress");
42
+ if (inProgress) return inProgress;
43
+ return tasks[0];
44
+ }
45
+ async isComplete(filter) {
46
+ const tasks = await this.getTasks(filter);
47
+ return tasks.every(
48
+ (t) => t.status === "done" || t.status === "skipped"
49
+ );
50
+ }
51
+ async isTaskReady(id) {
52
+ const task = await this.getTask(id);
53
+ if (!task) return false;
54
+ const allTasks = await this.getTasks();
55
+ return this.checkTaskReady(task, allTasks);
56
+ }
57
+ async sync() {
58
+ return {
59
+ success: true,
60
+ message: "Sync not required for this tracker",
61
+ syncedAt: (/* @__PURE__ */ new Date()).toISOString()
62
+ };
63
+ }
64
+ getTemplate() {
65
+ return `## Task
66
+ **ID**: {{taskId}}
67
+ **Title**: {{taskTitle}}
68
+
69
+ {{#if taskDescription}}
70
+ ## Description
71
+ {{taskDescription}}
72
+ {{/if}}
73
+
74
+ {{#if dependsOn}}
75
+ **Dependencies**: {{dependsOn}}
76
+ {{/if}}
77
+
78
+ ## Instructions
79
+ Complete the task described above.
80
+ When finished, signal completion.
81
+ `;
82
+ }
83
+ async dispose() {
84
+ this.ready = false;
85
+ }
86
+ // ─── Helper methods for subclasses ───
87
+ /**
88
+ * Filter tasks by the given criteria.
89
+ */
90
+ filterTasks(tasks, filter) {
91
+ if (!filter) return tasks;
92
+ let result = tasks;
93
+ if (filter.status) {
94
+ const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
95
+ result = result.filter((t) => statuses.includes(t.status));
96
+ }
97
+ if (filter.labels && filter.labels.length > 0) {
98
+ result = result.filter(
99
+ (t) => filter.labels.every((label) => t.labels?.includes(label))
100
+ );
101
+ }
102
+ if (filter.priority !== void 0) {
103
+ const priorities = Array.isArray(filter.priority) ? filter.priority : [filter.priority];
104
+ result = result.filter(
105
+ (t) => priorities.includes(t.priority)
106
+ );
107
+ }
108
+ if (filter.parentId) {
109
+ result = result.filter((t) => t.parentId === filter.parentId);
110
+ }
111
+ if (filter.assignee) {
112
+ result = result.filter((t) => t.assignee === filter.assignee);
113
+ }
114
+ if (filter.type) {
115
+ const types = Array.isArray(filter.type) ? filter.type : [filter.type];
116
+ result = result.filter((t) => t.type && types.includes(t.type));
117
+ }
118
+ if (filter.excludeIds && filter.excludeIds.length > 0) {
119
+ const excludeSet = new Set(filter.excludeIds);
120
+ result = result.filter((t) => !excludeSet.has(t.id));
121
+ }
122
+ if (filter.ready) {
123
+ result = result.filter((t) => this.checkTaskReady(t, tasks));
124
+ }
125
+ if (filter.offset && filter.offset > 0) {
126
+ result = result.slice(filter.offset);
127
+ }
128
+ if (filter.limit && filter.limit > 0) {
129
+ result = result.slice(0, filter.limit);
130
+ }
131
+ return result;
132
+ }
133
+ /**
134
+ * Check if a task is ready (all dependencies resolved).
135
+ */
136
+ checkTaskReady(task, allTasks) {
137
+ if (!task.dependsOn || task.dependsOn.length === 0) return true;
138
+ return task.dependsOn.every((depId) => {
139
+ const depTask = allTasks.find((t) => t.id === depId);
140
+ return !depTask || depTask.status === "done" || depTask.status === "skipped";
141
+ });
142
+ }
143
+ };
144
+ function clampPriority(p) {
145
+ if (p === void 0) return 2;
146
+ return Math.max(0, Math.min(4, p));
147
+ }
148
+ function jsonTaskToTrackerTask(task) {
149
+ return {
150
+ id: task.id,
151
+ title: task.title,
152
+ status: task.status,
153
+ priority: clampPriority(task.priority),
154
+ description: task.description,
155
+ labels: task.labels,
156
+ type: task.type,
157
+ parentId: task.parentId,
158
+ dependsOn: task.dependsOn,
159
+ blocks: task.blocks,
160
+ assignee: task.assignee,
161
+ metadata: task.metadata
162
+ };
163
+ }
164
+ var JsonTrackerPlugin = class extends BaseTrackerPlugin {
165
+ meta = {
166
+ id: "json",
167
+ name: "JSON File Tracker",
168
+ description: "Track tasks in a local tasks.json file",
169
+ version: "1.0.0",
170
+ supportsBidirectionalSync: false,
171
+ supportsHierarchy: true,
172
+ supportsDependencies: true
173
+ };
174
+ filePath = "";
175
+ cache = null;
176
+ cacheTime = 0;
177
+ CACHE_TTL_MS = 1e3;
178
+ async initialize(config) {
179
+ await super.initialize(config);
180
+ if (typeof config.path === "string") {
181
+ this.filePath = resolve(config.path);
182
+ }
183
+ if (this.filePath) {
184
+ try {
185
+ await access(this.filePath, constants.R_OK | constants.W_OK);
186
+ this.ready = true;
187
+ } catch {
188
+ this.ready = false;
189
+ }
190
+ }
191
+ }
192
+ async isReady() {
193
+ if (!this.filePath) return false;
194
+ try {
195
+ await access(this.filePath, constants.R_OK | constants.W_OK);
196
+ this.ready = true;
197
+ return true;
198
+ } catch {
199
+ this.ready = false;
200
+ return false;
201
+ }
202
+ }
203
+ async readTasksFile() {
204
+ const now = Date.now();
205
+ if (this.cache && now - this.cacheTime < this.CACHE_TTL_MS) {
206
+ return this.cache;
207
+ }
208
+ const content = await readFile(this.filePath, "utf-8");
209
+ const parsed = JSON.parse(content);
210
+ this.cache = parsed;
211
+ this.cacheTime = now;
212
+ return parsed;
213
+ }
214
+ async writeTasksFile(data) {
215
+ if (!data.metadata) data.metadata = {};
216
+ data.metadata.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
217
+ const content = JSON.stringify(data, null, 2);
218
+ await writeFile(this.filePath, content, "utf-8");
219
+ this.cache = data;
220
+ this.cacheTime = Date.now();
221
+ }
222
+ async getTasks(filter) {
223
+ if (!this.filePath) return [];
224
+ try {
225
+ const data = await this.readTasksFile();
226
+ const tasks = data.tasks.map(jsonTaskToTrackerTask);
227
+ return this.filterTasks(tasks, filter);
228
+ } catch {
229
+ return [];
230
+ }
231
+ }
232
+ async completeTask(id, result) {
233
+ try {
234
+ this.cache = null;
235
+ const data = await this.readTasksFile();
236
+ const taskIndex = data.tasks.findIndex((t) => t.id === id);
237
+ if (taskIndex === -1) {
238
+ return { success: false, message: `Task ${id} not found`, error: "Not found" };
239
+ }
240
+ const task = data.tasks[taskIndex];
241
+ task.status = "done";
242
+ if (result?.summary) {
243
+ if (!task.metadata) task.metadata = {};
244
+ task.metadata.completionSummary = result.summary;
245
+ }
246
+ await this.writeTasksFile(data);
247
+ return { success: true, message: `Task ${id} marked as done` };
248
+ } catch (err) {
249
+ const message = err instanceof Error ? err.message : String(err);
250
+ return { success: false, message: `Failed to complete task ${id}`, error: message };
251
+ }
252
+ }
253
+ async updateTaskStatus(id, status) {
254
+ try {
255
+ this.cache = null;
256
+ const data = await this.readTasksFile();
257
+ const taskIndex = data.tasks.findIndex((t) => t.id === id);
258
+ if (taskIndex === -1) return void 0;
259
+ const task = data.tasks[taskIndex];
260
+ task.status = status;
261
+ await this.writeTasksFile(data);
262
+ return jsonTaskToTrackerTask(task);
263
+ } catch {
264
+ return void 0;
265
+ }
266
+ }
267
+ getStateFiles() {
268
+ if (this.filePath) return [this.filePath];
269
+ return [];
270
+ }
271
+ getFilePath() {
272
+ return this.filePath;
273
+ }
274
+ };
275
+ var createJsonTracker = () => new JsonTrackerPlugin();
276
+ var json_default = createJsonTracker;
277
+ function mapGitHubStatus(state) {
278
+ return state === "closed" ? "done" : "open";
279
+ }
280
+ function mapPriorityFromLabels(labels) {
281
+ for (const label of labels) {
282
+ const name = label.name.toLowerCase();
283
+ if (name === "p0" || name === "critical") return 0;
284
+ if (name === "p1" || name === "high") return 1;
285
+ if (name === "p2" || name === "medium") return 2;
286
+ if (name === "p3" || name === "low") return 3;
287
+ if (name === "p4" || name === "backlog") return 4;
288
+ }
289
+ return 2;
290
+ }
291
+ function mapTypeFromLabels(labels) {
292
+ for (const label of labels) {
293
+ const name = label.name.toLowerCase();
294
+ if (name === "bug") return "bug";
295
+ if (name === "feature" || name === "enhancement") return "feature";
296
+ if (name === "task") return "task";
297
+ if (name === "epic") return "epic";
298
+ }
299
+ return void 0;
300
+ }
301
+ function issueToTask(issue, repo) {
302
+ return {
303
+ id: `${repo}#${issue.number}`,
304
+ title: issue.title,
305
+ status: mapGitHubStatus(issue.state),
306
+ priority: mapPriorityFromLabels(issue.labels),
307
+ description: issue.body ?? void 0,
308
+ labels: issue.labels.map((l) => l.name),
309
+ type: mapTypeFromLabels(issue.labels),
310
+ assignee: issue.assignee?.login,
311
+ createdAt: issue.created_at,
312
+ updatedAt: issue.updated_at,
313
+ metadata: {
314
+ number: issue.number,
315
+ repo,
316
+ milestone: issue.milestone?.title
317
+ }
318
+ };
319
+ }
320
+ var GitHubTrackerPlugin = class extends BaseTrackerPlugin {
321
+ meta = {
322
+ id: "github",
323
+ name: "GitHub Issues Tracker",
324
+ description: "Track tasks via GitHub Issues",
325
+ version: "1.0.0",
326
+ supportsBidirectionalSync: true,
327
+ supportsHierarchy: false,
328
+ supportsDependencies: true
329
+ };
330
+ token = "";
331
+ repo = "";
332
+ async initialize(config) {
333
+ await super.initialize(config);
334
+ if (typeof config.token === "string") {
335
+ this.token = config.token;
336
+ }
337
+ if (typeof config.repo === "string") {
338
+ this.repo = config.repo;
339
+ }
340
+ this.ready = Boolean(this.token && this.repo);
341
+ }
342
+ async fetchIssues(state = "all") {
343
+ const response = await githubFetch(
344
+ `/repos/${this.repo}/issues?state=${state}&per_page=100`,
345
+ this.token
346
+ );
347
+ const data = await response.json();
348
+ return data.filter((issue) => !("pull_request" in issue));
349
+ }
350
+ async getTasks(filter) {
351
+ try {
352
+ const issues = await this.fetchIssues();
353
+ const tasks = issues.map((issue) => issueToTask(issue, this.repo));
354
+ return this.filterTasks(tasks, filter);
355
+ } catch {
356
+ return [];
357
+ }
358
+ }
359
+ async completeTask(id, _result) {
360
+ try {
361
+ const issueNumber = this.extractIssueNumber(id);
362
+ if (!issueNumber) {
363
+ return { success: false, message: `Invalid issue ID: ${id}`, error: "Invalid ID" };
364
+ }
365
+ await githubFetch(
366
+ `/repos/${this.repo}/issues/${issueNumber}`,
367
+ this.token,
368
+ {
369
+ method: "PATCH",
370
+ headers: { "Content-Type": "application/json" },
371
+ body: JSON.stringify({ state: "closed" })
372
+ }
373
+ );
374
+ return { success: true, message: `Issue #${issueNumber} closed` };
375
+ } catch (err) {
376
+ const message = err instanceof Error ? err.message : String(err);
377
+ return { success: false, message: `Failed to complete task`, error: message };
378
+ }
379
+ }
380
+ async updateTaskStatus(id, status) {
381
+ try {
382
+ const issueNumber = this.extractIssueNumber(id);
383
+ if (!issueNumber) return void 0;
384
+ const ghState = status === "done" || status === "skipped" ? "closed" : "open";
385
+ const response = await githubFetch(
386
+ `/repos/${this.repo}/issues/${issueNumber}`,
387
+ this.token,
388
+ {
389
+ method: "PATCH",
390
+ headers: { "Content-Type": "application/json" },
391
+ body: JSON.stringify({ state: ghState })
392
+ }
393
+ );
394
+ const issue = await response.json();
395
+ return issueToTask(issue, this.repo);
396
+ } catch {
397
+ return void 0;
398
+ }
399
+ }
400
+ async sync() {
401
+ try {
402
+ const issues = await this.fetchIssues();
403
+ return {
404
+ success: true,
405
+ message: `Synced ${issues.length} issues from ${this.repo}`,
406
+ updated: issues.length,
407
+ syncedAt: (/* @__PURE__ */ new Date()).toISOString()
408
+ };
409
+ } catch (err) {
410
+ const message = err instanceof Error ? err.message : String(err);
411
+ return {
412
+ success: false,
413
+ message: "Sync failed",
414
+ error: message,
415
+ syncedAt: (/* @__PURE__ */ new Date()).toISOString()
416
+ };
417
+ }
418
+ }
419
+ extractIssueNumber(id) {
420
+ const match = id.match(/#(\d+)$/);
421
+ return match ? parseInt(match[1], 10) : null;
422
+ }
423
+ };
424
+ var createGitHubTracker = () => new GitHubTrackerPlugin();
425
+ var github_default = createGitHubTracker;
426
+ function mapJiraStatus(statusCategory) {
427
+ switch (statusCategory) {
428
+ case "done":
429
+ return "done";
430
+ case "indeterminate":
431
+ return "in_progress";
432
+ case "new":
433
+ default:
434
+ return "open";
435
+ }
436
+ }
437
+ function mapJiraPriority(priorityName) {
438
+ if (!priorityName) return 2;
439
+ const name = priorityName.toLowerCase();
440
+ if (name === "highest" || name === "blocker" || name === "critical") return 0;
441
+ if (name === "high") return 1;
442
+ if (name === "medium" || name === "normal") return 2;
443
+ if (name === "low") return 3;
444
+ if (name === "lowest" || name === "trivial") return 4;
445
+ return 2;
446
+ }
447
+ function mapJiraType(issuetypeName) {
448
+ const name = issuetypeName.toLowerCase();
449
+ if (name === "bug") return "bug";
450
+ if (name === "story" || name === "feature") return "feature";
451
+ if (name === "epic") return "epic";
452
+ if (name === "task" || name === "sub-task" || name === "subtask") return "task";
453
+ return name;
454
+ }
455
+ function jiraIssueToTask(issue, project) {
456
+ const { fields } = issue;
457
+ const dependsOn = [];
458
+ const blocks = [];
459
+ for (const link of fields.issuelinks) {
460
+ if (link.type.inward.toLowerCase().includes("blocked by") && link.inwardIssue) {
461
+ dependsOn.push(link.inwardIssue.key);
462
+ }
463
+ if (link.type.outward.toLowerCase().includes("blocks") && link.outwardIssue) {
464
+ blocks.push(link.outwardIssue.key);
465
+ }
466
+ }
467
+ return {
468
+ id: issue.key,
469
+ title: fields.summary,
470
+ status: mapJiraStatus(fields.status.statusCategory.key),
471
+ priority: mapJiraPriority(fields.priority?.name),
472
+ description: extractDescription(fields.description),
473
+ labels: fields.labels,
474
+ type: mapJiraType(fields.issuetype.name),
475
+ parentId: fields.parent?.key,
476
+ dependsOn: dependsOn.length > 0 ? dependsOn : void 0,
477
+ blocks: blocks.length > 0 ? blocks : void 0,
478
+ assignee: fields.assignee?.displayName,
479
+ createdAt: fields.created,
480
+ updatedAt: fields.updated,
481
+ metadata: {
482
+ project,
483
+ statusName: fields.status.name,
484
+ issuetype: fields.issuetype.name,
485
+ isSubtask: fields.issuetype.subtask
486
+ }
487
+ };
488
+ }
489
+ var JiraTrackerPlugin = class extends BaseTrackerPlugin {
490
+ meta = {
491
+ id: "jira",
492
+ name: "Jira Tracker",
493
+ description: "Track tasks via Jira Issues (REST API v3)",
494
+ version: "1.0.0",
495
+ supportsBidirectionalSync: true,
496
+ supportsHierarchy: true,
497
+ supportsDependencies: true
498
+ };
499
+ baseUrl = "";
500
+ email = "";
501
+ apiToken = "";
502
+ project = "";
503
+ jql = "";
504
+ async initialize(config) {
505
+ await super.initialize(config);
506
+ if (typeof config.baseUrl === "string") {
507
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
508
+ }
509
+ if (typeof config.email === "string") {
510
+ this.email = config.email;
511
+ }
512
+ if (typeof config.apiToken === "string") {
513
+ this.apiToken = config.apiToken;
514
+ }
515
+ if (typeof config.project === "string") {
516
+ this.project = config.project;
517
+ }
518
+ if (typeof config.jql === "string") {
519
+ this.jql = config.jql;
520
+ }
521
+ this.ready = Boolean(this.baseUrl && this.email && this.apiToken && this.project);
522
+ }
523
+ getAuthHeader() {
524
+ return `Basic ${Buffer.from(`${this.email}:${this.apiToken}`).toString("base64")}`;
525
+ }
526
+ async searchIssues(jql, startAt = 0) {
527
+ const url = `${this.baseUrl}/rest/api/3/search`;
528
+ const response = await fetch(url, {
529
+ method: "POST",
530
+ headers: {
531
+ Authorization: this.getAuthHeader(),
532
+ Accept: "application/json",
533
+ "Content-Type": "application/json"
534
+ },
535
+ body: JSON.stringify({
536
+ jql,
537
+ startAt,
538
+ maxResults: 100,
539
+ fields: [
540
+ "summary",
541
+ "description",
542
+ "status",
543
+ "priority",
544
+ "labels",
545
+ "assignee",
546
+ "issuetype",
547
+ "parent",
548
+ "issuelinks",
549
+ "created",
550
+ "updated"
551
+ ]
552
+ })
553
+ });
554
+ if (!response.ok) {
555
+ throw new Error(`Jira API error: ${response.status} ${response.statusText}`);
556
+ }
557
+ return await response.json();
558
+ }
559
+ async fetchAllIssues() {
560
+ const jql = this.jql || `project = "${this.project}" ORDER BY rank ASC`;
561
+ const allIssues = [];
562
+ let startAt = 0;
563
+ while (true) {
564
+ const result = await this.searchIssues(jql, startAt);
565
+ allIssues.push(...result.issues);
566
+ if (startAt + result.maxResults >= result.total) break;
567
+ startAt += result.maxResults;
568
+ }
569
+ return allIssues;
570
+ }
571
+ async getTasks(filter) {
572
+ try {
573
+ const issues = await this.fetchAllIssues();
574
+ const tasks = issues.map((issue) => jiraIssueToTask(issue, this.project));
575
+ return this.filterTasks(tasks, filter);
576
+ } catch {
577
+ return [];
578
+ }
579
+ }
580
+ async completeTask(id, _result) {
581
+ try {
582
+ const transUrl = `${this.baseUrl}/rest/api/3/issue/${encodeURIComponent(id)}/transitions`;
583
+ const transResponse = await fetch(transUrl, {
584
+ headers: {
585
+ Authorization: this.getAuthHeader(),
586
+ Accept: "application/json"
587
+ }
588
+ });
589
+ if (!transResponse.ok) {
590
+ return {
591
+ success: false,
592
+ message: `Failed to get transitions for ${id}`,
593
+ error: `Jira API error: ${transResponse.status}`
594
+ };
595
+ }
596
+ const transData = await transResponse.json();
597
+ const doneTransition = transData.transitions.find(
598
+ (t) => t.to.statusCategory.key === "done"
599
+ );
600
+ if (!doneTransition) {
601
+ return {
602
+ success: false,
603
+ message: `No "Done" transition available for ${id}`,
604
+ error: "No done transition found"
605
+ };
606
+ }
607
+ const response = await fetch(transUrl, {
608
+ method: "POST",
609
+ headers: {
610
+ Authorization: this.getAuthHeader(),
611
+ Accept: "application/json",
612
+ "Content-Type": "application/json"
613
+ },
614
+ body: JSON.stringify({ transition: { id: doneTransition.id } })
615
+ });
616
+ if (!response.ok) {
617
+ return {
618
+ success: false,
619
+ message: `Failed to transition ${id} to Done`,
620
+ error: `Jira API error: ${response.status}`
621
+ };
622
+ }
623
+ return { success: true, message: `${id} transitioned to Done` };
624
+ } catch (err) {
625
+ const message = err instanceof Error ? err.message : String(err);
626
+ return { success: false, message: `Failed to complete task`, error: message };
627
+ }
628
+ }
629
+ async updateTaskStatus(id, status) {
630
+ try {
631
+ const transUrl = `${this.baseUrl}/rest/api/3/issue/${encodeURIComponent(id)}/transitions`;
632
+ const transResponse = await fetch(transUrl, {
633
+ headers: {
634
+ Authorization: this.getAuthHeader(),
635
+ Accept: "application/json"
636
+ }
637
+ });
638
+ if (!transResponse.ok) return void 0;
639
+ const transData = await transResponse.json();
640
+ const targetCategory = status === "done" || status === "skipped" ? "done" : status === "in_progress" ? "indeterminate" : "new";
641
+ const transition = transData.transitions.find(
642
+ (t) => t.to.statusCategory.key === targetCategory
643
+ );
644
+ if (!transition) return void 0;
645
+ const response = await fetch(transUrl, {
646
+ method: "POST",
647
+ headers: {
648
+ Authorization: this.getAuthHeader(),
649
+ Accept: "application/json",
650
+ "Content-Type": "application/json"
651
+ },
652
+ body: JSON.stringify({ transition: { id: transition.id } })
653
+ });
654
+ if (!response.ok) return void 0;
655
+ const issueUrl = `${this.baseUrl}/rest/api/3/issue/${encodeURIComponent(id)}`;
656
+ const issueResponse = await fetch(issueUrl, {
657
+ headers: {
658
+ Authorization: this.getAuthHeader(),
659
+ Accept: "application/json"
660
+ }
661
+ });
662
+ if (!issueResponse.ok) return void 0;
663
+ const issue = await issueResponse.json();
664
+ return jiraIssueToTask(issue, this.project);
665
+ } catch {
666
+ return void 0;
667
+ }
668
+ }
669
+ async sync() {
670
+ try {
671
+ const issues = await this.fetchAllIssues();
672
+ return {
673
+ success: true,
674
+ message: `Synced ${issues.length} issues from Jira project ${this.project}`,
675
+ updated: issues.length,
676
+ syncedAt: (/* @__PURE__ */ new Date()).toISOString()
677
+ };
678
+ } catch (err) {
679
+ const message = err instanceof Error ? err.message : String(err);
680
+ return {
681
+ success: false,
682
+ message: "Sync failed",
683
+ error: message,
684
+ syncedAt: (/* @__PURE__ */ new Date()).toISOString()
685
+ };
686
+ }
687
+ }
688
+ };
689
+ var createJiraTracker = () => new JiraTrackerPlugin();
690
+ var jira_default = createJiraTracker;
691
+ var ISSUES_QUERY = `
692
+ query Issues($teamKey: String!, $after: String) {
693
+ issues(
694
+ filter: { team: { key: { eq: $teamKey } } }
695
+ first: 100
696
+ after: $after
697
+ orderBy: priority
698
+ ) {
699
+ nodes {
700
+ id
701
+ identifier
702
+ title
703
+ description
704
+ state { name type }
705
+ priority
706
+ labels { nodes { name } }
707
+ assignee { name email }
708
+ parent { identifier }
709
+ relations { nodes { type relatedIssue { identifier } } }
710
+ createdAt
711
+ updatedAt
712
+ }
713
+ pageInfo { hasNextPage endCursor }
714
+ }
715
+ }
716
+ `;
717
+ var UPDATE_STATE_MUTATION = `
718
+ mutation UpdateIssue($issueId: String!, $stateId: String!) {
719
+ issueUpdate(id: $issueId, input: { stateId: $stateId }) {
720
+ success
721
+ issue {
722
+ id
723
+ identifier
724
+ title
725
+ description
726
+ state { name type }
727
+ priority
728
+ labels { nodes { name } }
729
+ assignee { name email }
730
+ parent { identifier }
731
+ relations { nodes { type relatedIssue { identifier } } }
732
+ createdAt
733
+ updatedAt
734
+ }
735
+ }
736
+ }
737
+ `;
738
+ var WORKFLOW_STATES_QUERY = `
739
+ query WorkflowStates($teamKey: String!) {
740
+ team(key: $teamKey) { id }
741
+ workflowStates(filter: { team: { key: { eq: $teamKey } } }) {
742
+ nodes { id name type }
743
+ }
744
+ }
745
+ `;
746
+ function mapLinearStatus(stateType) {
747
+ switch (stateType) {
748
+ case "completed":
749
+ return "done";
750
+ case "cancelled":
751
+ return "skipped";
752
+ case "started":
753
+ return "in_progress";
754
+ case "unstarted":
755
+ case "backlog":
756
+ case "triage":
757
+ default:
758
+ return "open";
759
+ }
760
+ }
761
+ function mapLinearPriority(priority) {
762
+ switch (priority) {
763
+ case 1:
764
+ return 0;
765
+ // Urgent -> P0
766
+ case 2:
767
+ return 1;
768
+ // High -> P1
769
+ case 3:
770
+ return 2;
771
+ // Medium -> P2
772
+ case 4:
773
+ return 3;
774
+ // Low -> P3
775
+ case 0:
776
+ default:
777
+ return 2;
778
+ }
779
+ }
780
+ function linearIssueToTask(issue) {
781
+ const dependsOn = [];
782
+ const blocks = [];
783
+ for (const rel of issue.relations.nodes) {
784
+ if (rel.type === "blocks") {
785
+ blocks.push(rel.relatedIssue.identifier);
786
+ } else if (rel.type === "blocked") {
787
+ dependsOn.push(rel.relatedIssue.identifier);
788
+ }
789
+ }
790
+ return {
791
+ id: issue.identifier,
792
+ title: issue.title,
793
+ status: mapLinearStatus(issue.state.type),
794
+ priority: mapLinearPriority(issue.priority),
795
+ description: issue.description ?? void 0,
796
+ labels: issue.labels.nodes.map((l) => l.name),
797
+ parentId: issue.parent?.identifier,
798
+ dependsOn: dependsOn.length > 0 ? dependsOn : void 0,
799
+ blocks: blocks.length > 0 ? blocks : void 0,
800
+ assignee: issue.assignee?.name,
801
+ createdAt: issue.createdAt,
802
+ updatedAt: issue.updatedAt,
803
+ metadata: {
804
+ linearId: issue.id,
805
+ stateName: issue.state.name,
806
+ stateType: issue.state.type
807
+ }
808
+ };
809
+ }
810
+ var LinearTrackerPlugin = class extends BaseTrackerPlugin {
811
+ meta = {
812
+ id: "linear",
813
+ name: "Linear Tracker",
814
+ description: "Track tasks via Linear Issues (GraphQL API)",
815
+ version: "1.0.0",
816
+ supportsBidirectionalSync: true,
817
+ supportsHierarchy: true,
818
+ supportsDependencies: true
819
+ };
820
+ apiKey = "";
821
+ teamKey = "";
822
+ apiUrl = "https://api.linear.app/graphql";
823
+ // Cached workflow states for the team
824
+ workflowStates = [];
825
+ async initialize(config) {
826
+ await super.initialize(config);
827
+ if (typeof config.apiKey === "string") {
828
+ this.apiKey = config.apiKey;
829
+ }
830
+ if (typeof config.teamKey === "string") {
831
+ this.teamKey = config.teamKey;
832
+ }
833
+ if (typeof config.apiUrl === "string") {
834
+ this.apiUrl = config.apiUrl;
835
+ }
836
+ this.ready = Boolean(this.apiKey && this.teamKey);
837
+ if (this.ready) {
838
+ try {
839
+ await this.loadWorkflowStates();
840
+ } catch {
841
+ }
842
+ }
843
+ }
844
+ async graphql(query, variables) {
845
+ const response = await fetch(this.apiUrl, {
846
+ method: "POST",
847
+ headers: {
848
+ Authorization: this.apiKey,
849
+ "Content-Type": "application/json"
850
+ },
851
+ body: JSON.stringify({ query, variables })
852
+ });
853
+ if (!response.ok) {
854
+ throw new Error(`Linear API error: ${response.status} ${response.statusText}`);
855
+ }
856
+ const result = await response.json();
857
+ if (result.errors && result.errors.length > 0) {
858
+ throw new Error(`Linear GraphQL error: ${result.errors[0].message}`);
859
+ }
860
+ return result;
861
+ }
862
+ async loadWorkflowStates() {
863
+ const result = await this.graphql(WORKFLOW_STATES_QUERY, { teamKey: this.teamKey });
864
+ this.workflowStates = result.data?.workflowStates?.nodes ?? [];
865
+ }
866
+ async fetchAllIssues() {
867
+ const allIssues = [];
868
+ let cursor = null;
869
+ while (true) {
870
+ const variables = { teamKey: this.teamKey };
871
+ if (cursor) variables.after = cursor;
872
+ const result = await this.graphql(ISSUES_QUERY, variables);
873
+ const issues = result.data?.issues;
874
+ if (!issues) break;
875
+ allIssues.push(...issues.nodes);
876
+ if (!issues.pageInfo.hasNextPage) break;
877
+ cursor = issues.pageInfo.endCursor;
878
+ }
879
+ return allIssues;
880
+ }
881
+ async findWorkflowState(targetType) {
882
+ if (this.workflowStates.length === 0) {
883
+ await this.loadWorkflowStates();
884
+ }
885
+ return this.workflowStates.find((s) => s.type === targetType);
886
+ }
887
+ async getTasks(filter) {
888
+ try {
889
+ const issues = await this.fetchAllIssues();
890
+ const tasks = issues.map(linearIssueToTask);
891
+ return this.filterTasks(tasks, filter);
892
+ } catch {
893
+ return [];
894
+ }
895
+ }
896
+ async completeTask(id, _result) {
897
+ try {
898
+ const doneState = await this.findWorkflowState("completed");
899
+ if (!doneState) {
900
+ return {
901
+ success: false,
902
+ message: `No "completed" workflow state found for team ${this.teamKey}`,
903
+ error: "No completed state"
904
+ };
905
+ }
906
+ const issues = await this.fetchAllIssues();
907
+ const issue = issues.find((i) => i.identifier === id);
908
+ if (!issue) {
909
+ return { success: false, message: `Issue ${id} not found`, error: "Not found" };
910
+ }
911
+ const result = await this.graphql(UPDATE_STATE_MUTATION, {
912
+ issueId: issue.id,
913
+ stateId: doneState.id
914
+ });
915
+ if (!result.data?.issueUpdate?.success) {
916
+ return { success: false, message: `Failed to complete ${id}`, error: "Update failed" };
917
+ }
918
+ return { success: true, message: `${id} marked as completed` };
919
+ } catch (err) {
920
+ const message = err instanceof Error ? err.message : String(err);
921
+ return { success: false, message: `Failed to complete task`, error: message };
922
+ }
923
+ }
924
+ async updateTaskStatus(id, status) {
925
+ try {
926
+ const targetType = status === "done" ? "completed" : status === "skipped" ? "cancelled" : status === "in_progress" ? "started" : "unstarted";
927
+ const targetState = await this.findWorkflowState(targetType);
928
+ if (!targetState) return void 0;
929
+ const issues = await this.fetchAllIssues();
930
+ const issue = issues.find((i) => i.identifier === id);
931
+ if (!issue) return void 0;
932
+ const result = await this.graphql(UPDATE_STATE_MUTATION, {
933
+ issueId: issue.id,
934
+ stateId: targetState.id
935
+ });
936
+ if (!result.data?.issueUpdate?.success) return void 0;
937
+ return linearIssueToTask(result.data.issueUpdate.issue);
938
+ } catch {
939
+ return void 0;
940
+ }
941
+ }
942
+ async sync() {
943
+ try {
944
+ const issues = await this.fetchAllIssues();
945
+ return {
946
+ success: true,
947
+ message: `Synced ${issues.length} issues from Linear team ${this.teamKey}`,
948
+ updated: issues.length,
949
+ syncedAt: (/* @__PURE__ */ new Date()).toISOString()
950
+ };
951
+ } catch (err) {
952
+ const message = err instanceof Error ? err.message : String(err);
953
+ return {
954
+ success: false,
955
+ message: "Sync failed",
956
+ error: message,
957
+ syncedAt: (/* @__PURE__ */ new Date()).toISOString()
958
+ };
959
+ }
960
+ }
961
+ };
962
+ var createLinearTracker = () => new LinearTrackerPlugin();
963
+ var linear_default = createLinearTracker;
964
+ function extractTaskId(text, lineIndex) {
965
+ const bracketMatch = text.match(/^\[([^\]]+)\]\s*/);
966
+ if (bracketMatch) return bracketMatch[1];
967
+ const commentMatch = text.match(/<!--\s*id:\s*([^\s]+)\s*-->/);
968
+ if (commentMatch) return commentMatch[1];
969
+ return `md-L${lineIndex + 1}`;
970
+ }
971
+ function extractDependencies(text) {
972
+ const match = text.match(/<!--\s*depends:\s*(.+?)\s*-->/);
973
+ if (!match) return [];
974
+ return match[1].split(",").map((s) => s.trim()).filter(Boolean);
975
+ }
976
+ function extractPriority(text) {
977
+ const match = text.match(/<!--\s*priority:\s*(\d)\s*-->/);
978
+ if (!match) return 2;
979
+ const p = parseInt(match[1], 10);
980
+ return Math.max(0, Math.min(4, p));
981
+ }
982
+ function extractLabels(text) {
983
+ const match = text.match(/<!--\s*labels:\s*(.+?)\s*-->/);
984
+ if (!match) return [];
985
+ return match[1].split(",").map((s) => s.trim()).filter(Boolean);
986
+ }
987
+ function extractType(text) {
988
+ const match = text.match(/<!--\s*type:\s*(\w+)\s*-->/);
989
+ return match ? match[1] : void 0;
990
+ }
991
+ function cleanTaskTitle(text) {
992
+ return text.replace(/^\[([^\]]+)\]\s*/, "").replace(/<!--[^>]*-->/g, "").trim();
993
+ }
994
+ function parseChecklist(content) {
995
+ const lines = content.split("\n");
996
+ const items = [];
997
+ let currentHeading;
998
+ for (let i = 0; i < lines.length; i++) {
999
+ const line = lines[i];
1000
+ const headingMatch = line.match(/^(#{1,6})\s+(.+)/);
1001
+ if (headingMatch) {
1002
+ currentHeading = headingMatch[2].trim();
1003
+ continue;
1004
+ }
1005
+ const checkMatch = line.match(/^(\s*)- \[([ xX])\]\s+(.+)/);
1006
+ if (!checkMatch) continue;
1007
+ const text = checkMatch[3];
1008
+ const checked = checkMatch[2] !== " ";
1009
+ items.push({
1010
+ lineIndex: i,
1011
+ checked,
1012
+ text,
1013
+ id: extractTaskId(text, i),
1014
+ dependsOn: extractDependencies(text),
1015
+ priority: extractPriority(text),
1016
+ labels: extractLabels(text),
1017
+ type: extractType(text),
1018
+ parentHeading: currentHeading
1019
+ });
1020
+ }
1021
+ return items;
1022
+ }
1023
+ function checklistItemToTask(item, allItems) {
1024
+ let parentId;
1025
+ if (item.parentHeading) {
1026
+ const parentItem = allItems.find(
1027
+ (other) => other !== item && cleanTaskTitle(other.text) === item.parentHeading
1028
+ );
1029
+ if (parentItem) {
1030
+ parentId = parentItem.id;
1031
+ }
1032
+ }
1033
+ return {
1034
+ id: item.id,
1035
+ title: cleanTaskTitle(item.text),
1036
+ status: item.checked ? "done" : "open",
1037
+ priority: item.priority,
1038
+ labels: item.labels.length > 0 ? item.labels : void 0,
1039
+ type: item.type,
1040
+ parentId,
1041
+ dependsOn: item.dependsOn.length > 0 ? item.dependsOn : void 0,
1042
+ metadata: {
1043
+ lineIndex: item.lineIndex,
1044
+ heading: item.parentHeading
1045
+ }
1046
+ };
1047
+ }
1048
+ var MarkdownTrackerPlugin = class extends BaseTrackerPlugin {
1049
+ meta = {
1050
+ id: "markdown",
1051
+ name: "Markdown Checklist Tracker",
1052
+ description: "Track tasks from markdown checklist files",
1053
+ version: "1.0.0",
1054
+ supportsBidirectionalSync: false,
1055
+ supportsHierarchy: true,
1056
+ supportsDependencies: true
1057
+ };
1058
+ filePath = "";
1059
+ cache = null;
1060
+ cacheTime = 0;
1061
+ CACHE_TTL_MS = 1e3;
1062
+ async initialize(config) {
1063
+ await super.initialize(config);
1064
+ if (typeof config.path === "string") {
1065
+ this.filePath = resolve2(config.path);
1066
+ }
1067
+ if (this.filePath) {
1068
+ try {
1069
+ await access2(this.filePath, constants2.R_OK | constants2.W_OK);
1070
+ this.ready = true;
1071
+ } catch {
1072
+ this.ready = false;
1073
+ }
1074
+ }
1075
+ }
1076
+ async isReady() {
1077
+ if (!this.filePath) return false;
1078
+ try {
1079
+ await access2(this.filePath, constants2.R_OK | constants2.W_OK);
1080
+ this.ready = true;
1081
+ return true;
1082
+ } catch {
1083
+ this.ready = false;
1084
+ return false;
1085
+ }
1086
+ }
1087
+ async readAndParse() {
1088
+ const now = Date.now();
1089
+ if (this.cache && now - this.cacheTime < this.CACHE_TTL_MS) {
1090
+ return this.cache;
1091
+ }
1092
+ const content = await readFile2(this.filePath, "utf-8");
1093
+ const items = parseChecklist(content);
1094
+ this.cache = { content, items };
1095
+ this.cacheTime = now;
1096
+ return this.cache;
1097
+ }
1098
+ async writeContent(content) {
1099
+ await writeFile2(this.filePath, content, "utf-8");
1100
+ this.cache = null;
1101
+ }
1102
+ /**
1103
+ * Toggle a checklist item in the raw markdown content.
1104
+ */
1105
+ toggleChecklistItem(content, lineIndex, checked) {
1106
+ const lines = content.split("\n");
1107
+ const line = lines[lineIndex];
1108
+ if (!line) return content;
1109
+ const replacement = checked ? "[x]" : "[ ]";
1110
+ lines[lineIndex] = line.replace(/\[([ xX])\]/, replacement);
1111
+ return lines.join("\n");
1112
+ }
1113
+ async getTasks(filter) {
1114
+ if (!this.filePath) return [];
1115
+ try {
1116
+ const { items } = await this.readAndParse();
1117
+ const tasks = items.map((item) => checklistItemToTask(item, items));
1118
+ return this.filterTasks(tasks, filter);
1119
+ } catch {
1120
+ return [];
1121
+ }
1122
+ }
1123
+ async completeTask(id, _result) {
1124
+ try {
1125
+ this.cache = null;
1126
+ const { content, items } = await this.readAndParse();
1127
+ const item = items.find((i) => i.id === id);
1128
+ if (!item) {
1129
+ return { success: false, message: `Task ${id} not found`, error: "Not found" };
1130
+ }
1131
+ const newContent = this.toggleChecklistItem(content, item.lineIndex, true);
1132
+ await this.writeContent(newContent);
1133
+ return { success: true, message: `Task ${id} marked as done` };
1134
+ } catch (err) {
1135
+ const message = err instanceof Error ? err.message : String(err);
1136
+ return { success: false, message: `Failed to complete task ${id}`, error: message };
1137
+ }
1138
+ }
1139
+ async updateTaskStatus(id, status) {
1140
+ try {
1141
+ this.cache = null;
1142
+ const { content, items } = await this.readAndParse();
1143
+ const item = items.find((i) => i.id === id);
1144
+ if (!item) return void 0;
1145
+ const checked = status === "done" || status === "skipped";
1146
+ const newContent = this.toggleChecklistItem(content, item.lineIndex, checked);
1147
+ await this.writeContent(newContent);
1148
+ const updated = await this.readAndParse();
1149
+ const updatedItem = updated.items.find((i) => i.id === id);
1150
+ if (!updatedItem) return void 0;
1151
+ return checklistItemToTask(updatedItem, updated.items);
1152
+ } catch {
1153
+ return void 0;
1154
+ }
1155
+ }
1156
+ getStateFiles() {
1157
+ if (this.filePath) return [this.filePath];
1158
+ return [];
1159
+ }
1160
+ getFilePath() {
1161
+ return this.filePath;
1162
+ }
1163
+ };
1164
+ var createMarkdownTracker = () => new MarkdownTrackerPlugin();
1165
+ var markdown_default = createMarkdownTracker;
1166
+ var TrackerRegistry = class _TrackerRegistry {
1167
+ static instance = null;
1168
+ plugins = /* @__PURE__ */ new Map();
1169
+ loadedInstances = /* @__PURE__ */ new Map();
1170
+ initialized = false;
1171
+ constructor() {
1172
+ }
1173
+ static getInstance() {
1174
+ if (!_TrackerRegistry.instance) {
1175
+ _TrackerRegistry.instance = new _TrackerRegistry();
1176
+ }
1177
+ return _TrackerRegistry.instance;
1178
+ }
1179
+ static resetInstance() {
1180
+ if (_TrackerRegistry.instance) {
1181
+ for (const instance of _TrackerRegistry.instance.loadedInstances.values()) {
1182
+ instance.dispose().catch(() => {
1183
+ });
1184
+ }
1185
+ _TrackerRegistry.instance.plugins.clear();
1186
+ _TrackerRegistry.instance.loadedInstances.clear();
1187
+ _TrackerRegistry.instance.initialized = false;
1188
+ }
1189
+ _TrackerRegistry.instance = null;
1190
+ }
1191
+ registerBuiltin(factory) {
1192
+ const instance = factory();
1193
+ const { meta } = instance;
1194
+ this.plugins.set(meta.id, { factory, meta, builtin: true });
1195
+ instance.dispose().catch(() => {
1196
+ });
1197
+ }
1198
+ getRegisteredPlugins() {
1199
+ return Array.from(this.plugins.values()).map((p) => p.meta);
1200
+ }
1201
+ getPluginMeta(pluginId) {
1202
+ return this.plugins.get(pluginId)?.meta;
1203
+ }
1204
+ hasPlugin(pluginId) {
1205
+ return this.plugins.has(pluginId);
1206
+ }
1207
+ createInstance(pluginId) {
1208
+ const registered = this.plugins.get(pluginId);
1209
+ if (!registered) return void 0;
1210
+ return registered.factory();
1211
+ }
1212
+ async getInstance(config) {
1213
+ const cacheKey = config.name;
1214
+ const cached = this.loadedInstances.get(cacheKey);
1215
+ if (cached) return cached;
1216
+ const instance = this.createInstance(config.plugin);
1217
+ if (!instance) {
1218
+ throw new Error(`Unknown tracker plugin: ${config.plugin}`);
1219
+ }
1220
+ await instance.initialize(config.options);
1221
+ this.loadedInstances.set(cacheKey, instance);
1222
+ return instance;
1223
+ }
1224
+ async disposeInstance(configName) {
1225
+ const instance = this.loadedInstances.get(configName);
1226
+ if (instance) {
1227
+ await instance.dispose();
1228
+ this.loadedInstances.delete(configName);
1229
+ }
1230
+ }
1231
+ async disposeAll() {
1232
+ const disposals = Array.from(this.loadedInstances.values()).map(
1233
+ (instance) => instance.dispose()
1234
+ );
1235
+ await Promise.all(disposals);
1236
+ this.loadedInstances.clear();
1237
+ }
1238
+ async initialize() {
1239
+ if (this.initialized) return;
1240
+ this.initialized = true;
1241
+ const builtins = [
1242
+ json_default,
1243
+ github_default,
1244
+ jira_default,
1245
+ linear_default,
1246
+ markdown_default
1247
+ ];
1248
+ for (const factory of builtins) {
1249
+ this.registerBuiltin(factory);
1250
+ }
1251
+ }
1252
+ };
1253
+ function getTrackerRegistry() {
1254
+ return TrackerRegistry.getInstance();
1255
+ }
1256
+
1257
+ export {
1258
+ BaseTrackerPlugin,
1259
+ JsonTrackerPlugin,
1260
+ json_default,
1261
+ GitHubTrackerPlugin,
1262
+ github_default,
1263
+ JiraTrackerPlugin,
1264
+ jira_default,
1265
+ LinearTrackerPlugin,
1266
+ linear_default,
1267
+ MarkdownTrackerPlugin,
1268
+ markdown_default,
1269
+ TrackerRegistry,
1270
+ getTrackerRegistry
1271
+ };