@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.
- package/dist/{chunk-DVBQLA4R.js → chunk-3MKBSVAL.js} +2 -2
- package/dist/{chunk-Z464RBPB.js → chunk-BMDRQFY7.js} +10 -10
- package/dist/chunk-BMDRQFY7.js.map +1 -0
- package/dist/{chunk-S7QUPWSM.js → chunk-SDYQQ4ZY.js} +185 -31
- package/dist/chunk-SDYQQ4ZY.js.map +1 -0
- package/dist/cli.js +12 -12
- package/dist/cli.js.map +1 -1
- package/dist/{components.service-JUUV4CUI.js → components.service-NWAWKII3.js} +1 -2
- package/dist/show-FGCDTIU7.js +8 -0
- package/dist/summary-FIUGSB65.js +10 -0
- package/package.json +1 -1
- package/dist/chunk-DGUM43GV.js +0 -11
- package/dist/chunk-S7QUPWSM.js.map +0 -1
- package/dist/chunk-Z464RBPB.js.map +0 -1
- package/dist/show-AJ5M3SKQ.js +0 -9
- package/dist/summary-R4WPFJ2U.js +0 -11
- package/dist/summary-R4WPFJ2U.js.map +0 -1
- /package/dist/{chunk-DVBQLA4R.js.map → chunk-3MKBSVAL.js.map} +0 -0
- /package/dist/{chunk-DGUM43GV.js.map → components.service-NWAWKII3.js.map} +0 -0
- /package/dist/{components.service-JUUV4CUI.js.map → show-FGCDTIU7.js.map} +0 -0
- /package/dist/{show-AJ5M3SKQ.js.map → summary-FIUGSB65.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SettingsService
|
|
3
|
-
} from "./chunk-
|
|
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-
|
|
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 =
|
|
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
|
-
*
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
|
233
|
+
return isNaN(n) ? max : Math.max(max, n);
|
|
186
234
|
}, 0);
|
|
187
235
|
const taskId = `T-${String(maxNum + 1).padStart(3, "0")}`;
|
|
188
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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-
|
|
1196
|
+
//# sourceMappingURL=chunk-SDYQQ4ZY.js.map
|