@jvittechs/j 1.0.58 → 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,6 +1,6 @@
1
1
  import {
2
2
  SettingsService
3
- } from "./chunk-Z464RBPB.js";
3
+ } from "./chunk-BMDRQFY7.js";
4
4
 
5
5
  // src/commands/settings/show.ts
6
6
  import { Command } from "commander";
@@ -40,4 +40,4 @@ function createSettingsShowCommand() {
40
40
  export {
41
41
  createSettingsShowCommand
42
42
  };
43
- //# sourceMappingURL=chunk-DVBQLA4R.js.map
43
+ //# sourceMappingURL=chunk-3MKBSVAL.js.map
@@ -1,9 +1,5 @@
1
- import {
2
- __require
3
- } from "./chunk-DGUM43GV.js";
4
-
5
1
  // src/services/settings.service.ts
6
- import { promises as fs, existsSync } from "fs";
2
+ import { promises as fs, existsSync, readFileSync } from "fs";
7
3
  import { join } from "path";
8
4
  import { execSync } from "child_process";
9
5
  import { createHash } from "crypto";
@@ -58,7 +54,7 @@ var SettingsService = class {
58
54
  return this.cache;
59
55
  }
60
56
  try {
61
- const content = __require("fs").readFileSync(this.settingsPath, "utf-8");
57
+ const content = readFileSync(this.settingsPath, "utf-8");
62
58
  const raw = YAML.parse(content) || {};
63
59
  this.cache = ProjectSettingsSchema.parse(raw);
64
60
  return this.cache;
@@ -152,8 +148,8 @@ var SettingsService = class {
152
148
  return this.load().tasks;
153
149
  }
154
150
  /**
155
- * Get or derive projectId from git remote URL
156
- * Caches in settings.yaml after first derive
151
+ * Get or derive projectId from git remote URL.
152
+ * Synchronously derives if not cached; use getOrCreateProjectId() to also persist.
157
153
  */
158
154
  getProjectId() {
159
155
  const settings = this.load();
@@ -163,10 +159,14 @@ var SettingsService = class {
163
159
  const repoUrl = this.resolveGitRepoUrl();
164
160
  if (!repoUrl) return null;
165
161
  const projectId = this.deriveProjectId(repoUrl);
162
+ settings.tasks.projectId = projectId;
163
+ this.cache = settings;
164
+ this.save(settings).catch(() => {
165
+ });
166
166
  return projectId;
167
167
  }
168
168
  /**
169
- * Get and cache projectId (writes to settings.yaml)
169
+ * Get and cache projectId (writes to settings.yaml, awaitable)
170
170
  */
171
171
  async getOrCreateProjectId() {
172
172
  const settings = this.load();
@@ -244,4 +244,4 @@ var SettingsService = class {
244
244
  export {
245
245
  SettingsService
246
246
  };
247
- //# sourceMappingURL=chunk-Z464RBPB.js.map
247
+ //# sourceMappingURL=chunk-BMDRQFY7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/settings.service.ts","../src/types/settings.types.ts"],"sourcesContent":["/**\n * Settings Service\n * Read/write .jai1/settings.yaml\n */\nimport { promises as fs, existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { execSync } from 'child_process';\nimport { createHash } from 'crypto';\nimport YAML from 'yaml';\nimport {\n ProjectSettingsSchema,\n type ProjectSettings,\n type TaskSettings,\n DEFAULT_SETTINGS,\n} from '../types/settings.types.js';\n\nconst SETTINGS_FILE = '.jai1/settings.yaml';\n\nexport class SettingsService {\n private readonly settingsPath: string;\n private readonly cwd: string;\n private cache: ProjectSettings | null = null;\n\n constructor(cwd?: string) {\n this.cwd = cwd || process.cwd();\n this.settingsPath = join(this.cwd, SETTINGS_FILE);\n }\n\n // ============================================\n // READ\n // ============================================\n\n /**\n * Check if settings file exists\n */\n exists(): boolean {\n return existsSync(this.settingsPath);\n }\n\n /**\n * Load and validate settings from YAML file\n * Returns defaults if file doesn't exist\n */\n load(): ProjectSettings {\n if (this.cache) return this.cache;\n\n if (!this.exists()) {\n this.cache = { ...DEFAULT_SETTINGS };\n return this.cache;\n }\n\n try {\n const content = readFileSync(this.settingsPath, 'utf-8');\n const raw = YAML.parse(content) || {};\n this.cache = ProjectSettingsSchema.parse(raw);\n return this.cache;\n } catch (error) {\n throw new Error(\n `Failed to load settings: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n /**\n * Get a setting value by dot-notation key\n * e.g., get('tasks.cloud') → true\n */\n get(key: string): unknown {\n const settings = this.load();\n const parts = key.split('.');\n let current: unknown = settings;\n\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== 'object') {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n\n return current;\n }\n\n // ============================================\n // WRITE\n // ============================================\n\n /**\n * Save settings to YAML file\n * Creates .jai1 directory if it doesn't exist\n */\n async save(settings: ProjectSettings): Promise<void> {\n const dir = join(this.cwd, '.jai1');\n await fs.mkdir(dir, { recursive: true });\n\n const content = YAML.stringify(settings, {\n indent: 2,\n lineWidth: 0,\n });\n\n await fs.writeFile(this.settingsPath, content, 'utf-8');\n this.cache = settings;\n }\n\n /**\n * Set a single setting value by dot-notation key\n * e.g., set('tasks.cloud', true)\n */\n async set(key: string, value: unknown): Promise<ProjectSettings> {\n const settings = this.load();\n const parts = key.split('.');\n let current: Record<string, unknown> = settings as unknown as Record<string, unknown>;\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!;\n if (typeof current[part] !== 'object' || current[part] === null) {\n current[part] = {};\n }\n current = current[part] as Record<string, unknown>;\n }\n\n const lastKey = parts[parts.length - 1]!;\n\n // Auto-convert string values to proper types\n if (value === 'true') value = true;\n else if (value === 'false') value = false;\n else if (typeof value === 'string' && /^\\d+$/.test(value)) value = parseInt(value, 10);\n\n current[lastKey] = value;\n\n // Validate the entire settings object\n const validated = ProjectSettingsSchema.parse(settings);\n await this.save(validated);\n return validated;\n }\n\n /**\n * Initialize settings file with defaults\n */\n async init(force: boolean = false): Promise<ProjectSettings> {\n if (this.exists() && !force) {\n throw new Error('Settings file already exists. Use --force to overwrite.');\n }\n\n await this.save(DEFAULT_SETTINGS);\n return DEFAULT_SETTINGS;\n }\n\n // ============================================\n // SHORTCUTS\n // ============================================\n\n /**\n * Check if task cloud mode is enabled\n */\n isTaskCloudEnabled(): boolean {\n const settings = this.load();\n return settings.tasks.cloud === true;\n }\n\n /**\n * Get task settings\n */\n getTaskSettings(): TaskSettings {\n return this.load().tasks;\n }\n\n /**\n * Get or derive projectId from git remote URL.\n * Synchronously derives if not cached; use getOrCreateProjectId() to also persist.\n */\n getProjectId(): string | null {\n const settings = this.load();\n if (settings.tasks.projectId) {\n return settings.tasks.projectId;\n }\n\n // Try to derive from git remote\n const repoUrl = this.resolveGitRepoUrl();\n if (!repoUrl) return null;\n\n const projectId = this.deriveProjectId(repoUrl);\n\n // Cache in-memory so subsequent calls don't re-derive\n settings.tasks.projectId = projectId;\n this.cache = settings;\n\n // Fire-and-forget persist to settings.yaml (don't block sync callers)\n this.save(settings).catch(() => {});\n return projectId;\n }\n\n /**\n * Get and cache projectId (writes to settings.yaml, awaitable)\n */\n async getOrCreateProjectId(): Promise<string | null> {\n const settings = this.load();\n if (settings.tasks.projectId) {\n return settings.tasks.projectId;\n }\n\n const repoUrl = this.resolveGitRepoUrl();\n if (!repoUrl) return null;\n\n const projectId = this.deriveProjectId(repoUrl);\n await this.set('tasks.projectId', projectId);\n return projectId;\n }\n\n // ============================================\n // GIT HELPERS\n // ============================================\n\n /**\n * Resolve git remote URL from current repo\n * Returns normalized URL (strip .git, credentials)\n */\n resolveGitRepoUrl(): string | null {\n try {\n const raw = execSync('git remote get-url origin', {\n cwd: this.cwd,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n return this.normalizeRepoUrl(raw);\n } catch {\n return null;\n }\n }\n\n /**\n * Normalize git repo URL to consistent format\n * git@github.com:org/repo.git → github.com/org/repo\n * https://github.com/org/repo.git → github.com/org/repo\n */\n private normalizeRepoUrl(url: string): string {\n let normalized = url;\n\n // SSH format: git@github.com:org/repo.git\n const sshMatch = normalized.match(/^(?:git@|ssh:\\/\\/(?:[^@]+@)?)([^:\\/]+)[:/](.+?)(?:\\.git)?$/);\n if (sshMatch) {\n return `${sshMatch[1]}/${sshMatch[2]}`;\n }\n\n // HTTPS format: https://github.com/org/repo.git\n const httpsMatch = normalized.match(/^https?:\\/\\/(?:[^@]+@)?([^/]+)\\/(.+?)(?:\\.git)?$/);\n if (httpsMatch) {\n return `${httpsMatch[1]}/${httpsMatch[2]}`;\n }\n\n return normalized;\n }\n\n /**\n * Derive projectId from normalized repo URL\n * → SHA256 hash (12 chars) + slug\n */\n private deriveProjectId(repoUrl: string): string {\n const hash = createHash('sha256').update(repoUrl).digest('hex').substring(0, 12);\n const slug = repoUrl\n .replace(/[^a-zA-Z0-9]+/g, '-')\n .replace(/^-|-$/g, '')\n .toLowerCase();\n return `${hash}-${slug}`;\n }\n\n // ============================================\n // UTILITY\n // ============================================\n\n /**\n * Get settings file path\n */\n getSettingsPath(): string {\n return this.settingsPath;\n }\n\n /**\n * Clear cache (for testing or after external updates)\n */\n clearCache(): void {\n this.cache = null;\n }\n}\n","/**\n * Project Settings Types\n * Schema for .jai1/settings.yaml\n */\nimport { z } from 'zod';\n\n// ============================================\n// SETTINGS SCHEMA\n// ============================================\n\nexport const TaskSettingsSchema = z.object({\n cloud: z.boolean().default(false),\n projectId: z.string().optional(), // auto-derived from git remote URL\n});\n\nexport type TaskSettings = z.infer<typeof TaskSettingsSchema>;\n\nexport const ProjectSettingsSchema = z.object({\n tasks: TaskSettingsSchema.default({}),\n // Future extensions:\n // notifications: z.object({...}).default({}),\n // integrations: z.object({...}).default({}),\n});\n\nexport type ProjectSettings = z.infer<typeof ProjectSettingsSchema>;\n\n// ============================================\n// DEFAULT VALUES\n// ============================================\n\nexport const DEFAULT_SETTINGS: ProjectSettings = {\n tasks: {\n cloud: false,\n },\n};\n\n// ============================================\n// CLI OPTION TYPES\n// ============================================\n\nexport interface SettingsSetOptions {\n json?: boolean;\n}\n\nexport interface SettingsGetOptions {\n json?: boolean;\n}\n\nexport interface SettingsShowOptions {\n json?: boolean;\n}\n\nexport interface SettingsInitOptions {\n force?: boolean;\n json?: boolean;\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SettingsService
3
- } from "./chunk-Z464RBPB.js";
3
+ } from "./chunk-BMDRQFY7.js";
4
4
 
5
5
  // src/commands/tasks/summary.ts
6
6
  import { Command } from "commander";
@@ -122,6 +122,51 @@ var ConfigService = class {
122
122
  };
123
123
 
124
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
+ }
125
170
  var CloudTaskProvider = class {
126
171
  apiUrl;
127
172
  accessKey;
@@ -176,16 +221,19 @@ var CloudTaskProvider = class {
176
221
  // CRUD
177
222
  // ============================================
178
223
  async readAll() {
179
- return this.request(`/api/tasks?projectId=${encodeURIComponent(this.projectId)}`);
224
+ const rows = await this.request(
225
+ `/api/tasks?projectId=${encodeURIComponent(this.projectId)}`
226
+ );
227
+ return rows.map(mapCloudToLocal);
180
228
  }
181
229
  async add(input) {
182
230
  const all = await this.readAll().catch(() => []);
183
231
  const maxNum = all.reduce((max, t) => {
184
232
  const n = parseInt(t.id.replace("T-", ""), 10);
185
- return n > max ? n : max;
233
+ return isNaN(n) ? max : Math.max(max, n);
186
234
  }, 0);
187
235
  const taskId = `T-${String(maxNum + 1).padStart(3, "0")}`;
188
- return this.request("/api/tasks", {
236
+ const row = await this.request("/api/tasks", {
189
237
  method: "POST",
190
238
  body: JSON.stringify({
191
239
  task_id: taskId,
@@ -200,15 +248,17 @@ var CloudTaskProvider = class {
200
248
  notes: input.notes || ""
201
249
  })
202
250
  });
251
+ return mapCloudToLocal(row);
203
252
  }
204
253
  async update(id, updates) {
205
- return this.request(
254
+ const row = await this.request(
206
255
  `/api/tasks/${encodeURIComponent(id)}?projectId=${encodeURIComponent(this.projectId)}`,
207
256
  {
208
257
  method: "PATCH",
209
258
  body: JSON.stringify(updates)
210
259
  }
211
260
  );
261
+ return mapCloudToLocal(row);
212
262
  }
213
263
  async deleteTask(id) {
214
264
  await this.request(
@@ -230,13 +280,15 @@ var CloudTaskProvider = class {
230
280
  if (options?.status) params.set("status", options.status);
231
281
  if (options?.type) params.set("type", options.type);
232
282
  if (options?.parent !== void 0) params.set("parent", options.parent);
233
- return this.request(`/api/tasks?${params}`);
283
+ const rows = await this.request(`/api/tasks?${params}`);
284
+ return rows.map(mapCloudToLocal);
234
285
  }
235
286
  async getById(id) {
236
287
  try {
237
- return await this.request(
288
+ const row = await this.request(
238
289
  `/api/tasks/${encodeURIComponent(id)}?projectId=${encodeURIComponent(this.projectId)}`
239
290
  );
291
+ return mapCloudToLocal(row);
240
292
  } catch {
241
293
  return null;
242
294
  }
@@ -284,22 +336,33 @@ var CloudTaskProvider = class {
284
336
  // WORKFLOW
285
337
  // ============================================
286
338
  async getReady() {
287
- return this.request(`/api/tasks/ready?projectId=${encodeURIComponent(this.projectId)}`);
339
+ const rows = await this.request(
340
+ `/api/tasks/ready?projectId=${encodeURIComponent(this.projectId)}`
341
+ );
342
+ return rows.map(mapCloudToLocal);
288
343
  }
289
344
  async pick(id, agentId) {
290
- return this.request(`/api/tasks/${encodeURIComponent(id)}/pick`, {
291
- method: "POST",
292
- body: JSON.stringify({
293
- project_id: this.projectId,
294
- agent_id: agentId || "cli"
295
- })
296
- });
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);
297
356
  }
298
357
  async markDone(id) {
299
- return this.request(`/api/tasks/${encodeURIComponent(id)}/done`, {
300
- method: "POST",
301
- body: JSON.stringify({ project_id: this.projectId })
302
- });
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);
303
366
  }
304
367
  async cancel(id) {
305
368
  await this.request(`/api/tasks/${encodeURIComponent(id)}/cancel`, {
@@ -317,19 +380,19 @@ var CloudTaskProvider = class {
317
380
  }
318
381
  async push(tasks) {
319
382
  for (const task of tasks) {
383
+ const cloudFields = mapLocalToCloud(task);
320
384
  try {
321
385
  await this.request(
322
386
  `/api/tasks/${encodeURIComponent(task.id)}?projectId=${encodeURIComponent(this.projectId)}`,
323
- { method: "PATCH", body: JSON.stringify(task) }
387
+ { method: "PATCH", body: JSON.stringify(cloudFields) }
324
388
  );
325
389
  } catch {
326
390
  try {
327
391
  await this.request("/api/tasks", {
328
392
  method: "POST",
329
393
  body: JSON.stringify({
330
- task_id: task.id,
331
- project_id: this.projectId,
332
- ...task
394
+ ...cloudFields,
395
+ project_id: this.projectId
333
396
  })
334
397
  });
335
398
  } catch {
@@ -353,23 +416,41 @@ var TaskService = class {
353
416
  this._providerReady = this._initCloudProvider();
354
417
  }
355
418
  /**
356
- * Initialise cloud provider if settings.yaml has tasks.cloud = true
419
+ * Initialise cloud provider if settings.yaml has tasks.cloud = true.
420
+ * Auto-registers the project on the server on first use.
357
421
  */
358
422
  async _initCloudProvider() {
359
423
  try {
360
424
  const settings = new SettingsService(this.cwd);
361
425
  if (!settings.isTaskCloudEnabled()) return;
362
- const projectId = settings.getProjectId();
363
- if (!projectId) return;
364
426
  const config = await new ConfigService().load();
365
427
  if (!config?.apiUrl || !config?.accessKey) return;
366
- this._cloudProvider = new CloudTaskProvider(config, projectId);
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;
367
444
  } catch {
368
445
  }
369
446
  }
370
447
  /**
371
448
  * Wait for provider initialisation (call before any cloud operation)
372
449
  */
450
+ async waitForInit() {
451
+ await this._providerReady;
452
+ }
453
+ /** @internal alias kept for backward compat */
373
454
  async ready() {
374
455
  await this._providerReady;
375
456
  }
@@ -407,9 +488,13 @@ var TaskService = class {
407
488
  // READ
408
489
  // ============================================
409
490
  /**
410
- * Read all tasks from JSONL file
491
+ * Read all tasks from JSONL file (or cloud API if cloud mode)
411
492
  */
412
493
  async readAll() {
494
+ await this.ready();
495
+ if (this._cloudProvider) {
496
+ return this._cloudProvider.readAll();
497
+ }
413
498
  try {
414
499
  await this.ensureTasksFileNotDirectory();
415
500
  const content = await fs2.readFile(this.tasksPath, "utf-8");
@@ -426,6 +511,10 @@ var TaskService = class {
426
511
  * Find task by ID
427
512
  */
428
513
  async findById(id) {
514
+ await this.ready();
515
+ if (this._cloudProvider) {
516
+ return this._cloudProvider.getById(id);
517
+ }
429
518
  const tasks = await this.readAll();
430
519
  return tasks.find((t) => t.id === id) || null;
431
520
  }
@@ -433,6 +522,17 @@ var TaskService = class {
433
522
  * Filter tasks by criteria
434
523
  */
435
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
+ }
436
536
  const tasks = await this.readAll();
437
537
  return tasks.filter((t) => {
438
538
  if (criteria.status && t.status !== criteria.status) return false;
@@ -446,6 +546,12 @@ var TaskService = class {
446
546
  * status=todo, all depends_on done or cancelled, assigned_to empty
447
547
  */
448
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
+ }
449
555
  const tasks = await this.readAll();
450
556
  const resolvedIds = new Set(
451
557
  tasks.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
@@ -478,6 +584,11 @@ var TaskService = class {
478
584
  * Get stats by status
479
585
  */
480
586
  async getStats() {
587
+ await this.ready();
588
+ if (this._cloudProvider) {
589
+ const stats = await this._cloudProvider.getStats();
590
+ return { ...stats, blocked: 0 };
591
+ }
481
592
  const tasks = await this.readAll();
482
593
  const resolvedIds = new Set(
483
594
  tasks.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
@@ -507,6 +618,12 @@ var TaskService = class {
507
618
  * todo = otherwise (blocked or waiting)
508
619
  */
509
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
+ }
510
627
  const tasks = await this.readAll();
511
628
  const resolvedIds = new Set(
512
629
  tasks.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
@@ -621,6 +738,10 @@ var TaskService = class {
621
738
  * Update a task by ID
622
739
  */
623
740
  async update(id, changes) {
741
+ await this.ready();
742
+ if (this._cloudProvider) {
743
+ return this._cloudProvider.update(id, changes);
744
+ }
624
745
  const tasks = await this.readAll();
625
746
  const index = tasks.findIndex((t) => t.id === id);
626
747
  if (index === -1) {
@@ -636,6 +757,7 @@ var TaskService = class {
636
757
  * Add dependency: child depends on parent
637
758
  */
638
759
  async addDependency(childId, parentId) {
760
+ await this.ready();
639
761
  const tasks = await this.readAll();
640
762
  const child = tasks.find((t) => t.id === childId);
641
763
  const parent = tasks.find((t) => t.id === parentId);
@@ -656,6 +778,10 @@ var TaskService = class {
656
778
  * Mark task as done
657
779
  */
658
780
  async markDone(id) {
781
+ await this.ready();
782
+ if (this._cloudProvider) {
783
+ return this._cloudProvider.markDone(id);
784
+ }
659
785
  return this.update(id, { status: "done" });
660
786
  }
661
787
  /**
@@ -669,6 +795,10 @@ var TaskService = class {
669
795
  * Cancel a task: set status=cancelled, clear assignment
670
796
  */
671
797
  async cancel(id) {
798
+ await this.ready();
799
+ if (this._cloudProvider) {
800
+ return this._cloudProvider.cancel(id);
801
+ }
672
802
  return this.update(id, {
673
803
  status: "cancelled",
674
804
  assigned_to: "",
@@ -679,6 +809,10 @@ var TaskService = class {
679
809
  * Delete a task completely and clean up depends_on references
680
810
  */
681
811
  async deleteTask(id) {
812
+ await this.ready();
813
+ if (this._cloudProvider) {
814
+ return this._cloudProvider.deleteTask(id);
815
+ }
682
816
  return this.deleteTasks([id]);
683
817
  }
684
818
  /**
@@ -686,6 +820,13 @@ var TaskService = class {
686
820
  * Validates all IDs exist before deleting any.
687
821
  */
688
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
+ }
689
830
  const tasks = await this.readAll();
690
831
  const deleteSet = new Set(ids);
691
832
  const notFound = ids.filter((id) => !tasks.find((t) => t.id === id));
@@ -708,6 +849,15 @@ var TaskService = class {
708
849
  * Returns the list of deleted tasks for display purposes.
709
850
  */
710
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
+ }
711
861
  const tasks = await this.readAll();
712
862
  const groupTasks = tasks.filter((t) => t.parent === parentName);
713
863
  if (groupTasks.length === 0) {
@@ -721,6 +871,10 @@ var TaskService = class {
721
871
  * Pick next task: claim for current user
722
872
  */
723
873
  async pick(taskId) {
874
+ await this.ready();
875
+ if (this._cloudProvider) {
876
+ return this._cloudProvider.pick(taskId, this.getCurrentUser());
877
+ }
724
878
  const username = this.getCurrentUser();
725
879
  const now = (/* @__PURE__ */ new Date()).toISOString();
726
880
  return this.update(taskId, {
@@ -814,7 +968,7 @@ var TaskService = class {
814
968
  * - Dirty working tree → stash/unstash automatically
815
969
  */
816
970
  async syncPush() {
817
- const cwd = process.cwd();
971
+ const cwd = this.cwd;
818
972
  const branch = SYNC_BRANCH;
819
973
  const tasksFullPath = join2(cwd, TASKS_FILE);
820
974
  if (!existsSync(tasksFullPath)) {
@@ -880,7 +1034,7 @@ var TaskService = class {
880
1034
  * - tasks.jsonl doesn't exist on remote branch → returns empty
881
1035
  */
882
1036
  async syncPull() {
883
- const cwd = process.cwd();
1037
+ const cwd = this.cwd;
884
1038
  const branch = SYNC_BRANCH;
885
1039
  let merged = 0;
886
1040
  let conflicts = 0;
@@ -1039,4 +1193,4 @@ export {
1039
1193
  handleTaskSummary,
1040
1194
  createTaskSummaryCommand
1041
1195
  };
1042
- //# sourceMappingURL=chunk-S7QUPWSM.js.map
1196
+ //# sourceMappingURL=chunk-SDYQQ4ZY.js.map