@vemdev/cli 0.1.53 → 0.1.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-PPAFJ3LP.js → chunk-SOAUDPRS.js} +3 -1
- package/dist/{chunk-PPAFJ3LP.js.map → chunk-SOAUDPRS.js.map} +1 -1
- package/dist/{dist-2IBAWS6G.js → dist-27CAVU4D.js} +2 -2
- package/dist/index.js +522 -298
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- /package/dist/{dist-2IBAWS6G.js.map → dist-27CAVU4D.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
isVemInitialized,
|
|
24
24
|
listAllAgentSessions,
|
|
25
25
|
parseVemUpdateBlock
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-SOAUDPRS.js";
|
|
27
27
|
import {
|
|
28
28
|
readCopilotSessionDetail
|
|
29
29
|
} from "./chunk-PO3WNPAJ.js";
|
|
@@ -891,7 +891,13 @@ function truncateForDisplay(value, maxChars) {
|
|
|
891
891
|
return `${trimmed.slice(0, Math.max(0, maxChars - 15)).trimEnd()}
|
|
892
892
|
...[truncated]`;
|
|
893
893
|
}
|
|
894
|
-
var AGENT_TASK_STATUSES = /* @__PURE__ */ new Set([
|
|
894
|
+
var AGENT_TASK_STATUSES = /* @__PURE__ */ new Set([
|
|
895
|
+
"todo",
|
|
896
|
+
"in-review",
|
|
897
|
+
"in-progress",
|
|
898
|
+
"blocked",
|
|
899
|
+
"done"
|
|
900
|
+
]);
|
|
895
901
|
var MAX_CHILD_TASKS_IN_PROMPT = 12;
|
|
896
902
|
var TASK_STATUS_ORDER = {
|
|
897
903
|
"in-review": 0,
|
|
@@ -967,32 +973,20 @@ var fetchRemoteAgentTasks = async (configService) => {
|
|
|
967
973
|
return null;
|
|
968
974
|
}
|
|
969
975
|
};
|
|
970
|
-
var fetchRemoteAgentTaskById = async (configService,
|
|
976
|
+
var fetchRemoteAgentTaskById = async (configService, _taskId, dbId) => {
|
|
971
977
|
try {
|
|
972
|
-
const
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
id: taskId,
|
|
979
|
-
include_deleted: "true"
|
|
980
|
-
});
|
|
981
|
-
const response = await fetch(
|
|
982
|
-
`${API_URL}/projects/${projectId}/tasks?${query.toString()}`,
|
|
983
|
-
{
|
|
984
|
-
headers: {
|
|
985
|
-
Authorization: `Bearer ${apiKey}`,
|
|
986
|
-
...await buildDeviceHeaders(configService)
|
|
987
|
-
}
|
|
978
|
+
const apiKey = await resolveApiKey(configService);
|
|
979
|
+
if (!apiKey) return null;
|
|
980
|
+
const response = await fetch(`${API_URL}/tasks/${encodeURIComponent(dbId)}`, {
|
|
981
|
+
headers: {
|
|
982
|
+
Authorization: `Bearer ${apiKey}`,
|
|
983
|
+
...await buildDeviceHeaders(configService)
|
|
988
984
|
}
|
|
989
|
-
);
|
|
985
|
+
});
|
|
990
986
|
if (!response.ok) return null;
|
|
991
987
|
const body = await response.json();
|
|
992
|
-
if (!
|
|
993
|
-
|
|
994
|
-
if (normalized.length === 0) return null;
|
|
995
|
-
return normalized.find((task) => task.id === taskId) ?? normalized[0];
|
|
988
|
+
if (!body.task) return null;
|
|
989
|
+
return normalizeAgentTask(body.task);
|
|
996
990
|
} catch {
|
|
997
991
|
return null;
|
|
998
992
|
}
|
|
@@ -1012,44 +1006,12 @@ var mergeAgentTasks = (localTasks, remote) => {
|
|
|
1012
1006
|
};
|
|
1013
1007
|
var updateTaskMetaRemote = async (configService, task, patch) => {
|
|
1014
1008
|
try {
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
]);
|
|
1019
|
-
if (!apiKey || !projectId) {
|
|
1020
|
-
debugAgentSync(
|
|
1021
|
-
"updateTaskMetaRemote skipped:",
|
|
1022
|
-
`apiKey=${Boolean(apiKey)}`,
|
|
1023
|
-
`projectId=${Boolean(projectId)}`
|
|
1024
|
-
);
|
|
1025
|
-
return false;
|
|
1026
|
-
}
|
|
1027
|
-
const query = new URLSearchParams({
|
|
1028
|
-
id: task.id,
|
|
1029
|
-
include_deleted: "true"
|
|
1030
|
-
});
|
|
1031
|
-
const lookupResponse = await fetch(
|
|
1032
|
-
`${API_URL}/projects/${projectId}/tasks?${query.toString()}`,
|
|
1033
|
-
{
|
|
1034
|
-
headers: {
|
|
1035
|
-
Authorization: `Bearer ${apiKey}`,
|
|
1036
|
-
...await buildDeviceHeaders(configService)
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
);
|
|
1040
|
-
if (!lookupResponse.ok) {
|
|
1041
|
-
debugAgentSync(
|
|
1042
|
-
"task lookup failed:",
|
|
1043
|
-
String(lookupResponse.status),
|
|
1044
|
-
lookupResponse.statusText
|
|
1045
|
-
);
|
|
1009
|
+
const apiKey = await resolveApiKey(configService);
|
|
1010
|
+
if (!apiKey) {
|
|
1011
|
+
debugAgentSync("updateTaskMetaRemote skipped: no apiKey");
|
|
1046
1012
|
return false;
|
|
1047
1013
|
}
|
|
1048
|
-
const
|
|
1049
|
-
const remoteTask = Array.isArray(lookupBody.tasks) ? lookupBody.tasks.find(
|
|
1050
|
-
(entry) => asTrimmedString(entry.id) === task.id
|
|
1051
|
-
) ?? lookupBody.tasks[0] : null;
|
|
1052
|
-
const dbId = asTrimmedString(remoteTask?.db_id) ?? asTrimmedString(task.db_id);
|
|
1014
|
+
const dbId = asTrimmedString(task.db_id);
|
|
1053
1015
|
if (!dbId) {
|
|
1054
1016
|
debugAgentSync("task lookup missing db_id", `task=${task.id}`);
|
|
1055
1017
|
return false;
|
|
@@ -1069,25 +1031,24 @@ var updateTaskMetaRemote = async (configService, task, patch) => {
|
|
|
1069
1031
|
) : void 0;
|
|
1070
1032
|
const normalizedSessions = Array.isArray(patch.sessions) && patch.sessions.length > 0 ? patch.sessions : void 0;
|
|
1071
1033
|
const payload = {
|
|
1072
|
-
title: asTrimmedString(
|
|
1073
|
-
description: asTrimmedString(
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
deleted_at: asTrimmedString(remoteTask?.deleted_at) ?? null
|
|
1034
|
+
title: asTrimmedString(task.title) ?? task.title,
|
|
1035
|
+
description: asTrimmedString(task.description) ?? null,
|
|
1036
|
+
priority: asTrimmedString(task.priority) ?? "medium",
|
|
1037
|
+
tags: normalizeStringArray(task.tags),
|
|
1038
|
+
type: asTrimmedString(task.type) ?? null,
|
|
1039
|
+
estimate_hours: normalizeNumber(task.estimate_hours),
|
|
1040
|
+
depends_on: normalizeStringArray(task.depends_on),
|
|
1041
|
+
blocked_by: normalizeStringArray(task.blocked_by),
|
|
1042
|
+
recurrence_rule: asTrimmedString(task.recurrence_rule) ?? null,
|
|
1043
|
+
owner_id: asTrimmedString(task.owner_id) ?? null,
|
|
1044
|
+
reviewer_id: asTrimmedString(task.reviewer_id) ?? null,
|
|
1045
|
+
parent_id: asTrimmedString(task.parent_id) ?? null,
|
|
1046
|
+
subtask_order: typeof task.subtask_order === "number" ? task.subtask_order : null,
|
|
1047
|
+
due_at: asTrimmedString(task.due_at) ?? null,
|
|
1048
|
+
validation_steps: normalizeStringArray(task.validation_steps),
|
|
1049
|
+
evidence: normalizeStringArray(task.evidence),
|
|
1050
|
+
related_decisions: Array.isArray(task.related_decisions) ? task.related_decisions : [],
|
|
1051
|
+
deleted_at: asTrimmedString(task.deleted_at) ?? null
|
|
1091
1052
|
};
|
|
1092
1053
|
if (patch.status !== void 0) payload.status = patch.status;
|
|
1093
1054
|
if (normalizedEvidence !== void 0) payload.evidence = normalizedEvidence;
|
|
@@ -1102,24 +1063,39 @@ var updateTaskMetaRemote = async (configService, task, patch) => {
|
|
|
1102
1063
|
payload.actor = patch.actor.trim().length > 0 ? patch.actor.trim() : void 0;
|
|
1103
1064
|
}
|
|
1104
1065
|
if (patch.title !== void 0) payload.title = patch.title;
|
|
1105
|
-
if (patch.description !== void 0)
|
|
1066
|
+
if (patch.description !== void 0)
|
|
1067
|
+
payload.description = patch.description;
|
|
1106
1068
|
if (patch.priority !== void 0) payload.priority = patch.priority;
|
|
1107
1069
|
if (patch.tags !== void 0) payload.tags = patch.tags;
|
|
1108
1070
|
if (patch.type !== void 0) payload.type = patch.type;
|
|
1109
|
-
if (patch.estimate_hours !== void 0)
|
|
1071
|
+
if (patch.estimate_hours !== void 0)
|
|
1072
|
+
payload.estimate_hours = patch.estimate_hours;
|
|
1110
1073
|
if (patch.depends_on !== void 0) payload.depends_on = patch.depends_on;
|
|
1111
1074
|
if (patch.blocked_by !== void 0) payload.blocked_by = patch.blocked_by;
|
|
1112
|
-
if (patch.recurrence_rule !== void 0)
|
|
1075
|
+
if (patch.recurrence_rule !== void 0)
|
|
1076
|
+
payload.recurrence_rule = patch.recurrence_rule;
|
|
1113
1077
|
if (patch.owner_id !== void 0) payload.owner_id = patch.owner_id;
|
|
1114
|
-
if (patch.reviewer_id !== void 0)
|
|
1115
|
-
|
|
1078
|
+
if (patch.reviewer_id !== void 0)
|
|
1079
|
+
payload.reviewer_id = patch.reviewer_id;
|
|
1080
|
+
if (patch.validation_steps !== void 0)
|
|
1081
|
+
payload.validation_steps = patch.validation_steps;
|
|
1116
1082
|
if (patch.user_notes !== void 0) payload.user_notes = patch.user_notes;
|
|
1117
|
-
if (patch.github_issue_number !== void 0)
|
|
1083
|
+
if (patch.github_issue_number !== void 0)
|
|
1084
|
+
payload.github_issue_number = patch.github_issue_number;
|
|
1118
1085
|
if (patch.parent_id !== void 0) payload.parent_id = patch.parent_id;
|
|
1119
|
-
if (patch.subtask_order !== void 0)
|
|
1086
|
+
if (patch.subtask_order !== void 0)
|
|
1087
|
+
payload.subtask_order = patch.subtask_order;
|
|
1120
1088
|
if (patch.due_at !== void 0) payload.due_at = patch.due_at;
|
|
1121
|
-
if (patch.raw_vem_update !== void 0)
|
|
1122
|
-
|
|
1089
|
+
if (patch.raw_vem_update !== void 0)
|
|
1090
|
+
payload.raw_vem_update = patch.raw_vem_update;
|
|
1091
|
+
if (patch.cli_version !== void 0)
|
|
1092
|
+
payload.cli_version = patch.cli_version;
|
|
1093
|
+
if (patch.task_context !== void 0)
|
|
1094
|
+
payload.task_context = patch.task_context;
|
|
1095
|
+
if (patch.task_context_summary !== void 0)
|
|
1096
|
+
payload.task_context_summary = patch.task_context_summary;
|
|
1097
|
+
if (patch.changelog_entry !== void 0)
|
|
1098
|
+
payload.changelog_entry = patch.changelog_entry;
|
|
1123
1099
|
const response = await fetch(
|
|
1124
1100
|
`${API_URL}/tasks/${encodeURIComponent(dbId)}/meta`,
|
|
1125
1101
|
{
|
|
@@ -1150,30 +1126,6 @@ var updateTaskMetaRemote = async (configService, task, patch) => {
|
|
|
1150
1126
|
return false;
|
|
1151
1127
|
}
|
|
1152
1128
|
};
|
|
1153
|
-
var updateTaskContextRemote = async (configService, task, payload) => {
|
|
1154
|
-
try {
|
|
1155
|
-
const [apiKey, projectId] = await Promise.all([
|
|
1156
|
-
resolveApiKey(configService),
|
|
1157
|
-
configService.getProjectId()
|
|
1158
|
-
]);
|
|
1159
|
-
if (!apiKey || !projectId) return false;
|
|
1160
|
-
const response = await fetch(
|
|
1161
|
-
`${API_URL}/projects/${projectId}/tasks/${encodeURIComponent(task.id)}/context`,
|
|
1162
|
-
{
|
|
1163
|
-
method: "PUT",
|
|
1164
|
-
headers: {
|
|
1165
|
-
Authorization: `Bearer ${apiKey}`,
|
|
1166
|
-
"Content-Type": "application/json",
|
|
1167
|
-
...await buildDeviceHeaders(configService)
|
|
1168
|
-
},
|
|
1169
|
-
body: JSON.stringify(payload)
|
|
1170
|
-
}
|
|
1171
|
-
);
|
|
1172
|
-
return response.ok;
|
|
1173
|
-
} catch {
|
|
1174
|
-
return false;
|
|
1175
|
-
}
|
|
1176
|
-
};
|
|
1177
1129
|
var markTaskInProgressRemote = async (configService, task, actor) => {
|
|
1178
1130
|
return updateTaskMetaRemote(configService, task, {
|
|
1179
1131
|
status: "in-progress",
|
|
@@ -1203,10 +1155,41 @@ var buildRemoteTaskContextPatch = (patch, updatedTask) => {
|
|
|
1203
1155
|
}
|
|
1204
1156
|
return Object.keys(payload).length > 0 ? payload : null;
|
|
1205
1157
|
};
|
|
1206
|
-
var syncParsedTaskUpdatesToRemote = async (configService, update, result) => {
|
|
1207
|
-
|
|
1158
|
+
var syncParsedTaskUpdatesToRemote = async (configService, update, result, activeTask) => {
|
|
1159
|
+
const hasTasks = Array.isArray(update.tasks) && update.tasks.length > 0;
|
|
1160
|
+
if (!hasTasks) {
|
|
1161
|
+
const hasContent = typeof update.context === "string" && update.context.trim().length > 0 || Array.isArray(update.changelog_append) && update.changelog_append.length > 0 || typeof update.changelog_append === "string" && update.changelog_append.trim().length > 0;
|
|
1162
|
+
if (activeTask && hasContent) {
|
|
1163
|
+
const changelogEntry = Array.isArray(update.changelog_append) ? update.changelog_append.join("\n").trim() || null : update.changelog_append?.trim() ?? null;
|
|
1164
|
+
await updateTaskMetaRemote(configService, activeTask, {
|
|
1165
|
+
raw_vem_update: JSON.parse(JSON.stringify(update)),
|
|
1166
|
+
cli_version: "0.1.55",
|
|
1167
|
+
...changelogEntry ? { changelog_entry: changelogEntry } : {}
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
if (!result) return;
|
|
1208
1173
|
const changelogReasoning = Array.isArray(update.changelog_append) ? update.changelog_append.join("\n").trim() : update.changelog_append?.trim() ?? void 0;
|
|
1209
|
-
const
|
|
1174
|
+
const tasksMissingDbId = result.updatedTasks.filter(
|
|
1175
|
+
(t) => !asTrimmedString(t.db_id)
|
|
1176
|
+
);
|
|
1177
|
+
if (tasksMissingDbId.length > 0) {
|
|
1178
|
+
const remoteTasks = await fetchRemoteAgentTasks(configService);
|
|
1179
|
+
if (remoteTasks) {
|
|
1180
|
+
const remoteById = new Map(
|
|
1181
|
+
remoteTasks.visible.map((t) => [t.id, t])
|
|
1182
|
+
);
|
|
1183
|
+
for (const task of tasksMissingDbId) {
|
|
1184
|
+
const remote = remoteById.get(task.id);
|
|
1185
|
+
if (remote?.db_id) {
|
|
1186
|
+
task.db_id = remote.db_id;
|
|
1187
|
+
await taskService.updateTask(task.id, { db_id: remote.db_id });
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
const patchById = new Map((update.tasks ?? []).map((entry) => [entry.id, entry]));
|
|
1210
1193
|
for (const updatedTask of result.updatedTasks) {
|
|
1211
1194
|
const patch = patchById.get(updatedTask.id);
|
|
1212
1195
|
if (!patch) continue;
|
|
@@ -1237,16 +1220,11 @@ var syncParsedTaskUpdatesToRemote = async (configService, update, result) => {
|
|
|
1237
1220
|
...patch.subtask_order !== void 0 ? { subtask_order: patch.subtask_order } : {},
|
|
1238
1221
|
...patch.due_at !== void 0 ? { due_at: patch.due_at } : {},
|
|
1239
1222
|
raw_vem_update: JSON.parse(JSON.stringify(update)),
|
|
1240
|
-
cli_version: "0.1.
|
|
1223
|
+
cli_version: "0.1.55",
|
|
1224
|
+
// Task memory fields — stored in task_memory_entries on the API side.
|
|
1225
|
+
...buildRemoteTaskContextPatch(patch, updatedTask) ?? {},
|
|
1226
|
+
changelog_entry: changelogReasoning ?? null
|
|
1241
1227
|
});
|
|
1242
|
-
const taskContextPatch = buildRemoteTaskContextPatch(patch, updatedTask);
|
|
1243
|
-
if (taskContextPatch) {
|
|
1244
|
-
await updateTaskContextRemote(
|
|
1245
|
-
configService,
|
|
1246
|
-
remoteTaskRef,
|
|
1247
|
-
taskContextPatch
|
|
1248
|
-
);
|
|
1249
|
-
}
|
|
1250
1228
|
}
|
|
1251
1229
|
};
|
|
1252
1230
|
var mergeTaskContextWithNote = (existing, note) => {
|
|
@@ -1843,7 +1821,11 @@ Your task is ${activeTask?.id}: ${activeTask?.title}${childScopeText}.
|
|
|
1843
1821
|
|
|
1844
1822
|
Start by reading .vem/task_context.md and .vem/current_context.md for task and project context. Then explore the repository structure (list directories, read key files like package.json, README, and relevant source files) to understand the codebase before writing any code. Implement all required changes, run any existing tests or builds to verify, then provide the vem_update block.`;
|
|
1845
1823
|
if (options.autoExit) {
|
|
1846
|
-
console.log(
|
|
1824
|
+
console.log(
|
|
1825
|
+
chalk7.cyan(
|
|
1826
|
+
"Auto-injecting context via -p flag (autonomous mode)..."
|
|
1827
|
+
)
|
|
1828
|
+
);
|
|
1847
1829
|
launchArgs = [...launchArgs, "-p", autonomousPrompt, "--yolo"];
|
|
1848
1830
|
} else {
|
|
1849
1831
|
console.log(chalk7.cyan("Auto-injecting context via -i flag..."));
|
|
@@ -2081,7 +2063,8 @@ Agent exited with code ${exitCode}
|
|
|
2081
2063
|
await syncParsedTaskUpdatesToRemote(
|
|
2082
2064
|
configService,
|
|
2083
2065
|
parsedAgentUpdate,
|
|
2084
|
-
appliedUpdateResult
|
|
2066
|
+
appliedUpdateResult,
|
|
2067
|
+
activeTask
|
|
2085
2068
|
);
|
|
2086
2069
|
const syncedMemory = await syncProjectMemoryToRemote();
|
|
2087
2070
|
if (syncedMemory) {
|
|
@@ -2156,7 +2139,11 @@ Agent exited with code ${exitCode}
|
|
|
2156
2139
|
}
|
|
2157
2140
|
const freshTasks = await taskService.getTasks();
|
|
2158
2141
|
let localActiveTask = activeTask ? freshTasks.find((t) => t.id === activeTask.id) : void 0;
|
|
2159
|
-
const remoteActiveTask = activeTask ? await fetchRemoteAgentTaskById(
|
|
2142
|
+
const remoteActiveTask = activeTask?.db_id ? await fetchRemoteAgentTaskById(
|
|
2143
|
+
configService,
|
|
2144
|
+
activeTask.id,
|
|
2145
|
+
activeTask.db_id
|
|
2146
|
+
) : null;
|
|
2160
2147
|
if (localActiveTask && remoteActiveTask && localActiveTask.status !== remoteActiveTask.status) {
|
|
2161
2148
|
await taskService.updateTask(localActiveTask.id, {
|
|
2162
2149
|
status: remoteActiveTask.status
|
|
@@ -2273,18 +2260,16 @@ ${entry}`;
|
|
|
2273
2260
|
});
|
|
2274
2261
|
}
|
|
2275
2262
|
const remoteTaskRef = freshActiveTask ?? activeTask;
|
|
2276
|
-
const
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
contextSummary !== void 0 ?
|
|
2285
|
-
|
|
2286
|
-
}) : Promise.resolve(false)
|
|
2287
|
-
]
|
|
2263
|
+
const remoteMetaUpdated = await updateTaskMetaRemote(
|
|
2264
|
+
configService,
|
|
2265
|
+
remoteTaskRef,
|
|
2266
|
+
{
|
|
2267
|
+
status: "done",
|
|
2268
|
+
evidence: [evidence.desc],
|
|
2269
|
+
reasoning: reasoningText,
|
|
2270
|
+
actor: agentName,
|
|
2271
|
+
...contextSummary !== void 0 ? { task_context_summary: contextSummary || null } : {}
|
|
2272
|
+
}
|
|
2288
2273
|
);
|
|
2289
2274
|
activeTask.status = "done";
|
|
2290
2275
|
if (!remoteMetaUpdated) {
|
|
@@ -2297,7 +2282,7 @@ ${entry}`;
|
|
|
2297
2282
|
console.log(
|
|
2298
2283
|
chalk7.green(
|
|
2299
2284
|
`
|
|
2300
|
-
\u2714 Task ${freshActiveTask.id} marked as done${remoteMetaUpdated
|
|
2285
|
+
\u2714 Task ${freshActiveTask.id} marked as done${remoteMetaUpdated ? " (cloud + local cache)" : " (local cache)"}.`
|
|
2301
2286
|
)
|
|
2302
2287
|
);
|
|
2303
2288
|
} else {
|
|
@@ -2510,7 +2495,11 @@ function registerCycleCommands(program2) {
|
|
|
2510
2495
|
try {
|
|
2511
2496
|
const cycles = await cycleService.getCycles();
|
|
2512
2497
|
if (cycles.length === 0) {
|
|
2513
|
-
console.log(
|
|
2498
|
+
console.log(
|
|
2499
|
+
chalk9.gray(
|
|
2500
|
+
"\n No cycles yet. Create one with: vem cycle create\n"
|
|
2501
|
+
)
|
|
2502
|
+
);
|
|
2514
2503
|
return;
|
|
2515
2504
|
}
|
|
2516
2505
|
const table = new Table({
|
|
@@ -2634,7 +2623,9 @@ function registerCycleCommands(program2) {
|
|
|
2634
2623
|
process.exitCode = 1;
|
|
2635
2624
|
return;
|
|
2636
2625
|
}
|
|
2637
|
-
const updated = await cycleService.updateCycle(id, {
|
|
2626
|
+
const updated = await cycleService.updateCycle(id, {
|
|
2627
|
+
status: "active"
|
|
2628
|
+
});
|
|
2638
2629
|
console.log(chalk9.cyan(`
|
|
2639
2630
|
\u2714 Cycle ${id} is now active
|
|
2640
2631
|
`));
|
|
@@ -2662,7 +2653,9 @@ function registerCycleCommands(program2) {
|
|
|
2662
2653
|
`));
|
|
2663
2654
|
return;
|
|
2664
2655
|
}
|
|
2665
|
-
const updated = await cycleService.updateCycle(id, {
|
|
2656
|
+
const updated = await cycleService.updateCycle(id, {
|
|
2657
|
+
status: "closed"
|
|
2658
|
+
});
|
|
2666
2659
|
console.log(chalk9.green(`
|
|
2667
2660
|
\u2714 Cycle ${id} closed
|
|
2668
2661
|
`));
|
|
@@ -2678,8 +2671,10 @@ function registerCycleCommands(program2) {
|
|
|
2678
2671
|
);
|
|
2679
2672
|
}
|
|
2680
2673
|
console.log(
|
|
2681
|
-
chalk9.gray(
|
|
2682
|
-
`)
|
|
2674
|
+
chalk9.gray(
|
|
2675
|
+
` Closed: ${new Date(updated.closed_at).toLocaleDateString()}
|
|
2676
|
+
`
|
|
2677
|
+
)
|
|
2683
2678
|
);
|
|
2684
2679
|
} catch (error) {
|
|
2685
2680
|
console.error(chalk9.red(`Failed to close cycle: ${error.message}`));
|
|
@@ -2787,13 +2782,17 @@ function registerCycleCommands(program2) {
|
|
|
2787
2782
|
]);
|
|
2788
2783
|
}
|
|
2789
2784
|
const done = cycleTasks.filter((t) => t.status === "done").length;
|
|
2790
|
-
console.log(
|
|
2785
|
+
console.log(
|
|
2786
|
+
`
|
|
2791
2787
|
${chalk9.white(String(done))}/${chalk9.white(String(cycleTasks.length))} tasks done
|
|
2792
|
-
`
|
|
2788
|
+
`
|
|
2789
|
+
);
|
|
2793
2790
|
console.log(table.toString());
|
|
2794
2791
|
console.log();
|
|
2795
2792
|
} catch (error) {
|
|
2796
|
-
console.error(
|
|
2793
|
+
console.error(
|
|
2794
|
+
chalk9.red(`Failed to show cycle focus: ${error.message}`)
|
|
2795
|
+
);
|
|
2797
2796
|
}
|
|
2798
2797
|
});
|
|
2799
2798
|
}
|
|
@@ -2806,7 +2805,9 @@ import prompts5 from "prompts";
|
|
|
2806
2805
|
async function getRepoRoot2() {
|
|
2807
2806
|
const { execSync: execSync4 } = await import("child_process");
|
|
2808
2807
|
try {
|
|
2809
|
-
return execSync4("git rev-parse --show-toplevel", {
|
|
2808
|
+
return execSync4("git rev-parse --show-toplevel", {
|
|
2809
|
+
encoding: "utf-8"
|
|
2810
|
+
}).trim();
|
|
2810
2811
|
} catch {
|
|
2811
2812
|
return process.cwd();
|
|
2812
2813
|
}
|
|
@@ -2877,9 +2878,7 @@ function registerInstructionCommands(program2) {
|
|
|
2877
2878
|
const dest = path.resolve(repoRoot, entry.path);
|
|
2878
2879
|
const resolvedRoot = path.resolve(repoRoot);
|
|
2879
2880
|
if (!dest.startsWith(`${resolvedRoot}${path.sep}`) && dest !== resolvedRoot) {
|
|
2880
|
-
console.warn(
|
|
2881
|
-
chalk10.yellow(`Skipping unsafe path: ${entry.path}`)
|
|
2882
|
-
);
|
|
2881
|
+
console.warn(chalk10.yellow(`Skipping unsafe path: ${entry.path}`));
|
|
2883
2882
|
continue;
|
|
2884
2883
|
}
|
|
2885
2884
|
if (!options.force) {
|
|
@@ -2905,9 +2904,11 @@ function registerInstructionCommands(program2) {
|
|
|
2905
2904
|
}
|
|
2906
2905
|
const skippedMsg = skipped > 0 ? `, ${skipped} skipped` : "";
|
|
2907
2906
|
console.log(
|
|
2908
|
-
chalk10.green(
|
|
2907
|
+
chalk10.green(
|
|
2908
|
+
`
|
|
2909
2909
|
\u2714 Pulled ${written} instruction file(s)${skippedMsg}.
|
|
2910
|
-
`
|
|
2910
|
+
`
|
|
2911
|
+
)
|
|
2911
2912
|
);
|
|
2912
2913
|
} catch (error) {
|
|
2913
2914
|
console.error(
|
|
@@ -2935,9 +2936,7 @@ function registerInstructionCommands(program2) {
|
|
|
2935
2936
|
const localInstructions = await readLocalInstructions();
|
|
2936
2937
|
if (localInstructions.length === 0) {
|
|
2937
2938
|
console.log(
|
|
2938
|
-
chalk10.yellow(
|
|
2939
|
-
"No instruction files found locally. Looked for:"
|
|
2940
|
-
)
|
|
2939
|
+
chalk10.yellow("No instruction files found locally. Looked for:")
|
|
2941
2940
|
);
|
|
2942
2941
|
for (const f of KNOWN_AGENT_INSTRUCTION_FILES) {
|
|
2943
2942
|
console.log(chalk10.gray(` ${f}`));
|
|
@@ -2975,11 +2974,9 @@ function registerInstructionCommands(program2) {
|
|
|
2975
2974
|
console.log(chalk10.green(` \u2714 ${entry.path}`));
|
|
2976
2975
|
}
|
|
2977
2976
|
const versionNote = data.version_number ? ` (saved as v${data.version_number})` : "";
|
|
2978
|
-
console.log(
|
|
2979
|
-
chalk10.green(`
|
|
2977
|
+
console.log(chalk10.green(`
|
|
2980
2978
|
\u2714 Instructions pushed${versionNote}.
|
|
2981
|
-
`)
|
|
2982
|
-
);
|
|
2979
|
+
`));
|
|
2983
2980
|
} catch (error) {
|
|
2984
2981
|
console.error(
|
|
2985
2982
|
chalk10.red("\n\u2716 Instructions push failed:"),
|
|
@@ -2988,9 +2985,7 @@ function registerInstructionCommands(program2) {
|
|
|
2988
2985
|
process.exitCode = 1;
|
|
2989
2986
|
}
|
|
2990
2987
|
});
|
|
2991
|
-
instructionsCmd.command("status").description(
|
|
2992
|
-
"Check if local instruction files are in sync with the cloud"
|
|
2993
|
-
).action(async () => {
|
|
2988
|
+
instructionsCmd.command("status").description("Check if local instruction files are in sync with the cloud").action(async () => {
|
|
2994
2989
|
await trackCommandUsage("instructions.status");
|
|
2995
2990
|
try {
|
|
2996
2991
|
const configService = new ConfigService();
|
|
@@ -3022,8 +3017,12 @@ function registerInstructionCommands(program2) {
|
|
|
3022
3017
|
}
|
|
3023
3018
|
const cloudData = await cloudRes.json();
|
|
3024
3019
|
const cloudInstructions = cloudData.instructions ?? [];
|
|
3025
|
-
const localMap = new Map(
|
|
3026
|
-
|
|
3020
|
+
const localMap = new Map(
|
|
3021
|
+
localInstructions.map((e) => [e.path, e.content])
|
|
3022
|
+
);
|
|
3023
|
+
const cloudMap = new Map(
|
|
3024
|
+
cloudInstructions.map((e) => [e.path, e.content])
|
|
3025
|
+
);
|
|
3027
3026
|
const allPaths = /* @__PURE__ */ new Set([...localMap.keys(), ...cloudMap.keys()]);
|
|
3028
3027
|
let inSync = true;
|
|
3029
3028
|
console.log(chalk10.bold("\nInstruction file sync status:\n"));
|
|
@@ -3046,7 +3045,9 @@ function registerInstructionCommands(program2) {
|
|
|
3046
3045
|
);
|
|
3047
3046
|
inSync = false;
|
|
3048
3047
|
} else {
|
|
3049
|
-
console.log(
|
|
3048
|
+
console.log(
|
|
3049
|
+
chalk10.green(` \u2714 ${filePath}`) + chalk10.gray(" (in sync)")
|
|
3050
|
+
);
|
|
3050
3051
|
}
|
|
3051
3052
|
}
|
|
3052
3053
|
if (allPaths.size === 0) {
|
|
@@ -3165,9 +3166,7 @@ function registerInstructionCommands(program2) {
|
|
|
3165
3166
|
)
|
|
3166
3167
|
);
|
|
3167
3168
|
console.log(
|
|
3168
|
-
chalk10.gray(
|
|
3169
|
-
" Run `vem instructions pull` to update local files."
|
|
3170
|
-
)
|
|
3169
|
+
chalk10.gray(" Run `vem instructions pull` to update local files.")
|
|
3171
3170
|
);
|
|
3172
3171
|
} catch (error) {
|
|
3173
3172
|
console.error(
|
|
@@ -3402,7 +3401,7 @@ function registerMaintenanceCommands(program2) {
|
|
|
3402
3401
|
});
|
|
3403
3402
|
program2.command("diff").description("Show differences between local and cloud state").option("--detailed", "Show detailed content diffs").option("--json", "Output as JSON").action(async (options) => {
|
|
3404
3403
|
try {
|
|
3405
|
-
const { DiffService } = await import("./dist-
|
|
3404
|
+
const { DiffService } = await import("./dist-27CAVU4D.js");
|
|
3406
3405
|
const diffService = new DiffService();
|
|
3407
3406
|
const result = await diffService.compareWithLastPush();
|
|
3408
3407
|
if (options.json) {
|
|
@@ -3460,7 +3459,7 @@ ${"\u2500".repeat(50)}`));
|
|
|
3460
3459
|
});
|
|
3461
3460
|
program2.command("doctor").description("Run health checks on VEM setup").option("--json", "Output as JSON").action(async (options) => {
|
|
3462
3461
|
try {
|
|
3463
|
-
const { DoctorService } = await import("./dist-
|
|
3462
|
+
const { DoctorService } = await import("./dist-27CAVU4D.js");
|
|
3464
3463
|
const doctorService = new DoctorService();
|
|
3465
3464
|
const results = await doctorService.runAllChecks();
|
|
3466
3465
|
if (options.json) {
|
|
@@ -4109,7 +4108,13 @@ function commandExists(command) {
|
|
|
4109
4108
|
return false;
|
|
4110
4109
|
}
|
|
4111
4110
|
}
|
|
4112
|
-
var KNOWN_RUNNER_AGENTS = [
|
|
4111
|
+
var KNOWN_RUNNER_AGENTS = [
|
|
4112
|
+
"copilot",
|
|
4113
|
+
"gh",
|
|
4114
|
+
"claude",
|
|
4115
|
+
"gemini",
|
|
4116
|
+
"codex"
|
|
4117
|
+
];
|
|
4113
4118
|
function hasSandboxCredentials(agent) {
|
|
4114
4119
|
if (agent === "claude") {
|
|
4115
4120
|
return typeof process.env.ANTHROPIC_API_KEY === "string" && process.env.ANTHROPIC_API_KEY.trim().length > 0;
|
|
@@ -4118,7 +4123,9 @@ function hasSandboxCredentials(agent) {
|
|
|
4118
4123
|
const envToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
4119
4124
|
if (envToken && envToken.trim().length > 0) return true;
|
|
4120
4125
|
try {
|
|
4121
|
-
const token = execFileSync("gh", ["auth", "token"], {
|
|
4126
|
+
const token = execFileSync("gh", ["auth", "token"], {
|
|
4127
|
+
encoding: "utf-8"
|
|
4128
|
+
}).trim();
|
|
4122
4129
|
return token.length > 0;
|
|
4123
4130
|
} catch {
|
|
4124
4131
|
return false;
|
|
@@ -4134,9 +4141,13 @@ function hasSandboxCredentials(agent) {
|
|
|
4134
4141
|
}
|
|
4135
4142
|
function getAvailableAgentCommands(selectedAgent, sandbox) {
|
|
4136
4143
|
const isAvailable = (command) => commandExists(command) && (!sandbox || hasSandboxCredentials(command));
|
|
4137
|
-
const knownAvailable = KNOWN_RUNNER_AGENTS.filter(
|
|
4144
|
+
const knownAvailable = KNOWN_RUNNER_AGENTS.filter(
|
|
4145
|
+
(command) => isAvailable(command)
|
|
4146
|
+
);
|
|
4138
4147
|
const selectedAvailable = isAvailable(selectedAgent);
|
|
4139
|
-
if (selectedAvailable && !knownAvailable.includes(
|
|
4148
|
+
if (selectedAvailable && !knownAvailable.includes(
|
|
4149
|
+
selectedAgent
|
|
4150
|
+
)) {
|
|
4140
4151
|
return [selectedAgent, ...knownAvailable];
|
|
4141
4152
|
}
|
|
4142
4153
|
return knownAvailable;
|
|
@@ -4171,9 +4182,7 @@ function checkDockerAvailable() {
|
|
|
4171
4182
|
try {
|
|
4172
4183
|
execFileSync("docker", ["info"], { stdio: "ignore" });
|
|
4173
4184
|
} catch {
|
|
4174
|
-
console.error(
|
|
4175
|
-
chalk13.red("\u2717 Docker is not running or not installed.")
|
|
4176
|
-
);
|
|
4185
|
+
console.error(chalk13.red("\u2717 Docker is not running or not installed."));
|
|
4177
4186
|
console.error(
|
|
4178
4187
|
chalk13.yellow(
|
|
4179
4188
|
" The vem runner requires Docker to run agents in a secure sandbox."
|
|
@@ -4206,7 +4215,9 @@ function getSandboxImageDir() {
|
|
|
4206
4215
|
return dirname2(candidate);
|
|
4207
4216
|
}
|
|
4208
4217
|
}
|
|
4209
|
-
throw new Error(
|
|
4218
|
+
throw new Error(
|
|
4219
|
+
"Dockerfile.sandbox not found. Ensure the vem CLI is installed correctly."
|
|
4220
|
+
);
|
|
4210
4221
|
}
|
|
4211
4222
|
function buildSandboxImage() {
|
|
4212
4223
|
console.log(chalk13.cyan(" Building sandbox Docker image (first use)..."));
|
|
@@ -4220,7 +4231,9 @@ function buildSandboxImage() {
|
|
|
4220
4231
|
}
|
|
4221
4232
|
function ensureSandboxImage() {
|
|
4222
4233
|
try {
|
|
4223
|
-
execFileSync("docker", ["image", "inspect", SANDBOX_IMAGE_NAME], {
|
|
4234
|
+
execFileSync("docker", ["image", "inspect", SANDBOX_IMAGE_NAME], {
|
|
4235
|
+
stdio: "ignore"
|
|
4236
|
+
});
|
|
4224
4237
|
} catch {
|
|
4225
4238
|
buildSandboxImage();
|
|
4226
4239
|
}
|
|
@@ -4235,7 +4248,11 @@ function collectSandboxCredentials(agent) {
|
|
|
4235
4248
|
if (agent === "claude") {
|
|
4236
4249
|
addFromEnv("ANTHROPIC_API_KEY");
|
|
4237
4250
|
if (!creds.ANTHROPIC_API_KEY) {
|
|
4238
|
-
console.error(
|
|
4251
|
+
console.error(
|
|
4252
|
+
chalk13.red(
|
|
4253
|
+
`\u2717 ANTHROPIC_API_KEY is not set. Required for --agent claude.`
|
|
4254
|
+
)
|
|
4255
|
+
);
|
|
4239
4256
|
process.exit(1);
|
|
4240
4257
|
}
|
|
4241
4258
|
} else if (agent === "copilot" || agent === "gh") {
|
|
@@ -4244,31 +4261,43 @@ function collectSandboxCredentials(agent) {
|
|
|
4244
4261
|
creds.GITHUB_TOKEN = envToken;
|
|
4245
4262
|
} else {
|
|
4246
4263
|
try {
|
|
4247
|
-
const token = execFileSync("gh", ["auth", "token"], {
|
|
4264
|
+
const token = execFileSync("gh", ["auth", "token"], {
|
|
4265
|
+
encoding: "utf-8"
|
|
4266
|
+
}).trim();
|
|
4248
4267
|
if (token) creds.GITHUB_TOKEN = token;
|
|
4249
4268
|
} catch {
|
|
4250
4269
|
}
|
|
4251
4270
|
}
|
|
4252
4271
|
if (!creds.GITHUB_TOKEN) {
|
|
4253
|
-
console.error(
|
|
4254
|
-
|
|
4272
|
+
console.error(
|
|
4273
|
+
chalk13.red(`\u2717 GitHub token not found. Required for --agent copilot.`)
|
|
4274
|
+
);
|
|
4275
|
+
console.error(
|
|
4276
|
+
chalk13.gray(" Set GITHUB_TOKEN env var or run: gh auth login")
|
|
4277
|
+
);
|
|
4255
4278
|
process.exit(1);
|
|
4256
4279
|
}
|
|
4257
4280
|
} else if (agent === "gemini") {
|
|
4258
4281
|
addFromEnv("GEMINI_API_KEY");
|
|
4259
4282
|
if (!creds.GEMINI_API_KEY) {
|
|
4260
|
-
console.error(
|
|
4283
|
+
console.error(
|
|
4284
|
+
chalk13.red(`\u2717 GEMINI_API_KEY is not set. Required for --agent gemini.`)
|
|
4285
|
+
);
|
|
4261
4286
|
process.exit(1);
|
|
4262
4287
|
}
|
|
4263
4288
|
} else if (agent === "codex") {
|
|
4264
4289
|
addFromEnv("OPENAI_API_KEY");
|
|
4265
4290
|
if (!creds.OPENAI_API_KEY) {
|
|
4266
|
-
console.error(
|
|
4291
|
+
console.error(
|
|
4292
|
+
chalk13.red(`\u2717 OPENAI_API_KEY is not set. Required for --agent codex.`)
|
|
4293
|
+
);
|
|
4267
4294
|
process.exit(1);
|
|
4268
4295
|
}
|
|
4269
4296
|
}
|
|
4270
|
-
if (process.env.GIT_AUTHOR_NAME)
|
|
4271
|
-
|
|
4297
|
+
if (process.env.GIT_AUTHOR_NAME)
|
|
4298
|
+
creds.GIT_AUTHOR_NAME = process.env.GIT_AUTHOR_NAME;
|
|
4299
|
+
if (process.env.GIT_AUTHOR_EMAIL)
|
|
4300
|
+
creds.GIT_AUTHOR_EMAIL = process.env.GIT_AUTHOR_EMAIL;
|
|
4272
4301
|
return creds;
|
|
4273
4302
|
}
|
|
4274
4303
|
function sanitizeBranchSegment(value) {
|
|
@@ -4282,7 +4311,10 @@ async function resolveGitRemote(configService) {
|
|
|
4282
4311
|
const linkedRemote = (await configService.getLinkedRemoteName())?.trim();
|
|
4283
4312
|
const preferredRemote = linkedRemote || "origin";
|
|
4284
4313
|
try {
|
|
4285
|
-
return {
|
|
4314
|
+
return {
|
|
4315
|
+
name: preferredRemote,
|
|
4316
|
+
url: runGit(["remote", "get-url", preferredRemote])
|
|
4317
|
+
};
|
|
4286
4318
|
} catch {
|
|
4287
4319
|
if (preferredRemote !== "origin") {
|
|
4288
4320
|
try {
|
|
@@ -4316,14 +4348,26 @@ function getCommitHashesSince(baseHash) {
|
|
|
4316
4348
|
const output = runGit(["rev-list", `${baseHash}..HEAD`]);
|
|
4317
4349
|
return output.split("\n").map((entry) => entry.trim()).filter(Boolean);
|
|
4318
4350
|
}
|
|
4351
|
+
var _deviceHeadersCache = null;
|
|
4352
|
+
function getCachedDeviceHeaders(configService) {
|
|
4353
|
+
if (!_deviceHeadersCache) {
|
|
4354
|
+
_deviceHeadersCache = buildDeviceHeaders(configService);
|
|
4355
|
+
}
|
|
4356
|
+
return _deviceHeadersCache;
|
|
4357
|
+
}
|
|
4358
|
+
var FETCH_TIMEOUT_MS = 3e4;
|
|
4319
4359
|
async function apiRequest(configService, apiKey, path4, init) {
|
|
4320
4360
|
const headers = {
|
|
4321
4361
|
Authorization: `Bearer ${apiKey}`,
|
|
4322
4362
|
"Content-Type": "application/json",
|
|
4323
|
-
...await
|
|
4363
|
+
...await getCachedDeviceHeaders(configService),
|
|
4324
4364
|
...init?.headers ?? {}
|
|
4325
4365
|
};
|
|
4326
|
-
return fetch(`${API_URL}${path4}`, {
|
|
4366
|
+
return fetch(`${API_URL}${path4}`, {
|
|
4367
|
+
...init,
|
|
4368
|
+
headers,
|
|
4369
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
4370
|
+
});
|
|
4327
4371
|
}
|
|
4328
4372
|
async function appendRunLogs(configService, apiKey, runId, entries) {
|
|
4329
4373
|
if (entries.length === 0) return;
|
|
@@ -4333,23 +4377,33 @@ async function appendRunLogs(configService, apiKey, runId, entries) {
|
|
|
4333
4377
|
});
|
|
4334
4378
|
}
|
|
4335
4379
|
async function sendRunnerHeartbeat(configService, apiKey, projectId, status, currentTaskRunId, capabilities) {
|
|
4336
|
-
await apiRequest(
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4380
|
+
await apiRequest(
|
|
4381
|
+
configService,
|
|
4382
|
+
apiKey,
|
|
4383
|
+
`/projects/${projectId}/runners/heartbeat`,
|
|
4384
|
+
{
|
|
4385
|
+
method: "POST",
|
|
4386
|
+
body: JSON.stringify({
|
|
4387
|
+
status,
|
|
4388
|
+
current_task_run_id: currentTaskRunId,
|
|
4389
|
+
capabilities
|
|
4390
|
+
})
|
|
4391
|
+
}
|
|
4392
|
+
);
|
|
4344
4393
|
}
|
|
4345
4394
|
async function completeTaskRunWithRetry(configService, apiKey, runId, payload, attempts = 5) {
|
|
4346
4395
|
let lastError = "unknown error";
|
|
4347
4396
|
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
4348
4397
|
try {
|
|
4349
|
-
const response = await apiRequest(
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4398
|
+
const response = await apiRequest(
|
|
4399
|
+
configService,
|
|
4400
|
+
apiKey,
|
|
4401
|
+
`/task-runs/${runId}/complete`,
|
|
4402
|
+
{
|
|
4403
|
+
method: "POST",
|
|
4404
|
+
body: JSON.stringify(payload)
|
|
4405
|
+
}
|
|
4406
|
+
);
|
|
4353
4407
|
if (response.ok) return;
|
|
4354
4408
|
const bodyText = await response.text().catch(() => "");
|
|
4355
4409
|
lastError = `HTTP ${response.status}${bodyText ? `: ${bodyText}` : ""}`;
|
|
@@ -4363,7 +4417,15 @@ async function completeTaskRunWithRetry(configService, apiKey, runId, payload, a
|
|
|
4363
4417
|
throw new Error(`Failed to complete run ${runId}: ${lastError}`);
|
|
4364
4418
|
}
|
|
4365
4419
|
async function executeClaimedRun(input) {
|
|
4366
|
-
const {
|
|
4420
|
+
const {
|
|
4421
|
+
configService,
|
|
4422
|
+
apiKey,
|
|
4423
|
+
projectId,
|
|
4424
|
+
agent,
|
|
4425
|
+
useSandbox,
|
|
4426
|
+
agentPinned,
|
|
4427
|
+
run
|
|
4428
|
+
} = input;
|
|
4367
4429
|
const repoRoot = getRepoRoot3();
|
|
4368
4430
|
let sequence = 1;
|
|
4369
4431
|
let heartbeatTimer = null;
|
|
@@ -4390,7 +4452,11 @@ async function executeClaimedRun(input) {
|
|
|
4390
4452
|
} catch {
|
|
4391
4453
|
originalBranch = null;
|
|
4392
4454
|
}
|
|
4393
|
-
const preparedBranch = prepareTaskBranch(
|
|
4455
|
+
const preparedBranch = prepareTaskBranch(
|
|
4456
|
+
run.task_external_id,
|
|
4457
|
+
baseBranch,
|
|
4458
|
+
remote.name
|
|
4459
|
+
);
|
|
4394
4460
|
baseHash = preparedBranch.baseHash;
|
|
4395
4461
|
branchName = preparedBranch.branchName;
|
|
4396
4462
|
await appendRunLogs(configService, apiKey, run.id, [
|
|
@@ -4403,7 +4469,14 @@ async function executeClaimedRun(input) {
|
|
|
4403
4469
|
]);
|
|
4404
4470
|
const child = spawn3(
|
|
4405
4471
|
process.execPath,
|
|
4406
|
-
[
|
|
4472
|
+
[
|
|
4473
|
+
getCliEntrypoint(),
|
|
4474
|
+
"agent",
|
|
4475
|
+
agent,
|
|
4476
|
+
"--task",
|
|
4477
|
+
run.task_external_id,
|
|
4478
|
+
"--auto-exit"
|
|
4479
|
+
],
|
|
4407
4480
|
{
|
|
4408
4481
|
env: {
|
|
4409
4482
|
...process.env,
|
|
@@ -4473,15 +4546,13 @@ async function executeClaimedRun(input) {
|
|
|
4473
4546
|
{ sequence: sequence++, stream: "stderr", chunk: text }
|
|
4474
4547
|
]);
|
|
4475
4548
|
});
|
|
4476
|
-
const result = await new Promise(
|
|
4477
|
-
(
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
}
|
|
4484
|
-
);
|
|
4549
|
+
const result = await new Promise((resolve3) => {
|
|
4550
|
+
child.on("exit", (code, signal) => resolve3({ code, signal }));
|
|
4551
|
+
child.on("error", (error) => {
|
|
4552
|
+
completionError = error.message;
|
|
4553
|
+
resolve3({ code: null, signal: null });
|
|
4554
|
+
});
|
|
4555
|
+
});
|
|
4485
4556
|
exitCode = result.code;
|
|
4486
4557
|
if (completionError) {
|
|
4487
4558
|
completionStatus = cancellationRequested ? "cancelled" : "failed";
|
|
@@ -4503,7 +4574,11 @@ async function executeClaimedRun(input) {
|
|
|
4503
4574
|
if (completionStatus === "completed" && hasDirtyWorktree()) {
|
|
4504
4575
|
runGit(["add", "-A"], { stdio: "inherit" });
|
|
4505
4576
|
runGit(
|
|
4506
|
-
[
|
|
4577
|
+
[
|
|
4578
|
+
"commit",
|
|
4579
|
+
"-m",
|
|
4580
|
+
`chore(${run.task_external_id}): apply agent changes`
|
|
4581
|
+
],
|
|
4507
4582
|
{ stdio: "inherit" }
|
|
4508
4583
|
);
|
|
4509
4584
|
}
|
|
@@ -4599,15 +4674,19 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4599
4674
|
execFileSync("rm", ["-rf", worktreePath], { stdio: "ignore" });
|
|
4600
4675
|
}
|
|
4601
4676
|
console.log(chalk13.gray(` Cloning ${baseBranch} \u2192 ${worktreePath}`));
|
|
4602
|
-
execFileSync(
|
|
4603
|
-
"
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4677
|
+
execFileSync(
|
|
4678
|
+
"git",
|
|
4679
|
+
[
|
|
4680
|
+
"clone",
|
|
4681
|
+
"--quiet",
|
|
4682
|
+
`file://${repoRoot}`,
|
|
4683
|
+
"--branch",
|
|
4684
|
+
baseBranch,
|
|
4685
|
+
"--single-branch",
|
|
4686
|
+
worktreePath
|
|
4687
|
+
],
|
|
4688
|
+
{ stdio: "pipe" }
|
|
4689
|
+
);
|
|
4611
4690
|
runGitIn(worktreePath, ["checkout", "-b", branchName]);
|
|
4612
4691
|
if (remoteUrl) {
|
|
4613
4692
|
runGitIn(worktreePath, ["remote", "set-url", "origin", remoteUrl]);
|
|
@@ -4674,7 +4753,9 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4674
4753
|
if (dockerProcess?.pid) {
|
|
4675
4754
|
try {
|
|
4676
4755
|
if (containerName) {
|
|
4677
|
-
execFileSync("docker", ["stop", containerName], {
|
|
4756
|
+
execFileSync("docker", ["stop", containerName], {
|
|
4757
|
+
stdio: "ignore"
|
|
4758
|
+
});
|
|
4678
4759
|
} else {
|
|
4679
4760
|
dockerProcess.kill("SIGTERM");
|
|
4680
4761
|
}
|
|
@@ -4682,7 +4763,11 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4682
4763
|
}
|
|
4683
4764
|
}
|
|
4684
4765
|
await appendRunLogs(configService, apiKey, run.id, [
|
|
4685
|
-
{
|
|
4766
|
+
{
|
|
4767
|
+
sequence: sequence++,
|
|
4768
|
+
stream: "system",
|
|
4769
|
+
chunk: "Cancellation requested from web UI. Stopping sandbox container.\n"
|
|
4770
|
+
}
|
|
4686
4771
|
]);
|
|
4687
4772
|
}
|
|
4688
4773
|
if (maxRuntimeAt && !timedOut) {
|
|
@@ -4694,21 +4779,29 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4694
4779
|
if (dockerProcess?.pid) {
|
|
4695
4780
|
try {
|
|
4696
4781
|
if (containerName) {
|
|
4697
|
-
execFileSync("docker", ["stop", containerName], {
|
|
4782
|
+
execFileSync("docker", ["stop", containerName], {
|
|
4783
|
+
stdio: "ignore"
|
|
4784
|
+
});
|
|
4698
4785
|
}
|
|
4699
4786
|
} catch {
|
|
4700
4787
|
}
|
|
4701
4788
|
}
|
|
4702
4789
|
await appendRunLogs(configService, apiKey, run.id, [
|
|
4703
|
-
{
|
|
4790
|
+
{
|
|
4791
|
+
sequence: sequence++,
|
|
4792
|
+
stream: "system",
|
|
4793
|
+
chunk: "Run exceeded the maximum runtime. Stopping sandbox container.\n"
|
|
4794
|
+
}
|
|
4704
4795
|
]);
|
|
4705
4796
|
}
|
|
4706
4797
|
}
|
|
4707
4798
|
} catch {
|
|
4708
4799
|
}
|
|
4709
4800
|
}, 3e4);
|
|
4801
|
+
const stdoutChunks = [];
|
|
4710
4802
|
const streamLogs = (stream, data) => {
|
|
4711
4803
|
const chunk = data.toString("utf-8");
|
|
4804
|
+
if (stream === "stdout") stdoutChunks.push(chunk);
|
|
4712
4805
|
appendRunLogs(configService, apiKey, run.id, [
|
|
4713
4806
|
{ sequence: sequence++, stream, chunk }
|
|
4714
4807
|
]).catch(() => {
|
|
@@ -4728,7 +4821,10 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4728
4821
|
if (exitCode === 0 && !cancellationRequested && !timedOut) {
|
|
4729
4822
|
completionStatus = "completed";
|
|
4730
4823
|
try {
|
|
4731
|
-
const output = runGitIn(worktreePath, [
|
|
4824
|
+
const output = runGitIn(worktreePath, [
|
|
4825
|
+
"rev-list",
|
|
4826
|
+
`${baseHash}..HEAD`
|
|
4827
|
+
]);
|
|
4732
4828
|
commitHashes = output.split("\n").map((h) => h.trim()).filter(Boolean);
|
|
4733
4829
|
} catch {
|
|
4734
4830
|
}
|
|
@@ -4736,18 +4832,29 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4736
4832
|
} else if (!cancellationRequested && !timedOut) {
|
|
4737
4833
|
completionStatus = "failed";
|
|
4738
4834
|
}
|
|
4835
|
+
const fullDockerLogLines2 = stdoutChunks.join("").split("\n").filter(Boolean).slice(-1e3);
|
|
4739
4836
|
if (completionStatus === "completed" && commitHashes.length > 0) {
|
|
4740
4837
|
try {
|
|
4741
|
-
runGitIn(worktreePath, ["push", "-u", "origin", branchName], {
|
|
4838
|
+
runGitIn(worktreePath, ["push", "-u", "origin", branchName], {
|
|
4839
|
+
stdio: "inherit"
|
|
4840
|
+
});
|
|
4742
4841
|
await appendRunLogs(configService, apiKey, run.id, [
|
|
4743
|
-
{
|
|
4744
|
-
|
|
4842
|
+
{
|
|
4843
|
+
sequence: sequence++,
|
|
4844
|
+
stream: "system",
|
|
4845
|
+
chunk: `Pushed branch ${branchName} to ${remote.name}.
|
|
4846
|
+
`
|
|
4847
|
+
}
|
|
4745
4848
|
]);
|
|
4746
4849
|
} catch (pushErr) {
|
|
4747
4850
|
const msg = pushErr instanceof Error ? pushErr.message : String(pushErr);
|
|
4748
4851
|
await appendRunLogs(configService, apiKey, run.id, [
|
|
4749
|
-
{
|
|
4750
|
-
|
|
4852
|
+
{
|
|
4853
|
+
sequence: sequence++,
|
|
4854
|
+
stream: "system",
|
|
4855
|
+
chunk: `Warning: failed to push branch: ${msg}
|
|
4856
|
+
`
|
|
4857
|
+
}
|
|
4751
4858
|
]);
|
|
4752
4859
|
createPr = false;
|
|
4753
4860
|
}
|
|
@@ -4761,8 +4868,12 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4761
4868
|
heartbeatTimer = null;
|
|
4762
4869
|
}
|
|
4763
4870
|
await appendRunLogs(configService, apiKey, run.id, [
|
|
4764
|
-
{
|
|
4765
|
-
|
|
4871
|
+
{
|
|
4872
|
+
sequence: sequence++,
|
|
4873
|
+
stream: "system",
|
|
4874
|
+
chunk: `Sandbox run error: ${msg}
|
|
4875
|
+
`
|
|
4876
|
+
}
|
|
4766
4877
|
]).catch(() => {
|
|
4767
4878
|
});
|
|
4768
4879
|
} finally {
|
|
@@ -4797,19 +4908,35 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4797
4908
|
pr_body: run.user_prompt?.trim() ? `Triggered from VEM web.
|
|
4798
4909
|
|
|
4799
4910
|
Instructions:
|
|
4800
|
-
${run.user_prompt.trim()}` : "Triggered from VEM web."
|
|
4911
|
+
${run.user_prompt.trim()}` : "Triggered from VEM web.",
|
|
4912
|
+
// Pass the full Docker log so the API can parse the vem_update block
|
|
4913
|
+
// reliably even when some live-streamed chunks were dropped.
|
|
4914
|
+
full_log_lines: fullDockerLogLines.length > 0 ? fullDockerLogLines : void 0
|
|
4801
4915
|
});
|
|
4802
4916
|
}
|
|
4803
4917
|
}
|
|
4804
4918
|
async function appendTerminalLogs(configService, apiKey, sessionId, entries) {
|
|
4805
4919
|
if (entries.length === 0) return;
|
|
4806
|
-
await apiRequest(
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4920
|
+
await apiRequest(
|
|
4921
|
+
configService,
|
|
4922
|
+
apiKey,
|
|
4923
|
+
`/terminal-sessions/${sessionId}/logs`,
|
|
4924
|
+
{
|
|
4925
|
+
method: "POST",
|
|
4926
|
+
body: JSON.stringify({ entries })
|
|
4927
|
+
}
|
|
4928
|
+
);
|
|
4810
4929
|
}
|
|
4811
4930
|
async function executeClaimedTerminalSession(input) {
|
|
4812
|
-
const {
|
|
4931
|
+
const {
|
|
4932
|
+
configService,
|
|
4933
|
+
apiKey,
|
|
4934
|
+
projectId,
|
|
4935
|
+
agent,
|
|
4936
|
+
useSandbox,
|
|
4937
|
+
agentPinned,
|
|
4938
|
+
session
|
|
4939
|
+
} = input;
|
|
4813
4940
|
const repoRoot = getRepoRoot3();
|
|
4814
4941
|
let sequence = 2;
|
|
4815
4942
|
let heartbeatTimer = null;
|
|
@@ -4869,15 +4996,13 @@ $ ${session.command}
|
|
|
4869
4996
|
{ sequence: sequence++, stream: "stderr", chunk: text }
|
|
4870
4997
|
]);
|
|
4871
4998
|
});
|
|
4872
|
-
const result = await new Promise(
|
|
4873
|
-
(
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
}
|
|
4880
|
-
);
|
|
4999
|
+
const result = await new Promise((resolve3) => {
|
|
5000
|
+
child.on("exit", (code, signal) => resolve3({ code, signal }));
|
|
5001
|
+
child.on("error", (error) => {
|
|
5002
|
+
completionError = error.message;
|
|
5003
|
+
resolve3({ code: null, signal: null });
|
|
5004
|
+
});
|
|
5005
|
+
});
|
|
4881
5006
|
exitCode = result.code;
|
|
4882
5007
|
if (completionError) {
|
|
4883
5008
|
completionStatus = cancellationRequested ? "cancelled" : "failed";
|
|
@@ -4907,15 +5032,20 @@ $ ${session.command}
|
|
|
4907
5032
|
if (heartbeatTimer) {
|
|
4908
5033
|
clearInterval(heartbeatTimer);
|
|
4909
5034
|
}
|
|
4910
|
-
await apiRequest(
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
5035
|
+
await apiRequest(
|
|
5036
|
+
configService,
|
|
5037
|
+
apiKey,
|
|
5038
|
+
`/terminal-sessions/${session.id}/complete`,
|
|
5039
|
+
{
|
|
5040
|
+
method: "POST",
|
|
5041
|
+
body: JSON.stringify({
|
|
5042
|
+
status: completionStatus,
|
|
5043
|
+
exit_code: exitCode,
|
|
5044
|
+
error_message: completionError,
|
|
5045
|
+
terminal_reason: completionStatus === "cancelled" ? "Command cancelled from workspace UI." : null
|
|
5046
|
+
})
|
|
5047
|
+
}
|
|
5048
|
+
);
|
|
4919
5049
|
await sendRunnerHeartbeat(
|
|
4920
5050
|
configService,
|
|
4921
5051
|
apiKey,
|
|
@@ -4927,7 +5057,14 @@ $ ${session.command}
|
|
|
4927
5057
|
}
|
|
4928
5058
|
}
|
|
4929
5059
|
function registerRunnerCommands(program2) {
|
|
4930
|
-
program2.command("runner").description("Run a paired worker that executes queued web task runs").option(
|
|
5060
|
+
program2.command("runner").description("Run a paired worker that executes queued web task runs").option(
|
|
5061
|
+
"--agent <command>",
|
|
5062
|
+
"Agent command to launch for claimed tasks",
|
|
5063
|
+
"copilot"
|
|
5064
|
+
).option("--poll-interval <seconds>", "Polling interval in seconds", "10").option("--once", "Claim at most one run and then exit").option(
|
|
5065
|
+
"--unsafe",
|
|
5066
|
+
"Disable Docker sandbox (run agent directly on host \u2014 no isolation)"
|
|
5067
|
+
).action(async (options, command) => {
|
|
4931
5068
|
const configService = new ConfigService();
|
|
4932
5069
|
const apiKey = await ensureAuthenticated(configService);
|
|
4933
5070
|
const projectId = await configService.getProjectId();
|
|
@@ -4953,7 +5090,11 @@ function registerRunnerCommands(program2) {
|
|
|
4953
5090
|
)
|
|
4954
5091
|
);
|
|
4955
5092
|
if (!useSandbox) {
|
|
4956
|
-
console.log(
|
|
5093
|
+
console.log(
|
|
5094
|
+
chalk13.yellow(
|
|
5095
|
+
" \u26A0 Running in unsafe mode \u2014 agent has full host access."
|
|
5096
|
+
)
|
|
5097
|
+
);
|
|
4957
5098
|
}
|
|
4958
5099
|
let shouldStop = false;
|
|
4959
5100
|
let consecutiveErrors = 0;
|
|
@@ -4966,7 +5107,11 @@ function registerRunnerCommands(program2) {
|
|
|
4966
5107
|
const claimBackend = useSandbox ? "local_sandbox" : "local_runner";
|
|
4967
5108
|
while (!shouldStop) {
|
|
4968
5109
|
try {
|
|
4969
|
-
const capabilities = getRunnerCapabilities(
|
|
5110
|
+
const capabilities = getRunnerCapabilities(
|
|
5111
|
+
agent,
|
|
5112
|
+
useSandbox,
|
|
5113
|
+
agentPinned
|
|
5114
|
+
);
|
|
4970
5115
|
await sendRunnerHeartbeat(
|
|
4971
5116
|
configService,
|
|
4972
5117
|
apiKey,
|
|
@@ -4990,7 +5135,9 @@ function registerRunnerCommands(program2) {
|
|
|
4990
5135
|
);
|
|
4991
5136
|
if (!claimResponse.ok) {
|
|
4992
5137
|
const data = await claimResponse.json().catch(() => ({}));
|
|
4993
|
-
throw new Error(
|
|
5138
|
+
throw new Error(
|
|
5139
|
+
data.error || "Failed to claim task run"
|
|
5140
|
+
);
|
|
4994
5141
|
}
|
|
4995
5142
|
const payload = await claimResponse.json();
|
|
4996
5143
|
if (payload.run) {
|
|
@@ -5028,7 +5175,9 @@ function registerRunnerCommands(program2) {
|
|
|
5028
5175
|
);
|
|
5029
5176
|
if (!terminalClaimResponse.ok) {
|
|
5030
5177
|
const data = await terminalClaimResponse.json().catch(() => ({}));
|
|
5031
|
-
throw new Error(
|
|
5178
|
+
throw new Error(
|
|
5179
|
+
data.error || "Failed to claim terminal session"
|
|
5180
|
+
);
|
|
5032
5181
|
}
|
|
5033
5182
|
const terminalPayload = await terminalClaimResponse.json();
|
|
5034
5183
|
if (terminalPayload.session) {
|
|
@@ -6423,6 +6572,34 @@ Snapshot Contents:`));
|
|
|
6423
6572
|
return;
|
|
6424
6573
|
}
|
|
6425
6574
|
const update = parseVemUpdateBlock(input);
|
|
6575
|
+
const sandboxRunId = process.env.VEM_TASK_RUN_ID;
|
|
6576
|
+
const sandboxApiKey = process.env.VEM_API_KEY;
|
|
6577
|
+
const sandboxApiUrl = "https://api.vem.dev";
|
|
6578
|
+
if (sandboxRunId && sandboxApiKey) {
|
|
6579
|
+
const res = await fetch(
|
|
6580
|
+
`${sandboxApiUrl}/task-runs/${sandboxRunId}/vem-update-structured`,
|
|
6581
|
+
{
|
|
6582
|
+
method: "POST",
|
|
6583
|
+
headers: {
|
|
6584
|
+
Authorization: `Bearer ${sandboxApiKey}`,
|
|
6585
|
+
"Content-Type": "application/json"
|
|
6586
|
+
},
|
|
6587
|
+
body: JSON.stringify({ update })
|
|
6588
|
+
}
|
|
6589
|
+
);
|
|
6590
|
+
if (!res.ok) {
|
|
6591
|
+
const errText = await res.text().catch(() => "");
|
|
6592
|
+
console.error(
|
|
6593
|
+
chalk17.red("[vem finalize] API submission failed:"),
|
|
6594
|
+
res.status,
|
|
6595
|
+
errText
|
|
6596
|
+
);
|
|
6597
|
+
process.exitCode = 1;
|
|
6598
|
+
} else {
|
|
6599
|
+
console.log(chalk17.green("\n\u2714 vem update submitted to API\n"));
|
|
6600
|
+
}
|
|
6601
|
+
return;
|
|
6602
|
+
}
|
|
6426
6603
|
const result = await applyVemUpdate(update);
|
|
6427
6604
|
console.log(chalk17.green("\n\u2714 vem update applied\n"));
|
|
6428
6605
|
if (result.updatedTasks.length > 0) {
|
|
@@ -6467,14 +6644,16 @@ Snapshot Contents:`));
|
|
|
6467
6644
|
console.log(chalk17.gray("Context updated."));
|
|
6468
6645
|
}
|
|
6469
6646
|
const configService = new ConfigService();
|
|
6470
|
-
await syncParsedTaskUpdatesToRemote(
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6647
|
+
await syncParsedTaskUpdatesToRemote(
|
|
6648
|
+
configService,
|
|
6649
|
+
update,
|
|
6650
|
+
result
|
|
6651
|
+
).catch((err) => {
|
|
6652
|
+
console.error(
|
|
6653
|
+
chalk17.yellow("[vem finalize] syncParsed failed:"),
|
|
6654
|
+
err instanceof Error ? err.message : String(err)
|
|
6655
|
+
);
|
|
6656
|
+
});
|
|
6478
6657
|
const synced = await syncProjectMemoryToRemote().catch(() => false);
|
|
6479
6658
|
if (synced) {
|
|
6480
6659
|
console.log(chalk17.gray("\u2714 Synced to cloud."));
|
|
@@ -7084,7 +7263,14 @@ function registerTaskCommands(program2) {
|
|
|
7084
7263
|
const tasks = await getDisplayTasks({ includeDeleted: true });
|
|
7085
7264
|
const status = typeof options.status === "string" ? options.status : void 0;
|
|
7086
7265
|
const cycleFilter = typeof options.cycle === "string" ? options.cycle.trim() : void 0;
|
|
7087
|
-
const validStatuses = /* @__PURE__ */ new Set([
|
|
7266
|
+
const validStatuses = /* @__PURE__ */ new Set([
|
|
7267
|
+
"todo",
|
|
7268
|
+
"ready",
|
|
7269
|
+
"in-review",
|
|
7270
|
+
"in-progress",
|
|
7271
|
+
"blocked",
|
|
7272
|
+
"done"
|
|
7273
|
+
]);
|
|
7088
7274
|
if (status && !validStatuses.has(status)) {
|
|
7089
7275
|
console.error(
|
|
7090
7276
|
chalk18.red(
|
|
@@ -7450,7 +7636,10 @@ ${currentSummary}
|
|
|
7450
7636
|
).option("-d, --description <description>", "Task description").option("--tags <tags>", "Comma-separated tags").option("--type <type>", "Task type (feature, bug, chore, spike, enabler)").option("--estimate-hours <hours>", "Estimated hours (e.g. 2.5)").option("--depends-on <ids>", "Comma-separated task IDs").option("--blocked-by <ids>", "Comma-separated task IDs").option("--recurrence <rule>", "Recurrence rule (weekly, monthly, cron)").option("--owner <id>", "Owner ID").option("--reviewer <id>", "Reviewer ID").option("--parent <id>", "Parent task ID").option("--order <number>", "Subtask order").option("--due-at <iso>", "Due date ISO string (YYYY-MM-DD)").option(
|
|
7451
7637
|
"--validation <steps>",
|
|
7452
7638
|
'Comma-separated validation steps (e.g. "pnpm build, pnpm test")'
|
|
7453
|
-
).option("--cycle <id>", "Assign to a cycle (e.g. CYCLE-001)").option(
|
|
7639
|
+
).option("--cycle <id>", "Assign to a cycle (e.g. CYCLE-001)").option(
|
|
7640
|
+
"--impact-score <score>",
|
|
7641
|
+
"Impact score 0-100 (RICE-based priority)"
|
|
7642
|
+
).option("--actor <name>", "Actor name for task creation").option("-r, --reasoning <reasoning>", "Reasoning for creation").action(async (title, options) => {
|
|
7454
7643
|
await trackCommandUsage("task add");
|
|
7455
7644
|
try {
|
|
7456
7645
|
let taskTitle = typeof title === "string" && title.trim().length > 0 ? title.trim() : void 0;
|
|
@@ -7797,7 +7986,9 @@ ${currentSummary}
|
|
|
7797
7986
|
const dueAt = parseDueAtIso(dueAtInput);
|
|
7798
7987
|
const normalizedType = typeInput?.trim().toLowerCase();
|
|
7799
7988
|
if (normalizedType && normalizedType !== "feature" && normalizedType !== "bug" && normalizedType !== "chore" && normalizedType !== "spike" && normalizedType !== "enabler") {
|
|
7800
|
-
throw new Error(
|
|
7989
|
+
throw new Error(
|
|
7990
|
+
"type must be feature, bug, chore, spike, or enabler."
|
|
7991
|
+
);
|
|
7801
7992
|
}
|
|
7802
7993
|
const taskType = normalizedType === "feature" || normalizedType === "bug" || normalizedType === "chore" || normalizedType === "spike" || normalizedType === "enabler" ? normalizedType : void 0;
|
|
7803
7994
|
let validationSteps = parseCommaList(validationInput);
|
|
@@ -7924,7 +8115,10 @@ ${currentSummary}
|
|
|
7924
8115
|
taskCmd.command("update <id>").description("Update task metadata").option("--tags <tags>", "Comma-separated tags").option("--type <type>", "Task type (feature, bug, chore, spike, enabler)").option("--estimate-hours <hours>", "Estimated hours (e.g. 2.5)").option("--depends-on <ids>", "Comma-separated task IDs").option("--blocked-by <ids>", "Comma-separated task IDs").option("--recurrence <rule>", "Recurrence rule (weekly, monthly, cron)").option("--owner <id>", "Owner ID").option("--reviewer <id>", "Reviewer ID").option("--parent <id>", "Parent task ID").option("--order <number>", "Subtask order").option("--due-at <iso>", "Due date ISO string (YYYY-MM-DD)").option(
|
|
7925
8116
|
"--validation <steps>",
|
|
7926
8117
|
"Set validation steps (comma-separated). Use empty string to clear."
|
|
7927
|
-
).option("--cycle <id>", "Assign to a cycle (e.g. CYCLE-001)").option(
|
|
8118
|
+
).option("--cycle <id>", "Assign to a cycle (e.g. CYCLE-001)").option(
|
|
8119
|
+
"--impact-score <score>",
|
|
8120
|
+
"Impact score 0-100 (RICE-based priority)"
|
|
8121
|
+
).option("--actor <name>", "Actor name for task update").option("-r, --reasoning <reasoning>", "Reasoning for update").action(async (id, options) => {
|
|
7928
8122
|
try {
|
|
7929
8123
|
const estimate = options.estimateHours !== void 0 ? Number.parseFloat(options.estimateHours) : void 0;
|
|
7930
8124
|
if (estimate !== void 0 && Number.isNaN(estimate)) {
|
|
@@ -8565,8 +8759,12 @@ No agent sessions attached to ${id} yet.`)
|
|
|
8565
8759
|
console.log(chalk18.bold(`
|
|
8566
8760
|
\u23F1 Flow Metrics: ${id} \u2014 ${task.title}
|
|
8567
8761
|
`));
|
|
8568
|
-
console.log(
|
|
8569
|
-
|
|
8762
|
+
console.log(
|
|
8763
|
+
` ${chalk18.gray("Lead time (created \u2192 done):")} ${fmtMs(metrics.lead_time_ms)}`
|
|
8764
|
+
);
|
|
8765
|
+
console.log(
|
|
8766
|
+
` ${chalk18.gray("Cycle time (started \u2192 done):")} ${fmtMs(metrics.cycle_time_ms)}`
|
|
8767
|
+
);
|
|
8570
8768
|
if (Object.keys(metrics.time_in_status).length > 0) {
|
|
8571
8769
|
console.log(chalk18.gray("\n Time in each status:"));
|
|
8572
8770
|
for (const [status, ms] of Object.entries(metrics.time_in_status)) {
|
|
@@ -8584,15 +8782,27 @@ No agent sessions attached to ${id} yet.`)
|
|
|
8584
8782
|
return chalk18.white(`${hrs}h`);
|
|
8585
8783
|
};
|
|
8586
8784
|
console.log(chalk18.bold("\n\u{1F4CA} Project Flow Summary\n"));
|
|
8587
|
-
console.log(
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
console.log(
|
|
8591
|
-
|
|
8785
|
+
console.log(
|
|
8786
|
+
` ${chalk18.gray("WIP (active tasks):")} ${chalk18.yellow(String(summary.wip_count))}`
|
|
8787
|
+
);
|
|
8788
|
+
console.log(
|
|
8789
|
+
` ${chalk18.gray("Throughput (last 7d):")} ${chalk18.white(String(summary.throughput_last_7d))} tasks`
|
|
8790
|
+
);
|
|
8791
|
+
console.log(
|
|
8792
|
+
` ${chalk18.gray("Throughput (last 30d):")} ${chalk18.white(String(summary.throughput_last_30d))} tasks`
|
|
8793
|
+
);
|
|
8794
|
+
console.log(
|
|
8795
|
+
` ${chalk18.gray("Avg cycle time:")} ${fmtMs(summary.avg_cycle_time_ms)}`
|
|
8796
|
+
);
|
|
8797
|
+
console.log(
|
|
8798
|
+
` ${chalk18.gray("Avg lead time:")} ${fmtMs(summary.avg_lead_time_ms)}`
|
|
8799
|
+
);
|
|
8592
8800
|
console.log();
|
|
8593
8801
|
}
|
|
8594
8802
|
} catch (error) {
|
|
8595
|
-
console.error(
|
|
8803
|
+
console.error(
|
|
8804
|
+
chalk18.red(`Failed to get flow metrics: ${error.message}`)
|
|
8805
|
+
);
|
|
8596
8806
|
}
|
|
8597
8807
|
});
|
|
8598
8808
|
taskCmd.command("score [id]").description("Show or set the impact score (0-100) for a task").option("--set <score>", "Set impact score manually (0-100)").option("-r, --reasoning <reasoning>", "Reasoning for score change").action(async (id, options) => {
|
|
@@ -8604,7 +8814,9 @@ No agent sessions attached to ${id} yet.`)
|
|
|
8604
8814
|
(t) => t.impact_score === void 0 && t.status !== "done" && !t.deleted_at
|
|
8605
8815
|
);
|
|
8606
8816
|
if (unscored.length === 0) {
|
|
8607
|
-
console.log(
|
|
8817
|
+
console.log(
|
|
8818
|
+
chalk18.green("\n\u2714 All active tasks have impact scores.\n")
|
|
8819
|
+
);
|
|
8608
8820
|
return;
|
|
8609
8821
|
}
|
|
8610
8822
|
const table = new Table4({
|
|
@@ -8622,9 +8834,13 @@ No agent sessions attached to ${id} yet.`)
|
|
|
8622
8834
|
}
|
|
8623
8835
|
console.log(chalk18.bold("\n\u{1F3AF} Impact Scores\n"));
|
|
8624
8836
|
console.log(table.toString());
|
|
8625
|
-
console.log(
|
|
8837
|
+
console.log(
|
|
8838
|
+
chalk18.gray(
|
|
8839
|
+
`
|
|
8626
8840
|
Unscored: ${unscored.length} task(s). Use: vem task score <id> --set <0-100>
|
|
8627
|
-
`
|
|
8841
|
+
`
|
|
8842
|
+
)
|
|
8843
|
+
);
|
|
8628
8844
|
return;
|
|
8629
8845
|
}
|
|
8630
8846
|
const task = await taskService.getTask(id);
|
|
@@ -8635,7 +8851,9 @@ No agent sessions attached to ${id} yet.`)
|
|
|
8635
8851
|
if (options.set !== void 0) {
|
|
8636
8852
|
const score = Number.parseFloat(options.set);
|
|
8637
8853
|
if (Number.isNaN(score) || score < 0 || score > 100) {
|
|
8638
|
-
console.error(
|
|
8854
|
+
console.error(
|
|
8855
|
+
chalk18.red("Score must be a number between 0 and 100.")
|
|
8856
|
+
);
|
|
8639
8857
|
process.exitCode = 1;
|
|
8640
8858
|
return;
|
|
8641
8859
|
}
|
|
@@ -8643,15 +8861,21 @@ No agent sessions attached to ${id} yet.`)
|
|
|
8643
8861
|
impact_score: score,
|
|
8644
8862
|
reasoning: options.reasoning
|
|
8645
8863
|
});
|
|
8646
|
-
console.log(
|
|
8864
|
+
console.log(
|
|
8865
|
+
chalk18.green(`
|
|
8647
8866
|
\u2714 Impact score for ${id} set to ${score}
|
|
8648
|
-
`)
|
|
8867
|
+
`)
|
|
8868
|
+
);
|
|
8649
8869
|
} else {
|
|
8650
8870
|
console.log(chalk18.bold(`
|
|
8651
8871
|
\u{1F3AF} ${id}: ${task.title}`));
|
|
8652
|
-
console.log(
|
|
8653
|
-
|
|
8654
|
-
|
|
8872
|
+
console.log(
|
|
8873
|
+
` Impact score: ${task.impact_score !== void 0 ? chalk18.yellow(String(Math.round(task.impact_score))) : chalk18.gray("not set")}`
|
|
8874
|
+
);
|
|
8875
|
+
console.log(
|
|
8876
|
+
chalk18.gray(` Set with: vem task score ${id} --set <0-100>
|
|
8877
|
+
`)
|
|
8878
|
+
);
|
|
8655
8879
|
}
|
|
8656
8880
|
} catch (error) {
|
|
8657
8881
|
console.error(chalk18.red(`Failed to manage score: ${error.message}`));
|
|
@@ -8744,11 +8968,11 @@ async function initServerMonitoring(config) {
|
|
|
8744
8968
|
await initServerMonitoring({
|
|
8745
8969
|
dsn: "https://ed007f2c213d0aa07c1be256ca51750c@o4510863861612544.ingest.de.sentry.io/4510863921774672",
|
|
8746
8970
|
environment: process.env.NODE_ENV || "production",
|
|
8747
|
-
release: "0.1.
|
|
8971
|
+
release: "0.1.55",
|
|
8748
8972
|
serviceName: "cli"
|
|
8749
8973
|
});
|
|
8750
8974
|
var program = new Command();
|
|
8751
|
-
program.name("vem").description("vem Project Memory CLI").version("0.1.
|
|
8975
|
+
program.name("vem").description("vem Project Memory CLI").version("0.1.55").addHelpText(
|
|
8752
8976
|
"after",
|
|
8753
8977
|
`
|
|
8754
8978
|
${chalk19.bold("\n\u26A1 Power Workflows:")}
|