@jvittechs/j 1.0.57 → 1.0.59

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.
@@ -1,11 +1,15 @@
1
+ import {
2
+ SettingsService
3
+ } from "./chunk-BMDRQFY7.js";
4
+
1
5
  // src/commands/tasks/summary.ts
2
6
  import { Command } from "commander";
3
7
  import chalk2 from "chalk";
4
8
  import boxen from "boxen";
5
9
 
6
10
  // src/services/task.service.ts
7
- import { promises as fs, existsSync } from "fs";
8
- import { join, dirname } from "path";
11
+ import { promises as fs2, existsSync } from "fs";
12
+ import { join as join2, dirname } from "path";
9
13
  import { execSync } from "child_process";
10
14
  import chalk from "chalk";
11
15
 
@@ -13,6 +17,7 @@ import chalk from "chalk";
13
17
  import { z } from "zod";
14
18
  var TaskSchema = z.object({
15
19
  id: z.string().regex(/^T-\d+$/),
20
+ type: z.enum(["feature", "bug", "plan", "task", "prd", "prompt"]).default("task"),
16
21
  parent: z.string().default(""),
17
22
  title: z.string().min(1).max(500),
18
23
  status: z.enum(["todo", "in_progress", "done", "cancelled"]),
@@ -46,20 +51,427 @@ var PRIORITY_LABELS = {
46
51
  3: "Low"
47
52
  };
48
53
 
54
+ // src/services/config.service.ts
55
+ import { promises as fs } from "fs";
56
+ import { join } from "path";
57
+ import { homedir } from "os";
58
+ var ConfigService = class {
59
+ configDir;
60
+ configPath;
61
+ constructor() {
62
+ this.configDir = join(homedir(), ".jai1");
63
+ this.configPath = join(this.configDir, "config.json");
64
+ }
65
+ /**
66
+ * Check if config file exists
67
+ */
68
+ async exists() {
69
+ try {
70
+ await fs.access(this.configPath);
71
+ return true;
72
+ } catch {
73
+ return false;
74
+ }
75
+ }
76
+ /**
77
+ * Load configuration from file
78
+ * @returns Config object or null if not found
79
+ */
80
+ async load() {
81
+ if (!await this.exists()) {
82
+ return null;
83
+ }
84
+ try {
85
+ const content = await fs.readFile(this.configPath, "utf-8");
86
+ return JSON.parse(content);
87
+ } catch (error) {
88
+ throw new Error(
89
+ `Failed to load config: ${error instanceof Error ? error.message : String(error)}`
90
+ );
91
+ }
92
+ }
93
+ /**
94
+ * Save configuration to file
95
+ * Creates directory if it doesn't exist
96
+ * Sets proper file permissions (600)
97
+ */
98
+ async save(config) {
99
+ try {
100
+ await fs.mkdir(this.configDir, { recursive: true, mode: 448 });
101
+ await fs.writeFile(this.configPath, JSON.stringify(config, null, 2), {
102
+ mode: 384
103
+ });
104
+ } catch (error) {
105
+ throw new Error(
106
+ `Failed to save config: ${error instanceof Error ? error.message : String(error)}`
107
+ );
108
+ }
109
+ }
110
+ /**
111
+ * Get config file path
112
+ */
113
+ getConfigPath() {
114
+ return this.configPath;
115
+ }
116
+ /**
117
+ * Get config directory path
118
+ */
119
+ getConfigDir() {
120
+ return this.configDir;
121
+ }
122
+ };
123
+
124
+ // src/services/cloud-task-provider.ts
125
+ function mapCloudToLocal(ct) {
126
+ return {
127
+ id: ct.task_id,
128
+ type: ct.type,
129
+ parent: ct.parent || "",
130
+ title: ct.title,
131
+ status: ct.status,
132
+ assigned_to: ct.assigned_to || "",
133
+ claimed_at: ct.claimed_at || "",
134
+ priority: ct.priority ?? 2,
135
+ depends_on: Array.isArray(ct.depends_on) ? ct.depends_on : parseJsonArray(ct.depends_on),
136
+ tags: Array.isArray(ct.tags) ? ct.tags : parseJsonArray(ct.tags),
137
+ branch: ct.branch || "",
138
+ notes: ct.notes || "",
139
+ created: ct.created_at?.split("T")[0] || ct.created_at || "",
140
+ updated: ct.updated_at?.split("T")[0] || ct.updated_at || ""
141
+ };
142
+ }
143
+ function parseJsonArray(val) {
144
+ if (Array.isArray(val)) return val;
145
+ if (typeof val === "string") {
146
+ try {
147
+ return JSON.parse(val);
148
+ } catch {
149
+ return [];
150
+ }
151
+ }
152
+ return [];
153
+ }
154
+ function mapLocalToCloud(task) {
155
+ return {
156
+ task_id: task.id,
157
+ title: task.title,
158
+ type: task.type || "task",
159
+ parent: task.parent || "",
160
+ status: task.status,
161
+ assigned_to: task.assigned_to || "",
162
+ claimed_at: task.claimed_at || "",
163
+ priority: task.priority ?? 2,
164
+ depends_on: task.depends_on || [],
165
+ tags: task.tags || [],
166
+ branch: task.branch || "",
167
+ notes: task.notes || ""
168
+ };
169
+ }
170
+ var CloudTaskProvider = class {
171
+ apiUrl;
172
+ accessKey;
173
+ projectId;
174
+ constructor(config, projectId) {
175
+ this.apiUrl = config.apiUrl.replace(/\/$/, "");
176
+ this.accessKey = config.accessKey;
177
+ this.projectId = projectId;
178
+ }
179
+ // ============================================
180
+ // HELPERS
181
+ // ============================================
182
+ headers() {
183
+ return {
184
+ "Content-Type": "application/json",
185
+ "JAI1-Access-Key": this.accessKey
186
+ };
187
+ }
188
+ async request(path, options = {}) {
189
+ const url = `${this.apiUrl}${path}`;
190
+ const res = await fetch(url, {
191
+ ...options,
192
+ headers: { ...this.headers(), ...options.headers || {} }
193
+ });
194
+ if (!res.ok) {
195
+ let errMsg = `API error ${res.status}`;
196
+ try {
197
+ const body = await res.json();
198
+ if (body.error) errMsg = body.error;
199
+ } catch {
200
+ }
201
+ throw new Error(errMsg);
202
+ }
203
+ const data = await res.json();
204
+ if (!data.success) throw new Error(data.error || "API returned failure");
205
+ return data.data;
206
+ }
207
+ // ============================================
208
+ // ENSURE PROJECT REGISTERED
209
+ // ============================================
210
+ async ensureProjectRegistered(repoUrl) {
211
+ const result = await this.request(
212
+ "/api/projects/register",
213
+ {
214
+ method: "POST",
215
+ body: JSON.stringify({ repo_url: repoUrl })
216
+ }
217
+ );
218
+ return result.project_id;
219
+ }
220
+ // ============================================
221
+ // CRUD
222
+ // ============================================
223
+ async readAll() {
224
+ const rows = await this.request(
225
+ `/api/tasks?projectId=${encodeURIComponent(this.projectId)}`
226
+ );
227
+ return rows.map(mapCloudToLocal);
228
+ }
229
+ async add(input) {
230
+ const all = await this.readAll().catch(() => []);
231
+ const maxNum = all.reduce((max, t) => {
232
+ const n = parseInt(t.id.replace("T-", ""), 10);
233
+ return isNaN(n) ? max : Math.max(max, n);
234
+ }, 0);
235
+ const taskId = `T-${String(maxNum + 1).padStart(3, "0")}`;
236
+ const row = await this.request("/api/tasks", {
237
+ method: "POST",
238
+ body: JSON.stringify({
239
+ task_id: taskId,
240
+ project_id: this.projectId,
241
+ title: input.title,
242
+ type: input.type || "task",
243
+ parent: input.parent || "",
244
+ priority: input.priority ?? 2,
245
+ depends_on: input.depends_on || [],
246
+ tags: input.tags || [],
247
+ branch: input.branch || "",
248
+ notes: input.notes || ""
249
+ })
250
+ });
251
+ return mapCloudToLocal(row);
252
+ }
253
+ async update(id, updates) {
254
+ const row = await this.request(
255
+ `/api/tasks/${encodeURIComponent(id)}?projectId=${encodeURIComponent(this.projectId)}`,
256
+ {
257
+ method: "PATCH",
258
+ body: JSON.stringify(updates)
259
+ }
260
+ );
261
+ return mapCloudToLocal(row);
262
+ }
263
+ async deleteTask(id) {
264
+ await this.request(
265
+ `/api/tasks/${encodeURIComponent(id)}?projectId=${encodeURIComponent(this.projectId)}`,
266
+ { method: "DELETE" }
267
+ );
268
+ }
269
+ async deleteGroup(parent) {
270
+ await this.request(
271
+ `/api/tasks/group/${encodeURIComponent(parent)}?projectId=${encodeURIComponent(this.projectId)}`,
272
+ { method: "DELETE" }
273
+ );
274
+ }
275
+ // ============================================
276
+ // QUERY
277
+ // ============================================
278
+ async list(options) {
279
+ const params = new URLSearchParams({ projectId: this.projectId });
280
+ if (options?.status) params.set("status", options.status);
281
+ if (options?.type) params.set("type", options.type);
282
+ if (options?.parent !== void 0) params.set("parent", options.parent);
283
+ const rows = await this.request(`/api/tasks?${params}`);
284
+ return rows.map(mapCloudToLocal);
285
+ }
286
+ async getById(id) {
287
+ try {
288
+ const row = await this.request(
289
+ `/api/tasks/${encodeURIComponent(id)}?projectId=${encodeURIComponent(this.projectId)}`
290
+ );
291
+ return mapCloudToLocal(row);
292
+ } catch {
293
+ return null;
294
+ }
295
+ }
296
+ async getParents() {
297
+ const parents = await this.request(
298
+ `/api/tasks/parents?projectId=${encodeURIComponent(this.projectId)}`
299
+ );
300
+ const result = [];
301
+ for (const parent of parents) {
302
+ const tasks = await this.list({ parent });
303
+ const info = {
304
+ name: parent,
305
+ total: tasks.length,
306
+ todo: tasks.filter((t) => t.status === "todo").length,
307
+ in_progress: tasks.filter((t) => t.status === "in_progress").length,
308
+ done: tasks.filter((t) => t.status === "done").length,
309
+ cancelled: tasks.filter((t) => t.status === "cancelled").length,
310
+ blocked: 0,
311
+ ready: 0,
312
+ status: "todo"
313
+ };
314
+ if (info.done + info.cancelled === info.total) info.status = "done";
315
+ else if (info.in_progress > 0) info.status = "in_progress";
316
+ else if (info.todo > 0) info.status = "ready";
317
+ result.push(info);
318
+ }
319
+ return result;
320
+ }
321
+ async getStats() {
322
+ const raw = await this.request(
323
+ `/api/tasks/stats?projectId=${encodeURIComponent(this.projectId)}`
324
+ );
325
+ const s = raw.by_status;
326
+ const total = Object.values(s).reduce((a, b) => a + b, 0);
327
+ return {
328
+ total,
329
+ todo: s["todo"] || 0,
330
+ in_progress: s["in_progress"] || 0,
331
+ done: s["done"] || 0,
332
+ cancelled: s["cancelled"] || 0
333
+ };
334
+ }
335
+ // ============================================
336
+ // WORKFLOW
337
+ // ============================================
338
+ async getReady() {
339
+ const rows = await this.request(
340
+ `/api/tasks/ready?projectId=${encodeURIComponent(this.projectId)}`
341
+ );
342
+ return rows.map(mapCloudToLocal);
343
+ }
344
+ async pick(id, agentId) {
345
+ const row = await this.request(
346
+ `/api/tasks/${encodeURIComponent(id)}/pick`,
347
+ {
348
+ method: "POST",
349
+ body: JSON.stringify({
350
+ project_id: this.projectId,
351
+ agent_id: agentId || "cli"
352
+ })
353
+ }
354
+ );
355
+ return mapCloudToLocal(row);
356
+ }
357
+ async markDone(id) {
358
+ const row = await this.request(
359
+ `/api/tasks/${encodeURIComponent(id)}/done`,
360
+ {
361
+ method: "POST",
362
+ body: JSON.stringify({ project_id: this.projectId })
363
+ }
364
+ );
365
+ return mapCloudToLocal(row);
366
+ }
367
+ async cancel(id) {
368
+ await this.request(`/api/tasks/${encodeURIComponent(id)}/cancel`, {
369
+ method: "POST",
370
+ body: JSON.stringify({ project_id: this.projectId })
371
+ });
372
+ const task = await this.getById(id);
373
+ return task;
374
+ }
375
+ // ============================================
376
+ // SYNC
377
+ // ============================================
378
+ async pull() {
379
+ return this.readAll();
380
+ }
381
+ async push(tasks) {
382
+ for (const task of tasks) {
383
+ const cloudFields = mapLocalToCloud(task);
384
+ try {
385
+ await this.request(
386
+ `/api/tasks/${encodeURIComponent(task.id)}?projectId=${encodeURIComponent(this.projectId)}`,
387
+ { method: "PATCH", body: JSON.stringify(cloudFields) }
388
+ );
389
+ } catch {
390
+ try {
391
+ await this.request("/api/tasks", {
392
+ method: "POST",
393
+ body: JSON.stringify({
394
+ ...cloudFields,
395
+ project_id: this.projectId
396
+ })
397
+ });
398
+ } catch {
399
+ }
400
+ }
401
+ }
402
+ }
403
+ };
404
+
49
405
  // src/services/task.service.ts
50
406
  var TASKS_FILE = ".jai1/tasks.jsonl";
51
407
  var SYNC_BRANCH = "jai1";
52
408
  var TaskService = class {
53
409
  tasksPath;
410
+ cwd;
411
+ _cloudProvider = null;
412
+ _providerReady;
54
413
  constructor(cwd) {
55
- this.tasksPath = join(cwd || process.cwd(), TASKS_FILE);
414
+ this.cwd = cwd || process.cwd();
415
+ this.tasksPath = join2(this.cwd, TASKS_FILE);
416
+ this._providerReady = this._initCloudProvider();
417
+ }
418
+ /**
419
+ * Initialise cloud provider if settings.yaml has tasks.cloud = true.
420
+ * Auto-registers the project on the server on first use.
421
+ */
422
+ async _initCloudProvider() {
423
+ try {
424
+ const settings = new SettingsService(this.cwd);
425
+ if (!settings.isTaskCloudEnabled()) return;
426
+ const config = await new ConfigService().load();
427
+ if (!config?.apiUrl || !config?.accessKey) return;
428
+ const repoUrl = settings.resolveGitRepoUrl();
429
+ if (!repoUrl) return;
430
+ let projectId = settings.getProjectId();
431
+ if (!projectId) return;
432
+ const provider = new CloudTaskProvider(config, projectId);
433
+ try {
434
+ const registeredId = await provider.ensureProjectRegistered(repoUrl);
435
+ if (registeredId && registeredId !== projectId) {
436
+ projectId = registeredId;
437
+ await settings.set("tasks.projectId", registeredId);
438
+ this._cloudProvider = new CloudTaskProvider(config, registeredId);
439
+ return;
440
+ }
441
+ } catch {
442
+ }
443
+ this._cloudProvider = provider;
444
+ } catch {
445
+ }
446
+ }
447
+ /**
448
+ * Wait for provider initialisation (call before any cloud operation)
449
+ */
450
+ async waitForInit() {
451
+ await this._providerReady;
452
+ }
453
+ /** @internal alias kept for backward compat */
454
+ async ready() {
455
+ await this._providerReady;
456
+ }
457
+ /**
458
+ * Returns true when cloud mode is active
459
+ */
460
+ get isCloud() {
461
+ return this._cloudProvider !== null;
462
+ }
463
+ /**
464
+ * Returns cloud provider if active
465
+ */
466
+ get cloud() {
467
+ return this._cloudProvider;
56
468
  }
57
469
  /**
58
470
  * Check if .jai1 directory exists in CWD.
59
471
  * If not, print a helpful message and exit.
60
472
  */
61
473
  static ensureJai1Dir(cwd) {
62
- const dir = join(cwd || process.cwd(), ".jai1");
474
+ const dir = join2(cwd || process.cwd(), ".jai1");
63
475
  if (!existsSync(dir)) {
64
476
  console.error(chalk.red("\u274C Th\u01B0 m\u1EE5c .jai1 kh\xF4ng t\u1ED3n t\u1EA1i trong project n\xE0y."));
65
477
  console.error("");
@@ -76,12 +488,16 @@ var TaskService = class {
76
488
  // READ
77
489
  // ============================================
78
490
  /**
79
- * Read all tasks from JSONL file
491
+ * Read all tasks from JSONL file (or cloud API if cloud mode)
80
492
  */
81
493
  async readAll() {
494
+ await this.ready();
495
+ if (this._cloudProvider) {
496
+ return this._cloudProvider.readAll();
497
+ }
82
498
  try {
83
499
  await this.ensureTasksFileNotDirectory();
84
- const content = await fs.readFile(this.tasksPath, "utf-8");
500
+ const content = await fs2.readFile(this.tasksPath, "utf-8");
85
501
  const lines = content.trim().split("\n").filter(Boolean);
86
502
  return lines.map((line) => TaskSchema.parse(JSON.parse(line)));
87
503
  } catch (error) {
@@ -95,6 +511,10 @@ var TaskService = class {
95
511
  * Find task by ID
96
512
  */
97
513
  async findById(id) {
514
+ await this.ready();
515
+ if (this._cloudProvider) {
516
+ return this._cloudProvider.getById(id);
517
+ }
98
518
  const tasks = await this.readAll();
99
519
  return tasks.find((t) => t.id === id) || null;
100
520
  }
@@ -102,6 +522,17 @@ var TaskService = class {
102
522
  * Filter tasks by criteria
103
523
  */
104
524
  async filter(criteria) {
525
+ await this.ready();
526
+ if (this._cloudProvider) {
527
+ const listInput = {};
528
+ if (criteria.status) listInput.status = criteria.status;
529
+ if (criteria.parent) listInput.parent = criteria.parent;
530
+ const tasks2 = await this._cloudProvider.list(listInput);
531
+ if (criteria.assignee) {
532
+ return tasks2.filter((t) => t.assigned_to === criteria.assignee);
533
+ }
534
+ return tasks2;
535
+ }
105
536
  const tasks = await this.readAll();
106
537
  return tasks.filter((t) => {
107
538
  if (criteria.status && t.status !== criteria.status) return false;
@@ -115,6 +546,12 @@ var TaskService = class {
115
546
  * status=todo, all depends_on done or cancelled, assigned_to empty
116
547
  */
117
548
  async getReady(parent) {
549
+ await this.ready();
550
+ if (this._cloudProvider) {
551
+ const ready = await this._cloudProvider.getReady();
552
+ if (parent) return ready.filter((t) => t.parent === parent);
553
+ return ready;
554
+ }
118
555
  const tasks = await this.readAll();
119
556
  const resolvedIds = new Set(
120
557
  tasks.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
@@ -147,6 +584,11 @@ var TaskService = class {
147
584
  * Get stats by status
148
585
  */
149
586
  async getStats() {
587
+ await this.ready();
588
+ if (this._cloudProvider) {
589
+ const stats = await this._cloudProvider.getStats();
590
+ return { ...stats, blocked: 0 };
591
+ }
150
592
  const tasks = await this.readAll();
151
593
  const resolvedIds = new Set(
152
594
  tasks.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
@@ -176,6 +618,12 @@ var TaskService = class {
176
618
  * todo = otherwise (blocked or waiting)
177
619
  */
178
620
  async getParents(statusFilter) {
621
+ await this.ready();
622
+ if (this._cloudProvider) {
623
+ const parents2 = await this._cloudProvider.getParents();
624
+ if (statusFilter) return parents2.filter((p) => p.status === statusFilter);
625
+ return parents2;
626
+ }
179
627
  const tasks = await this.readAll();
180
628
  const resolvedIds = new Set(
181
629
  tasks.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
@@ -252,10 +700,24 @@ var TaskService = class {
252
700
  * Add a new task
253
701
  */
254
702
  async add(data) {
703
+ await this.ready();
704
+ if (this._cloudProvider) {
705
+ const input = {
706
+ title: data.title,
707
+ ...data.type !== void 0 && { type: data.type },
708
+ ...data.parent !== void 0 && { parent: data.parent },
709
+ ...data.priority !== void 0 && { priority: data.priority },
710
+ ...data.tags !== void 0 && { tags: data.tags },
711
+ ...data.notes !== void 0 && { notes: data.notes },
712
+ ...data.branch !== void 0 && { branch: data.branch }
713
+ };
714
+ return this._cloudProvider.add(input);
715
+ }
255
716
  const id = await this.nextId();
256
717
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
257
718
  const task = TaskSchema.parse({
258
719
  id,
720
+ type: data.type || "task",
259
721
  parent: data.parent || "",
260
722
  title: data.title,
261
723
  status: "todo",
@@ -266,8 +728,8 @@ var TaskService = class {
266
728
  created: now,
267
729
  updated: now,
268
730
  tags: data.tags || [],
269
- branch: "",
270
- notes: ""
731
+ branch: data.branch || "",
732
+ notes: data.notes || ""
271
733
  });
272
734
  await this.appendTask(task);
273
735
  return task;
@@ -276,6 +738,10 @@ var TaskService = class {
276
738
  * Update a task by ID
277
739
  */
278
740
  async update(id, changes) {
741
+ await this.ready();
742
+ if (this._cloudProvider) {
743
+ return this._cloudProvider.update(id, changes);
744
+ }
279
745
  const tasks = await this.readAll();
280
746
  const index = tasks.findIndex((t) => t.id === id);
281
747
  if (index === -1) {
@@ -291,6 +757,7 @@ var TaskService = class {
291
757
  * Add dependency: child depends on parent
292
758
  */
293
759
  async addDependency(childId, parentId) {
760
+ await this.ready();
294
761
  const tasks = await this.readAll();
295
762
  const child = tasks.find((t) => t.id === childId);
296
763
  const parent = tasks.find((t) => t.id === parentId);
@@ -311,6 +778,10 @@ var TaskService = class {
311
778
  * Mark task as done
312
779
  */
313
780
  async markDone(id) {
781
+ await this.ready();
782
+ if (this._cloudProvider) {
783
+ return this._cloudProvider.markDone(id);
784
+ }
314
785
  return this.update(id, { status: "done" });
315
786
  }
316
787
  /**
@@ -324,6 +795,10 @@ var TaskService = class {
324
795
  * Cancel a task: set status=cancelled, clear assignment
325
796
  */
326
797
  async cancel(id) {
798
+ await this.ready();
799
+ if (this._cloudProvider) {
800
+ return this._cloudProvider.cancel(id);
801
+ }
327
802
  return this.update(id, {
328
803
  status: "cancelled",
329
804
  assigned_to: "",
@@ -334,6 +809,10 @@ var TaskService = class {
334
809
  * Delete a task completely and clean up depends_on references
335
810
  */
336
811
  async deleteTask(id) {
812
+ await this.ready();
813
+ if (this._cloudProvider) {
814
+ return this._cloudProvider.deleteTask(id);
815
+ }
337
816
  return this.deleteTasks([id]);
338
817
  }
339
818
  /**
@@ -341,6 +820,13 @@ var TaskService = class {
341
820
  * Validates all IDs exist before deleting any.
342
821
  */
343
822
  async deleteTasks(ids) {
823
+ await this.ready();
824
+ if (this._cloudProvider) {
825
+ for (const id of ids) {
826
+ await this._cloudProvider.deleteTask(id);
827
+ }
828
+ return;
829
+ }
344
830
  const tasks = await this.readAll();
345
831
  const deleteSet = new Set(ids);
346
832
  const notFound = ids.filter((id) => !tasks.find((t) => t.id === id));
@@ -363,6 +849,15 @@ var TaskService = class {
363
849
  * Returns the list of deleted tasks for display purposes.
364
850
  */
365
851
  async deleteGroup(parentName) {
852
+ await this.ready();
853
+ if (this._cloudProvider) {
854
+ const groupTasks2 = await this._cloudProvider.list({ parent: parentName });
855
+ if (groupTasks2.length === 0) {
856
+ throw new Error(`No tasks found in group: ${parentName}`);
857
+ }
858
+ await this._cloudProvider.deleteGroup(parentName);
859
+ return groupTasks2;
860
+ }
366
861
  const tasks = await this.readAll();
367
862
  const groupTasks = tasks.filter((t) => t.parent === parentName);
368
863
  if (groupTasks.length === 0) {
@@ -376,6 +871,10 @@ var TaskService = class {
376
871
  * Pick next task: claim for current user
377
872
  */
378
873
  async pick(taskId) {
874
+ await this.ready();
875
+ if (this._cloudProvider) {
876
+ return this._cloudProvider.pick(taskId, this.getCurrentUser());
877
+ }
379
878
  const username = this.getCurrentUser();
380
879
  const now = (/* @__PURE__ */ new Date()).toISOString();
381
880
  return this.update(taskId, {
@@ -469,9 +968,9 @@ var TaskService = class {
469
968
  * - Dirty working tree → stash/unstash automatically
470
969
  */
471
970
  async syncPush() {
472
- const cwd = process.cwd();
971
+ const cwd = this.cwd;
473
972
  const branch = SYNC_BRANCH;
474
- const tasksFullPath = join(cwd, TASKS_FILE);
973
+ const tasksFullPath = join2(cwd, TASKS_FILE);
475
974
  if (!existsSync(tasksFullPath)) {
476
975
  return;
477
976
  }
@@ -535,7 +1034,7 @@ var TaskService = class {
535
1034
  * - tasks.jsonl doesn't exist on remote branch → returns empty
536
1035
  */
537
1036
  async syncPull() {
538
- const cwd = process.cwd();
1037
+ const cwd = this.cwd;
539
1038
  const branch = SYNC_BRANCH;
540
1039
  let merged = 0;
541
1040
  let conflicts = 0;
@@ -581,16 +1080,16 @@ var TaskService = class {
581
1080
  async appendTask(task) {
582
1081
  await this.ensureTasksFileNotDirectory();
583
1082
  const dir = dirname(this.tasksPath);
584
- await fs.mkdir(dir, { recursive: true });
1083
+ await fs2.mkdir(dir, { recursive: true });
585
1084
  const line = JSON.stringify(task) + "\n";
586
- await fs.appendFile(this.tasksPath, line, "utf-8");
1085
+ await fs2.appendFile(this.tasksPath, line, "utf-8");
587
1086
  }
588
1087
  async writeAll(tasks) {
589
1088
  await this.ensureTasksFileNotDirectory();
590
1089
  const dir = dirname(this.tasksPath);
591
- await fs.mkdir(dir, { recursive: true });
1090
+ await fs2.mkdir(dir, { recursive: true });
592
1091
  const content = tasks.map((t) => JSON.stringify(t)).join("\n") + "\n";
593
- await fs.writeFile(this.tasksPath, content, "utf-8");
1092
+ await fs2.writeFile(this.tasksPath, content, "utf-8");
594
1093
  }
595
1094
  getCurrentUser() {
596
1095
  try {
@@ -618,7 +1117,7 @@ var TaskService = class {
618
1117
  }
619
1118
  async ensureTasksFileNotDirectory() {
620
1119
  try {
621
- const stat = await fs.stat(this.tasksPath);
1120
+ const stat = await fs2.stat(this.tasksPath);
622
1121
  if (stat.isDirectory()) {
623
1122
  throw new Error(
624
1123
  `Invalid tasks path: expected file at ${TASKS_FILE} but found a directory. Please remove the directory and re-run the command.`
@@ -685,6 +1184,7 @@ function createTaskSummaryCommand() {
685
1184
  }
686
1185
 
687
1186
  export {
1187
+ ConfigService,
688
1188
  STATUS_ICONS,
689
1189
  BLOCKED_ICON,
690
1190
  PRIORITY_ICONS,
@@ -693,4 +1193,4 @@ export {
693
1193
  handleTaskSummary,
694
1194
  createTaskSummaryCommand
695
1195
  };
696
- //# sourceMappingURL=chunk-FZBVI5AX.js.map
1196
+ //# sourceMappingURL=chunk-SDYQQ4ZY.js.map