@jvittechs/j 1.0.57 → 1.0.58
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-DGUM43GV.js +11 -0
- package/dist/chunk-DVBQLA4R.js +43 -0
- package/dist/chunk-DVBQLA4R.js.map +1 -0
- package/dist/{chunk-FZBVI5AX.js → chunk-S7QUPWSM.js} +360 -14
- package/dist/chunk-S7QUPWSM.js.map +1 -0
- package/dist/chunk-Z464RBPB.js +247 -0
- package/dist/chunk-Z464RBPB.js.map +1 -0
- package/dist/cli.js +882 -777
- package/dist/cli.js.map +1 -1
- package/dist/{components.service-NWAWKII3.js → components.service-JUUV4CUI.js} +2 -1
- package/dist/show-AJ5M3SKQ.js +9 -0
- package/dist/show-AJ5M3SKQ.js.map +1 -0
- package/dist/summary-R4WPFJ2U.js +11 -0
- package/dist/summary-R4WPFJ2U.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-FZBVI5AX.js.map +0 -1
- package/dist/summary-4J2OCCSA.js +0 -9
- /package/dist/{components.service-NWAWKII3.js.map → chunk-DGUM43GV.js.map} +0 -0
- /package/dist/{summary-4J2OCCSA.js.map → components.service-JUUV4CUI.js.map} +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
__require
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=chunk-DGUM43GV.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SettingsService
|
|
3
|
+
} from "./chunk-Z464RBPB.js";
|
|
4
|
+
|
|
5
|
+
// src/commands/settings/show.ts
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import YAML from "yaml";
|
|
9
|
+
function createSettingsShowCommand() {
|
|
10
|
+
return new Command("show").description("Hi\u1EC3n th\u1ECB all settings").option("-j, --json", "Output JSON").action(async (options) => {
|
|
11
|
+
const service = new SettingsService();
|
|
12
|
+
const settings = service.load();
|
|
13
|
+
if (options.json) {
|
|
14
|
+
console.log(JSON.stringify(settings, null, 2));
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (!service.exists()) {
|
|
18
|
+
console.log(chalk.dim("No settings file found. Run `j settings init` to create one."));
|
|
19
|
+
console.log("");
|
|
20
|
+
console.log(chalk.dim("Using defaults:"));
|
|
21
|
+
} else {
|
|
22
|
+
console.log(chalk.bold("\u{1F4CB} Project Settings"));
|
|
23
|
+
console.log(chalk.dim(` Path: ${service.getSettingsPath()}`));
|
|
24
|
+
console.log("");
|
|
25
|
+
}
|
|
26
|
+
console.log(YAML.stringify(settings, { indent: 2, lineWidth: 0 }).trim());
|
|
27
|
+
const repoUrl = service.resolveGitRepoUrl();
|
|
28
|
+
if (repoUrl) {
|
|
29
|
+
console.log("");
|
|
30
|
+
console.log(chalk.dim("Derived:"));
|
|
31
|
+
console.log(` ${chalk.dim("repo_url:")} ${repoUrl}`);
|
|
32
|
+
const projectId = service.getProjectId();
|
|
33
|
+
if (projectId) {
|
|
34
|
+
console.log(` ${chalk.dim("project_id:")} ${projectId}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
createSettingsShowCommand
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=chunk-DVBQLA4R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/settings/show.ts"],"sourcesContent":["/**\n * jai1 settings show [-j]\n */\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport YAML from 'yaml';\nimport { SettingsService } from '../../services/settings.service.js';\nimport type { SettingsShowOptions } from '../../types/settings.types.js';\n\nexport function createSettingsShowCommand(): Command {\n return new Command('show')\n .description('Hiển thị all settings')\n .option('-j, --json', 'Output JSON')\n .action(async (options: SettingsShowOptions) => {\n const service = new SettingsService();\n const settings = service.load();\n\n if (options.json) {\n console.log(JSON.stringify(settings, null, 2));\n return;\n }\n\n if (!service.exists()) {\n console.log(chalk.dim('No settings file found. Run `j settings init` to create one.'));\n console.log('');\n console.log(chalk.dim('Using defaults:'));\n } else {\n console.log(chalk.bold('📋 Project Settings'));\n console.log(chalk.dim(` Path: ${service.getSettingsPath()}`));\n console.log('');\n }\n\n console.log(YAML.stringify(settings, { indent: 2, lineWidth: 0 }).trim());\n\n // Show derived info\n const repoUrl = service.resolveGitRepoUrl();\n if (repoUrl) {\n console.log('');\n console.log(chalk.dim('Derived:'));\n console.log(` ${chalk.dim('repo_url:')} ${repoUrl}`);\n const projectId = service.getProjectId();\n if (projectId) {\n console.log(` ${chalk.dim('project_id:')} ${projectId}`);\n }\n }\n });\n}\n"],"mappings":";;;;;AAGA,SAAS,eAAe;AACxB,OAAO,WAAW;AAClB,OAAO,UAAU;AAIV,SAAS,4BAAqC;AACjD,SAAO,IAAI,QAAQ,MAAM,EACpB,YAAY,iCAAuB,EACnC,OAAO,cAAc,aAAa,EAClC,OAAO,OAAO,YAAiC;AAC5C,UAAM,UAAU,IAAI,gBAAgB;AACpC,UAAM,WAAW,QAAQ,KAAK;AAE9B,QAAI,QAAQ,MAAM;AACd,cAAQ,IAAI,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAC7C;AAAA,IACJ;AAEA,QAAI,CAAC,QAAQ,OAAO,GAAG;AACnB,cAAQ,IAAI,MAAM,IAAI,8DAA8D,CAAC;AACrF,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,MAAM,IAAI,iBAAiB,CAAC;AAAA,IAC5C,OAAO;AACH,cAAQ,IAAI,MAAM,KAAK,4BAAqB,CAAC;AAC7C,cAAQ,IAAI,MAAM,IAAI,YAAY,QAAQ,gBAAgB,CAAC,EAAE,CAAC;AAC9D,cAAQ,IAAI,EAAE;AAAA,IAClB;AAEA,YAAQ,IAAI,KAAK,UAAU,UAAU,EAAE,QAAQ,GAAG,WAAW,EAAE,CAAC,EAAE,KAAK,CAAC;AAGxE,UAAM,UAAU,QAAQ,kBAAkB;AAC1C,QAAI,SAAS;AACT,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,MAAM,IAAI,UAAU,CAAC;AACjC,cAAQ,IAAI,KAAK,MAAM,IAAI,WAAW,CAAC,MAAM,OAAO,EAAE;AACtD,YAAM,YAAY,QAAQ,aAAa;AACvC,UAAI,WAAW;AACX,gBAAQ,IAAI,KAAK,MAAM,IAAI,aAAa,CAAC,IAAI,SAAS,EAAE;AAAA,MAC5D;AAAA,IACJ;AAAA,EACJ,CAAC;AACT;","names":[]}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SettingsService
|
|
3
|
+
} from "./chunk-Z464RBPB.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
|
|
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,346 @@ 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
|
+
var CloudTaskProvider = class {
|
|
126
|
+
apiUrl;
|
|
127
|
+
accessKey;
|
|
128
|
+
projectId;
|
|
129
|
+
constructor(config, projectId) {
|
|
130
|
+
this.apiUrl = config.apiUrl.replace(/\/$/, "");
|
|
131
|
+
this.accessKey = config.accessKey;
|
|
132
|
+
this.projectId = projectId;
|
|
133
|
+
}
|
|
134
|
+
// ============================================
|
|
135
|
+
// HELPERS
|
|
136
|
+
// ============================================
|
|
137
|
+
headers() {
|
|
138
|
+
return {
|
|
139
|
+
"Content-Type": "application/json",
|
|
140
|
+
"JAI1-Access-Key": this.accessKey
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
async request(path, options = {}) {
|
|
144
|
+
const url = `${this.apiUrl}${path}`;
|
|
145
|
+
const res = await fetch(url, {
|
|
146
|
+
...options,
|
|
147
|
+
headers: { ...this.headers(), ...options.headers || {} }
|
|
148
|
+
});
|
|
149
|
+
if (!res.ok) {
|
|
150
|
+
let errMsg = `API error ${res.status}`;
|
|
151
|
+
try {
|
|
152
|
+
const body = await res.json();
|
|
153
|
+
if (body.error) errMsg = body.error;
|
|
154
|
+
} catch {
|
|
155
|
+
}
|
|
156
|
+
throw new Error(errMsg);
|
|
157
|
+
}
|
|
158
|
+
const data = await res.json();
|
|
159
|
+
if (!data.success) throw new Error(data.error || "API returned failure");
|
|
160
|
+
return data.data;
|
|
161
|
+
}
|
|
162
|
+
// ============================================
|
|
163
|
+
// ENSURE PROJECT REGISTERED
|
|
164
|
+
// ============================================
|
|
165
|
+
async ensureProjectRegistered(repoUrl) {
|
|
166
|
+
const result = await this.request(
|
|
167
|
+
"/api/projects/register",
|
|
168
|
+
{
|
|
169
|
+
method: "POST",
|
|
170
|
+
body: JSON.stringify({ repo_url: repoUrl })
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
return result.project_id;
|
|
174
|
+
}
|
|
175
|
+
// ============================================
|
|
176
|
+
// CRUD
|
|
177
|
+
// ============================================
|
|
178
|
+
async readAll() {
|
|
179
|
+
return this.request(`/api/tasks?projectId=${encodeURIComponent(this.projectId)}`);
|
|
180
|
+
}
|
|
181
|
+
async add(input) {
|
|
182
|
+
const all = await this.readAll().catch(() => []);
|
|
183
|
+
const maxNum = all.reduce((max, t) => {
|
|
184
|
+
const n = parseInt(t.id.replace("T-", ""), 10);
|
|
185
|
+
return n > max ? n : max;
|
|
186
|
+
}, 0);
|
|
187
|
+
const taskId = `T-${String(maxNum + 1).padStart(3, "0")}`;
|
|
188
|
+
return this.request("/api/tasks", {
|
|
189
|
+
method: "POST",
|
|
190
|
+
body: JSON.stringify({
|
|
191
|
+
task_id: taskId,
|
|
192
|
+
project_id: this.projectId,
|
|
193
|
+
title: input.title,
|
|
194
|
+
type: input.type || "task",
|
|
195
|
+
parent: input.parent || "",
|
|
196
|
+
priority: input.priority ?? 2,
|
|
197
|
+
depends_on: input.depends_on || [],
|
|
198
|
+
tags: input.tags || [],
|
|
199
|
+
branch: input.branch || "",
|
|
200
|
+
notes: input.notes || ""
|
|
201
|
+
})
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
async update(id, updates) {
|
|
205
|
+
return this.request(
|
|
206
|
+
`/api/tasks/${encodeURIComponent(id)}?projectId=${encodeURIComponent(this.projectId)}`,
|
|
207
|
+
{
|
|
208
|
+
method: "PATCH",
|
|
209
|
+
body: JSON.stringify(updates)
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
async deleteTask(id) {
|
|
214
|
+
await this.request(
|
|
215
|
+
`/api/tasks/${encodeURIComponent(id)}?projectId=${encodeURIComponent(this.projectId)}`,
|
|
216
|
+
{ method: "DELETE" }
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
async deleteGroup(parent) {
|
|
220
|
+
await this.request(
|
|
221
|
+
`/api/tasks/group/${encodeURIComponent(parent)}?projectId=${encodeURIComponent(this.projectId)}`,
|
|
222
|
+
{ method: "DELETE" }
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
// ============================================
|
|
226
|
+
// QUERY
|
|
227
|
+
// ============================================
|
|
228
|
+
async list(options) {
|
|
229
|
+
const params = new URLSearchParams({ projectId: this.projectId });
|
|
230
|
+
if (options?.status) params.set("status", options.status);
|
|
231
|
+
if (options?.type) params.set("type", options.type);
|
|
232
|
+
if (options?.parent !== void 0) params.set("parent", options.parent);
|
|
233
|
+
return this.request(`/api/tasks?${params}`);
|
|
234
|
+
}
|
|
235
|
+
async getById(id) {
|
|
236
|
+
try {
|
|
237
|
+
return await this.request(
|
|
238
|
+
`/api/tasks/${encodeURIComponent(id)}?projectId=${encodeURIComponent(this.projectId)}`
|
|
239
|
+
);
|
|
240
|
+
} catch {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
async getParents() {
|
|
245
|
+
const parents = await this.request(
|
|
246
|
+
`/api/tasks/parents?projectId=${encodeURIComponent(this.projectId)}`
|
|
247
|
+
);
|
|
248
|
+
const result = [];
|
|
249
|
+
for (const parent of parents) {
|
|
250
|
+
const tasks = await this.list({ parent });
|
|
251
|
+
const info = {
|
|
252
|
+
name: parent,
|
|
253
|
+
total: tasks.length,
|
|
254
|
+
todo: tasks.filter((t) => t.status === "todo").length,
|
|
255
|
+
in_progress: tasks.filter((t) => t.status === "in_progress").length,
|
|
256
|
+
done: tasks.filter((t) => t.status === "done").length,
|
|
257
|
+
cancelled: tasks.filter((t) => t.status === "cancelled").length,
|
|
258
|
+
blocked: 0,
|
|
259
|
+
ready: 0,
|
|
260
|
+
status: "todo"
|
|
261
|
+
};
|
|
262
|
+
if (info.done + info.cancelled === info.total) info.status = "done";
|
|
263
|
+
else if (info.in_progress > 0) info.status = "in_progress";
|
|
264
|
+
else if (info.todo > 0) info.status = "ready";
|
|
265
|
+
result.push(info);
|
|
266
|
+
}
|
|
267
|
+
return result;
|
|
268
|
+
}
|
|
269
|
+
async getStats() {
|
|
270
|
+
const raw = await this.request(
|
|
271
|
+
`/api/tasks/stats?projectId=${encodeURIComponent(this.projectId)}`
|
|
272
|
+
);
|
|
273
|
+
const s = raw.by_status;
|
|
274
|
+
const total = Object.values(s).reduce((a, b) => a + b, 0);
|
|
275
|
+
return {
|
|
276
|
+
total,
|
|
277
|
+
todo: s["todo"] || 0,
|
|
278
|
+
in_progress: s["in_progress"] || 0,
|
|
279
|
+
done: s["done"] || 0,
|
|
280
|
+
cancelled: s["cancelled"] || 0
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
// ============================================
|
|
284
|
+
// WORKFLOW
|
|
285
|
+
// ============================================
|
|
286
|
+
async getReady() {
|
|
287
|
+
return this.request(`/api/tasks/ready?projectId=${encodeURIComponent(this.projectId)}`);
|
|
288
|
+
}
|
|
289
|
+
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
|
+
});
|
|
297
|
+
}
|
|
298
|
+
async markDone(id) {
|
|
299
|
+
return this.request(`/api/tasks/${encodeURIComponent(id)}/done`, {
|
|
300
|
+
method: "POST",
|
|
301
|
+
body: JSON.stringify({ project_id: this.projectId })
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
async cancel(id) {
|
|
305
|
+
await this.request(`/api/tasks/${encodeURIComponent(id)}/cancel`, {
|
|
306
|
+
method: "POST",
|
|
307
|
+
body: JSON.stringify({ project_id: this.projectId })
|
|
308
|
+
});
|
|
309
|
+
const task = await this.getById(id);
|
|
310
|
+
return task;
|
|
311
|
+
}
|
|
312
|
+
// ============================================
|
|
313
|
+
// SYNC
|
|
314
|
+
// ============================================
|
|
315
|
+
async pull() {
|
|
316
|
+
return this.readAll();
|
|
317
|
+
}
|
|
318
|
+
async push(tasks) {
|
|
319
|
+
for (const task of tasks) {
|
|
320
|
+
try {
|
|
321
|
+
await this.request(
|
|
322
|
+
`/api/tasks/${encodeURIComponent(task.id)}?projectId=${encodeURIComponent(this.projectId)}`,
|
|
323
|
+
{ method: "PATCH", body: JSON.stringify(task) }
|
|
324
|
+
);
|
|
325
|
+
} catch {
|
|
326
|
+
try {
|
|
327
|
+
await this.request("/api/tasks", {
|
|
328
|
+
method: "POST",
|
|
329
|
+
body: JSON.stringify({
|
|
330
|
+
task_id: task.id,
|
|
331
|
+
project_id: this.projectId,
|
|
332
|
+
...task
|
|
333
|
+
})
|
|
334
|
+
});
|
|
335
|
+
} catch {
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
49
342
|
// src/services/task.service.ts
|
|
50
343
|
var TASKS_FILE = ".jai1/tasks.jsonl";
|
|
51
344
|
var SYNC_BRANCH = "jai1";
|
|
52
345
|
var TaskService = class {
|
|
53
346
|
tasksPath;
|
|
347
|
+
cwd;
|
|
348
|
+
_cloudProvider = null;
|
|
349
|
+
_providerReady;
|
|
54
350
|
constructor(cwd) {
|
|
55
|
-
this.
|
|
351
|
+
this.cwd = cwd || process.cwd();
|
|
352
|
+
this.tasksPath = join2(this.cwd, TASKS_FILE);
|
|
353
|
+
this._providerReady = this._initCloudProvider();
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Initialise cloud provider if settings.yaml has tasks.cloud = true
|
|
357
|
+
*/
|
|
358
|
+
async _initCloudProvider() {
|
|
359
|
+
try {
|
|
360
|
+
const settings = new SettingsService(this.cwd);
|
|
361
|
+
if (!settings.isTaskCloudEnabled()) return;
|
|
362
|
+
const projectId = settings.getProjectId();
|
|
363
|
+
if (!projectId) return;
|
|
364
|
+
const config = await new ConfigService().load();
|
|
365
|
+
if (!config?.apiUrl || !config?.accessKey) return;
|
|
366
|
+
this._cloudProvider = new CloudTaskProvider(config, projectId);
|
|
367
|
+
} catch {
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Wait for provider initialisation (call before any cloud operation)
|
|
372
|
+
*/
|
|
373
|
+
async ready() {
|
|
374
|
+
await this._providerReady;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Returns true when cloud mode is active
|
|
378
|
+
*/
|
|
379
|
+
get isCloud() {
|
|
380
|
+
return this._cloudProvider !== null;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Returns cloud provider if active
|
|
384
|
+
*/
|
|
385
|
+
get cloud() {
|
|
386
|
+
return this._cloudProvider;
|
|
56
387
|
}
|
|
57
388
|
/**
|
|
58
389
|
* Check if .jai1 directory exists in CWD.
|
|
59
390
|
* If not, print a helpful message and exit.
|
|
60
391
|
*/
|
|
61
392
|
static ensureJai1Dir(cwd) {
|
|
62
|
-
const dir =
|
|
393
|
+
const dir = join2(cwd || process.cwd(), ".jai1");
|
|
63
394
|
if (!existsSync(dir)) {
|
|
64
395
|
console.error(chalk.red("\u274C Th\u01B0 m\u1EE5c .jai1 kh\xF4ng t\u1ED3n t\u1EA1i trong project n\xE0y."));
|
|
65
396
|
console.error("");
|
|
@@ -81,7 +412,7 @@ var TaskService = class {
|
|
|
81
412
|
async readAll() {
|
|
82
413
|
try {
|
|
83
414
|
await this.ensureTasksFileNotDirectory();
|
|
84
|
-
const content = await
|
|
415
|
+
const content = await fs2.readFile(this.tasksPath, "utf-8");
|
|
85
416
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
86
417
|
return lines.map((line) => TaskSchema.parse(JSON.parse(line)));
|
|
87
418
|
} catch (error) {
|
|
@@ -252,10 +583,24 @@ var TaskService = class {
|
|
|
252
583
|
* Add a new task
|
|
253
584
|
*/
|
|
254
585
|
async add(data) {
|
|
586
|
+
await this.ready();
|
|
587
|
+
if (this._cloudProvider) {
|
|
588
|
+
const input = {
|
|
589
|
+
title: data.title,
|
|
590
|
+
...data.type !== void 0 && { type: data.type },
|
|
591
|
+
...data.parent !== void 0 && { parent: data.parent },
|
|
592
|
+
...data.priority !== void 0 && { priority: data.priority },
|
|
593
|
+
...data.tags !== void 0 && { tags: data.tags },
|
|
594
|
+
...data.notes !== void 0 && { notes: data.notes },
|
|
595
|
+
...data.branch !== void 0 && { branch: data.branch }
|
|
596
|
+
};
|
|
597
|
+
return this._cloudProvider.add(input);
|
|
598
|
+
}
|
|
255
599
|
const id = await this.nextId();
|
|
256
600
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
257
601
|
const task = TaskSchema.parse({
|
|
258
602
|
id,
|
|
603
|
+
type: data.type || "task",
|
|
259
604
|
parent: data.parent || "",
|
|
260
605
|
title: data.title,
|
|
261
606
|
status: "todo",
|
|
@@ -266,8 +611,8 @@ var TaskService = class {
|
|
|
266
611
|
created: now,
|
|
267
612
|
updated: now,
|
|
268
613
|
tags: data.tags || [],
|
|
269
|
-
branch: "",
|
|
270
|
-
notes: ""
|
|
614
|
+
branch: data.branch || "",
|
|
615
|
+
notes: data.notes || ""
|
|
271
616
|
});
|
|
272
617
|
await this.appendTask(task);
|
|
273
618
|
return task;
|
|
@@ -471,7 +816,7 @@ var TaskService = class {
|
|
|
471
816
|
async syncPush() {
|
|
472
817
|
const cwd = process.cwd();
|
|
473
818
|
const branch = SYNC_BRANCH;
|
|
474
|
-
const tasksFullPath =
|
|
819
|
+
const tasksFullPath = join2(cwd, TASKS_FILE);
|
|
475
820
|
if (!existsSync(tasksFullPath)) {
|
|
476
821
|
return;
|
|
477
822
|
}
|
|
@@ -581,16 +926,16 @@ var TaskService = class {
|
|
|
581
926
|
async appendTask(task) {
|
|
582
927
|
await this.ensureTasksFileNotDirectory();
|
|
583
928
|
const dir = dirname(this.tasksPath);
|
|
584
|
-
await
|
|
929
|
+
await fs2.mkdir(dir, { recursive: true });
|
|
585
930
|
const line = JSON.stringify(task) + "\n";
|
|
586
|
-
await
|
|
931
|
+
await fs2.appendFile(this.tasksPath, line, "utf-8");
|
|
587
932
|
}
|
|
588
933
|
async writeAll(tasks) {
|
|
589
934
|
await this.ensureTasksFileNotDirectory();
|
|
590
935
|
const dir = dirname(this.tasksPath);
|
|
591
|
-
await
|
|
936
|
+
await fs2.mkdir(dir, { recursive: true });
|
|
592
937
|
const content = tasks.map((t) => JSON.stringify(t)).join("\n") + "\n";
|
|
593
|
-
await
|
|
938
|
+
await fs2.writeFile(this.tasksPath, content, "utf-8");
|
|
594
939
|
}
|
|
595
940
|
getCurrentUser() {
|
|
596
941
|
try {
|
|
@@ -618,7 +963,7 @@ var TaskService = class {
|
|
|
618
963
|
}
|
|
619
964
|
async ensureTasksFileNotDirectory() {
|
|
620
965
|
try {
|
|
621
|
-
const stat = await
|
|
966
|
+
const stat = await fs2.stat(this.tasksPath);
|
|
622
967
|
if (stat.isDirectory()) {
|
|
623
968
|
throw new Error(
|
|
624
969
|
`Invalid tasks path: expected file at ${TASKS_FILE} but found a directory. Please remove the directory and re-run the command.`
|
|
@@ -685,6 +1030,7 @@ function createTaskSummaryCommand() {
|
|
|
685
1030
|
}
|
|
686
1031
|
|
|
687
1032
|
export {
|
|
1033
|
+
ConfigService,
|
|
688
1034
|
STATUS_ICONS,
|
|
689
1035
|
BLOCKED_ICON,
|
|
690
1036
|
PRIORITY_ICONS,
|
|
@@ -693,4 +1039,4 @@ export {
|
|
|
693
1039
|
handleTaskSummary,
|
|
694
1040
|
createTaskSummaryCommand
|
|
695
1041
|
};
|
|
696
|
-
//# sourceMappingURL=chunk-
|
|
1042
|
+
//# sourceMappingURL=chunk-S7QUPWSM.js.map
|