@jvittechs/j 1.0.35 → 1.0.37
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-3XLJNHC6.js → chunk-NBOT35QM.js} +152 -24
- package/dist/chunk-NBOT35QM.js.map +1 -0
- package/dist/cli.js +41 -22
- package/dist/cli.js.map +1 -1
- package/dist/{summary-3BGB4JQH.js → summary-KPCAN522.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-3XLJNHC6.js.map +0 -1
- /package/dist/{summary-3BGB4JQH.js.map → summary-KPCAN522.js.map} +0 -0
|
@@ -48,6 +48,7 @@ var PRIORITY_LABELS = {
|
|
|
48
48
|
|
|
49
49
|
// src/services/task.service.ts
|
|
50
50
|
var TASKS_FILE = ".jai1/tasks.jsonl";
|
|
51
|
+
var SYNC_BRANCH = "jai1";
|
|
51
52
|
var TaskService = class {
|
|
52
53
|
tasksPath;
|
|
53
54
|
constructor(cwd) {
|
|
@@ -111,18 +112,18 @@ var TaskService = class {
|
|
|
111
112
|
}
|
|
112
113
|
/**
|
|
113
114
|
* Get tasks that are ready to pick:
|
|
114
|
-
* status=todo, all depends_on done, assigned_to empty
|
|
115
|
+
* status=todo, all depends_on done or cancelled, assigned_to empty
|
|
115
116
|
*/
|
|
116
117
|
async getReady(parent) {
|
|
117
118
|
const tasks = await this.readAll();
|
|
118
|
-
const
|
|
119
|
-
tasks.filter((t) => t.status === "done").map((t) => t.id)
|
|
119
|
+
const resolvedIds = new Set(
|
|
120
|
+
tasks.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
|
|
120
121
|
);
|
|
121
122
|
return tasks.filter((t) => {
|
|
122
123
|
if (t.status !== "todo") return false;
|
|
123
124
|
if (t.assigned_to) return false;
|
|
124
125
|
if (parent && t.parent !== parent) return false;
|
|
125
|
-
if (t.depends_on.length > 0 && !t.depends_on.every((depId) =>
|
|
126
|
+
if (t.depends_on.length > 0 && !t.depends_on.every((depId) => resolvedIds.has(depId))) {
|
|
126
127
|
return false;
|
|
127
128
|
}
|
|
128
129
|
return true;
|
|
@@ -136,10 +137,10 @@ var TaskService = class {
|
|
|
136
137
|
return { blocked: false, blockedBy: [] };
|
|
137
138
|
}
|
|
138
139
|
const tasks = await this.readAll();
|
|
139
|
-
const
|
|
140
|
-
tasks.filter((t) => t.status === "done").map((t) => t.id)
|
|
140
|
+
const resolvedIds = new Set(
|
|
141
|
+
tasks.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
|
|
141
142
|
);
|
|
142
|
-
const blockedBy = task.depends_on.filter((id) => !
|
|
143
|
+
const blockedBy = task.depends_on.filter((id) => !resolvedIds.has(id));
|
|
143
144
|
return { blocked: blockedBy.length > 0, blockedBy };
|
|
144
145
|
}
|
|
145
146
|
/**
|
|
@@ -147,12 +148,12 @@ var TaskService = class {
|
|
|
147
148
|
*/
|
|
148
149
|
async getStats() {
|
|
149
150
|
const tasks = await this.readAll();
|
|
150
|
-
const
|
|
151
|
-
tasks.filter((t) => t.status === "done").map((t) => t.id)
|
|
151
|
+
const resolvedIds = new Set(
|
|
152
|
+
tasks.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
|
|
152
153
|
);
|
|
153
154
|
let blocked = 0;
|
|
154
155
|
for (const t of tasks) {
|
|
155
|
-
if (t.status === "todo" && t.depends_on.length > 0 && !t.depends_on.every((id) =>
|
|
156
|
+
if (t.status === "todo" && t.depends_on.length > 0 && !t.depends_on.every((id) => resolvedIds.has(id))) {
|
|
156
157
|
blocked++;
|
|
157
158
|
}
|
|
158
159
|
}
|
|
@@ -260,51 +261,178 @@ var TaskService = class {
|
|
|
260
261
|
// GIT SYNC
|
|
261
262
|
// ============================================
|
|
262
263
|
/**
|
|
263
|
-
*
|
|
264
|
+
* Get the sync branch name
|
|
265
|
+
*/
|
|
266
|
+
getSyncBranch() {
|
|
267
|
+
return SYNC_BRANCH;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Check if a remote branch exists
|
|
271
|
+
*/
|
|
272
|
+
remoteBranchExists(cwd, branch) {
|
|
273
|
+
try {
|
|
274
|
+
const output = execSync(`git ls-remote --heads origin ${branch}`, { cwd, encoding: "utf-8" }).trim();
|
|
275
|
+
return output.length > 0;
|
|
276
|
+
} catch {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Check if a local branch exists
|
|
282
|
+
*/
|
|
283
|
+
localBranchExists(cwd, branch) {
|
|
284
|
+
try {
|
|
285
|
+
execSync(`git rev-parse --verify ${branch}`, { cwd, stdio: "pipe" });
|
|
286
|
+
return true;
|
|
287
|
+
} catch {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Stash changes if working tree is dirty, returns true if stash was created
|
|
293
|
+
*/
|
|
294
|
+
stashIfDirty(cwd) {
|
|
295
|
+
try {
|
|
296
|
+
execSync("git diff --quiet && git diff --cached --quiet", { cwd, stdio: "pipe" });
|
|
297
|
+
return false;
|
|
298
|
+
} catch {
|
|
299
|
+
execSync('git stash push -m "jai1-task-sync-auto-stash"', { cwd, stdio: "pipe" });
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Pop stash if it was created by us
|
|
305
|
+
*/
|
|
306
|
+
stashPopIfNeeded(cwd, didStash) {
|
|
307
|
+
if (!didStash) return;
|
|
308
|
+
try {
|
|
309
|
+
execSync("git stash pop", { cwd, stdio: "pipe" });
|
|
310
|
+
} catch {
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Create the jai1 orphan branch with just the tasks file.
|
|
315
|
+
* An orphan branch has no shared history with code branches.
|
|
316
|
+
*/
|
|
317
|
+
createOrphanBranch(cwd, branch) {
|
|
318
|
+
const currentBranch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
319
|
+
cwd,
|
|
320
|
+
encoding: "utf-8"
|
|
321
|
+
}).trim();
|
|
322
|
+
const didStash = this.stashIfDirty(cwd);
|
|
323
|
+
try {
|
|
324
|
+
execSync(`git checkout --orphan ${branch}`, { cwd, stdio: "pipe" });
|
|
325
|
+
execSync("git rm -rf .", { cwd, stdio: "pipe" });
|
|
326
|
+
execSync(`git checkout ${currentBranch} -- ${TASKS_FILE}`, { cwd, stdio: "pipe" });
|
|
327
|
+
execSync(`git add ${TASKS_FILE}`, { cwd, stdio: "pipe" });
|
|
328
|
+
execSync(`git commit -m "chore(tasks): init task sync branch"`, { cwd, stdio: "pipe" });
|
|
329
|
+
execSync(`git push -u origin ${branch}`, { cwd, stdio: "pipe" });
|
|
330
|
+
} finally {
|
|
331
|
+
execSync(`git checkout ${currentBranch}`, { cwd, stdio: "pipe" });
|
|
332
|
+
this.stashPopIfNeeded(cwd, didStash);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Commit and push only the tasks file to the sync branch.
|
|
337
|
+
* Works from any branch — pushes only the tasks commit to origin/<SYNC_BRANCH>.
|
|
338
|
+
*
|
|
339
|
+
* Edge cases handled:
|
|
340
|
+
* - jai1 branch doesn't exist → creates orphan branch
|
|
341
|
+
* - On feature branch → cherry-pick onto jai1
|
|
342
|
+
* - Dirty working tree → stash/unstash automatically
|
|
264
343
|
*/
|
|
265
344
|
async syncPush() {
|
|
266
345
|
const cwd = process.cwd();
|
|
346
|
+
const branch = SYNC_BRANCH;
|
|
267
347
|
execSync(`git add ${TASKS_FILE}`, { cwd, stdio: "pipe" });
|
|
268
348
|
try {
|
|
269
349
|
execSync("git diff --cached --quiet", { cwd, stdio: "pipe" });
|
|
270
350
|
return;
|
|
271
351
|
} catch {
|
|
272
352
|
}
|
|
353
|
+
const currentBranch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
354
|
+
cwd,
|
|
355
|
+
encoding: "utf-8"
|
|
356
|
+
}).trim();
|
|
357
|
+
if (!this.remoteBranchExists(cwd, branch) && !this.localBranchExists(cwd, branch)) {
|
|
358
|
+
execSync(`git commit -m "chore(tasks): sync task status"`, { cwd, stdio: "pipe" });
|
|
359
|
+
this.createOrphanBranch(cwd, branch);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
273
362
|
execSync(`git commit -m "chore(tasks): sync task status"`, {
|
|
274
363
|
cwd,
|
|
275
364
|
stdio: "pipe"
|
|
276
365
|
});
|
|
277
|
-
|
|
366
|
+
if (currentBranch === branch) {
|
|
367
|
+
execSync(`git push origin ${branch}`, { cwd, stdio: "pipe" });
|
|
368
|
+
} else {
|
|
369
|
+
const commitHash = execSync("git rev-parse HEAD", {
|
|
370
|
+
cwd,
|
|
371
|
+
encoding: "utf-8"
|
|
372
|
+
}).trim();
|
|
373
|
+
const didStash = this.stashIfDirty(cwd);
|
|
374
|
+
try {
|
|
375
|
+
if (this.localBranchExists(cwd, branch)) {
|
|
376
|
+
execSync(`git checkout ${branch}`, { cwd, stdio: "pipe" });
|
|
377
|
+
execSync(`git pull origin ${branch}`, { cwd, stdio: "pipe" });
|
|
378
|
+
} else {
|
|
379
|
+
execSync(`git checkout -b ${branch} origin/${branch}`, { cwd, stdio: "pipe" });
|
|
380
|
+
}
|
|
381
|
+
try {
|
|
382
|
+
execSync(`git cherry-pick ${commitHash}`, { cwd, stdio: "pipe" });
|
|
383
|
+
} catch {
|
|
384
|
+
execSync(`git checkout ${commitHash} -- ${TASKS_FILE}`, { cwd, stdio: "pipe" });
|
|
385
|
+
execSync("git add .", { cwd, stdio: "pipe" });
|
|
386
|
+
try {
|
|
387
|
+
execSync("git cherry-pick --continue", { cwd, stdio: "pipe" });
|
|
388
|
+
} catch {
|
|
389
|
+
execSync(`git commit --allow-empty -m "chore(tasks): sync task status"`, { cwd, stdio: "pipe" });
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
execSync(`git push origin ${branch}`, { cwd, stdio: "pipe" });
|
|
393
|
+
} finally {
|
|
394
|
+
execSync(`git checkout ${currentBranch}`, { cwd, stdio: "pipe" });
|
|
395
|
+
this.stashPopIfNeeded(cwd, didStash);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
278
398
|
}
|
|
279
399
|
/**
|
|
280
|
-
* Pull tasks from
|
|
400
|
+
* Pull tasks from sync branch and merge by timestamp.
|
|
401
|
+
*
|
|
402
|
+
* Edge cases handled:
|
|
403
|
+
* - jai1 branch doesn't exist on remote → returns empty (no-op)
|
|
404
|
+
* - tasks.jsonl doesn't exist on remote branch → returns empty
|
|
281
405
|
*/
|
|
282
406
|
async syncPull() {
|
|
283
407
|
const cwd = process.cwd();
|
|
408
|
+
const branch = SYNC_BRANCH;
|
|
284
409
|
let merged = 0;
|
|
285
410
|
let conflicts = 0;
|
|
411
|
+
if (!this.remoteBranchExists(cwd, branch)) {
|
|
412
|
+
return { merged: 0, conflicts: 0 };
|
|
413
|
+
}
|
|
286
414
|
try {
|
|
287
|
-
execSync(
|
|
288
|
-
let
|
|
415
|
+
execSync(`git fetch origin ${branch}`, { cwd, stdio: "pipe" });
|
|
416
|
+
let remoteContent;
|
|
289
417
|
try {
|
|
290
|
-
|
|
418
|
+
remoteContent = execSync(`git show origin/${branch}:${TASKS_FILE}`, {
|
|
291
419
|
cwd,
|
|
292
420
|
encoding: "utf-8"
|
|
293
421
|
});
|
|
294
422
|
} catch {
|
|
295
423
|
return { merged: 0, conflicts: 0 };
|
|
296
424
|
}
|
|
297
|
-
const
|
|
425
|
+
const remoteTasks = remoteContent.trim().split("\n").filter(Boolean).map((line) => TaskSchema.parse(JSON.parse(line)));
|
|
298
426
|
const localTasks = await this.readAll();
|
|
299
427
|
const localMap = new Map(localTasks.map((t) => [t.id, t]));
|
|
300
|
-
for (const
|
|
301
|
-
const local = localMap.get(
|
|
428
|
+
for (const remoteTask of remoteTasks) {
|
|
429
|
+
const local = localMap.get(remoteTask.id);
|
|
302
430
|
if (!local) {
|
|
303
|
-
localTasks.push(
|
|
431
|
+
localTasks.push(remoteTask);
|
|
304
432
|
merged++;
|
|
305
|
-
} else if (
|
|
306
|
-
const idx = localTasks.findIndex((t) => t.id ===
|
|
307
|
-
localTasks[idx] =
|
|
433
|
+
} else if (remoteTask.updated > local.updated) {
|
|
434
|
+
const idx = localTasks.findIndex((t) => t.id === remoteTask.id);
|
|
435
|
+
localTasks[idx] = remoteTask;
|
|
308
436
|
merged++;
|
|
309
437
|
}
|
|
310
438
|
}
|
|
@@ -434,4 +562,4 @@ export {
|
|
|
434
562
|
handleTaskSummary,
|
|
435
563
|
createTaskSummaryCommand
|
|
436
564
|
};
|
|
437
|
-
//# sourceMappingURL=chunk-
|
|
565
|
+
//# sourceMappingURL=chunk-NBOT35QM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/tasks/summary.ts","../src/services/task.service.ts","../src/types/task.types.ts"],"sourcesContent":["/**\n * jai1 tasks summary [-j]\n */\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport boxen from 'boxen';\nimport { TaskService } from '../../services/task.service.js';\nimport type { TaskSummaryOptions } from '../../types/task.types.js';\n\nexport async function handleTaskSummary(options: TaskSummaryOptions): Promise<void> {\n const service = new TaskService();\n const stats = await service.getStats();\n const tasks = await service.readAll();\n\n if (options.json) {\n console.log(JSON.stringify(stats, null, 2));\n return;\n }\n\n console.log(\n boxen(chalk.cyan.bold('📊 Task Summary'), {\n padding: { left: 1, right: 1, top: 0, bottom: 0 },\n borderStyle: 'round',\n borderColor: 'cyan',\n })\n );\n\n console.log();\n console.log(` 🔵 In Progress: ${chalk.bold(String(stats.in_progress))}`);\n console.log(` 📋 Todo: ${chalk.bold(String(stats.todo))}`);\n console.log(` 🔴 Blocked: ${chalk.bold(String(stats.blocked))}`);\n console.log(` ✅ Done: ${chalk.bold(String(stats.done))}`);\n console.log(` ⚫ Cancelled: ${chalk.bold(String(stats.cancelled))}`);\n console.log(chalk.dim(` ── Total: ${stats.total}`));\n\n // Show in-progress tasks\n const inProgress = tasks.filter((t) => t.status === 'in_progress');\n if (inProgress.length > 0) {\n console.log(chalk.bold('\\n 🔵 Currently working:'));\n for (const t of inProgress) {\n let line = ` ${chalk.dim(t.id)} ${t.title}`;\n if (t.assigned_to) line += chalk.cyan(` @${t.assigned_to}`);\n console.log(line);\n }\n }\n\n // Show ready count\n const ready = await service.getReady();\n if (ready.length > 0) {\n console.log(chalk.dim(`\\n 💡 ${ready.length} tasks ready to pick: jai1 t pick`));\n }\n\n console.log();\n}\n\nexport function createTaskSummaryCommand(): Command {\n return new Command('summary')\n .description('Show task dashboard')\n .option('-j, --json', 'Output JSON')\n .action(async (options: TaskSummaryOptions) => {\n await handleTaskSummary(options);\n });\n}\n","/**\n * Task Service\n * CRUD operations on .jai1/tasks.jsonl\n */\nimport { promises as fs, existsSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { execSync } from 'child_process';\nimport chalk from 'chalk';\nimport { TaskSchema, type Task, type TaskStatusType } from '../types/task.types.js';\n\nconst TASKS_FILE = '.jai1/tasks.jsonl';\nconst SYNC_BRANCH = 'jai1';\n\nexport class TaskService {\n private readonly tasksPath: string;\n\n constructor(cwd?: string) {\n this.tasksPath = join(cwd || process.cwd(), TASKS_FILE);\n }\n\n /**\n * Check if .jai1 directory exists in CWD.\n * If not, print a helpful message and exit.\n */\n static ensureJai1Dir(cwd?: string): void {\n const dir = join(cwd || process.cwd(), '.jai1');\n if (!existsSync(dir)) {\n console.error(chalk.red('❌ Thư mục .jai1 không tồn tại trong project này.'));\n console.error('');\n console.error(chalk.dim(' Bạn cần khởi tạo Jai1 framework trước khi sử dụng task management.'));\n console.error('');\n console.error(` ${chalk.bold('Hướng dẫn:')}`);\n console.error(` ${chalk.cyan('jai1 doctor')} Kiểm tra trạng thái cài đặt`);\n console.error(` ${chalk.cyan('jai1 guide')} Hướng dẫn sử dụng đầy đủ`);\n console.error('');\n process.exit(1);\n }\n }\n\n // ============================================\n // READ\n // ============================================\n\n /**\n * Read all tasks from JSONL file\n */\n async readAll(): Promise<Task[]> {\n try {\n await this.ensureTasksFileNotDirectory();\n const content = await fs.readFile(this.tasksPath, 'utf-8');\n const lines = content.trim().split('\\n').filter(Boolean);\n return lines.map((line) => TaskSchema.parse(JSON.parse(line)));\n } catch (error: unknown) {\n if (error && typeof error === 'object' && 'code' in error && (error as { code: string }).code === 'ENOENT') {\n return [];\n }\n throw error;\n }\n }\n\n /**\n * Find task by ID\n */\n async findById(id: string): Promise<Task | null> {\n const tasks = await this.readAll();\n return tasks.find((t) => t.id === id) || null;\n }\n\n /**\n * Filter tasks by criteria\n */\n async filter(criteria: {\n status?: TaskStatusType;\n parent?: string;\n assignee?: string;\n }): Promise<Task[]> {\n const tasks = await this.readAll();\n return tasks.filter((t) => {\n if (criteria.status && t.status !== criteria.status) return false;\n if (criteria.parent && t.parent !== criteria.parent) return false;\n if (criteria.assignee && t.assigned_to !== criteria.assignee) return false;\n return true;\n });\n }\n\n /**\n * Get tasks that are ready to pick:\n * status=todo, all depends_on done or cancelled, assigned_to empty\n */\n async getReady(parent?: string): Promise<Task[]> {\n const tasks = await this.readAll();\n const resolvedIds = new Set(\n tasks.filter((t) => t.status === 'done' || t.status === 'cancelled').map((t) => t.id)\n );\n\n return tasks\n .filter((t) => {\n if (t.status !== 'todo') return false;\n if (t.assigned_to) return false;\n if (parent && t.parent !== parent) return false;\n // All dependencies must be done or cancelled\n if (t.depends_on.length > 0 && !t.depends_on.every((depId) => resolvedIds.has(depId))) {\n return false;\n }\n return true;\n })\n .sort((a, b) => a.priority - b.priority || a.created.localeCompare(b.created));\n }\n\n /**\n * Check if a task is blocked (has unfinished dependencies)\n */\n async isBlocked(task: Task): Promise<{ blocked: boolean; blockedBy: string[] }> {\n if (task.depends_on.length === 0) {\n return { blocked: false, blockedBy: [] };\n }\n const tasks = await this.readAll();\n const resolvedIds = new Set(\n tasks.filter((t) => t.status === 'done' || t.status === 'cancelled').map((t) => t.id)\n );\n const blockedBy = task.depends_on.filter((id) => !resolvedIds.has(id));\n return { blocked: blockedBy.length > 0, blockedBy };\n }\n\n /**\n * Get stats by status\n */\n async getStats(): Promise<{\n total: number;\n todo: number;\n in_progress: number;\n done: number;\n cancelled: number;\n blocked: number;\n }> {\n const tasks = await this.readAll();\n const resolvedIds = new Set(\n tasks.filter((t) => t.status === 'done' || t.status === 'cancelled').map((t) => t.id)\n );\n\n let blocked = 0;\n for (const t of tasks) {\n if (\n t.status === 'todo' &&\n t.depends_on.length > 0 &&\n !t.depends_on.every((id) => resolvedIds.has(id))\n ) {\n blocked++;\n }\n }\n\n return {\n total: tasks.length,\n todo: tasks.filter((t) => t.status === 'todo').length,\n in_progress: tasks.filter((t) => t.status === 'in_progress').length,\n done: tasks.filter((t) => t.status === 'done').length,\n cancelled: tasks.filter((t) => t.status === 'cancelled').length,\n blocked,\n };\n }\n\n // ============================================\n // WRITE\n // ============================================\n\n /**\n * Generate next task ID\n */\n async nextId(): Promise<string> {\n const tasks = await this.readAll();\n if (tasks.length === 0) return 'T-001';\n\n const maxNum = Math.max(\n ...tasks.map((t) => parseInt(t.id.replace('T-', ''), 10))\n );\n return `T-${String(maxNum + 1).padStart(3, '0')}`;\n }\n\n /**\n * Add a new task\n */\n async add(data: {\n title: string;\n parent?: string;\n priority?: number;\n tags?: string[];\n }): Promise<Task> {\n const id = await this.nextId();\n const now = new Date().toISOString().split('T')[0]!;\n\n const task = TaskSchema.parse({\n id,\n parent: data.parent || '',\n title: data.title,\n status: 'todo',\n assigned_to: '',\n claimed_at: '',\n priority: data.priority ?? 2,\n depends_on: [],\n created: now,\n updated: now,\n tags: data.tags || [],\n branch: '',\n notes: '',\n });\n\n await this.appendTask(task);\n return task;\n }\n\n /**\n * Update a task by ID\n */\n async update(\n id: string,\n changes: Partial<Pick<Task, 'status' | 'assigned_to' | 'claimed_at' | 'notes' | 'branch'>>\n ): Promise<Task> {\n const tasks = await this.readAll();\n const index = tasks.findIndex((t) => t.id === id);\n\n if (index === -1) {\n throw new Error(`Task ${id} not found`);\n }\n\n const now = new Date().toISOString().split('T')[0]!;\n const existing = tasks[index]!;\n tasks[index] = { ...existing, ...changes, updated: now };\n\n await this.writeAll(tasks);\n return tasks[index]!;\n }\n\n /**\n * Add dependency: child depends on parent\n */\n async addDependency(childId: string, parentId: string): Promise<Task> {\n const tasks = await this.readAll();\n const child = tasks.find((t) => t.id === childId);\n const parent = tasks.find((t) => t.id === parentId);\n\n if (!child) throw new Error(`Task ${childId} not found`);\n if (!parent) throw new Error(`Task ${parentId} not found`);\n\n if (child.depends_on.includes(parentId)) {\n throw new Error(`${childId} already depends on ${parentId}`);\n }\n\n // Circular dependency check\n if (await this.wouldCreateCycle(childId, parentId)) {\n throw new Error(`Adding dependency would create a cycle: ${parentId} → ... → ${childId}`);\n }\n\n child.depends_on.push(parentId);\n child.updated = new Date().toISOString().split('T')[0]!;\n\n await this.writeAll(tasks);\n return child;\n }\n\n /**\n * Mark task as done\n */\n async markDone(id: string): Promise<Task> {\n return this.update(id, { status: 'done' });\n }\n\n /**\n * Pick next task: claim for current user\n */\n async pick(taskId: string): Promise<Task> {\n const username = this.getCurrentUser();\n const now = new Date().toISOString();\n\n return this.update(taskId, {\n status: 'in_progress',\n assigned_to: username,\n claimed_at: now,\n });\n }\n\n // ============================================\n // GIT SYNC\n // ============================================\n\n /**\n * Get the sync branch name\n */\n getSyncBranch(): string {\n return SYNC_BRANCH;\n }\n\n /**\n * Check if a remote branch exists\n */\n private remoteBranchExists(cwd: string, branch: string): boolean {\n try {\n const output = execSync(`git ls-remote --heads origin ${branch}`, { cwd, encoding: 'utf-8' }).trim();\n return output.length > 0;\n } catch {\n return false;\n }\n }\n\n /**\n * Check if a local branch exists\n */\n private localBranchExists(cwd: string, branch: string): boolean {\n try {\n execSync(`git rev-parse --verify ${branch}`, { cwd, stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Stash changes if working tree is dirty, returns true if stash was created\n */\n private stashIfDirty(cwd: string): boolean {\n try {\n execSync('git diff --quiet && git diff --cached --quiet', { cwd, stdio: 'pipe' });\n return false; // clean\n } catch {\n execSync('git stash push -m \"jai1-task-sync-auto-stash\"', { cwd, stdio: 'pipe' });\n return true;\n }\n }\n\n /**\n * Pop stash if it was created by us\n */\n private stashPopIfNeeded(cwd: string, didStash: boolean): void {\n if (!didStash) return;\n try {\n execSync('git stash pop', { cwd, stdio: 'pipe' });\n } catch {\n // stash pop conflict — leave in stash list, user can resolve\n }\n }\n\n /**\n * Create the jai1 orphan branch with just the tasks file.\n * An orphan branch has no shared history with code branches.\n */\n private createOrphanBranch(cwd: string, branch: string): void {\n const currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {\n cwd, encoding: 'utf-8',\n }).trim();\n\n const didStash = this.stashIfDirty(cwd);\n\n try {\n // Create orphan branch (no parent commits)\n execSync(`git checkout --orphan ${branch}`, { cwd, stdio: 'pipe' });\n // Remove all files from staging\n execSync('git rm -rf .', { cwd, stdio: 'pipe' });\n // Add only the tasks file\n execSync(`git checkout ${currentBranch} -- ${TASKS_FILE}`, { cwd, stdio: 'pipe' });\n execSync(`git add ${TASKS_FILE}`, { cwd, stdio: 'pipe' });\n execSync(`git commit -m \"chore(tasks): init task sync branch\"`, { cwd, stdio: 'pipe' });\n execSync(`git push -u origin ${branch}`, { cwd, stdio: 'pipe' });\n } finally {\n execSync(`git checkout ${currentBranch}`, { cwd, stdio: 'pipe' });\n this.stashPopIfNeeded(cwd, didStash);\n }\n }\n\n /**\n * Commit and push only the tasks file to the sync branch.\n * Works from any branch — pushes only the tasks commit to origin/<SYNC_BRANCH>.\n *\n * Edge cases handled:\n * - jai1 branch doesn't exist → creates orphan branch\n * - On feature branch → cherry-pick onto jai1\n * - Dirty working tree → stash/unstash automatically\n */\n async syncPush(): Promise<void> {\n const cwd = process.cwd();\n const branch = SYNC_BRANCH;\n\n // Stage the tasks file\n execSync(`git add ${TASKS_FILE}`, { cwd, stdio: 'pipe' });\n\n try {\n execSync('git diff --cached --quiet', { cwd, stdio: 'pipe' });\n // No changes staged — nothing to push\n return;\n } catch {\n // Changes exist, continue\n }\n\n // Detect current branch\n const currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {\n cwd,\n encoding: 'utf-8',\n }).trim();\n\n // If jai1 branch doesn't exist anywhere, create it as orphan\n if (!this.remoteBranchExists(cwd, branch) && !this.localBranchExists(cwd, branch)) {\n // Commit first on current branch so we have the file\n execSync(`git commit -m \"chore(tasks): sync task status\"`, { cwd, stdio: 'pipe' });\n this.createOrphanBranch(cwd, branch);\n return;\n }\n\n // Commit on current branch\n execSync(`git commit -m \"chore(tasks): sync task status\"`, {\n cwd,\n stdio: 'pipe',\n });\n\n if (currentBranch === branch) {\n // Already on sync branch → push normally\n execSync(`git push origin ${branch}`, { cwd, stdio: 'pipe' });\n } else {\n // On a different branch → cherry-pick the commit onto sync branch\n const commitHash = execSync('git rev-parse HEAD', {\n cwd,\n encoding: 'utf-8',\n }).trim();\n\n const didStash = this.stashIfDirty(cwd);\n\n try {\n // Ensure local jai1 branch exists\n if (this.localBranchExists(cwd, branch)) {\n execSync(`git checkout ${branch}`, { cwd, stdio: 'pipe' });\n execSync(`git pull origin ${branch}`, { cwd, stdio: 'pipe' });\n } else {\n // Remote exists but no local — track it\n execSync(`git checkout -b ${branch} origin/${branch}`, { cwd, stdio: 'pipe' });\n }\n\n // Cherry-pick the tasks commit\n try {\n execSync(`git cherry-pick ${commitHash}`, { cwd, stdio: 'pipe' });\n } catch {\n // Cherry-pick conflict — accept theirs for non-tasks files, ours for tasks\n execSync(`git checkout ${commitHash} -- ${TASKS_FILE}`, { cwd, stdio: 'pipe' });\n execSync('git add .', { cwd, stdio: 'pipe' });\n try {\n execSync('git cherry-pick --continue', { cwd, stdio: 'pipe' });\n } catch {\n execSync(`git commit --allow-empty -m \"chore(tasks): sync task status\"`, { cwd, stdio: 'pipe' });\n }\n }\n\n execSync(`git push origin ${branch}`, { cwd, stdio: 'pipe' });\n } finally {\n // Always switch back to original branch\n execSync(`git checkout ${currentBranch}`, { cwd, stdio: 'pipe' });\n this.stashPopIfNeeded(cwd, didStash);\n }\n }\n }\n\n /**\n * Pull tasks from sync branch and merge by timestamp.\n *\n * Edge cases handled:\n * - jai1 branch doesn't exist on remote → returns empty (no-op)\n * - tasks.jsonl doesn't exist on remote branch → returns empty\n */\n async syncPull(): Promise<{ merged: number; conflicts: number }> {\n const cwd = process.cwd();\n const branch = SYNC_BRANCH;\n let merged = 0;\n let conflicts = 0;\n\n // Check if remote branch exists first\n if (!this.remoteBranchExists(cwd, branch)) {\n return { merged: 0, conflicts: 0 };\n }\n\n try {\n // Fetch latest from sync branch\n execSync(`git fetch origin ${branch}`, { cwd, stdio: 'pipe' });\n\n // Read sync branch's tasks\n let remoteContent: string;\n try {\n remoteContent = execSync(`git show origin/${branch}:${TASKS_FILE}`, {\n cwd,\n encoding: 'utf-8',\n });\n } catch {\n // File doesn't exist on sync branch\n return { merged: 0, conflicts: 0 };\n }\n\n const remoteTasks = remoteContent\n .trim()\n .split('\\n')\n .filter(Boolean)\n .map((line: string) => TaskSchema.parse(JSON.parse(line)));\n\n const localTasks = await this.readAll();\n const localMap = new Map(localTasks.map((t) => [t.id, t]));\n\n for (const remoteTask of remoteTasks) {\n const local = localMap.get(remoteTask.id);\n if (!local) {\n // New task from remote → add\n localTasks.push(remoteTask);\n merged++;\n } else if (remoteTask.updated > local.updated) {\n // Remote is newer → replace\n const idx = localTasks.findIndex((t) => t.id === remoteTask.id);\n localTasks[idx] = remoteTask;\n merged++;\n }\n // If local is newer or same → keep local\n }\n\n await this.writeAll(localTasks);\n return { merged, conflicts };\n } catch (error) {\n throw new Error(\n `Sync pull failed: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n // ============================================\n // INTERNAL HELPERS\n // ============================================\n\n private async appendTask(task: Task): Promise<void> {\n await this.ensureTasksFileNotDirectory();\n const dir = dirname(this.tasksPath);\n await fs.mkdir(dir, { recursive: true });\n\n const line = JSON.stringify(task) + '\\n';\n await fs.appendFile(this.tasksPath, line, 'utf-8');\n }\n\n private async writeAll(tasks: Task[]): Promise<void> {\n await this.ensureTasksFileNotDirectory();\n const dir = dirname(this.tasksPath);\n await fs.mkdir(dir, { recursive: true });\n\n const content = tasks.map((t) => JSON.stringify(t)).join('\\n') + '\\n';\n await fs.writeFile(this.tasksPath, content, 'utf-8');\n }\n\n private getCurrentUser(): string {\n try {\n return execSync('git config user.name', { encoding: 'utf-8' }).trim();\n } catch {\n return 'unknown';\n }\n }\n\n private async wouldCreateCycle(childId: string, newDepId: string): Promise<boolean> {\n const tasks = await this.readAll();\n const taskMap = new Map(tasks.map((t) => [t.id, t]));\n\n // BFS from newDepId's dependencies to see if we reach childId\n const visited = new Set<string>();\n const queue = [newDepId];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n if (current === childId) return true;\n if (visited.has(current)) continue;\n visited.add(current);\n\n const task = taskMap.get(current);\n if (task) {\n queue.push(...task.depends_on);\n }\n }\n return false;\n }\n\n private async ensureTasksFileNotDirectory(): Promise<void> {\n try {\n const stat = await fs.stat(this.tasksPath);\n if (stat.isDirectory()) {\n throw new Error(\n `Invalid tasks path: expected file at ${TASKS_FILE} but found a directory. ` +\n `Please remove the directory and re-run the command.`\n );\n }\n } catch (error: unknown) {\n if (error && typeof error === 'object' && 'code' in error && (error as { code: string }).code === 'ENOENT') {\n return;\n }\n throw error;\n }\n }\n\n /**\n * Get tasks file path\n */\n getTasksPath(): string {\n return this.tasksPath;\n }\n}\n","/**\n * Task Management Types\n * Schema for .jai1/tasks.jsonl\n */\nimport { z } from 'zod';\n\n// ============================================\n// ENUMS\n// ============================================\n\nexport const TaskStatus = {\n TODO: 'todo',\n IN_PROGRESS: 'in_progress',\n DONE: 'done',\n CANCELLED: 'cancelled',\n} as const;\n\nexport type TaskStatusType = (typeof TaskStatus)[keyof typeof TaskStatus];\n\nexport const TaskPriority = {\n CRITICAL: 0,\n HIGH: 1,\n MEDIUM: 2,\n LOW: 3,\n} as const;\n\nexport type TaskPriorityType = (typeof TaskPriority)[keyof typeof TaskPriority];\n\n// ============================================\n// ZOD SCHEMAS\n// ============================================\n\nexport const TaskSchema = z.object({\n id: z.string().regex(/^T-\\d+$/),\n parent: z.string().default(''),\n title: z.string().min(1).max(500),\n status: z.enum(['todo', 'in_progress', 'done', 'cancelled']),\n assigned_to: z.string().default(''),\n claimed_at: z.string().default(''),\n priority: z.number().int().min(0).max(3).default(2),\n depends_on: z.array(z.string()).default([]),\n created: z.string(),\n updated: z.string(),\n tags: z.array(z.string()).default([]),\n branch: z.string().default(''),\n notes: z.string().default(''),\n});\n\nexport type Task = z.infer<typeof TaskSchema>;\n\n// ============================================\n// DISPLAY HELPERS\n// ============================================\n\nexport const STATUS_ICONS: Record<TaskStatusType, string> = {\n todo: '📋',\n in_progress: '🔵',\n done: '✅',\n cancelled: '⚫',\n};\n\nexport const BLOCKED_ICON = '🔴';\n\nexport const PRIORITY_ICONS: Record<number, string> = {\n 0: '🔥',\n 1: '🔴',\n 2: '🟡',\n 3: '🟢',\n};\n\nexport const PRIORITY_LABELS: Record<number, string> = {\n 0: 'Critical',\n 1: 'High',\n 2: 'Medium',\n 3: 'Low',\n};\n\n// ============================================\n// COMMAND OPTION TYPES\n// ============================================\n\nexport interface TaskListOptions {\n status?: TaskStatusType;\n parent?: string;\n json?: boolean;\n}\n\nexport interface TaskAddOptions {\n priority?: number;\n parent?: string;\n tags?: string;\n json?: boolean;\n}\n\nexport interface TaskUpdateOptions {\n status: TaskStatusType;\n json?: boolean;\n}\n\nexport interface TaskShowOptions {\n json?: boolean;\n}\n\nexport interface TaskPickOptions {\n json?: boolean;\n}\n\nexport interface TaskDoneOptions {\n json?: boolean;\n}\n\nexport interface TaskDepOptions {\n json?: boolean;\n}\n\nexport interface TaskReadyOptions {\n parent?: string;\n json?: boolean;\n}\n\nexport interface TaskSyncOptions {\n pull?: boolean;\n push?: boolean;\n}\n\nexport interface TaskSummaryOptions {\n json?: boolean;\n}\n"],"mappings":"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","names":["chalk","chalk"]}
|
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
STATUS_ICONS,
|
|
14
14
|
TaskService,
|
|
15
15
|
createTaskSummaryCommand
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-NBOT35QM.js";
|
|
17
17
|
|
|
18
18
|
// src/utils/node-version-check.ts
|
|
19
19
|
import chalk from "chalk";
|
|
@@ -149,7 +149,7 @@ import { basename as basename5 } from "path";
|
|
|
149
149
|
// package.json
|
|
150
150
|
var package_default = {
|
|
151
151
|
name: "@jvittechs/j",
|
|
152
|
-
version: "1.0.
|
|
152
|
+
version: "1.0.37",
|
|
153
153
|
description: "A unified CLI tool for JV-IT TECHS developers to manage Jai1 Framework. Supports both `j` and `jai1` commands. Please contact TeamAI for usage instructions.",
|
|
154
154
|
type: "module",
|
|
155
155
|
bin: {
|
|
@@ -10679,16 +10679,16 @@ async function handleTaskList(options) {
|
|
|
10679
10679
|
console.log(chalk21.dim("No tasks found."));
|
|
10680
10680
|
return;
|
|
10681
10681
|
}
|
|
10682
|
-
const
|
|
10682
|
+
const resolvedIds = new Set(allTasks.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id));
|
|
10683
10683
|
const header = options.parent ? `\u{1F4CB} ${options.parent} (${tasks.length} tasks)` : `\u{1F4CB} All tasks (${tasks.length})`;
|
|
10684
10684
|
console.log(chalk21.bold(header));
|
|
10685
10685
|
console.log();
|
|
10686
10686
|
for (const task of tasks) {
|
|
10687
|
-
printTaskLine(task,
|
|
10687
|
+
printTaskLine(task, resolvedIds);
|
|
10688
10688
|
}
|
|
10689
10689
|
}
|
|
10690
|
-
function printTaskLine(task,
|
|
10691
|
-
const isBlocked = task.status === "todo" && task.depends_on.length > 0 && !task.depends_on.every((id) =>
|
|
10690
|
+
function printTaskLine(task, resolvedIds) {
|
|
10691
|
+
const isBlocked = task.status === "todo" && task.depends_on.length > 0 && !task.depends_on.every((id) => resolvedIds.has(id));
|
|
10692
10692
|
const statusIcon = isBlocked ? BLOCKED_ICON : STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
10693
10693
|
const priorityIcon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
|
|
10694
10694
|
let line = ` ${statusIcon} ${chalk21.dim(task.id)} P${task.priority}${priorityIcon} ${task.title}`;
|
|
@@ -10696,7 +10696,7 @@ function printTaskLine(task, doneIds) {
|
|
|
10696
10696
|
line += chalk21.cyan(` @${task.assigned_to}`);
|
|
10697
10697
|
}
|
|
10698
10698
|
if (isBlocked) {
|
|
10699
|
-
const blockedBy = task.depends_on.filter((id) => !
|
|
10699
|
+
const blockedBy = task.depends_on.filter((id) => !resolvedIds.has(id));
|
|
10700
10700
|
line += chalk21.red(` (blocked: ${blockedBy.join(", ")})`);
|
|
10701
10701
|
}
|
|
10702
10702
|
if (task.parent) {
|
|
@@ -10752,6 +10752,16 @@ function createTaskUpdateCommand() {
|
|
|
10752
10752
|
}
|
|
10753
10753
|
const service = new TaskService();
|
|
10754
10754
|
try {
|
|
10755
|
+
if (options.status === "in_progress") {
|
|
10756
|
+
const existingTask = await service.findById(id);
|
|
10757
|
+
if (existingTask) {
|
|
10758
|
+
const { blocked, blockedBy } = await service.isBlocked(existingTask);
|
|
10759
|
+
if (blocked) {
|
|
10760
|
+
console.log(chalk23.yellow(`\u26A0\uFE0F Task ${id} is blocked by: ${blockedBy.join(", ")}`));
|
|
10761
|
+
console.log(chalk23.yellow(` Dependencies ch\u01B0a done. Ti\u1EBFp t\u1EE5c update...`));
|
|
10762
|
+
}
|
|
10763
|
+
}
|
|
10764
|
+
}
|
|
10755
10765
|
const changes = {};
|
|
10756
10766
|
if (options.status) changes.status = options.status;
|
|
10757
10767
|
if (options.notes) changes.notes = options.notes;
|
|
@@ -10948,27 +10958,36 @@ function createTaskDepCommand() {
|
|
|
10948
10958
|
import { Command as Command55 } from "commander";
|
|
10949
10959
|
import chalk28 from "chalk";
|
|
10950
10960
|
function createTaskSyncCommand() {
|
|
10951
|
-
return new Command55("sync").description("Sync tasks
|
|
10961
|
+
return new Command55("sync").description("Sync tasks via git (default: pull \u2192 push)").option("--pull", "Pull only: merge tasks from origin/jai1").option("--push", "Push only: commit and push tasks to origin/jai1").action(async (options) => {
|
|
10952
10962
|
const service = new TaskService();
|
|
10953
|
-
|
|
10954
|
-
|
|
10963
|
+
const branch = service.getSyncBranch();
|
|
10964
|
+
const doPull = options.pull || !options.pull && !options.push;
|
|
10965
|
+
const doPush = options.push || !options.pull && !options.push;
|
|
10966
|
+
if (doPull) {
|
|
10967
|
+
console.log(chalk28.dim(`\u23F3 Pulling tasks from origin/${branch}...`));
|
|
10955
10968
|
try {
|
|
10956
10969
|
const result = await service.syncPull();
|
|
10957
|
-
|
|
10970
|
+
if (result.merged > 0) {
|
|
10971
|
+
console.log(chalk28.green(` \u2193 ${result.merged} tasks merged`));
|
|
10972
|
+
} else {
|
|
10973
|
+
console.log(chalk28.dim(` \u2193 Already up to date`));
|
|
10974
|
+
}
|
|
10958
10975
|
} catch (error) {
|
|
10959
|
-
console.error(chalk28.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
|
|
10976
|
+
console.error(chalk28.red(`\u274C Pull failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
10960
10977
|
process.exit(1);
|
|
10961
10978
|
}
|
|
10962
|
-
return;
|
|
10963
10979
|
}
|
|
10964
|
-
|
|
10965
|
-
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
|
|
10969
|
-
|
|
10970
|
-
|
|
10980
|
+
if (doPush) {
|
|
10981
|
+
console.log(chalk28.dim(`\u23F3 Pushing tasks to origin/${branch}...`));
|
|
10982
|
+
try {
|
|
10983
|
+
await service.syncPush();
|
|
10984
|
+
console.log(chalk28.green(` \u2191 Tasks pushed to origin/${branch}`));
|
|
10985
|
+
} catch (error) {
|
|
10986
|
+
console.error(chalk28.red(`\u274C Push failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
10987
|
+
process.exit(1);
|
|
10988
|
+
}
|
|
10971
10989
|
}
|
|
10990
|
+
console.log(chalk28.green("\u2705 Sync complete"));
|
|
10972
10991
|
});
|
|
10973
10992
|
}
|
|
10974
10993
|
|
|
@@ -11101,7 +11120,7 @@ function createTasksCommand() {
|
|
|
11101
11120
|
cmd.addCommand(createTaskSummaryCommand());
|
|
11102
11121
|
cmd.addCommand(createTaskGuideCommand());
|
|
11103
11122
|
cmd.action(async () => {
|
|
11104
|
-
const { handleTaskSummary } = await import("./summary-
|
|
11123
|
+
const { handleTaskSummary } = await import("./summary-KPCAN522.js");
|
|
11105
11124
|
await handleTaskSummary({ json: false });
|
|
11106
11125
|
});
|
|
11107
11126
|
return cmd;
|
|
@@ -11473,7 +11492,7 @@ function createKitCreateCommand() {
|
|
|
11473
11492
|
selectedIdes = answer;
|
|
11474
11493
|
}
|
|
11475
11494
|
if (selectedIdes.length > 0) {
|
|
11476
|
-
await execa2("jai1", ["ide", "sync", "--
|
|
11495
|
+
await execa2("jai1", ["ide", "sync", "--ides", ...selectedIdes, "--type", "rules", "workflows", "commands"], {
|
|
11477
11496
|
cwd: targetDir,
|
|
11478
11497
|
stdio: "inherit"
|
|
11479
11498
|
});
|