@controlvector/cv-agent 1.10.0 → 1.10.1

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
@@ -3665,6 +3665,92 @@ async function validateToken(hubUrl, token) {
3665
3665
  return null;
3666
3666
  }
3667
3667
  }
3668
+ var DEVICE_CLIENT_ID = "cv-agent-cli";
3669
+ var DEVICE_SCOPES = "repo:read repo:write profile offline_access";
3670
+ var POLL_INTERVAL_MS = 5e3;
3671
+ var MAX_POLL_ATTEMPTS = 180;
3672
+ async function deviceAuthFlow(hubUrl, appUrl) {
3673
+ const authRes = await fetch(`${hubUrl}/oauth/device/authorize`, {
3674
+ method: "POST",
3675
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
3676
+ body: new URLSearchParams({
3677
+ client_id: DEVICE_CLIENT_ID,
3678
+ scope: DEVICE_SCOPES
3679
+ })
3680
+ });
3681
+ if (!authRes.ok) {
3682
+ const err = await authRes.json().catch(() => ({}));
3683
+ throw new Error(err.error_description || `Device auth failed: ${authRes.status}`);
3684
+ }
3685
+ const auth = await authRes.json();
3686
+ console.log(source_default.bold(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
3687
+ console.log(source_default.bold(" \u2502 CV-Hub Device Authorization \u2502"));
3688
+ console.log(source_default.bold(" \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"));
3689
+ console.log(source_default.bold(" \u2502 \u2502"));
3690
+ console.log(source_default.bold(" \u2502 Open this URL in your browser: \u2502"));
3691
+ console.log(source_default.bold(` \u2502 ${source_default.cyan(auth.verification_uri).padEnd(51)}\u2502`));
3692
+ console.log(source_default.bold(" \u2502 \u2502"));
3693
+ console.log(source_default.bold(" \u2502 Then enter this code: \u2502"));
3694
+ console.log(source_default.bold(` \u2502 ${source_default.white.bold(auth.user_code)} \u2502`));
3695
+ console.log(source_default.bold(" \u2502 \u2502"));
3696
+ console.log(source_default.bold(` \u2502 ${source_default.gray(`Expires in ${Math.floor(auth.expires_in / 60)} minutes`).padEnd(51)}\u2502`));
3697
+ console.log(source_default.bold(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
3698
+ console.log();
3699
+ try {
3700
+ const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
3701
+ (0, import_node_child_process.execSync)(`${openCmd} "${auth.verification_uri_complete}" 2>/dev/null`, { timeout: 5e3 });
3702
+ console.log(source_default.gray(" Browser opened. Waiting for authorization..."));
3703
+ } catch {
3704
+ console.log(source_default.gray(" Open the URL above in your browser."));
3705
+ }
3706
+ let interval = Math.max(auth.interval * 1e3, POLL_INTERVAL_MS);
3707
+ const expireTime = Date.now() + auth.expires_in * 1e3;
3708
+ for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) {
3709
+ await new Promise((r) => setTimeout(r, interval));
3710
+ if (Date.now() > expireTime) {
3711
+ throw new Error("Authorization timed out");
3712
+ }
3713
+ const remaining = Math.ceil((expireTime - Date.now()) / 1e3);
3714
+ const mins = Math.floor(remaining / 60);
3715
+ const secs = remaining % 60;
3716
+ process.stdout.write(`\r Waiting for authorization... (${mins}:${secs.toString().padStart(2, "0")} remaining) `);
3717
+ const tokenRes = await fetch(`${hubUrl}/oauth/token`, {
3718
+ method: "POST",
3719
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
3720
+ body: new URLSearchParams({
3721
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
3722
+ device_code: auth.device_code,
3723
+ client_id: DEVICE_CLIENT_ID
3724
+ })
3725
+ });
3726
+ const tokenData = await tokenRes.json();
3727
+ if (tokenData.access_token) {
3728
+ process.stdout.write("\r" + " ".repeat(60) + "\r");
3729
+ let uname = "user";
3730
+ try {
3731
+ const userRes = await fetch(`${hubUrl}/oauth/userinfo`, {
3732
+ headers: { Authorization: `Bearer ${tokenData.access_token}` }
3733
+ });
3734
+ if (userRes.ok) {
3735
+ const userInfo = await userRes.json();
3736
+ uname = userInfo.preferred_username || userInfo.name || "user";
3737
+ }
3738
+ } catch {
3739
+ }
3740
+ return { token: tokenData.access_token, username: uname };
3741
+ }
3742
+ if (tokenData.error === "slow_down") {
3743
+ interval += 5e3;
3744
+ } else if (tokenData.error === "access_denied") {
3745
+ process.stdout.write("\r" + " ".repeat(60) + "\r");
3746
+ throw new Error("Authorization denied by user");
3747
+ } else if (tokenData.error === "expired_token") {
3748
+ process.stdout.write("\r" + " ".repeat(60) + "\r");
3749
+ throw new Error("Authorization expired");
3750
+ }
3751
+ }
3752
+ throw new Error("Authorization timed out");
3753
+ }
3668
3754
  function checkBinary(cmd) {
3669
3755
  try {
3670
3756
  return (0, import_node_child_process.execSync)(`${cmd} 2>&1`, { encoding: "utf8", timeout: 5e3 }).trim().split("\n")[0];
@@ -3724,38 +3810,20 @@ async function runSetup() {
3724
3810
  if (!token) {
3725
3811
  console.log(" Let's connect you to CV-Hub.");
3726
3812
  console.log();
3727
- const autoName = `${(0, import_node_os2.hostname)()}-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
3728
- const tokenUrl = `${appUrl}/settings/tokens/new?name=${encodeURIComponent(autoName)}&scopes=agent,repo`;
3729
- console.log(source_default.gray(` Opening: ${tokenUrl}`));
3730
3813
  try {
3731
- const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
3732
- (0, import_node_child_process.execSync)(`${openCmd} "${tokenUrl}" 2>/dev/null`, { timeout: 5e3 });
3733
- } catch {
3734
- console.log(source_default.gray(" (Could not open browser \u2014 copy the URL above)"));
3735
- }
3736
- console.log();
3737
- const readline = await import("node:readline");
3738
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
3739
- token = await new Promise((resolve) => {
3740
- rl.question(" Paste your token here: ", (answer) => {
3741
- rl.close();
3742
- resolve(answer.trim());
3743
- });
3744
- });
3745
- if (!token) {
3746
- console.log(source_default.red(" No token provided. Run cva setup again."));
3747
- process.exit(1);
3748
- }
3749
- const user = await validateToken(hubUrl, token);
3750
- if (!user) {
3751
- console.log(source_default.red(" Token validation failed. Check your token and try again."));
3814
+ const deviceResult = await deviceAuthFlow(hubUrl, appUrl);
3815
+ token = deviceResult.token;
3816
+ username = deviceResult.username;
3817
+ console.log(source_default.green(" \u2713") + ` Authenticated as ${source_default.bold(username)}`);
3818
+ writeSharedCreds({ hub_url: hubUrl, token, username, created_at: (/* @__PURE__ */ new Date()).toISOString() });
3819
+ await writeCredentialField("CV_HUB_PAT", token);
3820
+ await writeCredentialField("CV_HUB_API", hubUrl);
3821
+ } catch (err) {
3822
+ console.log(source_default.red(` Authentication failed: ${err.message}`));
3823
+ console.log(source_default.gray(" You can retry with: cva setup"));
3824
+ console.log(source_default.gray(" Or manually: cva auth login --token <your-pat>"));
3752
3825
  process.exit(1);
3753
3826
  }
3754
- username = user;
3755
- console.log(source_default.green(" \u2713") + ` Authenticated as ${source_default.bold(user)}`);
3756
- writeSharedCreds({ hub_url: hubUrl, token, username, created_at: (/* @__PURE__ */ new Date()).toISOString() });
3757
- await writeCredentialField("CV_HUB_PAT", token);
3758
- await writeCredentialField("CV_HUB_API", hubUrl);
3759
3827
  }
3760
3828
  console.log();
3761
3829
  console.log(" Claude.ai MCP connector:");
@@ -3766,18 +3834,84 @@ async function runSetup() {
3766
3834
  console.log(source_default.gray(' Click "Add Integration" \u2192 "Allow" when prompted.'));
3767
3835
  console.log(source_default.gray(" (You can do this later \u2014 setup will continue.)"));
3768
3836
  console.log();
3769
- const cwd = process.cwd();
3770
- const isGitRepo = (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, ".git"));
3771
- const hasCVDir = (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, ".cv"));
3772
- const hasClaudeMd = (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, "CLAUDE.md"));
3773
- const repoName = (0, import_node_path.basename)(cwd);
3837
+ let cwd = process.cwd();
3838
+ let isGitRepo = (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, ".git"));
3839
+ let repoName = (0, import_node_path.basename)(cwd);
3774
3840
  if (isGitRepo) {
3775
3841
  console.log(source_default.green(" \u2713") + ` Git repo found: ${repoName}`);
3776
3842
  } else {
3777
- console.log(" Initializing git repository...");
3778
- (0, import_node_child_process.execSync)("git init && git checkout -b main", { cwd, stdio: "pipe" });
3779
- console.log(source_default.green(" \u2713") + " Git repo initialized");
3843
+ const readline = await import("node:readline");
3844
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
3845
+ console.log(" No git repository found in this directory.");
3846
+ console.log();
3847
+ console.log(` ${source_default.cyan("a")} \u2014 Initialize a new project here: ${cwd}`);
3848
+ console.log(` ${source_default.cyan("b")} \u2014 Clone an existing repo from CV-Hub`);
3849
+ console.log();
3850
+ const choice = await new Promise((resolve2) => {
3851
+ rl.question(" Choose [a/b]: ", (answer) => {
3852
+ rl.close();
3853
+ resolve2(answer.trim().toLowerCase() || "a");
3854
+ });
3855
+ });
3856
+ if (choice === "b" && token) {
3857
+ try {
3858
+ console.log(source_default.gray(" Fetching your repositories..."));
3859
+ const controller = new AbortController();
3860
+ const timeout = setTimeout(() => controller.abort(), 15e3);
3861
+ const res = await fetch(`${hubUrl}/api/v1/repos?limit=50`, {
3862
+ headers: { Authorization: `Bearer ${token}` },
3863
+ signal: controller.signal
3864
+ });
3865
+ clearTimeout(timeout);
3866
+ if (res.ok) {
3867
+ const data = await res.json();
3868
+ const repos = data.repositories || [];
3869
+ if (repos.length === 0) {
3870
+ console.log(source_default.yellow(" No repos found on CV-Hub. Initializing a new project instead."));
3871
+ } else {
3872
+ console.log();
3873
+ console.log(" Your CV-Hub repositories:");
3874
+ const displayRepos = repos.slice(0, 20);
3875
+ displayRepos.forEach((r, i) => {
3876
+ const desc = r.description ? source_default.gray(` \u2014 ${r.description.substring(0, 40)}`) : "";
3877
+ console.log(` ${source_default.cyan(String(i + 1).padStart(2))}. ${r.slug || r.name}${desc}`);
3878
+ });
3879
+ console.log();
3880
+ const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
3881
+ const selection = await new Promise((resolve2) => {
3882
+ rl2.question(` Select a repo [1-${displayRepos.length}]: `, (answer) => {
3883
+ rl2.close();
3884
+ resolve2(answer.trim());
3885
+ });
3886
+ });
3887
+ const idx = parseInt(selection, 10) - 1;
3888
+ if (idx >= 0 && idx < displayRepos.length) {
3889
+ const repo = displayRepos[idx];
3890
+ const gitHost = "git.hub.controlvector.io";
3891
+ const slug = repo.slug || repo.name;
3892
+ const cloneUrl = `https://${username}:${token}@${gitHost}/${username}/${slug}.git`;
3893
+ console.log(source_default.gray(` Cloning ${username}/${slug}...`));
3894
+ (0, import_node_child_process.execSync)(`git clone ${cloneUrl} ${slug}`, { cwd, stdio: "pipe", timeout: 6e4 });
3895
+ cwd = (0, import_node_path.join)(cwd, slug);
3896
+ process.chdir(cwd);
3897
+ repoName = slug;
3898
+ isGitRepo = true;
3899
+ console.log(source_default.green(" \u2713") + ` Cloned ${username}/${slug}`);
3900
+ }
3901
+ }
3902
+ }
3903
+ } catch (err) {
3904
+ console.log(source_default.yellow(` Could not fetch repos: ${err.message}. Initializing instead.`));
3905
+ }
3906
+ }
3907
+ if (!isGitRepo) {
3908
+ console.log(" Initializing git repository...");
3909
+ (0, import_node_child_process.execSync)("git init && git checkout -b main", { cwd, stdio: "pipe" });
3910
+ console.log(source_default.green(" \u2713") + " Git repo initialized");
3911
+ }
3780
3912
  }
3913
+ const hasClaudeMd = (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, "CLAUDE.md"));
3914
+ const hasCVDir = (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, ".cv"));
3781
3915
  if (!hasClaudeMd) {
3782
3916
  const template = `# ${repoName}
3783
3917
 
@@ -3795,6 +3929,41 @@ async function runSetup() {
3795
3929
  } else {
3796
3930
  console.log(source_default.green(" \u2713") + " CLAUDE.md present");
3797
3931
  }
3932
+ const gitignorePath = (0, import_node_path.join)(cwd, ".gitignore");
3933
+ const CREDENTIAL_PATTERNS = [
3934
+ "# Credentials and secrets (auto-added by cv-agent)",
3935
+ ".env",
3936
+ ".env.*",
3937
+ ".claude/",
3938
+ ".claude.json",
3939
+ ".credentials*",
3940
+ "*.pem",
3941
+ "*.key",
3942
+ ".ssh/",
3943
+ ".gnupg/",
3944
+ ".npm/",
3945
+ ".config/",
3946
+ ".zsh_history",
3947
+ ".bash_history",
3948
+ ".zsh_sessions/",
3949
+ "node_modules/",
3950
+ ".DS_Store"
3951
+ ];
3952
+ try {
3953
+ let gitignoreContent = "";
3954
+ if ((0, import_node_fs.existsSync)(gitignorePath)) {
3955
+ gitignoreContent = (0, import_node_fs.readFileSync)(gitignorePath, "utf-8");
3956
+ }
3957
+ const missing = CREDENTIAL_PATTERNS.filter((p) => !gitignoreContent.includes(p));
3958
+ if (missing.length > 0) {
3959
+ const addition = (gitignoreContent && !gitignoreContent.endsWith("\n") ? "\n" : "") + missing.join("\n") + "\n";
3960
+ (0, import_node_fs.writeFileSync)(gitignorePath, gitignoreContent + addition);
3961
+ console.log(source_default.green(" \u2713") + " .gitignore updated with credential protection");
3962
+ } else {
3963
+ console.log(source_default.green(" \u2713") + " .gitignore has credential protection");
3964
+ }
3965
+ } catch {
3966
+ }
3798
3967
  if (!hasCVDir) {
3799
3968
  (0, import_node_fs.mkdirSync)((0, import_node_path.join)(cwd, ".cv"), { recursive: true });
3800
3969
  }
@@ -3804,6 +3973,22 @@ async function runSetup() {
3804
3973
  try {
3805
3974
  const existingRemote = (0, import_node_child_process.execSync)('git remote get-url cv-hub 2>/dev/null || echo ""', { cwd, encoding: "utf8" }).trim();
3806
3975
  if (!existingRemote) {
3976
+ try {
3977
+ const controller = new AbortController();
3978
+ const timeout = setTimeout(() => controller.abort(), 15e3);
3979
+ await fetch(`${hubUrl}/api/v1/user/repos`, {
3980
+ method: "POST",
3981
+ headers: {
3982
+ Authorization: `Bearer ${token}`,
3983
+ "Content-Type": "application/json"
3984
+ },
3985
+ body: JSON.stringify({ name: repoName, auto_init: false }),
3986
+ signal: controller.signal
3987
+ });
3988
+ clearTimeout(timeout);
3989
+ console.log(source_default.green(" \u2713") + ` Repo created on CV-Hub: ${username}/${repoName}`);
3990
+ } catch {
3991
+ }
3807
3992
  (0, import_node_child_process.execSync)(`git remote add cv-hub ${remoteUrl}`, { cwd, stdio: "pipe" });
3808
3993
  console.log(source_default.green(" \u2713") + ` Remote added: cv-hub \u2192 ${remoteUrl}`);
3809
3994
  } else {
@@ -3811,6 +3996,99 @@ async function runSetup() {
3811
3996
  }
3812
3997
  } catch {
3813
3998
  }
3999
+ try {
4000
+ const credStorePath = (0, import_node_path.join)((0, import_node_os2.homedir)(), ".git-credentials");
4001
+ const credLine = `https://${username}:${token}@${gitHost}`;
4002
+ let existing = "";
4003
+ try {
4004
+ existing = (0, import_node_fs.readFileSync)(credStorePath, "utf-8");
4005
+ } catch {
4006
+ }
4007
+ if (!existing.includes(gitHost)) {
4008
+ (0, import_node_fs.writeFileSync)(credStorePath, existing + credLine + "\n", { mode: 384 });
4009
+ }
4010
+ (0, import_node_child_process.execSync)(`git config --global credential.helper store`, { stdio: "pipe" });
4011
+ } catch {
4012
+ }
4013
+ try {
4014
+ (0, import_node_child_process.execSync)("git log --oneline -1", { cwd, stdio: "pipe" });
4015
+ const status = (0, import_node_child_process.execSync)("git status --porcelain", { cwd, encoding: "utf8" }).trim();
4016
+ if (status) {
4017
+ (0, import_node_child_process.execSync)("git add -A", { cwd, stdio: "pipe" });
4018
+ (0, import_node_child_process.execSync)('git commit -m "chore: cv-agent setup"', { cwd, stdio: "pipe" });
4019
+ console.log(source_default.green(" \u2713") + " Changes committed");
4020
+ }
4021
+ } catch {
4022
+ try {
4023
+ (0, import_node_child_process.execSync)("git add -A", { cwd, stdio: "pipe" });
4024
+ (0, import_node_child_process.execSync)('git commit -m "Initial commit via cv-agent"', { cwd, stdio: "pipe" });
4025
+ console.log(source_default.green(" \u2713") + " Initial commit created");
4026
+ } catch {
4027
+ }
4028
+ }
4029
+ try {
4030
+ (0, import_node_child_process.execSync)("git push -u cv-hub main 2>&1", { cwd, stdio: "pipe", timeout: 3e4 });
4031
+ console.log(source_default.green(" \u2713") + " Pushed to CV-Hub");
4032
+ } catch {
4033
+ console.log(source_default.gray(" (Push skipped \u2014 you can push later with: git push cv-hub main)"));
4034
+ }
4035
+ }
4036
+ console.log();
4037
+ let agentStatus = "";
4038
+ const pidFile = (0, import_node_path.join)((0, import_node_os2.homedir)(), ".config", "controlvector", "agent.pid");
4039
+ let agentRunning = false;
4040
+ try {
4041
+ const pid = parseInt((0, import_node_fs.readFileSync)(pidFile, "utf-8").trim(), 10);
4042
+ if (pid > 0) {
4043
+ process.kill(pid, 0);
4044
+ agentRunning = true;
4045
+ agentStatus = `running (PID ${pid})`;
4046
+ console.log(source_default.green(" \u2713") + ` Agent already running (PID ${pid})`);
4047
+ }
4048
+ } catch {
4049
+ }
4050
+ if (!agentRunning) {
4051
+ const readline = await import("node:readline");
4052
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
4053
+ const answer = await new Promise((resolve2) => {
4054
+ rl.question(" Start the CV-Agent daemon? (Y/n): ", (a) => {
4055
+ rl.close();
4056
+ resolve2(a.trim().toLowerCase() || "y");
4057
+ });
4058
+ });
4059
+ if (answer === "y" || answer === "yes" || answer === "") {
4060
+ const machName = (0, import_node_os2.hostname)().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-");
4061
+ console.log(source_default.gray(` Starting agent as "${machName}"...`));
4062
+ try {
4063
+ const { spawn: spawnChild } = await import("node:child_process");
4064
+ const child = spawnChild("cva", [
4065
+ "agent",
4066
+ "--auto-approve",
4067
+ "--machine",
4068
+ machName,
4069
+ "--working-dir",
4070
+ cwd
4071
+ ], {
4072
+ detached: true,
4073
+ stdio: "ignore",
4074
+ env: { ...process.env }
4075
+ });
4076
+ child.unref();
4077
+ if (child.pid) {
4078
+ (0, import_node_fs.mkdirSync)((0, import_node_path.join)((0, import_node_os2.homedir)(), ".config", "controlvector"), { recursive: true });
4079
+ (0, import_node_fs.writeFileSync)(pidFile, String(child.pid), { mode: 384 });
4080
+ agentStatus = `running (PID ${child.pid})`;
4081
+ console.log(source_default.green(" \u2713") + ` Agent started (PID ${child.pid}) \u2014 executor "${machName}"`);
4082
+ }
4083
+ } catch (err) {
4084
+ console.log(source_default.yellow(` Could not start agent: ${err.message}`));
4085
+ console.log(source_default.gray(" Start manually with: cva agent --auto-approve"));
4086
+ agentStatus = "not started";
4087
+ }
4088
+ } else {
4089
+ agentStatus = "not started";
4090
+ console.log(source_default.gray(" Start anytime with: cva agent --auto-approve"));
4091
+ }
3814
4092
  }
3815
4093
  console.log();
3816
4094
  console.log(source_default.bold(" Setup Complete"));
@@ -3818,11 +4096,14 @@ async function runSetup() {
3818
4096
  console.log(` ${source_default.green("\u2713")} Authenticated as: ${source_default.cyan(username)}`);
3819
4097
  console.log(` ${source_default.green("\u2713")} Repository: ${source_default.cyan(repoName)}`);
3820
4098
  console.log(` ${source_default.green("\u2713")} CLAUDE.md: present`);
4099
+ if (agentStatus.startsWith("running")) {
4100
+ console.log(` ${source_default.green("\u2713")} Agent daemon: ${source_default.cyan(agentStatus)}`);
4101
+ }
3821
4102
  console.log(source_default.gray(" " + "\u2500".repeat(40)));
3822
4103
  console.log();
3823
4104
  console.log(" What's next:");
3824
- console.log(` ${source_default.cyan("cva agent --auto-approve")} \u2014 Start listening for tasks`);
3825
- console.log(` Or open Claude.ai and dispatch a task to this repo.`);
4105
+ console.log(" Open Claude.ai and try:");
4106
+ console.log(source_default.cyan(` "Create a task in ${repoName} to add a hello world index.html"`));
3826
4107
  console.log();
3827
4108
  console.log(source_default.gray(` Dashboard: ${appUrl}`));
3828
4109
  console.log();
@@ -3978,7 +4259,7 @@ async function apiCall(creds, method, path3, body) {
3978
4259
  body: body ? JSON.stringify(body) : void 0
3979
4260
  });
3980
4261
  }
3981
- async function registerExecutor(creds, machineName, workingDir, repositoryId, metadata) {
4262
+ async function registerExecutor(creds, machineName, workingDir, repositoryId, metadata, repoOwnerSlug) {
3982
4263
  const body = {
3983
4264
  name: `cva:${machineName}`,
3984
4265
  machine_name: machineName,
@@ -3997,7 +4278,23 @@ async function registerExecutor(creds, machineName, workingDir, repositoryId, me
3997
4278
  if (metadata?.tags) body.tags = metadata.tags;
3998
4279
  if (metadata?.owner_project) body.owner_project = metadata.owner_project;
3999
4280
  if (metadata?.integration) body.integration = metadata.integration;
4000
- const res = await apiCall(creds, "POST", "/api/v1/executors", body);
4281
+ let res = await apiCall(creds, "POST", "/api/v1/executors", body);
4282
+ if (res.status === 400 && repoOwnerSlug) {
4283
+ try {
4284
+ const errData = await res.json();
4285
+ const orgs = errData.error?.organizations;
4286
+ if (orgs && orgs.length > 0) {
4287
+ const match = orgs.find(
4288
+ (o) => o.slug.toLowerCase() === repoOwnerSlug.toLowerCase()
4289
+ );
4290
+ if (match) {
4291
+ body.organization_id = match.id;
4292
+ res = await apiCall(creds, "POST", "/api/v1/executors", body);
4293
+ }
4294
+ }
4295
+ } catch {
4296
+ }
4297
+ }
4001
4298
  if (!res.ok) {
4002
4299
  const err = await res.text();
4003
4300
  throw new Error(`Failed to register executor: ${res.status} ${err}`);
@@ -4440,11 +4737,78 @@ async function writeConfig(config) {
4440
4737
 
4441
4738
  // src/commands/git-safety.ts
4442
4739
  var import_node_child_process3 = require("node:child_process");
4740
+ var import_node_fs3 = require("node:fs");
4741
+ var import_node_path2 = require("node:path");
4742
+ var import_node_os3 = require("node:os");
4743
+ var DANGEROUS_PATTERNS = [
4744
+ ".claude/",
4745
+ ".claude.json",
4746
+ ".credentials",
4747
+ ".zsh_history",
4748
+ ".bash_history",
4749
+ ".zsh_sessions/",
4750
+ ".ssh/",
4751
+ ".gnupg/",
4752
+ ".npm/",
4753
+ ".config/",
4754
+ ".CFUserTextEncoding",
4755
+ "Library/",
4756
+ "Applications/",
4757
+ ".Trash/",
4758
+ ".DS_Store",
4759
+ "node_modules/",
4760
+ ".env",
4761
+ ".env.local",
4762
+ ".env.production"
4763
+ ];
4443
4764
  function git(cmd, cwd) {
4444
4765
  return (0, import_node_child_process3.execSync)(cmd, { cwd, encoding: "utf8", timeout: 3e4 }).trim();
4445
4766
  }
4767
+ function checkWorkspaceSafety(workspaceRoot) {
4768
+ const resolved = (0, import_node_path2.resolve)(workspaceRoot);
4769
+ const home = (0, import_node_path2.resolve)((0, import_node_os3.homedir)());
4770
+ if (resolved === home) {
4771
+ return `Workspace is user HOME directory (${home}). Refusing to auto-commit to prevent indexing personal files.`;
4772
+ }
4773
+ if (home.startsWith(resolved + "/")) {
4774
+ return `Workspace (${resolved}) is a parent of HOME. Refusing to auto-commit.`;
4775
+ }
4776
+ if (!(0, import_node_fs3.existsSync)((0, import_node_path2.join)(resolved, ".git"))) {
4777
+ return `No .git directory in ${resolved}. Not a git repository.`;
4778
+ }
4779
+ return null;
4780
+ }
4781
+ function filterDangerousFiles(statusLines) {
4782
+ const safe = [];
4783
+ const blocked = [];
4784
+ for (const line of statusLines) {
4785
+ const filePath = line.substring(3).trim();
4786
+ const isDangerous = DANGEROUS_PATTERNS.some(
4787
+ (pattern) => filePath.startsWith(pattern) || filePath.includes("/" + pattern)
4788
+ );
4789
+ if (isDangerous) {
4790
+ blocked.push(filePath);
4791
+ } else {
4792
+ safe.push(line);
4793
+ }
4794
+ }
4795
+ return { safe, blocked };
4796
+ }
4446
4797
  function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
4447
4798
  const targetBranch = branch || "main";
4799
+ const safetyError = checkWorkspaceSafety(workspaceRoot);
4800
+ if (safetyError) {
4801
+ console.log(` [git-safety] BLOCKED: ${safetyError}`);
4802
+ return {
4803
+ hadChanges: false,
4804
+ filesAdded: 0,
4805
+ filesModified: 0,
4806
+ filesDeleted: 0,
4807
+ pushed: false,
4808
+ skipped: true,
4809
+ skipReason: safetyError
4810
+ };
4811
+ }
4448
4812
  try {
4449
4813
  let statusOutput;
4450
4814
  try {
@@ -4452,8 +4816,8 @@ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
4452
4816
  } catch {
4453
4817
  return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false, error: "git status failed" };
4454
4818
  }
4455
- const lines = statusOutput.split("\n").filter(Boolean);
4456
- if (lines.length === 0) {
4819
+ const allLines = statusOutput.split("\n").filter(Boolean);
4820
+ if (allLines.length === 0) {
4457
4821
  try {
4458
4822
  const unpushed = git(`git log origin/${targetBranch}..HEAD --oneline 2>/dev/null`, workspaceRoot);
4459
4823
  if (unpushed) {
@@ -4465,8 +4829,16 @@ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
4465
4829
  }
4466
4830
  return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false };
4467
4831
  }
4832
+ const { safe: safeLines, blocked } = filterDangerousFiles(allLines);
4833
+ if (blocked.length > 0) {
4834
+ console.log(` [git-safety] Blocked ${blocked.length} sensitive file(s) from staging: ${blocked.slice(0, 5).join(", ")}${blocked.length > 5 ? "..." : ""}`);
4835
+ }
4836
+ if (safeLines.length === 0) {
4837
+ console.log(` [git-safety] All ${allLines.length} changed files were blocked by safety filter`);
4838
+ return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false };
4839
+ }
4468
4840
  let added = 0, modified = 0, deleted = 0;
4469
- for (const line of lines) {
4841
+ for (const line of safeLines) {
4470
4842
  const code = line.substring(0, 2);
4471
4843
  if (code.includes("?")) added++;
4472
4844
  else if (code.includes("D")) deleted++;
@@ -4474,9 +4846,15 @@ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
4474
4846
  else added++;
4475
4847
  }
4476
4848
  console.log(
4477
- ` [git-safety] ${lines.length} uncommitted changes (${added} new, ${modified} modified, ${deleted} deleted) \u2014 committing now`
4849
+ ` [git-safety] ${safeLines.length} safe changes (${added} new, ${modified} modified, ${deleted} deleted) \u2014 committing now`
4478
4850
  );
4479
- git("git add -A", workspaceRoot);
4851
+ for (const line of safeLines) {
4852
+ const filePath = line.substring(3).trim();
4853
+ try {
4854
+ git(`git add -- ${JSON.stringify(filePath)}`, workspaceRoot);
4855
+ } catch {
4856
+ }
4857
+ }
4480
4858
  const shortId = taskId.substring(0, 8);
4481
4859
  const commitMsg = `task: ${taskTitle} [${shortId}]
4482
4860
 
@@ -4537,8 +4915,8 @@ Files: ${added} added, ${modified} modified, ${deleted} deleted`;
4537
4915
 
4538
4916
  // src/commands/deploy-manifest.ts
4539
4917
  var import_node_child_process4 = require("node:child_process");
4540
- var import_node_fs3 = require("node:fs");
4541
- var import_node_path2 = require("node:path");
4918
+ var import_node_fs4 = require("node:fs");
4919
+ var import_node_path3 = require("node:path");
4542
4920
  function exec(cmd, cwd, timeoutMs = 3e5, env2) {
4543
4921
  try {
4544
4922
  const stdout = (0, import_node_child_process4.execSync)(cmd, {
@@ -4581,10 +4959,10 @@ function getHeadCommit(cwd) {
4581
4959
  }
4582
4960
  }
4583
4961
  function loadDeployManifest(workspaceRoot) {
4584
- const manifestPath = (0, import_node_path2.join)(workspaceRoot, ".cva", "deploy.json");
4585
- if (!(0, import_node_fs3.existsSync)(manifestPath)) return null;
4962
+ const manifestPath = (0, import_node_path3.join)(workspaceRoot, ".cva", "deploy.json");
4963
+ if (!(0, import_node_fs4.existsSync)(manifestPath)) return null;
4586
4964
  try {
4587
- const raw = (0, import_node_fs3.readFileSync)(manifestPath, "utf8");
4965
+ const raw = (0, import_node_fs4.readFileSync)(manifestPath, "utf8");
4588
4966
  const manifest = JSON.parse(raw);
4589
4967
  if (!manifest.version || manifest.version < 1) {
4590
4968
  console.log(" [deploy] Invalid manifest version");
@@ -4609,7 +4987,7 @@ async function postTaskDeploy(workspaceRoot, taskId, log) {
4609
4987
  console.log(` [deploy] Building: ${buildStep.name}`);
4610
4988
  log("lifecycle", `Building: ${buildStep.name}`);
4611
4989
  const start = Date.now();
4612
- const cwd = (0, import_node_path2.join)(workspaceRoot, buildStep.working_dir || ".");
4990
+ const cwd = (0, import_node_path3.join)(workspaceRoot, buildStep.working_dir || ".");
4613
4991
  const timeoutMs = (buildStep.timeout_seconds || 300) * 1e3;
4614
4992
  const result = exec(buildStep.command, cwd, timeoutMs);
4615
4993
  if (!result.ok) {
@@ -4700,7 +5078,7 @@ async function rollback(workspaceRoot, targetCommit, manifest, log) {
4700
5078
  }
4701
5079
  if (manifest.build?.steps) {
4702
5080
  for (const step of manifest.build.steps) {
4703
- exec(step.command, (0, import_node_path2.join)(workspaceRoot, step.working_dir || "."), (step.timeout_seconds || 300) * 1e3);
5081
+ exec(step.command, (0, import_node_path3.join)(workspaceRoot, step.working_dir || "."), (step.timeout_seconds || 300) * 1e3);
4704
5082
  }
4705
5083
  }
4706
5084
  if (manifest.service?.restart_command) {
@@ -4858,6 +5236,22 @@ Target repository: ${task.owner}/${task.repo}
4858
5236
  `;
4859
5237
  prompt += `
4860
5238
 
5239
+ ## Security \u2014 NEVER Commit Secrets
5240
+ `;
5241
+ prompt += `Do NOT add, commit, or push any of the following:
5242
+ `;
5243
+ prompt += `- API keys, tokens, passwords, or credentials
5244
+ `;
5245
+ prompt += `- .env files, .credentials files, .claude/ directory
5246
+ `;
5247
+ prompt += `- SSH keys (.ssh/), GPG keys (.gnupg/)
5248
+ `;
5249
+ prompt += `- Shell history (.zsh_history, .bash_history)
5250
+ `;
5251
+ prompt += `If you need to reference an API key, use an environment variable placeholder.
5252
+ `;
5253
+ prompt += `
5254
+
4861
5255
  ---
4862
5256
  `;
4863
5257
  prompt += `When complete, provide a brief summary of what you accomplished.
@@ -4937,7 +5331,7 @@ async function launchAutoApproveMode(prompt, options) {
4937
5331
  let lastProgressBytes = 0;
4938
5332
  let totalBytesStreamed = 0;
4939
5333
  const runOnce = (inputPrompt, isContinue) => {
4940
- return new Promise((resolve, reject) => {
5334
+ return new Promise((resolve2, reject) => {
4941
5335
  const args = isContinue ? ["-p", inputPrompt, "--continue", "--allowedTools", ...ALLOWED_TOOLS] : ["-p", inputPrompt, "--allowedTools", ...ALLOWED_TOOLS];
4942
5336
  if (sessionId && !isContinue) {
4943
5337
  args.push("--session-id", sessionId);
@@ -5047,7 +5441,7 @@ ${source_default.red("!")} Claude Code auth failure (stderr): "${authError}"`);
5047
5441
  });
5048
5442
  child.on("close", (code, signal) => {
5049
5443
  _activeChild = null;
5050
- resolve({
5444
+ resolve2({
5051
5445
  exitCode: signal === "SIGKILL" ? 137 : code ?? 1,
5052
5446
  stderr,
5053
5447
  output: fullOutput,
@@ -5086,7 +5480,7 @@ ${source_default.red("!")} Claude Code auth failure (stderr): "${authError}"`);
5086
5480
  return result;
5087
5481
  }
5088
5482
  async function launchRelayMode(prompt, options) {
5089
- return new Promise((resolve, reject) => {
5483
+ return new Promise((resolve2, reject) => {
5090
5484
  const child = (0, import_node_child_process5.spawn)("claude", [], {
5091
5485
  cwd: options.cwd,
5092
5486
  stdio: ["pipe", "pipe", "pipe"],
@@ -5291,9 +5685,9 @@ ${source_default.red("!")} Claude Code auth failure (stderr): "${authError}"`);
5291
5685
  child.on("close", (code, signal) => {
5292
5686
  _activeChild = null;
5293
5687
  if (signal === "SIGKILL") {
5294
- resolve({ exitCode: 137, stderr, output: fullOutput, authFailure });
5688
+ resolve2({ exitCode: 137, stderr, output: fullOutput, authFailure });
5295
5689
  } else {
5296
- resolve({ exitCode: code ?? 1, stderr, output: fullOutput, authFailure });
5690
+ resolve2({ exitCode: code ?? 1, stderr, output: fullOutput, authFailure });
5297
5691
  }
5298
5692
  });
5299
5693
  child.on("error", (err) => {
@@ -5394,14 +5788,45 @@ async function runAgent(options) {
5394
5788
  console.log();
5395
5789
  process.exit(1);
5396
5790
  }
5791
+ if (process.env.CV_DEBUG) {
5792
+ console.log(source_default.gray(` PATH: ${process.env.PATH}`));
5793
+ console.log(source_default.gray(` HOME: ${process.env.HOME}`));
5794
+ console.log(source_default.gray(` SHELL: ${process.env.SHELL}`));
5795
+ }
5796
+ let claudeBinary = "claude";
5397
5797
  try {
5398
5798
  (0, import_node_child_process5.execSync)("claude --version", { stdio: "pipe", timeout: 5e3 });
5399
5799
  } catch {
5400
- console.log();
5401
- console.log(source_default.red("Claude Code CLI not found.") + " Install it first:");
5402
- console.log(` ${source_default.cyan("npm install -g @anthropic-ai/claude-code")}`);
5403
- console.log();
5404
- process.exit(1);
5800
+ const candidates = [
5801
+ "/usr/local/bin/claude",
5802
+ "/opt/homebrew/bin/claude",
5803
+ `${process.env.HOME}/.npm-global/bin/claude`,
5804
+ `${process.env.HOME}/.nvm/versions/node/*/bin/claude`
5805
+ ];
5806
+ let found = false;
5807
+ for (const candidate of candidates) {
5808
+ try {
5809
+ const resolved = (0, import_node_child_process5.execSync)(`ls ${candidate} 2>/dev/null | head -1`, { encoding: "utf8", timeout: 3e3 }).trim();
5810
+ if (resolved) {
5811
+ (0, import_node_child_process5.execSync)(`${resolved} --version`, { stdio: "pipe", timeout: 5e3 });
5812
+ claudeBinary = resolved;
5813
+ found = true;
5814
+ console.log(source_default.yellow("!") + ` Claude Code found at ${resolved} (not on PATH)`);
5815
+ break;
5816
+ }
5817
+ } catch {
5818
+ }
5819
+ }
5820
+ if (!found) {
5821
+ console.log();
5822
+ console.log(source_default.red("Claude Code CLI not found.") + " Install it first:");
5823
+ console.log(` ${source_default.cyan("npm install -g @anthropic-ai/claude-code")}`);
5824
+ console.log();
5825
+ console.log(source_default.gray(` Current PATH: ${process.env.PATH}`));
5826
+ console.log(source_default.gray(" If running via Launch Agent, ensure PATH includes the directory where claude is installed."));
5827
+ console.log();
5828
+ process.exit(1);
5829
+ }
5405
5830
  }
5406
5831
  const { env: claudeEnv, usingApiKey } = await getClaudeEnv();
5407
5832
  const authCheck = await checkClaudeAuth();
@@ -5442,7 +5867,82 @@ async function runAgent(options) {
5442
5867
  console.log();
5443
5868
  }
5444
5869
  }
5870
+ {
5871
+ const isGitRepo = require("fs").existsSync(require("path").join(workingDir, ".git"));
5872
+ if (!isGitRepo) {
5873
+ console.log(source_default.gray(" Bootstrap: initializing git repo..."));
5874
+ try {
5875
+ (0, import_node_child_process5.execSync)("git init && git checkout -b main", { cwd: workingDir, stdio: "pipe" });
5876
+ } catch {
5877
+ }
5878
+ }
5879
+ const claudeMdPath = require("path").join(workingDir, "CLAUDE.md");
5880
+ if (!require("fs").existsSync(claudeMdPath)) {
5881
+ const name = require("path").basename(workingDir);
5882
+ const template = `# ${name}
5883
+
5884
+ ## Overview
5885
+ [Describe your project here]
5886
+
5887
+ ## Build & Run
5888
+ [How to build and run this project]
5889
+ `;
5890
+ try {
5891
+ require("fs").writeFileSync(claudeMdPath, template);
5892
+ console.log(source_default.gray(" Bootstrap: created CLAUDE.md"));
5893
+ } catch {
5894
+ }
5895
+ }
5896
+ const gitignorePath = require("path").join(workingDir, ".gitignore");
5897
+ try {
5898
+ const fs4 = require("fs");
5899
+ const credPatterns = ".env\n.env.*\n.claude/\n.claude.json\n.credentials*\n*.pem\n*.key\n.ssh/\n.gnupg/\n.npm/\n.config/\n.zsh_history\n.bash_history\nnode_modules/\n.DS_Store\n";
5900
+ let existing = "";
5901
+ try {
5902
+ existing = fs4.readFileSync(gitignorePath, "utf-8");
5903
+ } catch {
5904
+ }
5905
+ if (!existing.includes(".claude/")) {
5906
+ const prefix = existing && !existing.endsWith("\n") ? "\n" : "";
5907
+ fs4.writeFileSync(gitignorePath, existing + prefix + "# Credentials (auto-added by cv-agent)\n" + credPatterns);
5908
+ console.log(source_default.gray(" Bootstrap: .gitignore credential protection added"));
5909
+ }
5910
+ } catch {
5911
+ }
5912
+ const cvDir = require("path").join(workingDir, ".cv");
5913
+ if (!require("fs").existsSync(cvDir)) {
5914
+ try {
5915
+ require("fs").mkdirSync(cvDir, { recursive: true });
5916
+ } catch {
5917
+ }
5918
+ }
5919
+ if (creds.CV_HUB_PAT) {
5920
+ try {
5921
+ const existing = (0, import_node_child_process5.execSync)('git remote get-url cv-hub 2>/dev/null || echo ""', {
5922
+ cwd: workingDir,
5923
+ encoding: "utf8",
5924
+ timeout: 5e3
5925
+ }).trim();
5926
+ if (!existing) {
5927
+ const name = require("path").basename(workingDir);
5928
+ const gitHost = "git.hub.controlvector.io";
5929
+ let user = "user";
5930
+ try {
5931
+ const sharedPath = require("path").join(require("os").homedir(), ".config", "controlvector", "credentials.json");
5932
+ const shared = JSON.parse(require("fs").readFileSync(sharedPath, "utf-8"));
5933
+ if (shared.username) user = shared.username;
5934
+ } catch {
5935
+ }
5936
+ const remoteUrl = `https://${gitHost}/${user}/${name}.git`;
5937
+ (0, import_node_child_process5.execSync)(`git remote add cv-hub ${remoteUrl}`, { cwd: workingDir, stdio: "pipe" });
5938
+ console.log(source_default.gray(` Bootstrap: remote cv-hub \u2192 ${remoteUrl}`));
5939
+ }
5940
+ } catch {
5941
+ }
5942
+ }
5943
+ }
5445
5944
  let detectedRepoId;
5945
+ let detectedOwnerSlug;
5446
5946
  try {
5447
5947
  const remoteUrl = (0, import_node_child_process5.execSync)("git remote get-url origin 2>/dev/null", {
5448
5948
  cwd: workingDir,
@@ -5454,6 +5954,7 @@ async function runAgent(options) {
5454
5954
  );
5455
5955
  if (cvHubMatch) {
5456
5956
  const [, repoOwner, repoSlug] = cvHubMatch;
5957
+ detectedOwnerSlug = repoOwner;
5457
5958
  try {
5458
5959
  const repoData = await resolveRepoId(creds, repoOwner, repoSlug);
5459
5960
  if (repoData?.id) {
@@ -5465,6 +5966,9 @@ async function runAgent(options) {
5465
5966
  }
5466
5967
  } catch {
5467
5968
  }
5969
+ if (options.org) {
5970
+ detectedOwnerSlug = options.org;
5971
+ }
5468
5972
  const wsConfig = await readWorkspaceConfig(workingDir);
5469
5973
  const globalConfig = await readConfig();
5470
5974
  const role = options.role || wsConfig.role || globalConfig.role || "development";
@@ -5505,7 +6009,7 @@ async function runAgent(options) {
5505
6009
  console.log(source_default.yellow(` Guard: ${guardIcon} ${dispatchGuard}`));
5506
6010
  }
5507
6011
  const executor = await withRetry(
5508
- () => registerExecutor(creds, machineName, workingDir, detectedRepoId, executorMeta),
6012
+ () => registerExecutor(creds, machineName, workingDir, detectedRepoId, executorMeta, detectedOwnerSlug),
5509
6013
  "Executor registration"
5510
6014
  );
5511
6015
  const mode = options.autoApprove ? "auto-approve" : "relay";
@@ -5873,6 +6377,7 @@ function agentCommand() {
5873
6377
  cmd.option("--dispatch-guard <guard>", "Dispatch guard: open, confirm, locked (default: open)");
5874
6378
  cmd.option("--tags <tags>", "Comma-separated tags for this executor");
5875
6379
  cmd.option("--owner-project <project>", "Project this executor belongs to");
6380
+ cmd.option("--org <slug>", "Organization slug (auto-detected from repo owner if omitted)");
5876
6381
  cmd.action(async (opts) => {
5877
6382
  await runAgent(opts);
5878
6383
  });
@@ -5909,7 +6414,7 @@ async function promptForToken() {
5909
6414
  input: process.stdin,
5910
6415
  output: process.stdout
5911
6416
  });
5912
- return new Promise((resolve) => {
6417
+ return new Promise((resolve2) => {
5913
6418
  rl.question("Enter your CV-Hub PAT token: ", (answer) => {
5914
6419
  rl.close();
5915
6420
  const token = answer.trim();
@@ -5917,7 +6422,7 @@ async function promptForToken() {
5917
6422
  console.log(source_default.red("No token provided."));
5918
6423
  process.exit(1);
5919
6424
  }
5920
- resolve(token);
6425
+ resolve2(token);
5921
6426
  });
5922
6427
  });
5923
6428
  }
@@ -6196,7 +6701,7 @@ function statusCommand() {
6196
6701
 
6197
6702
  // src/index.ts
6198
6703
  var program2 = new Command();
6199
- program2.name("cva").description('CV-Hub Agent \u2014 bridges Claude Code with CV-Hub task dispatch.\n\nRun "cva setup" to get started.').version(true ? "1.10.0" : "1.6.0");
6704
+ program2.name("cva").description('CV-Hub Agent \u2014 bridges Claude Code with CV-Hub task dispatch.\n\nRun "cva setup" to get started.').version(true ? "1.10.1" : "1.6.0");
6200
6705
  program2.addCommand(setupCommand());
6201
6706
  program2.addCommand(agentCommand());
6202
6707
  program2.addCommand(authCommand());