@trail-pm/cli 0.1.9 → 0.2.2
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/README.md +6 -13
- package/dist/{chunk-TDIYLUKH.js → chunk-YSTYANXJ.js} +220 -42
- package/dist/chunk-YSTYANXJ.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/lib.d.ts +146 -1
- package/dist/lib.js +355 -0
- package/dist/lib.js.map +1 -1
- package/dist/{run-mcp-server-HD7I5M7Z.js → run-mcp-server-NKYRI73A.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-TDIYLUKH.js.map +0 -1
- /package/dist/{run-mcp-server-HD7I5M7Z.js.map → run-mcp-server-NKYRI73A.js.map} +0 -0
package/dist/lib.js
CHANGED
|
@@ -300,15 +300,370 @@ var TrailConfigSchema = z3.object({
|
|
|
300
300
|
/** Last successful full sync; useful for future `trail status` and similar. */
|
|
301
301
|
last_full_sync_at: z3.string().datetime({ offset: true }).optional()
|
|
302
302
|
}).strict();
|
|
303
|
+
|
|
304
|
+
// src/core/auth.ts
|
|
305
|
+
import * as childProcess from "child_process";
|
|
306
|
+
var AUTH_HINT = "Set GITHUB_TOKEN or install gh and run gh auth login";
|
|
307
|
+
function authRequired(message) {
|
|
308
|
+
return {
|
|
309
|
+
ok: false,
|
|
310
|
+
error: {
|
|
311
|
+
code: "AUTH_REQUIRED",
|
|
312
|
+
message,
|
|
313
|
+
hint: AUTH_HINT
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function resolveGitHubToken(env) {
|
|
318
|
+
const e = env ?? process.env;
|
|
319
|
+
const fromEnv = e.GITHUB_TOKEN;
|
|
320
|
+
if (typeof fromEnv === "string" && fromEnv.trim() !== "") {
|
|
321
|
+
return { ok: true, token: fromEnv.trim() };
|
|
322
|
+
}
|
|
323
|
+
try {
|
|
324
|
+
const out = childProcess.execFileSync("gh", ["auth", "token"], {
|
|
325
|
+
encoding: "utf-8"
|
|
326
|
+
});
|
|
327
|
+
const token = out.trim();
|
|
328
|
+
if (token === "") {
|
|
329
|
+
return authRequired("GitHub CLI returned an empty token");
|
|
330
|
+
}
|
|
331
|
+
return { ok: true, token };
|
|
332
|
+
} catch {
|
|
333
|
+
return authRequired(
|
|
334
|
+
"No GitHub token found; could not read from environment or gh CLI"
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// src/core/github-client.ts
|
|
340
|
+
var USER_AGENT = "trail-cli/0.0.1";
|
|
341
|
+
function normalizeBaseUrl(baseUrl) {
|
|
342
|
+
return baseUrl.replace(/\/$/, "");
|
|
343
|
+
}
|
|
344
|
+
var GitHubClient = class {
|
|
345
|
+
token;
|
|
346
|
+
baseUrl;
|
|
347
|
+
constructor(token, baseUrl = "https://api.github.com") {
|
|
348
|
+
this.token = token;
|
|
349
|
+
this.baseUrl = normalizeBaseUrl(baseUrl);
|
|
350
|
+
}
|
|
351
|
+
async request(method, path6, body) {
|
|
352
|
+
const url = `${this.baseUrl}${path6.startsWith("/") ? path6 : `/${path6}`}`;
|
|
353
|
+
const headers = new Headers({
|
|
354
|
+
Authorization: `Bearer ${this.token}`,
|
|
355
|
+
Accept: "application/vnd.github+json",
|
|
356
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
357
|
+
"User-Agent": USER_AGENT
|
|
358
|
+
});
|
|
359
|
+
if (body !== void 0) {
|
|
360
|
+
headers.set("Content-Type", "application/json");
|
|
361
|
+
}
|
|
362
|
+
return fetch(url, {
|
|
363
|
+
method,
|
|
364
|
+
headers,
|
|
365
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
async parseJson(response) {
|
|
369
|
+
const text = await response.text();
|
|
370
|
+
if (!response.ok) {
|
|
371
|
+
const snippet = text.slice(0, 200);
|
|
372
|
+
throw new Error(`GitHub API ${response.status}: ${snippet}`);
|
|
373
|
+
}
|
|
374
|
+
return JSON.parse(text);
|
|
375
|
+
}
|
|
376
|
+
async listIssues(owner, repo, params) {
|
|
377
|
+
const search = new URLSearchParams({
|
|
378
|
+
state: params.state,
|
|
379
|
+
per_page: String(params.per_page),
|
|
380
|
+
page: String(params.page)
|
|
381
|
+
});
|
|
382
|
+
const path6 = `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues?${search}`;
|
|
383
|
+
const response = await this.request("GET", path6);
|
|
384
|
+
return this.parseJson(response);
|
|
385
|
+
}
|
|
386
|
+
async getIssue(owner, repo, issueNumber) {
|
|
387
|
+
const path6 = `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}`;
|
|
388
|
+
const response = await this.request("GET", path6);
|
|
389
|
+
return this.parseJson(response);
|
|
390
|
+
}
|
|
391
|
+
async updateIssue(owner, repo, issueNumber, patch) {
|
|
392
|
+
const path6 = `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}`;
|
|
393
|
+
const response = await this.request("PATCH", path6, patch);
|
|
394
|
+
return this.parseJson(response);
|
|
395
|
+
}
|
|
396
|
+
async createIssueComment(owner, repo, issueNumber, body) {
|
|
397
|
+
const path6 = `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}/comments`;
|
|
398
|
+
const response = await this.request("POST", path6, { body });
|
|
399
|
+
return this.parseJson(response);
|
|
400
|
+
}
|
|
401
|
+
/** Create a new issue. Returns the created issue (same shape as list/get). */
|
|
402
|
+
async createIssue(owner, repo, input) {
|
|
403
|
+
const path6 = `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues`;
|
|
404
|
+
const response = await this.request("POST", path6, {
|
|
405
|
+
title: input.title,
|
|
406
|
+
body: input.body ?? "",
|
|
407
|
+
labels: input.labels ?? []
|
|
408
|
+
});
|
|
409
|
+
return this.parseJson(response);
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// src/core/github-mapper.ts
|
|
414
|
+
function statusFromIssue(issue, existing) {
|
|
415
|
+
if (issue.state === "closed") {
|
|
416
|
+
return "done";
|
|
417
|
+
}
|
|
418
|
+
const prev = existing?.status;
|
|
419
|
+
if (prev === "in_progress" || prev === "in_review") {
|
|
420
|
+
return prev;
|
|
421
|
+
}
|
|
422
|
+
return "todo";
|
|
423
|
+
}
|
|
424
|
+
function parseIssueTimestamp(issue, fallback) {
|
|
425
|
+
if (issue.updated_at.trim() === "") {
|
|
426
|
+
return fallback.toISOString();
|
|
427
|
+
}
|
|
428
|
+
return issue.updated_at;
|
|
429
|
+
}
|
|
430
|
+
function issueToTask(issue, existing, now) {
|
|
431
|
+
const updatedAt = parseIssueTimestamp(issue, now);
|
|
432
|
+
const createdAt = existing?.created_at ?? (issue.updated_at.trim() !== "" ? issue.updated_at : now.toISOString());
|
|
433
|
+
const raw = {
|
|
434
|
+
id: String(issue.number),
|
|
435
|
+
title: issue.title,
|
|
436
|
+
description: issue.body ?? "",
|
|
437
|
+
status: statusFromIssue(issue, existing),
|
|
438
|
+
type: existing?.type ?? "feature",
|
|
439
|
+
labels: issue.labels.map((l) => l.name),
|
|
440
|
+
assignee: issue.assignee?.login,
|
|
441
|
+
milestone: issue.milestone?.title,
|
|
442
|
+
depends_on: existing?.depends_on ?? [],
|
|
443
|
+
blocks: existing?.blocks ?? [],
|
|
444
|
+
refs: existing?.refs ?? [],
|
|
445
|
+
ai: existing?.ai,
|
|
446
|
+
branch: existing?.branch,
|
|
447
|
+
estimate: existing?.estimate,
|
|
448
|
+
priority: existing?.priority,
|
|
449
|
+
parent: existing?.parent,
|
|
450
|
+
due_date: existing?.due_date,
|
|
451
|
+
start_date: existing?.start_date,
|
|
452
|
+
github: {
|
|
453
|
+
issue_number: issue.number,
|
|
454
|
+
synced_at: now.toISOString(),
|
|
455
|
+
url: issue.html_url
|
|
456
|
+
},
|
|
457
|
+
created_at: createdAt,
|
|
458
|
+
updated_at: updatedAt
|
|
459
|
+
};
|
|
460
|
+
return TaskSchema.parse(raw);
|
|
461
|
+
}
|
|
462
|
+
function taskToIssueUpdate(task) {
|
|
463
|
+
const labels = [...task.labels];
|
|
464
|
+
if (task.priority) {
|
|
465
|
+
const priorityLabel = `priority:${task.priority}`;
|
|
466
|
+
if (!labels.includes(priorityLabel)) {
|
|
467
|
+
labels.push(priorityLabel);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
const state = task.status === "done" || task.status === "cancelled" ? "closed" : "open";
|
|
471
|
+
return {
|
|
472
|
+
title: task.title,
|
|
473
|
+
body: task.description ?? "",
|
|
474
|
+
state,
|
|
475
|
+
labels
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// src/core/sync.ts
|
|
480
|
+
import fs4 from "fs";
|
|
481
|
+
import path4 from "path";
|
|
482
|
+
|
|
483
|
+
// src/core/relevant-tasks.ts
|
|
484
|
+
var NON_TERMINAL = [
|
|
485
|
+
"draft",
|
|
486
|
+
"todo",
|
|
487
|
+
"in_progress",
|
|
488
|
+
"in_review"
|
|
489
|
+
];
|
|
490
|
+
function isNonTerminalStatus(status) {
|
|
491
|
+
return NON_TERMINAL.includes(status);
|
|
492
|
+
}
|
|
493
|
+
function isActiveForRelevance(task) {
|
|
494
|
+
return isNonTerminalStatus(task.status);
|
|
495
|
+
}
|
|
496
|
+
function computeRelevantTaskIds(tasks) {
|
|
497
|
+
const byId = /* @__PURE__ */ new Map();
|
|
498
|
+
for (const t of tasks) {
|
|
499
|
+
byId.set(t.id, t);
|
|
500
|
+
}
|
|
501
|
+
const relevant = /* @__PURE__ */ new Set();
|
|
502
|
+
const queue = [];
|
|
503
|
+
for (const t of tasks) {
|
|
504
|
+
if (isActiveForRelevance(t)) {
|
|
505
|
+
if (!relevant.has(t.id)) {
|
|
506
|
+
relevant.add(t.id);
|
|
507
|
+
queue.push(t.id);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
function enqueue(id) {
|
|
512
|
+
if (relevant.has(id)) return;
|
|
513
|
+
relevant.add(id);
|
|
514
|
+
queue.push(id);
|
|
515
|
+
}
|
|
516
|
+
while (queue.length > 0) {
|
|
517
|
+
const id = queue.pop();
|
|
518
|
+
const task = byId.get(id);
|
|
519
|
+
if (!task) continue;
|
|
520
|
+
for (const d of task.depends_on) {
|
|
521
|
+
if (byId.has(d)) enqueue(d);
|
|
522
|
+
}
|
|
523
|
+
for (const b of task.blocks) {
|
|
524
|
+
if (byId.has(b)) enqueue(b);
|
|
525
|
+
}
|
|
526
|
+
if (task.parent != null && task.parent !== "" && byId.has(task.parent)) {
|
|
527
|
+
enqueue(task.parent);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return relevant;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// src/core/sync.ts
|
|
534
|
+
var ISSUES_PER_PAGE = 100;
|
|
535
|
+
async function pullSync(options) {
|
|
536
|
+
const { client, owner, repo, tasksDir } = options;
|
|
537
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
538
|
+
const onProgress = options.onProgress;
|
|
539
|
+
let page = 1;
|
|
540
|
+
let issuesSoFar = 0;
|
|
541
|
+
for (; ; ) {
|
|
542
|
+
const issues = await client.listIssues(owner, repo, {
|
|
543
|
+
state: "all",
|
|
544
|
+
per_page: ISSUES_PER_PAGE,
|
|
545
|
+
page
|
|
546
|
+
});
|
|
547
|
+
if (issues.length === 0) {
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
for (const issue of issues) {
|
|
551
|
+
const filePath = path4.join(tasksDir, `${issue.number}.json`);
|
|
552
|
+
let existing = null;
|
|
553
|
+
if (fs4.existsSync(filePath)) {
|
|
554
|
+
existing = readTaskFile(filePath);
|
|
555
|
+
}
|
|
556
|
+
const task = issueToTask(issue, existing, now);
|
|
557
|
+
writeTaskFile(filePath, task);
|
|
558
|
+
}
|
|
559
|
+
issuesSoFar += issues.length;
|
|
560
|
+
onProgress?.({
|
|
561
|
+
phase: "pull",
|
|
562
|
+
page,
|
|
563
|
+
issuesInPage: issues.length,
|
|
564
|
+
issuesSoFar
|
|
565
|
+
});
|
|
566
|
+
if (issues.length < ISSUES_PER_PAGE) {
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
page += 1;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
function isLinkedTask(task) {
|
|
573
|
+
return task.github != null && typeof task.github === "object";
|
|
574
|
+
}
|
|
575
|
+
async function pushSync(options) {
|
|
576
|
+
const { client, owner, repo, tasks } = options;
|
|
577
|
+
const only = options.onlyTaskIds;
|
|
578
|
+
const onProgress = options.onProgress;
|
|
579
|
+
const toPush = tasks.filter((task) => {
|
|
580
|
+
if (task.status === "draft") {
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
if (!isLinkedTask(task)) {
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
if (only !== void 0 && !only.has(task.id)) {
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
return true;
|
|
590
|
+
});
|
|
591
|
+
let index = 0;
|
|
592
|
+
for (const task of toPush) {
|
|
593
|
+
if (!isLinkedTask(task)) {
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
index += 1;
|
|
597
|
+
onProgress?.({
|
|
598
|
+
phase: "push",
|
|
599
|
+
index,
|
|
600
|
+
total: toPush.length,
|
|
601
|
+
taskId: task.id
|
|
602
|
+
});
|
|
603
|
+
const patch = taskToIssueUpdate(task);
|
|
604
|
+
await client.updateIssue(owner, repo, task.github.issue_number, patch);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
async function fullSync(options) {
|
|
608
|
+
const { client, owner, repo, tasksDir, snapshotPath, now } = options;
|
|
609
|
+
await pullSync({ client, owner, repo, tasksDir, now, onProgress: options.onProgress });
|
|
610
|
+
const tasks = loadAllTasks(tasksDir);
|
|
611
|
+
const relevant = computeRelevantTaskIds(tasks);
|
|
612
|
+
await pushSync({
|
|
613
|
+
client,
|
|
614
|
+
owner,
|
|
615
|
+
repo,
|
|
616
|
+
tasks,
|
|
617
|
+
onlyTaskIds: relevant,
|
|
618
|
+
onProgress: options.onProgress
|
|
619
|
+
});
|
|
620
|
+
const snapshot = compileSnapshot(tasks, now);
|
|
621
|
+
writeSnapshot(snapshotPath, snapshot);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// src/core/config-update.ts
|
|
625
|
+
import fs5 from "fs";
|
|
626
|
+
function writeLastFullSyncAt(paths, iso) {
|
|
627
|
+
const raw = fs5.readFileSync(paths.configPath, "utf-8");
|
|
628
|
+
const config = TrailConfigSchema.parse(JSON.parse(raw));
|
|
629
|
+
const next = { ...config, last_full_sync_at: iso };
|
|
630
|
+
fs5.writeFileSync(paths.configPath, `${JSON.stringify(next, null, 2)}
|
|
631
|
+
`, "utf-8");
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// src/core/link-draft-issue.ts
|
|
635
|
+
import fs6 from "fs";
|
|
636
|
+
import path5 from "path";
|
|
637
|
+
async function linkDraftToNewGitHubIssue(options) {
|
|
638
|
+
const { client, owner, repo, draft, draftFilePath, tasksDir, now } = options;
|
|
639
|
+
const issue = await client.createIssue(owner, repo, {
|
|
640
|
+
title: draft.title,
|
|
641
|
+
body: draft.description ?? "",
|
|
642
|
+
labels: draft.labels
|
|
643
|
+
});
|
|
644
|
+
const promoted = issueToTask(issue, draft, now);
|
|
645
|
+
fs6.unlinkSync(draftFilePath);
|
|
646
|
+
const newPath = path5.join(tasksDir, `${issue.number}.json`);
|
|
647
|
+
writeTaskFile(newPath, promoted);
|
|
648
|
+
return promoted;
|
|
649
|
+
}
|
|
303
650
|
export {
|
|
651
|
+
GitHubClient,
|
|
304
652
|
TaskSchema,
|
|
305
653
|
TrailConfigSchema,
|
|
306
654
|
findTaskFileById,
|
|
307
655
|
findTrailRoot,
|
|
656
|
+
fullSync,
|
|
308
657
|
generateDraftId,
|
|
658
|
+
issueToTask,
|
|
659
|
+
linkDraftToNewGitHubIssue,
|
|
309
660
|
loadAllTasks,
|
|
661
|
+
pullSync,
|
|
310
662
|
rebuildSnapshot,
|
|
663
|
+
resolveGitHubToken,
|
|
664
|
+
taskToIssueUpdate,
|
|
311
665
|
trailPaths,
|
|
666
|
+
writeLastFullSyncAt,
|
|
312
667
|
writeTaskFile
|
|
313
668
|
};
|
|
314
669
|
//# sourceMappingURL=lib.js.map
|
package/dist/lib.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/paths.ts","../src/core/task-store.ts","../src/schemas/task.ts","../src/core/compile-snapshot.ts","../src/schemas/snapshot.ts","../src/core/rebuild-snapshot.ts","../src/core/draft-id.ts","../src/schemas/config.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\nconst MAX_TRAIL_ROOT_WALK = 20;\n\n/**\n * Walks upward from `startDir` looking for a Trail project root: a directory\n * that contains `.trail/config.json`.\n *\n * @returns The **project root** directory (the parent of the `.trail` folder),\n * or `null` if none is found within the walk limit. This is not the path to\n * `.trail` itself — use {@link trailPaths} for `.trail`-relative paths.\n */\nexport function findTrailRoot(startDir: string): string | null {\n let dir = path.resolve(startDir);\n for (let i = 0; i < MAX_TRAIL_ROOT_WALK; i++) {\n const configPath = path.join(dir, \".trail\", \"config.json\");\n if (fs.existsSync(configPath)) {\n return dir;\n }\n const parent = path.dirname(dir);\n if (parent === dir) {\n break;\n }\n dir = parent;\n }\n return null;\n}\n\nexport interface TrailPaths {\n root: string;\n trailDir: string;\n tasksDir: string;\n configPath: string;\n snapshotPath: string;\n gitignorePath: string;\n}\n\n/** Resolved paths under a Trail project root (the directory that contains `.trail`). */\nexport function trailPaths(root: string): TrailPaths {\n const trailDir = path.join(root, \".trail\");\n return {\n root,\n trailDir,\n tasksDir: path.join(trailDir, \"tasks\"),\n configPath: path.join(trailDir, \"config.json\"),\n snapshotPath: path.join(trailDir, \"snapshot.json\"),\n gitignorePath: path.join(trailDir, \".gitignore\"),\n };\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\nimport type { TrailError } from \"./errors.js\";\nimport { TaskSchema, type Task } from \"../schemas/task.js\";\n\n/** Thrown by task-store when validation fails; inspect `.trailError` for `VALIDATION_FAILED`. */\nexport type TaskStoreError = Error & { trailError: TrailError };\n\nexport function toValidationError(\n zodError: z.ZodError,\n context?: string,\n): Extract<TrailError, { code: \"VALIDATION_FAILED\" }> {\n const issues = zodError.issues.map(\n (i) => `${i.path.length ? i.path.join(\".\") : \"(root)\"}: ${i.message}`,\n );\n return {\n code: \"VALIDATION_FAILED\",\n message: context\n ? `Task validation failed (${context})`\n : \"Task validation failed\",\n details: zodError.message,\n issues,\n };\n}\n\nfunction throwValidationFailed(\n err: Extract<TrailError, { code: \"VALIDATION_FAILED\" }>,\n): never {\n const e = new Error(`[VALIDATION_FAILED] ${err.message}`) as TaskStoreError;\n e.name = \"TrailError\";\n e.trailError = err;\n throw e;\n}\n\nexport function isTaskStoreValidationError(\n e: unknown,\n): e is TaskStoreError & { trailError: Extract<TrailError, { code: \"VALIDATION_FAILED\" }> } {\n return (\n e instanceof Error &&\n \"trailError\" in e &&\n typeof (e as TaskStoreError).trailError === \"object\" &&\n (e as TaskStoreError).trailError !== null &&\n \"code\" in (e as TaskStoreError).trailError &&\n (e as TaskStoreError).trailError.code === \"VALIDATION_FAILED\"\n );\n}\n\n/**\n * Lists `*.json` basenames under `tasksDir`, excluding `snapshot.json`, sorted lexicographically.\n */\nexport function listTaskFiles(tasksDir: string): string[] {\n const names = fs.readdirSync(tasksDir);\n return names\n .filter(\n (n) => n.endsWith(\".json\") && n !== \"snapshot.json\",\n )\n .sort((a, b) => a.localeCompare(b));\n}\n\nexport function readTaskFile(filePath: string): Task {\n let raw: unknown;\n try {\n raw = JSON.parse(fs.readFileSync(filePath, \"utf8\"));\n } catch (e) {\n if (e instanceof SyntaxError) {\n throwValidationFailed({\n code: \"VALIDATION_FAILED\",\n message: \"Invalid JSON in task file\",\n details: filePath,\n });\n }\n throw e;\n }\n\n const parsed = TaskSchema.safeParse(raw);\n if (!parsed.success) {\n throwValidationFailed(toValidationError(parsed.error, filePath));\n }\n return parsed.data;\n}\n\nexport function writeTaskFile(filePath: string, task: Task): void {\n const parsed = TaskSchema.safeParse(task);\n if (!parsed.success) {\n throwValidationFailed(toValidationError(parsed.error, filePath));\n }\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n const body = `${JSON.stringify(parsed.data, null, 2)}\\n`;\n fs.writeFileSync(filePath, body, \"utf8\");\n}\n\n/**\n * Loads all task JSON files from `tasksDir`. Returns an empty array if the directory is missing.\n * Stops on the first file that fails to parse or validate.\n */\nexport function loadAllTasks(tasksDir: string): Task[] {\n if (!fs.existsSync(tasksDir)) {\n return [];\n }\n const files = listTaskFiles(tasksDir);\n return files.map((name) => readTaskFile(path.join(tasksDir, name)));\n}\n\n/**\n * Resolves the task file for `id`: prefers `${id}.json` when it exists and its `id` matches;\n * otherwise scans `*.json` for a task whose `id` field equals `id`.\n */\nexport function findTaskFileById(\n tasksDir: string,\n id: string,\n): { filePath: string; task: Task } | null {\n if (!fs.existsSync(tasksDir)) {\n return null;\n }\n const direct = path.join(tasksDir, `${id}.json`);\n if (fs.existsSync(direct)) {\n const task = readTaskFile(direct);\n if (task.id === id) {\n return { filePath: direct, task };\n }\n }\n for (const name of listTaskFiles(tasksDir)) {\n const filePath = path.join(tasksDir, name);\n const task = readTaskFile(filePath);\n if (task.id === id) {\n return { filePath, task };\n }\n }\n return null;\n}\n","import { z } from \"zod\";\n\nexport const TaskStatusSchema = z.enum([\n \"draft\",\n \"todo\",\n \"in_progress\",\n \"in_review\",\n \"done\",\n \"cancelled\",\n]);\n\nexport type TaskStatus = z.infer<typeof TaskStatusSchema>;\n\n/** YYYY-MM-DD (ISO 8601 calendar date). */\nconst isoDateStringSchema = z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"Expected ISO date string (YYYY-MM-DD)\");\n\n/**\n * GitHub issue link metadata. `null` means the task is not linked to an issue (e.g. local draft).\n */\nconst TaskGitHubSchema = z\n .object({\n issue_number: z.number().int(),\n synced_at: z.string().datetime({ offset: true }),\n url: z.string().url(),\n })\n .nullable();\n\nconst TaskAiSchema = z\n .object({\n summary: z.string().optional(),\n acceptance_criteria: z.array(z.string()).optional(),\n implementation_context: z.array(z.string()).optional(),\n test_strategy: z.array(z.string()).optional(),\n constraints: z.array(z.string()).optional(),\n })\n .strict()\n .optional();\n\nexport const TaskSchema = z\n .object({\n id: z.string(),\n title: z.string(),\n description: z.string().optional(),\n status: TaskStatusSchema,\n priority: z.enum([\"p0\", \"p1\", \"p2\", \"p3\"]).optional(),\n type: z.enum([\"feature\", \"bug\", \"chore\", \"epic\"]),\n assignee: z.string().optional(),\n milestone: z.string().optional(),\n branch: z.string().optional(),\n labels: z.array(z.string()).default([]),\n parent: z.string().nullable().optional(),\n depends_on: z.array(z.string()).default([]),\n blocks: z.array(z.string()).default([]),\n due_date: isoDateStringSchema.optional(),\n start_date: isoDateStringSchema.optional(),\n estimate: z.enum([\"xs\", \"sm\", \"md\", \"lg\", \"xl\"]).optional(),\n github: TaskGitHubSchema.optional(),\n refs: z\n .array(\n z\n .object({\n type: z.string(),\n path: z.string(),\n })\n .strict(),\n )\n .default([]),\n ai: TaskAiSchema,\n created_at: z.string().datetime({ offset: true }),\n updated_at: z.string().datetime({ offset: true }),\n })\n .strict();\n\nexport type Task = z.infer<typeof TaskSchema>;\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { Task } from \"../schemas/task.js\";\nimport { SnapshotSchema, type Snapshot } from \"../schemas/snapshot.js\";\n\nconst UNKNOWN_DEPENDENCY = \"UNKNOWN_DEPENDENCY\";\n\n/** Warning code when `depends_on` forms a directed cycle. */\nexport const DEPENDENCY_CYCLE = \"DEPENDENCY_CYCLE\";\n\n/**\n * Finds directed cycles in the dependency graph: edge `u → v` when task `u` lists `v` in `depends_on`.\n * Only vertices in `taskIds` participate; edges to ids outside `taskIds` are ignored here (handled as warnings in `compileSnapshot`).\n */\nexport function detectCycles(\n taskIds: Set<string>,\n deps: Map<string, string[]>,\n): string[][] {\n const cycles: string[][] = [];\n /** Finished nodes (BLACK). */\n const visited = new Set<string>();\n /** Nodes on the current DFS path (GRAY). */\n const visiting = new Set<string>();\n const stack: string[] = [];\n\n function recordCycle(fromIndex: number): void {\n cycles.push(stack.slice(fromIndex));\n }\n\n function dfs(u: string): void {\n visiting.add(u);\n stack.push(u);\n\n for (const v of deps.get(u) ?? []) {\n if (!taskIds.has(v)) {\n continue;\n }\n if (visiting.has(v)) {\n const i = stack.indexOf(v);\n if (i !== -1) {\n recordCycle(i);\n }\n continue;\n }\n if (!visited.has(v)) {\n dfs(v);\n }\n }\n\n stack.pop();\n visiting.delete(u);\n visited.add(u);\n }\n\n for (const id of taskIds) {\n if (!visited.has(id)) {\n dfs(id);\n }\n }\n\n return cycles;\n}\n\nfunction formatCycle(nodes: string[]): string {\n return nodes.join(\" → \");\n}\n\n/**\n * Builds a snapshot from tasks, with warnings for unknown dependency ids and dependency cycles.\n */\nexport function compileSnapshot(tasks: Task[], now?: Date): Snapshot {\n const generatedAt = (now ?? new Date()).toISOString();\n const taskList = [...tasks];\n const taskIds = new Set(taskList.map((t) => t.id));\n const deps = new Map<string, string[]>();\n for (const t of taskList) {\n deps.set(t.id, t.depends_on ?? []);\n }\n\n const warnings: Snapshot[\"warnings\"] = [];\n\n for (const t of taskList) {\n for (const depId of t.depends_on ?? []) {\n if (!taskIds.has(depId)) {\n warnings.push({\n code: UNKNOWN_DEPENDENCY,\n message: `Task \"${t.id}\" depends on unknown task id \"${depId}\"`,\n taskId: t.id,\n });\n }\n }\n }\n\n for (const cycle of detectCycles(taskIds, deps)) {\n warnings.push({\n code: DEPENDENCY_CYCLE,\n message: `Dependency cycle: ${formatCycle(cycle)}`,\n taskId: cycle[0],\n });\n }\n\n return {\n generated_at: generatedAt,\n tasks: taskList,\n warnings,\n };\n}\n\nexport function writeSnapshot(snapshotPath: string, snapshot: Snapshot): void {\n const parsed = SnapshotSchema.parse(snapshot);\n const dir = path.dirname(snapshotPath);\n fs.mkdirSync(dir, { recursive: true });\n const body = `${JSON.stringify(parsed, null, 2)}\\n`;\n fs.writeFileSync(snapshotPath, body, \"utf8\");\n}\n","import { z } from \"zod\";\n\nimport { TaskSchema } from \"./task.js\";\n\nexport const SnapshotSchema = z\n .object({\n generated_at: z.string().datetime({ offset: true }),\n tasks: z.array(TaskSchema),\n warnings: z.array(\n z\n .object({\n code: z.string(),\n message: z.string(),\n taskId: z.string().optional(),\n })\n .strict(),\n ),\n })\n .strict();\n\nexport type Snapshot = z.infer<typeof SnapshotSchema>;\n","import { compileSnapshot, writeSnapshot } from \"./compile-snapshot.js\";\nimport { loadAllTasks } from \"./task-store.js\";\nimport type { TrailPaths } from \"./paths.js\";\n\n/** Recompiles `.trail/snapshot.json` from all task files. */\nexport function rebuildSnapshot(paths: TrailPaths, now = new Date()): void {\n const tasks = loadAllTasks(paths.tasksDir);\n const snapshot = compileSnapshot(tasks, now);\n writeSnapshot(paths.snapshotPath, snapshot);\n}\n","import { randomBytes } from \"node:crypto\";\n\n/** Stable draft task id prefix + random suffix (e.g. `draft-a1b2c3d4`). */\nexport function generateDraftId(): string {\n return `draft-${randomBytes(4).toString(\"hex\")}`;\n}\n","import { z } from \"zod\";\n\nexport const TrailConfigSchema = z\n .object({\n github: z\n .object({\n owner: z.string(),\n repo: z.string(),\n })\n .strict(),\n sync: z\n .object({\n preset: z.enum([\"collaborative\", \"solo\", \"offline\"]),\n auto_sync_on_command: z.boolean(),\n ui_poll_interval_seconds: z.number(),\n ui_idle_backoff: z.boolean(),\n })\n .strict(),\n /** Last successful full sync; useful for future `trail status` and similar. */\n last_full_sync_at: z.string().datetime({ offset: true }).optional(),\n })\n .strict();\n\nexport type TrailConfig = z.infer<typeof TrailConfigSchema>;\n"],"mappings":"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","names":["fs","path","fs","path","fs","path","z","z","path","fs","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/paths.ts","../src/core/task-store.ts","../src/schemas/task.ts","../src/core/compile-snapshot.ts","../src/schemas/snapshot.ts","../src/core/rebuild-snapshot.ts","../src/core/draft-id.ts","../src/schemas/config.ts","../src/core/auth.ts","../src/core/github-client.ts","../src/core/github-mapper.ts","../src/core/sync.ts","../src/core/relevant-tasks.ts","../src/core/config-update.ts","../src/core/link-draft-issue.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\nconst MAX_TRAIL_ROOT_WALK = 20;\n\n/**\n * Walks upward from `startDir` looking for a Trail project root: a directory\n * that contains `.trail/config.json`.\n *\n * @returns The **project root** directory (the parent of the `.trail` folder),\n * or `null` if none is found within the walk limit. This is not the path to\n * `.trail` itself — use {@link trailPaths} for `.trail`-relative paths.\n */\nexport function findTrailRoot(startDir: string): string | null {\n let dir = path.resolve(startDir);\n for (let i = 0; i < MAX_TRAIL_ROOT_WALK; i++) {\n const configPath = path.join(dir, \".trail\", \"config.json\");\n if (fs.existsSync(configPath)) {\n return dir;\n }\n const parent = path.dirname(dir);\n if (parent === dir) {\n break;\n }\n dir = parent;\n }\n return null;\n}\n\nexport interface TrailPaths {\n root: string;\n trailDir: string;\n tasksDir: string;\n configPath: string;\n snapshotPath: string;\n gitignorePath: string;\n}\n\n/** Resolved paths under a Trail project root (the directory that contains `.trail`). */\nexport function trailPaths(root: string): TrailPaths {\n const trailDir = path.join(root, \".trail\");\n return {\n root,\n trailDir,\n tasksDir: path.join(trailDir, \"tasks\"),\n configPath: path.join(trailDir, \"config.json\"),\n snapshotPath: path.join(trailDir, \"snapshot.json\"),\n gitignorePath: path.join(trailDir, \".gitignore\"),\n };\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\nimport type { TrailError } from \"./errors.js\";\nimport { TaskSchema, type Task } from \"../schemas/task.js\";\n\n/** Thrown by task-store when validation fails; inspect `.trailError` for `VALIDATION_FAILED`. */\nexport type TaskStoreError = Error & { trailError: TrailError };\n\nexport function toValidationError(\n zodError: z.ZodError,\n context?: string,\n): Extract<TrailError, { code: \"VALIDATION_FAILED\" }> {\n const issues = zodError.issues.map(\n (i) => `${i.path.length ? i.path.join(\".\") : \"(root)\"}: ${i.message}`,\n );\n return {\n code: \"VALIDATION_FAILED\",\n message: context\n ? `Task validation failed (${context})`\n : \"Task validation failed\",\n details: zodError.message,\n issues,\n };\n}\n\nfunction throwValidationFailed(\n err: Extract<TrailError, { code: \"VALIDATION_FAILED\" }>,\n): never {\n const e = new Error(`[VALIDATION_FAILED] ${err.message}`) as TaskStoreError;\n e.name = \"TrailError\";\n e.trailError = err;\n throw e;\n}\n\nexport function isTaskStoreValidationError(\n e: unknown,\n): e is TaskStoreError & { trailError: Extract<TrailError, { code: \"VALIDATION_FAILED\" }> } {\n return (\n e instanceof Error &&\n \"trailError\" in e &&\n typeof (e as TaskStoreError).trailError === \"object\" &&\n (e as TaskStoreError).trailError !== null &&\n \"code\" in (e as TaskStoreError).trailError &&\n (e as TaskStoreError).trailError.code === \"VALIDATION_FAILED\"\n );\n}\n\n/**\n * Lists `*.json` basenames under `tasksDir`, excluding `snapshot.json`, sorted lexicographically.\n */\nexport function listTaskFiles(tasksDir: string): string[] {\n const names = fs.readdirSync(tasksDir);\n return names\n .filter(\n (n) => n.endsWith(\".json\") && n !== \"snapshot.json\",\n )\n .sort((a, b) => a.localeCompare(b));\n}\n\nexport function readTaskFile(filePath: string): Task {\n let raw: unknown;\n try {\n raw = JSON.parse(fs.readFileSync(filePath, \"utf8\"));\n } catch (e) {\n if (e instanceof SyntaxError) {\n throwValidationFailed({\n code: \"VALIDATION_FAILED\",\n message: \"Invalid JSON in task file\",\n details: filePath,\n });\n }\n throw e;\n }\n\n const parsed = TaskSchema.safeParse(raw);\n if (!parsed.success) {\n throwValidationFailed(toValidationError(parsed.error, filePath));\n }\n return parsed.data;\n}\n\nexport function writeTaskFile(filePath: string, task: Task): void {\n const parsed = TaskSchema.safeParse(task);\n if (!parsed.success) {\n throwValidationFailed(toValidationError(parsed.error, filePath));\n }\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n const body = `${JSON.stringify(parsed.data, null, 2)}\\n`;\n fs.writeFileSync(filePath, body, \"utf8\");\n}\n\n/**\n * Loads all task JSON files from `tasksDir`. Returns an empty array if the directory is missing.\n * Stops on the first file that fails to parse or validate.\n */\nexport function loadAllTasks(tasksDir: string): Task[] {\n if (!fs.existsSync(tasksDir)) {\n return [];\n }\n const files = listTaskFiles(tasksDir);\n return files.map((name) => readTaskFile(path.join(tasksDir, name)));\n}\n\n/**\n * Resolves the task file for `id`: prefers `${id}.json` when it exists and its `id` matches;\n * otherwise scans `*.json` for a task whose `id` field equals `id`.\n */\nexport function findTaskFileById(\n tasksDir: string,\n id: string,\n): { filePath: string; task: Task } | null {\n if (!fs.existsSync(tasksDir)) {\n return null;\n }\n const direct = path.join(tasksDir, `${id}.json`);\n if (fs.existsSync(direct)) {\n const task = readTaskFile(direct);\n if (task.id === id) {\n return { filePath: direct, task };\n }\n }\n for (const name of listTaskFiles(tasksDir)) {\n const filePath = path.join(tasksDir, name);\n const task = readTaskFile(filePath);\n if (task.id === id) {\n return { filePath, task };\n }\n }\n return null;\n}\n","import { z } from \"zod\";\n\nexport const TaskStatusSchema = z.enum([\n \"draft\",\n \"todo\",\n \"in_progress\",\n \"in_review\",\n \"done\",\n \"cancelled\",\n]);\n\nexport type TaskStatus = z.infer<typeof TaskStatusSchema>;\n\n/** YYYY-MM-DD (ISO 8601 calendar date). */\nconst isoDateStringSchema = z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"Expected ISO date string (YYYY-MM-DD)\");\n\n/**\n * GitHub issue link metadata. `null` means the task is not linked to an issue (e.g. local draft).\n */\nconst TaskGitHubSchema = z\n .object({\n issue_number: z.number().int(),\n synced_at: z.string().datetime({ offset: true }),\n url: z.string().url(),\n })\n .nullable();\n\nconst TaskAiSchema = z\n .object({\n summary: z.string().optional(),\n acceptance_criteria: z.array(z.string()).optional(),\n implementation_context: z.array(z.string()).optional(),\n test_strategy: z.array(z.string()).optional(),\n constraints: z.array(z.string()).optional(),\n })\n .strict()\n .optional();\n\nexport const TaskSchema = z\n .object({\n id: z.string(),\n title: z.string(),\n description: z.string().optional(),\n status: TaskStatusSchema,\n priority: z.enum([\"p0\", \"p1\", \"p2\", \"p3\"]).optional(),\n type: z.enum([\"feature\", \"bug\", \"chore\", \"epic\"]),\n assignee: z.string().optional(),\n milestone: z.string().optional(),\n branch: z.string().optional(),\n labels: z.array(z.string()).default([]),\n parent: z.string().nullable().optional(),\n depends_on: z.array(z.string()).default([]),\n blocks: z.array(z.string()).default([]),\n due_date: isoDateStringSchema.optional(),\n start_date: isoDateStringSchema.optional(),\n estimate: z.enum([\"xs\", \"sm\", \"md\", \"lg\", \"xl\"]).optional(),\n github: TaskGitHubSchema.optional(),\n refs: z\n .array(\n z\n .object({\n type: z.string(),\n path: z.string(),\n })\n .strict(),\n )\n .default([]),\n ai: TaskAiSchema,\n created_at: z.string().datetime({ offset: true }),\n updated_at: z.string().datetime({ offset: true }),\n })\n .strict();\n\nexport type Task = z.infer<typeof TaskSchema>;\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { Task } from \"../schemas/task.js\";\nimport { SnapshotSchema, type Snapshot } from \"../schemas/snapshot.js\";\n\nconst UNKNOWN_DEPENDENCY = \"UNKNOWN_DEPENDENCY\";\n\n/** Warning code when `depends_on` forms a directed cycle. */\nexport const DEPENDENCY_CYCLE = \"DEPENDENCY_CYCLE\";\n\n/**\n * Finds directed cycles in the dependency graph: edge `u → v` when task `u` lists `v` in `depends_on`.\n * Only vertices in `taskIds` participate; edges to ids outside `taskIds` are ignored here (handled as warnings in `compileSnapshot`).\n */\nexport function detectCycles(\n taskIds: Set<string>,\n deps: Map<string, string[]>,\n): string[][] {\n const cycles: string[][] = [];\n /** Finished nodes (BLACK). */\n const visited = new Set<string>();\n /** Nodes on the current DFS path (GRAY). */\n const visiting = new Set<string>();\n const stack: string[] = [];\n\n function recordCycle(fromIndex: number): void {\n cycles.push(stack.slice(fromIndex));\n }\n\n function dfs(u: string): void {\n visiting.add(u);\n stack.push(u);\n\n for (const v of deps.get(u) ?? []) {\n if (!taskIds.has(v)) {\n continue;\n }\n if (visiting.has(v)) {\n const i = stack.indexOf(v);\n if (i !== -1) {\n recordCycle(i);\n }\n continue;\n }\n if (!visited.has(v)) {\n dfs(v);\n }\n }\n\n stack.pop();\n visiting.delete(u);\n visited.add(u);\n }\n\n for (const id of taskIds) {\n if (!visited.has(id)) {\n dfs(id);\n }\n }\n\n return cycles;\n}\n\nfunction formatCycle(nodes: string[]): string {\n return nodes.join(\" → \");\n}\n\n/**\n * Builds a snapshot from tasks, with warnings for unknown dependency ids and dependency cycles.\n */\nexport function compileSnapshot(tasks: Task[], now?: Date): Snapshot {\n const generatedAt = (now ?? new Date()).toISOString();\n const taskList = [...tasks];\n const taskIds = new Set(taskList.map((t) => t.id));\n const deps = new Map<string, string[]>();\n for (const t of taskList) {\n deps.set(t.id, t.depends_on ?? []);\n }\n\n const warnings: Snapshot[\"warnings\"] = [];\n\n for (const t of taskList) {\n for (const depId of t.depends_on ?? []) {\n if (!taskIds.has(depId)) {\n warnings.push({\n code: UNKNOWN_DEPENDENCY,\n message: `Task \"${t.id}\" depends on unknown task id \"${depId}\"`,\n taskId: t.id,\n });\n }\n }\n }\n\n for (const cycle of detectCycles(taskIds, deps)) {\n warnings.push({\n code: DEPENDENCY_CYCLE,\n message: `Dependency cycle: ${formatCycle(cycle)}`,\n taskId: cycle[0],\n });\n }\n\n return {\n generated_at: generatedAt,\n tasks: taskList,\n warnings,\n };\n}\n\nexport function writeSnapshot(snapshotPath: string, snapshot: Snapshot): void {\n const parsed = SnapshotSchema.parse(snapshot);\n const dir = path.dirname(snapshotPath);\n fs.mkdirSync(dir, { recursive: true });\n const body = `${JSON.stringify(parsed, null, 2)}\\n`;\n fs.writeFileSync(snapshotPath, body, \"utf8\");\n}\n","import { z } from \"zod\";\n\nimport { TaskSchema } from \"./task.js\";\n\nexport const SnapshotSchema = z\n .object({\n generated_at: z.string().datetime({ offset: true }),\n tasks: z.array(TaskSchema),\n warnings: z.array(\n z\n .object({\n code: z.string(),\n message: z.string(),\n taskId: z.string().optional(),\n })\n .strict(),\n ),\n })\n .strict();\n\nexport type Snapshot = z.infer<typeof SnapshotSchema>;\n","import { compileSnapshot, writeSnapshot } from \"./compile-snapshot.js\";\nimport { loadAllTasks } from \"./task-store.js\";\nimport type { TrailPaths } from \"./paths.js\";\n\n/** Recompiles `.trail/snapshot.json` from all task files. */\nexport function rebuildSnapshot(paths: TrailPaths, now = new Date()): void {\n const tasks = loadAllTasks(paths.tasksDir);\n const snapshot = compileSnapshot(tasks, now);\n writeSnapshot(paths.snapshotPath, snapshot);\n}\n","import { randomBytes } from \"node:crypto\";\n\n/** Stable draft task id prefix + random suffix (e.g. `draft-a1b2c3d4`). */\nexport function generateDraftId(): string {\n return `draft-${randomBytes(4).toString(\"hex\")}`;\n}\n","import { z } from \"zod\";\n\nexport const TrailConfigSchema = z\n .object({\n github: z\n .object({\n owner: z.string(),\n repo: z.string(),\n })\n .strict(),\n sync: z\n .object({\n preset: z.enum([\"collaborative\", \"solo\", \"offline\"]),\n auto_sync_on_command: z.boolean(),\n ui_poll_interval_seconds: z.number(),\n ui_idle_backoff: z.boolean(),\n })\n .strict(),\n /** Last successful full sync; useful for future `trail status` and similar. */\n last_full_sync_at: z.string().datetime({ offset: true }).optional(),\n })\n .strict();\n\nexport type TrailConfig = z.infer<typeof TrailConfigSchema>;\n","import * as childProcess from \"node:child_process\";\nimport type { TrailError } from \"./errors.js\";\n\nexport type ResolveTokenResult =\n | { ok: true; token: string }\n | { ok: false; error: TrailError };\n\nconst AUTH_HINT =\n \"Set GITHUB_TOKEN or install gh and run gh auth login\";\n\nfunction authRequired(message: string): ResolveTokenResult {\n return {\n ok: false,\n error: {\n code: \"AUTH_REQUIRED\",\n message,\n hint: AUTH_HINT,\n },\n };\n}\n\n/**\n * Resolves a GitHub API token from `GITHUB_TOKEN` (trimmed) or `gh auth token`.\n */\nexport function resolveGitHubToken(\n env?: NodeJS.ProcessEnv,\n): ResolveTokenResult {\n const e = env ?? process.env;\n const fromEnv = e.GITHUB_TOKEN;\n if (typeof fromEnv === \"string\" && fromEnv.trim() !== \"\") {\n return { ok: true, token: fromEnv.trim() };\n }\n\n try {\n const out = childProcess.execFileSync(\"gh\", [\"auth\", \"token\"], {\n encoding: \"utf-8\",\n });\n const token = out.trim();\n if (token === \"\") {\n return authRequired(\"GitHub CLI returned an empty token\");\n }\n return { ok: true, token };\n } catch {\n return authRequired(\n \"No GitHub token found; could not read from environment or gh CLI\",\n );\n }\n}\n","import type { GitHubIssue } from \"./github-types.js\";\n\nconst USER_AGENT = \"trail-cli/0.0.1\";\n\nfunction normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.replace(/\\/$/, \"\");\n}\n\nexport class GitHubClient {\n private readonly token: string;\n private readonly baseUrl: string;\n\n constructor(token: string, baseUrl = \"https://api.github.com\") {\n this.token = token;\n this.baseUrl = normalizeBaseUrl(baseUrl);\n }\n\n private async request(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<Response> {\n const url = `${this.baseUrl}${path.startsWith(\"/\") ? path : `/${path}`}`;\n const headers = new Headers({\n Authorization: `Bearer ${this.token}`,\n Accept: \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n \"User-Agent\": USER_AGENT,\n });\n if (body !== undefined) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n return fetch(url, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n }\n\n private async parseJson<T>(response: Response): Promise<T> {\n const text = await response.text();\n if (!response.ok) {\n const snippet = text.slice(0, 200);\n throw new Error(`GitHub API ${response.status}: ${snippet}`);\n }\n return JSON.parse(text) as T;\n }\n\n async listIssues(\n owner: string,\n repo: string,\n params: {\n state: \"open\" | \"all\" | \"closed\";\n per_page: number;\n page: number;\n },\n ): Promise<GitHubIssue[]> {\n const search = new URLSearchParams({\n state: params.state,\n per_page: String(params.per_page),\n page: String(params.page),\n });\n const path = `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues?${search}`;\n const response = await this.request(\"GET\", path);\n return this.parseJson<GitHubIssue[]>(response);\n }\n\n async getIssue(\n owner: string,\n repo: string,\n issueNumber: number,\n ): Promise<GitHubIssue> {\n const path = `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}`;\n const response = await this.request(\"GET\", path);\n return this.parseJson<GitHubIssue>(response);\n }\n\n async updateIssue(\n owner: string,\n repo: string,\n issueNumber: number,\n patch: Record<string, unknown>,\n ): Promise<GitHubIssue> {\n const path = `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}`;\n const response = await this.request(\"PATCH\", path, patch);\n return this.parseJson<GitHubIssue>(response);\n }\n\n async createIssueComment(\n owner: string,\n repo: string,\n issueNumber: number,\n body: string,\n ): Promise<{ id: number }> {\n const path = `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}/comments`;\n const response = await this.request(\"POST\", path, { body });\n return this.parseJson<{ id: number }>(response);\n }\n\n /** Create a new issue. Returns the created issue (same shape as list/get). */\n async createIssue(\n owner: string,\n repo: string,\n input: { title: string; body?: string; labels?: string[] },\n ): Promise<GitHubIssue> {\n const path = `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues`;\n const response = await this.request(\"POST\", path, {\n title: input.title,\n body: input.body ?? \"\",\n labels: input.labels ?? [],\n });\n return this.parseJson<GitHubIssue>(response);\n }\n}\n","import type { GitHubIssue } from \"./github-types.js\";\nimport { TaskSchema, type Task } from \"../schemas/task.js\";\n\nfunction statusFromIssue(issue: GitHubIssue, existing: Task | null): Task[\"status\"] {\n if (issue.state === \"closed\") {\n return \"done\";\n }\n const prev = existing?.status;\n if (prev === \"in_progress\" || prev === \"in_review\") {\n return prev;\n }\n return \"todo\";\n}\n\nfunction parseIssueTimestamp(issue: GitHubIssue, fallback: Date): string {\n if (issue.updated_at.trim() === \"\") {\n return fallback.toISOString();\n }\n return issue.updated_at;\n}\n\n/**\n * Maps a GitHub issue to a Trail task. GitHub wins for title, body, labels, assignee, milestone;\n * local-only fields are preserved from `existing` when present.\n */\nexport function issueToTask(\n issue: GitHubIssue,\n existing: Task | null,\n now: Date,\n): Task {\n const updatedAt = parseIssueTimestamp(issue, now);\n const createdAt =\n existing?.created_at ?? (issue.updated_at.trim() !== \"\" ? issue.updated_at : now.toISOString());\n\n const raw: Task = {\n id: String(issue.number),\n title: issue.title,\n description: issue.body ?? \"\",\n status: statusFromIssue(issue, existing),\n type: existing?.type ?? \"feature\",\n labels: issue.labels.map((l) => l.name),\n assignee: issue.assignee?.login,\n milestone: issue.milestone?.title,\n depends_on: existing?.depends_on ?? [],\n blocks: existing?.blocks ?? [],\n refs: existing?.refs ?? [],\n ai: existing?.ai,\n branch: existing?.branch,\n estimate: existing?.estimate,\n priority: existing?.priority,\n parent: existing?.parent,\n due_date: existing?.due_date,\n start_date: existing?.start_date,\n github: {\n issue_number: issue.number,\n synced_at: now.toISOString(),\n url: issue.html_url,\n },\n created_at: createdAt,\n updated_at: updatedAt,\n };\n\n return TaskSchema.parse(raw);\n}\n\n/**\n * Maps a Trail task to fields for `GitHubClient.updateIssue`.\n */\nexport function taskToIssueUpdate(task: Task): {\n title?: string;\n body?: string;\n state?: \"open\" | \"closed\";\n labels?: string[];\n} {\n const labels = [...task.labels];\n if (task.priority) {\n const priorityLabel = `priority:${task.priority}`;\n if (!labels.includes(priorityLabel)) {\n labels.push(priorityLabel);\n }\n }\n\n const state: \"open\" | \"closed\" =\n task.status === \"done\" || task.status === \"cancelled\" ? \"closed\" : \"open\";\n\n return {\n title: task.title,\n body: task.description ?? \"\",\n state,\n labels,\n };\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { GitHubClient } from \"./github-client.js\";\nimport { issueToTask, taskToIssueUpdate } from \"./github-mapper.js\";\nimport { compileSnapshot, writeSnapshot } from \"./compile-snapshot.js\";\nimport { computeRelevantTaskIds } from \"./relevant-tasks.js\";\nimport { loadAllTasks, readTaskFile, writeTaskFile } from \"./task-store.js\";\nimport type { Task } from \"../schemas/task.js\";\n\nconst ISSUES_PER_PAGE = 100;\n\nexport type SyncProgress =\n | { phase: \"pull\"; page: number; issuesInPage: number; issuesSoFar: number }\n | { phase: \"push\"; index: number; total: number; taskId: string };\n\nexport async function pullSync(options: {\n client: GitHubClient;\n owner: string;\n repo: string;\n tasksDir: string;\n now?: Date;\n onProgress?: (p: SyncProgress) => void;\n}): Promise<void> {\n const { client, owner, repo, tasksDir } = options;\n const now = options.now ?? new Date();\n const onProgress = options.onProgress;\n let page = 1;\n let issuesSoFar = 0;\n\n for (;;) {\n const issues = await client.listIssues(owner, repo, {\n state: \"all\",\n per_page: ISSUES_PER_PAGE,\n page,\n });\n\n if (issues.length === 0) {\n break;\n }\n\n for (const issue of issues) {\n const filePath = path.join(tasksDir, `${issue.number}.json`);\n let existing: Task | null = null;\n if (fs.existsSync(filePath)) {\n existing = readTaskFile(filePath);\n }\n const task = issueToTask(issue, existing, now);\n writeTaskFile(filePath, task);\n }\n\n issuesSoFar += issues.length;\n onProgress?.({\n phase: \"pull\",\n page,\n issuesInPage: issues.length,\n issuesSoFar,\n });\n\n if (issues.length < ISSUES_PER_PAGE) {\n break;\n }\n page += 1;\n }\n}\n\nfunction isLinkedTask(\n task: Task,\n): task is Task & { github: NonNullable<Task[\"github\"]> } {\n return task.github != null && typeof task.github === \"object\";\n}\n\nexport async function pushSync(options: {\n client: GitHubClient;\n owner: string;\n repo: string;\n tasks: Task[];\n /** When set, only issues for these task ids are pushed (bulk sync). Omit to push every linked task. */\n onlyTaskIds?: Set<string>;\n onProgress?: (p: SyncProgress) => void;\n}): Promise<void> {\n const { client, owner, repo, tasks } = options;\n const only = options.onlyTaskIds;\n const onProgress = options.onProgress;\n\n const toPush = tasks.filter((task) => {\n if (task.status === \"draft\") {\n return false;\n }\n if (!isLinkedTask(task)) {\n return false;\n }\n if (only !== undefined && !only.has(task.id)) {\n return false;\n }\n return true;\n });\n\n let index = 0;\n for (const task of toPush) {\n if (!isLinkedTask(task)) {\n continue;\n }\n index += 1;\n onProgress?.({\n phase: \"push\",\n index,\n total: toPush.length,\n taskId: task.id,\n });\n const patch = taskToIssueUpdate(task);\n await client.updateIssue(owner, repo, task.github.issue_number, patch);\n }\n}\n\nexport async function syncFull(options: {\n client: GitHubClient;\n owner: string;\n repo: string;\n tasksDir: string;\n snapshotPath: string;\n now?: Date;\n onProgress?: (p: SyncProgress) => void;\n}): Promise<void> {\n const { client, owner, repo, tasksDir, snapshotPath, now } = options;\n await pullSync({ client, owner, repo, tasksDir, now, onProgress: options.onProgress });\n const tasks = loadAllTasks(tasksDir);\n const snapshot = compileSnapshot(tasks, now);\n writeSnapshot(snapshotPath, snapshot);\n}\n\nexport async function fullSync(options: {\n client: GitHubClient;\n owner: string;\n repo: string;\n tasksDir: string;\n snapshotPath: string;\n now?: Date;\n onProgress?: (p: SyncProgress) => void;\n}): Promise<void> {\n const { client, owner, repo, tasksDir, snapshotPath, now } = options;\n await pullSync({ client, owner, repo, tasksDir, now, onProgress: options.onProgress });\n const tasks = loadAllTasks(tasksDir);\n const relevant = computeRelevantTaskIds(tasks);\n await pushSync({\n client,\n owner,\n repo,\n tasks,\n onlyTaskIds: relevant,\n onProgress: options.onProgress,\n });\n const snapshot = compileSnapshot(tasks, now);\n writeSnapshot(snapshotPath, snapshot);\n}\n","import type { Task, TaskStatus } from \"../schemas/task.js\";\n\nconst NON_TERMINAL: TaskStatus[] = [\n \"draft\",\n \"todo\",\n \"in_progress\",\n \"in_review\",\n];\n\nfunction isNonTerminalStatus(status: TaskStatus): boolean {\n return NON_TERMINAL.includes(status);\n}\n\n/**\n * Tasks that are \"in play\" for day-to-day work: not done/cancelled.\n * Used as seeds for the relevance closure.\n */\nexport function isActiveForRelevance(task: Task): boolean {\n return isNonTerminalStatus(task.status);\n}\n\n/**\n * Collects task ids that are active (non-terminal) plus any task ids reachable via\n * `depends_on`, `blocks`, or `parent` from those seeds. Used to limit bulk `push`\n * during `trail sync` so we do not PATCH every linked issue in the repository.\n *\n * Individual commands (`trail update`, `trail done`, …) still push their target task.\n */\nexport function computeRelevantTaskIds(tasks: Task[]): Set<string> {\n const byId = new Map<string, Task>();\n for (const t of tasks) {\n byId.set(t.id, t);\n }\n\n const relevant = new Set<string>();\n const queue: string[] = [];\n\n for (const t of tasks) {\n if (isActiveForRelevance(t)) {\n if (!relevant.has(t.id)) {\n relevant.add(t.id);\n queue.push(t.id);\n }\n }\n }\n\n function enqueue(id: string): void {\n if (relevant.has(id)) return;\n relevant.add(id);\n queue.push(id);\n }\n\n while (queue.length > 0) {\n const id = queue.pop() as string;\n const task = byId.get(id);\n if (!task) continue;\n\n for (const d of task.depends_on) {\n if (byId.has(d)) enqueue(d);\n }\n for (const b of task.blocks) {\n if (byId.has(b)) enqueue(b);\n }\n if (task.parent != null && task.parent !== \"\" && byId.has(task.parent)) {\n enqueue(task.parent);\n }\n }\n\n return relevant;\n}\n","import fs from \"node:fs\";\n\nimport { TrailConfigSchema } from \"../schemas/config.js\";\nimport type { TrailPaths } from \"./paths.js\";\n\nexport function writeLastFullSyncAt(paths: TrailPaths, iso: string): void {\n const raw = fs.readFileSync(paths.configPath, \"utf-8\");\n const config = TrailConfigSchema.parse(JSON.parse(raw) as unknown);\n const next = { ...config, last_full_sync_at: iso };\n fs.writeFileSync(paths.configPath, `${JSON.stringify(next, null, 2)}\\n`, \"utf-8\");\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { GitHubClient } from \"./github-client.js\";\nimport { issueToTask } from \"./github-mapper.js\";\nimport { writeTaskFile } from \"./task-store.js\";\nimport type { Task } from \"../schemas/task.js\";\n\n/**\n * Creates a GitHub issue from a draft task, deletes the draft file, and writes\n * `{issueNumber}.json`. Mirrors `trail promote` without CLI validation.\n */\nexport async function linkDraftToNewGitHubIssue(options: {\n client: GitHubClient;\n owner: string;\n repo: string;\n draft: Task;\n draftFilePath: string;\n tasksDir: string;\n now: Date;\n}): Promise<Task> {\n const { client, owner, repo, draft, draftFilePath, tasksDir, now } = options;\n\n const issue = await client.createIssue(owner, repo, {\n title: draft.title,\n body: draft.description ?? \"\",\n labels: draft.labels,\n });\n\n const promoted = issueToTask(issue, draft, now);\n fs.unlinkSync(draftFilePath);\n const newPath = path.join(tasksDir, `${issue.number}.json`);\n writeTaskFile(newPath, promoted);\n return promoted;\n}\n"],"mappings":"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","names":["fs","path","fs","path","fs","path","z","z","path","fs","z","path","fs","path","path","fs","fs","fs","fs","path","fs","path"]}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
loadTrailReadContext,
|
|
11
11
|
selectNextTask,
|
|
12
12
|
selectTasksForList
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-YSTYANXJ.js";
|
|
14
14
|
|
|
15
15
|
// ../../node_modules/ajv/dist/compile/codegen/code.js
|
|
16
16
|
var require_code = __commonJS({
|
|
@@ -12584,4 +12584,4 @@ async function runMcpServer() {
|
|
|
12584
12584
|
export {
|
|
12585
12585
|
runMcpServer
|
|
12586
12586
|
};
|
|
12587
|
-
//# sourceMappingURL=run-mcp-server-
|
|
12587
|
+
//# sourceMappingURL=run-mcp-server-NKYRI73A.js.map
|