@cleocode/cleo 2026.3.16 → 2026.3.17
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/cli/index.js +1038 -389
- package/dist/cli/index.js.map +4 -4
- package/dist/mcp/index.js +866 -202
- package/dist/mcp/index.js.map +4 -4
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -4626,13 +4626,13 @@ async function getDb(cwd) {
|
|
|
4626
4626
|
if (!_gitTrackingChecked) {
|
|
4627
4627
|
_gitTrackingChecked = true;
|
|
4628
4628
|
try {
|
|
4629
|
-
const { execFileSync:
|
|
4629
|
+
const { execFileSync: execFileSync15 } = await import("node:child_process");
|
|
4630
4630
|
const gitCwd = resolve3(dbPath, "..", "..");
|
|
4631
4631
|
const filesToCheck = [dbPath, dbPath + "-wal", dbPath + "-shm"];
|
|
4632
4632
|
const log7 = getLogger("sqlite");
|
|
4633
4633
|
for (const fileToCheck of filesToCheck) {
|
|
4634
4634
|
try {
|
|
4635
|
-
|
|
4635
|
+
execFileSync15("git", ["ls-files", "--error-unmatch", fileToCheck], {
|
|
4636
4636
|
cwd: gitCwd,
|
|
4637
4637
|
stdio: "pipe"
|
|
4638
4638
|
});
|
|
@@ -15396,6 +15396,7 @@ async function getProjectStats(opts, accessor) {
|
|
|
15396
15396
|
const active = tasks2.filter((t) => t.status === "active").length;
|
|
15397
15397
|
const done = tasks2.filter((t) => t.status === "done").length;
|
|
15398
15398
|
const blocked = tasks2.filter((t) => t.status === "blocked").length;
|
|
15399
|
+
const cancelled = tasks2.filter((t) => t.status === "cancelled").length;
|
|
15399
15400
|
const totalActive = tasks2.length;
|
|
15400
15401
|
const cutoff = new Date(Date.now() - periodDays * 864e5).toISOString();
|
|
15401
15402
|
const entries = await queryAuditEntries(opts.cwd);
|
|
@@ -15412,11 +15413,45 @@ async function getProjectStats(opts, accessor) {
|
|
|
15412
15413
|
(e) => isArchive(e) && e.timestamp >= cutoff
|
|
15413
15414
|
).length;
|
|
15414
15415
|
const completionRate = createdInPeriod > 0 ? Math.round(completedInPeriod / createdInPeriod * 1e4) / 100 : 0;
|
|
15415
|
-
|
|
15416
|
-
|
|
15417
|
-
|
|
15416
|
+
let totalCreated = 0;
|
|
15417
|
+
let totalCompleted = 0;
|
|
15418
|
+
let totalCancelled = 0;
|
|
15419
|
+
let totalArchived = 0;
|
|
15420
|
+
let archivedCompleted = 0;
|
|
15421
|
+
let archivedCount = 0;
|
|
15422
|
+
try {
|
|
15423
|
+
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_sqlite(), sqlite_exports));
|
|
15424
|
+
const { count: dbCount, eq: dbEq, and: dbAnd } = await import("drizzle-orm");
|
|
15425
|
+
const { tasks: tasksTable } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
15426
|
+
const db = await getDb2(opts.cwd);
|
|
15427
|
+
const statusRows = await db.select({ status: tasksTable.status, c: dbCount() }).from(tasksTable).groupBy(tasksTable.status).all();
|
|
15428
|
+
const statusMap = {};
|
|
15429
|
+
for (const row of statusRows) {
|
|
15430
|
+
statusMap[row.status] = row.c;
|
|
15431
|
+
}
|
|
15432
|
+
archivedCount = statusMap["archived"] ?? 0;
|
|
15433
|
+
totalCreated = Object.values(statusMap).reduce((sum, n) => sum + n, 0);
|
|
15434
|
+
totalCancelled = statusMap["cancelled"] ?? 0;
|
|
15435
|
+
totalArchived = archivedCount;
|
|
15436
|
+
const archivedDoneRow = await db.select({ c: dbCount() }).from(tasksTable).where(dbAnd(dbEq(tasksTable.status, "archived"), dbEq(tasksTable.archiveReason, "completed"))).get();
|
|
15437
|
+
archivedCompleted = archivedDoneRow?.c ?? 0;
|
|
15438
|
+
totalCompleted = (statusMap["done"] ?? 0) + archivedCompleted;
|
|
15439
|
+
} catch {
|
|
15440
|
+
totalCreated = entries.filter(isCreate).length;
|
|
15441
|
+
totalCompleted = entries.filter(isComplete).length;
|
|
15442
|
+
totalArchived = entries.filter(isArchive).length;
|
|
15443
|
+
}
|
|
15418
15444
|
return {
|
|
15419
|
-
currentState: {
|
|
15445
|
+
currentState: {
|
|
15446
|
+
pending,
|
|
15447
|
+
active,
|
|
15448
|
+
done,
|
|
15449
|
+
blocked,
|
|
15450
|
+
cancelled,
|
|
15451
|
+
totalActive,
|
|
15452
|
+
archived: archivedCount,
|
|
15453
|
+
grandTotal: totalActive + archivedCount
|
|
15454
|
+
},
|
|
15420
15455
|
completionMetrics: {
|
|
15421
15456
|
periodDays,
|
|
15422
15457
|
completedInPeriod,
|
|
@@ -15428,7 +15463,7 @@ async function getProjectStats(opts, accessor) {
|
|
|
15428
15463
|
completedInPeriod,
|
|
15429
15464
|
archivedInPeriod
|
|
15430
15465
|
},
|
|
15431
|
-
allTime: { totalCreated, totalCompleted, totalArchived }
|
|
15466
|
+
allTime: { totalCreated, totalCompleted, totalCancelled, totalArchived, archivedCompleted }
|
|
15432
15467
|
};
|
|
15433
15468
|
}
|
|
15434
15469
|
function rankBlockedTask(task, allTasks, focusTask) {
|
|
@@ -15472,7 +15507,18 @@ async function getDashboard(opts, accessor) {
|
|
|
15472
15507
|
const active = tasks2.filter((t) => t.status === "active").length;
|
|
15473
15508
|
const done = tasks2.filter((t) => t.status === "done").length;
|
|
15474
15509
|
const blocked = tasks2.filter((t) => t.status === "blocked").length;
|
|
15510
|
+
const cancelled = tasks2.filter((t) => t.status === "cancelled").length;
|
|
15475
15511
|
const total = tasks2.length;
|
|
15512
|
+
let archived = 0;
|
|
15513
|
+
try {
|
|
15514
|
+
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_sqlite(), sqlite_exports));
|
|
15515
|
+
const { count: dbCount, eq: dbEq } = await import("drizzle-orm");
|
|
15516
|
+
const { tasks: tasksTable } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
15517
|
+
const db = await getDb2(opts.cwd);
|
|
15518
|
+
const row = await db.select({ c: dbCount() }).from(tasksTable).where(dbEq(tasksTable.status, "archived")).get();
|
|
15519
|
+
archived = row?.c ?? 0;
|
|
15520
|
+
} catch {
|
|
15521
|
+
}
|
|
15476
15522
|
const project = data.project?.name ?? "Unknown Project";
|
|
15477
15523
|
const currentPhase = data.project?.currentPhase ?? null;
|
|
15478
15524
|
const focusId = data.focus?.currentTask ?? null;
|
|
@@ -15480,7 +15526,7 @@ async function getDashboard(opts, accessor) {
|
|
|
15480
15526
|
if (focusId) {
|
|
15481
15527
|
focusTask = tasks2.find((t) => t.id === focusId) ?? null;
|
|
15482
15528
|
}
|
|
15483
|
-
const highPriority = tasks2.filter((t) => (t.priority === "critical" || t.priority === "high") && t.status !== "done").sort((a, b) => {
|
|
15529
|
+
const highPriority = tasks2.filter((t) => (t.priority === "critical" || t.priority === "high") && t.status !== "done" && t.status !== "cancelled").sort((a, b) => {
|
|
15484
15530
|
const pDiff = (PRIORITY_ORDER[a.priority ?? "low"] ?? 9) - (PRIORITY_ORDER[b.priority ?? "low"] ?? 9);
|
|
15485
15531
|
if (pDiff !== 0) return pDiff;
|
|
15486
15532
|
return (a.createdAt ?? "").localeCompare(b.createdAt ?? "");
|
|
@@ -15493,6 +15539,7 @@ async function getDashboard(opts, accessor) {
|
|
|
15493
15539
|
}).map((r) => r.task);
|
|
15494
15540
|
const labelMap = {};
|
|
15495
15541
|
for (const t of tasks2) {
|
|
15542
|
+
if (t.status === "cancelled") continue;
|
|
15496
15543
|
for (const label of t.labels ?? []) {
|
|
15497
15544
|
labelMap[label] = (labelMap[label] ?? 0) + 1;
|
|
15498
15545
|
}
|
|
@@ -15501,7 +15548,7 @@ async function getDashboard(opts, accessor) {
|
|
|
15501
15548
|
return {
|
|
15502
15549
|
project,
|
|
15503
15550
|
currentPhase,
|
|
15504
|
-
summary: { pending, active, blocked, done, total },
|
|
15551
|
+
summary: { pending, active, blocked, done, cancelled, total, archived, grandTotal: total + archived },
|
|
15505
15552
|
focus: { currentTask: focusId, task: focusTask },
|
|
15506
15553
|
highPriority: { count: highPriority.length, tasks: highPriority.slice(0, 5) },
|
|
15507
15554
|
blockedTasks: {
|
|
@@ -19553,7 +19600,9 @@ async function systemDash(projectRoot, params) {
|
|
|
19553
19600
|
blocked: summary.blocked,
|
|
19554
19601
|
done: summary.done,
|
|
19555
19602
|
cancelled: summary.cancelled ?? 0,
|
|
19556
|
-
total: summary.total
|
|
19603
|
+
total: summary.total,
|
|
19604
|
+
archived: summary.archived ?? 0,
|
|
19605
|
+
grandTotal: summary.grandTotal ?? summary.total
|
|
19557
19606
|
},
|
|
19558
19607
|
taskWork: data.focus ?? data.taskWork,
|
|
19559
19608
|
activeSession: data.activeSession ?? null,
|
|
@@ -19573,17 +19622,18 @@ async function systemStats(projectRoot, params) {
|
|
|
19573
19622
|
const result = await getProjectStats({ period: String(params?.period ?? 30), cwd: projectRoot }, accessor);
|
|
19574
19623
|
const taskData = await accessor.loadTaskFile();
|
|
19575
19624
|
const tasks2 = taskData?.tasks ?? [];
|
|
19625
|
+
const activeTasks = tasks2.filter((t) => t.status !== "cancelled");
|
|
19576
19626
|
const byPriority = {};
|
|
19577
|
-
for (const t of
|
|
19627
|
+
for (const t of activeTasks) {
|
|
19578
19628
|
byPriority[t.priority] = (byPriority[t.priority] ?? 0) + 1;
|
|
19579
19629
|
}
|
|
19580
19630
|
const byType = {};
|
|
19581
|
-
for (const t of
|
|
19631
|
+
for (const t of activeTasks) {
|
|
19582
19632
|
const type = t.type || "task";
|
|
19583
19633
|
byType[type] = (byType[type] ?? 0) + 1;
|
|
19584
19634
|
}
|
|
19585
19635
|
const byPhase = {};
|
|
19586
|
-
for (const t of
|
|
19636
|
+
for (const t of activeTasks) {
|
|
19587
19637
|
const phase = t.phase || "unassigned";
|
|
19588
19638
|
byPhase[phase] = (byPhase[phase] ?? 0) + 1;
|
|
19589
19639
|
}
|
|
@@ -19613,7 +19663,9 @@ async function systemStats(projectRoot, params) {
|
|
|
19613
19663
|
done: currentState.done,
|
|
19614
19664
|
blocked: currentState.blocked,
|
|
19615
19665
|
cancelled: tasks2.filter((t) => t.status === "cancelled").length,
|
|
19616
|
-
totalActive: currentState.totalActive
|
|
19666
|
+
totalActive: currentState.totalActive,
|
|
19667
|
+
archived: currentState.archived ?? 0,
|
|
19668
|
+
grandTotal: currentState.grandTotal ?? currentState.totalActive
|
|
19617
19669
|
},
|
|
19618
19670
|
byPriority,
|
|
19619
19671
|
byType,
|
|
@@ -19638,10 +19690,10 @@ async function systemLog(projectRoot, filters) {
|
|
|
19638
19690
|
}
|
|
19639
19691
|
async function queryAuditLogSqlite(projectRoot, filters) {
|
|
19640
19692
|
try {
|
|
19641
|
-
const { join:
|
|
19642
|
-
const { existsSync:
|
|
19643
|
-
const dbPath =
|
|
19644
|
-
if (!
|
|
19693
|
+
const { join: join77 } = await import("node:path");
|
|
19694
|
+
const { existsSync: existsSync72 } = await import("node:fs");
|
|
19695
|
+
const dbPath = join77(projectRoot, ".cleo", "tasks.db");
|
|
19696
|
+
if (!existsSync72(dbPath)) {
|
|
19645
19697
|
const offset = filters?.offset ?? 0;
|
|
19646
19698
|
const limit = filters?.limit ?? 20;
|
|
19647
19699
|
return {
|
|
@@ -20497,10 +20549,10 @@ async function readProjectMeta(projectPath) {
|
|
|
20497
20549
|
}
|
|
20498
20550
|
async function readProjectId(projectPath) {
|
|
20499
20551
|
try {
|
|
20500
|
-
const { readFileSync:
|
|
20552
|
+
const { readFileSync: readFileSync53, existsSync: existsSync72 } = await import("node:fs");
|
|
20501
20553
|
const infoPath = join33(projectPath, ".cleo", "project-info.json");
|
|
20502
|
-
if (!
|
|
20503
|
-
const data = JSON.parse(
|
|
20554
|
+
if (!existsSync72(infoPath)) return "";
|
|
20555
|
+
const data = JSON.parse(readFileSync53(infoPath, "utf-8"));
|
|
20504
20556
|
return typeof data.projectId === "string" ? data.projectId : "";
|
|
20505
20557
|
} catch {
|
|
20506
20558
|
return "";
|
|
@@ -27978,11 +28030,353 @@ var init_changelog_writer = __esm({
|
|
|
27978
28030
|
}
|
|
27979
28031
|
});
|
|
27980
28032
|
|
|
27981
|
-
// src/core/release/
|
|
27982
|
-
import { existsSync as existsSync48, renameSync as renameSync7 } from "node:fs";
|
|
27983
|
-
import { readFile as readFile10 } from "node:fs/promises";
|
|
28033
|
+
// src/core/release/github-pr.ts
|
|
27984
28034
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
28035
|
+
function isGhCliAvailable() {
|
|
28036
|
+
try {
|
|
28037
|
+
execFileSync6("gh", ["--version"], { stdio: "pipe" });
|
|
28038
|
+
return true;
|
|
28039
|
+
} catch {
|
|
28040
|
+
return false;
|
|
28041
|
+
}
|
|
28042
|
+
}
|
|
28043
|
+
function extractRepoOwnerAndName(remote) {
|
|
28044
|
+
const trimmed = remote.trim();
|
|
28045
|
+
const httpsMatch = trimmed.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
28046
|
+
if (httpsMatch) {
|
|
28047
|
+
return { owner: httpsMatch[1], repo: httpsMatch[2] };
|
|
28048
|
+
}
|
|
28049
|
+
const sshMatch = trimmed.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
28050
|
+
if (sshMatch) {
|
|
28051
|
+
return { owner: sshMatch[1], repo: sshMatch[2] };
|
|
28052
|
+
}
|
|
28053
|
+
return null;
|
|
28054
|
+
}
|
|
28055
|
+
async function detectBranchProtection(branch, remote, projectRoot) {
|
|
28056
|
+
const cwdOpts = projectRoot ? { cwd: projectRoot } : {};
|
|
28057
|
+
if (isGhCliAvailable()) {
|
|
28058
|
+
try {
|
|
28059
|
+
const remoteUrl = execFileSync6("git", ["remote", "get-url", remote], {
|
|
28060
|
+
encoding: "utf-8",
|
|
28061
|
+
stdio: "pipe",
|
|
28062
|
+
...cwdOpts
|
|
28063
|
+
}).trim();
|
|
28064
|
+
const identity = extractRepoOwnerAndName(remoteUrl);
|
|
28065
|
+
if (identity) {
|
|
28066
|
+
const { owner, repo } = identity;
|
|
28067
|
+
try {
|
|
28068
|
+
execFileSync6(
|
|
28069
|
+
"gh",
|
|
28070
|
+
["api", `/repos/${owner}/${repo}/branches/${branch}/protection`],
|
|
28071
|
+
{
|
|
28072
|
+
encoding: "utf-8",
|
|
28073
|
+
stdio: "pipe",
|
|
28074
|
+
...cwdOpts
|
|
28075
|
+
}
|
|
28076
|
+
);
|
|
28077
|
+
return { protected: true, detectionMethod: "gh-api" };
|
|
28078
|
+
} catch (apiErr) {
|
|
28079
|
+
const stderr2 = apiErr instanceof Error && "stderr" in apiErr ? String(apiErr.stderr ?? "") : "";
|
|
28080
|
+
if (stderr2.includes("404") || stderr2.includes("Not Found")) {
|
|
28081
|
+
return { protected: false, detectionMethod: "gh-api" };
|
|
28082
|
+
}
|
|
28083
|
+
}
|
|
28084
|
+
}
|
|
28085
|
+
} catch {
|
|
28086
|
+
}
|
|
28087
|
+
}
|
|
28088
|
+
try {
|
|
28089
|
+
const result = execFileSync6(
|
|
28090
|
+
"git",
|
|
28091
|
+
["push", "--dry-run", remote, `HEAD:${branch}`],
|
|
28092
|
+
{
|
|
28093
|
+
encoding: "utf-8",
|
|
28094
|
+
stdio: "pipe",
|
|
28095
|
+
...cwdOpts
|
|
28096
|
+
}
|
|
28097
|
+
);
|
|
28098
|
+
const output = typeof result === "string" ? result : "";
|
|
28099
|
+
if (output.includes("protected branch") || output.includes("GH006") || output.includes("refusing to allow")) {
|
|
28100
|
+
return { protected: true, detectionMethod: "push-dry-run" };
|
|
28101
|
+
}
|
|
28102
|
+
return { protected: false, detectionMethod: "push-dry-run" };
|
|
28103
|
+
} catch (pushErr) {
|
|
28104
|
+
const stderr2 = pushErr instanceof Error && "stderr" in pushErr ? String(pushErr.stderr ?? "") : pushErr instanceof Error ? pushErr.message : String(pushErr);
|
|
28105
|
+
if (stderr2.includes("protected branch") || stderr2.includes("GH006") || stderr2.includes("refusing to allow")) {
|
|
28106
|
+
return { protected: true, detectionMethod: "push-dry-run" };
|
|
28107
|
+
}
|
|
28108
|
+
return {
|
|
28109
|
+
protected: false,
|
|
28110
|
+
detectionMethod: "unknown",
|
|
28111
|
+
error: stderr2
|
|
28112
|
+
};
|
|
28113
|
+
}
|
|
28114
|
+
}
|
|
28115
|
+
function buildPRBody(opts) {
|
|
28116
|
+
const epicLine = opts.epicId ? `**Epic**: ${opts.epicId}
|
|
28117
|
+
|
|
28118
|
+
` : "";
|
|
28119
|
+
return [
|
|
28120
|
+
`## Release v${opts.version}`,
|
|
28121
|
+
"",
|
|
28122
|
+
`${epicLine}This PR merges the ${opts.head} branch into ${opts.base} to publish the release.`,
|
|
28123
|
+
"",
|
|
28124
|
+
"### Checklist",
|
|
28125
|
+
"- [ ] CHANGELOG.md updated",
|
|
28126
|
+
"- [ ] All release tasks complete",
|
|
28127
|
+
"- [ ] Version bump committed",
|
|
28128
|
+
"",
|
|
28129
|
+
"---",
|
|
28130
|
+
"*Created by CLEO release pipeline*"
|
|
28131
|
+
].join("\n");
|
|
28132
|
+
}
|
|
28133
|
+
function formatManualPRInstructions(opts) {
|
|
28134
|
+
const epicSuffix = opts.epicId ? ` (${opts.epicId})` : "";
|
|
28135
|
+
return [
|
|
28136
|
+
"Branch protection detected or gh CLI unavailable. Create the PR manually:",
|
|
28137
|
+
"",
|
|
28138
|
+
` gh pr create \\`,
|
|
28139
|
+
` --base ${opts.base} \\`,
|
|
28140
|
+
` --head ${opts.head} \\`,
|
|
28141
|
+
` --title "${opts.title}" \\`,
|
|
28142
|
+
` --body "Release v${opts.version}${epicSuffix}"`,
|
|
28143
|
+
"",
|
|
28144
|
+
`Or visit: https://github.com/[owner]/[repo]/compare/${opts.base}...${opts.head}`,
|
|
28145
|
+
"",
|
|
28146
|
+
"After merging, CI will automatically publish to npm."
|
|
28147
|
+
].join("\n");
|
|
28148
|
+
}
|
|
28149
|
+
async function createPullRequest(opts) {
|
|
28150
|
+
if (!isGhCliAvailable()) {
|
|
28151
|
+
return {
|
|
28152
|
+
mode: "manual",
|
|
28153
|
+
instructions: formatManualPRInstructions(opts)
|
|
28154
|
+
};
|
|
28155
|
+
}
|
|
28156
|
+
const body = buildPRBody(opts);
|
|
28157
|
+
const args = [
|
|
28158
|
+
"pr",
|
|
28159
|
+
"create",
|
|
28160
|
+
"--base",
|
|
28161
|
+
opts.base,
|
|
28162
|
+
"--head",
|
|
28163
|
+
opts.head,
|
|
28164
|
+
"--title",
|
|
28165
|
+
opts.title,
|
|
28166
|
+
"--body",
|
|
28167
|
+
body
|
|
28168
|
+
];
|
|
28169
|
+
if (opts.labels && opts.labels.length > 0) {
|
|
28170
|
+
for (const label of opts.labels) {
|
|
28171
|
+
args.push("--label", label);
|
|
28172
|
+
}
|
|
28173
|
+
}
|
|
28174
|
+
try {
|
|
28175
|
+
const output = execFileSync6("gh", args, {
|
|
28176
|
+
encoding: "utf-8",
|
|
28177
|
+
stdio: "pipe",
|
|
28178
|
+
...opts.projectRoot ? { cwd: opts.projectRoot } : {}
|
|
28179
|
+
});
|
|
28180
|
+
const prUrl = output.trim();
|
|
28181
|
+
const numberMatch = prUrl.match(/\/pull\/(\d+)$/);
|
|
28182
|
+
const prNumber = numberMatch ? parseInt(numberMatch[1], 10) : void 0;
|
|
28183
|
+
return {
|
|
28184
|
+
mode: "created",
|
|
28185
|
+
prUrl,
|
|
28186
|
+
prNumber
|
|
28187
|
+
};
|
|
28188
|
+
} catch (err) {
|
|
28189
|
+
const stderr2 = err instanceof Error && "stderr" in err ? String(err.stderr ?? "") : err instanceof Error ? err.message : String(err);
|
|
28190
|
+
if (stderr2.includes("already exists")) {
|
|
28191
|
+
const urlMatch = stderr2.match(/https:\/\/github\.com\/[^\s]+\/pull\/\d+/);
|
|
28192
|
+
const existingUrl = urlMatch ? urlMatch[0] : void 0;
|
|
28193
|
+
return {
|
|
28194
|
+
mode: "skipped",
|
|
28195
|
+
prUrl: existingUrl,
|
|
28196
|
+
instructions: "PR already exists"
|
|
28197
|
+
};
|
|
28198
|
+
}
|
|
28199
|
+
return {
|
|
28200
|
+
mode: "manual",
|
|
28201
|
+
instructions: formatManualPRInstructions(opts),
|
|
28202
|
+
error: stderr2
|
|
28203
|
+
};
|
|
28204
|
+
}
|
|
28205
|
+
}
|
|
28206
|
+
var init_github_pr = __esm({
|
|
28207
|
+
"src/core/release/github-pr.ts"() {
|
|
28208
|
+
"use strict";
|
|
28209
|
+
}
|
|
28210
|
+
});
|
|
28211
|
+
|
|
28212
|
+
// src/core/release/channel.ts
|
|
28213
|
+
function getDefaultChannelConfig() {
|
|
28214
|
+
return {
|
|
28215
|
+
main: "main",
|
|
28216
|
+
develop: "develop",
|
|
28217
|
+
feature: "feature/"
|
|
28218
|
+
};
|
|
28219
|
+
}
|
|
28220
|
+
function resolveChannelFromBranch(branch, config) {
|
|
28221
|
+
const cfg = config ?? getDefaultChannelConfig();
|
|
28222
|
+
if (cfg.custom) {
|
|
28223
|
+
if (Object.prototype.hasOwnProperty.call(cfg.custom, branch)) {
|
|
28224
|
+
return cfg.custom[branch];
|
|
28225
|
+
}
|
|
28226
|
+
let bestPrefix = "";
|
|
28227
|
+
let bestChannel;
|
|
28228
|
+
for (const [key, channel] of Object.entries(cfg.custom)) {
|
|
28229
|
+
if (branch.startsWith(key) && key.length > bestPrefix.length) {
|
|
28230
|
+
bestPrefix = key;
|
|
28231
|
+
bestChannel = channel;
|
|
28232
|
+
}
|
|
28233
|
+
}
|
|
28234
|
+
if (bestChannel !== void 0) {
|
|
28235
|
+
return bestChannel;
|
|
28236
|
+
}
|
|
28237
|
+
}
|
|
28238
|
+
if (branch === cfg.main) {
|
|
28239
|
+
return "latest";
|
|
28240
|
+
}
|
|
28241
|
+
if (branch === cfg.develop) {
|
|
28242
|
+
return "beta";
|
|
28243
|
+
}
|
|
28244
|
+
const alphaPrefixes = ["feature/", "hotfix/", "release/"];
|
|
28245
|
+
if (cfg.feature && !alphaPrefixes.includes(cfg.feature)) {
|
|
28246
|
+
alphaPrefixes.push(cfg.feature);
|
|
28247
|
+
}
|
|
28248
|
+
for (const prefix of alphaPrefixes) {
|
|
28249
|
+
if (branch.startsWith(prefix)) {
|
|
28250
|
+
return "alpha";
|
|
28251
|
+
}
|
|
28252
|
+
}
|
|
28253
|
+
return "alpha";
|
|
28254
|
+
}
|
|
28255
|
+
function channelToDistTag(channel) {
|
|
28256
|
+
const tags = {
|
|
28257
|
+
latest: "latest",
|
|
28258
|
+
beta: "beta",
|
|
28259
|
+
alpha: "alpha"
|
|
28260
|
+
};
|
|
28261
|
+
return tags[channel];
|
|
28262
|
+
}
|
|
28263
|
+
function describeChannel(channel) {
|
|
28264
|
+
const descriptions = {
|
|
28265
|
+
latest: "stable release published to npm @latest",
|
|
28266
|
+
beta: "pre-release published to npm @beta (develop branch)",
|
|
28267
|
+
alpha: "early pre-release published to npm @alpha (feature/hotfix branches)"
|
|
28268
|
+
};
|
|
28269
|
+
return descriptions[channel];
|
|
28270
|
+
}
|
|
28271
|
+
var init_channel = __esm({
|
|
28272
|
+
"src/core/release/channel.ts"() {
|
|
28273
|
+
"use strict";
|
|
28274
|
+
}
|
|
28275
|
+
});
|
|
28276
|
+
|
|
28277
|
+
// src/core/release/release-config.ts
|
|
28278
|
+
import { existsSync as existsSync48, readFileSync as readFileSync38 } from "node:fs";
|
|
27985
28279
|
import { join as join46 } from "node:path";
|
|
28280
|
+
function readConfigValueSync(path, defaultValue, cwd) {
|
|
28281
|
+
try {
|
|
28282
|
+
const configPath = join46(getCleoDir(cwd), "config.json");
|
|
28283
|
+
if (!existsSync48(configPath)) return defaultValue;
|
|
28284
|
+
const config = JSON.parse(readFileSync38(configPath, "utf-8"));
|
|
28285
|
+
const keys = path.split(".");
|
|
28286
|
+
let value = config;
|
|
28287
|
+
for (const key of keys) {
|
|
28288
|
+
if (value == null || typeof value !== "object") return defaultValue;
|
|
28289
|
+
value = value[key];
|
|
28290
|
+
}
|
|
28291
|
+
return value ?? defaultValue;
|
|
28292
|
+
} catch {
|
|
28293
|
+
return defaultValue;
|
|
28294
|
+
}
|
|
28295
|
+
}
|
|
28296
|
+
function loadReleaseConfig(cwd) {
|
|
28297
|
+
return {
|
|
28298
|
+
versioningScheme: readConfigValueSync("release.versioning.scheme", DEFAULTS2.versioningScheme, cwd),
|
|
28299
|
+
tagPrefix: readConfigValueSync("release.versioning.tagPrefix", DEFAULTS2.tagPrefix, cwd),
|
|
28300
|
+
changelogFormat: readConfigValueSync("release.changelog.format", DEFAULTS2.changelogFormat, cwd),
|
|
28301
|
+
changelogFile: readConfigValueSync("release.changelog.file", DEFAULTS2.changelogFile, cwd),
|
|
28302
|
+
artifactType: readConfigValueSync("release.artifact.type", DEFAULTS2.artifactType, cwd),
|
|
28303
|
+
gates: readConfigValueSync("release.gates", [], cwd),
|
|
28304
|
+
versionBump: {
|
|
28305
|
+
files: readConfigValueSync("release.versionBump.files", [], cwd)
|
|
28306
|
+
},
|
|
28307
|
+
security: {
|
|
28308
|
+
enableProvenance: readConfigValueSync("release.security.enableProvenance", false, cwd),
|
|
28309
|
+
slsaLevel: readConfigValueSync("release.security.slsaLevel", 3, cwd),
|
|
28310
|
+
requireSignedCommits: readConfigValueSync("release.security.requireSignedCommits", false, cwd)
|
|
28311
|
+
}
|
|
28312
|
+
};
|
|
28313
|
+
}
|
|
28314
|
+
function getDefaultGitFlowConfig() {
|
|
28315
|
+
return {
|
|
28316
|
+
enabled: true,
|
|
28317
|
+
branches: {
|
|
28318
|
+
main: "main",
|
|
28319
|
+
develop: "develop",
|
|
28320
|
+
featurePrefix: "feature/",
|
|
28321
|
+
hotfixPrefix: "hotfix/",
|
|
28322
|
+
releasePrefix: "release/"
|
|
28323
|
+
}
|
|
28324
|
+
};
|
|
28325
|
+
}
|
|
28326
|
+
function getGitFlowConfig(config) {
|
|
28327
|
+
const defaults = getDefaultGitFlowConfig();
|
|
28328
|
+
if (!config.gitflow) return defaults;
|
|
28329
|
+
return {
|
|
28330
|
+
enabled: config.gitflow.enabled ?? defaults.enabled,
|
|
28331
|
+
branches: {
|
|
28332
|
+
main: config.gitflow.branches?.main ?? defaults.branches.main,
|
|
28333
|
+
develop: config.gitflow.branches?.develop ?? defaults.branches.develop,
|
|
28334
|
+
featurePrefix: config.gitflow.branches?.featurePrefix ?? defaults.branches.featurePrefix,
|
|
28335
|
+
hotfixPrefix: config.gitflow.branches?.hotfixPrefix ?? defaults.branches.hotfixPrefix,
|
|
28336
|
+
releasePrefix: config.gitflow.branches?.releasePrefix ?? defaults.branches.releasePrefix
|
|
28337
|
+
}
|
|
28338
|
+
};
|
|
28339
|
+
}
|
|
28340
|
+
function getDefaultChannelConfig2() {
|
|
28341
|
+
return {
|
|
28342
|
+
main: "latest",
|
|
28343
|
+
develop: "beta",
|
|
28344
|
+
feature: "alpha"
|
|
28345
|
+
};
|
|
28346
|
+
}
|
|
28347
|
+
function getChannelConfig(config) {
|
|
28348
|
+
const defaults = getDefaultChannelConfig2();
|
|
28349
|
+
if (!config.channels) return defaults;
|
|
28350
|
+
return {
|
|
28351
|
+
main: config.channels.main ?? defaults.main,
|
|
28352
|
+
develop: config.channels.develop ?? defaults.develop,
|
|
28353
|
+
feature: config.channels.feature ?? defaults.feature,
|
|
28354
|
+
custom: config.channels.custom
|
|
28355
|
+
};
|
|
28356
|
+
}
|
|
28357
|
+
function getPushMode(config) {
|
|
28358
|
+
return config.push?.mode ?? "auto";
|
|
28359
|
+
}
|
|
28360
|
+
var DEFAULTS2;
|
|
28361
|
+
var init_release_config = __esm({
|
|
28362
|
+
"src/core/release/release-config.ts"() {
|
|
28363
|
+
"use strict";
|
|
28364
|
+
init_paths();
|
|
28365
|
+
DEFAULTS2 = {
|
|
28366
|
+
versioningScheme: "calver",
|
|
28367
|
+
tagPrefix: "v",
|
|
28368
|
+
changelogFormat: "keepachangelog",
|
|
28369
|
+
changelogFile: "CHANGELOG.md",
|
|
28370
|
+
artifactType: "generic-tarball"
|
|
28371
|
+
};
|
|
28372
|
+
}
|
|
28373
|
+
});
|
|
28374
|
+
|
|
28375
|
+
// src/core/release/release-manifest.ts
|
|
28376
|
+
import { existsSync as existsSync49, renameSync as renameSync7 } from "node:fs";
|
|
28377
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
28378
|
+
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
28379
|
+
import { join as join47 } from "node:path";
|
|
27986
28380
|
import { eq as eq14, desc as desc4 } from "drizzle-orm";
|
|
27987
28381
|
function isValidVersion(version) {
|
|
27988
28382
|
return /^v?\d+\.\d+\.\d+(-[\w.]+)?(\+[\w.]+)?$/.test(version);
|
|
@@ -28074,25 +28468,67 @@ async function generateReleaseChangelog(version, loadTasksFn, cwd) {
|
|
|
28074
28468
|
const chores = [];
|
|
28075
28469
|
const docs = [];
|
|
28076
28470
|
const tests = [];
|
|
28077
|
-
const
|
|
28471
|
+
const changes = [];
|
|
28472
|
+
function stripConventionalPrefix(title) {
|
|
28473
|
+
return title.replace(/^(feat|fix|docs?|test|chore|refactor|style|ci|build|perf)(\([^)]+\))?:\s*/i, "");
|
|
28474
|
+
}
|
|
28475
|
+
function capitalize(s) {
|
|
28476
|
+
return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
|
|
28477
|
+
}
|
|
28478
|
+
function buildEntry(task) {
|
|
28479
|
+
const cleanTitle = capitalize(stripConventionalPrefix(task.title));
|
|
28480
|
+
const desc6 = task.description?.trim();
|
|
28481
|
+
const shouldIncludeDesc = (() => {
|
|
28482
|
+
if (!desc6 || desc6.length === 0) return false;
|
|
28483
|
+
const titleNorm = cleanTitle.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim();
|
|
28484
|
+
const descNorm = desc6.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim();
|
|
28485
|
+
if (titleNorm === descNorm) return false;
|
|
28486
|
+
if (descNorm.startsWith(titleNorm) && descNorm.length < titleNorm.length * 1.3) return false;
|
|
28487
|
+
return desc6.length >= 20;
|
|
28488
|
+
})();
|
|
28489
|
+
if (shouldIncludeDesc) {
|
|
28490
|
+
const descDisplay = desc6.length > 150 ? desc6.slice(0, 147) + "..." : desc6;
|
|
28491
|
+
return `- **${cleanTitle}**: ${descDisplay} (${task.id})`;
|
|
28492
|
+
}
|
|
28493
|
+
return `- ${cleanTitle} (${task.id})`;
|
|
28494
|
+
}
|
|
28495
|
+
function categorizeTask(task) {
|
|
28496
|
+
if (task.type === "epic") return "changes";
|
|
28497
|
+
const labels = task.labels ?? [];
|
|
28498
|
+
const titleLower = stripConventionalPrefix(task.title).toLowerCase();
|
|
28499
|
+
const rawTitleLower = task.title.toLowerCase();
|
|
28500
|
+
if (/^feat(\([^)]+\))?:/.test(task.title.toLowerCase())) return "features";
|
|
28501
|
+
if (/^fix(\([^)]+\))?:/.test(task.title.toLowerCase())) return "fixes";
|
|
28502
|
+
if (/^docs?(\([^)]+\))?:/.test(task.title.toLowerCase())) return "docs";
|
|
28503
|
+
if (/^test(\([^)]+\))?:/.test(task.title.toLowerCase())) return "tests";
|
|
28504
|
+
if (/^(chore|refactor|style|ci|build|perf)(\([^)]+\))?:/.test(task.title.toLowerCase())) return "chores";
|
|
28505
|
+
if (labels.some((l) => ["feat", "feature", "enhancement", "add"].includes(l.toLowerCase()))) return "features";
|
|
28506
|
+
if (labels.some((l) => ["fix", "bug", "bugfix", "regression"].includes(l.toLowerCase()))) return "fixes";
|
|
28507
|
+
if (labels.some((l) => ["docs", "documentation"].includes(l.toLowerCase()))) return "docs";
|
|
28508
|
+
if (labels.some((l) => ["test", "testing"].includes(l.toLowerCase()))) return "tests";
|
|
28509
|
+
if (labels.some((l) => ["chore", "refactor", "cleanup", "maintenance"].includes(l.toLowerCase()))) return "chores";
|
|
28510
|
+
if (titleLower.startsWith("add ") || titleLower.includes("implement") || titleLower.startsWith("create ") || titleLower.startsWith("introduce ")) return "features";
|
|
28511
|
+
if (titleLower.includes("bug") || titleLower.startsWith("fix") || titleLower.includes("regression") || titleLower.includes("broken")) return "fixes";
|
|
28512
|
+
if (titleLower.startsWith("doc") || titleLower.includes("documentation") || titleLower.includes("readme") || titleLower.includes("changelog")) return "docs";
|
|
28513
|
+
if (titleLower.startsWith("test") || titleLower.includes("test") && titleLower.includes("add")) return "tests";
|
|
28514
|
+
if (titleLower.startsWith("chore") || titleLower.includes("refactor") || titleLower.includes("cleanup") || titleLower.includes("migrate") || titleLower.includes("upgrade") || titleLower.includes("remove ") || titleLower.startsWith("audit")) return "chores";
|
|
28515
|
+
if (rawTitleLower.startsWith("feat")) return "features";
|
|
28516
|
+
return "changes";
|
|
28517
|
+
}
|
|
28078
28518
|
for (const taskId of releaseTasks) {
|
|
28079
28519
|
const task = taskMap.get(taskId);
|
|
28080
28520
|
if (!task) continue;
|
|
28081
|
-
|
|
28082
|
-
|
|
28083
|
-
if (
|
|
28084
|
-
|
|
28085
|
-
|
|
28086
|
-
|
|
28087
|
-
|
|
28088
|
-
|
|
28089
|
-
|
|
28090
|
-
|
|
28091
|
-
|
|
28092
|
-
chores.push(entry);
|
|
28093
|
-
} else {
|
|
28094
|
-
other.push(entry);
|
|
28095
|
-
}
|
|
28521
|
+
if (task.type === "epic") continue;
|
|
28522
|
+
if (task.labels?.some((l) => l.toLowerCase() === "epic")) continue;
|
|
28523
|
+
if (/^epic:/i.test(task.title.trim())) continue;
|
|
28524
|
+
const category = categorizeTask(task);
|
|
28525
|
+
const entry = buildEntry(task);
|
|
28526
|
+
if (category === "features") features.push(entry);
|
|
28527
|
+
else if (category === "fixes") fixes.push(entry);
|
|
28528
|
+
else if (category === "docs") docs.push(entry);
|
|
28529
|
+
else if (category === "tests") tests.push(entry);
|
|
28530
|
+
else if (category === "chores") chores.push(entry);
|
|
28531
|
+
else changes.push(entry);
|
|
28096
28532
|
}
|
|
28097
28533
|
const sections = [];
|
|
28098
28534
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -28127,14 +28563,14 @@ async function generateReleaseChangelog(version, loadTasksFn, cwd) {
|
|
|
28127
28563
|
sections.push(...chores);
|
|
28128
28564
|
sections.push("");
|
|
28129
28565
|
}
|
|
28130
|
-
if (
|
|
28131
|
-
sections.push("###
|
|
28132
|
-
sections.push(...
|
|
28566
|
+
if (changes.length > 0) {
|
|
28567
|
+
sections.push("### Changes");
|
|
28568
|
+
sections.push(...changes);
|
|
28133
28569
|
sections.push("");
|
|
28134
28570
|
}
|
|
28135
28571
|
const changelog = sections.join("\n");
|
|
28136
28572
|
await db.update(releaseManifests).set({ changelog }).where(eq14(releaseManifests.version, normalizedVersion)).run();
|
|
28137
|
-
const changelogPath =
|
|
28573
|
+
const changelogPath = join47(cwd ?? process.cwd(), "CHANGELOG.md");
|
|
28138
28574
|
let existingChangelogContent = "";
|
|
28139
28575
|
try {
|
|
28140
28576
|
existingChangelogContent = await readFile10(changelogPath, "utf8");
|
|
@@ -28153,7 +28589,7 @@ async function generateReleaseChangelog(version, loadTasksFn, cwd) {
|
|
|
28153
28589
|
docs: docs.length,
|
|
28154
28590
|
tests: tests.length,
|
|
28155
28591
|
chores: chores.length,
|
|
28156
|
-
|
|
28592
|
+
changes: changes.length
|
|
28157
28593
|
}
|
|
28158
28594
|
};
|
|
28159
28595
|
}
|
|
@@ -28254,19 +28690,19 @@ async function runReleaseGates(version, loadTasksFn, cwd) {
|
|
|
28254
28690
|
message: incompleteTasks.length === 0 ? "All tasks completed" : `${incompleteTasks.length} tasks not completed: ${incompleteTasks.join(", ")}`
|
|
28255
28691
|
});
|
|
28256
28692
|
const projectRoot = cwd ?? getProjectRoot();
|
|
28257
|
-
const distPath =
|
|
28258
|
-
const isNodeProject =
|
|
28693
|
+
const distPath = join47(projectRoot, "dist", "cli", "index.js");
|
|
28694
|
+
const isNodeProject = existsSync49(join47(projectRoot, "package.json"));
|
|
28259
28695
|
if (isNodeProject) {
|
|
28260
28696
|
gates.push({
|
|
28261
28697
|
name: "build_artifact",
|
|
28262
|
-
status:
|
|
28263
|
-
message:
|
|
28698
|
+
status: existsSync49(distPath) ? "passed" : "failed",
|
|
28699
|
+
message: existsSync49(distPath) ? "dist/cli/index.js present" : "dist/ not built \u2014 run: npm run build"
|
|
28264
28700
|
});
|
|
28265
28701
|
}
|
|
28266
28702
|
let workingTreeClean = true;
|
|
28267
28703
|
let dirtyFiles = [];
|
|
28268
28704
|
try {
|
|
28269
|
-
const porcelain =
|
|
28705
|
+
const porcelain = execFileSync7("git", ["status", "--porcelain"], {
|
|
28270
28706
|
cwd: projectRoot,
|
|
28271
28707
|
encoding: "utf-8",
|
|
28272
28708
|
stdio: "pipe"
|
|
@@ -28283,27 +28719,60 @@ async function runReleaseGates(version, loadTasksFn, cwd) {
|
|
|
28283
28719
|
const isPreRelease = normalizedVersion.includes("-");
|
|
28284
28720
|
let currentBranch = "";
|
|
28285
28721
|
try {
|
|
28286
|
-
currentBranch =
|
|
28722
|
+
currentBranch = execFileSync7("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
28287
28723
|
cwd: projectRoot,
|
|
28288
28724
|
encoding: "utf-8",
|
|
28289
28725
|
stdio: "pipe"
|
|
28290
28726
|
}).trim();
|
|
28291
28727
|
} catch {
|
|
28292
28728
|
}
|
|
28293
|
-
const
|
|
28294
|
-
const
|
|
28729
|
+
const releaseConfig = loadReleaseConfig(cwd);
|
|
28730
|
+
const gitFlowCfg = getGitFlowConfig(releaseConfig);
|
|
28731
|
+
const channelCfg = getChannelConfig(releaseConfig);
|
|
28732
|
+
const expectedBranch = isPreRelease ? gitFlowCfg.branches.develop : gitFlowCfg.branches.main;
|
|
28733
|
+
const isFeatureBranch = currentBranch.startsWith(gitFlowCfg.branches.featurePrefix) || currentBranch.startsWith(gitFlowCfg.branches.hotfixPrefix) || currentBranch.startsWith(gitFlowCfg.branches.releasePrefix);
|
|
28734
|
+
const branchOk = !currentBranch || currentBranch === "HEAD" || currentBranch === expectedBranch || isPreRelease && isFeatureBranch;
|
|
28735
|
+
const detectedChannel = currentBranch ? resolveChannelFromBranch(currentBranch, channelCfg) : isPreRelease ? "beta" : "latest";
|
|
28295
28736
|
gates.push({
|
|
28296
28737
|
name: "branch_target",
|
|
28297
28738
|
status: branchOk ? "passed" : "failed",
|
|
28298
|
-
message: branchOk ? `On correct branch: ${currentBranch}` : `Expected branch '${expectedBranch}' for ${isPreRelease ? "pre-release" : "stable"} release, but on '${currentBranch}'`
|
|
28739
|
+
message: branchOk ? `On correct branch: ${currentBranch} (channel: ${detectedChannel})` : `Expected branch '${expectedBranch}' for ${isPreRelease ? "pre-release" : "stable"} release, but on '${currentBranch}'`
|
|
28740
|
+
});
|
|
28741
|
+
const pushMode = getPushMode(releaseConfig);
|
|
28742
|
+
let requiresPR = false;
|
|
28743
|
+
if (pushMode === "pr") {
|
|
28744
|
+
requiresPR = true;
|
|
28745
|
+
} else if (pushMode === "auto") {
|
|
28746
|
+
try {
|
|
28747
|
+
const protectionResult = await detectBranchProtection(
|
|
28748
|
+
expectedBranch,
|
|
28749
|
+
"origin",
|
|
28750
|
+
projectRoot
|
|
28751
|
+
);
|
|
28752
|
+
requiresPR = protectionResult.protected;
|
|
28753
|
+
} catch {
|
|
28754
|
+
requiresPR = false;
|
|
28755
|
+
}
|
|
28756
|
+
}
|
|
28757
|
+
gates.push({
|
|
28758
|
+
name: "branch_protection",
|
|
28759
|
+
status: "passed",
|
|
28760
|
+
message: requiresPR ? `Branch '${expectedBranch}' is protected \u2014 release.ship will create a PR` : `Branch '${expectedBranch}' allows direct push`
|
|
28299
28761
|
});
|
|
28300
28762
|
const allPassed = gates.every((g) => g.status === "passed");
|
|
28763
|
+
const metadata = {
|
|
28764
|
+
channel: detectedChannel,
|
|
28765
|
+
requiresPR,
|
|
28766
|
+
targetBranch: expectedBranch,
|
|
28767
|
+
currentBranch
|
|
28768
|
+
};
|
|
28301
28769
|
return {
|
|
28302
28770
|
version: normalizedVersion,
|
|
28303
28771
|
allPassed,
|
|
28304
28772
|
gates,
|
|
28305
28773
|
passedCount: gates.filter((g) => g.status === "passed").length,
|
|
28306
|
-
failedCount: gates.filter((g) => g.status === "failed").length
|
|
28774
|
+
failedCount: gates.filter((g) => g.status === "failed").length,
|
|
28775
|
+
metadata
|
|
28307
28776
|
};
|
|
28308
28777
|
}
|
|
28309
28778
|
async function rollbackRelease(version, reason, cwd) {
|
|
@@ -28326,7 +28795,7 @@ async function rollbackRelease(version, reason, cwd) {
|
|
|
28326
28795
|
};
|
|
28327
28796
|
}
|
|
28328
28797
|
async function readPushPolicy(cwd) {
|
|
28329
|
-
const configPath =
|
|
28798
|
+
const configPath = join47(getCleoDirAbsolute(cwd), "config.json");
|
|
28330
28799
|
const config = await readJson(configPath);
|
|
28331
28800
|
if (!config) return void 0;
|
|
28332
28801
|
const release2 = config.release;
|
|
@@ -28340,6 +28809,33 @@ async function pushRelease(version, remote, cwd, opts) {
|
|
|
28340
28809
|
const normalizedVersion = normalizeVersion(version);
|
|
28341
28810
|
const projectRoot = getProjectRoot(cwd);
|
|
28342
28811
|
const pushPolicy = await readPushPolicy(cwd);
|
|
28812
|
+
const configPushMode = getPushMode(loadReleaseConfig(cwd));
|
|
28813
|
+
const effectivePushMode = opts?.mode ?? pushPolicy?.mode ?? configPushMode;
|
|
28814
|
+
if (effectivePushMode === "pr" || effectivePushMode === "auto") {
|
|
28815
|
+
const targetRemoteForCheck = remote ?? pushPolicy?.remote ?? "origin";
|
|
28816
|
+
let branchIsProtected = effectivePushMode === "pr";
|
|
28817
|
+
if (effectivePushMode === "auto") {
|
|
28818
|
+
try {
|
|
28819
|
+
const protection = await detectBranchProtection(
|
|
28820
|
+
pushPolicy?.allowedBranches?.[0] ?? "main",
|
|
28821
|
+
targetRemoteForCheck,
|
|
28822
|
+
projectRoot
|
|
28823
|
+
);
|
|
28824
|
+
branchIsProtected = protection.protected;
|
|
28825
|
+
} catch {
|
|
28826
|
+
branchIsProtected = false;
|
|
28827
|
+
}
|
|
28828
|
+
}
|
|
28829
|
+
if (branchIsProtected) {
|
|
28830
|
+
return {
|
|
28831
|
+
version: normalizedVersion,
|
|
28832
|
+
status: "requires_pr",
|
|
28833
|
+
remote: targetRemoteForCheck,
|
|
28834
|
+
pushedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28835
|
+
requiresPR: true
|
|
28836
|
+
};
|
|
28837
|
+
}
|
|
28838
|
+
}
|
|
28343
28839
|
if (pushPolicy && pushPolicy.enabled === false && !opts?.explicitPush) {
|
|
28344
28840
|
throw new Error(
|
|
28345
28841
|
"Push is disabled by config (release.push.enabled=false). Use --push to override."
|
|
@@ -28347,7 +28843,7 @@ async function pushRelease(version, remote, cwd, opts) {
|
|
|
28347
28843
|
}
|
|
28348
28844
|
const targetRemote = remote ?? pushPolicy?.remote ?? "origin";
|
|
28349
28845
|
if (pushPolicy?.requireCleanTree) {
|
|
28350
|
-
const statusOutput =
|
|
28846
|
+
const statusOutput = execFileSync7("git", ["status", "--porcelain"], {
|
|
28351
28847
|
cwd: projectRoot,
|
|
28352
28848
|
timeout: 1e4,
|
|
28353
28849
|
encoding: "utf-8",
|
|
@@ -28360,7 +28856,7 @@ async function pushRelease(version, remote, cwd, opts) {
|
|
|
28360
28856
|
}
|
|
28361
28857
|
}
|
|
28362
28858
|
if (pushPolicy?.allowedBranches && pushPolicy.allowedBranches.length > 0) {
|
|
28363
|
-
const currentBranch =
|
|
28859
|
+
const currentBranch = execFileSync7("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
28364
28860
|
cwd: projectRoot,
|
|
28365
28861
|
timeout: 1e4,
|
|
28366
28862
|
encoding: "utf-8",
|
|
@@ -28372,7 +28868,7 @@ async function pushRelease(version, remote, cwd, opts) {
|
|
|
28372
28868
|
);
|
|
28373
28869
|
}
|
|
28374
28870
|
}
|
|
28375
|
-
|
|
28871
|
+
execFileSync7("git", ["push", targetRemote, "--follow-tags"], {
|
|
28376
28872
|
cwd: projectRoot,
|
|
28377
28873
|
timeout: 6e4,
|
|
28378
28874
|
encoding: "utf-8",
|
|
@@ -28403,6 +28899,9 @@ var init_release_manifest = __esm({
|
|
|
28403
28899
|
init_paths();
|
|
28404
28900
|
init_json();
|
|
28405
28901
|
init_changelog_writer();
|
|
28902
|
+
init_github_pr();
|
|
28903
|
+
init_channel();
|
|
28904
|
+
init_release_config();
|
|
28406
28905
|
}
|
|
28407
28906
|
});
|
|
28408
28907
|
|
|
@@ -28483,7 +28982,7 @@ var init_guards = __esm({
|
|
|
28483
28982
|
});
|
|
28484
28983
|
|
|
28485
28984
|
// src/dispatch/engines/release-engine.ts
|
|
28486
|
-
import { execFileSync as
|
|
28985
|
+
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
28487
28986
|
function isAgentContext() {
|
|
28488
28987
|
return !!(process.env["CLEO_SESSION_ID"] || process.env["CLAUDE_AGENT_TYPE"]);
|
|
28489
28988
|
}
|
|
@@ -28625,7 +29124,7 @@ async function releasePush(version, remote, projectRoot, opts) {
|
|
|
28625
29124
|
const result = await pushRelease(version, remote, projectRoot, opts);
|
|
28626
29125
|
let commitSha;
|
|
28627
29126
|
try {
|
|
28628
|
-
commitSha =
|
|
29127
|
+
commitSha = execFileSync8("git", ["rev-parse", "HEAD"], {
|
|
28629
29128
|
cwd: projectRoot ?? process.cwd(),
|
|
28630
29129
|
encoding: "utf-8",
|
|
28631
29130
|
stdio: "pipe"
|
|
@@ -28655,7 +29154,17 @@ async function releaseShip(params, projectRoot) {
|
|
|
28655
29154
|
return engineError("E_INVALID_INPUT", "epicId is required");
|
|
28656
29155
|
}
|
|
28657
29156
|
const cwd = projectRoot ?? resolveProjectRoot();
|
|
29157
|
+
const logStep = (n, total, label, done, error) => {
|
|
29158
|
+
if (done === void 0) {
|
|
29159
|
+
console.log(`[Step ${n}/${total}] ${label}...`);
|
|
29160
|
+
} else if (done) {
|
|
29161
|
+
console.log(` \u2713 ${label}`);
|
|
29162
|
+
} else {
|
|
29163
|
+
console.log(` \u2717 ${label}: ${error ?? "failed"}`);
|
|
29164
|
+
}
|
|
29165
|
+
};
|
|
28658
29166
|
try {
|
|
29167
|
+
logStep(1, 7, "Validate release gates");
|
|
28659
29168
|
const gatesResult = await runReleaseGates(
|
|
28660
29169
|
version,
|
|
28661
29170
|
() => loadTasks2(projectRoot),
|
|
@@ -28663,10 +29172,32 @@ async function releaseShip(params, projectRoot) {
|
|
|
28663
29172
|
);
|
|
28664
29173
|
if (gatesResult && !gatesResult.allPassed) {
|
|
28665
29174
|
const failedGates = gatesResult.gates.filter((g) => g.status === "failed");
|
|
29175
|
+
logStep(1, 7, "Validate release gates", false, failedGates.map((g) => g.name).join(", "));
|
|
28666
29176
|
return engineError("E_LIFECYCLE_GATE_FAILED", `Release gates failed for ${version}: ${failedGates.map((g) => g.name).join(", ")}`, {
|
|
28667
29177
|
details: { gates: gatesResult.gates, failedCount: gatesResult.failedCount }
|
|
28668
29178
|
});
|
|
28669
29179
|
}
|
|
29180
|
+
logStep(1, 7, "Validate release gates", true);
|
|
29181
|
+
let resolvedChannel = "latest";
|
|
29182
|
+
let currentBranchForPR = "HEAD";
|
|
29183
|
+
try {
|
|
29184
|
+
const branchName = execFileSync8("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
29185
|
+
cwd,
|
|
29186
|
+
encoding: "utf-8",
|
|
29187
|
+
stdio: "pipe"
|
|
29188
|
+
}).trim();
|
|
29189
|
+
currentBranchForPR = branchName;
|
|
29190
|
+
const channelEnum = resolveChannelFromBranch(branchName);
|
|
29191
|
+
resolvedChannel = channelToDistTag(channelEnum);
|
|
29192
|
+
} catch {
|
|
29193
|
+
}
|
|
29194
|
+
const gateMetadata = gatesResult.metadata;
|
|
29195
|
+
const requiresPRFromGates = gateMetadata?.requiresPR ?? false;
|
|
29196
|
+
const targetBranchFromGates = gateMetadata?.targetBranch;
|
|
29197
|
+
if (gateMetadata?.currentBranch) {
|
|
29198
|
+
currentBranchForPR = gateMetadata.currentBranch;
|
|
29199
|
+
}
|
|
29200
|
+
logStep(2, 7, "Check epic completeness");
|
|
28670
29201
|
let releaseTaskIds = [];
|
|
28671
29202
|
try {
|
|
28672
29203
|
const manifest = await showManifestRelease(version, projectRoot);
|
|
@@ -28677,10 +29208,13 @@ async function releaseShip(params, projectRoot) {
|
|
|
28677
29208
|
const epicCheck = await checkEpicCompleteness(releaseTaskIds, projectRoot, epicAccessor);
|
|
28678
29209
|
if (epicCheck.hasIncomplete) {
|
|
28679
29210
|
const incomplete = epicCheck.epics.filter((e) => e.missingChildren.length > 0).map((e) => `${e.epicId}: missing ${e.missingChildren.map((c) => c.id).join(", ")}`).join("; ");
|
|
29211
|
+
logStep(2, 7, "Check epic completeness", false, incomplete);
|
|
28680
29212
|
return engineError("E_LIFECYCLE_GATE_FAILED", `Epic completeness check failed: ${incomplete}`, {
|
|
28681
29213
|
details: { epics: epicCheck.epics }
|
|
28682
29214
|
});
|
|
28683
29215
|
}
|
|
29216
|
+
logStep(2, 7, "Check epic completeness", true);
|
|
29217
|
+
logStep(3, 7, "Check task double-listing");
|
|
28684
29218
|
const allReleases = await listManifestReleases(projectRoot);
|
|
28685
29219
|
const existingReleases = (allReleases.releases ?? []).filter((r) => r.version !== version);
|
|
28686
29220
|
const doubleCheck = checkDoubleListing(
|
|
@@ -28689,10 +29223,13 @@ async function releaseShip(params, projectRoot) {
|
|
|
28689
29223
|
);
|
|
28690
29224
|
if (doubleCheck.hasDoubleListing) {
|
|
28691
29225
|
const dupes = doubleCheck.duplicates.map((d) => `${d.taskId} (in ${d.releases.join(", ")})`).join("; ");
|
|
29226
|
+
logStep(3, 7, "Check task double-listing", false, dupes);
|
|
28692
29227
|
return engineError("E_VALIDATION", `Double-listing detected: ${dupes}`, {
|
|
28693
29228
|
details: { duplicates: doubleCheck.duplicates }
|
|
28694
29229
|
});
|
|
28695
29230
|
}
|
|
29231
|
+
logStep(3, 7, "Check task double-listing", true);
|
|
29232
|
+
logStep(4, 7, "Generate CHANGELOG");
|
|
28696
29233
|
const changelogResult = await generateReleaseChangelog(
|
|
28697
29234
|
version,
|
|
28698
29235
|
() => loadTasks2(projectRoot),
|
|
@@ -28700,62 +29237,128 @@ async function releaseShip(params, projectRoot) {
|
|
|
28700
29237
|
);
|
|
28701
29238
|
const changelogPath = `${cwd}/CHANGELOG.md`;
|
|
28702
29239
|
const generatedContent = changelogResult.changelog ?? "";
|
|
29240
|
+
logStep(4, 7, "Generate CHANGELOG", true);
|
|
29241
|
+
const loadedConfig = loadReleaseConfig(cwd);
|
|
29242
|
+
const pushMode = getPushMode(loadedConfig);
|
|
29243
|
+
const gitflowCfg = getGitFlowConfig(loadedConfig);
|
|
29244
|
+
const targetBranch = targetBranchFromGates ?? gitflowCfg.branches.main;
|
|
28703
29245
|
if (dryRun) {
|
|
28704
|
-
|
|
28705
|
-
|
|
28706
|
-
|
|
28707
|
-
|
|
28708
|
-
|
|
28709
|
-
|
|
28710
|
-
|
|
28711
|
-
|
|
28712
|
-
|
|
28713
|
-
|
|
28714
|
-
|
|
28715
|
-
|
|
28716
|
-
|
|
28717
|
-
]
|
|
28718
|
-
}
|
|
29246
|
+
const wouldCreatePR = requiresPRFromGates || pushMode === "pr";
|
|
29247
|
+
const dryRunOutput = {
|
|
29248
|
+
version,
|
|
29249
|
+
epicId,
|
|
29250
|
+
dryRun: true,
|
|
29251
|
+
channel: resolvedChannel,
|
|
29252
|
+
pushMode,
|
|
29253
|
+
wouldDo: [
|
|
29254
|
+
`write CHANGELOG section for ${version} (${generatedContent.length} chars)`,
|
|
29255
|
+
"git add CHANGELOG.md",
|
|
29256
|
+
`git commit -m "release: ship v${version} (${epicId})"`,
|
|
29257
|
+
`git tag -a v${version} -m "Release v${version}"`
|
|
29258
|
+
]
|
|
28719
29259
|
};
|
|
29260
|
+
if (wouldCreatePR) {
|
|
29261
|
+
const ghAvailable = isGhCliAvailable();
|
|
29262
|
+
dryRunOutput["wouldDo"].push(
|
|
29263
|
+
ghAvailable ? `gh pr create --base ${targetBranch} --head ${currentBranchForPR} --title "release: ship v${version}"` : `manual PR: ${currentBranchForPR} \u2192 ${targetBranch} (gh CLI not available)`
|
|
29264
|
+
);
|
|
29265
|
+
dryRunOutput["wouldCreatePR"] = true;
|
|
29266
|
+
dryRunOutput["prTitle"] = `release: ship v${version}`;
|
|
29267
|
+
dryRunOutput["prTargetBranch"] = targetBranch;
|
|
29268
|
+
} else {
|
|
29269
|
+
dryRunOutput["wouldDo"].push(
|
|
29270
|
+
`git push ${remote ?? "origin"} --follow-tags`
|
|
29271
|
+
);
|
|
29272
|
+
dryRunOutput["wouldCreatePR"] = false;
|
|
29273
|
+
}
|
|
29274
|
+
dryRunOutput["wouldDo"].push("markReleasePushed(...)");
|
|
29275
|
+
return { success: true, data: dryRunOutput };
|
|
28720
29276
|
}
|
|
28721
|
-
|
|
29277
|
+
logStep(5, 7, "Commit release");
|
|
28722
29278
|
const gitCwd = { cwd, encoding: "utf-8", stdio: "pipe" };
|
|
28723
29279
|
try {
|
|
28724
|
-
|
|
29280
|
+
execFileSync8("git", ["add", "CHANGELOG.md"], gitCwd);
|
|
28725
29281
|
} catch (err) {
|
|
28726
29282
|
const msg = err.message ?? String(err);
|
|
29283
|
+
logStep(5, 7, "Commit release", false, `git add failed: ${msg}`);
|
|
28727
29284
|
return engineError("E_GENERAL", `git add failed: ${msg}`);
|
|
28728
29285
|
}
|
|
28729
29286
|
try {
|
|
28730
|
-
|
|
29287
|
+
execFileSync8(
|
|
28731
29288
|
"git",
|
|
28732
29289
|
["commit", "-m", `release: ship v${version} (${epicId})`],
|
|
28733
29290
|
gitCwd
|
|
28734
29291
|
);
|
|
28735
29292
|
} catch (err) {
|
|
28736
29293
|
const msg = err.stderr ?? err.message ?? String(err);
|
|
29294
|
+
logStep(5, 7, "Commit release", false, `git commit failed: ${msg}`);
|
|
28737
29295
|
return engineError("E_GENERAL", `git commit failed: ${msg}`);
|
|
28738
29296
|
}
|
|
29297
|
+
logStep(5, 7, "Commit release", true);
|
|
28739
29298
|
let commitSha;
|
|
28740
29299
|
try {
|
|
28741
|
-
commitSha =
|
|
29300
|
+
commitSha = execFileSync8("git", ["rev-parse", "HEAD"], gitCwd).toString().trim();
|
|
28742
29301
|
} catch {
|
|
28743
29302
|
}
|
|
29303
|
+
logStep(6, 7, "Tag release");
|
|
28744
29304
|
const gitTag = `v${version.replace(/^v/, "")}`;
|
|
28745
29305
|
try {
|
|
28746
|
-
|
|
29306
|
+
execFileSync8("git", ["tag", "-a", gitTag, "-m", `Release ${gitTag}`], gitCwd);
|
|
28747
29307
|
} catch (err) {
|
|
28748
29308
|
const msg = err.stderr ?? err.message ?? String(err);
|
|
29309
|
+
logStep(6, 7, "Tag release", false, `git tag failed: ${msg}`);
|
|
28749
29310
|
return engineError("E_GENERAL", `git tag failed: ${msg}`);
|
|
28750
29311
|
}
|
|
28751
|
-
|
|
28752
|
-
|
|
28753
|
-
|
|
28754
|
-
|
|
28755
|
-
|
|
28756
|
-
|
|
28757
|
-
|
|
29312
|
+
logStep(6, 7, "Tag release", true);
|
|
29313
|
+
logStep(7, 7, "Push / create PR");
|
|
29314
|
+
let prResult = null;
|
|
29315
|
+
const pushResult = await pushRelease(version, remote, projectRoot, {
|
|
29316
|
+
explicitPush: true,
|
|
29317
|
+
mode: pushMode
|
|
29318
|
+
});
|
|
29319
|
+
if (pushResult.requiresPR || requiresPRFromGates) {
|
|
29320
|
+
const prBody = buildPRBody({
|
|
29321
|
+
base: targetBranch,
|
|
29322
|
+
head: currentBranchForPR,
|
|
29323
|
+
title: `release: ship v${version}`,
|
|
29324
|
+
body: "",
|
|
29325
|
+
version,
|
|
29326
|
+
epicId,
|
|
29327
|
+
projectRoot: cwd
|
|
29328
|
+
});
|
|
29329
|
+
prResult = await createPullRequest({
|
|
29330
|
+
base: targetBranch,
|
|
29331
|
+
head: currentBranchForPR,
|
|
29332
|
+
title: `release: ship v${version}`,
|
|
29333
|
+
body: prBody,
|
|
29334
|
+
labels: ["release", resolvedChannel],
|
|
29335
|
+
version,
|
|
29336
|
+
epicId,
|
|
29337
|
+
projectRoot: cwd
|
|
28758
29338
|
});
|
|
29339
|
+
if (prResult.mode === "created") {
|
|
29340
|
+
console.log(` \u2713 Push / create PR`);
|
|
29341
|
+
console.log(` PR created: ${prResult.prUrl}`);
|
|
29342
|
+
console.log(` \u2192 Next: merge the PR, then CI will publish to npm @${resolvedChannel}`);
|
|
29343
|
+
} else if (prResult.mode === "skipped") {
|
|
29344
|
+
console.log(` \u2713 Push / create PR`);
|
|
29345
|
+
console.log(` PR already exists: ${prResult.prUrl}`);
|
|
29346
|
+
} else {
|
|
29347
|
+
console.log(` ! Push / create PR \u2014 manual PR required:`);
|
|
29348
|
+
console.log(prResult.instructions);
|
|
29349
|
+
}
|
|
29350
|
+
} else {
|
|
29351
|
+
try {
|
|
29352
|
+
execFileSync8("git", ["push", remote ?? "origin", "--follow-tags"], gitCwd);
|
|
29353
|
+
logStep(7, 7, "Push / create PR", true);
|
|
29354
|
+
} catch (err) {
|
|
29355
|
+
const execError = err;
|
|
29356
|
+
const msg = (execError.stderr ?? execError.message ?? "").slice(0, 500);
|
|
29357
|
+
logStep(7, 7, "Push / create PR", false, `git push failed: ${msg}`);
|
|
29358
|
+
return engineError("E_GENERAL", `git push failed: ${msg}`, {
|
|
29359
|
+
details: { exitCode: execError.status }
|
|
29360
|
+
});
|
|
29361
|
+
}
|
|
28759
29362
|
}
|
|
28760
29363
|
const pushedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
28761
29364
|
await markReleasePushed(version, pushedAt, projectRoot, { commitSha, gitTag });
|
|
@@ -28767,7 +29370,16 @@ async function releaseShip(params, projectRoot) {
|
|
|
28767
29370
|
commitSha,
|
|
28768
29371
|
gitTag,
|
|
28769
29372
|
pushedAt,
|
|
28770
|
-
changelog: changelogPath
|
|
29373
|
+
changelog: changelogPath,
|
|
29374
|
+
channel: resolvedChannel,
|
|
29375
|
+
...prResult ? {
|
|
29376
|
+
pr: {
|
|
29377
|
+
mode: prResult.mode,
|
|
29378
|
+
prUrl: prResult.prUrl,
|
|
29379
|
+
prNumber: prResult.prNumber,
|
|
29380
|
+
instructions: prResult.instructions
|
|
29381
|
+
}
|
|
29382
|
+
} : {}
|
|
28771
29383
|
}
|
|
28772
29384
|
};
|
|
28773
29385
|
} catch (err) {
|
|
@@ -28780,15 +29392,17 @@ var init_release_engine = __esm({
|
|
|
28780
29392
|
init_platform();
|
|
28781
29393
|
init_data_accessor();
|
|
28782
29394
|
init_release_manifest();
|
|
28783
|
-
init_changelog_writer();
|
|
28784
29395
|
init_guards();
|
|
29396
|
+
init_github_pr();
|
|
29397
|
+
init_channel();
|
|
29398
|
+
init_release_config();
|
|
28785
29399
|
init_error();
|
|
28786
29400
|
}
|
|
28787
29401
|
});
|
|
28788
29402
|
|
|
28789
29403
|
// src/dispatch/engines/template-parser.ts
|
|
28790
|
-
import { readFileSync as
|
|
28791
|
-
import { join as
|
|
29404
|
+
import { readFileSync as readFileSync39, readdirSync as readdirSync12, existsSync as existsSync50 } from "fs";
|
|
29405
|
+
import { join as join48 } from "path";
|
|
28792
29406
|
import { parse as parseYaml } from "yaml";
|
|
28793
29407
|
function deriveSubcommand(filename) {
|
|
28794
29408
|
let stem = filename.replace(/\.ya?ml$/i, "");
|
|
@@ -28802,8 +29416,8 @@ function deriveSubcommand(filename) {
|
|
|
28802
29416
|
return firstWord.toLowerCase();
|
|
28803
29417
|
}
|
|
28804
29418
|
function parseTemplateFile(templateDir, filename) {
|
|
28805
|
-
const filePath =
|
|
28806
|
-
const raw =
|
|
29419
|
+
const filePath = join48(templateDir, filename);
|
|
29420
|
+
const raw = readFileSync39(filePath, "utf-8");
|
|
28807
29421
|
const parsed = parseYaml(raw);
|
|
28808
29422
|
const name = typeof parsed.name === "string" ? parsed.name : filename;
|
|
28809
29423
|
const titlePrefix = typeof parsed.title === "string" ? parsed.title : "";
|
|
@@ -28852,8 +29466,8 @@ function parseTemplateFile(templateDir, filename) {
|
|
|
28852
29466
|
};
|
|
28853
29467
|
}
|
|
28854
29468
|
function parseIssueTemplates(projectRoot) {
|
|
28855
|
-
const templateDir =
|
|
28856
|
-
if (!
|
|
29469
|
+
const templateDir = join48(projectRoot, ".github", "ISSUE_TEMPLATE");
|
|
29470
|
+
if (!existsSync50(templateDir)) {
|
|
28857
29471
|
return engineError("E_NOT_FOUND", `Issue template directory not found: ${templateDir}`);
|
|
28858
29472
|
}
|
|
28859
29473
|
let files;
|
|
@@ -30532,26 +31146,26 @@ var init_check = __esm({
|
|
|
30532
31146
|
});
|
|
30533
31147
|
|
|
30534
31148
|
// src/core/adrs/validate.ts
|
|
30535
|
-
import { readFileSync as
|
|
30536
|
-
import { join as
|
|
31149
|
+
import { readFileSync as readFileSync40, readdirSync as readdirSync13, existsSync as existsSync51 } from "node:fs";
|
|
31150
|
+
import { join as join49 } from "node:path";
|
|
30537
31151
|
import AjvModule3 from "ajv";
|
|
30538
31152
|
async function validateAllAdrs(projectRoot) {
|
|
30539
|
-
const adrsDir =
|
|
30540
|
-
const schemaPath =
|
|
30541
|
-
if (!
|
|
31153
|
+
const adrsDir = join49(projectRoot, ".cleo", "adrs");
|
|
31154
|
+
const schemaPath = join49(projectRoot, "schemas", "adr-frontmatter.schema.json");
|
|
31155
|
+
if (!existsSync51(schemaPath)) {
|
|
30542
31156
|
return {
|
|
30543
31157
|
valid: false,
|
|
30544
31158
|
errors: [{ file: "schemas/adr-frontmatter.schema.json", field: "schema", message: "Schema file not found" }],
|
|
30545
31159
|
checked: 0
|
|
30546
31160
|
};
|
|
30547
31161
|
}
|
|
30548
|
-
if (!
|
|
31162
|
+
if (!existsSync51(adrsDir)) {
|
|
30549
31163
|
return { valid: true, errors: [], checked: 0 };
|
|
30550
31164
|
}
|
|
30551
|
-
const schema = JSON.parse(
|
|
31165
|
+
const schema = JSON.parse(readFileSync40(schemaPath, "utf-8"));
|
|
30552
31166
|
const ajv = new Ajv3({ allErrors: true });
|
|
30553
31167
|
const validate = ajv.compile(schema);
|
|
30554
|
-
const files = readdirSync13(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).map((f) =>
|
|
31168
|
+
const files = readdirSync13(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).map((f) => join49(adrsDir, f));
|
|
30555
31169
|
const errors = [];
|
|
30556
31170
|
for (const filePath of files) {
|
|
30557
31171
|
const record = parseAdrFile(filePath, projectRoot);
|
|
@@ -30578,15 +31192,15 @@ var init_validate = __esm({
|
|
|
30578
31192
|
});
|
|
30579
31193
|
|
|
30580
31194
|
// src/core/adrs/list.ts
|
|
30581
|
-
import { readdirSync as readdirSync14, existsSync as
|
|
30582
|
-
import { join as
|
|
31195
|
+
import { readdirSync as readdirSync14, existsSync as existsSync52 } from "node:fs";
|
|
31196
|
+
import { join as join50 } from "node:path";
|
|
30583
31197
|
async function listAdrs(projectRoot, opts) {
|
|
30584
|
-
const adrsDir =
|
|
30585
|
-
if (!
|
|
31198
|
+
const adrsDir = join50(projectRoot, ".cleo", "adrs");
|
|
31199
|
+
if (!existsSync52(adrsDir)) {
|
|
30586
31200
|
return { adrs: [], total: 0 };
|
|
30587
31201
|
}
|
|
30588
31202
|
const files = readdirSync14(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).sort();
|
|
30589
|
-
const records = files.map((f) => parseAdrFile(
|
|
31203
|
+
const records = files.map((f) => parseAdrFile(join50(adrsDir, f), projectRoot));
|
|
30590
31204
|
const filtered = records.filter((r) => {
|
|
30591
31205
|
if (opts?.status && r.frontmatter.Status !== opts.status) return false;
|
|
30592
31206
|
if (opts?.since && r.frontmatter.Date < opts.since) return false;
|
|
@@ -30611,14 +31225,14 @@ var init_list2 = __esm({
|
|
|
30611
31225
|
});
|
|
30612
31226
|
|
|
30613
31227
|
// src/core/adrs/show.ts
|
|
30614
|
-
import { existsSync as
|
|
30615
|
-
import { join as
|
|
31228
|
+
import { existsSync as existsSync53, readdirSync as readdirSync15 } from "node:fs";
|
|
31229
|
+
import { join as join51 } from "node:path";
|
|
30616
31230
|
async function showAdr(projectRoot, adrId) {
|
|
30617
|
-
const adrsDir =
|
|
30618
|
-
if (!
|
|
31231
|
+
const adrsDir = join51(projectRoot, ".cleo", "adrs");
|
|
31232
|
+
if (!existsSync53(adrsDir)) return null;
|
|
30619
31233
|
const files = readdirSync15(adrsDir).filter((f) => f.startsWith(adrId) && f.endsWith(".md"));
|
|
30620
31234
|
if (files.length === 0) return null;
|
|
30621
|
-
const filePath =
|
|
31235
|
+
const filePath = join51(adrsDir, files[0]);
|
|
30622
31236
|
return parseAdrFile(filePath, projectRoot);
|
|
30623
31237
|
}
|
|
30624
31238
|
var init_show2 = __esm({
|
|
@@ -30629,8 +31243,8 @@ var init_show2 = __esm({
|
|
|
30629
31243
|
});
|
|
30630
31244
|
|
|
30631
31245
|
// src/core/adrs/find.ts
|
|
30632
|
-
import { readdirSync as readdirSync16, existsSync as
|
|
30633
|
-
import { join as
|
|
31246
|
+
import { readdirSync as readdirSync16, existsSync as existsSync54 } from "node:fs";
|
|
31247
|
+
import { join as join52 } from "node:path";
|
|
30634
31248
|
function normalise(s) {
|
|
30635
31249
|
return s.toLowerCase().replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim();
|
|
30636
31250
|
}
|
|
@@ -30647,8 +31261,8 @@ function matchedTerms(target, terms) {
|
|
|
30647
31261
|
return terms.filter((term) => t.includes(term));
|
|
30648
31262
|
}
|
|
30649
31263
|
async function findAdrs(projectRoot, query, opts) {
|
|
30650
|
-
const adrsDir =
|
|
30651
|
-
if (!
|
|
31264
|
+
const adrsDir = join52(projectRoot, ".cleo", "adrs");
|
|
31265
|
+
if (!existsSync54(adrsDir)) {
|
|
30652
31266
|
return { adrs: [], query, total: 0 };
|
|
30653
31267
|
}
|
|
30654
31268
|
const files = readdirSync16(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).sort();
|
|
@@ -30657,7 +31271,7 @@ async function findAdrs(projectRoot, query, opts) {
|
|
|
30657
31271
|
const filterKeywords = opts?.keywords ? parseTags(opts.keywords) : null;
|
|
30658
31272
|
const results = [];
|
|
30659
31273
|
for (const file of files) {
|
|
30660
|
-
const record = parseAdrFile(
|
|
31274
|
+
const record = parseAdrFile(join52(adrsDir, file), projectRoot);
|
|
30661
31275
|
const fm = record.frontmatter;
|
|
30662
31276
|
if (opts?.status && fm.Status !== opts.status) continue;
|
|
30663
31277
|
if (filterTopics && filterTopics.length > 0) {
|
|
@@ -30735,12 +31349,12 @@ var init_adrs = __esm({
|
|
|
30735
31349
|
});
|
|
30736
31350
|
|
|
30737
31351
|
// src/core/admin/sync.ts
|
|
30738
|
-
import { join as
|
|
31352
|
+
import { join as join53 } from "node:path";
|
|
30739
31353
|
import { rm as rm2, rmdir, stat as stat2 } from "node:fs/promises";
|
|
30740
31354
|
async function getSyncStatus(projectRoot) {
|
|
30741
31355
|
try {
|
|
30742
31356
|
const cleoDir = getCleoDir(projectRoot);
|
|
30743
|
-
const stateFile =
|
|
31357
|
+
const stateFile = join53(cleoDir, "sync", "todowrite-session.json");
|
|
30744
31358
|
const sessionState = await readJson(stateFile);
|
|
30745
31359
|
if (!sessionState) {
|
|
30746
31360
|
return {
|
|
@@ -30784,8 +31398,8 @@ async function getSyncStatus(projectRoot) {
|
|
|
30784
31398
|
async function clearSyncState(projectRoot, dryRun) {
|
|
30785
31399
|
try {
|
|
30786
31400
|
const cleoDir = getCleoDir(projectRoot);
|
|
30787
|
-
const syncDir =
|
|
30788
|
-
const stateFile =
|
|
31401
|
+
const syncDir = join53(cleoDir, "sync");
|
|
31402
|
+
const stateFile = join53(syncDir, "todowrite-session.json");
|
|
30789
31403
|
let exists = false;
|
|
30790
31404
|
try {
|
|
30791
31405
|
await stat2(stateFile);
|
|
@@ -31552,8 +32166,8 @@ var init_import_tasks = __esm({
|
|
|
31552
32166
|
// src/core/snapshot/index.ts
|
|
31553
32167
|
import { createHash as createHash6 } from "node:crypto";
|
|
31554
32168
|
import { readFile as readFile13, writeFile as writeFile10, mkdir as mkdir10 } from "node:fs/promises";
|
|
31555
|
-
import { existsSync as
|
|
31556
|
-
import { join as
|
|
32169
|
+
import { existsSync as existsSync55 } from "node:fs";
|
|
32170
|
+
import { join as join54, dirname as dirname14 } from "node:path";
|
|
31557
32171
|
function toSnapshotTask(task) {
|
|
31558
32172
|
return {
|
|
31559
32173
|
id: task.id,
|
|
@@ -31606,7 +32220,7 @@ async function exportSnapshot(cwd) {
|
|
|
31606
32220
|
}
|
|
31607
32221
|
async function writeSnapshot(snapshot, outputPath) {
|
|
31608
32222
|
const dir = dirname14(outputPath);
|
|
31609
|
-
if (!
|
|
32223
|
+
if (!existsSync55(dir)) {
|
|
31610
32224
|
await mkdir10(dir, { recursive: true });
|
|
31611
32225
|
}
|
|
31612
32226
|
await writeFile10(outputPath, JSON.stringify(snapshot, null, 2) + "\n");
|
|
@@ -31622,7 +32236,7 @@ async function readSnapshot(inputPath) {
|
|
|
31622
32236
|
function getDefaultSnapshotPath(cwd) {
|
|
31623
32237
|
const cleoDir = getCleoDirAbsolute(cwd);
|
|
31624
32238
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
31625
|
-
return
|
|
32239
|
+
return join54(cleoDir, "snapshots", `snapshot-${timestamp}.json`);
|
|
31626
32240
|
}
|
|
31627
32241
|
async function importSnapshot(snapshot, cwd) {
|
|
31628
32242
|
const accessor = await getAccessor(cwd);
|
|
@@ -31920,15 +32534,15 @@ var init_archive_stats2 = __esm({
|
|
|
31920
32534
|
});
|
|
31921
32535
|
|
|
31922
32536
|
// src/core/project-info.ts
|
|
31923
|
-
import { readFileSync as
|
|
31924
|
-
import { join as
|
|
32537
|
+
import { readFileSync as readFileSync41, existsSync as existsSync56 } from "node:fs";
|
|
32538
|
+
import { join as join55 } from "node:path";
|
|
31925
32539
|
function getProjectInfoSync(cwd) {
|
|
31926
32540
|
const projectRoot = cwd ?? process.cwd();
|
|
31927
32541
|
const cleoDir = getCleoDirAbsolute(projectRoot);
|
|
31928
|
-
const infoPath =
|
|
31929
|
-
if (!
|
|
32542
|
+
const infoPath = join55(cleoDir, "project-info.json");
|
|
32543
|
+
if (!existsSync56(infoPath)) return null;
|
|
31930
32544
|
try {
|
|
31931
|
-
const raw =
|
|
32545
|
+
const raw = readFileSync41(infoPath, "utf-8");
|
|
31932
32546
|
const data = JSON.parse(raw);
|
|
31933
32547
|
if (typeof data.projectHash !== "string" || data.projectHash.length === 0) {
|
|
31934
32548
|
return null;
|
|
@@ -32015,8 +32629,8 @@ var init_defaults = __esm({
|
|
|
32015
32629
|
});
|
|
32016
32630
|
|
|
32017
32631
|
// src/mcp/lib/config.ts
|
|
32018
|
-
import { readFileSync as
|
|
32019
|
-
import { join as
|
|
32632
|
+
import { readFileSync as readFileSync42, existsSync as existsSync57 } from "fs";
|
|
32633
|
+
import { join as join56 } from "path";
|
|
32020
32634
|
function loadFromEnv(key) {
|
|
32021
32635
|
const envKey = `${ENV_PREFIX}${key.toUpperCase()}`;
|
|
32022
32636
|
return process.env[envKey];
|
|
@@ -32037,12 +32651,12 @@ function parseEnvValue2(key, value) {
|
|
|
32037
32651
|
}
|
|
32038
32652
|
function loadFromFile(projectRoot) {
|
|
32039
32653
|
const root = projectRoot || process.cwd();
|
|
32040
|
-
const configPath =
|
|
32041
|
-
if (!
|
|
32654
|
+
const configPath = join56(root, ".cleo", "config.json");
|
|
32655
|
+
if (!existsSync57(configPath)) {
|
|
32042
32656
|
return {};
|
|
32043
32657
|
}
|
|
32044
32658
|
try {
|
|
32045
|
-
const content =
|
|
32659
|
+
const content = readFileSync42(configPath, "utf-8");
|
|
32046
32660
|
const config = JSON.parse(content);
|
|
32047
32661
|
const result = {};
|
|
32048
32662
|
if (config.mcp) {
|
|
@@ -32382,8 +32996,8 @@ __export(session_grade_exports, {
|
|
|
32382
32996
|
gradeSession: () => gradeSession,
|
|
32383
32997
|
readGrades: () => readGrades
|
|
32384
32998
|
});
|
|
32385
|
-
import { join as
|
|
32386
|
-
import { existsSync as
|
|
32999
|
+
import { join as join57 } from "node:path";
|
|
33000
|
+
import { existsSync as existsSync58 } from "node:fs";
|
|
32387
33001
|
import { readFile as readFile14, appendFile, mkdir as mkdir11 } from "node:fs/promises";
|
|
32388
33002
|
async function gradeSession(sessionId, cwd) {
|
|
32389
33003
|
const sessionEntries = await queryAudit({ sessionId });
|
|
@@ -32563,9 +33177,9 @@ function detectDuplicateCreates(entries) {
|
|
|
32563
33177
|
async function appendGradeResult(result, cwd) {
|
|
32564
33178
|
try {
|
|
32565
33179
|
const cleoDir = getCleoDirAbsolute(cwd);
|
|
32566
|
-
const metricsDir =
|
|
33180
|
+
const metricsDir = join57(cleoDir, "metrics");
|
|
32567
33181
|
await mkdir11(metricsDir, { recursive: true });
|
|
32568
|
-
const gradesPath =
|
|
33182
|
+
const gradesPath = join57(metricsDir, "GRADES.jsonl");
|
|
32569
33183
|
const line = JSON.stringify({ ...result, evaluator: "auto" }) + "\n";
|
|
32570
33184
|
await appendFile(gradesPath, line, "utf8");
|
|
32571
33185
|
} catch {
|
|
@@ -32574,8 +33188,8 @@ async function appendGradeResult(result, cwd) {
|
|
|
32574
33188
|
async function readGrades(sessionId, cwd) {
|
|
32575
33189
|
try {
|
|
32576
33190
|
const cleoDir = getCleoDirAbsolute(cwd);
|
|
32577
|
-
const gradesPath =
|
|
32578
|
-
if (!
|
|
33191
|
+
const gradesPath = join57(cleoDir, "metrics", "GRADES.jsonl");
|
|
33192
|
+
if (!existsSync58(gradesPath)) return [];
|
|
32579
33193
|
const content = await readFile14(gradesPath, "utf8");
|
|
32580
33194
|
const results = content.split("\n").filter((l) => l.trim()).map((l) => JSON.parse(l));
|
|
32581
33195
|
return sessionId ? results.filter((r) => r.sessionId === sessionId) : results;
|
|
@@ -34812,6 +35426,7 @@ var init_phase = __esm({
|
|
|
34812
35426
|
});
|
|
34813
35427
|
|
|
34814
35428
|
// src/dispatch/domains/pipeline.ts
|
|
35429
|
+
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
34815
35430
|
var PipelineHandler;
|
|
34816
35431
|
var init_pipeline2 = __esm({
|
|
34817
35432
|
"src/dispatch/domains/pipeline.ts"() {
|
|
@@ -34822,6 +35437,7 @@ var init_pipeline2 = __esm({
|
|
|
34822
35437
|
init_data_accessor();
|
|
34823
35438
|
init_engine();
|
|
34824
35439
|
init_release_engine();
|
|
35440
|
+
init_channel();
|
|
34825
35441
|
init_phase();
|
|
34826
35442
|
init_phases();
|
|
34827
35443
|
init_pipeline_manifest_sqlite();
|
|
@@ -34907,6 +35523,7 @@ var init_pipeline2 = __esm({
|
|
|
34907
35523
|
"manifest.stats",
|
|
34908
35524
|
"release.list",
|
|
34909
35525
|
"release.show",
|
|
35526
|
+
"release.channel.show",
|
|
34910
35527
|
"phase.show",
|
|
34911
35528
|
"phase.list",
|
|
34912
35529
|
"chain.show",
|
|
@@ -35149,6 +35766,29 @@ var init_pipeline2 = __esm({
|
|
|
35149
35766
|
const result = await releaseShow(version, this.projectRoot);
|
|
35150
35767
|
return this.wrapEngineResult(result, "query", "release.show", startTime);
|
|
35151
35768
|
}
|
|
35769
|
+
case "channel.show": {
|
|
35770
|
+
let currentBranch = "unknown";
|
|
35771
|
+
try {
|
|
35772
|
+
currentBranch = execFileSync9("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
35773
|
+
encoding: "utf-8",
|
|
35774
|
+
stdio: "pipe",
|
|
35775
|
+
cwd: this.projectRoot
|
|
35776
|
+
}).trim();
|
|
35777
|
+
} catch {
|
|
35778
|
+
}
|
|
35779
|
+
const resolvedChannel = resolveChannelFromBranch(currentBranch);
|
|
35780
|
+
const distTag = channelToDistTag(resolvedChannel);
|
|
35781
|
+
const description = describeChannel(resolvedChannel);
|
|
35782
|
+
return this.wrapEngineResult({
|
|
35783
|
+
success: true,
|
|
35784
|
+
data: {
|
|
35785
|
+
branch: currentBranch,
|
|
35786
|
+
channel: resolvedChannel,
|
|
35787
|
+
distTag,
|
|
35788
|
+
description
|
|
35789
|
+
}
|
|
35790
|
+
}, "query", "release.channel.show", startTime);
|
|
35791
|
+
}
|
|
35152
35792
|
default:
|
|
35153
35793
|
return this.errorResponse(
|
|
35154
35794
|
"query",
|
|
@@ -35753,11 +36393,11 @@ var init_pipeline2 = __esm({
|
|
|
35753
36393
|
});
|
|
35754
36394
|
|
|
35755
36395
|
// src/core/issue/diagnostics.ts
|
|
35756
|
-
import { execFileSync as
|
|
36396
|
+
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
35757
36397
|
function collectDiagnostics() {
|
|
35758
36398
|
const getVersion3 = (cmd, args) => {
|
|
35759
36399
|
try {
|
|
35760
|
-
return
|
|
36400
|
+
return execFileSync10(cmd, args, {
|
|
35761
36401
|
encoding: "utf-8",
|
|
35762
36402
|
stdio: ["pipe", "pipe", "pipe"]
|
|
35763
36403
|
}).trim();
|
|
@@ -35808,7 +36448,7 @@ var init_build_config = __esm({
|
|
|
35808
36448
|
"use strict";
|
|
35809
36449
|
BUILD_CONFIG = {
|
|
35810
36450
|
"name": "@cleocode/cleo",
|
|
35811
|
-
"version": "2026.3.
|
|
36451
|
+
"version": "2026.3.17",
|
|
35812
36452
|
"description": "CLEO V2 - TypeScript task management CLI for AI coding agents",
|
|
35813
36453
|
"repository": {
|
|
35814
36454
|
"owner": "kryptobaseddev",
|
|
@@ -35817,7 +36457,7 @@ var init_build_config = __esm({
|
|
|
35817
36457
|
"url": "https://github.com/kryptobaseddev/cleo.git",
|
|
35818
36458
|
"issuesUrl": "https://github.com/kryptobaseddev/cleo/issues"
|
|
35819
36459
|
},
|
|
35820
|
-
"buildDate": "2026-03-
|
|
36460
|
+
"buildDate": "2026-03-07T07:08:25.337Z",
|
|
35821
36461
|
"templates": {
|
|
35822
36462
|
"issueTemplatesDir": "templates/issue-templates"
|
|
35823
36463
|
}
|
|
@@ -35826,8 +36466,8 @@ var init_build_config = __esm({
|
|
|
35826
36466
|
});
|
|
35827
36467
|
|
|
35828
36468
|
// src/core/issue/template-parser.ts
|
|
35829
|
-
import { existsSync as
|
|
35830
|
-
import { join as
|
|
36469
|
+
import { existsSync as existsSync59, readFileSync as readFileSync43, readdirSync as readdirSync17, writeFileSync as writeFileSync8 } from "node:fs";
|
|
36470
|
+
import { join as join58, basename as basename10 } from "node:path";
|
|
35831
36471
|
function extractYamlField(content, field) {
|
|
35832
36472
|
const regex = new RegExp(`^${field}:\\s*["']?(.+?)["']?\\s*$`, "m");
|
|
35833
36473
|
const match = content.match(regex);
|
|
@@ -35859,9 +36499,9 @@ function extractYamlArray(content, field) {
|
|
|
35859
36499
|
return items;
|
|
35860
36500
|
}
|
|
35861
36501
|
function parseTemplateFile2(filePath) {
|
|
35862
|
-
if (!
|
|
36502
|
+
if (!existsSync59(filePath)) return null;
|
|
35863
36503
|
try {
|
|
35864
|
-
const content =
|
|
36504
|
+
const content = readFileSync43(filePath, "utf-8");
|
|
35865
36505
|
const fileName = basename10(filePath);
|
|
35866
36506
|
const stem = fileName.replace(/\.ya?ml$/, "");
|
|
35867
36507
|
const name = extractYamlField(content, "name");
|
|
@@ -35878,12 +36518,12 @@ function parseTemplateFile2(filePath) {
|
|
|
35878
36518
|
function parseIssueTemplates2(projectDir) {
|
|
35879
36519
|
try {
|
|
35880
36520
|
const packageRoot = getPackageRoot();
|
|
35881
|
-
const packagedTemplateDir =
|
|
35882
|
-
if (
|
|
36521
|
+
const packagedTemplateDir = join58(packageRoot, PACKAGED_TEMPLATE_DIR);
|
|
36522
|
+
if (existsSync59(packagedTemplateDir)) {
|
|
35883
36523
|
const templates3 = [];
|
|
35884
36524
|
for (const file of readdirSync17(packagedTemplateDir)) {
|
|
35885
36525
|
if (!file.endsWith(".yml") && !file.endsWith(".yaml")) continue;
|
|
35886
|
-
const template = parseTemplateFile2(
|
|
36526
|
+
const template = parseTemplateFile2(join58(packagedTemplateDir, file));
|
|
35887
36527
|
if (template) templates3.push(template);
|
|
35888
36528
|
}
|
|
35889
36529
|
if (templates3.length > 0) return templates3;
|
|
@@ -35891,12 +36531,12 @@ function parseIssueTemplates2(projectDir) {
|
|
|
35891
36531
|
} catch {
|
|
35892
36532
|
}
|
|
35893
36533
|
const dir = projectDir ?? getProjectRoot();
|
|
35894
|
-
const templateDir =
|
|
35895
|
-
if (!
|
|
36534
|
+
const templateDir = join58(dir, TEMPLATE_DIR);
|
|
36535
|
+
if (!existsSync59(templateDir)) return [];
|
|
35896
36536
|
const templates2 = [];
|
|
35897
36537
|
for (const file of readdirSync17(templateDir)) {
|
|
35898
36538
|
if (!file.endsWith(".yml") && !file.endsWith(".yaml")) continue;
|
|
35899
|
-
const template = parseTemplateFile2(
|
|
36539
|
+
const template = parseTemplateFile2(join58(templateDir, file));
|
|
35900
36540
|
if (template) templates2.push(template);
|
|
35901
36541
|
}
|
|
35902
36542
|
return templates2;
|
|
@@ -35905,10 +36545,10 @@ function getTemplateConfig(cwd) {
|
|
|
35905
36545
|
const projectDir = cwd ?? getProjectRoot();
|
|
35906
36546
|
const liveTemplates = parseIssueTemplates2(projectDir);
|
|
35907
36547
|
if (liveTemplates.length > 0) return liveTemplates;
|
|
35908
|
-
const cachePath =
|
|
35909
|
-
if (
|
|
36548
|
+
const cachePath = join58(getCleoDir(cwd), CACHE_FILE);
|
|
36549
|
+
if (existsSync59(cachePath)) {
|
|
35910
36550
|
try {
|
|
35911
|
-
const cached = JSON.parse(
|
|
36551
|
+
const cached = JSON.parse(readFileSync43(cachePath, "utf-8"));
|
|
35912
36552
|
if (cached.templates?.length > 0) return cached.templates;
|
|
35913
36553
|
} catch {
|
|
35914
36554
|
}
|
|
@@ -35964,7 +36604,7 @@ var init_template_parser2 = __esm({
|
|
|
35964
36604
|
});
|
|
35965
36605
|
|
|
35966
36606
|
// src/core/issue/create.ts
|
|
35967
|
-
import { execFileSync as
|
|
36607
|
+
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
35968
36608
|
function buildIssueBody(subcommand, rawBody, severity, area) {
|
|
35969
36609
|
const template = getTemplateForSubcommand2(subcommand);
|
|
35970
36610
|
const sectionLabel = template?.name ?? "Description";
|
|
@@ -35983,7 +36623,7 @@ function buildIssueBody(subcommand, rawBody, severity, area) {
|
|
|
35983
36623
|
}
|
|
35984
36624
|
function checkGhCli() {
|
|
35985
36625
|
try {
|
|
35986
|
-
|
|
36626
|
+
execFileSync11("gh", ["--version"], {
|
|
35987
36627
|
encoding: "utf-8",
|
|
35988
36628
|
stdio: ["pipe", "pipe", "pipe"]
|
|
35989
36629
|
});
|
|
@@ -35993,7 +36633,7 @@ function checkGhCli() {
|
|
|
35993
36633
|
});
|
|
35994
36634
|
}
|
|
35995
36635
|
try {
|
|
35996
|
-
|
|
36636
|
+
execFileSync11("gh", ["auth", "status", "--hostname", "github.com"], {
|
|
35997
36637
|
encoding: "utf-8",
|
|
35998
36638
|
stdio: ["pipe", "pipe", "pipe"]
|
|
35999
36639
|
});
|
|
@@ -36005,7 +36645,7 @@ function checkGhCli() {
|
|
|
36005
36645
|
}
|
|
36006
36646
|
function addGhIssue(title, body, labels) {
|
|
36007
36647
|
try {
|
|
36008
|
-
const result =
|
|
36648
|
+
const result = execFileSync11("gh", [
|
|
36009
36649
|
"issue",
|
|
36010
36650
|
"create",
|
|
36011
36651
|
"--repo",
|
|
@@ -36967,8 +37607,8 @@ var init_tools = __esm({
|
|
|
36967
37607
|
});
|
|
36968
37608
|
|
|
36969
37609
|
// src/core/nexus/query.ts
|
|
36970
|
-
import { join as
|
|
36971
|
-
import { existsSync as
|
|
37610
|
+
import { join as join59, basename as basename11 } from "node:path";
|
|
37611
|
+
import { existsSync as existsSync60, readFileSync as readFileSync44 } from "node:fs";
|
|
36972
37612
|
import { z as z3 } from "zod";
|
|
36973
37613
|
function validateSyntax(query) {
|
|
36974
37614
|
if (!query) return false;
|
|
@@ -37004,9 +37644,9 @@ function getCurrentProject() {
|
|
|
37004
37644
|
return process.env["NEXUS_CURRENT_PROJECT"];
|
|
37005
37645
|
}
|
|
37006
37646
|
try {
|
|
37007
|
-
const infoPath =
|
|
37008
|
-
if (
|
|
37009
|
-
const data = JSON.parse(
|
|
37647
|
+
const infoPath = join59(process.cwd(), ".cleo", "project-info.json");
|
|
37648
|
+
if (existsSync60(infoPath)) {
|
|
37649
|
+
const data = JSON.parse(readFileSync44(infoPath, "utf-8"));
|
|
37010
37650
|
if (typeof data.name === "string" && data.name.length > 0) {
|
|
37011
37651
|
return data.name;
|
|
37012
37652
|
}
|
|
@@ -37042,7 +37682,7 @@ async function resolveProjectPath2(projectName) {
|
|
|
37042
37682
|
return project.path;
|
|
37043
37683
|
}
|
|
37044
37684
|
async function readProjectTasks(projectPath) {
|
|
37045
|
-
const tasksDbPath =
|
|
37685
|
+
const tasksDbPath = join59(projectPath, ".cleo", "tasks.db");
|
|
37046
37686
|
try {
|
|
37047
37687
|
const accessor = await getAccessor(projectPath);
|
|
37048
37688
|
const taskFile = await accessor.loadTaskFile();
|
|
@@ -37456,8 +38096,8 @@ var init_deps2 = __esm({
|
|
|
37456
38096
|
|
|
37457
38097
|
// src/core/nexus/sharing/index.ts
|
|
37458
38098
|
import { readFile as readFile15, writeFile as writeFile11 } from "node:fs/promises";
|
|
37459
|
-
import { existsSync as
|
|
37460
|
-
import { join as
|
|
38099
|
+
import { existsSync as existsSync61, readdirSync as readdirSync18, statSync as statSync8 } from "node:fs";
|
|
38100
|
+
import { join as join60, relative as relative4 } from "node:path";
|
|
37461
38101
|
function matchesPattern(filePath, pattern) {
|
|
37462
38102
|
const normalizedPath = filePath.replace(/^\/+|\/+$/g, "");
|
|
37463
38103
|
const normalizedPattern = pattern.replace(/^\/+|\/+$/g, "");
|
|
@@ -37482,7 +38122,7 @@ function collectCleoFiles(cleoDir) {
|
|
|
37482
38122
|
const entries = readdirSync18(dir);
|
|
37483
38123
|
for (const entry of entries) {
|
|
37484
38124
|
if (entry === ".git") continue;
|
|
37485
|
-
const fullPath =
|
|
38125
|
+
const fullPath = join60(dir, entry);
|
|
37486
38126
|
const relPath = relative4(cleoDir, fullPath);
|
|
37487
38127
|
try {
|
|
37488
38128
|
const stat5 = statSync8(fullPath);
|
|
@@ -37542,7 +38182,7 @@ function generateGitignoreEntries(sharing) {
|
|
|
37542
38182
|
async function syncGitignore(cwd) {
|
|
37543
38183
|
const config = await loadConfig(cwd);
|
|
37544
38184
|
const projectRoot = getProjectRoot(cwd);
|
|
37545
|
-
const gitignorePath =
|
|
38185
|
+
const gitignorePath = join60(projectRoot, ".gitignore");
|
|
37546
38186
|
const entries = generateGitignoreEntries(config.sharing);
|
|
37547
38187
|
const managedSection = [
|
|
37548
38188
|
"",
|
|
@@ -37552,7 +38192,7 @@ async function syncGitignore(cwd) {
|
|
|
37552
38192
|
""
|
|
37553
38193
|
].join("\n");
|
|
37554
38194
|
let content = "";
|
|
37555
|
-
if (
|
|
38195
|
+
if (existsSync61(gitignorePath)) {
|
|
37556
38196
|
content = await readFile15(gitignorePath, "utf-8");
|
|
37557
38197
|
}
|
|
37558
38198
|
const startIdx = content.indexOf(GITIGNORE_START);
|
|
@@ -39114,40 +39754,49 @@ var init_session_resolver = __esm({
|
|
|
39114
39754
|
}
|
|
39115
39755
|
});
|
|
39116
39756
|
|
|
39757
|
+
// src/core/tasks/id-generator.ts
|
|
39758
|
+
function normalizeTaskId(input) {
|
|
39759
|
+
if (typeof input !== "string") return null;
|
|
39760
|
+
const trimmed = input.trim();
|
|
39761
|
+
if (trimmed === "") return null;
|
|
39762
|
+
const match = trimmed.match(/^[Tt]?(\d+)(?:_.*)?$/);
|
|
39763
|
+
if (!match) return null;
|
|
39764
|
+
return `T${match[1]}`;
|
|
39765
|
+
}
|
|
39766
|
+
var init_id_generator = __esm({
|
|
39767
|
+
"src/core/tasks/id-generator.ts"() {
|
|
39768
|
+
"use strict";
|
|
39769
|
+
init_data_accessor();
|
|
39770
|
+
}
|
|
39771
|
+
});
|
|
39772
|
+
|
|
39117
39773
|
// src/dispatch/lib/security.ts
|
|
39118
39774
|
import { resolve as resolve9, normalize, relative as relative5, isAbsolute as isAbsolute2 } from "path";
|
|
39119
|
-
function sanitizeTaskId(
|
|
39120
|
-
if (typeof
|
|
39775
|
+
function sanitizeTaskId(value) {
|
|
39776
|
+
if (typeof value !== "string") {
|
|
39121
39777
|
throw new SecurityError(
|
|
39122
39778
|
"Task ID must be a string",
|
|
39123
39779
|
"E_INVALID_TASK_ID",
|
|
39124
39780
|
"taskId"
|
|
39125
39781
|
);
|
|
39126
39782
|
}
|
|
39127
|
-
const
|
|
39128
|
-
if (
|
|
39129
|
-
throw new SecurityError(
|
|
39130
|
-
"Task ID cannot be empty",
|
|
39131
|
-
"E_INVALID_TASK_ID",
|
|
39132
|
-
"taskId"
|
|
39133
|
-
);
|
|
39134
|
-
}
|
|
39135
|
-
if (!TASK_ID_PATTERN2.test(trimmed)) {
|
|
39783
|
+
const normalized = normalizeTaskId(value);
|
|
39784
|
+
if (normalized === null) {
|
|
39136
39785
|
throw new SecurityError(
|
|
39137
|
-
`Invalid task ID format:
|
|
39786
|
+
`Invalid task ID format: ${value}`,
|
|
39138
39787
|
"E_INVALID_TASK_ID",
|
|
39139
39788
|
"taskId"
|
|
39140
39789
|
);
|
|
39141
39790
|
}
|
|
39142
|
-
const numericPart = parseInt(
|
|
39791
|
+
const numericPart = parseInt(normalized.slice(1), 10);
|
|
39143
39792
|
if (numericPart > MAX_TASK_ID_NUMBER) {
|
|
39144
39793
|
throw new SecurityError(
|
|
39145
|
-
`Task ID
|
|
39794
|
+
`Task ID exceeds maximum value: ${value}`,
|
|
39146
39795
|
"E_INVALID_TASK_ID",
|
|
39147
39796
|
"taskId"
|
|
39148
39797
|
);
|
|
39149
39798
|
}
|
|
39150
|
-
return
|
|
39799
|
+
return normalized;
|
|
39151
39800
|
}
|
|
39152
39801
|
function sanitizePath(path, projectRoot) {
|
|
39153
39802
|
if (typeof path !== "string") {
|
|
@@ -39246,14 +39895,14 @@ function sanitizeParams(params, projectRoot, context) {
|
|
|
39246
39895
|
if (value === void 0 || value === null) {
|
|
39247
39896
|
continue;
|
|
39248
39897
|
}
|
|
39249
|
-
if (typeof value === "string" && (key === "taskId" || key === "parent" || key === "epicId")) {
|
|
39898
|
+
if (typeof value === "string" && (key === "taskId" || key === "parent" || key === "epicId" || key === "parentId" || key === "newParentId" || key === "relatedId" || key === "targetId")) {
|
|
39250
39899
|
if (key === "parent" && value === "") {
|
|
39251
39900
|
continue;
|
|
39252
39901
|
}
|
|
39253
39902
|
sanitized[key] = sanitizeTaskId(value);
|
|
39254
39903
|
continue;
|
|
39255
39904
|
}
|
|
39256
|
-
if (key === "depends" && Array.isArray(value)) {
|
|
39905
|
+
if ((key === "depends" || key === "addDepends" || key === "removeDepends") && Array.isArray(value)) {
|
|
39257
39906
|
sanitized[key] = value.map((v) => {
|
|
39258
39907
|
if (typeof v === "string") {
|
|
39259
39908
|
return sanitizeTaskId(v);
|
|
@@ -39303,10 +39952,11 @@ function sanitizeParams(params, projectRoot, context) {
|
|
|
39303
39952
|
}
|
|
39304
39953
|
return sanitized;
|
|
39305
39954
|
}
|
|
39306
|
-
var SecurityError,
|
|
39955
|
+
var SecurityError, MAX_TASK_ID_NUMBER, CONTROL_CHAR_PATTERN, DEFAULT_MAX_CONTENT_LENGTH, ALL_VALID_STATUSES, VALID_PRIORITIES2, ARRAY_PARAMS;
|
|
39307
39956
|
var init_security = __esm({
|
|
39308
39957
|
"src/dispatch/lib/security.ts"() {
|
|
39309
39958
|
"use strict";
|
|
39959
|
+
init_id_generator();
|
|
39310
39960
|
init_schema();
|
|
39311
39961
|
init_status_registry();
|
|
39312
39962
|
SecurityError = class extends Error {
|
|
@@ -39317,7 +39967,6 @@ var init_security = __esm({
|
|
|
39317
39967
|
this.name = "SecurityError";
|
|
39318
39968
|
}
|
|
39319
39969
|
};
|
|
39320
|
-
TASK_ID_PATTERN2 = /^T[0-9]+$/;
|
|
39321
39970
|
MAX_TASK_ID_NUMBER = 999999;
|
|
39322
39971
|
CONTROL_CHAR_PATTERN = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/g;
|
|
39323
39972
|
DEFAULT_MAX_CONTENT_LENGTH = 64 * 1024;
|
|
@@ -39465,13 +40114,13 @@ var init_field_context = __esm({
|
|
|
39465
40114
|
});
|
|
39466
40115
|
|
|
39467
40116
|
// src/core/sessions/context-alert.ts
|
|
39468
|
-
import { existsSync as
|
|
39469
|
-
import { join as
|
|
40117
|
+
import { existsSync as existsSync62, readFileSync as readFileSync45, writeFileSync as writeFileSync9 } from "node:fs";
|
|
40118
|
+
import { join as join61 } from "node:path";
|
|
39470
40119
|
function getCurrentSessionId(cwd) {
|
|
39471
40120
|
if (process.env.CLEO_SESSION) return process.env.CLEO_SESSION;
|
|
39472
|
-
const sessionFile =
|
|
39473
|
-
if (
|
|
39474
|
-
return
|
|
40121
|
+
const sessionFile = join61(getCleoDir(cwd), ".current-session");
|
|
40122
|
+
if (existsSync62(sessionFile)) {
|
|
40123
|
+
return readFileSync45(sessionFile, "utf-8").trim() || null;
|
|
39475
40124
|
}
|
|
39476
40125
|
return null;
|
|
39477
40126
|
}
|
|
@@ -40718,14 +41367,14 @@ __export(logger_exports, {
|
|
|
40718
41367
|
logFileExists: () => logFileExists,
|
|
40719
41368
|
readMigrationLog: () => readMigrationLog
|
|
40720
41369
|
});
|
|
40721
|
-
import { existsSync as
|
|
40722
|
-
import { join as
|
|
41370
|
+
import { existsSync as existsSync65, mkdirSync as mkdirSync16, statSync as statSync9, appendFileSync as appendFileSync4 } from "node:fs";
|
|
41371
|
+
import { join as join68, dirname as dirname16, relative as relative6 } from "node:path";
|
|
40723
41372
|
function createMigrationLogger(cleoDir, config) {
|
|
40724
41373
|
return new MigrationLogger(cleoDir, config);
|
|
40725
41374
|
}
|
|
40726
41375
|
function readMigrationLog(logPath) {
|
|
40727
|
-
const { readFileSync:
|
|
40728
|
-
const content =
|
|
41376
|
+
const { readFileSync: readFileSync53 } = __require("node:fs");
|
|
41377
|
+
const content = readFileSync53(logPath, "utf-8");
|
|
40729
41378
|
return content.split("\n").filter((line) => line.trim()).map((line) => JSON.parse(line));
|
|
40730
41379
|
}
|
|
40731
41380
|
function logFileExists(logPath) {
|
|
@@ -40740,14 +41389,14 @@ function logFileExists(logPath) {
|
|
|
40740
41389
|
function getLatestMigrationLog(cleoDir) {
|
|
40741
41390
|
try {
|
|
40742
41391
|
const { readdirSync: readdirSync21, statSync: statSync10 } = __require("node:fs");
|
|
40743
|
-
const logsDir =
|
|
40744
|
-
if (!
|
|
41392
|
+
const logsDir = join68(cleoDir, "logs");
|
|
41393
|
+
if (!existsSync65(logsDir)) {
|
|
40745
41394
|
return null;
|
|
40746
41395
|
}
|
|
40747
41396
|
const files = readdirSync21(logsDir).filter((f) => f.startsWith("migration-") && f.endsWith(".jsonl")).map((f) => ({
|
|
40748
41397
|
name: f,
|
|
40749
|
-
path:
|
|
40750
|
-
mtime: statSync10(
|
|
41398
|
+
path: join68(logsDir, f),
|
|
41399
|
+
mtime: statSync10(join68(logsDir, f)).mtime.getTime()
|
|
40751
41400
|
})).sort((a, b) => b.mtime - a.mtime);
|
|
40752
41401
|
return files.length > 0 ? files[0].path : null;
|
|
40753
41402
|
} catch {
|
|
@@ -40777,9 +41426,9 @@ var init_logger2 = __esm({
|
|
|
40777
41426
|
consoleOutput: config.consoleOutput ?? false
|
|
40778
41427
|
};
|
|
40779
41428
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
40780
|
-
this.logPath =
|
|
41429
|
+
this.logPath = join68(cleoDir, "logs", `migration-${timestamp}.jsonl`);
|
|
40781
41430
|
const logsDir = dirname16(this.logPath);
|
|
40782
|
-
if (!
|
|
41431
|
+
if (!existsSync65(logsDir)) {
|
|
40783
41432
|
mkdirSync16(logsDir, { recursive: true });
|
|
40784
41433
|
}
|
|
40785
41434
|
this.startTime = Date.now();
|
|
@@ -40868,7 +41517,7 @@ var init_logger2 = __esm({
|
|
|
40868
41517
|
sourcePath: relative6(this.cleoDir, sourcePath),
|
|
40869
41518
|
...additionalData
|
|
40870
41519
|
};
|
|
40871
|
-
if (
|
|
41520
|
+
if (existsSync65(sourcePath)) {
|
|
40872
41521
|
try {
|
|
40873
41522
|
const stats2 = statSync9(sourcePath);
|
|
40874
41523
|
data.sourceSize = stats2.size;
|
|
@@ -40878,7 +41527,7 @@ var init_logger2 = __esm({
|
|
|
40878
41527
|
}
|
|
40879
41528
|
if (targetPath) {
|
|
40880
41529
|
data.targetPath = relative6(this.cleoDir, targetPath);
|
|
40881
|
-
if (
|
|
41530
|
+
if (existsSync65(targetPath)) {
|
|
40882
41531
|
try {
|
|
40883
41532
|
const stats2 = statSync9(targetPath);
|
|
40884
41533
|
data.targetSize = stats2.size;
|
|
@@ -40947,15 +41596,15 @@ var init_logger2 = __esm({
|
|
|
40947
41596
|
*/
|
|
40948
41597
|
cleanupOldLogs() {
|
|
40949
41598
|
try {
|
|
40950
|
-
const logsDir =
|
|
40951
|
-
if (!
|
|
41599
|
+
const logsDir = join68(this.cleoDir, "logs");
|
|
41600
|
+
if (!existsSync65(logsDir)) {
|
|
40952
41601
|
return;
|
|
40953
41602
|
}
|
|
40954
41603
|
const { readdirSync: readdirSync21, unlinkSync: unlinkSync5 } = __require("node:fs");
|
|
40955
41604
|
const files = readdirSync21(logsDir).filter((f) => f.startsWith("migration-") && f.endsWith(".jsonl")).map((f) => ({
|
|
40956
41605
|
name: f,
|
|
40957
|
-
path:
|
|
40958
|
-
mtime: statSync9(
|
|
41606
|
+
path: join68(logsDir, f),
|
|
41607
|
+
mtime: statSync9(join68(logsDir, f)).mtime.getTime()
|
|
40959
41608
|
})).sort((a, b) => b.mtime - a.mtime);
|
|
40960
41609
|
const filesToRemove = files.slice(this.config.maxLogFiles);
|
|
40961
41610
|
for (const file of filesToRemove) {
|
|
@@ -41051,8 +41700,8 @@ __export(state_exports, {
|
|
|
41051
41700
|
verifySourceIntegrity: () => verifySourceIntegrity
|
|
41052
41701
|
});
|
|
41053
41702
|
import { readFile as readFile19, writeFile as writeFile13, unlink as unlink5 } from "node:fs/promises";
|
|
41054
|
-
import { join as
|
|
41055
|
-
import { existsSync as
|
|
41703
|
+
import { join as join69 } from "node:path";
|
|
41704
|
+
import { existsSync as existsSync66 } from "node:fs";
|
|
41056
41705
|
import { createHash as createHash8 } from "node:crypto";
|
|
41057
41706
|
async function computeFileChecksum(filePath) {
|
|
41058
41707
|
try {
|
|
@@ -41074,8 +41723,8 @@ async function countRecords(filePath, key) {
|
|
|
41074
41723
|
async function createMigrationState(cleoDir, sourceFiles) {
|
|
41075
41724
|
const files = sourceFiles ?? {};
|
|
41076
41725
|
if (!files.todoJson) {
|
|
41077
|
-
const todoPath =
|
|
41078
|
-
if (
|
|
41726
|
+
const todoPath = join69(cleoDir, "todo.json");
|
|
41727
|
+
if (existsSync66(todoPath)) {
|
|
41079
41728
|
files.todoJson = {
|
|
41080
41729
|
path: todoPath,
|
|
41081
41730
|
checksum: await computeFileChecksum(todoPath),
|
|
@@ -41084,8 +41733,8 @@ async function createMigrationState(cleoDir, sourceFiles) {
|
|
|
41084
41733
|
}
|
|
41085
41734
|
}
|
|
41086
41735
|
if (!files.sessionsJson) {
|
|
41087
|
-
const sessionsPath =
|
|
41088
|
-
if (
|
|
41736
|
+
const sessionsPath = join69(cleoDir, "sessions.json");
|
|
41737
|
+
if (existsSync66(sessionsPath)) {
|
|
41089
41738
|
files.sessionsJson = {
|
|
41090
41739
|
path: sessionsPath,
|
|
41091
41740
|
checksum: await computeFileChecksum(sessionsPath),
|
|
@@ -41094,8 +41743,8 @@ async function createMigrationState(cleoDir, sourceFiles) {
|
|
|
41094
41743
|
}
|
|
41095
41744
|
}
|
|
41096
41745
|
if (!files.archiveJson) {
|
|
41097
|
-
const archivePath =
|
|
41098
|
-
if (
|
|
41746
|
+
const archivePath = join69(cleoDir, "todo-archive.json");
|
|
41747
|
+
if (existsSync66(archivePath)) {
|
|
41099
41748
|
files.archiveJson = {
|
|
41100
41749
|
path: archivePath,
|
|
41101
41750
|
checksum: await computeFileChecksum(archivePath),
|
|
@@ -41123,7 +41772,7 @@ async function createMigrationState(cleoDir, sourceFiles) {
|
|
|
41123
41772
|
return state;
|
|
41124
41773
|
}
|
|
41125
41774
|
async function writeMigrationState(cleoDir, state) {
|
|
41126
|
-
const statePath =
|
|
41775
|
+
const statePath = join69(cleoDir, STATE_FILENAME);
|
|
41127
41776
|
const tempPath = `${statePath}.tmp`;
|
|
41128
41777
|
await writeFile13(tempPath, JSON.stringify(state, null, 2));
|
|
41129
41778
|
await writeFile13(statePath, await readFile19(tempPath));
|
|
@@ -41198,7 +41847,7 @@ async function addMigrationWarning(cleoDir, warning) {
|
|
|
41198
41847
|
}
|
|
41199
41848
|
async function loadMigrationState(cleoDir) {
|
|
41200
41849
|
try {
|
|
41201
|
-
const statePath =
|
|
41850
|
+
const statePath = join69(cleoDir, STATE_FILENAME);
|
|
41202
41851
|
const content = await readFile19(statePath, "utf-8");
|
|
41203
41852
|
return JSON.parse(content);
|
|
41204
41853
|
} catch {
|
|
@@ -41240,7 +41889,7 @@ async function failMigration(cleoDir, error) {
|
|
|
41240
41889
|
}
|
|
41241
41890
|
async function clearMigrationState(cleoDir) {
|
|
41242
41891
|
try {
|
|
41243
|
-
const statePath =
|
|
41892
|
+
const statePath = join69(cleoDir, STATE_FILENAME);
|
|
41244
41893
|
await unlink5(statePath);
|
|
41245
41894
|
} catch {
|
|
41246
41895
|
}
|
|
@@ -41280,7 +41929,7 @@ async function verifySourceIntegrity(cleoDir) {
|
|
|
41280
41929
|
const missing = [];
|
|
41281
41930
|
for (const [key, fileInfo] of Object.entries(state.sourceFiles)) {
|
|
41282
41931
|
if (!fileInfo) continue;
|
|
41283
|
-
if (!
|
|
41932
|
+
if (!existsSync66(fileInfo.path)) {
|
|
41284
41933
|
missing.push(key);
|
|
41285
41934
|
continue;
|
|
41286
41935
|
}
|
|
@@ -41311,8 +41960,8 @@ __export(migration_sqlite_exports, {
|
|
|
41311
41960
|
migrateJsonToSqlite: () => migrateJsonToSqlite2,
|
|
41312
41961
|
migrateJsonToSqliteAtomic: () => migrateJsonToSqliteAtomic
|
|
41313
41962
|
});
|
|
41314
|
-
import { existsSync as
|
|
41315
|
-
import { join as
|
|
41963
|
+
import { existsSync as existsSync67, readFileSync as readFileSync48 } from "node:fs";
|
|
41964
|
+
import { join as join70, dirname as dirname17 } from "node:path";
|
|
41316
41965
|
import { mkdirSync as mkdirSync17 } from "node:fs";
|
|
41317
41966
|
import { drizzle as drizzle4 } from "drizzle-orm/sqlite-proxy";
|
|
41318
41967
|
import { migrate as migrate4 } from "drizzle-orm/sqlite-proxy/migrator";
|
|
@@ -41350,26 +41999,26 @@ function countJsonRecords(cleoDir) {
|
|
|
41350
41999
|
let tasks2 = 0;
|
|
41351
42000
|
let archived = 0;
|
|
41352
42001
|
let sessions2 = 0;
|
|
41353
|
-
const todoPath =
|
|
41354
|
-
if (
|
|
42002
|
+
const todoPath = join70(cleoDir, "todo.json");
|
|
42003
|
+
if (existsSync67(todoPath)) {
|
|
41355
42004
|
try {
|
|
41356
|
-
const data = JSON.parse(
|
|
42005
|
+
const data = JSON.parse(readFileSync48(todoPath, "utf-8"));
|
|
41357
42006
|
tasks2 = (data.tasks ?? []).length;
|
|
41358
42007
|
} catch {
|
|
41359
42008
|
}
|
|
41360
42009
|
}
|
|
41361
|
-
const archivePath =
|
|
41362
|
-
if (
|
|
42010
|
+
const archivePath = join70(cleoDir, "todo-archive.json");
|
|
42011
|
+
if (existsSync67(archivePath)) {
|
|
41363
42012
|
try {
|
|
41364
|
-
const data = JSON.parse(
|
|
42013
|
+
const data = JSON.parse(readFileSync48(archivePath, "utf-8"));
|
|
41365
42014
|
archived = (data.tasks ?? data.archivedTasks ?? []).length;
|
|
41366
42015
|
} catch {
|
|
41367
42016
|
}
|
|
41368
42017
|
}
|
|
41369
|
-
const sessionsPath =
|
|
41370
|
-
if (
|
|
42018
|
+
const sessionsPath = join70(cleoDir, "sessions.json");
|
|
42019
|
+
if (existsSync67(sessionsPath)) {
|
|
41371
42020
|
try {
|
|
41372
|
-
const data = JSON.parse(
|
|
42021
|
+
const data = JSON.parse(readFileSync48(sessionsPath, "utf-8"));
|
|
41373
42022
|
sessions2 = (data.sessions ?? []).length;
|
|
41374
42023
|
} catch {
|
|
41375
42024
|
}
|
|
@@ -41446,13 +42095,13 @@ async function migrateJsonToSqliteAtomic(cwd, tempDbPath, logger) {
|
|
|
41446
42095
|
}
|
|
41447
42096
|
}
|
|
41448
42097
|
async function runMigrationDataImport(db, cleoDir, result, logger) {
|
|
41449
|
-
const todoPath =
|
|
41450
|
-
if (
|
|
42098
|
+
const todoPath = join70(cleoDir, "todo.json");
|
|
42099
|
+
if (existsSync67(todoPath)) {
|
|
41451
42100
|
try {
|
|
41452
42101
|
logger?.info("import", "read-todo", "Reading todo.json", {
|
|
41453
42102
|
path: todoPath.replace(cleoDir, ".")
|
|
41454
42103
|
});
|
|
41455
|
-
const todoData = JSON.parse(
|
|
42104
|
+
const todoData = JSON.parse(readFileSync48(todoPath, "utf-8"));
|
|
41456
42105
|
const tasks2 = topoSortTasks(todoData.tasks ?? []);
|
|
41457
42106
|
const totalTasks = tasks2.length;
|
|
41458
42107
|
logger?.info("import", "tasks-start", `Starting import of ${totalTasks} tasks`, {
|
|
@@ -41521,13 +42170,13 @@ async function runMigrationDataImport(db, cleoDir, result, logger) {
|
|
|
41521
42170
|
result.warnings.push("todo.json not found, skipping task import");
|
|
41522
42171
|
logger?.warn("import", "todo-missing", "todo.json not found, skipping task import");
|
|
41523
42172
|
}
|
|
41524
|
-
const archivePath =
|
|
41525
|
-
if (
|
|
42173
|
+
const archivePath = join70(cleoDir, "todo-archive.json");
|
|
42174
|
+
if (existsSync67(archivePath)) {
|
|
41526
42175
|
try {
|
|
41527
42176
|
logger?.info("import", "read-archive", "Reading todo-archive.json", {
|
|
41528
42177
|
path: archivePath.replace(cleoDir, ".")
|
|
41529
42178
|
});
|
|
41530
|
-
const archiveData = JSON.parse(
|
|
42179
|
+
const archiveData = JSON.parse(readFileSync48(archivePath, "utf-8"));
|
|
41531
42180
|
const archivedTasks = topoSortTasks(archiveData.tasks ?? archiveData.archivedTasks ?? []);
|
|
41532
42181
|
const totalArchived = archivedTasks.length;
|
|
41533
42182
|
logger?.info("import", "archive-start", `Starting import of ${totalArchived} archived tasks`, {
|
|
@@ -41580,13 +42229,13 @@ async function runMigrationDataImport(db, cleoDir, result, logger) {
|
|
|
41580
42229
|
logger?.error("import", "parse-archive", errorMsg);
|
|
41581
42230
|
}
|
|
41582
42231
|
}
|
|
41583
|
-
const sessionsPath =
|
|
41584
|
-
if (
|
|
42232
|
+
const sessionsPath = join70(cleoDir, "sessions.json");
|
|
42233
|
+
if (existsSync67(sessionsPath)) {
|
|
41585
42234
|
try {
|
|
41586
42235
|
logger?.info("import", "read-sessions", "Reading sessions.json", {
|
|
41587
42236
|
path: sessionsPath.replace(cleoDir, ".")
|
|
41588
42237
|
});
|
|
41589
|
-
const sessionsData = JSON.parse(
|
|
42238
|
+
const sessionsData = JSON.parse(readFileSync48(sessionsPath, "utf-8"));
|
|
41590
42239
|
const sessions2 = sessionsData.sessions ?? [];
|
|
41591
42240
|
const totalSessions = sessions2.length;
|
|
41592
42241
|
logger?.info("import", "sessions-start", `Starting import of ${totalSessions} sessions`, {
|
|
@@ -41718,10 +42367,10 @@ async function migrateJsonToSqlite2(cwd, options) {
|
|
|
41718
42367
|
return result;
|
|
41719
42368
|
}
|
|
41720
42369
|
const db = await getDb(cwd);
|
|
41721
|
-
const todoPath =
|
|
41722
|
-
if (
|
|
42370
|
+
const todoPath = join70(cleoDir, "todo.json");
|
|
42371
|
+
if (existsSync67(todoPath)) {
|
|
41723
42372
|
try {
|
|
41724
|
-
const todoData = JSON.parse(
|
|
42373
|
+
const todoData = JSON.parse(readFileSync48(todoPath, "utf-8"));
|
|
41725
42374
|
const tasks2 = topoSortTasks(todoData.tasks ?? []);
|
|
41726
42375
|
for (const task of tasks2) {
|
|
41727
42376
|
try {
|
|
@@ -41770,10 +42419,10 @@ async function migrateJsonToSqlite2(cwd, options) {
|
|
|
41770
42419
|
} else {
|
|
41771
42420
|
result.warnings.push("todo.json not found, skipping task import");
|
|
41772
42421
|
}
|
|
41773
|
-
const archivePath =
|
|
41774
|
-
if (
|
|
42422
|
+
const archivePath = join70(cleoDir, "todo-archive.json");
|
|
42423
|
+
if (existsSync67(archivePath)) {
|
|
41775
42424
|
try {
|
|
41776
|
-
const archiveData = JSON.parse(
|
|
42425
|
+
const archiveData = JSON.parse(readFileSync48(archivePath, "utf-8"));
|
|
41777
42426
|
const archivedTasks = topoSortTasks(archiveData.tasks ?? archiveData.archivedTasks ?? []);
|
|
41778
42427
|
for (const task of archivedTasks) {
|
|
41779
42428
|
try {
|
|
@@ -41810,11 +42459,11 @@ async function migrateJsonToSqlite2(cwd, options) {
|
|
|
41810
42459
|
result.errors.push(`Failed to parse todo-archive.json: ${String(err)}`);
|
|
41811
42460
|
}
|
|
41812
42461
|
}
|
|
41813
|
-
const sessionsPath =
|
|
41814
|
-
if (
|
|
42462
|
+
const sessionsPath = join70(cleoDir, "sessions.json");
|
|
42463
|
+
if (existsSync67(sessionsPath)) {
|
|
41815
42464
|
try {
|
|
41816
42465
|
const sessionsData = JSON.parse(
|
|
41817
|
-
|
|
42466
|
+
readFileSync48(sessionsPath, "utf-8")
|
|
41818
42467
|
);
|
|
41819
42468
|
const sessions2 = sessionsData.sessions ?? [];
|
|
41820
42469
|
for (const session of sessions2) {
|
|
@@ -41882,8 +42531,8 @@ var init_migration_sqlite = __esm({
|
|
|
41882
42531
|
|
|
41883
42532
|
// src/cli/index.ts
|
|
41884
42533
|
import { Command, Help } from "commander";
|
|
41885
|
-
import { readFileSync as
|
|
41886
|
-
import { join as
|
|
42534
|
+
import { readFileSync as readFileSync52 } from "node:fs";
|
|
42535
|
+
import { join as join76 } from "node:path";
|
|
41887
42536
|
|
|
41888
42537
|
// src/cli/commands/add.ts
|
|
41889
42538
|
init_cli();
|
|
@@ -42733,14 +43382,14 @@ init_errors();
|
|
|
42733
43382
|
init_exit_codes();
|
|
42734
43383
|
init_paths();
|
|
42735
43384
|
init_json();
|
|
42736
|
-
import { join as
|
|
43385
|
+
import { join as join62 } from "node:path";
|
|
42737
43386
|
var VALID_CATEGORIES = ["write", "read", "sync", "maintenance"];
|
|
42738
43387
|
var VALID_RELEVANCE = ["critical", "high", "medium", "low"];
|
|
42739
43388
|
async function locateCommandsIndex() {
|
|
42740
43389
|
const cleoHome = getCleoHome();
|
|
42741
43390
|
const paths = [
|
|
42742
|
-
|
|
42743
|
-
|
|
43391
|
+
join62(cleoHome, "docs", "commands", "COMMANDS-INDEX.json"),
|
|
43392
|
+
join62(process.cwd(), "docs", "commands", "COMMANDS-INDEX.json")
|
|
42744
43393
|
];
|
|
42745
43394
|
for (const p of paths) {
|
|
42746
43395
|
const data = await readJson(p);
|
|
@@ -42821,9 +43470,9 @@ init_errors();
|
|
|
42821
43470
|
init_json();
|
|
42822
43471
|
init_paths();
|
|
42823
43472
|
import { readdir as readdir2, readFile as readFile16 } from "node:fs/promises";
|
|
42824
|
-
import { join as
|
|
43473
|
+
import { join as join63 } from "node:path";
|
|
42825
43474
|
async function getScriptNames(projectRoot) {
|
|
42826
|
-
const scriptsDir =
|
|
43475
|
+
const scriptsDir = join63(projectRoot, "scripts");
|
|
42827
43476
|
try {
|
|
42828
43477
|
const files = await readdir2(scriptsDir);
|
|
42829
43478
|
return files.filter((f) => f.endsWith(".sh")).map((f) => f.replace(".sh", "")).sort();
|
|
@@ -42832,7 +43481,7 @@ async function getScriptNames(projectRoot) {
|
|
|
42832
43481
|
}
|
|
42833
43482
|
}
|
|
42834
43483
|
async function getIndexedCommands(projectRoot) {
|
|
42835
|
-
const indexPath =
|
|
43484
|
+
const indexPath = join63(projectRoot, "docs", "commands", "COMMANDS-INDEX.json");
|
|
42836
43485
|
const index5 = await readJson(indexPath);
|
|
42837
43486
|
if (!index5) return [];
|
|
42838
43487
|
return index5.commands.map((c) => c.name).sort();
|
|
@@ -42865,7 +43514,7 @@ async function runGapCheck(_projectRoot, filterId) {
|
|
|
42865
43514
|
const reviewFiles = files.filter((f) => f.endsWith(".md"));
|
|
42866
43515
|
for (const file of reviewFiles) {
|
|
42867
43516
|
if (filterId && !file.includes(filterId)) continue;
|
|
42868
|
-
const filePath =
|
|
43517
|
+
const filePath = join63(reviewDir, file);
|
|
42869
43518
|
const content = await readFile16(filePath, "utf-8");
|
|
42870
43519
|
const taskMatch = file.match(/^(T\d+)/);
|
|
42871
43520
|
const taskId = taskMatch ? taskMatch[1] : "UNKNOWN";
|
|
@@ -42969,7 +43618,7 @@ init_paths();
|
|
|
42969
43618
|
init_json();
|
|
42970
43619
|
init_data_accessor();
|
|
42971
43620
|
import { readFile as readFile17, rm as rm3, stat as stat3 } from "node:fs/promises";
|
|
42972
|
-
import { join as
|
|
43621
|
+
import { join as join64 } from "node:path";
|
|
42973
43622
|
function parseTaskId(content) {
|
|
42974
43623
|
const match = content.match(/^\[T(\d+)\]/);
|
|
42975
43624
|
return match ? `T${match[1]}` : null;
|
|
@@ -43025,7 +43674,7 @@ function registerExtractCommand(program2) {
|
|
|
43025
43674
|
const accessor = await getAccessor();
|
|
43026
43675
|
const taskData = await accessor.loadTaskFile();
|
|
43027
43676
|
const cleoDir = getCleoDir();
|
|
43028
|
-
const stateFile =
|
|
43677
|
+
const stateFile = join64(cleoDir, "sync", "todowrite-session.json");
|
|
43029
43678
|
let sessionState = null;
|
|
43030
43679
|
try {
|
|
43031
43680
|
sessionState = await readJson(stateFile);
|
|
@@ -43211,17 +43860,17 @@ init_renderers();
|
|
|
43211
43860
|
init_errors();
|
|
43212
43861
|
init_exit_codes();
|
|
43213
43862
|
init_paths();
|
|
43214
|
-
import { spawn as spawn2, execFileSync as
|
|
43863
|
+
import { spawn as spawn2, execFileSync as execFileSync12 } from "node:child_process";
|
|
43215
43864
|
import { readFile as readFile18, writeFile as writeFile12, mkdir as mkdir12, rm as rm4, stat as stat4 } from "node:fs/promises";
|
|
43216
|
-
import { join as
|
|
43865
|
+
import { join as join65 } from "node:path";
|
|
43217
43866
|
var DEFAULT_PORT = 3456;
|
|
43218
43867
|
var DEFAULT_HOST = "127.0.0.1";
|
|
43219
43868
|
function getWebPaths() {
|
|
43220
43869
|
const cleoHome = getCleoHome();
|
|
43221
43870
|
return {
|
|
43222
|
-
pidFile:
|
|
43223
|
-
configFile:
|
|
43224
|
-
logFile:
|
|
43871
|
+
pidFile: join65(cleoHome, "web-server.pid"),
|
|
43872
|
+
configFile: join65(cleoHome, "web-server.json"),
|
|
43873
|
+
logFile: join65(cleoHome, "logs", "web-server.log")
|
|
43225
43874
|
};
|
|
43226
43875
|
}
|
|
43227
43876
|
function isProcessRunning(pid) {
|
|
@@ -43265,19 +43914,19 @@ function registerWebCommand(program2) {
|
|
|
43265
43914
|
throw new CleoError(1 /* GENERAL_ERROR */, `Server already running (PID: ${status.pid})`);
|
|
43266
43915
|
}
|
|
43267
43916
|
const projectRoot = process.env["CLEO_ROOT"] ?? process.cwd();
|
|
43268
|
-
const distMcpDir =
|
|
43269
|
-
await mkdir12(
|
|
43917
|
+
const distMcpDir = join65(projectRoot, "dist", "mcp");
|
|
43918
|
+
await mkdir12(join65(getCleoHome(), "logs"), { recursive: true });
|
|
43270
43919
|
await writeFile12(configFile, JSON.stringify({
|
|
43271
43920
|
port,
|
|
43272
43921
|
host,
|
|
43273
43922
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
43274
43923
|
}));
|
|
43275
|
-
const webIndexPath =
|
|
43924
|
+
const webIndexPath = join65(distMcpDir, "index.js");
|
|
43276
43925
|
try {
|
|
43277
43926
|
await stat4(webIndexPath);
|
|
43278
43927
|
} catch {
|
|
43279
43928
|
try {
|
|
43280
|
-
|
|
43929
|
+
execFileSync12("npm", ["run", "build"], { cwd: projectRoot, stdio: "ignore" });
|
|
43281
43930
|
} catch {
|
|
43282
43931
|
throw new CleoError(1 /* GENERAL_ERROR */, `Build failed. Check logs: ${logFile}`);
|
|
43283
43932
|
}
|
|
@@ -43496,13 +44145,13 @@ init_renderers();
|
|
|
43496
44145
|
init_errors();
|
|
43497
44146
|
init_exit_codes();
|
|
43498
44147
|
init_paths();
|
|
43499
|
-
import { existsSync as
|
|
43500
|
-
import { execFileSync as
|
|
43501
|
-
import { join as
|
|
44148
|
+
import { existsSync as existsSync63, readFileSync as readFileSync46, writeFileSync as writeFileSync10, mkdirSync as mkdirSync15 } from "node:fs";
|
|
44149
|
+
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
44150
|
+
import { join as join66, dirname as dirname15 } from "node:path";
|
|
43502
44151
|
function getChangelogSource(cwd) {
|
|
43503
44152
|
const configPath = getConfigPath(cwd);
|
|
43504
44153
|
try {
|
|
43505
|
-
const config = JSON.parse(
|
|
44154
|
+
const config = JSON.parse(readFileSync46(configPath, "utf-8"));
|
|
43506
44155
|
return config?.release?.changelog?.source ?? "CHANGELOG.md";
|
|
43507
44156
|
} catch {
|
|
43508
44157
|
return "CHANGELOG.md";
|
|
@@ -43511,7 +44160,7 @@ function getChangelogSource(cwd) {
|
|
|
43511
44160
|
function getEnabledPlatforms(cwd) {
|
|
43512
44161
|
const configPath = getConfigPath(cwd);
|
|
43513
44162
|
try {
|
|
43514
|
-
const config = JSON.parse(
|
|
44163
|
+
const config = JSON.parse(readFileSync46(configPath, "utf-8"));
|
|
43515
44164
|
const outputs = config?.release?.changelog?.outputs ?? [];
|
|
43516
44165
|
return outputs.filter((o) => o.enabled);
|
|
43517
44166
|
} catch {
|
|
@@ -43533,7 +44182,7 @@ function getDefaultOutputPath(platform2) {
|
|
|
43533
44182
|
function getGitHubRepoSlug(cwd) {
|
|
43534
44183
|
const projectRoot = getProjectRoot(cwd);
|
|
43535
44184
|
try {
|
|
43536
|
-
const remoteUrl =
|
|
44185
|
+
const remoteUrl = execFileSync13("git", ["remote", "get-url", "origin"], {
|
|
43537
44186
|
cwd: projectRoot,
|
|
43538
44187
|
encoding: "utf-8",
|
|
43539
44188
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -43638,11 +44287,11 @@ function registerGenerateChangelogCommand(program2) {
|
|
|
43638
44287
|
const targetPlatform = opts["platform"];
|
|
43639
44288
|
const dryRun = !!opts["dryRun"];
|
|
43640
44289
|
const sourceFile = getChangelogSource();
|
|
43641
|
-
const sourcePath =
|
|
43642
|
-
if (!
|
|
44290
|
+
const sourcePath = join66(getProjectRoot(), sourceFile);
|
|
44291
|
+
if (!existsSync63(sourcePath)) {
|
|
43643
44292
|
throw new CleoError(4 /* NOT_FOUND */, `Changelog source not found: ${sourcePath}`);
|
|
43644
44293
|
}
|
|
43645
|
-
const sourceContent =
|
|
44294
|
+
const sourceContent = readFileSync46(sourcePath, "utf-8");
|
|
43646
44295
|
const repoSlug = getGitHubRepoSlug();
|
|
43647
44296
|
const results = [];
|
|
43648
44297
|
if (targetPlatform) {
|
|
@@ -43651,7 +44300,7 @@ function registerGenerateChangelogCommand(program2) {
|
|
|
43651
44300
|
const outputPath = platformConfig?.path ?? getDefaultOutputPath(targetPlatform);
|
|
43652
44301
|
const content = generateForPlatform(targetPlatform, sourceContent, repoSlug, limit);
|
|
43653
44302
|
if (!dryRun) {
|
|
43654
|
-
const fullPath =
|
|
44303
|
+
const fullPath = join66(getProjectRoot(), outputPath);
|
|
43655
44304
|
mkdirSync15(dirname15(fullPath), { recursive: true });
|
|
43656
44305
|
writeFileSync10(fullPath, content, "utf-8");
|
|
43657
44306
|
}
|
|
@@ -43672,7 +44321,7 @@ function registerGenerateChangelogCommand(program2) {
|
|
|
43672
44321
|
limit
|
|
43673
44322
|
);
|
|
43674
44323
|
if (!dryRun) {
|
|
43675
|
-
const fullPath =
|
|
44324
|
+
const fullPath = join66(getProjectRoot(), platformConfig.path);
|
|
43676
44325
|
mkdirSync15(dirname15(fullPath), { recursive: true });
|
|
43677
44326
|
writeFileSync10(fullPath, content, "utf-8");
|
|
43678
44327
|
}
|
|
@@ -43703,7 +44352,7 @@ function registerGenerateChangelogCommand(program2) {
|
|
|
43703
44352
|
init_cli();
|
|
43704
44353
|
init_renderers();
|
|
43705
44354
|
init_build_config();
|
|
43706
|
-
import { execFileSync as
|
|
44355
|
+
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
43707
44356
|
var CLEO_REPO2 = BUILD_CONFIG.repository.fullName;
|
|
43708
44357
|
function registerIssueCommand(program2) {
|
|
43709
44358
|
const issueCmd = program2.command("issue").description("File bug reports, feature requests, or questions to CLEO GitHub repo");
|
|
@@ -43742,7 +44391,7 @@ async function handleIssueType(issueType, opts) {
|
|
|
43742
44391
|
if (opts["open"] && typeof result["url"] === "string" && result["url"].startsWith("https://")) {
|
|
43743
44392
|
const issueNumber = result["url"].match(/(\d+)$/)?.[1] ?? "unknown";
|
|
43744
44393
|
try {
|
|
43745
|
-
|
|
44394
|
+
execFileSync14("gh", ["issue", "view", issueNumber, "--repo", CLEO_REPO2, "--web"], {
|
|
43746
44395
|
stdio: ["pipe", "pipe", "pipe"]
|
|
43747
44396
|
});
|
|
43748
44397
|
} catch {
|
|
@@ -44427,22 +45076,22 @@ function registerPlanCommand(program2) {
|
|
|
44427
45076
|
}
|
|
44428
45077
|
|
|
44429
45078
|
// src/core/otel/index.ts
|
|
44430
|
-
import { readFileSync as
|
|
44431
|
-
import { join as
|
|
45079
|
+
import { readFileSync as readFileSync47, existsSync as existsSync64, writeFileSync as writeFileSync11, copyFileSync as copyFileSync4 } from "node:fs";
|
|
45080
|
+
import { join as join67 } from "node:path";
|
|
44432
45081
|
function getProjectRoot2() {
|
|
44433
45082
|
let dir = process.cwd();
|
|
44434
45083
|
while (dir !== "/") {
|
|
44435
|
-
if (
|
|
44436
|
-
dir =
|
|
45084
|
+
if (existsSync64(join67(dir, ".cleo", "config.json"))) return dir;
|
|
45085
|
+
dir = join67(dir, "..");
|
|
44437
45086
|
}
|
|
44438
45087
|
return process.cwd();
|
|
44439
45088
|
}
|
|
44440
45089
|
function getTokenFilePath() {
|
|
44441
|
-
return
|
|
45090
|
+
return join67(getProjectRoot2(), ".cleo", "metrics", "TOKEN_USAGE.jsonl");
|
|
44442
45091
|
}
|
|
44443
45092
|
function readJsonlFile(filePath) {
|
|
44444
|
-
if (!
|
|
44445
|
-
const content =
|
|
45093
|
+
if (!existsSync64(filePath)) return [];
|
|
45094
|
+
const content = readFileSync47(filePath, "utf-8").trim();
|
|
44446
45095
|
if (!content) return [];
|
|
44447
45096
|
return content.split("\n").map((line) => JSON.parse(line));
|
|
44448
45097
|
}
|
|
@@ -44524,7 +45173,7 @@ async function getRealTokenUsage(_opts) {
|
|
|
44524
45173
|
}
|
|
44525
45174
|
async function clearOtelData() {
|
|
44526
45175
|
const tokenFile = getTokenFilePath();
|
|
44527
|
-
if (
|
|
45176
|
+
if (existsSync64(tokenFile)) {
|
|
44528
45177
|
const backup = `${tokenFile}.backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
44529
45178
|
copyFileSync4(tokenFile, backup);
|
|
44530
45179
|
writeFileSync11(tokenFile, "");
|
|
@@ -44889,8 +45538,8 @@ init_storage_preflight();
|
|
|
44889
45538
|
init_paths();
|
|
44890
45539
|
init_storage_preflight();
|
|
44891
45540
|
init_agent_outputs();
|
|
44892
|
-
import { existsSync as
|
|
44893
|
-
import { join as
|
|
45541
|
+
import { existsSync as existsSync68, readFileSync as readFileSync49, writeFileSync as writeFileSync12, mkdirSync as mkdirSync18, readdirSync as readdirSync19, copyFileSync as copyFileSync5 } from "node:fs";
|
|
45542
|
+
import { join as join71 } from "node:path";
|
|
44894
45543
|
|
|
44895
45544
|
// src/store/index.ts
|
|
44896
45545
|
init_atomic();
|
|
@@ -44926,8 +45575,8 @@ async function runUpgrade(options = {}) {
|
|
|
44926
45575
|
return { success: false, upToDate: false, dryRun: isDryRun, actions, applied: 0, errors: [String(err)] };
|
|
44927
45576
|
}
|
|
44928
45577
|
const cleoDir = getCleoDirAbsolute(options.cwd);
|
|
44929
|
-
const dbPath =
|
|
44930
|
-
const dbExists2 =
|
|
45578
|
+
const dbPath = join71(cleoDir, "tasks.db");
|
|
45579
|
+
const dbExists2 = existsSync68(dbPath);
|
|
44931
45580
|
const legacyRecordCount = preflight.details.todoJsonTaskCount + preflight.details.archiveJsonTaskCount + preflight.details.sessionsJsonCount;
|
|
44932
45581
|
const needsMigration = !dbExists2 && legacyRecordCount > 0;
|
|
44933
45582
|
const needsCleanup = dbExists2 && preflight.migrationNeeded;
|
|
@@ -44943,7 +45592,7 @@ async function runUpgrade(options = {}) {
|
|
|
44943
45592
|
let migrationLock = null;
|
|
44944
45593
|
try {
|
|
44945
45594
|
const cleoDir2 = getCleoDirAbsolute(options.cwd);
|
|
44946
|
-
const dbPath2 =
|
|
45595
|
+
const dbPath2 = join71(cleoDir2, "tasks.db");
|
|
44947
45596
|
try {
|
|
44948
45597
|
migrationLock = await acquireLock(dbPath2, { stale: 3e4, retries: 0 });
|
|
44949
45598
|
} catch {
|
|
@@ -44968,23 +45617,23 @@ async function runUpgrade(options = {}) {
|
|
|
44968
45617
|
} = await Promise.resolve().then(() => (init_state(), state_exports));
|
|
44969
45618
|
const logger = new MigrationLogger2(cleoDir2);
|
|
44970
45619
|
await createMigrationState2(cleoDir2, {
|
|
44971
|
-
todoJson: { path:
|
|
44972
|
-
sessionsJson: { path:
|
|
44973
|
-
archiveJson: { path:
|
|
45620
|
+
todoJson: { path: join71(cleoDir2, "todo.json"), checksum: "" },
|
|
45621
|
+
sessionsJson: { path: join71(cleoDir2, "sessions.json"), checksum: "" },
|
|
45622
|
+
archiveJson: { path: join71(cleoDir2, "todo-archive.json"), checksum: "" }
|
|
44974
45623
|
});
|
|
44975
45624
|
await updateMigrationPhase2(cleoDir2, "backup");
|
|
44976
45625
|
logger.info("init", "start", "Migration state initialized");
|
|
44977
|
-
const dbBackupPath =
|
|
44978
|
-
const dbTempPath =
|
|
44979
|
-
if (
|
|
44980
|
-
const backupDir =
|
|
44981
|
-
if (!
|
|
45626
|
+
const dbBackupPath = join71(cleoDir2, "backups", "safety", `tasks.db.pre-migration.${Date.now()}`);
|
|
45627
|
+
const dbTempPath = join71(cleoDir2, "tasks.db.migrating");
|
|
45628
|
+
if (existsSync68(dbPath2)) {
|
|
45629
|
+
const backupDir = join71(cleoDir2, "backups", "safety");
|
|
45630
|
+
if (!existsSync68(backupDir)) {
|
|
44982
45631
|
mkdirSync18(backupDir, { recursive: true });
|
|
44983
45632
|
}
|
|
44984
45633
|
copyFileSync5(dbPath2, dbBackupPath);
|
|
44985
45634
|
const { createHash: createHash9 } = await import("node:crypto");
|
|
44986
|
-
const origChecksum = createHash9("sha256").update(
|
|
44987
|
-
const backupChecksum = createHash9("sha256").update(
|
|
45635
|
+
const origChecksum = createHash9("sha256").update(readFileSync49(dbPath2)).digest("hex");
|
|
45636
|
+
const backupChecksum = createHash9("sha256").update(readFileSync49(dbBackupPath)).digest("hex");
|
|
44988
45637
|
if (origChecksum !== backupChecksum) {
|
|
44989
45638
|
throw new Error(
|
|
44990
45639
|
`Backup verification failed: checksum mismatch. Aborting migration to prevent data loss.`
|
|
@@ -44999,14 +45648,14 @@ async function runUpgrade(options = {}) {
|
|
|
44999
45648
|
}
|
|
45000
45649
|
logger.info("backup", "verified", "Backup integrity verified", { checksum: origChecksum });
|
|
45001
45650
|
}
|
|
45002
|
-
if (
|
|
45651
|
+
if (existsSync68(dbTempPath)) {
|
|
45003
45652
|
const { unlinkSync: unlinkSync5 } = await import("node:fs");
|
|
45004
45653
|
unlinkSync5(dbTempPath);
|
|
45005
45654
|
}
|
|
45006
|
-
const configPath =
|
|
45655
|
+
const configPath = join71(cleoDir2, "config.json");
|
|
45007
45656
|
let configBackup = null;
|
|
45008
|
-
if (
|
|
45009
|
-
configBackup =
|
|
45657
|
+
if (existsSync68(configPath)) {
|
|
45658
|
+
configBackup = readFileSync49(configPath, "utf-8");
|
|
45010
45659
|
}
|
|
45011
45660
|
const { resetDbState: resetDbState2 } = await Promise.resolve().then(() => (init_sqlite(), sqlite_exports));
|
|
45012
45661
|
resetDbState2();
|
|
@@ -45032,7 +45681,7 @@ async function runUpgrade(options = {}) {
|
|
|
45032
45681
|
resetDbState2();
|
|
45033
45682
|
if (result.success) {
|
|
45034
45683
|
const totalImported = result.tasksImported + result.archivedImported;
|
|
45035
|
-
if (totalImported === 0 &&
|
|
45684
|
+
if (totalImported === 0 && existsSync68(dbBackupPath)) {
|
|
45036
45685
|
copyFileSync5(dbBackupPath, dbPath2);
|
|
45037
45686
|
if (configBackup) {
|
|
45038
45687
|
writeFileSync12(configPath, configBackup);
|
|
@@ -45046,9 +45695,9 @@ async function runUpgrade(options = {}) {
|
|
|
45046
45695
|
errors.push("Migration imported 0 tasks \u2014 restored from backup to prevent data loss");
|
|
45047
45696
|
} else {
|
|
45048
45697
|
let config = {};
|
|
45049
|
-
if (
|
|
45698
|
+
if (existsSync68(configPath)) {
|
|
45050
45699
|
try {
|
|
45051
|
-
config = JSON.parse(
|
|
45700
|
+
config = JSON.parse(readFileSync49(configPath, "utf-8"));
|
|
45052
45701
|
} catch {
|
|
45053
45702
|
}
|
|
45054
45703
|
}
|
|
@@ -45074,7 +45723,7 @@ async function runUpgrade(options = {}) {
|
|
|
45074
45723
|
logger.info("complete", "finish", "Migration completed successfully");
|
|
45075
45724
|
}
|
|
45076
45725
|
} else {
|
|
45077
|
-
if (
|
|
45726
|
+
if (existsSync68(dbBackupPath)) {
|
|
45078
45727
|
copyFileSync5(dbBackupPath, dbPath2);
|
|
45079
45728
|
}
|
|
45080
45729
|
if (configBackup) {
|
|
@@ -45097,12 +45746,12 @@ async function runUpgrade(options = {}) {
|
|
|
45097
45746
|
} catch (err) {
|
|
45098
45747
|
try {
|
|
45099
45748
|
const cleoDir2 = getCleoDirAbsolute(options.cwd);
|
|
45100
|
-
const dbPath2 =
|
|
45101
|
-
const safetyDir =
|
|
45102
|
-
if (
|
|
45749
|
+
const dbPath2 = join71(cleoDir2, "tasks.db");
|
|
45750
|
+
const safetyDir = join71(cleoDir2, "backups", "safety");
|
|
45751
|
+
if (existsSync68(safetyDir)) {
|
|
45103
45752
|
const backups = readdirSync19(safetyDir).filter((f) => f.startsWith("tasks.db.pre-migration.")).sort().reverse();
|
|
45104
|
-
if (backups.length > 0 && !
|
|
45105
|
-
copyFileSync5(
|
|
45753
|
+
if (backups.length > 0 && !existsSync68(dbPath2)) {
|
|
45754
|
+
copyFileSync5(join71(safetyDir, backups[0]), dbPath2);
|
|
45106
45755
|
}
|
|
45107
45756
|
}
|
|
45108
45757
|
} catch {
|
|
@@ -45137,7 +45786,7 @@ async function runUpgrade(options = {}) {
|
|
|
45137
45786
|
details: preflight.summary
|
|
45138
45787
|
});
|
|
45139
45788
|
}
|
|
45140
|
-
if (
|
|
45789
|
+
if (existsSync68(dbPath)) {
|
|
45141
45790
|
try {
|
|
45142
45791
|
const { runAllRepairs: runAllRepairs2 } = await Promise.resolve().then(() => (init_repair(), repair_exports));
|
|
45143
45792
|
const repairActions = await runAllRepairs2(options.cwd, isDryRun);
|
|
@@ -45147,8 +45796,8 @@ async function runUpgrade(options = {}) {
|
|
|
45147
45796
|
} catch {
|
|
45148
45797
|
}
|
|
45149
45798
|
}
|
|
45150
|
-
if (
|
|
45151
|
-
const legacySequenceFiles = [".sequence", ".sequence.json"].filter((f) =>
|
|
45799
|
+
if (existsSync68(dbPath)) {
|
|
45800
|
+
const legacySequenceFiles = [".sequence", ".sequence.json"].filter((f) => existsSync68(join71(cleoDir, f)));
|
|
45152
45801
|
if (legacySequenceFiles.length > 0) {
|
|
45153
45802
|
if (isDryRun) {
|
|
45154
45803
|
actions.push({
|
|
@@ -45177,7 +45826,7 @@ async function runUpgrade(options = {}) {
|
|
|
45177
45826
|
}
|
|
45178
45827
|
if (needsCleanup) {
|
|
45179
45828
|
const staleJsonFiles = ["todo.json", "sessions.json", "todo-archive.json", ".sequence", ".sequence.json"];
|
|
45180
|
-
const foundStale = staleJsonFiles.filter((f) =>
|
|
45829
|
+
const foundStale = staleJsonFiles.filter((f) => existsSync68(join71(cleoDir, f)));
|
|
45181
45830
|
if (foundStale.length > 0) {
|
|
45182
45831
|
if (isDryRun) {
|
|
45183
45832
|
actions.push({
|
|
@@ -45187,15 +45836,15 @@ async function runUpgrade(options = {}) {
|
|
|
45187
45836
|
});
|
|
45188
45837
|
} else {
|
|
45189
45838
|
try {
|
|
45190
|
-
const backupDir =
|
|
45839
|
+
const backupDir = join71(cleoDir, ".backups", `legacy-json-${Date.now()}`);
|
|
45191
45840
|
mkdirSync18(backupDir, { recursive: true });
|
|
45192
45841
|
for (const f of foundStale) {
|
|
45193
|
-
const src =
|
|
45194
|
-
copyFileSync5(src,
|
|
45842
|
+
const src = join71(cleoDir, f);
|
|
45843
|
+
copyFileSync5(src, join71(backupDir, f));
|
|
45195
45844
|
}
|
|
45196
45845
|
const { unlinkSync: unlinkSync5 } = await import("node:fs");
|
|
45197
45846
|
for (const f of foundStale) {
|
|
45198
|
-
unlinkSync5(
|
|
45847
|
+
unlinkSync5(join71(cleoDir, f));
|
|
45199
45848
|
}
|
|
45200
45849
|
actions.push({
|
|
45201
45850
|
action: "stale_json_cleanup",
|
|
@@ -45215,7 +45864,7 @@ async function runUpgrade(options = {}) {
|
|
|
45215
45864
|
if (options.includeGlobal) {
|
|
45216
45865
|
try {
|
|
45217
45866
|
const globalDir = getCleoHome();
|
|
45218
|
-
const globalPreflight = checkStorageMigration(
|
|
45867
|
+
const globalPreflight = checkStorageMigration(join71(globalDir, ".."));
|
|
45219
45868
|
if (globalPreflight.migrationNeeded) {
|
|
45220
45869
|
actions.push({
|
|
45221
45870
|
action: "global_storage_check",
|
|
@@ -45236,8 +45885,8 @@ async function runUpgrade(options = {}) {
|
|
|
45236
45885
|
try {
|
|
45237
45886
|
const projectRoot = getProjectRoot(options.cwd);
|
|
45238
45887
|
if (isDryRun) {
|
|
45239
|
-
const gitignorePath =
|
|
45240
|
-
if (!
|
|
45888
|
+
const gitignorePath = join71(cleoDir, ".gitignore");
|
|
45889
|
+
if (!existsSync68(gitignorePath)) {
|
|
45241
45890
|
actions.push({ action: "gitignore_integrity", status: "preview", details: "Would create .cleo/.gitignore from template" });
|
|
45242
45891
|
} else {
|
|
45243
45892
|
actions.push({ action: "gitignore_integrity", status: "preview", details: "Would verify .cleo/.gitignore matches template" });
|
|
@@ -45280,12 +45929,12 @@ async function runUpgrade(options = {}) {
|
|
|
45280
45929
|
try {
|
|
45281
45930
|
const projectRootForContext = getProjectRoot(options.cwd);
|
|
45282
45931
|
if (isDryRun) {
|
|
45283
|
-
const contextPath =
|
|
45284
|
-
if (!
|
|
45932
|
+
const contextPath = join71(cleoDir, "project-context.json");
|
|
45933
|
+
if (!existsSync68(contextPath)) {
|
|
45285
45934
|
actions.push({ action: "project_context_detection", status: "preview", details: "Would detect and create project-context.json" });
|
|
45286
45935
|
} else {
|
|
45287
45936
|
try {
|
|
45288
|
-
const context = JSON.parse(
|
|
45937
|
+
const context = JSON.parse(readFileSync49(contextPath, "utf-8"));
|
|
45289
45938
|
if (context.detectedAt) {
|
|
45290
45939
|
const daysSince = (Date.now() - new Date(context.detectedAt).getTime()) / (1e3 * 60 * 60 * 24);
|
|
45291
45940
|
if (daysSince > 30) {
|
|
@@ -45446,7 +46095,7 @@ async function runUpgrade(options = {}) {
|
|
|
45446
46095
|
init_runtime();
|
|
45447
46096
|
import { readFile as readFile20 } from "node:fs/promises";
|
|
45448
46097
|
import * as readline from "node:readline";
|
|
45449
|
-
import { join as
|
|
46098
|
+
import { join as join72 } from "node:path";
|
|
45450
46099
|
import { execFile as execFile7 } from "node:child_process";
|
|
45451
46100
|
import { promisify as promisify9 } from "node:util";
|
|
45452
46101
|
init_build_config();
|
|
@@ -45455,7 +46104,7 @@ var GITHUB_REPO = BUILD_CONFIG.repository.fullName;
|
|
|
45455
46104
|
async function getCurrentVersion() {
|
|
45456
46105
|
const cleoHome = getCleoHome();
|
|
45457
46106
|
try {
|
|
45458
|
-
const content = await readFile20(
|
|
46107
|
+
const content = await readFile20(join72(cleoHome, "VERSION"), "utf-8");
|
|
45459
46108
|
return (content.split("\n")[0] ?? "unknown").trim();
|
|
45460
46109
|
} catch {
|
|
45461
46110
|
return "unknown";
|
|
@@ -45502,7 +46151,7 @@ async function writeRuntimeVersionMetadata(mode, source, version) {
|
|
|
45502
46151
|
`installed=${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
45503
46152
|
];
|
|
45504
46153
|
await import("node:fs/promises").then(
|
|
45505
|
-
({ writeFile: writeFile15, mkdir: mkdir14 }) => mkdir14(cleoHome, { recursive: true }).then(() => writeFile15(
|
|
46154
|
+
({ writeFile: writeFile15, mkdir: mkdir14 }) => mkdir14(cleoHome, { recursive: true }).then(() => writeFile15(join72(cleoHome, "VERSION"), `${lines.join("\n")}
|
|
45506
46155
|
`, "utf-8"))
|
|
45507
46156
|
);
|
|
45508
46157
|
}
|
|
@@ -45861,14 +46510,14 @@ function registerVerifyCommand(program2) {
|
|
|
45861
46510
|
|
|
45862
46511
|
// src/cli/commands/detect-drift.ts
|
|
45863
46512
|
init_renderers();
|
|
45864
|
-
import { readFileSync as
|
|
45865
|
-
import { join as
|
|
46513
|
+
import { readFileSync as readFileSync50, existsSync as existsSync69, readdirSync as readdirSync20 } from "node:fs";
|
|
46514
|
+
import { join as join73, dirname as dirname18 } from "node:path";
|
|
45866
46515
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
45867
46516
|
function findProjectRoot() {
|
|
45868
46517
|
const currentFile = fileURLToPath5(import.meta.url);
|
|
45869
46518
|
let currentDir = dirname18(currentFile);
|
|
45870
46519
|
while (currentDir !== "/") {
|
|
45871
|
-
if (
|
|
46520
|
+
if (existsSync69(join73(currentDir, "package.json"))) {
|
|
45872
46521
|
return currentDir;
|
|
45873
46522
|
}
|
|
45874
46523
|
const parent = dirname18(currentDir);
|
|
@@ -45906,16 +46555,16 @@ function registerDetectDriftCommand(program2) {
|
|
|
45906
46555
|
};
|
|
45907
46556
|
const safeRead = (path) => {
|
|
45908
46557
|
try {
|
|
45909
|
-
return
|
|
46558
|
+
return readFileSync50(path, "utf-8");
|
|
45910
46559
|
} catch {
|
|
45911
46560
|
return "";
|
|
45912
46561
|
}
|
|
45913
46562
|
};
|
|
45914
46563
|
try {
|
|
45915
|
-
const specPath =
|
|
45916
|
-
const queryPath =
|
|
45917
|
-
const mutatePath =
|
|
45918
|
-
if (!
|
|
46564
|
+
const specPath = join73(projectRoot, "docs", "specs", "CLEO-OPERATIONS-REFERENCE.md");
|
|
46565
|
+
const queryPath = join73(projectRoot, "src", "mcp", "gateways", "query.ts");
|
|
46566
|
+
const mutatePath = join73(projectRoot, "src", "mcp", "gateways", "mutate.ts");
|
|
46567
|
+
if (!existsSync69(specPath)) {
|
|
45919
46568
|
addCheck("Gateway-to-spec sync", "fail", "CLEO-OPERATIONS-REFERENCE.md missing", [{
|
|
45920
46569
|
severity: "error",
|
|
45921
46570
|
category: "spec",
|
|
@@ -45923,7 +46572,7 @@ function registerDetectDriftCommand(program2) {
|
|
|
45923
46572
|
file: specPath,
|
|
45924
46573
|
recommendation: "Create docs/specs/CLEO-OPERATIONS-REFERENCE.md with canonical operation definitions"
|
|
45925
46574
|
}]);
|
|
45926
|
-
} else if (!
|
|
46575
|
+
} else if (!existsSync69(queryPath) || !existsSync69(mutatePath)) {
|
|
45927
46576
|
addCheck("Gateway-to-spec sync", "fail", "MCP gateway files missing", [{
|
|
45928
46577
|
severity: "error",
|
|
45929
46578
|
category: "implementation",
|
|
@@ -45977,16 +46626,16 @@ function registerDetectDriftCommand(program2) {
|
|
|
45977
46626
|
}]);
|
|
45978
46627
|
}
|
|
45979
46628
|
try {
|
|
45980
|
-
const cliDir =
|
|
45981
|
-
const coreDir =
|
|
45982
|
-
if (!
|
|
46629
|
+
const cliDir = join73(projectRoot, "src", "cli", "commands");
|
|
46630
|
+
const coreDir = join73(projectRoot, "src", "core");
|
|
46631
|
+
if (!existsSync69(cliDir)) {
|
|
45983
46632
|
addCheck("CLI-to-core sync", "fail", "CLI commands directory missing", [{
|
|
45984
46633
|
severity: "error",
|
|
45985
46634
|
category: "structure",
|
|
45986
46635
|
message: "src/cli/commands/ directory not found",
|
|
45987
46636
|
recommendation: "Verify TypeScript source structure is intact"
|
|
45988
46637
|
}]);
|
|
45989
|
-
} else if (!
|
|
46638
|
+
} else if (!existsSync69(coreDir)) {
|
|
45990
46639
|
addCheck("CLI-to-core sync", "fail", "Core directory missing", [{
|
|
45991
46640
|
severity: "error",
|
|
45992
46641
|
category: "structure",
|
|
@@ -46001,8 +46650,8 @@ function registerDetectDriftCommand(program2) {
|
|
|
46001
46650
|
addCheck("CLI-to-core sync", "fail", `Error: ${e.message}`);
|
|
46002
46651
|
}
|
|
46003
46652
|
try {
|
|
46004
|
-
const domainsDir =
|
|
46005
|
-
if (!
|
|
46653
|
+
const domainsDir = join73(projectRoot, "src", "mcp", "domains");
|
|
46654
|
+
if (!existsSync69(domainsDir)) {
|
|
46006
46655
|
addCheck("Domain handler coverage", "fail", "MCP domains directory missing", [{
|
|
46007
46656
|
severity: "error",
|
|
46008
46657
|
category: "structure",
|
|
@@ -46017,8 +46666,8 @@ function registerDetectDriftCommand(program2) {
|
|
|
46017
46666
|
addCheck("Domain handler coverage", "fail", `Error: ${e.message}`);
|
|
46018
46667
|
}
|
|
46019
46668
|
try {
|
|
46020
|
-
const matrixPath =
|
|
46021
|
-
if (!
|
|
46669
|
+
const matrixPath = join73(projectRoot, "src", "dispatch", "lib", "capability-matrix.ts");
|
|
46670
|
+
if (!existsSync69(matrixPath)) {
|
|
46022
46671
|
addCheck("Capability matrix", "fail", "Capability matrix missing", [{
|
|
46023
46672
|
severity: "error",
|
|
46024
46673
|
category: "configuration",
|
|
@@ -46032,8 +46681,8 @@ function registerDetectDriftCommand(program2) {
|
|
|
46032
46681
|
addCheck("Capability matrix", "fail", `Error: ${e.message}`);
|
|
46033
46682
|
}
|
|
46034
46683
|
try {
|
|
46035
|
-
const schemaPath =
|
|
46036
|
-
if (!
|
|
46684
|
+
const schemaPath = join73(projectRoot, "src", "store", "schema.ts");
|
|
46685
|
+
if (!existsSync69(schemaPath)) {
|
|
46037
46686
|
addCheck("Schema validation", "fail", "Schema definition missing", [{
|
|
46038
46687
|
severity: "error",
|
|
46039
46688
|
category: "data-model",
|
|
@@ -46058,10 +46707,10 @@ function registerDetectDriftCommand(program2) {
|
|
|
46058
46707
|
addCheck("Schema validation", "fail", `Error: ${e.message}`);
|
|
46059
46708
|
}
|
|
46060
46709
|
try {
|
|
46061
|
-
const visionPath =
|
|
46062
|
-
const specPath =
|
|
46710
|
+
const visionPath = join73(projectRoot, "docs", "concepts", "CLEO-VISION.md");
|
|
46711
|
+
const specPath = join73(projectRoot, "docs", "specs", "PORTABLE-BRAIN-SPEC.md");
|
|
46063
46712
|
const issues = [];
|
|
46064
|
-
if (!
|
|
46713
|
+
if (!existsSync69(visionPath)) {
|
|
46065
46714
|
issues.push({
|
|
46066
46715
|
severity: "error",
|
|
46067
46716
|
category: "vision",
|
|
@@ -46070,7 +46719,7 @@ function registerDetectDriftCommand(program2) {
|
|
|
46070
46719
|
recommendation: "Create docs/concepts/CLEO-VISION.md with project vision"
|
|
46071
46720
|
});
|
|
46072
46721
|
}
|
|
46073
|
-
if (!
|
|
46722
|
+
if (!existsSync69(specPath)) {
|
|
46074
46723
|
issues.push({
|
|
46075
46724
|
severity: "error",
|
|
46076
46725
|
category: "spec",
|
|
@@ -46107,8 +46756,8 @@ function registerDetectDriftCommand(program2) {
|
|
|
46107
46756
|
addCheck("Canonical identity", "fail", `Error: ${e.message}`);
|
|
46108
46757
|
}
|
|
46109
46758
|
try {
|
|
46110
|
-
const injectionPath =
|
|
46111
|
-
if (!
|
|
46759
|
+
const injectionPath = join73(projectRoot, ".cleo", "templates", "CLEO-INJECTION.md");
|
|
46760
|
+
if (!existsSync69(injectionPath)) {
|
|
46112
46761
|
addCheck("Agent injection", "fail", "Agent injection template missing", [{
|
|
46113
46762
|
severity: "error",
|
|
46114
46763
|
category: "agent-support",
|
|
@@ -46133,8 +46782,8 @@ function registerDetectDriftCommand(program2) {
|
|
|
46133
46782
|
addCheck("Agent injection", "fail", `Error: ${e.message}`);
|
|
46134
46783
|
}
|
|
46135
46784
|
try {
|
|
46136
|
-
const exitCodesPath =
|
|
46137
|
-
if (!
|
|
46785
|
+
const exitCodesPath = join73(projectRoot, "src", "types", "exit-codes.ts");
|
|
46786
|
+
if (!existsSync69(exitCodesPath)) {
|
|
46138
46787
|
addCheck("Exit codes", "fail", "Exit codes definition missing", [{
|
|
46139
46788
|
severity: "error",
|
|
46140
46789
|
category: "protocol",
|
|
@@ -46373,8 +47022,8 @@ function registerRemoteCommand(program2) {
|
|
|
46373
47022
|
init_renderers();
|
|
46374
47023
|
init_paths();
|
|
46375
47024
|
import { mkdir as mkdir13, writeFile as writeFile14, readFile as readFile21 } from "node:fs/promises";
|
|
46376
|
-
import { existsSync as
|
|
46377
|
-
import { join as
|
|
47025
|
+
import { existsSync as existsSync70, readFileSync as readFileSync51 } from "node:fs";
|
|
47026
|
+
import { join as join74, resolve as resolve10, dirname as dirname19 } from "node:path";
|
|
46378
47027
|
import { homedir as homedir4 } from "node:os";
|
|
46379
47028
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
46380
47029
|
function registerInstallGlobalCommand(program2) {
|
|
@@ -46384,17 +47033,17 @@ function registerInstallGlobalCommand(program2) {
|
|
|
46384
47033
|
const warnings = [];
|
|
46385
47034
|
try {
|
|
46386
47035
|
const cleoHome = getCleoHome();
|
|
46387
|
-
const globalTemplatesDir =
|
|
47036
|
+
const globalTemplatesDir = join74(cleoHome, "templates");
|
|
46388
47037
|
if (!isDryRun) {
|
|
46389
47038
|
await mkdir13(globalTemplatesDir, { recursive: true });
|
|
46390
47039
|
}
|
|
46391
47040
|
try {
|
|
46392
47041
|
const thisFile = fileURLToPath6(import.meta.url);
|
|
46393
47042
|
const packageRoot = resolve10(dirname19(thisFile), "..", "..", "..");
|
|
46394
|
-
const templatePath =
|
|
46395
|
-
if (
|
|
46396
|
-
const content =
|
|
46397
|
-
const globalPath =
|
|
47043
|
+
const templatePath = join74(packageRoot, "templates", "CLEO-INJECTION.md");
|
|
47044
|
+
if (existsSync70(templatePath)) {
|
|
47045
|
+
const content = readFileSync51(templatePath, "utf-8");
|
|
47046
|
+
const globalPath = join74(globalTemplatesDir, "CLEO-INJECTION.md");
|
|
46398
47047
|
if (!isDryRun) {
|
|
46399
47048
|
await writeFile14(globalPath, content);
|
|
46400
47049
|
}
|
|
@@ -46404,12 +47053,12 @@ function registerInstallGlobalCommand(program2) {
|
|
|
46404
47053
|
warnings.push("Could not refresh CLEO-INJECTION.md template");
|
|
46405
47054
|
}
|
|
46406
47055
|
const globalAgentsDir = getAgentsHome();
|
|
46407
|
-
const globalAgentsMd =
|
|
47056
|
+
const globalAgentsMd = join74(globalAgentsDir, "AGENTS.md");
|
|
46408
47057
|
try {
|
|
46409
47058
|
const { inject, getInstalledProviders: getInstalledProviders3, injectAll: injectAll2, buildInjectionContent: buildInjectionContent2 } = await import("@cleocode/caamp");
|
|
46410
47059
|
if (!isDryRun) {
|
|
46411
47060
|
await mkdir13(globalAgentsDir, { recursive: true });
|
|
46412
|
-
if (
|
|
47061
|
+
if (existsSync70(globalAgentsMd)) {
|
|
46413
47062
|
const content = await readFile21(globalAgentsMd, "utf8");
|
|
46414
47063
|
const stripped = content.replace(/\n?<!-- CLEO:START -->[\s\S]*?<!-- CLEO:END -->\n?/g, "");
|
|
46415
47064
|
if (stripped !== content) {
|
|
@@ -46428,8 +47077,8 @@ function registerInstallGlobalCommand(program2) {
|
|
|
46428
47077
|
const injectionContent = buildInjectionContent2({ references: ["@~/.agents/AGENTS.md"] });
|
|
46429
47078
|
if (!isDryRun) {
|
|
46430
47079
|
for (const provider of providers) {
|
|
46431
|
-
const instructFilePath =
|
|
46432
|
-
if (
|
|
47080
|
+
const instructFilePath = join74(provider.pathGlobal, provider.instructFile);
|
|
47081
|
+
if (existsSync70(instructFilePath)) {
|
|
46433
47082
|
const fileContent = await readFile21(instructFilePath, "utf8");
|
|
46434
47083
|
const stripped = fileContent.replace(/\n?<!-- CLEO:START -->[\s\S]*?<!-- CLEO:END -->\n?/g, "");
|
|
46435
47084
|
if (stripped !== fileContent) {
|
|
@@ -46444,7 +47093,7 @@ function registerInstallGlobalCommand(program2) {
|
|
|
46444
47093
|
}
|
|
46445
47094
|
} else {
|
|
46446
47095
|
for (const p of providers) {
|
|
46447
|
-
const displayPath =
|
|
47096
|
+
const displayPath = join74(p.pathGlobal, p.instructFile).replace(homedir4(), "~");
|
|
46448
47097
|
created.push(`${displayPath} (would update CAAMP block)`);
|
|
46449
47098
|
}
|
|
46450
47099
|
}
|
|
@@ -46650,7 +47299,7 @@ init_paths();
|
|
|
46650
47299
|
init_paths();
|
|
46651
47300
|
init_brain_sqlite();
|
|
46652
47301
|
init_brain_search();
|
|
46653
|
-
import { existsSync as
|
|
47302
|
+
import { existsSync as existsSync71 } from "node:fs";
|
|
46654
47303
|
import { createRequire as createRequire5 } from "node:module";
|
|
46655
47304
|
var _require5 = createRequire5(import.meta.url);
|
|
46656
47305
|
var { DatabaseSync: DatabaseSync3 } = _require5("node:sqlite");
|
|
@@ -46694,7 +47343,7 @@ async function migrateClaudeMem(projectRoot, options = {}) {
|
|
|
46694
47343
|
errors: [],
|
|
46695
47344
|
dryRun
|
|
46696
47345
|
};
|
|
46697
|
-
if (!
|
|
47346
|
+
if (!existsSync71(sourcePath)) {
|
|
46698
47347
|
throw new Error(
|
|
46699
47348
|
`claude-mem database not found at: ${sourcePath}
|
|
46700
47349
|
Expected location: ~/.claude-mem/claude-mem.db
|
|
@@ -47096,10 +47745,10 @@ init_config();
|
|
|
47096
47745
|
// src/cli/logger-bootstrap.ts
|
|
47097
47746
|
init_logger();
|
|
47098
47747
|
init_project_info();
|
|
47099
|
-
import { join as
|
|
47748
|
+
import { join as join75 } from "node:path";
|
|
47100
47749
|
function initCliLogger(cwd, loggingConfig) {
|
|
47101
47750
|
const projectInfo = getProjectInfoSync(cwd);
|
|
47102
|
-
initLogger(
|
|
47751
|
+
initLogger(join75(cwd, ".cleo"), loggingConfig, projectInfo?.projectHash);
|
|
47103
47752
|
}
|
|
47104
47753
|
|
|
47105
47754
|
// src/cli/index.ts
|
|
@@ -47332,8 +47981,8 @@ Upgrade options:
|
|
|
47332
47981
|
}
|
|
47333
47982
|
function getPackageVersion() {
|
|
47334
47983
|
try {
|
|
47335
|
-
const moduleRoot =
|
|
47336
|
-
const pkg = JSON.parse(
|
|
47984
|
+
const moduleRoot = join76(import.meta.dirname ?? "", "..", "..");
|
|
47985
|
+
const pkg = JSON.parse(readFileSync52(join76(moduleRoot, "package.json"), "utf-8"));
|
|
47337
47986
|
return pkg.version ?? "0.0.0";
|
|
47338
47987
|
} catch {
|
|
47339
47988
|
return "0.0.0";
|
|
@@ -47444,7 +48093,7 @@ program.hook("preAction", async () => {
|
|
|
47444
48093
|
const config = await loadConfig();
|
|
47445
48094
|
initCliLogger(process.cwd(), config.logging);
|
|
47446
48095
|
const { pruneAuditLog: pruneAuditLog2 } = await Promise.resolve().then(() => (init_audit_prune(), audit_prune_exports));
|
|
47447
|
-
pruneAuditLog2(
|
|
48096
|
+
pruneAuditLog2(join76(process.cwd(), ".cleo"), config.logging).catch(() => {
|
|
47448
48097
|
});
|
|
47449
48098
|
} catch {
|
|
47450
48099
|
}
|
|
@@ -47484,7 +48133,7 @@ program.hook("preAction", (thisCommand) => {
|
|
|
47484
48133
|
}
|
|
47485
48134
|
});
|
|
47486
48135
|
if (process.argv[2] === "mcp") {
|
|
47487
|
-
const mcpPath =
|
|
48136
|
+
const mcpPath = join76(import.meta.dirname ?? "", "..", "mcp", "index.js");
|
|
47488
48137
|
const { spawn: spawn3 } = await import("node:child_process");
|
|
47489
48138
|
const child = spawn3(
|
|
47490
48139
|
process.execPath,
|