@controlvector/cv-agent 1.4.0 → 1.5.0

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/bundle.cjs CHANGED
@@ -3037,7 +3037,7 @@ var {
3037
3037
  } = import_index.default;
3038
3038
 
3039
3039
  // src/commands/agent.ts
3040
- var import_node_child_process2 = require("node:child_process");
3040
+ var import_node_child_process4 = require("node:child_process");
3041
3041
 
3042
3042
  // node_modules/chalk/source/vendor/ansi-styles/index.js
3043
3043
  var ANSI_BACKGROUND_OFFSET = 10;
@@ -4089,6 +4089,277 @@ async function writeConfig(config) {
4089
4089
  await import_fs2.promises.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
4090
4090
  }
4091
4091
 
4092
+ // src/commands/git-safety.ts
4093
+ var import_node_child_process2 = require("node:child_process");
4094
+ function git(cmd, cwd) {
4095
+ return (0, import_node_child_process2.execSync)(cmd, { cwd, encoding: "utf8", timeout: 3e4 }).trim();
4096
+ }
4097
+ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
4098
+ const targetBranch = branch || "main";
4099
+ try {
4100
+ let statusOutput;
4101
+ try {
4102
+ statusOutput = git("git status --porcelain", workspaceRoot);
4103
+ } catch {
4104
+ return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false, error: "git status failed" };
4105
+ }
4106
+ const lines = statusOutput.split("\n").filter(Boolean);
4107
+ if (lines.length === 0) {
4108
+ try {
4109
+ const unpushed = git(`git log origin/${targetBranch}..HEAD --oneline 2>/dev/null`, workspaceRoot);
4110
+ if (unpushed) {
4111
+ console.log(` [git-safety] Found unpushed commits \u2014 pushing now`);
4112
+ git(`git push origin ${targetBranch}`, workspaceRoot);
4113
+ return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: true };
4114
+ }
4115
+ } catch {
4116
+ }
4117
+ return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false };
4118
+ }
4119
+ let added = 0, modified = 0, deleted = 0;
4120
+ for (const line of lines) {
4121
+ const code = line.substring(0, 2);
4122
+ if (code.includes("?")) added++;
4123
+ else if (code.includes("D")) deleted++;
4124
+ else if (code.includes("M") || code.includes("A")) modified++;
4125
+ else added++;
4126
+ }
4127
+ console.log(
4128
+ ` [git-safety] ${lines.length} uncommitted changes (${added} new, ${modified} modified, ${deleted} deleted) \u2014 committing now`
4129
+ );
4130
+ git("git add -A", workspaceRoot);
4131
+ const shortId = taskId.substring(0, 8);
4132
+ const commitMsg = `task: ${taskTitle} [${shortId}]
4133
+
4134
+ Auto-committed by cv-agent git safety net.
4135
+ Task ID: ${taskId}
4136
+ Files: ${added} added, ${modified} modified, ${deleted} deleted`;
4137
+ let commitSha;
4138
+ try {
4139
+ const commitOutput = git(`git commit -m ${JSON.stringify(commitMsg)}`, workspaceRoot);
4140
+ const shaMatch = commitOutput.match(/\[[\w/]+ ([a-f0-9]+)\]/);
4141
+ commitSha = shaMatch ? shaMatch[1] : void 0;
4142
+ } catch (e) {
4143
+ if (!e.message?.includes("nothing to commit")) {
4144
+ return {
4145
+ hadChanges: true,
4146
+ filesAdded: added,
4147
+ filesModified: modified,
4148
+ filesDeleted: deleted,
4149
+ pushed: false,
4150
+ error: `Commit failed: ${e.message}`
4151
+ };
4152
+ }
4153
+ }
4154
+ try {
4155
+ git(`git push origin ${targetBranch}`, workspaceRoot);
4156
+ console.log(` [git-safety] Committed and pushed: ${commitSha || "ok"}`);
4157
+ } catch (pushErr) {
4158
+ console.log(` [git-safety] Push failed: ${pushErr.message}`);
4159
+ return {
4160
+ hadChanges: true,
4161
+ filesAdded: added,
4162
+ filesModified: modified,
4163
+ filesDeleted: deleted,
4164
+ commitSha,
4165
+ pushed: false,
4166
+ error: `Push failed: ${pushErr.message}`
4167
+ };
4168
+ }
4169
+ return {
4170
+ hadChanges: true,
4171
+ filesAdded: added,
4172
+ filesModified: modified,
4173
+ filesDeleted: deleted,
4174
+ commitSha,
4175
+ pushed: true
4176
+ };
4177
+ } catch (err) {
4178
+ return {
4179
+ hadChanges: false,
4180
+ filesAdded: 0,
4181
+ filesModified: 0,
4182
+ filesDeleted: 0,
4183
+ pushed: false,
4184
+ error: err.message
4185
+ };
4186
+ }
4187
+ }
4188
+
4189
+ // src/commands/deploy-manifest.ts
4190
+ var import_node_child_process3 = require("node:child_process");
4191
+ var import_node_fs = require("node:fs");
4192
+ var import_node_path = require("node:path");
4193
+ function exec(cmd, cwd, timeoutMs = 3e5, env2) {
4194
+ try {
4195
+ const stdout = (0, import_node_child_process3.execSync)(cmd, {
4196
+ cwd,
4197
+ encoding: "utf8",
4198
+ timeout: timeoutMs,
4199
+ env: env2 ? { ...process.env, ...env2 } : void 0,
4200
+ stdio: ["pipe", "pipe", "pipe"]
4201
+ });
4202
+ return { ok: true, stdout, stderr: "" };
4203
+ } catch (err) {
4204
+ return { ok: false, stdout: err.stdout || "", stderr: err.stderr || err.message || "" };
4205
+ }
4206
+ }
4207
+ async function httpStatus(url, timeoutMs = 1e4) {
4208
+ try {
4209
+ const controller = new AbortController();
4210
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
4211
+ const res = await fetch(url, { signal: controller.signal });
4212
+ clearTimeout(timer);
4213
+ return res.status;
4214
+ } catch {
4215
+ return 0;
4216
+ }
4217
+ }
4218
+ async function checkHealth(url, timeoutSeconds) {
4219
+ const deadline = Date.now() + timeoutSeconds * 1e3;
4220
+ while (Date.now() < deadline) {
4221
+ const status = await httpStatus(url);
4222
+ if (status >= 200 && status < 500) return true;
4223
+ await new Promise((r) => setTimeout(r, 2e3));
4224
+ }
4225
+ return false;
4226
+ }
4227
+ function getHeadCommit(cwd) {
4228
+ try {
4229
+ return (0, import_node_child_process3.execSync)("git rev-parse HEAD", { cwd, encoding: "utf8", timeout: 5e3 }).trim();
4230
+ } catch {
4231
+ return null;
4232
+ }
4233
+ }
4234
+ function loadDeployManifest(workspaceRoot) {
4235
+ const manifestPath = (0, import_node_path.join)(workspaceRoot, ".cva", "deploy.json");
4236
+ if (!(0, import_node_fs.existsSync)(manifestPath)) return null;
4237
+ try {
4238
+ const raw = (0, import_node_fs.readFileSync)(manifestPath, "utf8");
4239
+ const manifest = JSON.parse(raw);
4240
+ if (!manifest.version || manifest.version < 1) {
4241
+ console.log(" [deploy] Invalid manifest version");
4242
+ return null;
4243
+ }
4244
+ return manifest;
4245
+ } catch (err) {
4246
+ console.log(` [deploy] Failed to read .cva/deploy.json: ${err.message}`);
4247
+ return null;
4248
+ }
4249
+ }
4250
+ async function postTaskDeploy(workspaceRoot, taskId, log) {
4251
+ const manifest = loadDeployManifest(workspaceRoot);
4252
+ if (!manifest) {
4253
+ return { deployed: false, steps: [{ step: "manifest", status: "skipped", message: "No .cva/deploy.json" }] };
4254
+ }
4255
+ const steps = [];
4256
+ const preDeployCommit = getHeadCommit(workspaceRoot);
4257
+ if (manifest.build?.steps) {
4258
+ for (const buildStep of manifest.build.steps) {
4259
+ const stepName = `build:${buildStep.name}`;
4260
+ console.log(` [deploy] Building: ${buildStep.name}`);
4261
+ log("lifecycle", `Building: ${buildStep.name}`);
4262
+ const start = Date.now();
4263
+ const cwd = (0, import_node_path.join)(workspaceRoot, buildStep.working_dir || ".");
4264
+ const timeoutMs = (buildStep.timeout_seconds || 300) * 1e3;
4265
+ const result = exec(buildStep.command, cwd, timeoutMs);
4266
+ if (!result.ok) {
4267
+ const msg = `Build failed: ${buildStep.name}
4268
+ ${result.stderr.slice(-500)}`;
4269
+ steps.push({ step: stepName, status: "failed", message: msg, durationMs: Date.now() - start });
4270
+ log("error", msg);
4271
+ return { deployed: false, steps, error: msg };
4272
+ }
4273
+ steps.push({ step: stepName, status: "ok", durationMs: Date.now() - start });
4274
+ }
4275
+ }
4276
+ if (manifest.migrate?.command) {
4277
+ console.log(" [deploy] Running migration...");
4278
+ log("lifecycle", "Applying database migration");
4279
+ const start = Date.now();
4280
+ const env2 = manifest.migrate.env || {};
4281
+ const result = exec(manifest.migrate.command, workspaceRoot, 6e4, env2);
4282
+ if (!result.ok) {
4283
+ const msg = `Migration failed:
4284
+ ${result.stderr.slice(-500)}`;
4285
+ steps.push({ step: "migrate", status: "failed", message: msg, durationMs: Date.now() - start });
4286
+ log("error", msg);
4287
+ return { deployed: false, steps, error: msg };
4288
+ }
4289
+ steps.push({ step: "migrate", status: "ok", durationMs: Date.now() - start });
4290
+ }
4291
+ if (manifest.service && manifest.service.type !== "none" && manifest.service.restart_command) {
4292
+ console.log(` [deploy] Restarting service: ${manifest.service.name || "default"}`);
4293
+ log("lifecycle", `Restarting service: ${manifest.service.name || "default"}`);
4294
+ const start = Date.now();
4295
+ const result = exec(manifest.service.restart_command, workspaceRoot, 3e4);
4296
+ if (!result.ok) {
4297
+ const msg = `Restart failed:
4298
+ ${result.stderr.slice(-300)}`;
4299
+ steps.push({ step: "restart", status: "failed", message: msg, durationMs: Date.now() - start });
4300
+ } else {
4301
+ steps.push({ step: "restart", status: "ok", durationMs: Date.now() - start });
4302
+ }
4303
+ const waitMs = (manifest.service.startup_wait_seconds || 5) * 1e3;
4304
+ await new Promise((r) => setTimeout(r, waitMs));
4305
+ }
4306
+ if (manifest.verify) {
4307
+ console.log(" [deploy] Verifying deployment...");
4308
+ log("lifecycle", "Verifying deployment");
4309
+ const timeoutSec = manifest.verify.timeout_seconds || 30;
4310
+ if (manifest.verify.health_url) {
4311
+ const healthy = await checkHealth(manifest.verify.health_url, timeoutSec);
4312
+ if (!healthy) {
4313
+ const msg = `Health check failed: ${manifest.verify.health_url} did not respond within ${timeoutSec}s`;
4314
+ steps.push({ step: "verify:health", status: "failed", message: msg });
4315
+ log("error", msg);
4316
+ if (manifest.rollback?.auto_rollback_on_verify_failure && preDeployCommit) {
4317
+ await rollback(workspaceRoot, preDeployCommit, manifest, log);
4318
+ return { deployed: false, steps, error: msg, rolledBack: true };
4319
+ }
4320
+ return { deployed: false, steps, error: msg };
4321
+ }
4322
+ steps.push({ step: "verify:health", status: "ok" });
4323
+ }
4324
+ for (const test of manifest.verify.smoke_tests || []) {
4325
+ const status = await httpStatus(test.url);
4326
+ if (!test.expected_status.includes(status)) {
4327
+ const msg = `Smoke test "${test.name}" failed: got ${status}, expected ${test.expected_status.join("|")}`;
4328
+ steps.push({ step: `verify:${test.name}`, status: "failed", message: msg });
4329
+ log("error", msg);
4330
+ if (manifest.rollback?.auto_rollback_on_verify_failure && preDeployCommit) {
4331
+ await rollback(workspaceRoot, preDeployCommit, manifest, log);
4332
+ return { deployed: false, steps, error: msg, rolledBack: true };
4333
+ }
4334
+ return { deployed: false, steps, error: msg };
4335
+ }
4336
+ steps.push({ step: `verify:${test.name}`, status: "ok" });
4337
+ }
4338
+ }
4339
+ console.log(" [deploy] Deployment verified");
4340
+ log("lifecycle", "Deployment verified successfully");
4341
+ return { deployed: true, steps };
4342
+ }
4343
+ async function rollback(workspaceRoot, targetCommit, manifest, log) {
4344
+ console.log(` [deploy] Rolling back to ${targetCommit.substring(0, 8)}`);
4345
+ log("lifecycle", `Rolling back to ${targetCommit.substring(0, 8)}`);
4346
+ try {
4347
+ exec(`git checkout ${targetCommit}`, workspaceRoot, 1e4);
4348
+ } catch {
4349
+ log("error", "Rollback: git checkout failed");
4350
+ return;
4351
+ }
4352
+ if (manifest.build?.steps) {
4353
+ for (const step of manifest.build.steps) {
4354
+ exec(step.command, (0, import_node_path.join)(workspaceRoot, step.working_dir || "."), (step.timeout_seconds || 300) * 1e3);
4355
+ }
4356
+ }
4357
+ if (manifest.service?.restart_command) {
4358
+ exec(manifest.service.restart_command, workspaceRoot, 3e4);
4359
+ }
4360
+ log("lifecycle", "Rollback complete");
4361
+ }
4362
+
4092
4363
  // src/commands/agent.ts
4093
4364
  var AUTH_ERROR_PATTERNS = [
4094
4365
  "Not logged in",
@@ -4110,7 +4381,7 @@ function containsAuthError(text) {
4110
4381
  }
4111
4382
  async function checkClaudeAuth() {
4112
4383
  try {
4113
- const output = (0, import_node_child_process2.execSync)("claude --version 2>&1", {
4384
+ const output = (0, import_node_child_process4.execSync)("claude --version 2>&1", {
4114
4385
  encoding: "utf8",
4115
4386
  timeout: 1e4,
4116
4387
  env: { ...process.env }
@@ -4321,7 +4592,7 @@ async function launchAutoApproveMode(prompt, options) {
4321
4592
  if (sessionId && !isContinue) {
4322
4593
  args.push("--session-id", sessionId);
4323
4594
  }
4324
- const child = (0, import_node_child_process2.spawn)("claude", args, {
4595
+ const child = (0, import_node_child_process4.spawn)("claude", args, {
4325
4596
  cwd: options.cwd,
4326
4597
  stdio: ["inherit", "pipe", "pipe"],
4327
4598
  env: options.spawnEnv || { ...process.env }
@@ -4455,7 +4726,7 @@ ${source_default.red("!")} Claude Code auth failure (stderr): "${authError}"`);
4455
4726
  }
4456
4727
  async function launchRelayMode(prompt, options) {
4457
4728
  return new Promise((resolve, reject) => {
4458
- const child = (0, import_node_child_process2.spawn)("claude", [], {
4729
+ const child = (0, import_node_child_process4.spawn)("claude", [], {
4459
4730
  cwd: options.cwd,
4460
4731
  stdio: ["pipe", "pipe", "pipe"],
4461
4732
  env: options.spawnEnv || { ...process.env }
@@ -4681,19 +4952,19 @@ async function handleSelfUpdate(task, state, creds) {
4681
4952
  let output = "";
4682
4953
  if (source === "npm" || source.startsWith("npm:")) {
4683
4954
  const pkg = source === "npm" ? "@controlvector/cv-agent@latest" : source.replace("npm:", "");
4684
- output = (0, import_node_child_process2.execSync)(`npm install -g ${pkg} 2>&1`, { encoding: "utf8", timeout: 12e4 });
4955
+ output = (0, import_node_child_process4.execSync)(`npm install -g ${pkg} 2>&1`, { encoding: "utf8", timeout: 12e4 });
4685
4956
  } else if (source.startsWith("git:")) {
4686
4957
  const repoPath = source.replace("git:", "");
4687
- output = (0, import_node_child_process2.execSync)(`cd ${repoPath} && git pull && npm install && npm run build && npm link 2>&1`, {
4958
+ output = (0, import_node_child_process4.execSync)(`cd ${repoPath} && git pull && npm install && npm run build && npm link 2>&1`, {
4688
4959
  encoding: "utf8",
4689
4960
  timeout: 3e5
4690
4961
  });
4691
4962
  } else {
4692
- output = (0, import_node_child_process2.execSync)(`npm install -g @controlvector/cv-agent@latest 2>&1`, { encoding: "utf8", timeout: 12e4 });
4963
+ output = (0, import_node_child_process4.execSync)(`npm install -g @controlvector/cv-agent@latest 2>&1`, { encoding: "utf8", timeout: 12e4 });
4693
4964
  }
4694
4965
  let newVersion = "unknown";
4695
4966
  try {
4696
- newVersion = (0, import_node_child_process2.execSync)("cva --version 2>/dev/null || echo unknown", { encoding: "utf8" }).trim();
4967
+ newVersion = (0, import_node_child_process4.execSync)("cva --version 2>/dev/null || echo unknown", { encoding: "utf8" }).trim();
4697
4968
  } catch {
4698
4969
  }
4699
4970
  postTaskEvent(creds, task.id, {
@@ -4719,7 +4990,7 @@ async function handleSelfUpdate(task, state, creds) {
4719
4990
  if (task.input?.constraints?.includes("restart")) {
4720
4991
  console.log(source_default.yellow("Restarting agent with updated binary..."));
4721
4992
  const args = process.argv.slice(1).join(" ");
4722
- (0, import_node_child_process2.execSync)(`nohup cva ${args} > /tmp/cva-restart.log 2>&1 &`, { stdio: "ignore" });
4993
+ (0, import_node_child_process4.execSync)(`nohup cva ${args} > /tmp/cva-restart.log 2>&1 &`, { stdio: "ignore" });
4723
4994
  process.exit(0);
4724
4995
  }
4725
4996
  } catch (err) {
@@ -4751,7 +5022,7 @@ async function runAgent(options) {
4751
5022
  process.exit(1);
4752
5023
  }
4753
5024
  try {
4754
- (0, import_node_child_process2.execSync)("claude --version", { stdio: "pipe", timeout: 5e3 });
5025
+ (0, import_node_child_process4.execSync)("claude --version", { stdio: "pipe", timeout: 5e3 });
4755
5026
  } catch {
4756
5027
  console.log();
4757
5028
  console.log(source_default.red("Claude Code CLI not found.") + " Install it first:");
@@ -4780,7 +5051,7 @@ async function runAgent(options) {
4780
5051
  currentAuthStatus = "api_key_fallback";
4781
5052
  }
4782
5053
  try {
4783
- (0, import_node_child_process2.execSync)("cv --version", { stdio: "pipe", timeout: 5e3 });
5054
+ (0, import_node_child_process4.execSync)("cv --version", { stdio: "pipe", timeout: 5e3 });
4784
5055
  } catch {
4785
5056
  console.log(source_default.yellow("!") + " cv-git CLI not found. Claude Code will fall back to raw git commands.");
4786
5057
  console.log(` Install it: ${source_default.cyan("npm install -g @controlvector/cv-git")}`);
@@ -4800,7 +5071,7 @@ async function runAgent(options) {
4800
5071
  }
4801
5072
  let detectedRepoId;
4802
5073
  try {
4803
- const remoteUrl = (0, import_node_child_process2.execSync)("git remote get-url origin 2>/dev/null", {
5074
+ const remoteUrl = (0, import_node_child_process4.execSync)("git remote get-url origin 2>/dev/null", {
4804
5075
  cwd: workingDir,
4805
5076
  encoding: "utf8",
4806
5077
  timeout: 5e3
@@ -4970,6 +5241,33 @@ ${source_default.red("Timeout")} Task timed out after ${formatDuration(timeoutMs
4970
5241
  });
4971
5242
  }
4972
5243
  console.log(source_default.gray("\n" + "-".repeat(60)));
5244
+ if (result.exitCode === 0 || result.exitCode === 1) {
5245
+ try {
5246
+ const gitSafety = gitSafetyNet(
5247
+ options.workingDir,
5248
+ task.title,
5249
+ task.id,
5250
+ task.branch
5251
+ );
5252
+ if (gitSafety.hadChanges) {
5253
+ sendTaskLog(
5254
+ creds,
5255
+ state.executorId,
5256
+ task.id,
5257
+ "git",
5258
+ `Safety net: committed ${gitSafety.filesAdded + gitSafety.filesModified} files (${gitSafety.commitSha || "ok"})`,
5259
+ { added: gitSafety.filesAdded, modified: gitSafety.filesModified, deleted: gitSafety.filesDeleted }
5260
+ );
5261
+ } else if (gitSafety.pushed) {
5262
+ sendTaskLog(creds, state.executorId, task.id, "git", "Safety net: pushed unpushed commits");
5263
+ }
5264
+ if (gitSafety.error) {
5265
+ sendTaskLog(creds, state.executorId, task.id, "info", `Git safety warning: ${gitSafety.error}`);
5266
+ }
5267
+ } catch (gitErr) {
5268
+ sendTaskLog(creds, state.executorId, task.id, "info", `Git safety net error: ${gitErr.message}`);
5269
+ }
5270
+ }
4973
5271
  const postGitState = capturePostTaskState(options.workingDir, preGitState);
4974
5272
  const payload = buildCompletionPayload(result.exitCode, preGitState, postGitState, startTime, result.output);
4975
5273
  const elapsed = formatDuration(Date.now() - startTime);
@@ -4978,6 +5276,28 @@ ${source_default.red("Timeout")} Task timed out after ${formatDuration(timeoutMs
4978
5276
  ...postGitState.filesModified,
4979
5277
  ...postGitState.filesDeleted
4980
5278
  ];
5279
+ let deployResult;
5280
+ if (result.exitCode === 0) {
5281
+ try {
5282
+ const logFn = (type, msg) => {
5283
+ sendTaskLog(creds, state.executorId, task.id, type, msg);
5284
+ };
5285
+ deployResult = await postTaskDeploy(options.workingDir, task.id, logFn);
5286
+ if (deployResult.deployed) {
5287
+ sendTaskLog(creds, state.executorId, task.id, "lifecycle", "Post-task deploy: verified");
5288
+ } else if (deployResult.error) {
5289
+ sendTaskLog(
5290
+ creds,
5291
+ state.executorId,
5292
+ task.id,
5293
+ "error",
5294
+ `Post-task deploy failed: ${deployResult.error}${deployResult.rolledBack ? " (rolled back)" : ""}`
5295
+ );
5296
+ }
5297
+ } catch (deployErr) {
5298
+ sendTaskLog(creds, state.executorId, task.id, "info", `Deploy manifest error: ${deployErr.message}`);
5299
+ }
5300
+ }
4981
5301
  postTaskEvent(creds, task.id, {
4982
5302
  event_type: "completed",
4983
5303
  content: {
@@ -5228,7 +5548,7 @@ function authCommand() {
5228
5548
  }
5229
5549
 
5230
5550
  // src/commands/remote.ts
5231
- var import_node_child_process3 = require("node:child_process");
5551
+ var import_node_child_process5 = require("node:child_process");
5232
5552
  function getGitHost(apiUrl) {
5233
5553
  return apiUrl.replace(/^https?:\/\//, "").replace(/^api\./, "git.");
5234
5554
  }
@@ -5243,16 +5563,16 @@ async function remoteAdd(ownerRepo) {
5243
5563
  }
5244
5564
  const remoteUrl = `https://${gitHost}/${owner}/${repo}.git`;
5245
5565
  try {
5246
- const existing = (0, import_node_child_process3.execSync)("git remote get-url cvhub", { encoding: "utf-8", timeout: 5e3 }).trim();
5566
+ const existing = (0, import_node_child_process5.execSync)("git remote get-url cvhub", { encoding: "utf-8", timeout: 5e3 }).trim();
5247
5567
  if (existing === remoteUrl) {
5248
5568
  console.log(source_default.gray(`Remote 'cvhub' already set to ${remoteUrl}`));
5249
5569
  return;
5250
5570
  }
5251
- (0, import_node_child_process3.execSync)(`git remote set-url cvhub ${remoteUrl}`, { timeout: 5e3 });
5571
+ (0, import_node_child_process5.execSync)(`git remote set-url cvhub ${remoteUrl}`, { timeout: 5e3 });
5252
5572
  console.log(source_default.green("Updated") + ` remote 'cvhub' -> ${remoteUrl}`);
5253
5573
  } catch {
5254
5574
  try {
5255
- (0, import_node_child_process3.execSync)(`git remote add cvhub ${remoteUrl}`, { timeout: 5e3 });
5575
+ (0, import_node_child_process5.execSync)(`git remote add cvhub ${remoteUrl}`, { timeout: 5e3 });
5256
5576
  console.log(source_default.green("Added") + ` remote 'cvhub' -> ${remoteUrl}`);
5257
5577
  } catch (err) {
5258
5578
  console.log(source_default.red("Failed to add remote:") + ` ${err.message}`);
@@ -5444,7 +5764,7 @@ function statusCommand() {
5444
5764
 
5445
5765
  // src/index.ts
5446
5766
  var program2 = new Command();
5447
- program2.name("cva").description("CV-Hub Agent \u2014 bridges Claude Code with CV-Hub task dispatch").version(true ? "1.4.0" : "1.1.0");
5767
+ program2.name("cva").description("CV-Hub Agent \u2014 bridges Claude Code with CV-Hub task dispatch").version(true ? "1.5.0" : "1.1.0");
5448
5768
  program2.addCommand(agentCommand());
5449
5769
  program2.addCommand(authCommand());
5450
5770
  program2.addCommand(remoteCommand());