@vemdev/cli 0.1.52 → 0.1.54
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/index.js +1317 -722
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -32,7 +32,7 @@ import "./chunk-VL6CJCOB.js";
|
|
|
32
32
|
import "./chunk-PZ5AY32C.js";
|
|
33
33
|
|
|
34
34
|
// src/index.ts
|
|
35
|
-
import
|
|
35
|
+
import chalk19 from "chalk";
|
|
36
36
|
import { Command } from "commander";
|
|
37
37
|
|
|
38
38
|
// src/commands/agent.ts
|
|
@@ -349,7 +349,7 @@ async function hasNonVemChanges() {
|
|
|
349
349
|
const root = await getRepoRoot();
|
|
350
350
|
const status = execSync("git status --porcelain", { cwd: root }).toString().trim();
|
|
351
351
|
if (!status) return false;
|
|
352
|
-
return status.split("\n").map((line) => normalizeStatusPath(line)).some((
|
|
352
|
+
return status.split("\n").map((line) => normalizeStatusPath(line)).some((path4) => path4.length > 0 && !path4.startsWith(".vem/"));
|
|
353
353
|
} catch (_e) {
|
|
354
354
|
return false;
|
|
355
355
|
}
|
|
@@ -480,13 +480,13 @@ async function detectVemUpdateInOutput(vemDir) {
|
|
|
480
480
|
}
|
|
481
481
|
}
|
|
482
482
|
async function readStdin() {
|
|
483
|
-
return new Promise((
|
|
483
|
+
return new Promise((resolve3, reject) => {
|
|
484
484
|
let data = "";
|
|
485
485
|
process.stdin.setEncoding("utf-8");
|
|
486
486
|
process.stdin.on("data", (chunk) => {
|
|
487
487
|
data += chunk;
|
|
488
488
|
});
|
|
489
|
-
process.stdin.on("end", () =>
|
|
489
|
+
process.stdin.on("end", () => resolve3(data));
|
|
490
490
|
process.stdin.on("error", reject);
|
|
491
491
|
});
|
|
492
492
|
}
|
|
@@ -891,14 +891,21 @@ 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,
|
|
898
904
|
"in-progress": 1,
|
|
899
905
|
todo: 2,
|
|
900
|
-
|
|
901
|
-
|
|
906
|
+
ready: 3,
|
|
907
|
+
blocked: 4,
|
|
908
|
+
done: 5
|
|
902
909
|
};
|
|
903
910
|
var debugAgentSync = (...messages) => {
|
|
904
911
|
if (process.env.VEM_DEBUG !== "1") return;
|
|
@@ -1101,24 +1108,33 @@ var updateTaskMetaRemote = async (configService, task, patch) => {
|
|
|
1101
1108
|
payload.actor = patch.actor.trim().length > 0 ? patch.actor.trim() : void 0;
|
|
1102
1109
|
}
|
|
1103
1110
|
if (patch.title !== void 0) payload.title = patch.title;
|
|
1104
|
-
if (patch.description !== void 0)
|
|
1111
|
+
if (patch.description !== void 0)
|
|
1112
|
+
payload.description = patch.description;
|
|
1105
1113
|
if (patch.priority !== void 0) payload.priority = patch.priority;
|
|
1106
1114
|
if (patch.tags !== void 0) payload.tags = patch.tags;
|
|
1107
1115
|
if (patch.type !== void 0) payload.type = patch.type;
|
|
1108
|
-
if (patch.estimate_hours !== void 0)
|
|
1116
|
+
if (patch.estimate_hours !== void 0)
|
|
1117
|
+
payload.estimate_hours = patch.estimate_hours;
|
|
1109
1118
|
if (patch.depends_on !== void 0) payload.depends_on = patch.depends_on;
|
|
1110
1119
|
if (patch.blocked_by !== void 0) payload.blocked_by = patch.blocked_by;
|
|
1111
|
-
if (patch.recurrence_rule !== void 0)
|
|
1120
|
+
if (patch.recurrence_rule !== void 0)
|
|
1121
|
+
payload.recurrence_rule = patch.recurrence_rule;
|
|
1112
1122
|
if (patch.owner_id !== void 0) payload.owner_id = patch.owner_id;
|
|
1113
|
-
if (patch.reviewer_id !== void 0)
|
|
1114
|
-
|
|
1123
|
+
if (patch.reviewer_id !== void 0)
|
|
1124
|
+
payload.reviewer_id = patch.reviewer_id;
|
|
1125
|
+
if (patch.validation_steps !== void 0)
|
|
1126
|
+
payload.validation_steps = patch.validation_steps;
|
|
1115
1127
|
if (patch.user_notes !== void 0) payload.user_notes = patch.user_notes;
|
|
1116
|
-
if (patch.github_issue_number !== void 0)
|
|
1128
|
+
if (patch.github_issue_number !== void 0)
|
|
1129
|
+
payload.github_issue_number = patch.github_issue_number;
|
|
1117
1130
|
if (patch.parent_id !== void 0) payload.parent_id = patch.parent_id;
|
|
1118
|
-
if (patch.subtask_order !== void 0)
|
|
1131
|
+
if (patch.subtask_order !== void 0)
|
|
1132
|
+
payload.subtask_order = patch.subtask_order;
|
|
1119
1133
|
if (patch.due_at !== void 0) payload.due_at = patch.due_at;
|
|
1120
|
-
if (patch.raw_vem_update !== void 0)
|
|
1121
|
-
|
|
1134
|
+
if (patch.raw_vem_update !== void 0)
|
|
1135
|
+
payload.raw_vem_update = patch.raw_vem_update;
|
|
1136
|
+
if (patch.cli_version !== void 0)
|
|
1137
|
+
payload.cli_version = patch.cli_version;
|
|
1122
1138
|
const response = await fetch(
|
|
1123
1139
|
`${API_URL}/tasks/${encodeURIComponent(dbId)}/meta`,
|
|
1124
1140
|
{
|
|
@@ -1236,7 +1252,7 @@ var syncParsedTaskUpdatesToRemote = async (configService, update, result) => {
|
|
|
1236
1252
|
...patch.subtask_order !== void 0 ? { subtask_order: patch.subtask_order } : {},
|
|
1237
1253
|
...patch.due_at !== void 0 ? { due_at: patch.due_at } : {},
|
|
1238
1254
|
raw_vem_update: JSON.parse(JSON.stringify(update)),
|
|
1239
|
-
cli_version: "0.1.
|
|
1255
|
+
cli_version: "0.1.54"
|
|
1240
1256
|
});
|
|
1241
1257
|
const taskContextPatch = buildRemoteTaskContextPatch(patch, updatedTask);
|
|
1242
1258
|
if (taskContextPatch) {
|
|
@@ -1842,7 +1858,11 @@ Your task is ${activeTask?.id}: ${activeTask?.title}${childScopeText}.
|
|
|
1842
1858
|
|
|
1843
1859
|
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.`;
|
|
1844
1860
|
if (options.autoExit) {
|
|
1845
|
-
console.log(
|
|
1861
|
+
console.log(
|
|
1862
|
+
chalk7.cyan(
|
|
1863
|
+
"Auto-injecting context via -p flag (autonomous mode)..."
|
|
1864
|
+
)
|
|
1865
|
+
);
|
|
1846
1866
|
launchArgs = [...launchArgs, "-p", autonomousPrompt, "--yolo"];
|
|
1847
1867
|
} else {
|
|
1848
1868
|
console.log(chalk7.cyan("Auto-injecting context via -i flag..."));
|
|
@@ -1881,7 +1901,7 @@ Start by reading .vem/task_context.md and .vem/current_context.md for task and p
|
|
|
1881
1901
|
});
|
|
1882
1902
|
let startError = null;
|
|
1883
1903
|
let exitCode = null;
|
|
1884
|
-
await new Promise((
|
|
1904
|
+
await new Promise((resolve3) => {
|
|
1885
1905
|
child.on("exit", (code, signal) => {
|
|
1886
1906
|
exitCode = code;
|
|
1887
1907
|
if (code === null && signal) {
|
|
@@ -1893,11 +1913,11 @@ Start by reading .vem/task_context.md and .vem/current_context.md for task and p
|
|
|
1893
1913
|
process.kill(-child.pid, "SIGTERM");
|
|
1894
1914
|
} catch {
|
|
1895
1915
|
}
|
|
1896
|
-
|
|
1916
|
+
resolve3();
|
|
1897
1917
|
});
|
|
1898
1918
|
child.on("error", (err) => {
|
|
1899
1919
|
startError = err;
|
|
1900
|
-
|
|
1920
|
+
resolve3();
|
|
1901
1921
|
});
|
|
1902
1922
|
});
|
|
1903
1923
|
const capturedError = startError;
|
|
@@ -1922,17 +1942,17 @@ Start by reading .vem/task_context.md and .vem/current_context.md for task and p
|
|
|
1922
1942
|
VEM_AGENT_NAME: agentName
|
|
1923
1943
|
}
|
|
1924
1944
|
});
|
|
1925
|
-
const shellResult = await new Promise((
|
|
1945
|
+
const shellResult = await new Promise((resolve3) => {
|
|
1926
1946
|
shellChild.on("exit", (code) => {
|
|
1927
1947
|
try {
|
|
1928
1948
|
process.kill(-shellChild.pid, "SIGTERM");
|
|
1929
1949
|
} catch {
|
|
1930
1950
|
}
|
|
1931
|
-
|
|
1951
|
+
resolve3({ exitCode: code, error: null });
|
|
1932
1952
|
});
|
|
1933
1953
|
shellChild.on(
|
|
1934
1954
|
"error",
|
|
1935
|
-
(err) =>
|
|
1955
|
+
(err) => resolve3({ exitCode: null, error: err })
|
|
1936
1956
|
);
|
|
1937
1957
|
});
|
|
1938
1958
|
if (shellResult.error) {
|
|
@@ -2509,7 +2529,11 @@ function registerCycleCommands(program2) {
|
|
|
2509
2529
|
try {
|
|
2510
2530
|
const cycles = await cycleService.getCycles();
|
|
2511
2531
|
if (cycles.length === 0) {
|
|
2512
|
-
console.log(
|
|
2532
|
+
console.log(
|
|
2533
|
+
chalk9.gray(
|
|
2534
|
+
"\n No cycles yet. Create one with: vem cycle create\n"
|
|
2535
|
+
)
|
|
2536
|
+
);
|
|
2513
2537
|
return;
|
|
2514
2538
|
}
|
|
2515
2539
|
const table = new Table({
|
|
@@ -2633,7 +2657,9 @@ function registerCycleCommands(program2) {
|
|
|
2633
2657
|
process.exitCode = 1;
|
|
2634
2658
|
return;
|
|
2635
2659
|
}
|
|
2636
|
-
const updated = await cycleService.updateCycle(id, {
|
|
2660
|
+
const updated = await cycleService.updateCycle(id, {
|
|
2661
|
+
status: "active"
|
|
2662
|
+
});
|
|
2637
2663
|
console.log(chalk9.cyan(`
|
|
2638
2664
|
\u2714 Cycle ${id} is now active
|
|
2639
2665
|
`));
|
|
@@ -2661,7 +2687,9 @@ function registerCycleCommands(program2) {
|
|
|
2661
2687
|
`));
|
|
2662
2688
|
return;
|
|
2663
2689
|
}
|
|
2664
|
-
const updated = await cycleService.updateCycle(id, {
|
|
2690
|
+
const updated = await cycleService.updateCycle(id, {
|
|
2691
|
+
status: "closed"
|
|
2692
|
+
});
|
|
2665
2693
|
console.log(chalk9.green(`
|
|
2666
2694
|
\u2714 Cycle ${id} closed
|
|
2667
2695
|
`));
|
|
@@ -2677,8 +2705,10 @@ function registerCycleCommands(program2) {
|
|
|
2677
2705
|
);
|
|
2678
2706
|
}
|
|
2679
2707
|
console.log(
|
|
2680
|
-
chalk9.gray(
|
|
2681
|
-
`)
|
|
2708
|
+
chalk9.gray(
|
|
2709
|
+
` Closed: ${new Date(updated.closed_at).toLocaleDateString()}
|
|
2710
|
+
`
|
|
2711
|
+
)
|
|
2682
2712
|
);
|
|
2683
2713
|
} catch (error) {
|
|
2684
2714
|
console.error(chalk9.red(`Failed to close cycle: ${error.message}`));
|
|
@@ -2786,37 +2816,422 @@ function registerCycleCommands(program2) {
|
|
|
2786
2816
|
]);
|
|
2787
2817
|
}
|
|
2788
2818
|
const done = cycleTasks.filter((t) => t.status === "done").length;
|
|
2789
|
-
console.log(
|
|
2819
|
+
console.log(
|
|
2820
|
+
`
|
|
2790
2821
|
${chalk9.white(String(done))}/${chalk9.white(String(cycleTasks.length))} tasks done
|
|
2791
|
-
`
|
|
2822
|
+
`
|
|
2823
|
+
);
|
|
2792
2824
|
console.log(table.toString());
|
|
2793
2825
|
console.log();
|
|
2794
2826
|
} catch (error) {
|
|
2795
|
-
console.error(
|
|
2827
|
+
console.error(
|
|
2828
|
+
chalk9.red(`Failed to show cycle focus: ${error.message}`)
|
|
2829
|
+
);
|
|
2830
|
+
}
|
|
2831
|
+
});
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
// src/commands/instructions.ts
|
|
2835
|
+
import * as fs2 from "fs/promises";
|
|
2836
|
+
import * as path from "path";
|
|
2837
|
+
import chalk10 from "chalk";
|
|
2838
|
+
import prompts5 from "prompts";
|
|
2839
|
+
async function getRepoRoot2() {
|
|
2840
|
+
const { execSync: execSync4 } = await import("child_process");
|
|
2841
|
+
try {
|
|
2842
|
+
return execSync4("git rev-parse --show-toplevel", {
|
|
2843
|
+
encoding: "utf-8"
|
|
2844
|
+
}).trim();
|
|
2845
|
+
} catch {
|
|
2846
|
+
return process.cwd();
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
async function readLocalInstructions() {
|
|
2850
|
+
const repoRoot = await getRepoRoot2();
|
|
2851
|
+
const result = [];
|
|
2852
|
+
for (const relativePath of KNOWN_AGENT_INSTRUCTION_FILES) {
|
|
2853
|
+
const absPath = path.join(repoRoot, relativePath);
|
|
2854
|
+
try {
|
|
2855
|
+
const content = await fs2.readFile(absPath, "utf-8");
|
|
2856
|
+
result.push({ path: relativePath, content });
|
|
2857
|
+
} catch {
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
return result;
|
|
2861
|
+
}
|
|
2862
|
+
function registerInstructionCommands(program2) {
|
|
2863
|
+
const instructionsCmd = program2.command("instructions").alias("instr").description("Manage and sync agent instruction files");
|
|
2864
|
+
instructionsCmd.command("pull").description(
|
|
2865
|
+
"Pull the latest instructions from the cloud and write them to local files"
|
|
2866
|
+
).option("-f, --force", "Overwrite local files without prompt").action(async (options) => {
|
|
2867
|
+
await trackCommandUsage("instructions.pull");
|
|
2868
|
+
try {
|
|
2869
|
+
const configService = new ConfigService();
|
|
2870
|
+
const key = await ensureAuthenticated(configService);
|
|
2871
|
+
const projectId = await configService.getProjectId();
|
|
2872
|
+
if (!projectId) {
|
|
2873
|
+
console.error(
|
|
2874
|
+
chalk10.red(
|
|
2875
|
+
"Error: No project linked. Run `vem link <projectId>` first."
|
|
2876
|
+
)
|
|
2877
|
+
);
|
|
2878
|
+
process.exitCode = 1;
|
|
2879
|
+
return;
|
|
2880
|
+
}
|
|
2881
|
+
console.log(chalk10.blue("\u2B07 Fetching instructions from cloud..."));
|
|
2882
|
+
const res = await fetch(
|
|
2883
|
+
`${API_URL}/projects/${projectId}/instructions`,
|
|
2884
|
+
{
|
|
2885
|
+
headers: {
|
|
2886
|
+
Authorization: `Bearer ${key}`,
|
|
2887
|
+
...await buildDeviceHeaders(configService)
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
);
|
|
2891
|
+
if (!res.ok) {
|
|
2892
|
+
const data2 = await res.json().catch(() => ({}));
|
|
2893
|
+
throw new Error(
|
|
2894
|
+
`API Error ${res.status}: ${data2.error || res.statusText}`
|
|
2895
|
+
);
|
|
2896
|
+
}
|
|
2897
|
+
const data = await res.json();
|
|
2898
|
+
const instructions = data.instructions ?? [];
|
|
2899
|
+
if (instructions.length === 0) {
|
|
2900
|
+
console.log(
|
|
2901
|
+
chalk10.yellow("No instructions configured for this project.")
|
|
2902
|
+
);
|
|
2903
|
+
return;
|
|
2904
|
+
}
|
|
2905
|
+
const repoRoot = await getRepoRoot2();
|
|
2906
|
+
let written = 0;
|
|
2907
|
+
let skipped = 0;
|
|
2908
|
+
for (const entry of instructions) {
|
|
2909
|
+
if (typeof entry.path !== "string" || typeof entry.content !== "string")
|
|
2910
|
+
continue;
|
|
2911
|
+
if (!entry.content.trim()) continue;
|
|
2912
|
+
const dest = path.resolve(repoRoot, entry.path);
|
|
2913
|
+
const resolvedRoot = path.resolve(repoRoot);
|
|
2914
|
+
if (!dest.startsWith(`${resolvedRoot}${path.sep}`) && dest !== resolvedRoot) {
|
|
2915
|
+
console.warn(chalk10.yellow(`Skipping unsafe path: ${entry.path}`));
|
|
2916
|
+
continue;
|
|
2917
|
+
}
|
|
2918
|
+
if (!options.force) {
|
|
2919
|
+
const fileExists = await fs2.access(dest).then(() => true).catch(() => false);
|
|
2920
|
+
if (fileExists) {
|
|
2921
|
+
const { overwrite } = await prompts5({
|
|
2922
|
+
type: "confirm",
|
|
2923
|
+
name: "overwrite",
|
|
2924
|
+
message: `File ${entry.path} (${dest}) already exists. Overwrite?`,
|
|
2925
|
+
initial: false
|
|
2926
|
+
});
|
|
2927
|
+
if (!overwrite) {
|
|
2928
|
+
console.log(chalk10.yellow(` \u2298 Skipped ${entry.path}`));
|
|
2929
|
+
skipped++;
|
|
2930
|
+
continue;
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
await fs2.mkdir(path.dirname(dest), { recursive: true });
|
|
2935
|
+
await fs2.writeFile(dest, entry.content, "utf-8");
|
|
2936
|
+
console.log(chalk10.green(` \u2714 ${entry.path}`));
|
|
2937
|
+
written++;
|
|
2938
|
+
}
|
|
2939
|
+
const skippedMsg = skipped > 0 ? `, ${skipped} skipped` : "";
|
|
2940
|
+
console.log(
|
|
2941
|
+
chalk10.green(
|
|
2942
|
+
`
|
|
2943
|
+
\u2714 Pulled ${written} instruction file(s)${skippedMsg}.
|
|
2944
|
+
`
|
|
2945
|
+
)
|
|
2946
|
+
);
|
|
2947
|
+
} catch (error) {
|
|
2948
|
+
console.error(
|
|
2949
|
+
chalk10.red("\n\u2716 Instructions pull failed:"),
|
|
2950
|
+
error instanceof Error ? error.message : String(error)
|
|
2951
|
+
);
|
|
2952
|
+
process.exitCode = 1;
|
|
2953
|
+
}
|
|
2954
|
+
});
|
|
2955
|
+
instructionsCmd.command("push").description("Push local instruction files to the cloud").option("-m, --message <msg>", "Commit message for this version").action(async (options) => {
|
|
2956
|
+
await trackCommandUsage("instructions.push");
|
|
2957
|
+
try {
|
|
2958
|
+
const configService = new ConfigService();
|
|
2959
|
+
const key = await ensureAuthenticated(configService);
|
|
2960
|
+
const projectId = await configService.getProjectId();
|
|
2961
|
+
if (!projectId) {
|
|
2962
|
+
console.error(
|
|
2963
|
+
chalk10.red(
|
|
2964
|
+
"Error: No project linked. Run `vem link <projectId>` first."
|
|
2965
|
+
)
|
|
2966
|
+
);
|
|
2967
|
+
process.exitCode = 1;
|
|
2968
|
+
return;
|
|
2969
|
+
}
|
|
2970
|
+
const localInstructions = await readLocalInstructions();
|
|
2971
|
+
if (localInstructions.length === 0) {
|
|
2972
|
+
console.log(
|
|
2973
|
+
chalk10.yellow("No instruction files found locally. Looked for:")
|
|
2974
|
+
);
|
|
2975
|
+
for (const f of KNOWN_AGENT_INSTRUCTION_FILES) {
|
|
2976
|
+
console.log(chalk10.gray(` ${f}`));
|
|
2977
|
+
}
|
|
2978
|
+
return;
|
|
2979
|
+
}
|
|
2980
|
+
console.log(
|
|
2981
|
+
chalk10.blue(
|
|
2982
|
+
`\u2B06 Pushing ${localInstructions.length} instruction file(s)...`
|
|
2983
|
+
)
|
|
2984
|
+
);
|
|
2985
|
+
const res = await fetch(
|
|
2986
|
+
`${API_URL}/projects/${projectId}/instructions`,
|
|
2987
|
+
{
|
|
2988
|
+
method: "PUT",
|
|
2989
|
+
headers: {
|
|
2990
|
+
"Content-Type": "application/json",
|
|
2991
|
+
Authorization: `Bearer ${key}`,
|
|
2992
|
+
...await buildDeviceHeaders(configService)
|
|
2993
|
+
},
|
|
2994
|
+
body: JSON.stringify({
|
|
2995
|
+
instructions: localInstructions,
|
|
2996
|
+
commit_message: options.message
|
|
2997
|
+
})
|
|
2998
|
+
}
|
|
2999
|
+
);
|
|
3000
|
+
if (!res.ok) {
|
|
3001
|
+
const data2 = await res.json().catch(() => ({}));
|
|
3002
|
+
throw new Error(
|
|
3003
|
+
`API Error ${res.status}: ${data2.error || res.statusText}`
|
|
3004
|
+
);
|
|
3005
|
+
}
|
|
3006
|
+
const data = await res.json();
|
|
3007
|
+
for (const entry of localInstructions) {
|
|
3008
|
+
console.log(chalk10.green(` \u2714 ${entry.path}`));
|
|
3009
|
+
}
|
|
3010
|
+
const versionNote = data.version_number ? ` (saved as v${data.version_number})` : "";
|
|
3011
|
+
console.log(chalk10.green(`
|
|
3012
|
+
\u2714 Instructions pushed${versionNote}.
|
|
3013
|
+
`));
|
|
3014
|
+
} catch (error) {
|
|
3015
|
+
console.error(
|
|
3016
|
+
chalk10.red("\n\u2716 Instructions push failed:"),
|
|
3017
|
+
error instanceof Error ? error.message : String(error)
|
|
3018
|
+
);
|
|
3019
|
+
process.exitCode = 1;
|
|
3020
|
+
}
|
|
3021
|
+
});
|
|
3022
|
+
instructionsCmd.command("status").description("Check if local instruction files are in sync with the cloud").action(async () => {
|
|
3023
|
+
await trackCommandUsage("instructions.status");
|
|
3024
|
+
try {
|
|
3025
|
+
const configService = new ConfigService();
|
|
3026
|
+
const key = await ensureAuthenticated(configService);
|
|
3027
|
+
const projectId = await configService.getProjectId();
|
|
3028
|
+
if (!projectId) {
|
|
3029
|
+
console.error(
|
|
3030
|
+
chalk10.red(
|
|
3031
|
+
"Error: No project linked. Run `vem link <projectId>` first."
|
|
3032
|
+
)
|
|
3033
|
+
);
|
|
3034
|
+
process.exitCode = 1;
|
|
3035
|
+
return;
|
|
3036
|
+
}
|
|
3037
|
+
const [localInstructions, cloudRes] = await Promise.all([
|
|
3038
|
+
readLocalInstructions(),
|
|
3039
|
+
fetch(`${API_URL}/projects/${projectId}/instructions`, {
|
|
3040
|
+
headers: {
|
|
3041
|
+
Authorization: `Bearer ${key}`,
|
|
3042
|
+
...await buildDeviceHeaders(configService)
|
|
3043
|
+
}
|
|
3044
|
+
})
|
|
3045
|
+
]);
|
|
3046
|
+
if (!cloudRes.ok) {
|
|
3047
|
+
const data = await cloudRes.json().catch(() => ({}));
|
|
3048
|
+
throw new Error(
|
|
3049
|
+
`API Error ${cloudRes.status}: ${data.error || cloudRes.statusText}`
|
|
3050
|
+
);
|
|
3051
|
+
}
|
|
3052
|
+
const cloudData = await cloudRes.json();
|
|
3053
|
+
const cloudInstructions = cloudData.instructions ?? [];
|
|
3054
|
+
const localMap = new Map(
|
|
3055
|
+
localInstructions.map((e) => [e.path, e.content])
|
|
3056
|
+
);
|
|
3057
|
+
const cloudMap = new Map(
|
|
3058
|
+
cloudInstructions.map((e) => [e.path, e.content])
|
|
3059
|
+
);
|
|
3060
|
+
const allPaths = /* @__PURE__ */ new Set([...localMap.keys(), ...cloudMap.keys()]);
|
|
3061
|
+
let inSync = true;
|
|
3062
|
+
console.log(chalk10.bold("\nInstruction file sync status:\n"));
|
|
3063
|
+
for (const filePath of [...allPaths].sort()) {
|
|
3064
|
+
const local = localMap.get(filePath);
|
|
3065
|
+
const cloud = cloudMap.get(filePath);
|
|
3066
|
+
if (local === void 0) {
|
|
3067
|
+
console.log(
|
|
3068
|
+
chalk10.yellow(` \u2193 ${filePath}`) + chalk10.gray(" (cloud only \u2014 run `vem instructions pull`)")
|
|
3069
|
+
);
|
|
3070
|
+
inSync = false;
|
|
3071
|
+
} else if (cloud === void 0) {
|
|
3072
|
+
console.log(
|
|
3073
|
+
chalk10.cyan(` \u2191 ${filePath}`) + chalk10.gray(" (local only \u2014 run `vem instructions push`)")
|
|
3074
|
+
);
|
|
3075
|
+
inSync = false;
|
|
3076
|
+
} else if (local !== cloud) {
|
|
3077
|
+
console.log(
|
|
3078
|
+
chalk10.magenta(` \u2260 ${filePath}`) + chalk10.gray(" (differs \u2014 run pull or push to sync)")
|
|
3079
|
+
);
|
|
3080
|
+
inSync = false;
|
|
3081
|
+
} else {
|
|
3082
|
+
console.log(
|
|
3083
|
+
chalk10.green(` \u2714 ${filePath}`) + chalk10.gray(" (in sync)")
|
|
3084
|
+
);
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
if (allPaths.size === 0) {
|
|
3088
|
+
console.log(chalk10.gray(" No instructions configured."));
|
|
3089
|
+
}
|
|
3090
|
+
console.log(
|
|
3091
|
+
inSync ? chalk10.green("\n\u2714 All instruction files are in sync.\n") : chalk10.yellow("\n\u26A0 Some instruction files are out of sync.\n")
|
|
3092
|
+
);
|
|
3093
|
+
} catch (error) {
|
|
3094
|
+
console.error(
|
|
3095
|
+
chalk10.red("\n\u2716 Instructions status check failed:"),
|
|
3096
|
+
error instanceof Error ? error.message : String(error)
|
|
3097
|
+
);
|
|
3098
|
+
process.exitCode = 1;
|
|
3099
|
+
}
|
|
3100
|
+
});
|
|
3101
|
+
instructionsCmd.command("versions").description("List instruction version history from the cloud").option("-n, --limit <n>", "Maximum number of versions to show", "20").action(async (options) => {
|
|
3102
|
+
await trackCommandUsage("instructions.versions");
|
|
3103
|
+
try {
|
|
3104
|
+
const configService = new ConfigService();
|
|
3105
|
+
const key = await ensureAuthenticated(configService);
|
|
3106
|
+
const projectId = await configService.getProjectId();
|
|
3107
|
+
if (!projectId) {
|
|
3108
|
+
console.error(
|
|
3109
|
+
chalk10.red(
|
|
3110
|
+
"Error: No project linked. Run `vem link <projectId>` first."
|
|
3111
|
+
)
|
|
3112
|
+
);
|
|
3113
|
+
process.exitCode = 1;
|
|
3114
|
+
return;
|
|
3115
|
+
}
|
|
3116
|
+
const limit = Math.min(
|
|
3117
|
+
100,
|
|
3118
|
+
Math.max(1, Number.parseInt(options.limit ?? "20", 10) || 20)
|
|
3119
|
+
);
|
|
3120
|
+
const res = await fetch(
|
|
3121
|
+
`${API_URL}/projects/${projectId}/instructions/versions?limit=${limit}`,
|
|
3122
|
+
{
|
|
3123
|
+
headers: {
|
|
3124
|
+
Authorization: `Bearer ${key}`,
|
|
3125
|
+
...await buildDeviceHeaders(configService)
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
);
|
|
3129
|
+
if (!res.ok) {
|
|
3130
|
+
const data2 = await res.json().catch(() => ({}));
|
|
3131
|
+
throw new Error(
|
|
3132
|
+
`API Error ${res.status}: ${data2.error || res.statusText}`
|
|
3133
|
+
);
|
|
3134
|
+
}
|
|
3135
|
+
const data = await res.json();
|
|
3136
|
+
const versions = data.versions ?? [];
|
|
3137
|
+
if (versions.length === 0) {
|
|
3138
|
+
console.log(chalk10.yellow("No instruction versions found."));
|
|
3139
|
+
return;
|
|
3140
|
+
}
|
|
3141
|
+
console.log(chalk10.bold("\nInstruction Version History:\n"));
|
|
3142
|
+
for (const [index, version] of versions.entries()) {
|
|
3143
|
+
const isLatest = index === 0;
|
|
3144
|
+
const date = new Date(version.created_at).toLocaleString();
|
|
3145
|
+
const tag = isLatest ? chalk10.green(" [current]") : "";
|
|
3146
|
+
const msg = version.commit_message ? chalk10.gray(` \u2014 ${version.commit_message}`) : "";
|
|
3147
|
+
const author = version.author ? chalk10.gray(` by ${version.author}`) : "";
|
|
3148
|
+
console.log(
|
|
3149
|
+
` ${chalk10.bold(`v${version.version_number}`)}${tag}${msg}${author}`
|
|
3150
|
+
);
|
|
3151
|
+
console.log(chalk10.gray(` ${date} \xB7 id: ${version.id}`));
|
|
3152
|
+
}
|
|
3153
|
+
console.log();
|
|
3154
|
+
} catch (error) {
|
|
3155
|
+
console.error(
|
|
3156
|
+
chalk10.red("\n\u2716 Failed to fetch versions:"),
|
|
3157
|
+
error instanceof Error ? error.message : String(error)
|
|
3158
|
+
);
|
|
3159
|
+
process.exitCode = 1;
|
|
3160
|
+
}
|
|
3161
|
+
});
|
|
3162
|
+
instructionsCmd.command("revert <versionId>").description("Revert instructions to a specific version by version ID").action(async (versionId) => {
|
|
3163
|
+
await trackCommandUsage("instructions.revert");
|
|
3164
|
+
try {
|
|
3165
|
+
const configService = new ConfigService();
|
|
3166
|
+
const key = await ensureAuthenticated(configService);
|
|
3167
|
+
const projectId = await configService.getProjectId();
|
|
3168
|
+
if (!projectId) {
|
|
3169
|
+
console.error(
|
|
3170
|
+
chalk10.red(
|
|
3171
|
+
"Error: No project linked. Run `vem link <projectId>` first."
|
|
3172
|
+
)
|
|
3173
|
+
);
|
|
3174
|
+
process.exitCode = 1;
|
|
3175
|
+
return;
|
|
3176
|
+
}
|
|
3177
|
+
console.log(
|
|
3178
|
+
chalk10.blue(`\u27F2 Reverting instructions to version ${versionId}...`)
|
|
3179
|
+
);
|
|
3180
|
+
const res = await fetch(
|
|
3181
|
+
`${API_URL}/projects/${projectId}/instructions/versions/${versionId}/revert`,
|
|
3182
|
+
{
|
|
3183
|
+
method: "POST",
|
|
3184
|
+
headers: {
|
|
3185
|
+
Authorization: `Bearer ${key}`,
|
|
3186
|
+
...await buildDeviceHeaders(configService)
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
);
|
|
3190
|
+
if (!res.ok) {
|
|
3191
|
+
const data2 = await res.json().catch(() => ({}));
|
|
3192
|
+
throw new Error(
|
|
3193
|
+
`API Error ${res.status}: ${data2.error || res.statusText}`
|
|
3194
|
+
);
|
|
3195
|
+
}
|
|
3196
|
+
const data = await res.json();
|
|
3197
|
+
console.log(
|
|
3198
|
+
chalk10.green(
|
|
3199
|
+
`\u2714 Reverted to v${data.reverted_from} (new version: v${data.version_number})`
|
|
3200
|
+
)
|
|
3201
|
+
);
|
|
3202
|
+
console.log(
|
|
3203
|
+
chalk10.gray(" Run `vem instructions pull` to update local files.")
|
|
3204
|
+
);
|
|
3205
|
+
} catch (error) {
|
|
3206
|
+
console.error(
|
|
3207
|
+
chalk10.red("\n\u2716 Revert failed:"),
|
|
3208
|
+
error instanceof Error ? error.message : String(error)
|
|
3209
|
+
);
|
|
3210
|
+
process.exitCode = 1;
|
|
2796
3211
|
}
|
|
2797
3212
|
});
|
|
2798
3213
|
}
|
|
2799
3214
|
|
|
2800
3215
|
// src/commands/maintenance.ts
|
|
2801
3216
|
import { execSync as execSync3 } from "child_process";
|
|
2802
|
-
import
|
|
2803
|
-
import
|
|
2804
|
-
import
|
|
3217
|
+
import path2 from "path";
|
|
3218
|
+
import chalk11 from "chalk";
|
|
3219
|
+
import fs3 from "fs-extra";
|
|
2805
3220
|
function registerMaintenanceCommands(program2) {
|
|
2806
3221
|
const getCurrentStateFromLocalCache = async () => {
|
|
2807
3222
|
try {
|
|
2808
3223
|
const vemDir = await getVemDir();
|
|
2809
|
-
const currentStatePath =
|
|
2810
|
-
if (!await
|
|
2811
|
-
return await
|
|
3224
|
+
const currentStatePath = path2.join(vemDir, CURRENT_STATE_FILE);
|
|
3225
|
+
if (!await fs3.pathExists(currentStatePath)) return "";
|
|
3226
|
+
return await fs3.readFile(currentStatePath, "utf-8");
|
|
2812
3227
|
} catch {
|
|
2813
3228
|
return "";
|
|
2814
3229
|
}
|
|
2815
3230
|
};
|
|
2816
3231
|
const writeCurrentStateToLocalCache = async (content) => {
|
|
2817
3232
|
const vemDir = await getVemDir();
|
|
2818
|
-
const currentStatePath =
|
|
2819
|
-
await
|
|
3233
|
+
const currentStatePath = path2.join(vemDir, CURRENT_STATE_FILE);
|
|
3234
|
+
await fs3.writeFile(currentStatePath, content, "utf-8");
|
|
2820
3235
|
};
|
|
2821
3236
|
const resolveRemoteProjectAuth = async () => {
|
|
2822
3237
|
const configService = new ConfigService();
|
|
@@ -2836,21 +3251,21 @@ function registerMaintenanceCommands(program2) {
|
|
|
2836
3251
|
try {
|
|
2837
3252
|
if (!options.context || !options.decision) {
|
|
2838
3253
|
console.error(
|
|
2839
|
-
|
|
3254
|
+
chalk11.red("\n\u2716 Both --context and --decision are required.\n")
|
|
2840
3255
|
);
|
|
2841
|
-
console.log(
|
|
3256
|
+
console.log(chalk11.gray("Example:"));
|
|
2842
3257
|
console.log(
|
|
2843
|
-
|
|
3258
|
+
chalk11.gray(' vem decision add "Use Zod for validation" \\')
|
|
2844
3259
|
);
|
|
2845
3260
|
console.log(
|
|
2846
|
-
|
|
3261
|
+
chalk11.gray(' --context "Need runtime type checking" \\')
|
|
2847
3262
|
);
|
|
2848
3263
|
console.log(
|
|
2849
|
-
|
|
3264
|
+
chalk11.gray(
|
|
2850
3265
|
' --decision "Chose Zod over Yup for better TypeScript inference" \\'
|
|
2851
3266
|
)
|
|
2852
3267
|
);
|
|
2853
|
-
console.log(
|
|
3268
|
+
console.log(chalk11.gray(" --tasks TASK-042,TASK-043"));
|
|
2854
3269
|
return;
|
|
2855
3270
|
}
|
|
2856
3271
|
const relatedTasks = options.tasks ? options.tasks.split(",").map((t) => t.trim()).filter(Boolean) : void 0;
|
|
@@ -2890,20 +3305,20 @@ function registerMaintenanceCommands(program2) {
|
|
|
2890
3305
|
relatedTasks
|
|
2891
3306
|
);
|
|
2892
3307
|
console.log(
|
|
2893
|
-
|
|
3308
|
+
chalk11.green(
|
|
2894
3309
|
`
|
|
2895
3310
|
\u2714 Decision recorded${savedToCloud ? " (cloud + local cache)" : " (local cache)"}: ${title}`
|
|
2896
3311
|
)
|
|
2897
3312
|
);
|
|
2898
3313
|
if (relatedTasks && relatedTasks.length > 0) {
|
|
2899
3314
|
console.log(
|
|
2900
|
-
|
|
3315
|
+
chalk11.gray(` Related tasks: ${relatedTasks.join(", ")}`)
|
|
2901
3316
|
);
|
|
2902
3317
|
}
|
|
2903
3318
|
console.log();
|
|
2904
3319
|
} catch (error) {
|
|
2905
3320
|
console.error(
|
|
2906
|
-
|
|
3321
|
+
chalk11.red(`
|
|
2907
3322
|
\u2716 Failed to record decision: ${error.message}
|
|
2908
3323
|
`)
|
|
2909
3324
|
);
|
|
@@ -2926,13 +3341,13 @@ function registerMaintenanceCommands(program2) {
|
|
|
2926
3341
|
);
|
|
2927
3342
|
if (response.ok) {
|
|
2928
3343
|
const payload = await response.json();
|
|
2929
|
-
console.log(
|
|
2930
|
-
console.log(
|
|
3344
|
+
console.log(chalk11.bold("\nProject Context"));
|
|
3345
|
+
console.log(chalk11.gray(`Source: ${payload.source || "db"}`));
|
|
2931
3346
|
console.log(payload.context || "");
|
|
2932
|
-
console.log(
|
|
3347
|
+
console.log(chalk11.bold("\nCurrent State"));
|
|
2933
3348
|
console.log(payload.current_state || "");
|
|
2934
3349
|
if (payload.decisions && payload.decisions.trim().length > 0) {
|
|
2935
|
-
console.log(
|
|
3350
|
+
console.log(chalk11.bold("\nDecisions"));
|
|
2936
3351
|
console.log(payload.decisions);
|
|
2937
3352
|
}
|
|
2938
3353
|
console.log("");
|
|
@@ -2944,14 +3359,14 @@ function registerMaintenanceCommands(program2) {
|
|
|
2944
3359
|
configService.getContext(),
|
|
2945
3360
|
getCurrentStateFromLocalCache()
|
|
2946
3361
|
]);
|
|
2947
|
-
console.log(
|
|
3362
|
+
console.log(chalk11.bold("\nProject Context (local cache)"));
|
|
2948
3363
|
console.log(context || "");
|
|
2949
|
-
console.log(
|
|
3364
|
+
console.log(chalk11.bold("\nCurrent State (local cache)"));
|
|
2950
3365
|
console.log(currentState || "");
|
|
2951
3366
|
console.log("");
|
|
2952
3367
|
} catch (error) {
|
|
2953
3368
|
console.error(
|
|
2954
|
-
|
|
3369
|
+
chalk11.red(`
|
|
2955
3370
|
\u2716 Failed to read context: ${error.message}
|
|
2956
3371
|
`)
|
|
2957
3372
|
);
|
|
@@ -2961,7 +3376,7 @@ function registerMaintenanceCommands(program2) {
|
|
|
2961
3376
|
try {
|
|
2962
3377
|
if (options.context === void 0 && options.currentState === void 0) {
|
|
2963
3378
|
console.error(
|
|
2964
|
-
|
|
3379
|
+
chalk11.red(
|
|
2965
3380
|
"\n\u2716 Provide at least one of --context or --current-state.\n"
|
|
2966
3381
|
)
|
|
2967
3382
|
);
|
|
@@ -2990,7 +3405,7 @@ function registerMaintenanceCommands(program2) {
|
|
|
2990
3405
|
} else {
|
|
2991
3406
|
const payload = await response.json().catch(() => ({}));
|
|
2992
3407
|
console.log(
|
|
2993
|
-
|
|
3408
|
+
chalk11.yellow(
|
|
2994
3409
|
`Cloud context update failed; continuing with local cache only: ${payload.error || response.statusText}`
|
|
2995
3410
|
)
|
|
2996
3411
|
);
|
|
@@ -3004,7 +3419,7 @@ function registerMaintenanceCommands(program2) {
|
|
|
3004
3419
|
await writeCurrentStateToLocalCache(options.currentState);
|
|
3005
3420
|
}
|
|
3006
3421
|
console.log(
|
|
3007
|
-
|
|
3422
|
+
chalk11.green(
|
|
3008
3423
|
`
|
|
3009
3424
|
\u2714 Context updated${savedToCloud ? " (cloud + local cache)" : " (local cache)"}
|
|
3010
3425
|
`
|
|
@@ -3012,7 +3427,7 @@ function registerMaintenanceCommands(program2) {
|
|
|
3012
3427
|
);
|
|
3013
3428
|
} catch (error) {
|
|
3014
3429
|
console.error(
|
|
3015
|
-
|
|
3430
|
+
chalk11.red(`
|
|
3016
3431
|
\u2716 Failed to update context: ${error.message}
|
|
3017
3432
|
`)
|
|
3018
3433
|
);
|
|
@@ -3027,50 +3442,50 @@ function registerMaintenanceCommands(program2) {
|
|
|
3027
3442
|
console.log(JSON.stringify(result, null, 2));
|
|
3028
3443
|
return;
|
|
3029
3444
|
}
|
|
3030
|
-
console.log(
|
|
3031
|
-
console.log(
|
|
3445
|
+
console.log(chalk11.bold("\nVEM Diff (local vs. cloud)"));
|
|
3446
|
+
console.log(chalk11.gray("\u2500".repeat(50)));
|
|
3032
3447
|
if (result.tasks.added.length > 0 || result.tasks.modified.length > 0) {
|
|
3033
|
-
console.log(
|
|
3448
|
+
console.log(chalk11.bold("\nTasks:"));
|
|
3034
3449
|
for (const id of result.tasks.added) {
|
|
3035
|
-
console.log(
|
|
3450
|
+
console.log(chalk11.green(` + ${id} (new)`));
|
|
3036
3451
|
}
|
|
3037
3452
|
for (const mod of result.tasks.modified) {
|
|
3038
|
-
console.log(
|
|
3453
|
+
console.log(chalk11.yellow(` ~ ${mod.id} (${mod.changes})`));
|
|
3039
3454
|
}
|
|
3040
3455
|
}
|
|
3041
3456
|
if (result.decisions.added.length > 0) {
|
|
3042
|
-
console.log(
|
|
3457
|
+
console.log(chalk11.bold("\nDecisions:"));
|
|
3043
3458
|
console.log(
|
|
3044
|
-
|
|
3459
|
+
chalk11.green(` + ${result.decisions.added.length} new decisions`)
|
|
3045
3460
|
);
|
|
3046
3461
|
}
|
|
3047
3462
|
if (result.changelog.added.length > 0) {
|
|
3048
|
-
console.log(
|
|
3463
|
+
console.log(chalk11.bold("\nChangelog:"));
|
|
3049
3464
|
console.log(
|
|
3050
|
-
|
|
3465
|
+
chalk11.green(` + ${result.changelog.added.length} new entries`)
|
|
3051
3466
|
);
|
|
3052
3467
|
}
|
|
3053
3468
|
if (result.currentState.changed) {
|
|
3054
|
-
console.log(
|
|
3469
|
+
console.log(chalk11.bold("\nCurrent State:"));
|
|
3055
3470
|
console.log(
|
|
3056
|
-
|
|
3471
|
+
chalk11.yellow(
|
|
3057
3472
|
` ~ Modified locally (${result.currentState.lineCount} lines)`
|
|
3058
3473
|
)
|
|
3059
3474
|
);
|
|
3060
3475
|
}
|
|
3061
|
-
console.log(
|
|
3476
|
+
console.log(chalk11.gray(`
|
|
3062
3477
|
${"\u2500".repeat(50)}`));
|
|
3063
3478
|
console.log(
|
|
3064
|
-
|
|
3479
|
+
chalk11.bold(`Summary: ${result.summary.totalChanges} changes`)
|
|
3065
3480
|
);
|
|
3066
3481
|
if (result.summary.totalChanges > 0) {
|
|
3067
|
-
console.log(
|
|
3482
|
+
console.log(chalk11.gray("Run: vem push\n"));
|
|
3068
3483
|
} else {
|
|
3069
|
-
console.log(
|
|
3484
|
+
console.log(chalk11.gray("No changes to push\n"));
|
|
3070
3485
|
}
|
|
3071
3486
|
} catch (error) {
|
|
3072
3487
|
console.error(
|
|
3073
|
-
|
|
3488
|
+
chalk11.red(`
|
|
3074
3489
|
\u2716 Failed to generate diff: ${error.message}
|
|
3075
3490
|
`)
|
|
3076
3491
|
);
|
|
@@ -3088,8 +3503,8 @@ ${"\u2500".repeat(50)}`));
|
|
|
3088
3503
|
);
|
|
3089
3504
|
return;
|
|
3090
3505
|
}
|
|
3091
|
-
console.log(
|
|
3092
|
-
console.log(
|
|
3506
|
+
console.log(chalk11.bold("\nVEM Health Check"));
|
|
3507
|
+
console.log(chalk11.gray("\u2500".repeat(50)));
|
|
3093
3508
|
let hasErrors = false;
|
|
3094
3509
|
let hasWarnings = false;
|
|
3095
3510
|
for (const result of results) {
|
|
@@ -3097,36 +3512,36 @@ ${"\u2500".repeat(50)}`));
|
|
|
3097
3512
|
let color;
|
|
3098
3513
|
if (result.status === "pass") {
|
|
3099
3514
|
icon = "\u2713";
|
|
3100
|
-
color =
|
|
3515
|
+
color = chalk11.green;
|
|
3101
3516
|
} else if (result.status === "warn") {
|
|
3102
3517
|
icon = "\u26A0";
|
|
3103
|
-
color =
|
|
3518
|
+
color = chalk11.yellow;
|
|
3104
3519
|
hasWarnings = true;
|
|
3105
3520
|
} else {
|
|
3106
3521
|
icon = "\u2717";
|
|
3107
|
-
color =
|
|
3522
|
+
color = chalk11.red;
|
|
3108
3523
|
hasErrors = true;
|
|
3109
3524
|
}
|
|
3110
3525
|
console.log(color(`${icon} ${result.name}`));
|
|
3111
|
-
console.log(
|
|
3526
|
+
console.log(chalk11.gray(` ${result.message}`));
|
|
3112
3527
|
if (result.fix) {
|
|
3113
|
-
console.log(
|
|
3528
|
+
console.log(chalk11.gray(` \u2192 ${result.fix}`));
|
|
3114
3529
|
}
|
|
3115
3530
|
}
|
|
3116
|
-
console.log(
|
|
3531
|
+
console.log(chalk11.gray("\u2500".repeat(50)));
|
|
3117
3532
|
if (hasErrors) {
|
|
3118
|
-
console.log(
|
|
3533
|
+
console.log(chalk11.red("\n\u2717 Issues found that need attention\n"));
|
|
3119
3534
|
process.exit(2);
|
|
3120
3535
|
} else if (hasWarnings) {
|
|
3121
|
-
console.log(
|
|
3536
|
+
console.log(chalk11.yellow("\n\u26A0 Minor issues found\n"));
|
|
3122
3537
|
process.exit(1);
|
|
3123
3538
|
} else {
|
|
3124
|
-
console.log(
|
|
3539
|
+
console.log(chalk11.green("\n\u2713 All checks passed\n"));
|
|
3125
3540
|
process.exit(0);
|
|
3126
3541
|
}
|
|
3127
3542
|
} catch (error) {
|
|
3128
3543
|
console.error(
|
|
3129
|
-
|
|
3544
|
+
chalk11.red(`
|
|
3130
3545
|
\u2716 Failed to run health checks: ${error.message}
|
|
3131
3546
|
`)
|
|
3132
3547
|
);
|
|
@@ -3141,15 +3556,15 @@ ${"\u2500".repeat(50)}`));
|
|
|
3141
3556
|
const projectId = await configService.getProjectId();
|
|
3142
3557
|
if (!key || !projectId) {
|
|
3143
3558
|
console.error(
|
|
3144
|
-
|
|
3559
|
+
chalk11.red("\n\u2716 Authentication or project link missing.\n")
|
|
3145
3560
|
);
|
|
3146
3561
|
return;
|
|
3147
3562
|
}
|
|
3148
|
-
console.log(
|
|
3563
|
+
console.log(chalk11.blue("Analyzing local changes..."));
|
|
3149
3564
|
const diffCmd = options.staged ? "git diff --cached" : "git diff HEAD";
|
|
3150
3565
|
const diff = execSync3(diffCmd).toString();
|
|
3151
3566
|
if (!diff.trim()) {
|
|
3152
|
-
console.log(
|
|
3567
|
+
console.log(chalk11.yellow("No changes detected to summarize."));
|
|
3153
3568
|
return;
|
|
3154
3569
|
}
|
|
3155
3570
|
const res = await fetch(`${API_URL}/projects/${projectId}/summarize`, {
|
|
@@ -3166,37 +3581,37 @@ ${"\u2500".repeat(50)}`));
|
|
|
3166
3581
|
throw new Error(data.error || "Summarization request failed");
|
|
3167
3582
|
}
|
|
3168
3583
|
const { suggestions } = await res.json();
|
|
3169
|
-
console.log(
|
|
3170
|
-
console.log(
|
|
3584
|
+
console.log(chalk11.bold("\n\u2728 AI-Suggested Memory Updates"));
|
|
3585
|
+
console.log(chalk11.gray("\u2500".repeat(50)));
|
|
3171
3586
|
if (suggestions.changelog) {
|
|
3172
|
-
console.log(
|
|
3587
|
+
console.log(chalk11.cyan("\n[Changelog]"));
|
|
3173
3588
|
console.log(suggestions.changelog);
|
|
3174
3589
|
}
|
|
3175
3590
|
if (suggestions.decisions?.length > 0) {
|
|
3176
|
-
console.log(
|
|
3591
|
+
console.log(chalk11.cyan("\n[Decisions]"));
|
|
3177
3592
|
suggestions.decisions.forEach((d) => {
|
|
3178
|
-
console.log(
|
|
3179
|
-
console.log(
|
|
3593
|
+
console.log(chalk11.bold(`- ${d.title}`));
|
|
3594
|
+
console.log(chalk11.gray(` ${d.decision}`));
|
|
3180
3595
|
});
|
|
3181
3596
|
}
|
|
3182
3597
|
if (suggestions.context_updates) {
|
|
3183
|
-
console.log(
|
|
3598
|
+
console.log(chalk11.cyan("\n[Context Updates]"));
|
|
3184
3599
|
console.log(suggestions.context_updates);
|
|
3185
3600
|
}
|
|
3186
3601
|
if (suggestions.current_state_updates) {
|
|
3187
|
-
console.log(
|
|
3602
|
+
console.log(chalk11.cyan("\n[Current State Updates]"));
|
|
3188
3603
|
console.log(suggestions.current_state_updates);
|
|
3189
3604
|
}
|
|
3190
|
-
console.log(
|
|
3605
|
+
console.log(chalk11.gray(`
|
|
3191
3606
|
${"\u2500".repeat(50)}`));
|
|
3192
3607
|
console.log(
|
|
3193
|
-
|
|
3608
|
+
chalk11.gray(
|
|
3194
3609
|
"Tip: Use these suggestions to update your .vem/ files before pushing.\n"
|
|
3195
3610
|
)
|
|
3196
3611
|
);
|
|
3197
3612
|
} catch (error) {
|
|
3198
3613
|
console.error(
|
|
3199
|
-
|
|
3614
|
+
chalk11.red(`
|
|
3200
3615
|
\u2716 Failed to generate summary: ${error.message}
|
|
3201
3616
|
`)
|
|
3202
3617
|
);
|
|
@@ -3205,8 +3620,8 @@ ${"\u2500".repeat(50)}`));
|
|
|
3205
3620
|
}
|
|
3206
3621
|
|
|
3207
3622
|
// src/commands/project.ts
|
|
3208
|
-
import
|
|
3209
|
-
import
|
|
3623
|
+
import chalk12 from "chalk";
|
|
3624
|
+
import prompts6 from "prompts";
|
|
3210
3625
|
async function runInteractiveLinkFlow(apiKey, configService) {
|
|
3211
3626
|
let projectId;
|
|
3212
3627
|
let projectOrgId = await configService.getProjectOrgId();
|
|
@@ -3220,7 +3635,7 @@ async function runInteractiveLinkFlow(apiKey, configService) {
|
|
|
3220
3635
|
});
|
|
3221
3636
|
if (!res.ok) {
|
|
3222
3637
|
console.error(
|
|
3223
|
-
|
|
3638
|
+
chalk12.red(`
|
|
3224
3639
|
\u2716 Failed to fetch projects: ${res.statusText}
|
|
3225
3640
|
`)
|
|
3226
3641
|
);
|
|
@@ -3272,7 +3687,7 @@ async function runInteractiveLinkFlow(apiKey, configService) {
|
|
|
3272
3687
|
);
|
|
3273
3688
|
const choices = [
|
|
3274
3689
|
{
|
|
3275
|
-
title:
|
|
3690
|
+
title: chalk12.green("+ Create New Project"),
|
|
3276
3691
|
value: CREATE_NEW,
|
|
3277
3692
|
description: `Create a new project in ${workspace.label}`
|
|
3278
3693
|
}
|
|
@@ -3287,19 +3702,19 @@ async function runInteractiveLinkFlow(apiKey, configService) {
|
|
|
3287
3702
|
}
|
|
3288
3703
|
} else {
|
|
3289
3704
|
choices.push({
|
|
3290
|
-
title:
|
|
3705
|
+
title: chalk12.gray("No projects yet"),
|
|
3291
3706
|
value: "NO_PROJECTS",
|
|
3292
3707
|
disabled: true
|
|
3293
3708
|
});
|
|
3294
3709
|
}
|
|
3295
3710
|
if (allowBack) {
|
|
3296
3711
|
choices.push({
|
|
3297
|
-
title:
|
|
3712
|
+
title: chalk12.gray("\u2190 Back"),
|
|
3298
3713
|
value: BACK
|
|
3299
3714
|
});
|
|
3300
3715
|
}
|
|
3301
3716
|
const message = workspace.isPersonal ? "Select a personal project to link:" : `Select a project in ${workspace.label}:`;
|
|
3302
|
-
const response = await
|
|
3717
|
+
const response = await prompts6({
|
|
3303
3718
|
type: "select",
|
|
3304
3719
|
name: "projectId",
|
|
3305
3720
|
message,
|
|
@@ -3309,7 +3724,7 @@ async function runInteractiveLinkFlow(apiKey, configService) {
|
|
|
3309
3724
|
if (!selectedProjectId) return { type: "cancel" };
|
|
3310
3725
|
if (selectedProjectId === BACK) return { type: "back" };
|
|
3311
3726
|
if (selectedProjectId === CREATE_NEW) {
|
|
3312
|
-
const projectInput = await
|
|
3727
|
+
const projectInput = await prompts6({
|
|
3313
3728
|
type: "text",
|
|
3314
3729
|
name: "name",
|
|
3315
3730
|
message: `Enter project name for ${workspace.label}:`,
|
|
@@ -3339,7 +3754,7 @@ async function runInteractiveLinkFlow(apiKey, configService) {
|
|
|
3339
3754
|
const err = await createRes.json().catch(() => ({}));
|
|
3340
3755
|
if (createRes.status === 403) {
|
|
3341
3756
|
console.error(
|
|
3342
|
-
|
|
3757
|
+
chalk12.red(
|
|
3343
3758
|
`
|
|
3344
3759
|
\u2716 Check failed: ${err.error || "Tier limit reached"}
|
|
3345
3760
|
`
|
|
@@ -3347,7 +3762,7 @@ async function runInteractiveLinkFlow(apiKey, configService) {
|
|
|
3347
3762
|
);
|
|
3348
3763
|
} else if (createRes.status === 409) {
|
|
3349
3764
|
console.error(
|
|
3350
|
-
|
|
3765
|
+
chalk12.red(
|
|
3351
3766
|
`
|
|
3352
3767
|
\u2716 ${err.error || "Failed to create project: Already exists."}
|
|
3353
3768
|
`
|
|
@@ -3355,7 +3770,7 @@ async function runInteractiveLinkFlow(apiKey, configService) {
|
|
|
3355
3770
|
);
|
|
3356
3771
|
} else {
|
|
3357
3772
|
console.error(
|
|
3358
|
-
|
|
3773
|
+
chalk12.red(
|
|
3359
3774
|
`
|
|
3360
3775
|
\u2716 Failed to create project: ${err.error || createRes.statusText}
|
|
3361
3776
|
`
|
|
@@ -3365,7 +3780,7 @@ async function runInteractiveLinkFlow(apiKey, configService) {
|
|
|
3365
3780
|
return { type: "cancel" };
|
|
3366
3781
|
}
|
|
3367
3782
|
const { project } = await createRes.json();
|
|
3368
|
-
console.log(
|
|
3783
|
+
console.log(chalk12.green(`
|
|
3369
3784
|
\u2714 Project created: ${project.id}`));
|
|
3370
3785
|
return {
|
|
3371
3786
|
type: "selected",
|
|
@@ -3387,19 +3802,19 @@ async function runInteractiveLinkFlow(apiKey, configService) {
|
|
|
3387
3802
|
const personalWorkspace = workspaceChoices.find((item) => item.isPersonal);
|
|
3388
3803
|
const activeWorkspace = personalWorkspace || workspaceChoices[0];
|
|
3389
3804
|
if (!activeWorkspace) {
|
|
3390
|
-
console.log(
|
|
3805
|
+
console.log(chalk12.yellow("\nNo available workspaces found.\n"));
|
|
3391
3806
|
return null;
|
|
3392
3807
|
}
|
|
3393
3808
|
const selection = await chooseProjectForWorkspace(activeWorkspace, false);
|
|
3394
3809
|
if (selection.type !== "selected") {
|
|
3395
|
-
console.log(
|
|
3810
|
+
console.log(chalk12.yellow("\nOperation cancelled.\n"));
|
|
3396
3811
|
return null;
|
|
3397
3812
|
}
|
|
3398
3813
|
projectId = selection.projectId;
|
|
3399
3814
|
projectOrgId = selection.orgId || projectOrgId;
|
|
3400
3815
|
} else {
|
|
3401
3816
|
while (!projectId) {
|
|
3402
|
-
const workspaceResponse = await
|
|
3817
|
+
const workspaceResponse = await prompts6({
|
|
3403
3818
|
type: "select",
|
|
3404
3819
|
name: "workspaceId",
|
|
3405
3820
|
message: "Select personal or organization workspace:",
|
|
@@ -3410,12 +3825,12 @@ async function runInteractiveLinkFlow(apiKey, configService) {
|
|
|
3410
3825
|
});
|
|
3411
3826
|
const selectedWorkspaceId = workspaceResponse.workspaceId;
|
|
3412
3827
|
if (!selectedWorkspaceId) {
|
|
3413
|
-
console.log(
|
|
3828
|
+
console.log(chalk12.yellow("\nOperation cancelled.\n"));
|
|
3414
3829
|
return null;
|
|
3415
3830
|
}
|
|
3416
3831
|
const selectedWorkspace = workspaceMap.get(selectedWorkspaceId);
|
|
3417
3832
|
if (!selectedWorkspace) {
|
|
3418
|
-
console.log(
|
|
3833
|
+
console.log(chalk12.yellow("\nOperation cancelled.\n"));
|
|
3419
3834
|
return null;
|
|
3420
3835
|
}
|
|
3421
3836
|
const selection = await chooseProjectForWorkspace(
|
|
@@ -3423,7 +3838,7 @@ async function runInteractiveLinkFlow(apiKey, configService) {
|
|
|
3423
3838
|
true
|
|
3424
3839
|
);
|
|
3425
3840
|
if (selection.type === "cancel") {
|
|
3426
|
-
console.log(
|
|
3841
|
+
console.log(chalk12.yellow("\nOperation cancelled.\n"));
|
|
3427
3842
|
return null;
|
|
3428
3843
|
}
|
|
3429
3844
|
if (selection.type === "back") {
|
|
@@ -3458,14 +3873,14 @@ async function runInteractiveLinkFlow(apiKey, configService) {
|
|
|
3458
3873
|
if (!patchRes.ok) {
|
|
3459
3874
|
const err = await patchRes.text().catch(() => "");
|
|
3460
3875
|
console.log(
|
|
3461
|
-
|
|
3876
|
+
chalk12.yellow(
|
|
3462
3877
|
` \u26A0 Warning: Failed to update server-side repo URL: ${err || patchRes.statusText}`
|
|
3463
3878
|
)
|
|
3464
3879
|
);
|
|
3465
3880
|
}
|
|
3466
3881
|
} catch (_err) {
|
|
3467
3882
|
console.log(
|
|
3468
|
-
|
|
3883
|
+
chalk12.yellow(" \u26A0 Warning: Could not reach server to update repo URL.")
|
|
3469
3884
|
);
|
|
3470
3885
|
}
|
|
3471
3886
|
if (repoUrl === "REMOVE" || !repoUrl) {
|
|
@@ -3479,14 +3894,14 @@ async function runInteractiveLinkFlow(apiKey, configService) {
|
|
|
3479
3894
|
await installGitHook({ promptIfMissing: false, quiet: true });
|
|
3480
3895
|
if (!repoUrl || repoUrl === "REMOVE") {
|
|
3481
3896
|
console.log(
|
|
3482
|
-
|
|
3897
|
+
chalk12.yellow(
|
|
3483
3898
|
"\n\u26A0 For full advantage of vem (automatic indexing, code search, and PR summaries), you should link a repo origin."
|
|
3484
3899
|
)
|
|
3485
3900
|
);
|
|
3486
3901
|
} else {
|
|
3487
|
-
console.log(
|
|
3902
|
+
console.log(chalk12.gray(`Repo: ${repoUrl}`));
|
|
3488
3903
|
}
|
|
3489
|
-
console.log(
|
|
3904
|
+
console.log(chalk12.green(`
|
|
3490
3905
|
\u2714 Linked to project ${projectId}
|
|
3491
3906
|
`));
|
|
3492
3907
|
return projectId;
|
|
@@ -3505,7 +3920,7 @@ function registerProjectCommands(program2) {
|
|
|
3505
3920
|
projectId = await configService.getProjectId();
|
|
3506
3921
|
if (!projectId) {
|
|
3507
3922
|
console.error(
|
|
3508
|
-
|
|
3923
|
+
chalk12.red(
|
|
3509
3924
|
"\n\u2716 Not linked to any project. Link a project first or provide a projectId.\n"
|
|
3510
3925
|
)
|
|
3511
3926
|
);
|
|
@@ -3516,7 +3931,7 @@ function registerProjectCommands(program2) {
|
|
|
3516
3931
|
const check = await validateProject(projectId, apiKey, configService);
|
|
3517
3932
|
if (!check.valid) {
|
|
3518
3933
|
console.error(
|
|
3519
|
-
|
|
3934
|
+
chalk12.red(
|
|
3520
3935
|
`
|
|
3521
3936
|
\u2716 Project ${projectId} not found. It may have been deleted or you may not have access.
|
|
3522
3937
|
`
|
|
@@ -3553,14 +3968,14 @@ function registerProjectCommands(program2) {
|
|
|
3553
3968
|
if (!res.ok) {
|
|
3554
3969
|
const err = await res.text().catch(() => "");
|
|
3555
3970
|
console.log(
|
|
3556
|
-
|
|
3971
|
+
chalk12.yellow(
|
|
3557
3972
|
` \u26A0 Warning: Failed to update server-side repo URL: ${err || res.statusText}`
|
|
3558
3973
|
)
|
|
3559
3974
|
);
|
|
3560
3975
|
}
|
|
3561
3976
|
} catch (_err) {
|
|
3562
3977
|
console.log(
|
|
3563
|
-
|
|
3978
|
+
chalk12.yellow(
|
|
3564
3979
|
" \u26A0 Warning: Could not reach server to update repo URL."
|
|
3565
3980
|
)
|
|
3566
3981
|
);
|
|
@@ -3577,39 +3992,39 @@ function registerProjectCommands(program2) {
|
|
|
3577
3992
|
await installGitHook({ promptIfMissing: false, quiet: true });
|
|
3578
3993
|
if (options.reset) {
|
|
3579
3994
|
if (repoUrl === "REMOVE") {
|
|
3580
|
-
console.log(
|
|
3995
|
+
console.log(chalk12.green("\n\u2714 Repository binding removed."));
|
|
3581
3996
|
console.log(
|
|
3582
|
-
|
|
3997
|
+
chalk12.yellow(
|
|
3583
3998
|
"\u26A0 For full advantage of vem (automatic indexing, code search, and PR summaries), you should link a repo origin."
|
|
3584
3999
|
)
|
|
3585
4000
|
);
|
|
3586
4001
|
} else if (repoUrl) {
|
|
3587
4002
|
console.log(
|
|
3588
|
-
|
|
4003
|
+
chalk12.green(`
|
|
3589
4004
|
\u2714 Repository binding updated to: ${repoUrl}`)
|
|
3590
4005
|
);
|
|
3591
4006
|
}
|
|
3592
4007
|
} else {
|
|
3593
4008
|
if (!repoUrl || repoUrl === "REMOVE") {
|
|
3594
4009
|
console.log(
|
|
3595
|
-
|
|
4010
|
+
chalk12.yellow(
|
|
3596
4011
|
"\n\u26A0 For full advantage of vem (automatic indexing, code search, and PR summaries), you should link a repo origin."
|
|
3597
4012
|
)
|
|
3598
4013
|
);
|
|
3599
4014
|
} else {
|
|
3600
|
-
console.log(
|
|
4015
|
+
console.log(chalk12.gray(`Repo: ${repoUrl}`));
|
|
3601
4016
|
}
|
|
3602
4017
|
}
|
|
3603
4018
|
if (!options.reset) {
|
|
3604
|
-
console.log(
|
|
4019
|
+
console.log(chalk12.green(`
|
|
3605
4020
|
\u2714 Linked to project ${projectId}
|
|
3606
4021
|
`));
|
|
3607
4022
|
}
|
|
3608
4023
|
} catch (error) {
|
|
3609
4024
|
if (error instanceof Error) {
|
|
3610
|
-
console.error(
|
|
4025
|
+
console.error(chalk12.red("\n\u2716 Link Failed:"), error.message);
|
|
3611
4026
|
} else {
|
|
3612
|
-
console.error(
|
|
4027
|
+
console.error(chalk12.red("\n\u2716 Link Failed:"), String(error));
|
|
3613
4028
|
}
|
|
3614
4029
|
}
|
|
3615
4030
|
});
|
|
@@ -3618,7 +4033,7 @@ function registerProjectCommands(program2) {
|
|
|
3618
4033
|
const configService = new ConfigService();
|
|
3619
4034
|
const projectId = await configService.getProjectId();
|
|
3620
4035
|
if (!projectId) {
|
|
3621
|
-
console.log(
|
|
4036
|
+
console.log(chalk12.yellow("\n\u26A0 Not linked to any project.\n"));
|
|
3622
4037
|
return;
|
|
3623
4038
|
}
|
|
3624
4039
|
const apiKey = await ensureAuthenticated(configService);
|
|
@@ -3637,25 +4052,25 @@ function registerProjectCommands(program2) {
|
|
|
3637
4052
|
}
|
|
3638
4053
|
} catch (_) {
|
|
3639
4054
|
}
|
|
3640
|
-
const response = await
|
|
4055
|
+
const response = await prompts6({
|
|
3641
4056
|
type: "confirm",
|
|
3642
4057
|
name: "confirmed",
|
|
3643
|
-
message: `Are you sure you want to unlink from project ${
|
|
4058
|
+
message: `Are you sure you want to unlink from project ${chalk12.bold(projectName)} (${projectId})?`,
|
|
3644
4059
|
initial: false
|
|
3645
4060
|
});
|
|
3646
4061
|
if (response.confirmed) {
|
|
3647
4062
|
await configService.setProjectId(null);
|
|
3648
4063
|
await configService.setProjectOrgId(null);
|
|
3649
4064
|
await configService.setLinkedRemote(null);
|
|
3650
|
-
console.log(
|
|
4065
|
+
console.log(chalk12.green("\n\u2714 Unlinked from project.\n"));
|
|
3651
4066
|
} else {
|
|
3652
|
-
console.log(
|
|
4067
|
+
console.log(chalk12.yellow("\nOperation cancelled.\n"));
|
|
3653
4068
|
}
|
|
3654
4069
|
} catch (error) {
|
|
3655
4070
|
if (error instanceof Error) {
|
|
3656
|
-
console.error(
|
|
4071
|
+
console.error(chalk12.red("\n\u2716 Unlink Failed:"), error.message);
|
|
3657
4072
|
} else {
|
|
3658
|
-
console.error(
|
|
4073
|
+
console.error(chalk12.red("\n\u2716 Unlink Failed:"), String(error));
|
|
3659
4074
|
}
|
|
3660
4075
|
}
|
|
3661
4076
|
});
|
|
@@ -3666,18 +4081,18 @@ function registerProjectCommands(program2) {
|
|
|
3666
4081
|
const resolvedProjectId = projectId || await configService.getProjectId();
|
|
3667
4082
|
if (!resolvedProjectId) {
|
|
3668
4083
|
console.error(
|
|
3669
|
-
|
|
4084
|
+
chalk12.red("\n\u2716 Project not linked. Run `vem link` first.\n")
|
|
3670
4085
|
);
|
|
3671
4086
|
process.exit(1);
|
|
3672
4087
|
}
|
|
3673
4088
|
const projectUrl = `${WEB_URL}/project/${resolvedProjectId}`;
|
|
3674
|
-
console.log(
|
|
4089
|
+
console.log(chalk12.blue(`
|
|
3675
4090
|
\u{1F310} Opening: ${projectUrl}
|
|
3676
4091
|
`));
|
|
3677
4092
|
openBrowser(projectUrl);
|
|
3678
4093
|
} catch (error) {
|
|
3679
4094
|
console.error(
|
|
3680
|
-
|
|
4095
|
+
chalk12.red("\n\u2716 Failed to open project:"),
|
|
3681
4096
|
error?.message ?? String(error)
|
|
3682
4097
|
);
|
|
3683
4098
|
}
|
|
@@ -3687,10 +4102,10 @@ function registerProjectCommands(program2) {
|
|
|
3687
4102
|
// src/commands/runner.ts
|
|
3688
4103
|
import { execFileSync, spawn as spawn3 } from "child_process";
|
|
3689
4104
|
import { existsSync } from "fs";
|
|
3690
|
-
import { dirname, resolve } from "path";
|
|
3691
|
-
import
|
|
4105
|
+
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
4106
|
+
import chalk13 from "chalk";
|
|
3692
4107
|
function sleep(ms) {
|
|
3693
|
-
return new Promise((
|
|
4108
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
3694
4109
|
}
|
|
3695
4110
|
function getCliEntrypoint() {
|
|
3696
4111
|
const entry = process.argv[1];
|
|
@@ -3716,7 +4131,7 @@ function runGitIn(cwd, args, options) {
|
|
|
3716
4131
|
function hasDirtyWorktree() {
|
|
3717
4132
|
return runGit(["status", "--porcelain"]).trim().length > 0;
|
|
3718
4133
|
}
|
|
3719
|
-
function
|
|
4134
|
+
function getRepoRoot3() {
|
|
3720
4135
|
return runGit(["rev-parse", "--show-toplevel"]);
|
|
3721
4136
|
}
|
|
3722
4137
|
function commandExists(command) {
|
|
@@ -3727,7 +4142,13 @@ function commandExists(command) {
|
|
|
3727
4142
|
return false;
|
|
3728
4143
|
}
|
|
3729
4144
|
}
|
|
3730
|
-
var KNOWN_RUNNER_AGENTS = [
|
|
4145
|
+
var KNOWN_RUNNER_AGENTS = [
|
|
4146
|
+
"copilot",
|
|
4147
|
+
"gh",
|
|
4148
|
+
"claude",
|
|
4149
|
+
"gemini",
|
|
4150
|
+
"codex"
|
|
4151
|
+
];
|
|
3731
4152
|
function hasSandboxCredentials(agent) {
|
|
3732
4153
|
if (agent === "claude") {
|
|
3733
4154
|
return typeof process.env.ANTHROPIC_API_KEY === "string" && process.env.ANTHROPIC_API_KEY.trim().length > 0;
|
|
@@ -3736,7 +4157,9 @@ function hasSandboxCredentials(agent) {
|
|
|
3736
4157
|
const envToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
3737
4158
|
if (envToken && envToken.trim().length > 0) return true;
|
|
3738
4159
|
try {
|
|
3739
|
-
const token = execFileSync("gh", ["auth", "token"], {
|
|
4160
|
+
const token = execFileSync("gh", ["auth", "token"], {
|
|
4161
|
+
encoding: "utf-8"
|
|
4162
|
+
}).trim();
|
|
3740
4163
|
return token.length > 0;
|
|
3741
4164
|
} catch {
|
|
3742
4165
|
return false;
|
|
@@ -3752,15 +4175,19 @@ function hasSandboxCredentials(agent) {
|
|
|
3752
4175
|
}
|
|
3753
4176
|
function getAvailableAgentCommands(selectedAgent, sandbox) {
|
|
3754
4177
|
const isAvailable = (command) => commandExists(command) && (!sandbox || hasSandboxCredentials(command));
|
|
3755
|
-
const knownAvailable = KNOWN_RUNNER_AGENTS.filter(
|
|
4178
|
+
const knownAvailable = KNOWN_RUNNER_AGENTS.filter(
|
|
4179
|
+
(command) => isAvailable(command)
|
|
4180
|
+
);
|
|
3756
4181
|
const selectedAvailable = isAvailable(selectedAgent);
|
|
3757
|
-
if (selectedAvailable && !knownAvailable.includes(
|
|
4182
|
+
if (selectedAvailable && !knownAvailable.includes(
|
|
4183
|
+
selectedAgent
|
|
4184
|
+
)) {
|
|
3758
4185
|
return [selectedAgent, ...knownAvailable];
|
|
3759
4186
|
}
|
|
3760
4187
|
return knownAvailable;
|
|
3761
4188
|
}
|
|
3762
4189
|
function getRunnerCapabilities(agent, sandbox = true, agentPinned = false) {
|
|
3763
|
-
const repoRoot =
|
|
4190
|
+
const repoRoot = getRepoRoot3();
|
|
3764
4191
|
let branch = null;
|
|
3765
4192
|
try {
|
|
3766
4193
|
branch = runGit(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
@@ -3789,21 +4216,19 @@ function checkDockerAvailable() {
|
|
|
3789
4216
|
try {
|
|
3790
4217
|
execFileSync("docker", ["info"], { stdio: "ignore" });
|
|
3791
4218
|
} catch {
|
|
4219
|
+
console.error(chalk13.red("\u2717 Docker is not running or not installed."));
|
|
3792
4220
|
console.error(
|
|
3793
|
-
|
|
3794
|
-
);
|
|
3795
|
-
console.error(
|
|
3796
|
-
chalk12.yellow(
|
|
4221
|
+
chalk13.yellow(
|
|
3797
4222
|
" The vem runner requires Docker to run agents in a secure sandbox."
|
|
3798
4223
|
)
|
|
3799
4224
|
);
|
|
3800
4225
|
console.error(
|
|
3801
|
-
|
|
4226
|
+
chalk13.gray(
|
|
3802
4227
|
" Install Docker Desktop: https://www.docker.com/products/docker-desktop/"
|
|
3803
4228
|
)
|
|
3804
4229
|
);
|
|
3805
4230
|
console.error(
|
|
3806
|
-
|
|
4231
|
+
chalk13.gray(
|
|
3807
4232
|
" Or run without sandbox (no isolation): vem runner --unsafe"
|
|
3808
4233
|
)
|
|
3809
4234
|
);
|
|
@@ -3813,32 +4238,36 @@ function checkDockerAvailable() {
|
|
|
3813
4238
|
var SANDBOX_IMAGE_NAME = "vem-sandbox:latest";
|
|
3814
4239
|
function getSandboxImageDir() {
|
|
3815
4240
|
const cliDist = getCliEntrypoint();
|
|
3816
|
-
const distDir =
|
|
4241
|
+
const distDir = dirname2(cliDist);
|
|
3817
4242
|
const candidates = [
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
4243
|
+
resolve2(distDir, "Dockerfile.sandbox"),
|
|
4244
|
+
resolve2(distDir, "..", "Dockerfile.sandbox"),
|
|
4245
|
+
resolve2(distDir, "..", "..", "apps", "cli", "Dockerfile.sandbox")
|
|
3821
4246
|
];
|
|
3822
4247
|
for (const candidate of candidates) {
|
|
3823
4248
|
if (existsSync(candidate)) {
|
|
3824
|
-
return
|
|
4249
|
+
return dirname2(candidate);
|
|
3825
4250
|
}
|
|
3826
4251
|
}
|
|
3827
|
-
throw new Error(
|
|
4252
|
+
throw new Error(
|
|
4253
|
+
"Dockerfile.sandbox not found. Ensure the vem CLI is installed correctly."
|
|
4254
|
+
);
|
|
3828
4255
|
}
|
|
3829
4256
|
function buildSandboxImage() {
|
|
3830
|
-
console.log(
|
|
4257
|
+
console.log(chalk13.cyan(" Building sandbox Docker image (first use)..."));
|
|
3831
4258
|
const contextDir = getSandboxImageDir();
|
|
3832
4259
|
execFileSync(
|
|
3833
4260
|
"docker",
|
|
3834
4261
|
["build", "-t", SANDBOX_IMAGE_NAME, "-f", "Dockerfile.sandbox", "."],
|
|
3835
4262
|
{ cwd: contextDir, stdio: "inherit" }
|
|
3836
4263
|
);
|
|
3837
|
-
console.log(
|
|
4264
|
+
console.log(chalk13.green(" \u2713 Sandbox image built."));
|
|
3838
4265
|
}
|
|
3839
4266
|
function ensureSandboxImage() {
|
|
3840
4267
|
try {
|
|
3841
|
-
execFileSync("docker", ["image", "inspect", SANDBOX_IMAGE_NAME], {
|
|
4268
|
+
execFileSync("docker", ["image", "inspect", SANDBOX_IMAGE_NAME], {
|
|
4269
|
+
stdio: "ignore"
|
|
4270
|
+
});
|
|
3842
4271
|
} catch {
|
|
3843
4272
|
buildSandboxImage();
|
|
3844
4273
|
}
|
|
@@ -3853,7 +4282,11 @@ function collectSandboxCredentials(agent) {
|
|
|
3853
4282
|
if (agent === "claude") {
|
|
3854
4283
|
addFromEnv("ANTHROPIC_API_KEY");
|
|
3855
4284
|
if (!creds.ANTHROPIC_API_KEY) {
|
|
3856
|
-
console.error(
|
|
4285
|
+
console.error(
|
|
4286
|
+
chalk13.red(
|
|
4287
|
+
`\u2717 ANTHROPIC_API_KEY is not set. Required for --agent claude.`
|
|
4288
|
+
)
|
|
4289
|
+
);
|
|
3857
4290
|
process.exit(1);
|
|
3858
4291
|
}
|
|
3859
4292
|
} else if (agent === "copilot" || agent === "gh") {
|
|
@@ -3862,31 +4295,43 @@ function collectSandboxCredentials(agent) {
|
|
|
3862
4295
|
creds.GITHUB_TOKEN = envToken;
|
|
3863
4296
|
} else {
|
|
3864
4297
|
try {
|
|
3865
|
-
const token = execFileSync("gh", ["auth", "token"], {
|
|
4298
|
+
const token = execFileSync("gh", ["auth", "token"], {
|
|
4299
|
+
encoding: "utf-8"
|
|
4300
|
+
}).trim();
|
|
3866
4301
|
if (token) creds.GITHUB_TOKEN = token;
|
|
3867
4302
|
} catch {
|
|
3868
4303
|
}
|
|
3869
4304
|
}
|
|
3870
4305
|
if (!creds.GITHUB_TOKEN) {
|
|
3871
|
-
console.error(
|
|
3872
|
-
|
|
4306
|
+
console.error(
|
|
4307
|
+
chalk13.red(`\u2717 GitHub token not found. Required for --agent copilot.`)
|
|
4308
|
+
);
|
|
4309
|
+
console.error(
|
|
4310
|
+
chalk13.gray(" Set GITHUB_TOKEN env var or run: gh auth login")
|
|
4311
|
+
);
|
|
3873
4312
|
process.exit(1);
|
|
3874
4313
|
}
|
|
3875
4314
|
} else if (agent === "gemini") {
|
|
3876
4315
|
addFromEnv("GEMINI_API_KEY");
|
|
3877
4316
|
if (!creds.GEMINI_API_KEY) {
|
|
3878
|
-
console.error(
|
|
4317
|
+
console.error(
|
|
4318
|
+
chalk13.red(`\u2717 GEMINI_API_KEY is not set. Required for --agent gemini.`)
|
|
4319
|
+
);
|
|
3879
4320
|
process.exit(1);
|
|
3880
4321
|
}
|
|
3881
4322
|
} else if (agent === "codex") {
|
|
3882
4323
|
addFromEnv("OPENAI_API_KEY");
|
|
3883
4324
|
if (!creds.OPENAI_API_KEY) {
|
|
3884
|
-
console.error(
|
|
4325
|
+
console.error(
|
|
4326
|
+
chalk13.red(`\u2717 OPENAI_API_KEY is not set. Required for --agent codex.`)
|
|
4327
|
+
);
|
|
3885
4328
|
process.exit(1);
|
|
3886
4329
|
}
|
|
3887
4330
|
}
|
|
3888
|
-
if (process.env.GIT_AUTHOR_NAME)
|
|
3889
|
-
|
|
4331
|
+
if (process.env.GIT_AUTHOR_NAME)
|
|
4332
|
+
creds.GIT_AUTHOR_NAME = process.env.GIT_AUTHOR_NAME;
|
|
4333
|
+
if (process.env.GIT_AUTHOR_EMAIL)
|
|
4334
|
+
creds.GIT_AUTHOR_EMAIL = process.env.GIT_AUTHOR_EMAIL;
|
|
3890
4335
|
return creds;
|
|
3891
4336
|
}
|
|
3892
4337
|
function sanitizeBranchSegment(value) {
|
|
@@ -3900,7 +4345,10 @@ async function resolveGitRemote(configService) {
|
|
|
3900
4345
|
const linkedRemote = (await configService.getLinkedRemoteName())?.trim();
|
|
3901
4346
|
const preferredRemote = linkedRemote || "origin";
|
|
3902
4347
|
try {
|
|
3903
|
-
return {
|
|
4348
|
+
return {
|
|
4349
|
+
name: preferredRemote,
|
|
4350
|
+
url: runGit(["remote", "get-url", preferredRemote])
|
|
4351
|
+
};
|
|
3904
4352
|
} catch {
|
|
3905
4353
|
if (preferredRemote !== "origin") {
|
|
3906
4354
|
try {
|
|
@@ -3934,14 +4382,14 @@ function getCommitHashesSince(baseHash) {
|
|
|
3934
4382
|
const output = runGit(["rev-list", `${baseHash}..HEAD`]);
|
|
3935
4383
|
return output.split("\n").map((entry) => entry.trim()).filter(Boolean);
|
|
3936
4384
|
}
|
|
3937
|
-
async function apiRequest(configService, apiKey,
|
|
4385
|
+
async function apiRequest(configService, apiKey, path4, init) {
|
|
3938
4386
|
const headers = {
|
|
3939
4387
|
Authorization: `Bearer ${apiKey}`,
|
|
3940
4388
|
"Content-Type": "application/json",
|
|
3941
4389
|
...await buildDeviceHeaders(configService),
|
|
3942
4390
|
...init?.headers ?? {}
|
|
3943
4391
|
};
|
|
3944
|
-
return fetch(`${API_URL}${
|
|
4392
|
+
return fetch(`${API_URL}${path4}`, { ...init, headers });
|
|
3945
4393
|
}
|
|
3946
4394
|
async function appendRunLogs(configService, apiKey, runId, entries) {
|
|
3947
4395
|
if (entries.length === 0) return;
|
|
@@ -3951,23 +4399,33 @@ async function appendRunLogs(configService, apiKey, runId, entries) {
|
|
|
3951
4399
|
});
|
|
3952
4400
|
}
|
|
3953
4401
|
async function sendRunnerHeartbeat(configService, apiKey, projectId, status, currentTaskRunId, capabilities) {
|
|
3954
|
-
await apiRequest(
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
4402
|
+
await apiRequest(
|
|
4403
|
+
configService,
|
|
4404
|
+
apiKey,
|
|
4405
|
+
`/projects/${projectId}/runners/heartbeat`,
|
|
4406
|
+
{
|
|
4407
|
+
method: "POST",
|
|
4408
|
+
body: JSON.stringify({
|
|
4409
|
+
status,
|
|
4410
|
+
current_task_run_id: currentTaskRunId,
|
|
4411
|
+
capabilities
|
|
4412
|
+
})
|
|
4413
|
+
}
|
|
4414
|
+
);
|
|
3962
4415
|
}
|
|
3963
4416
|
async function completeTaskRunWithRetry(configService, apiKey, runId, payload, attempts = 5) {
|
|
3964
4417
|
let lastError = "unknown error";
|
|
3965
4418
|
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
3966
4419
|
try {
|
|
3967
|
-
const response = await apiRequest(
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
4420
|
+
const response = await apiRequest(
|
|
4421
|
+
configService,
|
|
4422
|
+
apiKey,
|
|
4423
|
+
`/task-runs/${runId}/complete`,
|
|
4424
|
+
{
|
|
4425
|
+
method: "POST",
|
|
4426
|
+
body: JSON.stringify(payload)
|
|
4427
|
+
}
|
|
4428
|
+
);
|
|
3971
4429
|
if (response.ok) return;
|
|
3972
4430
|
const bodyText = await response.text().catch(() => "");
|
|
3973
4431
|
lastError = `HTTP ${response.status}${bodyText ? `: ${bodyText}` : ""}`;
|
|
@@ -3981,8 +4439,16 @@ async function completeTaskRunWithRetry(configService, apiKey, runId, payload, a
|
|
|
3981
4439
|
throw new Error(`Failed to complete run ${runId}: ${lastError}`);
|
|
3982
4440
|
}
|
|
3983
4441
|
async function executeClaimedRun(input) {
|
|
3984
|
-
const {
|
|
3985
|
-
|
|
4442
|
+
const {
|
|
4443
|
+
configService,
|
|
4444
|
+
apiKey,
|
|
4445
|
+
projectId,
|
|
4446
|
+
agent,
|
|
4447
|
+
useSandbox,
|
|
4448
|
+
agentPinned,
|
|
4449
|
+
run
|
|
4450
|
+
} = input;
|
|
4451
|
+
const repoRoot = getRepoRoot3();
|
|
3986
4452
|
let sequence = 1;
|
|
3987
4453
|
let heartbeatTimer = null;
|
|
3988
4454
|
let cancellationRequested = false;
|
|
@@ -4008,7 +4474,11 @@ async function executeClaimedRun(input) {
|
|
|
4008
4474
|
} catch {
|
|
4009
4475
|
originalBranch = null;
|
|
4010
4476
|
}
|
|
4011
|
-
const preparedBranch = prepareTaskBranch(
|
|
4477
|
+
const preparedBranch = prepareTaskBranch(
|
|
4478
|
+
run.task_external_id,
|
|
4479
|
+
baseBranch,
|
|
4480
|
+
remote.name
|
|
4481
|
+
);
|
|
4012
4482
|
baseHash = preparedBranch.baseHash;
|
|
4013
4483
|
branchName = preparedBranch.branchName;
|
|
4014
4484
|
await appendRunLogs(configService, apiKey, run.id, [
|
|
@@ -4021,7 +4491,14 @@ async function executeClaimedRun(input) {
|
|
|
4021
4491
|
]);
|
|
4022
4492
|
const child = spawn3(
|
|
4023
4493
|
process.execPath,
|
|
4024
|
-
[
|
|
4494
|
+
[
|
|
4495
|
+
getCliEntrypoint(),
|
|
4496
|
+
"agent",
|
|
4497
|
+
agent,
|
|
4498
|
+
"--task",
|
|
4499
|
+
run.task_external_id,
|
|
4500
|
+
"--auto-exit"
|
|
4501
|
+
],
|
|
4025
4502
|
{
|
|
4026
4503
|
env: {
|
|
4027
4504
|
...process.env,
|
|
@@ -4091,15 +4568,13 @@ async function executeClaimedRun(input) {
|
|
|
4091
4568
|
{ sequence: sequence++, stream: "stderr", chunk: text }
|
|
4092
4569
|
]);
|
|
4093
4570
|
});
|
|
4094
|
-
const result = await new Promise(
|
|
4095
|
-
(
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
}
|
|
4102
|
-
);
|
|
4571
|
+
const result = await new Promise((resolve3) => {
|
|
4572
|
+
child.on("exit", (code, signal) => resolve3({ code, signal }));
|
|
4573
|
+
child.on("error", (error) => {
|
|
4574
|
+
completionError = error.message;
|
|
4575
|
+
resolve3({ code: null, signal: null });
|
|
4576
|
+
});
|
|
4577
|
+
});
|
|
4103
4578
|
exitCode = result.code;
|
|
4104
4579
|
if (completionError) {
|
|
4105
4580
|
completionStatus = cancellationRequested ? "cancelled" : "failed";
|
|
@@ -4121,7 +4596,11 @@ async function executeClaimedRun(input) {
|
|
|
4121
4596
|
if (completionStatus === "completed" && hasDirtyWorktree()) {
|
|
4122
4597
|
runGit(["add", "-A"], { stdio: "inherit" });
|
|
4123
4598
|
runGit(
|
|
4124
|
-
[
|
|
4599
|
+
[
|
|
4600
|
+
"commit",
|
|
4601
|
+
"-m",
|
|
4602
|
+
`chore(${run.task_external_id}): apply agent changes`
|
|
4603
|
+
],
|
|
4125
4604
|
{ stdio: "inherit" }
|
|
4126
4605
|
);
|
|
4127
4606
|
}
|
|
@@ -4182,7 +4661,7 @@ ${run.user_prompt.trim()}` : "Triggered from VEM web.",
|
|
|
4182
4661
|
}
|
|
4183
4662
|
async function executeClaimedRunInSandbox(input) {
|
|
4184
4663
|
const { configService, apiKey, projectId, agent, run, credentials } = input;
|
|
4185
|
-
const repoRoot =
|
|
4664
|
+
const repoRoot = getRepoRoot3();
|
|
4186
4665
|
let sequence = 1;
|
|
4187
4666
|
let heartbeatTimer = null;
|
|
4188
4667
|
let worktreePath = null;
|
|
@@ -4216,16 +4695,20 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4216
4695
|
if (existsSync(worktreePath)) {
|
|
4217
4696
|
execFileSync("rm", ["-rf", worktreePath], { stdio: "ignore" });
|
|
4218
4697
|
}
|
|
4219
|
-
console.log(
|
|
4220
|
-
execFileSync(
|
|
4221
|
-
"
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4698
|
+
console.log(chalk13.gray(` Cloning ${baseBranch} \u2192 ${worktreePath}`));
|
|
4699
|
+
execFileSync(
|
|
4700
|
+
"git",
|
|
4701
|
+
[
|
|
4702
|
+
"clone",
|
|
4703
|
+
"--quiet",
|
|
4704
|
+
`file://${repoRoot}`,
|
|
4705
|
+
"--branch",
|
|
4706
|
+
baseBranch,
|
|
4707
|
+
"--single-branch",
|
|
4708
|
+
worktreePath
|
|
4709
|
+
],
|
|
4710
|
+
{ stdio: "pipe" }
|
|
4711
|
+
);
|
|
4229
4712
|
runGitIn(worktreePath, ["checkout", "-b", branchName]);
|
|
4230
4713
|
if (remoteUrl) {
|
|
4231
4714
|
runGitIn(worktreePath, ["remote", "set-url", "origin", remoteUrl]);
|
|
@@ -4292,7 +4775,9 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4292
4775
|
if (dockerProcess?.pid) {
|
|
4293
4776
|
try {
|
|
4294
4777
|
if (containerName) {
|
|
4295
|
-
execFileSync("docker", ["stop", containerName], {
|
|
4778
|
+
execFileSync("docker", ["stop", containerName], {
|
|
4779
|
+
stdio: "ignore"
|
|
4780
|
+
});
|
|
4296
4781
|
} else {
|
|
4297
4782
|
dockerProcess.kill("SIGTERM");
|
|
4298
4783
|
}
|
|
@@ -4300,7 +4785,11 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4300
4785
|
}
|
|
4301
4786
|
}
|
|
4302
4787
|
await appendRunLogs(configService, apiKey, run.id, [
|
|
4303
|
-
{
|
|
4788
|
+
{
|
|
4789
|
+
sequence: sequence++,
|
|
4790
|
+
stream: "system",
|
|
4791
|
+
chunk: "Cancellation requested from web UI. Stopping sandbox container.\n"
|
|
4792
|
+
}
|
|
4304
4793
|
]);
|
|
4305
4794
|
}
|
|
4306
4795
|
if (maxRuntimeAt && !timedOut) {
|
|
@@ -4312,13 +4801,19 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4312
4801
|
if (dockerProcess?.pid) {
|
|
4313
4802
|
try {
|
|
4314
4803
|
if (containerName) {
|
|
4315
|
-
execFileSync("docker", ["stop", containerName], {
|
|
4804
|
+
execFileSync("docker", ["stop", containerName], {
|
|
4805
|
+
stdio: "ignore"
|
|
4806
|
+
});
|
|
4316
4807
|
}
|
|
4317
4808
|
} catch {
|
|
4318
4809
|
}
|
|
4319
4810
|
}
|
|
4320
4811
|
await appendRunLogs(configService, apiKey, run.id, [
|
|
4321
|
-
{
|
|
4812
|
+
{
|
|
4813
|
+
sequence: sequence++,
|
|
4814
|
+
stream: "system",
|
|
4815
|
+
chunk: "Run exceeded the maximum runtime. Stopping sandbox container.\n"
|
|
4816
|
+
}
|
|
4322
4817
|
]);
|
|
4323
4818
|
}
|
|
4324
4819
|
}
|
|
@@ -4335,9 +4830,9 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4335
4830
|
};
|
|
4336
4831
|
dockerProcess.stdout?.on("data", (d) => streamLogs("stdout", d));
|
|
4337
4832
|
dockerProcess.stderr?.on("data", (d) => streamLogs("stderr", d));
|
|
4338
|
-
exitCode = await new Promise((
|
|
4339
|
-
dockerProcess.once("exit", (code) =>
|
|
4340
|
-
dockerProcess.once("error", () =>
|
|
4833
|
+
exitCode = await new Promise((resolve3) => {
|
|
4834
|
+
dockerProcess.once("exit", (code) => resolve3(code ?? 1));
|
|
4835
|
+
dockerProcess.once("error", () => resolve3(1));
|
|
4341
4836
|
});
|
|
4342
4837
|
if (heartbeatTimer) {
|
|
4343
4838
|
clearInterval(heartbeatTimer);
|
|
@@ -4346,7 +4841,10 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4346
4841
|
if (exitCode === 0 && !cancellationRequested && !timedOut) {
|
|
4347
4842
|
completionStatus = "completed";
|
|
4348
4843
|
try {
|
|
4349
|
-
const output = runGitIn(worktreePath, [
|
|
4844
|
+
const output = runGitIn(worktreePath, [
|
|
4845
|
+
"rev-list",
|
|
4846
|
+
`${baseHash}..HEAD`
|
|
4847
|
+
]);
|
|
4350
4848
|
commitHashes = output.split("\n").map((h) => h.trim()).filter(Boolean);
|
|
4351
4849
|
} catch {
|
|
4352
4850
|
}
|
|
@@ -4356,16 +4854,26 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4356
4854
|
}
|
|
4357
4855
|
if (completionStatus === "completed" && commitHashes.length > 0) {
|
|
4358
4856
|
try {
|
|
4359
|
-
runGitIn(worktreePath, ["push", "-u", "origin", branchName], {
|
|
4857
|
+
runGitIn(worktreePath, ["push", "-u", "origin", branchName], {
|
|
4858
|
+
stdio: "inherit"
|
|
4859
|
+
});
|
|
4360
4860
|
await appendRunLogs(configService, apiKey, run.id, [
|
|
4361
|
-
{
|
|
4362
|
-
|
|
4861
|
+
{
|
|
4862
|
+
sequence: sequence++,
|
|
4863
|
+
stream: "system",
|
|
4864
|
+
chunk: `Pushed branch ${branchName} to ${remote.name}.
|
|
4865
|
+
`
|
|
4866
|
+
}
|
|
4363
4867
|
]);
|
|
4364
4868
|
} catch (pushErr) {
|
|
4365
4869
|
const msg = pushErr instanceof Error ? pushErr.message : String(pushErr);
|
|
4366
4870
|
await appendRunLogs(configService, apiKey, run.id, [
|
|
4367
|
-
{
|
|
4368
|
-
|
|
4871
|
+
{
|
|
4872
|
+
sequence: sequence++,
|
|
4873
|
+
stream: "system",
|
|
4874
|
+
chunk: `Warning: failed to push branch: ${msg}
|
|
4875
|
+
`
|
|
4876
|
+
}
|
|
4369
4877
|
]);
|
|
4370
4878
|
createPr = false;
|
|
4371
4879
|
}
|
|
@@ -4379,8 +4887,12 @@ async function executeClaimedRunInSandbox(input) {
|
|
|
4379
4887
|
heartbeatTimer = null;
|
|
4380
4888
|
}
|
|
4381
4889
|
await appendRunLogs(configService, apiKey, run.id, [
|
|
4382
|
-
{
|
|
4383
|
-
|
|
4890
|
+
{
|
|
4891
|
+
sequence: sequence++,
|
|
4892
|
+
stream: "system",
|
|
4893
|
+
chunk: `Sandbox run error: ${msg}
|
|
4894
|
+
`
|
|
4895
|
+
}
|
|
4384
4896
|
]).catch(() => {
|
|
4385
4897
|
});
|
|
4386
4898
|
} finally {
|
|
@@ -4421,14 +4933,27 @@ ${run.user_prompt.trim()}` : "Triggered from VEM web."
|
|
|
4421
4933
|
}
|
|
4422
4934
|
async function appendTerminalLogs(configService, apiKey, sessionId, entries) {
|
|
4423
4935
|
if (entries.length === 0) return;
|
|
4424
|
-
await apiRequest(
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4936
|
+
await apiRequest(
|
|
4937
|
+
configService,
|
|
4938
|
+
apiKey,
|
|
4939
|
+
`/terminal-sessions/${sessionId}/logs`,
|
|
4940
|
+
{
|
|
4941
|
+
method: "POST",
|
|
4942
|
+
body: JSON.stringify({ entries })
|
|
4943
|
+
}
|
|
4944
|
+
);
|
|
4428
4945
|
}
|
|
4429
4946
|
async function executeClaimedTerminalSession(input) {
|
|
4430
|
-
const {
|
|
4431
|
-
|
|
4947
|
+
const {
|
|
4948
|
+
configService,
|
|
4949
|
+
apiKey,
|
|
4950
|
+
projectId,
|
|
4951
|
+
agent,
|
|
4952
|
+
useSandbox,
|
|
4953
|
+
agentPinned,
|
|
4954
|
+
session
|
|
4955
|
+
} = input;
|
|
4956
|
+
const repoRoot = getRepoRoot3();
|
|
4432
4957
|
let sequence = 2;
|
|
4433
4958
|
let heartbeatTimer = null;
|
|
4434
4959
|
let completionStatus = "failed";
|
|
@@ -4487,15 +5012,13 @@ $ ${session.command}
|
|
|
4487
5012
|
{ sequence: sequence++, stream: "stderr", chunk: text }
|
|
4488
5013
|
]);
|
|
4489
5014
|
});
|
|
4490
|
-
const result = await new Promise(
|
|
4491
|
-
(
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
}
|
|
4498
|
-
);
|
|
5015
|
+
const result = await new Promise((resolve3) => {
|
|
5016
|
+
child.on("exit", (code, signal) => resolve3({ code, signal }));
|
|
5017
|
+
child.on("error", (error) => {
|
|
5018
|
+
completionError = error.message;
|
|
5019
|
+
resolve3({ code: null, signal: null });
|
|
5020
|
+
});
|
|
5021
|
+
});
|
|
4499
5022
|
exitCode = result.code;
|
|
4500
5023
|
if (completionError) {
|
|
4501
5024
|
completionStatus = cancellationRequested ? "cancelled" : "failed";
|
|
@@ -4525,15 +5048,20 @@ $ ${session.command}
|
|
|
4525
5048
|
if (heartbeatTimer) {
|
|
4526
5049
|
clearInterval(heartbeatTimer);
|
|
4527
5050
|
}
|
|
4528
|
-
await apiRequest(
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
5051
|
+
await apiRequest(
|
|
5052
|
+
configService,
|
|
5053
|
+
apiKey,
|
|
5054
|
+
`/terminal-sessions/${session.id}/complete`,
|
|
5055
|
+
{
|
|
5056
|
+
method: "POST",
|
|
5057
|
+
body: JSON.stringify({
|
|
5058
|
+
status: completionStatus,
|
|
5059
|
+
exit_code: exitCode,
|
|
5060
|
+
error_message: completionError,
|
|
5061
|
+
terminal_reason: completionStatus === "cancelled" ? "Command cancelled from workspace UI." : null
|
|
5062
|
+
})
|
|
5063
|
+
}
|
|
5064
|
+
);
|
|
4537
5065
|
await sendRunnerHeartbeat(
|
|
4538
5066
|
configService,
|
|
4539
5067
|
apiKey,
|
|
@@ -4545,7 +5073,14 @@ $ ${session.command}
|
|
|
4545
5073
|
}
|
|
4546
5074
|
}
|
|
4547
5075
|
function registerRunnerCommands(program2) {
|
|
4548
|
-
program2.command("runner").description("Run a paired worker that executes queued web task runs").option(
|
|
5076
|
+
program2.command("runner").description("Run a paired worker that executes queued web task runs").option(
|
|
5077
|
+
"--agent <command>",
|
|
5078
|
+
"Agent command to launch for claimed tasks",
|
|
5079
|
+
"copilot"
|
|
5080
|
+
).option("--poll-interval <seconds>", "Polling interval in seconds", "10").option("--once", "Claim at most one run and then exit").option(
|
|
5081
|
+
"--unsafe",
|
|
5082
|
+
"Disable Docker sandbox (run agent directly on host \u2014 no isolation)"
|
|
5083
|
+
).action(async (options, command) => {
|
|
4549
5084
|
const configService = new ConfigService();
|
|
4550
5085
|
const apiKey = await ensureAuthenticated(configService);
|
|
4551
5086
|
const projectId = await configService.getProjectId();
|
|
@@ -4566,12 +5101,16 @@ function registerRunnerCommands(program2) {
|
|
|
4566
5101
|
const agentPinned = optionSource === "cli";
|
|
4567
5102
|
const modeLabel = useSandbox ? "sandbox (Docker)" : "unsafe (direct)";
|
|
4568
5103
|
console.log(
|
|
4569
|
-
|
|
5104
|
+
chalk13.cyan(
|
|
4570
5105
|
`Starting paired runner for project ${projectId} using agent "${agent}" [${modeLabel}]...`
|
|
4571
5106
|
)
|
|
4572
5107
|
);
|
|
4573
5108
|
if (!useSandbox) {
|
|
4574
|
-
console.log(
|
|
5109
|
+
console.log(
|
|
5110
|
+
chalk13.yellow(
|
|
5111
|
+
" \u26A0 Running in unsafe mode \u2014 agent has full host access."
|
|
5112
|
+
)
|
|
5113
|
+
);
|
|
4575
5114
|
}
|
|
4576
5115
|
let shouldStop = false;
|
|
4577
5116
|
let consecutiveErrors = 0;
|
|
@@ -4584,7 +5123,11 @@ function registerRunnerCommands(program2) {
|
|
|
4584
5123
|
const claimBackend = useSandbox ? "local_sandbox" : "local_runner";
|
|
4585
5124
|
while (!shouldStop) {
|
|
4586
5125
|
try {
|
|
4587
|
-
const capabilities = getRunnerCapabilities(
|
|
5126
|
+
const capabilities = getRunnerCapabilities(
|
|
5127
|
+
agent,
|
|
5128
|
+
useSandbox,
|
|
5129
|
+
agentPinned
|
|
5130
|
+
);
|
|
4588
5131
|
await sendRunnerHeartbeat(
|
|
4589
5132
|
configService,
|
|
4590
5133
|
apiKey,
|
|
@@ -4608,7 +5151,9 @@ function registerRunnerCommands(program2) {
|
|
|
4608
5151
|
);
|
|
4609
5152
|
if (!claimResponse.ok) {
|
|
4610
5153
|
const data = await claimResponse.json().catch(() => ({}));
|
|
4611
|
-
throw new Error(
|
|
5154
|
+
throw new Error(
|
|
5155
|
+
data.error || "Failed to claim task run"
|
|
5156
|
+
);
|
|
4612
5157
|
}
|
|
4613
5158
|
const payload = await claimResponse.json();
|
|
4614
5159
|
if (payload.run) {
|
|
@@ -4646,7 +5191,9 @@ function registerRunnerCommands(program2) {
|
|
|
4646
5191
|
);
|
|
4647
5192
|
if (!terminalClaimResponse.ok) {
|
|
4648
5193
|
const data = await terminalClaimResponse.json().catch(() => ({}));
|
|
4649
|
-
throw new Error(
|
|
5194
|
+
throw new Error(
|
|
5195
|
+
data.error || "Failed to claim terminal session"
|
|
5196
|
+
);
|
|
4650
5197
|
}
|
|
4651
5198
|
const terminalPayload = await terminalClaimResponse.json();
|
|
4652
5199
|
if (terminalPayload.session) {
|
|
@@ -4689,14 +5236,14 @@ function registerRunnerCommands(program2) {
|
|
|
4689
5236
|
}
|
|
4690
5237
|
|
|
4691
5238
|
// src/commands/search.ts
|
|
4692
|
-
import
|
|
5239
|
+
import chalk14 from "chalk";
|
|
4693
5240
|
function registerSearchCommands(program2) {
|
|
4694
5241
|
program2.command("search <query>").description("Search project memory (tasks, context, decisions)").action(async (query) => {
|
|
4695
5242
|
await trackCommandUsage("search");
|
|
4696
5243
|
try {
|
|
4697
5244
|
const configService = new ConfigService();
|
|
4698
5245
|
const key = await ensureAuthenticated(configService);
|
|
4699
|
-
console.log(
|
|
5246
|
+
console.log(chalk14.blue(`\u{1F50D} Searching for "${query}"...`));
|
|
4700
5247
|
const res = await fetch(
|
|
4701
5248
|
`${API_URL}/search?q=${encodeURIComponent(query)}`,
|
|
4702
5249
|
{
|
|
@@ -4709,14 +5256,14 @@ function registerSearchCommands(program2) {
|
|
|
4709
5256
|
if (!res.ok) {
|
|
4710
5257
|
if (res.status === 401) {
|
|
4711
5258
|
console.error(
|
|
4712
|
-
|
|
5259
|
+
chalk14.red("Error: Unauthorized. Your API Key is invalid.")
|
|
4713
5260
|
);
|
|
4714
5261
|
return;
|
|
4715
5262
|
}
|
|
4716
5263
|
if (res.status === 403) {
|
|
4717
5264
|
const errorData = await res.json().catch(() => ({}));
|
|
4718
5265
|
console.error(
|
|
4719
|
-
|
|
5266
|
+
chalk14.red(
|
|
4720
5267
|
errorData.error || "Device limit reached. Disconnect a device or upgrade your plan."
|
|
4721
5268
|
)
|
|
4722
5269
|
);
|
|
@@ -4727,36 +5274,36 @@ function registerSearchCommands(program2) {
|
|
|
4727
5274
|
}
|
|
4728
5275
|
const data = await res.json();
|
|
4729
5276
|
if (!data.results || data.results.length === 0) {
|
|
4730
|
-
console.log(
|
|
5277
|
+
console.log(chalk14.yellow("No results found."));
|
|
4731
5278
|
return;
|
|
4732
5279
|
}
|
|
4733
|
-
console.log(
|
|
5280
|
+
console.log(chalk14.green(`
|
|
4734
5281
|
Found ${data.results.length} results:
|
|
4735
5282
|
`));
|
|
4736
5283
|
data.results.forEach((item, i) => {
|
|
4737
|
-
const typeLabel =
|
|
5284
|
+
const typeLabel = chalk14.gray(
|
|
4738
5285
|
`[${item.type?.toUpperCase() || "UNKNOWN"}]`
|
|
4739
5286
|
);
|
|
4740
5287
|
console.log(
|
|
4741
|
-
`${i + 1}. ${typeLabel} ${
|
|
5288
|
+
`${i + 1}. ${typeLabel} ${chalk14.bold(item.title || "Untitled")}`
|
|
4742
5289
|
);
|
|
4743
5290
|
if (item.content) {
|
|
4744
5291
|
console.log(
|
|
4745
|
-
|
|
5292
|
+
chalk14.gray(
|
|
4746
5293
|
` ${item.content.substring(0, 100).replace(/\n/g, " ")}...`
|
|
4747
5294
|
)
|
|
4748
5295
|
);
|
|
4749
5296
|
}
|
|
4750
5297
|
if (item.score) {
|
|
4751
|
-
console.log(
|
|
5298
|
+
console.log(chalk14.gray(` Score: ${item.score.toFixed(2)}`));
|
|
4752
5299
|
}
|
|
4753
5300
|
console.log("");
|
|
4754
5301
|
});
|
|
4755
5302
|
} catch (error) {
|
|
4756
5303
|
if (error instanceof Error) {
|
|
4757
|
-
console.error(
|
|
5304
|
+
console.error(chalk14.red("\n\u2716 Search Failed:"), error.message);
|
|
4758
5305
|
} else {
|
|
4759
|
-
console.error(
|
|
5306
|
+
console.error(chalk14.red("\n\u2716 Search Failed:"), String(error));
|
|
4760
5307
|
}
|
|
4761
5308
|
}
|
|
4762
5309
|
});
|
|
@@ -4765,7 +5312,7 @@ Found ${data.results.length} results:
|
|
|
4765
5312
|
try {
|
|
4766
5313
|
const cleanedQuestion = typeof question === "string" ? question.trim() : "";
|
|
4767
5314
|
if (!cleanedQuestion) {
|
|
4768
|
-
console.error(
|
|
5315
|
+
console.error(chalk14.red("\n\u2716 Question is required.\n"));
|
|
4769
5316
|
return;
|
|
4770
5317
|
}
|
|
4771
5318
|
const configService = new ConfigService();
|
|
@@ -4773,11 +5320,11 @@ Found ${data.results.length} results:
|
|
|
4773
5320
|
const projectId = await configService.getProjectId();
|
|
4774
5321
|
if (!projectId) {
|
|
4775
5322
|
console.error(
|
|
4776
|
-
|
|
5323
|
+
chalk14.red("\n\u2716 Project not linked. Run `vem link` first.\n")
|
|
4777
5324
|
);
|
|
4778
5325
|
return;
|
|
4779
5326
|
}
|
|
4780
|
-
console.log(
|
|
5327
|
+
console.log(chalk14.blue(`Asking: "${cleanedQuestion}"...`));
|
|
4781
5328
|
const payload = {
|
|
4782
5329
|
question: cleanedQuestion
|
|
4783
5330
|
};
|
|
@@ -4803,14 +5350,14 @@ Found ${data.results.length} results:
|
|
|
4803
5350
|
}
|
|
4804
5351
|
const data = await res.json();
|
|
4805
5352
|
if (data.answer) {
|
|
4806
|
-
console.log(
|
|
5353
|
+
console.log(chalk14.green("\nAnswer:\n"));
|
|
4807
5354
|
console.log(data.answer.trim());
|
|
4808
5355
|
} else {
|
|
4809
|
-
console.log(
|
|
5356
|
+
console.log(chalk14.yellow("\nNo answer generated."));
|
|
4810
5357
|
}
|
|
4811
5358
|
const repoUrl = await getGitRemote();
|
|
4812
5359
|
if (data.citations && data.citations.length > 0) {
|
|
4813
|
-
console.log(
|
|
5360
|
+
console.log(chalk14.green("\nCitations:"));
|
|
4814
5361
|
data.citations.forEach((cite, idx) => {
|
|
4815
5362
|
const source = data.sources?.find((s) => s.id === cite.id);
|
|
4816
5363
|
let label = cite.id;
|
|
@@ -4830,14 +5377,14 @@ Found ${data.results.length} results:
|
|
|
4830
5377
|
}
|
|
4831
5378
|
const note = cite.reason ? ` - ${cite.reason}` : "";
|
|
4832
5379
|
if (link) {
|
|
4833
|
-
console.log(
|
|
5380
|
+
console.log(chalk14.gray(`${idx + 1}. ${label} (${link})${note}`));
|
|
4834
5381
|
} else {
|
|
4835
|
-
console.log(
|
|
5382
|
+
console.log(chalk14.gray(`${idx + 1}. ${label}${note}`));
|
|
4836
5383
|
}
|
|
4837
5384
|
});
|
|
4838
5385
|
}
|
|
4839
5386
|
if (data.sources && data.sources.length > 0) {
|
|
4840
|
-
console.log(
|
|
5387
|
+
console.log(chalk14.green("\nSources:"));
|
|
4841
5388
|
data.sources.forEach((source, idx) => {
|
|
4842
5389
|
const details = [];
|
|
4843
5390
|
if (source.type) details.push(source.type.toUpperCase());
|
|
@@ -4846,28 +5393,28 @@ Found ${data.results.length} results:
|
|
|
4846
5393
|
details.push(source.commit_hash.slice(0, 7));
|
|
4847
5394
|
if (source.task_id) details.push(source.task_id);
|
|
4848
5395
|
const header = [source.id, ...details].filter(Boolean).join(" \u2022 ");
|
|
4849
|
-
console.log(
|
|
5396
|
+
console.log(chalk14.gray(`${idx + 1}. ${header || "SOURCE"}`));
|
|
4850
5397
|
if (source.title) {
|
|
4851
|
-
console.log(
|
|
5398
|
+
console.log(chalk14.gray(` ${source.title}`));
|
|
4852
5399
|
} else if (source.description) {
|
|
4853
|
-
console.log(
|
|
5400
|
+
console.log(chalk14.gray(` ${source.description}`));
|
|
4854
5401
|
}
|
|
4855
5402
|
});
|
|
4856
5403
|
}
|
|
4857
5404
|
} catch (error) {
|
|
4858
5405
|
if (error instanceof Error) {
|
|
4859
|
-
console.error(
|
|
5406
|
+
console.error(chalk14.red("\n\u2716 Ask Failed:"), error.message);
|
|
4860
5407
|
} else {
|
|
4861
|
-
console.error(
|
|
5408
|
+
console.error(chalk14.red("\n\u2716 Ask Failed:"), String(error));
|
|
4862
5409
|
}
|
|
4863
5410
|
}
|
|
4864
5411
|
});
|
|
4865
5412
|
}
|
|
4866
5413
|
|
|
4867
5414
|
// src/commands/sessions.ts
|
|
4868
|
-
import
|
|
5415
|
+
import chalk15 from "chalk";
|
|
4869
5416
|
import Table2 from "cli-table3";
|
|
4870
|
-
import
|
|
5417
|
+
import prompts7 from "prompts";
|
|
4871
5418
|
function formatDate(iso) {
|
|
4872
5419
|
if (!iso) return "\u2014";
|
|
4873
5420
|
const d = new Date(iso);
|
|
@@ -4906,22 +5453,22 @@ function registerSessionsCommands(program2) {
|
|
|
4906
5453
|
const limit = Number.parseInt(opts.limit, 10) || 20;
|
|
4907
5454
|
sessions = sessions.slice(0, limit);
|
|
4908
5455
|
if (sessions.length === 0) {
|
|
4909
|
-
console.log(
|
|
5456
|
+
console.log(chalk15.gray("No agent sessions found for this repository."));
|
|
4910
5457
|
return;
|
|
4911
5458
|
}
|
|
4912
5459
|
const sourceColor = (src) => {
|
|
4913
|
-
if (src === "copilot") return
|
|
4914
|
-
if (src === "claude") return
|
|
4915
|
-
if (src === "gemini") return
|
|
4916
|
-
return
|
|
5460
|
+
if (src === "copilot") return chalk15.blue(src);
|
|
5461
|
+
if (src === "claude") return chalk15.yellow(src);
|
|
5462
|
+
if (src === "gemini") return chalk15.cyan(src);
|
|
5463
|
+
return chalk15.gray(src);
|
|
4917
5464
|
};
|
|
4918
5465
|
const table = new Table2({
|
|
4919
5466
|
head: [
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
5467
|
+
chalk15.bold("Source"),
|
|
5468
|
+
chalk15.bold("ID"),
|
|
5469
|
+
chalk15.bold("Summary"),
|
|
5470
|
+
chalk15.bold("Branch"),
|
|
5471
|
+
chalk15.bold("Updated")
|
|
4925
5472
|
],
|
|
4926
5473
|
colWidths: [10, 12, 42, 18, 18],
|
|
4927
5474
|
style: { head: [], border: ["gray"] }
|
|
@@ -4929,17 +5476,17 @@ function registerSessionsCommands(program2) {
|
|
|
4929
5476
|
for (const s of sessions) {
|
|
4930
5477
|
table.push([
|
|
4931
5478
|
sourceColor(s.source),
|
|
4932
|
-
|
|
4933
|
-
s.summary ||
|
|
4934
|
-
|
|
4935
|
-
|
|
5479
|
+
chalk15.gray(`${s.id.slice(0, 8)}\u2026`),
|
|
5480
|
+
s.summary || chalk15.gray("(no summary)"),
|
|
5481
|
+
chalk15.cyan(s.branch || "\u2014"),
|
|
5482
|
+
chalk15.gray(formatDate(s.updated_at))
|
|
4936
5483
|
]);
|
|
4937
5484
|
}
|
|
4938
5485
|
console.log(table.toString());
|
|
4939
5486
|
console.log(
|
|
4940
|
-
|
|
5487
|
+
chalk15.gray(
|
|
4941
5488
|
`
|
|
4942
|
-
Showing ${sessions.length} session(s). Use ${
|
|
5489
|
+
Showing ${sessions.length} session(s). Use ${chalk15.white("vem sessions import <id>")} to import a session into project memory.`
|
|
4943
5490
|
)
|
|
4944
5491
|
);
|
|
4945
5492
|
});
|
|
@@ -4951,12 +5498,12 @@ Showing ${sessions.length} session(s). Use ${chalk14.white("vem sessions import
|
|
|
4951
5498
|
const all = await listAllAgentSessions(gitRoot);
|
|
4952
5499
|
const match = all.find((s) => s.id.startsWith(id));
|
|
4953
5500
|
if (!match) {
|
|
4954
|
-
console.error(
|
|
5501
|
+
console.error(chalk15.red(`No session found matching prefix: ${id}`));
|
|
4955
5502
|
process.exit(1);
|
|
4956
5503
|
}
|
|
4957
5504
|
session = match;
|
|
4958
5505
|
console.log(
|
|
4959
|
-
|
|
5506
|
+
chalk15.gray(`Resolved to ${match.source} session: ${match.id}`)
|
|
4960
5507
|
);
|
|
4961
5508
|
} else {
|
|
4962
5509
|
const all = await listAllAgentSessions(gitRoot);
|
|
@@ -4981,34 +5528,34 @@ Showing ${sessions.length} session(s). Use ${chalk14.white("vem sessions import
|
|
|
4981
5528
|
}
|
|
4982
5529
|
}
|
|
4983
5530
|
if (!session) {
|
|
4984
|
-
console.error(
|
|
5531
|
+
console.error(chalk15.red(`Session not found: ${id}`));
|
|
4985
5532
|
process.exit(1);
|
|
4986
5533
|
}
|
|
4987
|
-
console.log(
|
|
4988
|
-
console.log(
|
|
4989
|
-
console.log(
|
|
4990
|
-
console.log(
|
|
4991
|
-
console.log(
|
|
5534
|
+
console.log(chalk15.bold("\n\u{1F4CB} Session Summary"));
|
|
5535
|
+
console.log(chalk15.white(` ID: ${session.id}`));
|
|
5536
|
+
console.log(chalk15.white(` Source: ${session.source}`));
|
|
5537
|
+
console.log(chalk15.white(` Branch: ${session.branch || "\u2014"}`));
|
|
5538
|
+
console.log(chalk15.white(` Updated: ${formatDate(session.updated_at)}`));
|
|
4992
5539
|
console.log(
|
|
4993
|
-
|
|
5540
|
+
chalk15.white(` Summary: ${session.summary || "(no summary)"}`)
|
|
4994
5541
|
);
|
|
4995
5542
|
if (session.intents.length > 0) {
|
|
4996
|
-
console.log(
|
|
5543
|
+
console.log(chalk15.bold("\n\u{1F3AF} Intents recorded in this session:"));
|
|
4997
5544
|
for (const intent of session.intents) {
|
|
4998
|
-
console.log(
|
|
5545
|
+
console.log(chalk15.gray(` \u2022 ${intent}`));
|
|
4999
5546
|
}
|
|
5000
5547
|
}
|
|
5001
5548
|
if (session.user_messages.length > 0) {
|
|
5002
|
-
console.log(
|
|
5549
|
+
console.log(chalk15.bold("\n\u{1F4AC} First user message:"));
|
|
5003
5550
|
const preview = session.user_messages[0].slice(0, 200);
|
|
5004
5551
|
console.log(
|
|
5005
|
-
|
|
5552
|
+
chalk15.gray(
|
|
5006
5553
|
` ${preview}${session.user_messages[0].length > 200 ? "\u2026" : ""}`
|
|
5007
5554
|
)
|
|
5008
5555
|
);
|
|
5009
5556
|
}
|
|
5010
5557
|
console.log();
|
|
5011
|
-
const { addChangelog } = await
|
|
5558
|
+
const { addChangelog } = await prompts7({
|
|
5012
5559
|
type: "confirm",
|
|
5013
5560
|
name: "addChangelog",
|
|
5014
5561
|
message: "Add session summary as a changelog entry?",
|
|
@@ -5023,7 +5570,7 @@ Showing ${sessions.length} session(s). Use ${chalk14.white("vem sessions import
|
|
|
5023
5570
|
`- ${changelogEntry}`,
|
|
5024
5571
|
gitHash ? { commitHash: gitHash } : void 0
|
|
5025
5572
|
);
|
|
5026
|
-
console.log(
|
|
5573
|
+
console.log(chalk15.green("\u2713 Changelog entry added."));
|
|
5027
5574
|
}
|
|
5028
5575
|
const taskService2 = new TaskService();
|
|
5029
5576
|
const tasks = await taskService2.getTasks();
|
|
@@ -5031,14 +5578,14 @@ Showing ${sessions.length} session(s). Use ${chalk14.white("vem sessions import
|
|
|
5031
5578
|
(t) => !t.deleted_at && t.status !== "done"
|
|
5032
5579
|
);
|
|
5033
5580
|
if (activeTasks.length > 0) {
|
|
5034
|
-
const { linkTask } = await
|
|
5581
|
+
const { linkTask } = await prompts7({
|
|
5035
5582
|
type: "confirm",
|
|
5036
5583
|
name: "linkTask",
|
|
5037
5584
|
message: "Link this session to an active task (add evidence)?",
|
|
5038
5585
|
initial: false
|
|
5039
5586
|
});
|
|
5040
5587
|
if (linkTask) {
|
|
5041
|
-
const { taskId } = await
|
|
5588
|
+
const { taskId } = await prompts7({
|
|
5042
5589
|
type: "select",
|
|
5043
5590
|
name: "taskId",
|
|
5044
5591
|
message: "Which task?",
|
|
@@ -5065,20 +5612,20 @@ Showing ${sessions.length} session(s). Use ${chalk14.white("vem sessions import
|
|
|
5065
5612
|
...sessionRef ? { sessions: [...existingSessions, sessionRef] } : {}
|
|
5066
5613
|
});
|
|
5067
5614
|
console.log(
|
|
5068
|
-
|
|
5615
|
+
chalk15.green(`\u2713 Linked to task ${taskId} with evidence.`)
|
|
5069
5616
|
);
|
|
5070
5617
|
}
|
|
5071
5618
|
}
|
|
5072
5619
|
}
|
|
5073
|
-
console.log(
|
|
5620
|
+
console.log(chalk15.bold("\n\u2705 Done."));
|
|
5074
5621
|
});
|
|
5075
5622
|
}
|
|
5076
5623
|
|
|
5077
5624
|
// src/commands/setup.ts
|
|
5078
|
-
import
|
|
5079
|
-
import
|
|
5080
|
-
import
|
|
5081
|
-
import
|
|
5625
|
+
import path3 from "path";
|
|
5626
|
+
import chalk16 from "chalk";
|
|
5627
|
+
import fs4 from "fs-extra";
|
|
5628
|
+
import prompts8 from "prompts";
|
|
5082
5629
|
var COMMAND_BASELINE = [
|
|
5083
5630
|
"quickstart",
|
|
5084
5631
|
"agent",
|
|
@@ -5112,9 +5659,9 @@ All AI agents in this repository must use \`vem\` and follow the working rules.
|
|
|
5112
5659
|
`;
|
|
5113
5660
|
async function ensureVemGitignoreEntry() {
|
|
5114
5661
|
const repoRoot = await getRepoRoot();
|
|
5115
|
-
const gitignorePath =
|
|
5116
|
-
if (!await
|
|
5117
|
-
await
|
|
5662
|
+
const gitignorePath = path3.join(repoRoot, ".gitignore");
|
|
5663
|
+
if (!await fs4.pathExists(gitignorePath)) {
|
|
5664
|
+
await fs4.writeFile(
|
|
5118
5665
|
gitignorePath,
|
|
5119
5666
|
`${REQUIRED_GITIGNORE_ENTRIES.join("\n")}
|
|
5120
5667
|
`,
|
|
@@ -5122,7 +5669,7 @@ async function ensureVemGitignoreEntry() {
|
|
|
5122
5669
|
);
|
|
5123
5670
|
return;
|
|
5124
5671
|
}
|
|
5125
|
-
const content = await
|
|
5672
|
+
const content = await fs4.readFile(gitignorePath, "utf-8");
|
|
5126
5673
|
const entries = content.split(/\r?\n/).map((line) => line.trim());
|
|
5127
5674
|
const missingEntries = REQUIRED_GITIGNORE_ENTRIES.filter(
|
|
5128
5675
|
(entry) => !entries.includes(entry)
|
|
@@ -5131,7 +5678,7 @@ async function ensureVemGitignoreEntry() {
|
|
|
5131
5678
|
return;
|
|
5132
5679
|
}
|
|
5133
5680
|
const separator = content.endsWith("\n") ? "" : "\n";
|
|
5134
|
-
await
|
|
5681
|
+
await fs4.appendFile(
|
|
5135
5682
|
gitignorePath,
|
|
5136
5683
|
`${separator}${missingEntries.join("\n")}
|
|
5137
5684
|
`,
|
|
@@ -5142,15 +5689,15 @@ async function ensureAgentInstructionPolicy() {
|
|
|
5142
5689
|
const repoRoot = await getRepoRoot();
|
|
5143
5690
|
const existingFiles = [];
|
|
5144
5691
|
for (const file of KNOWN_AGENT_INSTRUCTION_FILES) {
|
|
5145
|
-
if (await
|
|
5692
|
+
if (await fs4.pathExists(path3.join(repoRoot, file))) {
|
|
5146
5693
|
existingFiles.push(file);
|
|
5147
5694
|
}
|
|
5148
5695
|
}
|
|
5149
5696
|
let createdAgentsFile = false;
|
|
5150
5697
|
let targets = [];
|
|
5151
5698
|
if (existingFiles.length === 0) {
|
|
5152
|
-
await
|
|
5153
|
-
|
|
5699
|
+
await fs4.writeFile(
|
|
5700
|
+
path3.join(repoRoot, "AGENTS.md"),
|
|
5154
5701
|
"# AGENTS\n\nThis repository uses `vem` for agent workflows.\n",
|
|
5155
5702
|
"utf-8"
|
|
5156
5703
|
);
|
|
@@ -5163,13 +5710,13 @@ async function ensureAgentInstructionPolicy() {
|
|
|
5163
5710
|
}
|
|
5164
5711
|
const updatedFiles = [];
|
|
5165
5712
|
for (const relativePath of targets) {
|
|
5166
|
-
const absolutePath =
|
|
5167
|
-
const content = await
|
|
5713
|
+
const absolutePath = path3.join(repoRoot, relativePath);
|
|
5714
|
+
const content = await fs4.readFile(absolutePath, "utf-8");
|
|
5168
5715
|
if (content.includes(VEM_AGENT_ENFORCEMENT_MARKER)) {
|
|
5169
5716
|
continue;
|
|
5170
5717
|
}
|
|
5171
5718
|
const separator = content.endsWith("\n") ? "" : "\n";
|
|
5172
|
-
await
|
|
5719
|
+
await fs4.appendFile(
|
|
5173
5720
|
absolutePath,
|
|
5174
5721
|
`${separator}
|
|
5175
5722
|
${VEM_AGENT_ENFORCEMENT_BLOCK}`,
|
|
@@ -5186,13 +5733,13 @@ async function collectAgentInstructionPayload() {
|
|
|
5186
5733
|
const repoRoot = await getRepoRoot();
|
|
5187
5734
|
const payload = [];
|
|
5188
5735
|
for (const relativePath of KNOWN_AGENT_INSTRUCTION_FILES) {
|
|
5189
|
-
const absolutePath =
|
|
5190
|
-
if (!await
|
|
5191
|
-
const stat2 = await
|
|
5736
|
+
const absolutePath = path3.join(repoRoot, relativePath);
|
|
5737
|
+
if (!await fs4.pathExists(absolutePath)) continue;
|
|
5738
|
+
const stat2 = await fs4.stat(absolutePath);
|
|
5192
5739
|
if (!stat2.isFile()) continue;
|
|
5193
5740
|
payload.push({
|
|
5194
5741
|
path: relativePath,
|
|
5195
|
-
content: await
|
|
5742
|
+
content: await fs4.readFile(absolutePath, "utf-8")
|
|
5196
5743
|
});
|
|
5197
5744
|
}
|
|
5198
5745
|
return payload;
|
|
@@ -5228,40 +5775,40 @@ var formatRelativeTime = (timestamp) => {
|
|
|
5228
5775
|
};
|
|
5229
5776
|
var renderUsageInsights = (stats, detailed = false) => {
|
|
5230
5777
|
const entries = getSortedCommandEntries(stats);
|
|
5231
|
-
console.log(
|
|
5778
|
+
console.log(chalk16.bold("\n\u{1F4C8} Command Insights\n"));
|
|
5232
5779
|
if (entries.length === 0) {
|
|
5233
|
-
console.log(
|
|
5234
|
-
console.log(
|
|
5780
|
+
console.log(chalk16.gray(" No command usage recorded yet."));
|
|
5781
|
+
console.log(chalk16.gray(" Start with: vem quickstart"));
|
|
5235
5782
|
return;
|
|
5236
5783
|
}
|
|
5237
5784
|
const rows = detailed ? entries : entries.slice(0, 6);
|
|
5238
|
-
console.log(
|
|
5785
|
+
console.log(chalk16.gray(` Commands tracked: ${entries.length}`));
|
|
5239
5786
|
rows.forEach(([command, count], index) => {
|
|
5240
5787
|
console.log(
|
|
5241
|
-
` ${
|
|
5788
|
+
` ${chalk16.gray(`${index + 1}.`)} ${chalk16.white(command)} ${chalk16.gray(`(${count})`)}`
|
|
5242
5789
|
);
|
|
5243
5790
|
});
|
|
5244
5791
|
if (!detailed && entries.length > rows.length) {
|
|
5245
|
-
console.log(
|
|
5792
|
+
console.log(chalk16.gray(` ...and ${entries.length - rows.length} more`));
|
|
5246
5793
|
}
|
|
5247
5794
|
const neverUsed = COMMAND_BASELINE.filter(
|
|
5248
5795
|
(command) => (stats.commandCounts[command] || 0) === 0
|
|
5249
5796
|
);
|
|
5250
5797
|
if (neverUsed.length > 0) {
|
|
5251
|
-
console.log(
|
|
5798
|
+
console.log(chalk16.gray("\n Suggested next commands:"));
|
|
5252
5799
|
neverUsed.slice(0, 3).forEach((command) => {
|
|
5253
|
-
console.log(` ${
|
|
5800
|
+
console.log(` ${chalk16.cyan(command)}`);
|
|
5254
5801
|
});
|
|
5255
5802
|
}
|
|
5256
5803
|
if (stats.lastPush) {
|
|
5257
5804
|
console.log(
|
|
5258
|
-
|
|
5805
|
+
chalk16.gray(`
|
|
5259
5806
|
Last push: ${formatRelativeTime(stats.lastPush)}`)
|
|
5260
5807
|
);
|
|
5261
5808
|
}
|
|
5262
5809
|
if (stats.lastAgentRun) {
|
|
5263
5810
|
console.log(
|
|
5264
|
-
|
|
5811
|
+
chalk16.gray(
|
|
5265
5812
|
` Last agent session: ${formatRelativeTime(stats.lastAgentRun)}`
|
|
5266
5813
|
)
|
|
5267
5814
|
);
|
|
@@ -5272,18 +5819,18 @@ function registerSetupCommands(program2) {
|
|
|
5272
5819
|
try {
|
|
5273
5820
|
if (await hasUncommittedChanges()) {
|
|
5274
5821
|
console.log(
|
|
5275
|
-
|
|
5822
|
+
chalk16.yellow(
|
|
5276
5823
|
"\n\u26A0 Uncommitted changes detected in this workspace.\n"
|
|
5277
5824
|
)
|
|
5278
5825
|
);
|
|
5279
|
-
const proceed = await
|
|
5826
|
+
const proceed = await prompts8({
|
|
5280
5827
|
type: "confirm",
|
|
5281
5828
|
name: "confirmInit",
|
|
5282
5829
|
message: "Continue with `vem init` anyway?",
|
|
5283
5830
|
initial: false
|
|
5284
5831
|
});
|
|
5285
5832
|
if (!proceed.confirmInit) {
|
|
5286
|
-
console.log(
|
|
5833
|
+
console.log(chalk16.yellow("Initialization cancelled.\n"));
|
|
5287
5834
|
return;
|
|
5288
5835
|
}
|
|
5289
5836
|
}
|
|
@@ -5294,19 +5841,19 @@ function registerSetupCommands(program2) {
|
|
|
5294
5841
|
const initHash = await computeVemHash();
|
|
5295
5842
|
await configService.setLastSyncedVemHash(initHash);
|
|
5296
5843
|
const agentInstructions = await ensureAgentInstructionPolicy();
|
|
5297
|
-
console.log(
|
|
5844
|
+
console.log(chalk16.green(`
|
|
5298
5845
|
\u2714 vem initialized at ${dir}
|
|
5299
5846
|
`));
|
|
5300
5847
|
if (agentInstructions.createdAgentsFile) {
|
|
5301
5848
|
console.log(
|
|
5302
|
-
|
|
5849
|
+
chalk16.gray(
|
|
5303
5850
|
"Created AGENTS.md because no agent instruction files were found."
|
|
5304
5851
|
)
|
|
5305
5852
|
);
|
|
5306
5853
|
}
|
|
5307
5854
|
if (agentInstructions.updatedFiles.length > 0) {
|
|
5308
5855
|
console.log(
|
|
5309
|
-
|
|
5856
|
+
chalk16.gray(
|
|
5310
5857
|
`Updated agent instructions: ${agentInstructions.updatedFiles.join(", ")}`
|
|
5311
5858
|
)
|
|
5312
5859
|
);
|
|
@@ -5316,7 +5863,7 @@ function registerSetupCommands(program2) {
|
|
|
5316
5863
|
const apiKey = await tryAuthenticatedKey(configService);
|
|
5317
5864
|
let resolvedProjectId = projectId;
|
|
5318
5865
|
if (apiKey && !projectId) {
|
|
5319
|
-
const { doLink } = await
|
|
5866
|
+
const { doLink } = await prompts8({
|
|
5320
5867
|
type: "confirm",
|
|
5321
5868
|
name: "doLink",
|
|
5322
5869
|
message: "Link this repo to a vem cloud project now?",
|
|
@@ -5330,11 +5877,11 @@ function registerSetupCommands(program2) {
|
|
|
5330
5877
|
);
|
|
5331
5878
|
} catch (err) {
|
|
5332
5879
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5333
|
-
console.log(
|
|
5880
|
+
console.log(chalk16.yellow(`\u26A0 Link skipped: ${msg}`));
|
|
5334
5881
|
}
|
|
5335
5882
|
} else {
|
|
5336
5883
|
console.log(
|
|
5337
|
-
|
|
5884
|
+
chalk16.gray(
|
|
5338
5885
|
"Tip: Run `vem link` at any time to connect this repo to a project."
|
|
5339
5886
|
)
|
|
5340
5887
|
);
|
|
@@ -5348,43 +5895,43 @@ function registerSetupCommands(program2) {
|
|
|
5348
5895
|
apiKey
|
|
5349
5896
|
);
|
|
5350
5897
|
console.log(
|
|
5351
|
-
|
|
5898
|
+
chalk16.gray(
|
|
5352
5899
|
`Synced ${syncedCount} agent instruction file${syncedCount === 1 ? "" : "s"} to cloud memory.`
|
|
5353
5900
|
)
|
|
5354
5901
|
);
|
|
5355
5902
|
} catch (error) {
|
|
5356
5903
|
const message = error instanceof Error ? error.message : String(error);
|
|
5357
5904
|
console.log(
|
|
5358
|
-
|
|
5905
|
+
chalk16.yellow(`\u26A0 Agent instruction sync skipped: ${message}`)
|
|
5359
5906
|
);
|
|
5360
5907
|
}
|
|
5361
5908
|
} else if (!apiKey) {
|
|
5362
5909
|
console.log(
|
|
5363
|
-
|
|
5910
|
+
chalk16.gray(
|
|
5364
5911
|
"Tip: Use the web dashboard project settings to run reindexing after `vem login` + `vem link`."
|
|
5365
5912
|
)
|
|
5366
5913
|
);
|
|
5367
5914
|
}
|
|
5368
5915
|
} catch (error) {
|
|
5369
|
-
console.error(
|
|
5916
|
+
console.error(chalk16.red("\n\u2716 Failed to initialize vem:"), error);
|
|
5370
5917
|
process.exit(1);
|
|
5371
5918
|
}
|
|
5372
5919
|
});
|
|
5373
5920
|
program2.command("quickstart").description("Interactive guide to powerful VEM workflows").action(async () => {
|
|
5374
5921
|
await trackCommandUsage("quickstart");
|
|
5375
|
-
console.log(
|
|
5922
|
+
console.log(chalk16.bold.cyan("\n\u{1F680} VEM Quickstart Guide\n"));
|
|
5376
5923
|
console.log("Let's set up a powerful agent-driven workflow!\n");
|
|
5377
5924
|
const configService = new ConfigService();
|
|
5378
5925
|
if (!await isVemInitialized()) {
|
|
5379
|
-
console.log(
|
|
5380
|
-
const initResponse = await
|
|
5926
|
+
console.log(chalk16.yellow("Step 1: Initialize VEM\n"));
|
|
5927
|
+
const initResponse = await prompts8({
|
|
5381
5928
|
type: "confirm",
|
|
5382
5929
|
name: "init",
|
|
5383
5930
|
message: "Initialize .vem/ in this repository?",
|
|
5384
5931
|
initial: true
|
|
5385
5932
|
});
|
|
5386
5933
|
if (!initResponse.init) {
|
|
5387
|
-
console.log(
|
|
5934
|
+
console.log(chalk16.yellow("Quickstart cancelled."));
|
|
5388
5935
|
return;
|
|
5389
5936
|
}
|
|
5390
5937
|
try {
|
|
@@ -5393,13 +5940,13 @@ function registerSetupCommands(program2) {
|
|
|
5393
5940
|
await ensureVemGitignoreEntry();
|
|
5394
5941
|
const initHash = await computeVemHash();
|
|
5395
5942
|
await configService.setLastSyncedVemHash(initHash);
|
|
5396
|
-
console.log(
|
|
5943
|
+
console.log(chalk16.green("\u2713 VEM initialized\n"));
|
|
5397
5944
|
} catch (error) {
|
|
5398
|
-
console.error(
|
|
5945
|
+
console.error(chalk16.red("Failed to initialize:"), error.message);
|
|
5399
5946
|
return;
|
|
5400
5947
|
}
|
|
5401
5948
|
} else {
|
|
5402
|
-
console.log(
|
|
5949
|
+
console.log(chalk16.green("\u2713 VEM already initialized\n"));
|
|
5403
5950
|
}
|
|
5404
5951
|
let isAuthenticated = false;
|
|
5405
5952
|
try {
|
|
@@ -5409,52 +5956,52 @@ function registerSetupCommands(program2) {
|
|
|
5409
5956
|
isAuthenticated = false;
|
|
5410
5957
|
}
|
|
5411
5958
|
if (!isAuthenticated) {
|
|
5412
|
-
console.log(
|
|
5959
|
+
console.log(chalk16.yellow("Step 2: Authenticate\n"));
|
|
5413
5960
|
console.log("Get your API key from: https://vem.dev/keys\n");
|
|
5414
|
-
const authResponse = await
|
|
5961
|
+
const authResponse = await prompts8({
|
|
5415
5962
|
type: "text",
|
|
5416
5963
|
name: "apiKey",
|
|
5417
5964
|
message: "Paste your API key:"
|
|
5418
5965
|
});
|
|
5419
5966
|
if (!authResponse.apiKey) {
|
|
5420
|
-
console.log(
|
|
5967
|
+
console.log(chalk16.yellow("Quickstart cancelled."));
|
|
5421
5968
|
return;
|
|
5422
5969
|
}
|
|
5423
5970
|
await configService.setApiKey(authResponse.apiKey);
|
|
5424
|
-
console.log(
|
|
5971
|
+
console.log(chalk16.green("\u2713 Authenticated\n"));
|
|
5425
5972
|
} else {
|
|
5426
|
-
console.log(
|
|
5973
|
+
console.log(chalk16.green("\u2713 Already authenticated\n"));
|
|
5427
5974
|
}
|
|
5428
5975
|
const projectId = await configService.getProjectId().catch(() => null);
|
|
5429
5976
|
if (!projectId) {
|
|
5430
|
-
console.log(
|
|
5977
|
+
console.log(chalk16.yellow("Step 3: Link to project\n"));
|
|
5431
5978
|
console.log("This connects your local .vem/ to cloud sync.\n");
|
|
5432
|
-
const linkResponse = await
|
|
5979
|
+
const linkResponse = await prompts8({
|
|
5433
5980
|
type: "confirm",
|
|
5434
5981
|
name: "link",
|
|
5435
5982
|
message: "Link to a project now?",
|
|
5436
5983
|
initial: true
|
|
5437
5984
|
});
|
|
5438
5985
|
if (linkResponse.link) {
|
|
5439
|
-
console.log(
|
|
5440
|
-
console.log(
|
|
5986
|
+
console.log(chalk16.cyan("\nRun: vem link"));
|
|
5987
|
+
console.log(chalk16.gray("(You can select or create a project)\n"));
|
|
5441
5988
|
}
|
|
5442
5989
|
} else {
|
|
5443
|
-
console.log(
|
|
5990
|
+
console.log(chalk16.green(`\u2713 Linked to project: ${projectId}
|
|
5444
5991
|
`));
|
|
5445
5992
|
}
|
|
5446
|
-
console.log(
|
|
5993
|
+
console.log(chalk16.bold.cyan("\n\u{1F4CB} Task-Driven Workflow\n"));
|
|
5447
5994
|
console.log(
|
|
5448
5995
|
"Tasks help you track work and provide context to AI agents.\n"
|
|
5449
5996
|
);
|
|
5450
|
-
const taskResponse = await
|
|
5997
|
+
const taskResponse = await prompts8({
|
|
5451
5998
|
type: "confirm",
|
|
5452
5999
|
name: "createTask",
|
|
5453
6000
|
message: "Create your first task?",
|
|
5454
6001
|
initial: true
|
|
5455
6002
|
});
|
|
5456
6003
|
if (taskResponse.createTask) {
|
|
5457
|
-
const taskDetails = await
|
|
6004
|
+
const taskDetails = await prompts8([
|
|
5458
6005
|
{
|
|
5459
6006
|
type: "text",
|
|
5460
6007
|
name: "title",
|
|
@@ -5473,53 +6020,53 @@ function registerSetupCommands(program2) {
|
|
|
5473
6020
|
taskDetails.description || "",
|
|
5474
6021
|
"medium"
|
|
5475
6022
|
);
|
|
5476
|
-
console.log(
|
|
6023
|
+
console.log(chalk16.green(`
|
|
5477
6024
|
\u2713 Created task: ${task.id}`));
|
|
5478
6025
|
}
|
|
5479
6026
|
}
|
|
5480
|
-
console.log(
|
|
6027
|
+
console.log(chalk16.bold.cyan("\n\u{1F916} Agent-Driven Development\n"));
|
|
5481
6028
|
console.log("The 'vem agent' command wraps AI tools with:\n");
|
|
5482
6029
|
console.log(" \u2022 Automatic context injection");
|
|
5483
6030
|
console.log(" \u2022 Task tracking");
|
|
5484
6031
|
console.log(" \u2022 Strict memory enforcement");
|
|
5485
6032
|
console.log(" \u2022 Validation workflows\n");
|
|
5486
|
-
const agentResponse = await
|
|
6033
|
+
const agentResponse = await prompts8({
|
|
5487
6034
|
type: "confirm",
|
|
5488
6035
|
name: "launchAgent",
|
|
5489
6036
|
message: "Launch an agent session now?",
|
|
5490
6037
|
initial: false
|
|
5491
6038
|
});
|
|
5492
6039
|
if (agentResponse.launchAgent) {
|
|
5493
|
-
console.log(
|
|
5494
|
-
console.log(
|
|
6040
|
+
console.log(chalk16.cyan("\n\u{1F680} Launching agent...\n"));
|
|
6041
|
+
console.log(chalk16.white("Run: vem agent\n"));
|
|
5495
6042
|
}
|
|
5496
|
-
console.log(
|
|
6043
|
+
console.log(chalk16.bold.cyan("\n\u2728 Quick Reference\n"));
|
|
5497
6044
|
console.log(
|
|
5498
|
-
|
|
6045
|
+
chalk16.white(" vem agent") + chalk16.gray(" # Start AI-assisted work")
|
|
5499
6046
|
);
|
|
5500
6047
|
console.log(
|
|
5501
|
-
|
|
6048
|
+
chalk16.white(" vem task list") + chalk16.gray(" # View tasks")
|
|
5502
6049
|
);
|
|
5503
6050
|
console.log(
|
|
5504
|
-
|
|
6051
|
+
chalk16.white(" vem task add") + chalk16.gray(" # Create task")
|
|
5505
6052
|
);
|
|
5506
6053
|
console.log(
|
|
5507
|
-
|
|
6054
|
+
chalk16.white(" vem push") + chalk16.gray(" # Sync to cloud")
|
|
5508
6055
|
);
|
|
5509
6056
|
console.log(
|
|
5510
|
-
|
|
6057
|
+
chalk16.white(" vem search") + chalk16.gray(" # Query memory")
|
|
5511
6058
|
);
|
|
5512
6059
|
console.log(
|
|
5513
|
-
|
|
6060
|
+
chalk16.white(" vem status") + chalk16.gray(" # Check power score\n")
|
|
5514
6061
|
);
|
|
5515
|
-
console.log(
|
|
6062
|
+
console.log(chalk16.green("\u{1F389} You're ready to use VEM powerfully!\n"));
|
|
5516
6063
|
});
|
|
5517
6064
|
program2.command("status").description("Show current project status").action(async () => {
|
|
5518
6065
|
await trackCommandUsage("status");
|
|
5519
6066
|
try {
|
|
5520
6067
|
await ensureVemFiles();
|
|
5521
6068
|
const configService = new ConfigService();
|
|
5522
|
-
console.log(
|
|
6069
|
+
console.log(chalk16.bold("\n\u{1F4CA} vem Status\n"));
|
|
5523
6070
|
const apiKey = await configService.getApiKey();
|
|
5524
6071
|
if (apiKey) {
|
|
5525
6072
|
try {
|
|
@@ -5532,28 +6079,28 @@ function registerSetupCommands(program2) {
|
|
|
5532
6079
|
if (response.ok) {
|
|
5533
6080
|
const data = await response.json();
|
|
5534
6081
|
console.log(
|
|
5535
|
-
`Login Status: ${
|
|
6082
|
+
`Login Status: ${chalk16.green("Logged In")} (User: ${data.userId})`
|
|
5536
6083
|
);
|
|
5537
6084
|
console.log(
|
|
5538
|
-
|
|
6085
|
+
chalk16.gray(" (Run `vem logout` to sign out)")
|
|
5539
6086
|
);
|
|
5540
6087
|
} else {
|
|
5541
6088
|
console.log(
|
|
5542
|
-
`Login Status: ${
|
|
6089
|
+
`Login Status: ${chalk16.red(
|
|
5543
6090
|
"Invalid Session"
|
|
5544
6091
|
)} (Run \`vem login\` to fix)`
|
|
5545
6092
|
);
|
|
5546
6093
|
}
|
|
5547
6094
|
} catch (_err) {
|
|
5548
6095
|
console.log(
|
|
5549
|
-
`Login Status: ${
|
|
6096
|
+
`Login Status: ${chalk16.yellow(
|
|
5550
6097
|
"Logged In (Offline/Unverified)"
|
|
5551
6098
|
)} (Cannot reach API)`
|
|
5552
6099
|
);
|
|
5553
6100
|
}
|
|
5554
6101
|
} else {
|
|
5555
6102
|
console.log(
|
|
5556
|
-
`Login Status: ${
|
|
6103
|
+
`Login Status: ${chalk16.red(
|
|
5557
6104
|
"Not Logged In"
|
|
5558
6105
|
)} (Run \`vem login\` options)`
|
|
5559
6106
|
);
|
|
@@ -5568,25 +6115,25 @@ function registerSetupCommands(program2) {
|
|
|
5568
6115
|
);
|
|
5569
6116
|
if (check.valid) {
|
|
5570
6117
|
const label = check.name ? `${check.name} (${projectId})` : projectId;
|
|
5571
|
-
console.log(`Linked Project: ${
|
|
6118
|
+
console.log(`Linked Project: ${chalk16.green(label)}`);
|
|
5572
6119
|
} else {
|
|
5573
6120
|
console.log(
|
|
5574
|
-
`Linked Project: ${
|
|
6121
|
+
`Linked Project: ${chalk16.red(projectId)} ${chalk16.red("(not found \u2014 project may have been deleted)")}`
|
|
5575
6122
|
);
|
|
5576
6123
|
console.log(
|
|
5577
|
-
|
|
6124
|
+
chalk16.gray(
|
|
5578
6125
|
" Run `vem unlink` then `vem link` to fix."
|
|
5579
6126
|
)
|
|
5580
6127
|
);
|
|
5581
6128
|
}
|
|
5582
6129
|
} else {
|
|
5583
6130
|
console.log(
|
|
5584
|
-
`Linked Project: ${
|
|
6131
|
+
`Linked Project: ${chalk16.yellow(projectId)} (unverified \u2014 not logged in)`
|
|
5585
6132
|
);
|
|
5586
6133
|
}
|
|
5587
6134
|
} else {
|
|
5588
6135
|
console.log(
|
|
5589
|
-
`Linked Project: ${
|
|
6136
|
+
`Linked Project: ${chalk16.yellow("Not Linked")} (Run \`vem link\`)`
|
|
5590
6137
|
);
|
|
5591
6138
|
}
|
|
5592
6139
|
try {
|
|
@@ -5599,17 +6146,17 @@ function registerSetupCommands(program2) {
|
|
|
5599
6146
|
).length;
|
|
5600
6147
|
console.log(`
|
|
5601
6148
|
Local Tasks:`);
|
|
5602
|
-
console.log(` Open: ${
|
|
5603
|
-
console.log(` Completed: ${
|
|
6149
|
+
console.log(` Open: ${chalk16.yellow(active)}`);
|
|
6150
|
+
console.log(` Completed: ${chalk16.green(completed)}`);
|
|
5604
6151
|
} catch (_err) {
|
|
5605
6152
|
console.log(
|
|
5606
6153
|
`
|
|
5607
|
-
Local Tasks: ${
|
|
6154
|
+
Local Tasks: ${chalk16.gray("Not initialized (Run `vem init`)")}`
|
|
5608
6155
|
);
|
|
5609
6156
|
}
|
|
5610
6157
|
const stats = await metricsService.getStats();
|
|
5611
|
-
console.log(
|
|
5612
|
-
const scoreColor = stats.powerScore >= 70 ?
|
|
6158
|
+
console.log(chalk16.bold("\n\u26A1 Power Feature Usage\n"));
|
|
6159
|
+
const scoreColor = stats.powerScore >= 70 ? chalk16.green : stats.powerScore >= 40 ? chalk16.yellow : chalk16.gray;
|
|
5613
6160
|
console.log(` Power Score: ${scoreColor(`${stats.powerScore}/100`)}`);
|
|
5614
6161
|
const features = [
|
|
5615
6162
|
{
|
|
@@ -5643,34 +6190,34 @@ Local Tasks: ${chalk15.gray("Not initialized (Run `vem init`)")}`
|
|
|
5643
6190
|
points: 5
|
|
5644
6191
|
}
|
|
5645
6192
|
];
|
|
5646
|
-
console.log(
|
|
6193
|
+
console.log(chalk16.gray("\n Features:"));
|
|
5647
6194
|
for (const feature of features) {
|
|
5648
|
-
const icon = feature.used ?
|
|
5649
|
-
const name = feature.used ?
|
|
5650
|
-
const pts = feature.used ?
|
|
6195
|
+
const icon = feature.used ? chalk16.green("\u2713") : chalk16.gray("\u25CB");
|
|
6196
|
+
const name = feature.used ? chalk16.white(feature.name) : chalk16.gray(feature.name);
|
|
6197
|
+
const pts = feature.used ? chalk16.green(`+${feature.points}`) : chalk16.gray(`+${feature.points}`);
|
|
5651
6198
|
console.log(` ${icon} ${name} ${pts}`);
|
|
5652
6199
|
}
|
|
5653
6200
|
if (stats.powerScore < 40) {
|
|
5654
6201
|
console.log(
|
|
5655
|
-
|
|
6202
|
+
chalk16.yellow(
|
|
5656
6203
|
"\n \u{1F4A1} Tip: Try 'vem agent' to unlock powerful workflows"
|
|
5657
6204
|
)
|
|
5658
6205
|
);
|
|
5659
6206
|
} else if (stats.powerScore < 70) {
|
|
5660
6207
|
console.log(
|
|
5661
|
-
|
|
6208
|
+
chalk16.cyan(
|
|
5662
6209
|
"\n \u{1F4A1} You're on your way! Keep using task-driven workflows"
|
|
5663
6210
|
)
|
|
5664
6211
|
);
|
|
5665
6212
|
} else {
|
|
5666
6213
|
console.log(
|
|
5667
|
-
|
|
6214
|
+
chalk16.green("\n \u{1F389} Excellent! You're using VEM like a pro")
|
|
5668
6215
|
);
|
|
5669
6216
|
}
|
|
5670
6217
|
if (stats.lastAgentRun) {
|
|
5671
6218
|
const timeSince = Date.now() - stats.lastAgentRun;
|
|
5672
6219
|
const days = Math.floor(timeSince / (1e3 * 60 * 60 * 24));
|
|
5673
|
-
console.log(
|
|
6220
|
+
console.log(chalk16.bold("\n\u{1F4C5} Recent Activity\n"));
|
|
5674
6221
|
console.log(
|
|
5675
6222
|
` Last agent session: ${days === 0 ? "today" : `${days} days ago`}`
|
|
5676
6223
|
);
|
|
@@ -5678,7 +6225,7 @@ Local Tasks: ${chalk15.gray("Not initialized (Run `vem init`)")}`
|
|
|
5678
6225
|
renderUsageInsights(stats, false);
|
|
5679
6226
|
console.log("");
|
|
5680
6227
|
} catch (error) {
|
|
5681
|
-
console.error(
|
|
6228
|
+
console.error(chalk16.red("\n\u2716 Failed to check status:"), error.message);
|
|
5682
6229
|
}
|
|
5683
6230
|
});
|
|
5684
6231
|
program2.command("insights").description("Show detailed usage metrics and workflow insights").option("--json", "Output raw usage metrics as JSON").action(async (options) => {
|
|
@@ -5690,19 +6237,19 @@ Local Tasks: ${chalk15.gray("Not initialized (Run `vem init`)")}`
|
|
|
5690
6237
|
console.log(JSON.stringify(stats, null, 2));
|
|
5691
6238
|
return;
|
|
5692
6239
|
}
|
|
5693
|
-
console.log(
|
|
5694
|
-
console.log(`Power Score: ${
|
|
6240
|
+
console.log(chalk16.bold("\n\u{1F4CA} vem Insights\n"));
|
|
6241
|
+
console.log(`Power Score: ${chalk16.cyan(`${stats.powerScore}/100`)}`);
|
|
5695
6242
|
renderUsageInsights(stats, true);
|
|
5696
6243
|
console.log("");
|
|
5697
6244
|
} catch (error) {
|
|
5698
|
-
console.error(
|
|
6245
|
+
console.error(chalk16.red("\n\u2716 Failed to load insights:"), error.message);
|
|
5699
6246
|
}
|
|
5700
6247
|
});
|
|
5701
6248
|
}
|
|
5702
6249
|
|
|
5703
6250
|
// src/commands/sync.ts
|
|
5704
|
-
import { readFile as
|
|
5705
|
-
import
|
|
6251
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
6252
|
+
import chalk17 from "chalk";
|
|
5706
6253
|
import Table3 from "cli-table3";
|
|
5707
6254
|
function registerSyncCommands(program2) {
|
|
5708
6255
|
program2.command("push").description("Push local snapshot to cloud").option(
|
|
@@ -5715,7 +6262,7 @@ function registerSyncCommands(program2) {
|
|
|
5715
6262
|
const projectId = await configService.getProjectId();
|
|
5716
6263
|
if (!projectId) {
|
|
5717
6264
|
console.error(
|
|
5718
|
-
|
|
6265
|
+
chalk17.red(
|
|
5719
6266
|
"Error: Project not linked. Run `vem link <projectId>` before pushing snapshots."
|
|
5720
6267
|
)
|
|
5721
6268
|
);
|
|
@@ -5728,7 +6275,7 @@ function registerSyncCommands(program2) {
|
|
|
5728
6275
|
const gitHash = getGitHash();
|
|
5729
6276
|
if (!gitHash) {
|
|
5730
6277
|
console.error(
|
|
5731
|
-
|
|
6278
|
+
chalk17.red(
|
|
5732
6279
|
"Error: git HEAD not found. Create at least one commit before running `vem push`."
|
|
5733
6280
|
)
|
|
5734
6281
|
);
|
|
@@ -5740,25 +6287,25 @@ function registerSyncCommands(program2) {
|
|
|
5740
6287
|
if (!hasChanges && !options.force) {
|
|
5741
6288
|
const lastPushTime = lastPush.gitHash ? "previously" : "never";
|
|
5742
6289
|
console.log(
|
|
5743
|
-
|
|
6290
|
+
chalk17.gray(
|
|
5744
6291
|
`\u2714 No changes since last push (git HEAD and .vem unchanged). Last push: ${lastPushTime}`
|
|
5745
6292
|
)
|
|
5746
6293
|
);
|
|
5747
|
-
console.log(
|
|
6294
|
+
console.log(chalk17.gray(" Use --force to push anyway."));
|
|
5748
6295
|
return;
|
|
5749
6296
|
}
|
|
5750
|
-
console.log(
|
|
6297
|
+
console.log(chalk17.blue("\u{1F4E6} Packing snapshot..."));
|
|
5751
6298
|
const snapshot = await syncService.pack();
|
|
5752
6299
|
const snapshotHash = computeSnapshotHash(snapshot);
|
|
5753
6300
|
const targetLabel = `linked project ${projectId}`;
|
|
5754
6301
|
if (options.dryRun) {
|
|
5755
|
-
console.log(
|
|
5756
|
-
console.log(
|
|
5757
|
-
console.log(
|
|
5758
|
-
console.log(
|
|
5759
|
-
console.log(
|
|
6302
|
+
console.log(chalk17.cyan("\n\u{1F4CB} Dry Run Preview\n"));
|
|
6303
|
+
console.log(chalk17.white(`Target: ${targetLabel}`));
|
|
6304
|
+
console.log(chalk17.white(`Git Hash: ${gitHash}`));
|
|
6305
|
+
console.log(chalk17.white(`Snapshot Hash: ${snapshotHash}`));
|
|
6306
|
+
console.log(chalk17.white(`Base Version: ${baseVersion || "none"}`));
|
|
5760
6307
|
console.log(
|
|
5761
|
-
|
|
6308
|
+
chalk17.white(
|
|
5762
6309
|
"Verification: pending until Git webhook matches git hash + snapshot hash"
|
|
5763
6310
|
)
|
|
5764
6311
|
);
|
|
@@ -5766,29 +6313,29 @@ function registerSyncCommands(program2) {
|
|
|
5766
6313
|
const decisionCount = snapshot.decisions?.length || 0;
|
|
5767
6314
|
const changelogCount = snapshot.changelog?.length || 0;
|
|
5768
6315
|
const agentInstructionCount = snapshot.agent_instructions?.length || 0;
|
|
5769
|
-
console.log(
|
|
6316
|
+
console.log(chalk17.white(`
|
|
5770
6317
|
Snapshot Contents:`));
|
|
5771
|
-
console.log(
|
|
5772
|
-
console.log(
|
|
5773
|
-
console.log(
|
|
6318
|
+
console.log(chalk17.gray(` Tasks: ${taskCount}`));
|
|
6319
|
+
console.log(chalk17.gray(` Decisions (chars): ${decisionCount}`));
|
|
6320
|
+
console.log(chalk17.gray(` Changelog (chars): ${changelogCount}`));
|
|
5774
6321
|
console.log(
|
|
5775
|
-
|
|
6322
|
+
chalk17.gray(` Context: ${snapshot.context ? "yes" : "no"}`)
|
|
5776
6323
|
);
|
|
5777
6324
|
console.log(
|
|
5778
|
-
|
|
6325
|
+
chalk17.gray(
|
|
5779
6326
|
` Current state: ${snapshot.current_state ? "yes" : "no"}`
|
|
5780
6327
|
)
|
|
5781
6328
|
);
|
|
5782
6329
|
console.log(
|
|
5783
|
-
|
|
6330
|
+
chalk17.gray(
|
|
5784
6331
|
` Agent instructions: ${agentInstructionCount} file${agentInstructionCount === 1 ? "" : "s"}`
|
|
5785
6332
|
)
|
|
5786
6333
|
);
|
|
5787
|
-
console.log(
|
|
5788
|
-
console.log(
|
|
6334
|
+
console.log(chalk17.cyan("\n\u2714 Dry run complete. No changes pushed.\n"));
|
|
6335
|
+
console.log(chalk17.gray(" Run without --dry-run to push for real."));
|
|
5789
6336
|
return;
|
|
5790
6337
|
}
|
|
5791
|
-
console.log(
|
|
6338
|
+
console.log(chalk17.blue(`\u{1F680} Pushing to cloud (${targetLabel})...`));
|
|
5792
6339
|
const commits = await getCommits(50);
|
|
5793
6340
|
const payload = {
|
|
5794
6341
|
...snapshot,
|
|
@@ -5804,12 +6351,12 @@ Snapshot Contents:`));
|
|
|
5804
6351
|
const expectedRepoUrl = result.data.expected_repo_url;
|
|
5805
6352
|
const actualRepo = repoUrl || "(no git remote)";
|
|
5806
6353
|
console.log(
|
|
5807
|
-
|
|
6354
|
+
chalk17.yellow(
|
|
5808
6355
|
`Project is linked to ${expectedRepoUrl}. Local repo is ${actualRepo}. Retrying using the linked project only...`
|
|
5809
6356
|
)
|
|
5810
6357
|
);
|
|
5811
6358
|
console.log(
|
|
5812
|
-
|
|
6359
|
+
chalk17.blue(
|
|
5813
6360
|
`\u{1F680} Pushing to cloud (linked repo ${expectedRepoUrl})...`
|
|
5814
6361
|
)
|
|
5815
6362
|
);
|
|
@@ -5823,7 +6370,7 @@ Snapshot Contents:`));
|
|
|
5823
6370
|
await configService.setLastSyncedVemHash(vemHash);
|
|
5824
6371
|
}
|
|
5825
6372
|
console.log(
|
|
5826
|
-
|
|
6373
|
+
chalk17.green(
|
|
5827
6374
|
`
|
|
5828
6375
|
\u2714 Snapshot pushed! Version: ${result.data.version || "v1"}
|
|
5829
6376
|
`
|
|
@@ -5835,12 +6382,12 @@ Snapshot Contents:`));
|
|
|
5835
6382
|
});
|
|
5836
6383
|
if (archivedCount > 0) {
|
|
5837
6384
|
console.log(
|
|
5838
|
-
|
|
6385
|
+
chalk17.green(`\u2714 Archived ${archivedCount} completed tasks.`)
|
|
5839
6386
|
);
|
|
5840
6387
|
}
|
|
5841
6388
|
} catch (err) {
|
|
5842
6389
|
console.error(
|
|
5843
|
-
|
|
6390
|
+
chalk17.yellow(
|
|
5844
6391
|
`\u26A0 Failed to archive completed tasks: ${err instanceof Error ? err.message : String(err)}`
|
|
5845
6392
|
)
|
|
5846
6393
|
);
|
|
@@ -5852,7 +6399,7 @@ Snapshot Contents:`));
|
|
|
5852
6399
|
if (data.latest_version) {
|
|
5853
6400
|
const latest = data.latest_version || "unknown";
|
|
5854
6401
|
console.error(
|
|
5855
|
-
|
|
6402
|
+
chalk17.yellow(
|
|
5856
6403
|
`Conflict: local base version ${baseVersion || "none"} does not match latest ${latest}. Pull the latest snapshot (\`vem pull\`) or re-run push from the latest memory state.`
|
|
5857
6404
|
)
|
|
5858
6405
|
);
|
|
@@ -5862,18 +6409,18 @@ Snapshot Contents:`));
|
|
|
5862
6409
|
const expectedRepoUrl = data.expected_repo_url;
|
|
5863
6410
|
const actualRepo = repoUrl || "(no git remote)";
|
|
5864
6411
|
console.error(
|
|
5865
|
-
|
|
6412
|
+
chalk17.yellow(
|
|
5866
6413
|
`Project is linked to ${expectedRepoUrl}, local repo is ${actualRepo}. Update your git remote or re-link the project, then retry.`
|
|
5867
6414
|
)
|
|
5868
6415
|
);
|
|
5869
6416
|
return;
|
|
5870
6417
|
}
|
|
5871
|
-
console.error(
|
|
6418
|
+
console.error(chalk17.yellow(data.error || "Conflict detected."));
|
|
5872
6419
|
return;
|
|
5873
6420
|
}
|
|
5874
6421
|
if (result.status === 403) {
|
|
5875
6422
|
console.error(
|
|
5876
|
-
|
|
6423
|
+
chalk17.red(
|
|
5877
6424
|
result.error || "Device limit reached. Disconnect a device or upgrade your plan."
|
|
5878
6425
|
)
|
|
5879
6426
|
);
|
|
@@ -5881,26 +6428,26 @@ Snapshot Contents:`));
|
|
|
5881
6428
|
}
|
|
5882
6429
|
if (result.status === 404) {
|
|
5883
6430
|
console.error(
|
|
5884
|
-
|
|
6431
|
+
chalk17.red(
|
|
5885
6432
|
result.error || "Project not found. It may have been deleted. Run `vem unlink` then `vem link` to reconnect."
|
|
5886
6433
|
)
|
|
5887
6434
|
);
|
|
5888
6435
|
return;
|
|
5889
6436
|
}
|
|
5890
6437
|
console.log(
|
|
5891
|
-
|
|
6438
|
+
chalk17.yellow(
|
|
5892
6439
|
`
|
|
5893
6440
|
\u26A0 Push failed (${result.error}). Queuing snapshot for later...`
|
|
5894
6441
|
)
|
|
5895
6442
|
);
|
|
5896
6443
|
const id = await syncService.enqueue(payload);
|
|
5897
|
-
console.log(
|
|
6444
|
+
console.log(chalk17.gray(`Queued as ${id}`));
|
|
5898
6445
|
}
|
|
5899
6446
|
} catch (error) {
|
|
5900
6447
|
if (error instanceof Error) {
|
|
5901
|
-
console.error(
|
|
6448
|
+
console.error(chalk17.red("\n\u2716 Push Failed:"), error.message);
|
|
5902
6449
|
} else {
|
|
5903
|
-
console.error(
|
|
6450
|
+
console.error(chalk17.red("\n\u2716 Push Failed:"), String(error));
|
|
5904
6451
|
}
|
|
5905
6452
|
}
|
|
5906
6453
|
});
|
|
@@ -5911,12 +6458,12 @@ Snapshot Contents:`));
|
|
|
5911
6458
|
const projectId = await configService.getProjectId();
|
|
5912
6459
|
if (await isVemDirty(configService) && !options.force) {
|
|
5913
6460
|
console.error(
|
|
5914
|
-
|
|
6461
|
+
chalk17.yellow(
|
|
5915
6462
|
"\u26A0 Local .vem memory has unsynced changes. Pulling will overwrite it."
|
|
5916
6463
|
)
|
|
5917
6464
|
);
|
|
5918
6465
|
console.log(
|
|
5919
|
-
|
|
6466
|
+
chalk17.gray(
|
|
5920
6467
|
"Push your snapshot first, or use `vem pull --force` to proceed."
|
|
5921
6468
|
)
|
|
5922
6469
|
);
|
|
@@ -5925,7 +6472,7 @@ Snapshot Contents:`));
|
|
|
5925
6472
|
const repoUrl = projectId ? null : await getGitRemote();
|
|
5926
6473
|
if (!repoUrl && !projectId) {
|
|
5927
6474
|
console.error(
|
|
5928
|
-
|
|
6475
|
+
chalk17.red(
|
|
5929
6476
|
"Error: Could not detect git remote URL or linked project. Run `vem link <projectId>` or set a git remote."
|
|
5930
6477
|
)
|
|
5931
6478
|
);
|
|
@@ -5933,7 +6480,7 @@ Snapshot Contents:`));
|
|
|
5933
6480
|
}
|
|
5934
6481
|
const targetLabel = repoUrl || projectId || "project";
|
|
5935
6482
|
console.log(
|
|
5936
|
-
|
|
6483
|
+
chalk17.blue(`\u2B07 Finding latest snapshot for ${targetLabel}...`)
|
|
5937
6484
|
);
|
|
5938
6485
|
const query = new URLSearchParams();
|
|
5939
6486
|
if (repoUrl) query.set("repo_url", repoUrl);
|
|
@@ -5948,10 +6495,10 @@ Snapshot Contents:`));
|
|
|
5948
6495
|
const data2 = await res.json().catch(() => ({}));
|
|
5949
6496
|
if (res.status === 404) {
|
|
5950
6497
|
const message = typeof data2.error === "string" ? data2.error : "Project not found. It may have been deleted. Run `vem unlink` then `vem link` to reconnect.";
|
|
5951
|
-
console.log(
|
|
6498
|
+
console.log(chalk17.yellow(message));
|
|
5952
6499
|
if (message.toLowerCase().includes("no snapshots")) {
|
|
5953
6500
|
console.log(
|
|
5954
|
-
|
|
6501
|
+
chalk17.gray(
|
|
5955
6502
|
"Tip: push a snapshot first (`vem push`) and wait for verification if needed."
|
|
5956
6503
|
)
|
|
5957
6504
|
);
|
|
@@ -5961,18 +6508,18 @@ Snapshot Contents:`));
|
|
|
5961
6508
|
if (res.status === 409) {
|
|
5962
6509
|
if (data2.expected_repo_url) {
|
|
5963
6510
|
console.error(
|
|
5964
|
-
|
|
6511
|
+
chalk17.yellow(
|
|
5965
6512
|
`Repo URL mismatch. Expected ${data2.expected_repo_url}. Update your git remote or project settings, then retry.`
|
|
5966
6513
|
)
|
|
5967
6514
|
);
|
|
5968
6515
|
return;
|
|
5969
6516
|
}
|
|
5970
|
-
console.error(
|
|
6517
|
+
console.error(chalk17.yellow(data2.error || "Conflict detected."));
|
|
5971
6518
|
return;
|
|
5972
6519
|
}
|
|
5973
6520
|
if (res.status === 403) {
|
|
5974
6521
|
console.error(
|
|
5975
|
-
|
|
6522
|
+
chalk17.red(
|
|
5976
6523
|
data2.error || "Device limit reached. Disconnect a device or upgrade your plan."
|
|
5977
6524
|
)
|
|
5978
6525
|
);
|
|
@@ -5984,10 +6531,10 @@ Snapshot Contents:`));
|
|
|
5984
6531
|
}
|
|
5985
6532
|
const data = await res.json();
|
|
5986
6533
|
if (!data.snapshot) {
|
|
5987
|
-
console.log(
|
|
6534
|
+
console.log(chalk17.yellow("No snapshot data in response."));
|
|
5988
6535
|
return;
|
|
5989
6536
|
}
|
|
5990
|
-
console.log(
|
|
6537
|
+
console.log(chalk17.blue("\u{1F4E6} Unpacking snapshot..."));
|
|
5991
6538
|
await syncService.unpack(data.snapshot);
|
|
5992
6539
|
const localHash = await computeVemHash();
|
|
5993
6540
|
await configService.setLastSyncedVemHash(localHash);
|
|
@@ -5995,15 +6542,15 @@ Snapshot Contents:`));
|
|
|
5995
6542
|
await configService.setLastVersion(data.version);
|
|
5996
6543
|
}
|
|
5997
6544
|
console.log(
|
|
5998
|
-
|
|
6545
|
+
chalk17.green(`
|
|
5999
6546
|
\u2714 Synced to version ${data.version || "unknown"}
|
|
6000
6547
|
`)
|
|
6001
6548
|
);
|
|
6002
6549
|
} catch (error) {
|
|
6003
6550
|
if (error instanceof Error) {
|
|
6004
|
-
console.error(
|
|
6551
|
+
console.error(chalk17.red("\n\u2716 Pull Failed:"), error.message);
|
|
6005
6552
|
} else {
|
|
6006
|
-
console.error(
|
|
6553
|
+
console.error(chalk17.red("\n\u2716 Pull Failed:"), String(error));
|
|
6007
6554
|
}
|
|
6008
6555
|
}
|
|
6009
6556
|
});
|
|
@@ -6017,9 +6564,9 @@ Snapshot Contents:`));
|
|
|
6017
6564
|
await showWorkflowHint("pack");
|
|
6018
6565
|
} catch (error) {
|
|
6019
6566
|
if (error instanceof Error) {
|
|
6020
|
-
console.error(
|
|
6567
|
+
console.error(chalk17.red("\n\u2716 Pack Failed:"), error.message);
|
|
6021
6568
|
} else {
|
|
6022
|
-
console.error(
|
|
6569
|
+
console.error(chalk17.red("\n\u2716 Pack Failed:"), String(error));
|
|
6023
6570
|
}
|
|
6024
6571
|
}
|
|
6025
6572
|
});
|
|
@@ -6028,12 +6575,12 @@ Snapshot Contents:`));
|
|
|
6028
6575
|
try {
|
|
6029
6576
|
let input = "";
|
|
6030
6577
|
if (options.file) {
|
|
6031
|
-
input = await
|
|
6578
|
+
input = await readFile6(options.file, "utf-8");
|
|
6032
6579
|
} else if (!process.stdin.isTTY) {
|
|
6033
6580
|
input = await readStdin();
|
|
6034
6581
|
} else {
|
|
6035
6582
|
console.error(
|
|
6036
|
-
|
|
6583
|
+
chalk17.red(
|
|
6037
6584
|
"Provide a vem_update block via --file or pipe it into stdin."
|
|
6038
6585
|
)
|
|
6039
6586
|
);
|
|
@@ -6042,66 +6589,68 @@ Snapshot Contents:`));
|
|
|
6042
6589
|
}
|
|
6043
6590
|
const update = parseVemUpdateBlock(input);
|
|
6044
6591
|
const result = await applyVemUpdate(update);
|
|
6045
|
-
console.log(
|
|
6592
|
+
console.log(chalk17.green("\n\u2714 vem update applied\n"));
|
|
6046
6593
|
if (result.updatedTasks.length > 0) {
|
|
6047
6594
|
console.log(
|
|
6048
|
-
|
|
6595
|
+
chalk17.gray(
|
|
6049
6596
|
`Updated tasks: ${result.updatedTasks.map((task) => task.id).join(", ")}`
|
|
6050
6597
|
)
|
|
6051
6598
|
);
|
|
6052
6599
|
}
|
|
6053
6600
|
if (result.newTasks.length > 0) {
|
|
6054
6601
|
console.log(
|
|
6055
|
-
|
|
6602
|
+
chalk17.gray(
|
|
6056
6603
|
`New tasks: ${result.newTasks.map((task) => task.id).join(", ")}`
|
|
6057
6604
|
)
|
|
6058
6605
|
);
|
|
6059
6606
|
}
|
|
6060
6607
|
if (result.changelogLines.length > 0) {
|
|
6061
6608
|
console.log(
|
|
6062
|
-
|
|
6609
|
+
chalk17.gray(`Changelog entries: ${result.changelogLines.length}`)
|
|
6063
6610
|
);
|
|
6064
6611
|
}
|
|
6065
6612
|
if (result.newCycles.length > 0) {
|
|
6066
6613
|
console.log(
|
|
6067
|
-
|
|
6614
|
+
chalk17.gray(
|
|
6068
6615
|
`New cycles: ${result.newCycles.map((c) => c.name).join(", ")}`
|
|
6069
6616
|
)
|
|
6070
6617
|
);
|
|
6071
6618
|
}
|
|
6072
6619
|
if (result.decisionsAppended) {
|
|
6073
|
-
console.log(
|
|
6620
|
+
console.log(chalk17.gray("Decisions updated."));
|
|
6074
6621
|
}
|
|
6075
6622
|
if (result.currentStateUpdated) {
|
|
6076
|
-
console.log(
|
|
6623
|
+
console.log(chalk17.gray("Current state updated."));
|
|
6077
6624
|
} else {
|
|
6078
6625
|
console.log(
|
|
6079
|
-
|
|
6626
|
+
chalk17.yellow(
|
|
6080
6627
|
"No current_state provided; CURRENT_STATE.md was left unchanged."
|
|
6081
6628
|
)
|
|
6082
6629
|
);
|
|
6083
6630
|
}
|
|
6084
6631
|
if (result.contextUpdated) {
|
|
6085
|
-
console.log(
|
|
6632
|
+
console.log(chalk17.gray("Context updated."));
|
|
6086
6633
|
}
|
|
6087
6634
|
const configService = new ConfigService();
|
|
6088
|
-
await syncParsedTaskUpdatesToRemote(
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6635
|
+
await syncParsedTaskUpdatesToRemote(
|
|
6636
|
+
configService,
|
|
6637
|
+
update,
|
|
6638
|
+
result
|
|
6639
|
+
).catch((err) => {
|
|
6640
|
+
console.error(
|
|
6641
|
+
chalk17.yellow("[vem finalize] syncParsed failed:"),
|
|
6642
|
+
err instanceof Error ? err.message : String(err)
|
|
6643
|
+
);
|
|
6644
|
+
});
|
|
6096
6645
|
const synced = await syncProjectMemoryToRemote().catch(() => false);
|
|
6097
6646
|
if (synced) {
|
|
6098
|
-
console.log(
|
|
6647
|
+
console.log(chalk17.gray("\u2714 Synced to cloud."));
|
|
6099
6648
|
}
|
|
6100
6649
|
} catch (error) {
|
|
6101
6650
|
if (error instanceof Error) {
|
|
6102
|
-
console.error(
|
|
6651
|
+
console.error(chalk17.red("\n\u2716 Finalize Failed:"), error.message);
|
|
6103
6652
|
} else {
|
|
6104
|
-
console.error(
|
|
6653
|
+
console.error(chalk17.red("\n\u2716 Finalize Failed:"), String(error));
|
|
6105
6654
|
}
|
|
6106
6655
|
process.exitCode = 1;
|
|
6107
6656
|
}
|
|
@@ -6114,7 +6663,7 @@ Snapshot Contents:`));
|
|
|
6114
6663
|
for (const item of queue2) {
|
|
6115
6664
|
await syncService.removeFromQueue(item.id);
|
|
6116
6665
|
}
|
|
6117
|
-
console.log(
|
|
6666
|
+
console.log(chalk17.green("\n\u2714 Queue cleared\n"));
|
|
6118
6667
|
return;
|
|
6119
6668
|
}
|
|
6120
6669
|
if (options.retry) {
|
|
@@ -6124,10 +6673,10 @@ Snapshot Contents:`));
|
|
|
6124
6673
|
}
|
|
6125
6674
|
const queue = await syncService.getQueue();
|
|
6126
6675
|
if (queue.length === 0) {
|
|
6127
|
-
console.log(
|
|
6676
|
+
console.log(chalk17.gray("\nOffline queue is empty.\n"));
|
|
6128
6677
|
return;
|
|
6129
6678
|
}
|
|
6130
|
-
console.log(
|
|
6679
|
+
console.log(chalk17.bold(`
|
|
6131
6680
|
\u{1F4E6} Offline Queue (${queue.length} items)
|
|
6132
6681
|
`));
|
|
6133
6682
|
const table = new Table3({
|
|
@@ -6137,7 +6686,7 @@ Snapshot Contents:`));
|
|
|
6137
6686
|
queue.forEach((item) => {
|
|
6138
6687
|
const date = new Date(parseInt(item.id.split("-")[0], 10));
|
|
6139
6688
|
table.push([
|
|
6140
|
-
|
|
6689
|
+
chalk17.gray(item.id),
|
|
6141
6690
|
date.toLocaleString(),
|
|
6142
6691
|
item.payload.repo_url || "unknown",
|
|
6143
6692
|
item.payload.base_version || "none"
|
|
@@ -6145,10 +6694,10 @@ Snapshot Contents:`));
|
|
|
6145
6694
|
});
|
|
6146
6695
|
console.log(table.toString());
|
|
6147
6696
|
console.log(
|
|
6148
|
-
|
|
6697
|
+
chalk17.gray("\nUse `vem queue --retry` to push these snapshots.\n")
|
|
6149
6698
|
);
|
|
6150
6699
|
} catch (error) {
|
|
6151
|
-
console.error(
|
|
6700
|
+
console.error(chalk17.red("Queue Error:"), error.message);
|
|
6152
6701
|
}
|
|
6153
6702
|
});
|
|
6154
6703
|
program2.command("archive").description("Archive old memory files to keep context small").option("--all", "Archive decisions, changelogs, and tasks").option("--decisions", "Archive decisions only").option("--changelog", "Archive changelog only").option("--tasks", "Archive completed tasks only").option(
|
|
@@ -6166,9 +6715,9 @@ Snapshot Contents:`));
|
|
|
6166
6715
|
const keepCount = options.keep ?? 20;
|
|
6167
6716
|
const olderThanDays = options.olderThan ?? 30;
|
|
6168
6717
|
const all = options.all || !options.decisions && !options.changelog && !options.tasks;
|
|
6169
|
-
console.log(
|
|
6718
|
+
console.log(chalk17.bold("\n\u{1F5C4}\uFE0F Archiving Memory...\n"));
|
|
6170
6719
|
console.log(
|
|
6171
|
-
|
|
6720
|
+
chalk17.gray(
|
|
6172
6721
|
`Criteria: Keep ${keepCount} items OR younger than ${olderThanDays} days.`
|
|
6173
6722
|
)
|
|
6174
6723
|
);
|
|
@@ -6179,9 +6728,9 @@ Snapshot Contents:`));
|
|
|
6179
6728
|
olderThanDays
|
|
6180
6729
|
});
|
|
6181
6730
|
if (count > 0) {
|
|
6182
|
-
console.log(
|
|
6731
|
+
console.log(chalk17.green(`\u2714 Archived ${count} decision(s)`));
|
|
6183
6732
|
} else {
|
|
6184
|
-
console.log(
|
|
6733
|
+
console.log(chalk17.gray("Decisions: Nothing to archive"));
|
|
6185
6734
|
}
|
|
6186
6735
|
}
|
|
6187
6736
|
if (all || options.changelog) {
|
|
@@ -6191,9 +6740,9 @@ Snapshot Contents:`));
|
|
|
6191
6740
|
olderThanDays
|
|
6192
6741
|
});
|
|
6193
6742
|
if (count > 0) {
|
|
6194
|
-
console.log(
|
|
6743
|
+
console.log(chalk17.green(`\u2714 Archived ${count} changelog entry(s)`));
|
|
6195
6744
|
} else {
|
|
6196
|
-
console.log(
|
|
6745
|
+
console.log(chalk17.gray("Changelog: Nothing to archive"));
|
|
6197
6746
|
}
|
|
6198
6747
|
}
|
|
6199
6748
|
if (all || options.tasks) {
|
|
@@ -6202,17 +6751,17 @@ Snapshot Contents:`));
|
|
|
6202
6751
|
olderThanDays
|
|
6203
6752
|
});
|
|
6204
6753
|
if (count > 0) {
|
|
6205
|
-
console.log(
|
|
6754
|
+
console.log(chalk17.green(`\u2714 Archived ${count} completed task(s)`));
|
|
6206
6755
|
} else {
|
|
6207
|
-
console.log(
|
|
6756
|
+
console.log(chalk17.gray("Tasks: Nothing to archive"));
|
|
6208
6757
|
}
|
|
6209
6758
|
}
|
|
6210
6759
|
console.log("");
|
|
6211
6760
|
} catch (error) {
|
|
6212
6761
|
if (error instanceof Error) {
|
|
6213
|
-
console.error(
|
|
6762
|
+
console.error(chalk17.red("\n\u2716 Archive Failed:"), error.message);
|
|
6214
6763
|
} else {
|
|
6215
|
-
console.error(
|
|
6764
|
+
console.error(chalk17.red("\n\u2716 Archive Failed:"), String(error));
|
|
6216
6765
|
}
|
|
6217
6766
|
process.exit(1);
|
|
6218
6767
|
}
|
|
@@ -6220,29 +6769,29 @@ Snapshot Contents:`));
|
|
|
6220
6769
|
}
|
|
6221
6770
|
|
|
6222
6771
|
// src/commands/task.ts
|
|
6223
|
-
import
|
|
6772
|
+
import chalk18 from "chalk";
|
|
6224
6773
|
import Table4 from "cli-table3";
|
|
6225
|
-
import
|
|
6774
|
+
import prompts9 from "prompts";
|
|
6226
6775
|
function registerTaskCommands(program2) {
|
|
6227
6776
|
const taskCmd = program2.command("task").description("Manage tasks");
|
|
6228
6777
|
const formatTaskStatusLabel = (status, deletedAt) => {
|
|
6229
|
-
if (deletedAt) return
|
|
6778
|
+
if (deletedAt) return chalk18.red("DELETED");
|
|
6230
6779
|
switch (status) {
|
|
6231
6780
|
case "ready":
|
|
6232
|
-
return
|
|
6781
|
+
return chalk18.cyan("READY");
|
|
6233
6782
|
case "in-review":
|
|
6234
|
-
return
|
|
6783
|
+
return chalk18.magenta("IN REVW");
|
|
6235
6784
|
case "in-progress":
|
|
6236
|
-
return
|
|
6785
|
+
return chalk18.blue("IN PROG");
|
|
6237
6786
|
case "blocked":
|
|
6238
|
-
return
|
|
6787
|
+
return chalk18.yellow("BLOCKED");
|
|
6239
6788
|
case "done":
|
|
6240
|
-
return
|
|
6789
|
+
return chalk18.green("DONE");
|
|
6241
6790
|
default:
|
|
6242
|
-
return
|
|
6791
|
+
return chalk18.gray("TODO");
|
|
6243
6792
|
}
|
|
6244
6793
|
};
|
|
6245
|
-
const formatTaskPriority = (priority) => priority === "high" || priority === "critical" ?
|
|
6794
|
+
const formatTaskPriority = (priority) => priority === "high" || priority === "critical" ? chalk18.red(priority) : chalk18.white(priority || "");
|
|
6246
6795
|
const ADD_TASK_BACK_VALUE = "__vem_back__";
|
|
6247
6796
|
const ADD_TASK_PRIORITIES = ["low", "medium", "high", "critical"];
|
|
6248
6797
|
const TASK_STATUS_VALUES = /* @__PURE__ */ new Set([
|
|
@@ -6626,7 +7175,7 @@ function registerTaskCommands(program2) {
|
|
|
6626
7175
|
validate
|
|
6627
7176
|
}) => {
|
|
6628
7177
|
let cancelled = false;
|
|
6629
|
-
const response = await
|
|
7178
|
+
const response = await prompts9(
|
|
6630
7179
|
{
|
|
6631
7180
|
type: "text",
|
|
6632
7181
|
name: "value",
|
|
@@ -6668,7 +7217,7 @@ function registerTaskCommands(program2) {
|
|
|
6668
7217
|
allowBack = false
|
|
6669
7218
|
}) => {
|
|
6670
7219
|
let cancelled = false;
|
|
6671
|
-
const response = await
|
|
7220
|
+
const response = await prompts9(
|
|
6672
7221
|
{
|
|
6673
7222
|
type: "select",
|
|
6674
7223
|
name: "value",
|
|
@@ -6702,10 +7251,17 @@ function registerTaskCommands(program2) {
|
|
|
6702
7251
|
const tasks = await getDisplayTasks({ includeDeleted: true });
|
|
6703
7252
|
const status = typeof options.status === "string" ? options.status : void 0;
|
|
6704
7253
|
const cycleFilter = typeof options.cycle === "string" ? options.cycle.trim() : void 0;
|
|
6705
|
-
const validStatuses = /* @__PURE__ */ new Set([
|
|
7254
|
+
const validStatuses = /* @__PURE__ */ new Set([
|
|
7255
|
+
"todo",
|
|
7256
|
+
"ready",
|
|
7257
|
+
"in-review",
|
|
7258
|
+
"in-progress",
|
|
7259
|
+
"blocked",
|
|
7260
|
+
"done"
|
|
7261
|
+
]);
|
|
6706
7262
|
if (status && !validStatuses.has(status)) {
|
|
6707
7263
|
console.error(
|
|
6708
|
-
|
|
7264
|
+
chalk18.red(
|
|
6709
7265
|
`Invalid status "${status}". Use: todo, ready, in-review, in-progress, blocked, done.`
|
|
6710
7266
|
)
|
|
6711
7267
|
);
|
|
@@ -6723,29 +7279,29 @@ function registerTaskCommands(program2) {
|
|
|
6723
7279
|
style: { head: ["cyan"] }
|
|
6724
7280
|
});
|
|
6725
7281
|
const fmtMs = (ms) => {
|
|
6726
|
-
if (!ms) return
|
|
7282
|
+
if (!ms) return chalk18.gray("-");
|
|
6727
7283
|
const days = Math.floor(ms / 864e5);
|
|
6728
7284
|
const hrs = Math.floor(ms % 864e5 / 36e5);
|
|
6729
|
-
return days > 0 ?
|
|
7285
|
+
return days > 0 ? chalk18.white(`${days}d ${hrs}h`) : chalk18.white(`${hrs}h`);
|
|
6730
7286
|
};
|
|
6731
7287
|
filtered.forEach((t) => {
|
|
6732
7288
|
if (showFlow) {
|
|
6733
7289
|
const cycleTime = t.started_at && t.status === "done" ? Date.now() - new Date(t.started_at).getTime() : void 0;
|
|
6734
7290
|
table.push([
|
|
6735
|
-
|
|
7291
|
+
chalk18.white(t.id),
|
|
6736
7292
|
formatTaskStatusLabel(t.status, t.deleted_at),
|
|
6737
7293
|
t.title,
|
|
6738
|
-
t.cycle_id ?
|
|
6739
|
-
t.impact_score !== void 0 ?
|
|
6740
|
-
|
|
7294
|
+
t.cycle_id ? chalk18.cyan(t.cycle_id) : chalk18.gray("-"),
|
|
7295
|
+
t.impact_score !== void 0 ? chalk18.yellow(String(Math.round(t.impact_score))) : chalk18.gray("-"),
|
|
7296
|
+
chalk18.gray(t.assignee || "-"),
|
|
6741
7297
|
formatTaskPriority(t.priority)
|
|
6742
7298
|
]);
|
|
6743
7299
|
} else {
|
|
6744
7300
|
table.push([
|
|
6745
|
-
|
|
7301
|
+
chalk18.white(t.id),
|
|
6746
7302
|
formatTaskStatusLabel(t.status, t.deleted_at),
|
|
6747
7303
|
t.title,
|
|
6748
|
-
|
|
7304
|
+
chalk18.gray(t.assignee || "-"),
|
|
6749
7305
|
formatTaskPriority(t.priority)
|
|
6750
7306
|
]);
|
|
6751
7307
|
}
|
|
@@ -6756,7 +7312,7 @@ function registerTaskCommands(program2) {
|
|
|
6756
7312
|
const parentId = options.parent;
|
|
6757
7313
|
const parent = await taskService.getTask(parentId);
|
|
6758
7314
|
if (!parent) {
|
|
6759
|
-
console.error(
|
|
7315
|
+
console.error(chalk18.red(`
|
|
6760
7316
|
\u2716 Task ${parentId} not found.
|
|
6761
7317
|
`));
|
|
6762
7318
|
process.exitCode = 1;
|
|
@@ -6774,16 +7330,16 @@ function registerTaskCommands(program2) {
|
|
|
6774
7330
|
style: { head: ["cyan"] }
|
|
6775
7331
|
});
|
|
6776
7332
|
parentTable.push([
|
|
6777
|
-
|
|
7333
|
+
chalk18.white(parent.id),
|
|
6778
7334
|
formatTaskStatusLabel(parent.status),
|
|
6779
7335
|
parent.title,
|
|
6780
|
-
|
|
7336
|
+
chalk18.gray(parent.assignee || "-"),
|
|
6781
7337
|
formatTaskPriority(parent.priority)
|
|
6782
7338
|
]);
|
|
6783
|
-
console.log(
|
|
7339
|
+
console.log(chalk18.bold("\nParent Task"));
|
|
6784
7340
|
console.log(parentTable.toString());
|
|
6785
7341
|
if (subtasks.length === 0) {
|
|
6786
|
-
console.log(
|
|
7342
|
+
console.log(chalk18.gray("\nNo subtasks found."));
|
|
6787
7343
|
return;
|
|
6788
7344
|
}
|
|
6789
7345
|
const subtaskTable = new Table4({
|
|
@@ -6792,15 +7348,15 @@ function registerTaskCommands(program2) {
|
|
|
6792
7348
|
});
|
|
6793
7349
|
subtasks.forEach((t) => {
|
|
6794
7350
|
subtaskTable.push([
|
|
6795
|
-
|
|
7351
|
+
chalk18.white(t.id),
|
|
6796
7352
|
formatTaskStatusLabel(t.status),
|
|
6797
7353
|
t.title,
|
|
6798
|
-
|
|
7354
|
+
chalk18.gray(t.assignee || "-"),
|
|
6799
7355
|
formatTaskPriority(t.priority),
|
|
6800
7356
|
typeof t.subtask_order === "number" ? `#${t.subtask_order}` : "-"
|
|
6801
7357
|
]);
|
|
6802
7358
|
});
|
|
6803
|
-
console.log(
|
|
7359
|
+
console.log(chalk18.bold("\nSubtasks"));
|
|
6804
7360
|
console.log(subtaskTable.toString());
|
|
6805
7361
|
});
|
|
6806
7362
|
taskCmd.command("details").description("Show task details").requiredOption("--id <id>", "Task ID").action(async (options) => {
|
|
@@ -6813,77 +7369,77 @@ function registerTaskCommands(program2) {
|
|
|
6813
7369
|
const localTask = await taskService.getTask(options.id);
|
|
6814
7370
|
const task = remoteTask ?? localTask;
|
|
6815
7371
|
if (!task) {
|
|
6816
|
-
console.error(
|
|
7372
|
+
console.error(chalk18.red(`
|
|
6817
7373
|
\u2716 Task ${options.id} not found.
|
|
6818
7374
|
`));
|
|
6819
7375
|
process.exitCode = 1;
|
|
6820
7376
|
return;
|
|
6821
7377
|
}
|
|
6822
|
-
console.log(
|
|
7378
|
+
console.log(chalk18.bold(`
|
|
6823
7379
|
\u{1F4CB} Task Details: ${task.id}
|
|
6824
7380
|
`));
|
|
6825
|
-
console.log(`${
|
|
7381
|
+
console.log(`${chalk18.cyan("Title:")} ${task.title}`);
|
|
6826
7382
|
console.log(
|
|
6827
|
-
`${
|
|
7383
|
+
`${chalk18.cyan("Status:")} ${task.status.toUpperCase()}`
|
|
6828
7384
|
);
|
|
6829
7385
|
console.log(
|
|
6830
|
-
`${
|
|
7386
|
+
`${chalk18.cyan("Priority:")} ${(task.priority || "medium").toUpperCase()}`
|
|
6831
7387
|
);
|
|
6832
7388
|
if (task.assignee) {
|
|
6833
|
-
console.log(`${
|
|
7389
|
+
console.log(`${chalk18.cyan("Assignee:")} ${task.assignee}`);
|
|
6834
7390
|
}
|
|
6835
7391
|
if (task.github_issue_number) {
|
|
6836
7392
|
console.log(
|
|
6837
|
-
`${
|
|
7393
|
+
`${chalk18.cyan("GitHub Issue:")} #${task.github_issue_number}`
|
|
6838
7394
|
);
|
|
6839
7395
|
}
|
|
6840
7396
|
if (task.tags && task.tags.length > 0) {
|
|
6841
|
-
console.log(`${
|
|
7397
|
+
console.log(`${chalk18.cyan("Tags:")} ${task.tags.join(", ")}`);
|
|
6842
7398
|
}
|
|
6843
7399
|
if (task.type) {
|
|
6844
|
-
console.log(`${
|
|
7400
|
+
console.log(`${chalk18.cyan("Type:")} ${task.type}`);
|
|
6845
7401
|
}
|
|
6846
7402
|
if (typeof task.estimate_hours === "number") {
|
|
6847
|
-
console.log(`${
|
|
7403
|
+
console.log(`${chalk18.cyan("Estimate:")} ${task.estimate_hours}h`);
|
|
6848
7404
|
}
|
|
6849
7405
|
if (task.depends_on && task.depends_on.length > 0) {
|
|
6850
7406
|
console.log(
|
|
6851
|
-
`${
|
|
7407
|
+
`${chalk18.cyan("Depends On:")} ${task.depends_on.join(", ")}`
|
|
6852
7408
|
);
|
|
6853
7409
|
}
|
|
6854
7410
|
if (task.blocked_by && task.blocked_by.length > 0) {
|
|
6855
7411
|
console.log(
|
|
6856
|
-
`${
|
|
7412
|
+
`${chalk18.cyan("Blocked By:")} ${task.blocked_by.join(", ")}`
|
|
6857
7413
|
);
|
|
6858
7414
|
}
|
|
6859
7415
|
if (task.recurrence_rule) {
|
|
6860
|
-
console.log(`${
|
|
7416
|
+
console.log(`${chalk18.cyan("Recurrence:")} ${task.recurrence_rule}`);
|
|
6861
7417
|
}
|
|
6862
7418
|
if (task.owner_id) {
|
|
6863
|
-
console.log(`${
|
|
7419
|
+
console.log(`${chalk18.cyan("Owner:")} ${task.owner_id}`);
|
|
6864
7420
|
}
|
|
6865
7421
|
if (task.reviewer_id) {
|
|
6866
|
-
console.log(`${
|
|
7422
|
+
console.log(`${chalk18.cyan("Reviewer:")} ${task.reviewer_id}`);
|
|
6867
7423
|
}
|
|
6868
7424
|
if (task.deleted_at) {
|
|
6869
|
-
console.log(`${
|
|
7425
|
+
console.log(`${chalk18.cyan("Deleted At:")} ${task.deleted_at}`);
|
|
6870
7426
|
}
|
|
6871
7427
|
if (task.parent_id) {
|
|
6872
|
-
console.log(`${
|
|
7428
|
+
console.log(`${chalk18.cyan("Parent Task:")} ${task.parent_id}`);
|
|
6873
7429
|
}
|
|
6874
7430
|
if (typeof task.subtask_order === "number") {
|
|
6875
|
-
console.log(`${
|
|
7431
|
+
console.log(`${chalk18.cyan("Subtask Order:")} #${task.subtask_order}`);
|
|
6876
7432
|
}
|
|
6877
7433
|
if (task.due_at) {
|
|
6878
|
-
console.log(`${
|
|
7434
|
+
console.log(`${chalk18.cyan("Due At:")} ${task.due_at}`);
|
|
6879
7435
|
}
|
|
6880
7436
|
const createdAt = task.created_at ?? localTask?.created_at ?? "N/A";
|
|
6881
7437
|
const updatedAt = task.updated_at ?? localTask?.updated_at ?? "N/A";
|
|
6882
|
-
console.log(`${
|
|
6883
|
-
console.log(`${
|
|
7438
|
+
console.log(`${chalk18.cyan("Created At:")} ${createdAt}`);
|
|
7439
|
+
console.log(`${chalk18.cyan("Updated At:")} ${updatedAt}`);
|
|
6884
7440
|
if (task.description) {
|
|
6885
7441
|
console.log(`
|
|
6886
|
-
${
|
|
7442
|
+
${chalk18.cyan("Description:")}
|
|
6887
7443
|
${task.description}`);
|
|
6888
7444
|
}
|
|
6889
7445
|
const remoteContext = await getRemoteTaskContext(task.id);
|
|
@@ -6892,22 +7448,22 @@ ${task.description}`);
|
|
|
6892
7448
|
const relatedDecisionSource = task.related_decisions && task.related_decisions.length > 0 ? task.related_decisions : localTask?.related_decisions ?? [];
|
|
6893
7449
|
const relatedDecisions = relatedDecisionSource.map((entry) => entry.trim()).filter(Boolean);
|
|
6894
7450
|
console.log(`
|
|
6895
|
-
${
|
|
7451
|
+
${chalk18.cyan("Context:")}`);
|
|
6896
7452
|
if (effectiveTaskContextSummary) {
|
|
6897
|
-
console.log(
|
|
7453
|
+
console.log(chalk18.gray(" Summary:"));
|
|
6898
7454
|
console.log(` ${effectiveTaskContextSummary}`);
|
|
6899
7455
|
}
|
|
6900
7456
|
if (effectiveTaskContext) {
|
|
6901
|
-
console.log(
|
|
7457
|
+
console.log(chalk18.gray(" Full context:"));
|
|
6902
7458
|
console.log(` ${effectiveTaskContext}`);
|
|
6903
7459
|
}
|
|
6904
7460
|
if (!effectiveTaskContextSummary && !effectiveTaskContext) {
|
|
6905
|
-
console.log(
|
|
7461
|
+
console.log(chalk18.gray(" No context recorded."));
|
|
6906
7462
|
}
|
|
6907
7463
|
console.log(`
|
|
6908
|
-
${
|
|
7464
|
+
${chalk18.cyan("Decisions:")}`);
|
|
6909
7465
|
if (relatedDecisions.length === 0) {
|
|
6910
|
-
console.log(
|
|
7466
|
+
console.log(chalk18.gray(" No related decisions."));
|
|
6911
7467
|
} else {
|
|
6912
7468
|
for (const decision of relatedDecisions) {
|
|
6913
7469
|
console.log(` - ${decision}`);
|
|
@@ -6915,25 +7471,25 @@ ${chalk17.cyan("Decisions:")}`);
|
|
|
6915
7471
|
}
|
|
6916
7472
|
if (task.evidence && task.evidence.length > 0) {
|
|
6917
7473
|
console.log(`
|
|
6918
|
-
${
|
|
7474
|
+
${chalk18.cyan("Evidence:")}`);
|
|
6919
7475
|
task.evidence.forEach((e) => {
|
|
6920
7476
|
console.log(` - ${e}`);
|
|
6921
7477
|
});
|
|
6922
7478
|
}
|
|
6923
7479
|
if (task.actions && task.actions.length > 0) {
|
|
6924
7480
|
console.log(`
|
|
6925
|
-
${
|
|
7481
|
+
${chalk18.cyan("Actions:")}`);
|
|
6926
7482
|
task.actions.forEach((a) => {
|
|
6927
7483
|
const type = a.type.replace(/_/g, " ").toUpperCase();
|
|
6928
7484
|
console.log(
|
|
6929
|
-
` ${
|
|
7485
|
+
` ${chalk18.gray(`[${a.created_at}]`)} ${chalk18.bold(type)}${a.reasoning ? `: ${a.reasoning}` : ""}`
|
|
6930
7486
|
);
|
|
6931
7487
|
});
|
|
6932
7488
|
}
|
|
6933
7489
|
console.log("");
|
|
6934
7490
|
} catch (error) {
|
|
6935
7491
|
console.error(
|
|
6936
|
-
|
|
7492
|
+
chalk18.red(`
|
|
6937
7493
|
\u2716 Failed to get task details: ${error.message}
|
|
6938
7494
|
`)
|
|
6939
7495
|
);
|
|
@@ -6949,7 +7505,7 @@ ${chalk17.cyan("Actions:")}`);
|
|
|
6949
7505
|
const localTask = await taskService.getTask(id);
|
|
6950
7506
|
const task = remoteTask ?? localTask;
|
|
6951
7507
|
if (!task) {
|
|
6952
|
-
console.error(
|
|
7508
|
+
console.error(chalk18.red(`Task ${id} not found.`));
|
|
6953
7509
|
return;
|
|
6954
7510
|
}
|
|
6955
7511
|
const remoteContext = await getRemoteTaskContext(id);
|
|
@@ -6958,16 +7514,16 @@ ${chalk17.cyan("Actions:")}`);
|
|
|
6958
7514
|
const currentContext = remoteContext?.task_context ?? task.task_context ?? "";
|
|
6959
7515
|
if (currentContext.trim().length > 0) {
|
|
6960
7516
|
console.log(`
|
|
6961
|
-
${
|
|
7517
|
+
${chalk18.cyan("Task Context:")}
|
|
6962
7518
|
${currentContext}`);
|
|
6963
7519
|
} else {
|
|
6964
|
-
console.log(
|
|
7520
|
+
console.log(chalk18.yellow("\nNo task context found.\n"));
|
|
6965
7521
|
}
|
|
6966
7522
|
const currentSummary = remoteContext?.task_context_summary ?? task.task_context_summary ?? "";
|
|
6967
7523
|
if (currentSummary.trim().length > 0) {
|
|
6968
7524
|
console.log(
|
|
6969
7525
|
`
|
|
6970
|
-
${
|
|
7526
|
+
${chalk18.cyan("Task Context Summary:")}
|
|
6971
7527
|
${currentSummary}
|
|
6972
7528
|
`
|
|
6973
7529
|
);
|
|
@@ -6989,7 +7545,7 @@ ${currentSummary}
|
|
|
6989
7545
|
await taskService.updateTask(id, { task_context: nextContext });
|
|
6990
7546
|
}
|
|
6991
7547
|
console.log(
|
|
6992
|
-
|
|
7548
|
+
chalk18.green(
|
|
6993
7549
|
`
|
|
6994
7550
|
\u2714 Updated context for ${id}${remoteUpdated ? " (cloud + local cache)" : " (local cache)"}
|
|
6995
7551
|
`
|
|
@@ -6997,7 +7553,7 @@ ${currentSummary}
|
|
|
6997
7553
|
);
|
|
6998
7554
|
} catch (error) {
|
|
6999
7555
|
console.error(
|
|
7000
|
-
|
|
7556
|
+
chalk18.red(`Failed to update task context: ${error.message}`)
|
|
7001
7557
|
);
|
|
7002
7558
|
}
|
|
7003
7559
|
});
|
|
@@ -7007,7 +7563,7 @@ ${currentSummary}
|
|
|
7007
7563
|
const key = await tryAuthenticatedKey(configService);
|
|
7008
7564
|
const projectId = await configService.getProjectId();
|
|
7009
7565
|
if (!assignee && key && projectId) {
|
|
7010
|
-
console.log(
|
|
7566
|
+
console.log(chalk18.blue("Fetching assignable users..."));
|
|
7011
7567
|
const res = await fetch(
|
|
7012
7568
|
`${API_URL}/projects/${projectId}/collaborators`,
|
|
7013
7569
|
{
|
|
@@ -7027,7 +7583,7 @@ ${currentSummary}
|
|
|
7027
7583
|
}))
|
|
7028
7584
|
];
|
|
7029
7585
|
if (choices.length > 0) {
|
|
7030
|
-
const response = await
|
|
7586
|
+
const response = await prompts9({
|
|
7031
7587
|
type: "select",
|
|
7032
7588
|
name: "assignee",
|
|
7033
7589
|
message: "Select assignee:",
|
|
@@ -7038,7 +7594,7 @@ ${currentSummary}
|
|
|
7038
7594
|
}
|
|
7039
7595
|
}
|
|
7040
7596
|
if (!assignee) {
|
|
7041
|
-
const response = await
|
|
7597
|
+
const response = await prompts9({
|
|
7042
7598
|
type: "text",
|
|
7043
7599
|
name: "assignee",
|
|
7044
7600
|
message: "Enter assignee (User ID or GitHub username):"
|
|
@@ -7046,20 +7602,20 @@ ${currentSummary}
|
|
|
7046
7602
|
assignee = response.assignee;
|
|
7047
7603
|
}
|
|
7048
7604
|
if (!assignee) {
|
|
7049
|
-
console.log(
|
|
7605
|
+
console.log(chalk18.yellow("No assignee provided."));
|
|
7050
7606
|
return;
|
|
7051
7607
|
}
|
|
7052
7608
|
await taskService.updateTask(id, { assignee });
|
|
7053
|
-
console.log(
|
|
7609
|
+
console.log(chalk18.green(`
|
|
7054
7610
|
\u2714 Task ${id} assigned to ${assignee}
|
|
7055
7611
|
`));
|
|
7056
7612
|
if (key && projectId) {
|
|
7057
7613
|
console.log(
|
|
7058
|
-
|
|
7614
|
+
chalk18.gray("Tip: Run `vem push` to sync assignment to cloud.")
|
|
7059
7615
|
);
|
|
7060
7616
|
}
|
|
7061
7617
|
} catch (error) {
|
|
7062
|
-
console.error(
|
|
7618
|
+
console.error(chalk18.red(`Failed to assign task: ${error.message}`));
|
|
7063
7619
|
}
|
|
7064
7620
|
});
|
|
7065
7621
|
taskCmd.command("add [title]").description("Create a new task (interactive when title is omitted)").option(
|
|
@@ -7068,7 +7624,10 @@ ${currentSummary}
|
|
|
7068
7624
|
).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(
|
|
7069
7625
|
"--validation <steps>",
|
|
7070
7626
|
'Comma-separated validation steps (e.g. "pnpm build, pnpm test")'
|
|
7071
|
-
).option("--cycle <id>", "Assign to a cycle (e.g. CYCLE-001)").option(
|
|
7627
|
+
).option("--cycle <id>", "Assign to a cycle (e.g. CYCLE-001)").option(
|
|
7628
|
+
"--impact-score <score>",
|
|
7629
|
+
"Impact score 0-100 (RICE-based priority)"
|
|
7630
|
+
).option("--actor <name>", "Actor name for task creation").option("-r, --reasoning <reasoning>", "Reasoning for creation").action(async (title, options) => {
|
|
7072
7631
|
await trackCommandUsage("task add");
|
|
7073
7632
|
try {
|
|
7074
7633
|
let taskTitle = typeof title === "string" && title.trim().length > 0 ? title.trim() : void 0;
|
|
@@ -7095,9 +7654,9 @@ ${currentSummary}
|
|
|
7095
7654
|
if (!process.stdin.isTTY) {
|
|
7096
7655
|
throw new Error("Title is required in non-interactive mode.");
|
|
7097
7656
|
}
|
|
7098
|
-
console.log(
|
|
7657
|
+
console.log(chalk18.cyan("\nTask creation wizard"));
|
|
7099
7658
|
console.log(
|
|
7100
|
-
|
|
7659
|
+
chalk18.gray(
|
|
7101
7660
|
"Fill required fields first, then optional fields. Type :back to go back."
|
|
7102
7661
|
)
|
|
7103
7662
|
);
|
|
@@ -7111,7 +7670,7 @@ ${currentSummary}
|
|
|
7111
7670
|
optional: false
|
|
7112
7671
|
});
|
|
7113
7672
|
if (prompt.kind === "cancel") {
|
|
7114
|
-
console.log(
|
|
7673
|
+
console.log(chalk18.yellow("Task creation cancelled."));
|
|
7115
7674
|
return;
|
|
7116
7675
|
}
|
|
7117
7676
|
if (prompt.kind === "next") {
|
|
@@ -7130,7 +7689,7 @@ ${currentSummary}
|
|
|
7130
7689
|
allowBack: true
|
|
7131
7690
|
});
|
|
7132
7691
|
if (priorityPrompt.kind === "cancel") {
|
|
7133
|
-
console.log(
|
|
7692
|
+
console.log(chalk18.yellow("Task creation cancelled."));
|
|
7134
7693
|
return;
|
|
7135
7694
|
}
|
|
7136
7695
|
if (priorityPrompt.kind === "back") {
|
|
@@ -7156,7 +7715,7 @@ ${currentSummary}
|
|
|
7156
7715
|
}
|
|
7157
7716
|
);
|
|
7158
7717
|
if (optionalMode.kind === "cancel") {
|
|
7159
|
-
console.log(
|
|
7718
|
+
console.log(chalk18.yellow("Task creation cancelled."));
|
|
7160
7719
|
return;
|
|
7161
7720
|
}
|
|
7162
7721
|
if (optionalMode.kind === "back") {
|
|
@@ -7170,7 +7729,7 @@ ${currentSummary}
|
|
|
7170
7729
|
allowBack: true
|
|
7171
7730
|
});
|
|
7172
7731
|
if (priorityPrompt.kind === "cancel") {
|
|
7173
|
-
console.log(
|
|
7732
|
+
console.log(chalk18.yellow("Task creation cancelled."));
|
|
7174
7733
|
return;
|
|
7175
7734
|
}
|
|
7176
7735
|
if (priorityPrompt.kind === "back") {
|
|
@@ -7180,7 +7739,7 @@ ${currentSummary}
|
|
|
7180
7739
|
optional: false
|
|
7181
7740
|
});
|
|
7182
7741
|
if (titlePrompt.kind !== "next") {
|
|
7183
|
-
console.log(
|
|
7742
|
+
console.log(chalk18.yellow("Task creation cancelled."));
|
|
7184
7743
|
return;
|
|
7185
7744
|
}
|
|
7186
7745
|
taskTitle = titlePrompt.value;
|
|
@@ -7194,7 +7753,7 @@ ${currentSummary}
|
|
|
7194
7753
|
allowBack: false
|
|
7195
7754
|
});
|
|
7196
7755
|
if (retryPriorityPrompt.kind !== "next") {
|
|
7197
|
-
console.log(
|
|
7756
|
+
console.log(chalk18.yellow("Task creation cancelled."));
|
|
7198
7757
|
return;
|
|
7199
7758
|
}
|
|
7200
7759
|
priorityInput = retryPriorityPrompt.value;
|
|
@@ -7340,7 +7899,7 @@ ${currentSummary}
|
|
|
7340
7899
|
validate: field.validate
|
|
7341
7900
|
});
|
|
7342
7901
|
if (prompt.kind === "cancel") {
|
|
7343
|
-
console.log(
|
|
7902
|
+
console.log(chalk18.yellow("Task creation cancelled."));
|
|
7344
7903
|
return;
|
|
7345
7904
|
}
|
|
7346
7905
|
if (prompt.kind === "back") {
|
|
@@ -7358,7 +7917,7 @@ ${currentSummary}
|
|
|
7358
7917
|
allowBack: true
|
|
7359
7918
|
});
|
|
7360
7919
|
if (gatePrompt.kind === "cancel") {
|
|
7361
|
-
console.log(
|
|
7920
|
+
console.log(chalk18.yellow("Task creation cancelled."));
|
|
7362
7921
|
return;
|
|
7363
7922
|
}
|
|
7364
7923
|
if (gatePrompt.kind === "next" && gatePrompt.value === "skip") {
|
|
@@ -7375,7 +7934,7 @@ ${currentSummary}
|
|
|
7375
7934
|
allowBack: true
|
|
7376
7935
|
});
|
|
7377
7936
|
if (priorityPrompt.kind === "cancel") {
|
|
7378
|
-
console.log(
|
|
7937
|
+
console.log(chalk18.yellow("Task creation cancelled."));
|
|
7379
7938
|
return;
|
|
7380
7939
|
}
|
|
7381
7940
|
if (priorityPrompt.kind === "back") {
|
|
@@ -7385,7 +7944,7 @@ ${currentSummary}
|
|
|
7385
7944
|
optional: false
|
|
7386
7945
|
});
|
|
7387
7946
|
if (titlePrompt.kind !== "next") {
|
|
7388
|
-
console.log(
|
|
7947
|
+
console.log(chalk18.yellow("Task creation cancelled."));
|
|
7389
7948
|
return;
|
|
7390
7949
|
}
|
|
7391
7950
|
taskTitle = titlePrompt.value;
|
|
@@ -7415,19 +7974,21 @@ ${currentSummary}
|
|
|
7415
7974
|
const dueAt = parseDueAtIso(dueAtInput);
|
|
7416
7975
|
const normalizedType = typeInput?.trim().toLowerCase();
|
|
7417
7976
|
if (normalizedType && normalizedType !== "feature" && normalizedType !== "bug" && normalizedType !== "chore" && normalizedType !== "spike" && normalizedType !== "enabler") {
|
|
7418
|
-
throw new Error(
|
|
7977
|
+
throw new Error(
|
|
7978
|
+
"type must be feature, bug, chore, spike, or enabler."
|
|
7979
|
+
);
|
|
7419
7980
|
}
|
|
7420
7981
|
const taskType = normalizedType === "feature" || normalizedType === "bug" || normalizedType === "chore" || normalizedType === "spike" || normalizedType === "enabler" ? normalizedType : void 0;
|
|
7421
7982
|
let validationSteps = parseCommaList(validationInput);
|
|
7422
7983
|
if (validationSteps === void 0 && process.stdin.isTTY && !validationInput && !runWizard) {
|
|
7423
|
-
const wantsValidation = await
|
|
7984
|
+
const wantsValidation = await prompts9({
|
|
7424
7985
|
type: "confirm",
|
|
7425
7986
|
name: "add",
|
|
7426
7987
|
message: "Add validation steps for this task?",
|
|
7427
7988
|
initial: false
|
|
7428
7989
|
});
|
|
7429
7990
|
if (wantsValidation.add) {
|
|
7430
|
-
const response = await
|
|
7991
|
+
const response = await prompts9({
|
|
7431
7992
|
type: "text",
|
|
7432
7993
|
name: "steps",
|
|
7433
7994
|
message: 'Enter validation steps (comma-separated, e.g. "pnpm build, pnpm test"):'
|
|
@@ -7489,14 +8050,14 @@ ${currentSummary}
|
|
|
7489
8050
|
);
|
|
7490
8051
|
}
|
|
7491
8052
|
console.log(
|
|
7492
|
-
|
|
8053
|
+
chalk18.green(
|
|
7493
8054
|
`
|
|
7494
8055
|
\u2714 Task created: ${remoteTask.id} (cloud + local cache)
|
|
7495
8056
|
`
|
|
7496
8057
|
)
|
|
7497
8058
|
);
|
|
7498
8059
|
console.log(
|
|
7499
|
-
|
|
8060
|
+
chalk18.gray(
|
|
7500
8061
|
`Tip: Start working with AI context via \`vem agent --task ${remoteTask.id}\``
|
|
7501
8062
|
)
|
|
7502
8063
|
);
|
|
@@ -7526,23 +8087,26 @@ ${currentSummary}
|
|
|
7526
8087
|
}
|
|
7527
8088
|
);
|
|
7528
8089
|
console.log(
|
|
7529
|
-
|
|
8090
|
+
chalk18.green(`
|
|
7530
8091
|
\u2714 Task created: ${task.id} (local cache)
|
|
7531
8092
|
`)
|
|
7532
8093
|
);
|
|
7533
8094
|
console.log(
|
|
7534
|
-
|
|
8095
|
+
chalk18.gray(
|
|
7535
8096
|
`Tip: Start working with AI context via \`vem agent --task ${task.id}\``
|
|
7536
8097
|
)
|
|
7537
8098
|
);
|
|
7538
8099
|
} catch (error) {
|
|
7539
|
-
console.error(
|
|
8100
|
+
console.error(chalk18.red(`Failed to create task: ${error.message}`));
|
|
7540
8101
|
}
|
|
7541
8102
|
});
|
|
7542
8103
|
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(
|
|
7543
8104
|
"--validation <steps>",
|
|
7544
8105
|
"Set validation steps (comma-separated). Use empty string to clear."
|
|
7545
|
-
).option("--cycle <id>", "Assign to a cycle (e.g. CYCLE-001)").option(
|
|
8106
|
+
).option("--cycle <id>", "Assign to a cycle (e.g. CYCLE-001)").option(
|
|
8107
|
+
"--impact-score <score>",
|
|
8108
|
+
"Impact score 0-100 (RICE-based priority)"
|
|
8109
|
+
).option("--actor <name>", "Actor name for task update").option("-r, --reasoning <reasoning>", "Reasoning for update").action(async (id, options) => {
|
|
7546
8110
|
try {
|
|
7547
8111
|
const estimate = options.estimateHours !== void 0 ? Number.parseFloat(options.estimateHours) : void 0;
|
|
7548
8112
|
if (estimate !== void 0 && Number.isNaN(estimate)) {
|
|
@@ -7596,7 +8160,7 @@ ${currentSummary}
|
|
|
7596
8160
|
}
|
|
7597
8161
|
if (remoteUpdated) {
|
|
7598
8162
|
console.log(
|
|
7599
|
-
|
|
8163
|
+
chalk18.green(
|
|
7600
8164
|
`
|
|
7601
8165
|
\u2714 Task ${id} updated${localTask ? " (cloud + local cache)" : " (cloud)"}
|
|
7602
8166
|
`
|
|
@@ -7609,11 +8173,11 @@ ${currentSummary}
|
|
|
7609
8173
|
`Task ${id} not found in cloud or local cache. Verify the ID and project link.`
|
|
7610
8174
|
);
|
|
7611
8175
|
}
|
|
7612
|
-
console.log(
|
|
8176
|
+
console.log(chalk18.green(`
|
|
7613
8177
|
\u2714 Task ${id} updated (local cache)
|
|
7614
8178
|
`));
|
|
7615
8179
|
} catch (error) {
|
|
7616
|
-
console.error(
|
|
8180
|
+
console.error(chalk18.red(`Failed to update task: ${error.message}`));
|
|
7617
8181
|
}
|
|
7618
8182
|
});
|
|
7619
8183
|
taskCmd.command("done [id]").description("Mark a task as complete").option(
|
|
@@ -7635,13 +8199,13 @@ ${currentSummary}
|
|
|
7635
8199
|
);
|
|
7636
8200
|
if (inProgress.length === 0) {
|
|
7637
8201
|
console.error(
|
|
7638
|
-
|
|
8202
|
+
chalk18.yellow(
|
|
7639
8203
|
"No tasks in progress. Provide an ID explicitly or start a task first."
|
|
7640
8204
|
)
|
|
7641
8205
|
);
|
|
7642
8206
|
return;
|
|
7643
8207
|
}
|
|
7644
|
-
const response = await
|
|
8208
|
+
const response = await prompts9({
|
|
7645
8209
|
type: "select",
|
|
7646
8210
|
name: "id",
|
|
7647
8211
|
message: "Select a task to complete:",
|
|
@@ -7651,7 +8215,7 @@ ${currentSummary}
|
|
|
7651
8215
|
}))
|
|
7652
8216
|
});
|
|
7653
8217
|
if (!response.id) {
|
|
7654
|
-
console.log(
|
|
8218
|
+
console.log(chalk18.yellow("Operation cancelled."));
|
|
7655
8219
|
return;
|
|
7656
8220
|
}
|
|
7657
8221
|
id = response.id;
|
|
@@ -7663,14 +8227,14 @@ ${currentSummary}
|
|
|
7663
8227
|
const localTask = await taskService.getTask(id);
|
|
7664
8228
|
const task = remoteTask ?? localTask;
|
|
7665
8229
|
if (!task) {
|
|
7666
|
-
console.error(
|
|
8230
|
+
console.error(chalk18.red(`Task ${id} not found.`));
|
|
7667
8231
|
return;
|
|
7668
8232
|
}
|
|
7669
8233
|
const evidence = parseCommaList(options.evidence) ?? [];
|
|
7670
8234
|
const actorName = resolveActorName(options.actor);
|
|
7671
8235
|
let contextSummary = options.contextSummary;
|
|
7672
8236
|
if (!contextSummary && task.task_context && process.stdin.isTTY) {
|
|
7673
|
-
const summary = await
|
|
8237
|
+
const summary = await prompts9({
|
|
7674
8238
|
type: "text",
|
|
7675
8239
|
name: "text",
|
|
7676
8240
|
message: "Task has context. Provide a brief summary to keep after completion (optional):"
|
|
@@ -7690,7 +8254,7 @@ ${currentSummary}
|
|
|
7690
8254
|
}
|
|
7691
8255
|
const confirmed = [];
|
|
7692
8256
|
for (const step of requiredValidation) {
|
|
7693
|
-
const response = await
|
|
8257
|
+
const response = await prompts9({
|
|
7694
8258
|
type: "confirm",
|
|
7695
8259
|
name: "done",
|
|
7696
8260
|
message: `Validation step completed? ${step}`,
|
|
@@ -7698,7 +8262,7 @@ ${currentSummary}
|
|
|
7698
8262
|
});
|
|
7699
8263
|
if (!response.done) {
|
|
7700
8264
|
console.log(
|
|
7701
|
-
|
|
8265
|
+
chalk18.yellow(
|
|
7702
8266
|
"Task completion cancelled. Complete all validation steps first."
|
|
7703
8267
|
)
|
|
7704
8268
|
);
|
|
@@ -7745,7 +8309,7 @@ ${currentSummary}
|
|
|
7745
8309
|
}
|
|
7746
8310
|
if (remoteUpdated || remoteContextUpdated) {
|
|
7747
8311
|
console.log(
|
|
7748
|
-
|
|
8312
|
+
chalk18.green(
|
|
7749
8313
|
`
|
|
7750
8314
|
\u2714 Task ${id} marked as DONE${localTask ? " (cloud + local cache)" : " (cloud)"}
|
|
7751
8315
|
`
|
|
@@ -7759,12 +8323,12 @@ ${currentSummary}
|
|
|
7759
8323
|
);
|
|
7760
8324
|
}
|
|
7761
8325
|
console.log(
|
|
7762
|
-
|
|
8326
|
+
chalk18.green(`
|
|
7763
8327
|
\u2714 Task ${id} marked as DONE (local cache)
|
|
7764
8328
|
`)
|
|
7765
8329
|
);
|
|
7766
8330
|
} catch (error) {
|
|
7767
|
-
console.error(
|
|
8331
|
+
console.error(chalk18.red(`Failed to complete task: ${error.message}`));
|
|
7768
8332
|
}
|
|
7769
8333
|
});
|
|
7770
8334
|
taskCmd.command("start [id]").description("Start working on a task (set status to in-progress)").option("-r, --reasoning <reasoning>", "Reasoning for starting the task").option("--actor <name>", "Actor name").action(async (id, options) => {
|
|
@@ -7776,10 +8340,10 @@ ${currentSummary}
|
|
|
7776
8340
|
(t) => t.status === "todo" && !t.deleted_at
|
|
7777
8341
|
);
|
|
7778
8342
|
if (todoTasks.length === 0) {
|
|
7779
|
-
console.error(
|
|
8343
|
+
console.error(chalk18.yellow("No tasks in TODO status to start."));
|
|
7780
8344
|
return;
|
|
7781
8345
|
}
|
|
7782
|
-
const response = await
|
|
8346
|
+
const response = await prompts9({
|
|
7783
8347
|
type: "select",
|
|
7784
8348
|
name: "id",
|
|
7785
8349
|
message: "Select a task to start:",
|
|
@@ -7789,7 +8353,7 @@ ${currentSummary}
|
|
|
7789
8353
|
}))
|
|
7790
8354
|
});
|
|
7791
8355
|
if (!response.id) {
|
|
7792
|
-
console.log(
|
|
8356
|
+
console.log(chalk18.yellow("Operation cancelled."));
|
|
7793
8357
|
return;
|
|
7794
8358
|
}
|
|
7795
8359
|
id = response.id;
|
|
@@ -7801,15 +8365,15 @@ ${currentSummary}
|
|
|
7801
8365
|
const localTask = await taskService.getTask(id);
|
|
7802
8366
|
const task = remoteTask ?? localTask;
|
|
7803
8367
|
if (!task) {
|
|
7804
|
-
console.error(
|
|
8368
|
+
console.error(chalk18.red(`Task ${id} not found.`));
|
|
7805
8369
|
return;
|
|
7806
8370
|
}
|
|
7807
8371
|
if (task.status === "in-progress") {
|
|
7808
|
-
console.log(
|
|
8372
|
+
console.log(chalk18.yellow(`Task ${id} is already in progress.`));
|
|
7809
8373
|
return;
|
|
7810
8374
|
}
|
|
7811
8375
|
if (task.status === "done") {
|
|
7812
|
-
console.error(
|
|
8376
|
+
console.error(chalk18.red(`Task ${id} is already completed.`));
|
|
7813
8377
|
return;
|
|
7814
8378
|
}
|
|
7815
8379
|
const reasoning = options.reasoning || "Started working on task";
|
|
@@ -7867,7 +8431,7 @@ ${currentSummary}
|
|
|
7867
8431
|
}
|
|
7868
8432
|
if (remoteUpdated) {
|
|
7869
8433
|
console.log(
|
|
7870
|
-
|
|
8434
|
+
chalk18.green(
|
|
7871
8435
|
`
|
|
7872
8436
|
\u2714 Task ${id} is now IN PROGRESS${localTask ? " (cloud + local cache)" : " (cloud)"}
|
|
7873
8437
|
`
|
|
@@ -7881,12 +8445,12 @@ ${currentSummary}
|
|
|
7881
8445
|
);
|
|
7882
8446
|
}
|
|
7883
8447
|
console.log(
|
|
7884
|
-
|
|
8448
|
+
chalk18.green(`
|
|
7885
8449
|
\u2714 Task ${id} is now IN PROGRESS (local cache)
|
|
7886
8450
|
`)
|
|
7887
8451
|
);
|
|
7888
8452
|
} catch (error) {
|
|
7889
|
-
console.error(
|
|
8453
|
+
console.error(chalk18.red(`Failed to start task: ${error.message}`));
|
|
7890
8454
|
}
|
|
7891
8455
|
});
|
|
7892
8456
|
taskCmd.command("block <id>").description("Mark a task as blocked").option("-r, --reasoning <reasoning>", "Reason for blocking (required)").option("--blocked-by <ids>", "Comma-separated task IDs blocking this task").option("--actor <name>", "Actor name").action(async (id, options) => {
|
|
@@ -7898,16 +8462,16 @@ ${currentSummary}
|
|
|
7898
8462
|
const localTask = await taskService.getTask(id);
|
|
7899
8463
|
const task = remoteTask ?? localTask;
|
|
7900
8464
|
if (!task) {
|
|
7901
|
-
console.error(
|
|
8465
|
+
console.error(chalk18.red(`Task ${id} not found.`));
|
|
7902
8466
|
return;
|
|
7903
8467
|
}
|
|
7904
8468
|
if (task.status === "done") {
|
|
7905
|
-
console.error(
|
|
8469
|
+
console.error(chalk18.red(`Cannot block a completed task.`));
|
|
7906
8470
|
return;
|
|
7907
8471
|
}
|
|
7908
8472
|
if (!options.reasoning) {
|
|
7909
8473
|
console.error(
|
|
7910
|
-
|
|
8474
|
+
chalk18.red(
|
|
7911
8475
|
"Reasoning is required when blocking a task. Use -r or --reasoning."
|
|
7912
8476
|
)
|
|
7913
8477
|
);
|
|
@@ -7931,7 +8495,7 @@ ${currentSummary}
|
|
|
7931
8495
|
}
|
|
7932
8496
|
if (remoteUpdated) {
|
|
7933
8497
|
console.log(
|
|
7934
|
-
|
|
8498
|
+
chalk18.yellow(
|
|
7935
8499
|
`
|
|
7936
8500
|
\u26A0 Task ${id} is now BLOCKED${localTask ? " (cloud + local cache)" : " (cloud)"}
|
|
7937
8501
|
`
|
|
@@ -7945,12 +8509,12 @@ ${currentSummary}
|
|
|
7945
8509
|
);
|
|
7946
8510
|
}
|
|
7947
8511
|
console.log(
|
|
7948
|
-
|
|
8512
|
+
chalk18.yellow(`
|
|
7949
8513
|
\u26A0 Task ${id} is now BLOCKED (local cache)
|
|
7950
8514
|
`)
|
|
7951
8515
|
);
|
|
7952
8516
|
} catch (error) {
|
|
7953
|
-
console.error(
|
|
8517
|
+
console.error(chalk18.red(`Failed to block task: ${error.message}`));
|
|
7954
8518
|
}
|
|
7955
8519
|
});
|
|
7956
8520
|
taskCmd.command("unblock <id>").description("Unblock a task (set status back to todo)").option("-r, --reasoning <reasoning>", "Reason for unblocking").option("--actor <name>", "Actor name").action(async (id, options) => {
|
|
@@ -7962,12 +8526,12 @@ ${currentSummary}
|
|
|
7962
8526
|
const localTask = await taskService.getTask(id);
|
|
7963
8527
|
const task = remoteTask ?? localTask;
|
|
7964
8528
|
if (!task) {
|
|
7965
|
-
console.error(
|
|
8529
|
+
console.error(chalk18.red(`Task ${id} not found.`));
|
|
7966
8530
|
return;
|
|
7967
8531
|
}
|
|
7968
8532
|
if (task.status !== "blocked") {
|
|
7969
8533
|
console.log(
|
|
7970
|
-
|
|
8534
|
+
chalk18.yellow(`Task ${id} is not blocked (status: ${task.status}).`)
|
|
7971
8535
|
);
|
|
7972
8536
|
return;
|
|
7973
8537
|
}
|
|
@@ -7989,7 +8553,7 @@ ${currentSummary}
|
|
|
7989
8553
|
}
|
|
7990
8554
|
if (remoteUpdated) {
|
|
7991
8555
|
console.log(
|
|
7992
|
-
|
|
8556
|
+
chalk18.green(
|
|
7993
8557
|
`
|
|
7994
8558
|
\u2714 Task ${id} is now unblocked (TODO)${localTask ? " (cloud + local cache)" : " (cloud)"}
|
|
7995
8559
|
`
|
|
@@ -8003,12 +8567,12 @@ ${currentSummary}
|
|
|
8003
8567
|
);
|
|
8004
8568
|
}
|
|
8005
8569
|
console.log(
|
|
8006
|
-
|
|
8570
|
+
chalk18.green(`
|
|
8007
8571
|
\u2714 Task ${id} is now unblocked (TODO) (local cache)
|
|
8008
8572
|
`)
|
|
8009
8573
|
);
|
|
8010
8574
|
} catch (error) {
|
|
8011
|
-
console.error(
|
|
8575
|
+
console.error(chalk18.red(`Failed to unblock task: ${error.message}`));
|
|
8012
8576
|
}
|
|
8013
8577
|
});
|
|
8014
8578
|
taskCmd.command("delete <id>").description("Soft delete a task").option("-r, --reasoning <reasoning>", "Reasoning for deletion").action(async (id, options) => {
|
|
@@ -8020,7 +8584,7 @@ ${currentSummary}
|
|
|
8020
8584
|
const localTask = await taskService.getTask(id);
|
|
8021
8585
|
const task = remoteTask ?? localTask;
|
|
8022
8586
|
if (!task) {
|
|
8023
|
-
console.error(
|
|
8587
|
+
console.error(chalk18.red(`Task ${id} not found.`));
|
|
8024
8588
|
return;
|
|
8025
8589
|
}
|
|
8026
8590
|
const deletedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -8036,7 +8600,7 @@ ${currentSummary}
|
|
|
8036
8600
|
}
|
|
8037
8601
|
if (remoteUpdated) {
|
|
8038
8602
|
console.log(
|
|
8039
|
-
|
|
8603
|
+
chalk18.green(
|
|
8040
8604
|
`
|
|
8041
8605
|
\u2714 Task ${id} soft deleted${localTask ? " (cloud + local cache)" : " (cloud)"}
|
|
8042
8606
|
`
|
|
@@ -8049,11 +8613,11 @@ ${currentSummary}
|
|
|
8049
8613
|
`Task ${id} not found in cloud or local cache. Verify the ID and project link.`
|
|
8050
8614
|
);
|
|
8051
8615
|
}
|
|
8052
|
-
console.log(
|
|
8616
|
+
console.log(chalk18.green(`
|
|
8053
8617
|
\u2714 Task ${id} soft deleted (local cache)
|
|
8054
8618
|
`));
|
|
8055
8619
|
} catch (error) {
|
|
8056
|
-
console.error(
|
|
8620
|
+
console.error(chalk18.red(`Failed to delete task: ${error.message}`));
|
|
8057
8621
|
}
|
|
8058
8622
|
});
|
|
8059
8623
|
program2.command("delete <id>").description("Soft delete a task").option("-r, --reasoning <reasoning>", "Reasoning for deletion").action(async (id, options) => {
|
|
@@ -8065,7 +8629,7 @@ ${currentSummary}
|
|
|
8065
8629
|
const localTask = await taskService.getTask(id);
|
|
8066
8630
|
const task = remoteTask ?? localTask;
|
|
8067
8631
|
if (!task) {
|
|
8068
|
-
console.error(
|
|
8632
|
+
console.error(chalk18.red(`Task ${id} not found.`));
|
|
8069
8633
|
return;
|
|
8070
8634
|
}
|
|
8071
8635
|
const deletedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -8081,7 +8645,7 @@ ${currentSummary}
|
|
|
8081
8645
|
}
|
|
8082
8646
|
if (remoteUpdated) {
|
|
8083
8647
|
console.log(
|
|
8084
|
-
|
|
8648
|
+
chalk18.green(
|
|
8085
8649
|
`
|
|
8086
8650
|
\u2714 Task ${id} soft deleted${localTask ? " (cloud + local cache)" : " (cloud)"}
|
|
8087
8651
|
`
|
|
@@ -8094,11 +8658,11 @@ ${currentSummary}
|
|
|
8094
8658
|
`Task ${id} not found in cloud or local cache. Verify the ID and project link.`
|
|
8095
8659
|
);
|
|
8096
8660
|
}
|
|
8097
|
-
console.log(
|
|
8661
|
+
console.log(chalk18.green(`
|
|
8098
8662
|
\u2714 Task ${id} soft deleted (local cache)
|
|
8099
8663
|
`));
|
|
8100
8664
|
} catch (error) {
|
|
8101
|
-
console.error(
|
|
8665
|
+
console.error(chalk18.red(`Failed to delete task: ${error.message}`));
|
|
8102
8666
|
}
|
|
8103
8667
|
});
|
|
8104
8668
|
taskCmd.command("sessions <id>").description("Show all agent sessions attached to a task").action(async (id) => {
|
|
@@ -8106,17 +8670,17 @@ ${currentSummary}
|
|
|
8106
8670
|
try {
|
|
8107
8671
|
const task = await taskService.getTask(id);
|
|
8108
8672
|
if (!task) {
|
|
8109
|
-
console.error(
|
|
8673
|
+
console.error(chalk18.red(`Task ${id} not found.`));
|
|
8110
8674
|
return;
|
|
8111
8675
|
}
|
|
8112
8676
|
const sessions = task.sessions || [];
|
|
8113
8677
|
if (sessions.length === 0) {
|
|
8114
8678
|
console.log(
|
|
8115
|
-
|
|
8679
|
+
chalk18.yellow(`
|
|
8116
8680
|
No agent sessions attached to ${id} yet.`)
|
|
8117
8681
|
);
|
|
8118
8682
|
console.log(
|
|
8119
|
-
|
|
8683
|
+
chalk18.gray(
|
|
8120
8684
|
` Run "vem task start ${id}" to attach the current session.
|
|
8121
8685
|
`
|
|
8122
8686
|
)
|
|
@@ -8124,23 +8688,23 @@ No agent sessions attached to ${id} yet.`)
|
|
|
8124
8688
|
return;
|
|
8125
8689
|
}
|
|
8126
8690
|
console.log(
|
|
8127
|
-
|
|
8691
|
+
chalk18.bold(`
|
|
8128
8692
|
\u{1F517} Sessions attached to ${id}: ${task.title}
|
|
8129
8693
|
`)
|
|
8130
8694
|
);
|
|
8131
8695
|
const table = new Table4({
|
|
8132
8696
|
head: ["Source", "Session ID", "Started", "Summary"].map(
|
|
8133
|
-
(h) =>
|
|
8697
|
+
(h) => chalk18.white.bold(h)
|
|
8134
8698
|
),
|
|
8135
8699
|
colWidths: [10, 20, 18, 50],
|
|
8136
8700
|
style: { border: ["gray"] }
|
|
8137
8701
|
});
|
|
8138
8702
|
for (const s of sessions) {
|
|
8139
|
-
const sourceColor = s.source === "copilot" ?
|
|
8703
|
+
const sourceColor = s.source === "copilot" ? chalk18.blue : s.source === "claude" ? chalk18.magenta : chalk18.green;
|
|
8140
8704
|
table.push([
|
|
8141
8705
|
sourceColor(s.source),
|
|
8142
|
-
|
|
8143
|
-
|
|
8706
|
+
chalk18.gray(`${s.id.slice(0, 16)}\u2026`),
|
|
8707
|
+
chalk18.white(
|
|
8144
8708
|
new Date(s.started_at).toLocaleDateString(void 0, {
|
|
8145
8709
|
month: "short",
|
|
8146
8710
|
day: "numeric",
|
|
@@ -8148,14 +8712,14 @@ No agent sessions attached to ${id} yet.`)
|
|
|
8148
8712
|
minute: "2-digit"
|
|
8149
8713
|
})
|
|
8150
8714
|
),
|
|
8151
|
-
|
|
8715
|
+
chalk18.gray(s.summary?.slice(0, 48) || "\u2014")
|
|
8152
8716
|
]);
|
|
8153
8717
|
}
|
|
8154
8718
|
console.log(table.toString());
|
|
8155
8719
|
console.log();
|
|
8156
8720
|
} catch (error) {
|
|
8157
8721
|
console.error(
|
|
8158
|
-
|
|
8722
|
+
chalk18.red(`Failed to show task sessions: ${error.message}`)
|
|
8159
8723
|
);
|
|
8160
8724
|
}
|
|
8161
8725
|
});
|
|
@@ -8167,50 +8731,66 @@ No agent sessions attached to ${id} yet.`)
|
|
|
8167
8731
|
if (id) {
|
|
8168
8732
|
const task = await taskService.getTask(id);
|
|
8169
8733
|
if (!task) {
|
|
8170
|
-
console.error(
|
|
8734
|
+
console.error(chalk18.red(`Task ${id} not found.`));
|
|
8171
8735
|
return;
|
|
8172
8736
|
}
|
|
8173
8737
|
const metrics = await taskService.getFlowMetrics(id);
|
|
8174
8738
|
const fmtMs = (ms) => {
|
|
8175
|
-
if (!ms) return
|
|
8739
|
+
if (!ms) return chalk18.gray("\u2014");
|
|
8176
8740
|
const days = Math.floor(ms / 864e5);
|
|
8177
8741
|
const hrs = Math.floor(ms % 864e5 / 36e5);
|
|
8178
8742
|
const mins = Math.floor(ms % 36e5 / 6e4);
|
|
8179
|
-
if (days > 0) return
|
|
8180
|
-
if (hrs > 0) return
|
|
8181
|
-
return
|
|
8743
|
+
if (days > 0) return chalk18.white(`${days}d ${hrs}h`);
|
|
8744
|
+
if (hrs > 0) return chalk18.white(`${hrs}h ${mins}m`);
|
|
8745
|
+
return chalk18.white(`${mins}m`);
|
|
8182
8746
|
};
|
|
8183
|
-
console.log(
|
|
8747
|
+
console.log(chalk18.bold(`
|
|
8184
8748
|
\u23F1 Flow Metrics: ${id} \u2014 ${task.title}
|
|
8185
8749
|
`));
|
|
8186
|
-
console.log(
|
|
8187
|
-
|
|
8750
|
+
console.log(
|
|
8751
|
+
` ${chalk18.gray("Lead time (created \u2192 done):")} ${fmtMs(metrics.lead_time_ms)}`
|
|
8752
|
+
);
|
|
8753
|
+
console.log(
|
|
8754
|
+
` ${chalk18.gray("Cycle time (started \u2192 done):")} ${fmtMs(metrics.cycle_time_ms)}`
|
|
8755
|
+
);
|
|
8188
8756
|
if (Object.keys(metrics.time_in_status).length > 0) {
|
|
8189
|
-
console.log(
|
|
8757
|
+
console.log(chalk18.gray("\n Time in each status:"));
|
|
8190
8758
|
for (const [status, ms] of Object.entries(metrics.time_in_status)) {
|
|
8191
|
-
console.log(` ${
|
|
8759
|
+
console.log(` ${chalk18.cyan(status.padEnd(12))} ${fmtMs(ms)}`);
|
|
8192
8760
|
}
|
|
8193
8761
|
}
|
|
8194
8762
|
console.log();
|
|
8195
8763
|
} else {
|
|
8196
8764
|
const summary = await taskService.getProjectFlowSummary();
|
|
8197
8765
|
const fmtMs = (ms) => {
|
|
8198
|
-
if (!ms) return
|
|
8766
|
+
if (!ms) return chalk18.gray("\u2014");
|
|
8199
8767
|
const days = Math.floor(ms / 864e5);
|
|
8200
8768
|
const hrs = Math.floor(ms % 864e5 / 36e5);
|
|
8201
|
-
if (days > 0) return
|
|
8202
|
-
return
|
|
8769
|
+
if (days > 0) return chalk18.white(`${days}d ${hrs}h`);
|
|
8770
|
+
return chalk18.white(`${hrs}h`);
|
|
8203
8771
|
};
|
|
8204
|
-
console.log(
|
|
8205
|
-
console.log(
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
console.log(
|
|
8209
|
-
|
|
8772
|
+
console.log(chalk18.bold("\n\u{1F4CA} Project Flow Summary\n"));
|
|
8773
|
+
console.log(
|
|
8774
|
+
` ${chalk18.gray("WIP (active tasks):")} ${chalk18.yellow(String(summary.wip_count))}`
|
|
8775
|
+
);
|
|
8776
|
+
console.log(
|
|
8777
|
+
` ${chalk18.gray("Throughput (last 7d):")} ${chalk18.white(String(summary.throughput_last_7d))} tasks`
|
|
8778
|
+
);
|
|
8779
|
+
console.log(
|
|
8780
|
+
` ${chalk18.gray("Throughput (last 30d):")} ${chalk18.white(String(summary.throughput_last_30d))} tasks`
|
|
8781
|
+
);
|
|
8782
|
+
console.log(
|
|
8783
|
+
` ${chalk18.gray("Avg cycle time:")} ${fmtMs(summary.avg_cycle_time_ms)}`
|
|
8784
|
+
);
|
|
8785
|
+
console.log(
|
|
8786
|
+
` ${chalk18.gray("Avg lead time:")} ${fmtMs(summary.avg_lead_time_ms)}`
|
|
8787
|
+
);
|
|
8210
8788
|
console.log();
|
|
8211
8789
|
}
|
|
8212
8790
|
} catch (error) {
|
|
8213
|
-
console.error(
|
|
8791
|
+
console.error(
|
|
8792
|
+
chalk18.red(`Failed to get flow metrics: ${error.message}`)
|
|
8793
|
+
);
|
|
8214
8794
|
}
|
|
8215
8795
|
});
|
|
8216
8796
|
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) => {
|
|
@@ -8222,7 +8802,9 @@ No agent sessions attached to ${id} yet.`)
|
|
|
8222
8802
|
(t) => t.impact_score === void 0 && t.status !== "done" && !t.deleted_at
|
|
8223
8803
|
);
|
|
8224
8804
|
if (unscored.length === 0) {
|
|
8225
|
-
console.log(
|
|
8805
|
+
console.log(
|
|
8806
|
+
chalk18.green("\n\u2714 All active tasks have impact scores.\n")
|
|
8807
|
+
);
|
|
8226
8808
|
return;
|
|
8227
8809
|
}
|
|
8228
8810
|
const table = new Table4({
|
|
@@ -8232,28 +8814,34 @@ No agent sessions attached to ${id} yet.`)
|
|
|
8232
8814
|
const all = tasks.filter((t) => t.status !== "done" && !t.deleted_at);
|
|
8233
8815
|
for (const t of all) {
|
|
8234
8816
|
table.push([
|
|
8235
|
-
|
|
8817
|
+
chalk18.white(t.id),
|
|
8236
8818
|
t.title,
|
|
8237
8819
|
formatTaskPriority(t.priority),
|
|
8238
|
-
t.impact_score !== void 0 ?
|
|
8820
|
+
t.impact_score !== void 0 ? chalk18.yellow(String(Math.round(t.impact_score))) : chalk18.gray("\u2014")
|
|
8239
8821
|
]);
|
|
8240
8822
|
}
|
|
8241
|
-
console.log(
|
|
8823
|
+
console.log(chalk18.bold("\n\u{1F3AF} Impact Scores\n"));
|
|
8242
8824
|
console.log(table.toString());
|
|
8243
|
-
console.log(
|
|
8825
|
+
console.log(
|
|
8826
|
+
chalk18.gray(
|
|
8827
|
+
`
|
|
8244
8828
|
Unscored: ${unscored.length} task(s). Use: vem task score <id> --set <0-100>
|
|
8245
|
-
`
|
|
8829
|
+
`
|
|
8830
|
+
)
|
|
8831
|
+
);
|
|
8246
8832
|
return;
|
|
8247
8833
|
}
|
|
8248
8834
|
const task = await taskService.getTask(id);
|
|
8249
8835
|
if (!task) {
|
|
8250
|
-
console.error(
|
|
8836
|
+
console.error(chalk18.red(`Task ${id} not found.`));
|
|
8251
8837
|
return;
|
|
8252
8838
|
}
|
|
8253
8839
|
if (options.set !== void 0) {
|
|
8254
8840
|
const score = Number.parseFloat(options.set);
|
|
8255
8841
|
if (Number.isNaN(score) || score < 0 || score > 100) {
|
|
8256
|
-
console.error(
|
|
8842
|
+
console.error(
|
|
8843
|
+
chalk18.red("Score must be a number between 0 and 100.")
|
|
8844
|
+
);
|
|
8257
8845
|
process.exitCode = 1;
|
|
8258
8846
|
return;
|
|
8259
8847
|
}
|
|
@@ -8261,18 +8849,24 @@ No agent sessions attached to ${id} yet.`)
|
|
|
8261
8849
|
impact_score: score,
|
|
8262
8850
|
reasoning: options.reasoning
|
|
8263
8851
|
});
|
|
8264
|
-
console.log(
|
|
8852
|
+
console.log(
|
|
8853
|
+
chalk18.green(`
|
|
8265
8854
|
\u2714 Impact score for ${id} set to ${score}
|
|
8266
|
-
`)
|
|
8855
|
+
`)
|
|
8856
|
+
);
|
|
8267
8857
|
} else {
|
|
8268
|
-
console.log(
|
|
8858
|
+
console.log(chalk18.bold(`
|
|
8269
8859
|
\u{1F3AF} ${id}: ${task.title}`));
|
|
8270
|
-
console.log(
|
|
8271
|
-
|
|
8272
|
-
|
|
8860
|
+
console.log(
|
|
8861
|
+
` Impact score: ${task.impact_score !== void 0 ? chalk18.yellow(String(Math.round(task.impact_score))) : chalk18.gray("not set")}`
|
|
8862
|
+
);
|
|
8863
|
+
console.log(
|
|
8864
|
+
chalk18.gray(` Set with: vem task score ${id} --set <0-100>
|
|
8865
|
+
`)
|
|
8866
|
+
);
|
|
8273
8867
|
}
|
|
8274
8868
|
} catch (error) {
|
|
8275
|
-
console.error(
|
|
8869
|
+
console.error(chalk18.red(`Failed to manage score: ${error.message}`));
|
|
8276
8870
|
}
|
|
8277
8871
|
});
|
|
8278
8872
|
taskCmd.command("ready [id]").description("Mark a task as ready (refined and ready to start)").option("-r, --reasoning <reasoning>", "Reasoning for marking ready").option("--actor <name>", "Actor name").action(async (id, options) => {
|
|
@@ -8284,10 +8878,10 @@ No agent sessions attached to ${id} yet.`)
|
|
|
8284
8878
|
(t) => t.status === "todo" && !t.deleted_at
|
|
8285
8879
|
);
|
|
8286
8880
|
if (todos.length === 0) {
|
|
8287
|
-
console.error(
|
|
8881
|
+
console.error(chalk18.yellow("No todo tasks found."));
|
|
8288
8882
|
return;
|
|
8289
8883
|
}
|
|
8290
|
-
const response = await
|
|
8884
|
+
const response = await prompts9({
|
|
8291
8885
|
type: "select",
|
|
8292
8886
|
name: "value",
|
|
8293
8887
|
message: "Which task is ready to start?",
|
|
@@ -8305,11 +8899,11 @@ No agent sessions attached to ${id} yet.`)
|
|
|
8305
8899
|
reasoning: options.reasoning || "Marked as refined and ready to start.",
|
|
8306
8900
|
actor: actorName
|
|
8307
8901
|
});
|
|
8308
|
-
console.log(
|
|
8902
|
+
console.log(chalk18.cyan(`
|
|
8309
8903
|
\u2714 Task ${id} marked as ready
|
|
8310
8904
|
`));
|
|
8311
8905
|
} catch (error) {
|
|
8312
|
-
console.error(
|
|
8906
|
+
console.error(chalk18.red(`Failed to mark task ready: ${error.message}`));
|
|
8313
8907
|
}
|
|
8314
8908
|
});
|
|
8315
8909
|
}
|
|
@@ -8362,25 +8956,25 @@ async function initServerMonitoring(config) {
|
|
|
8362
8956
|
await initServerMonitoring({
|
|
8363
8957
|
dsn: "https://ed007f2c213d0aa07c1be256ca51750c@o4510863861612544.ingest.de.sentry.io/4510863921774672",
|
|
8364
8958
|
environment: process.env.NODE_ENV || "production",
|
|
8365
|
-
release: "0.1.
|
|
8959
|
+
release: "0.1.54",
|
|
8366
8960
|
serviceName: "cli"
|
|
8367
8961
|
});
|
|
8368
8962
|
var program = new Command();
|
|
8369
|
-
program.name("vem").description("vem Project Memory CLI").version("0.1.
|
|
8963
|
+
program.name("vem").description("vem Project Memory CLI").version("0.1.54").addHelpText(
|
|
8370
8964
|
"after",
|
|
8371
8965
|
`
|
|
8372
|
-
${
|
|
8373
|
-
${
|
|
8374
|
-
${
|
|
8375
|
-
${
|
|
8966
|
+
${chalk19.bold("\n\u26A1 Power Workflows:")}
|
|
8967
|
+
${chalk19.cyan("vem agent")} Start AI-assisted work (${chalk19.bold("recommended")})
|
|
8968
|
+
${chalk19.cyan("vem quickstart")} Interactive setup wizard
|
|
8969
|
+
${chalk19.cyan("vem status")} Check your power feature usage
|
|
8376
8970
|
|
|
8377
|
-
${
|
|
8378
|
-
1. ${
|
|
8379
|
-
2. ${
|
|
8380
|
-
3. ${
|
|
8381
|
-
4. ${
|
|
8971
|
+
${chalk19.bold("\u{1F4A1} Getting Started:")}
|
|
8972
|
+
1. ${chalk19.white("vem init")} Initialize memory
|
|
8973
|
+
2. ${chalk19.white("vem login")} Authenticate
|
|
8974
|
+
3. ${chalk19.white("vem link")} Connect to project
|
|
8975
|
+
4. ${chalk19.white("vem agent")} Start working with AI
|
|
8382
8976
|
|
|
8383
|
-
${
|
|
8977
|
+
${chalk19.gray("For full command list: vem --help")}
|
|
8384
8978
|
`
|
|
8385
8979
|
);
|
|
8386
8980
|
program.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
@@ -8398,7 +8992,7 @@ program.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
8398
8992
|
}
|
|
8399
8993
|
if (!await isVemInitialized()) {
|
|
8400
8994
|
console.error(
|
|
8401
|
-
|
|
8995
|
+
chalk19.red("\n\u2716 vem is not initialized. Run `vem init` first.\n")
|
|
8402
8996
|
);
|
|
8403
8997
|
process.exit(1);
|
|
8404
8998
|
}
|
|
@@ -8414,12 +9008,13 @@ registerSearchCommands(program);
|
|
|
8414
9008
|
registerAgentCommands(program);
|
|
8415
9009
|
registerMaintenanceCommands(program);
|
|
8416
9010
|
registerSessionsCommands(program);
|
|
9011
|
+
registerInstructionCommands(program);
|
|
8417
9012
|
await trackHelpUsageFromArgv(process.argv.slice(2));
|
|
8418
9013
|
try {
|
|
8419
9014
|
program.parse();
|
|
8420
9015
|
} catch (error) {
|
|
8421
9016
|
NodeSentry.captureException(error);
|
|
8422
|
-
console.error(
|
|
9017
|
+
console.error(chalk19.red("\n\u2716 An unexpected error occurred."));
|
|
8423
9018
|
if (process.env.NODE_ENV === "development") {
|
|
8424
9019
|
console.error(error);
|
|
8425
9020
|
}
|