@controlvector/cv-agent 1.8.0 → 1.9.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 +331 -35
- package/dist/bundle.cjs.map +3 -3
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +94 -6
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/git-safety.d.ts +12 -6
- package/dist/commands/git-safety.d.ts.map +1 -1
- package/dist/commands/git-safety.js +114 -22
- package/dist/commands/git-safety.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +143 -11
- package/dist/commands/setup.js.map +1 -1
- package/package.json +1 -1
package/dist/bundle.cjs
CHANGED
|
@@ -3834,18 +3834,84 @@ async function runSetup() {
|
|
|
3834
3834
|
console.log(source_default.gray(' Click "Add Integration" \u2192 "Allow" when prompted.'));
|
|
3835
3835
|
console.log(source_default.gray(" (You can do this later \u2014 setup will continue.)"));
|
|
3836
3836
|
console.log();
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
const hasClaudeMd = (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, "CLAUDE.md"));
|
|
3841
|
-
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);
|
|
3842
3840
|
if (isGitRepo) {
|
|
3843
3841
|
console.log(source_default.green(" \u2713") + ` Git repo found: ${repoName}`);
|
|
3844
3842
|
} else {
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
console.log(
|
|
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
|
+
}
|
|
3848
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"));
|
|
3849
3915
|
if (!hasClaudeMd) {
|
|
3850
3916
|
const template = `# ${repoName}
|
|
3851
3917
|
|
|
@@ -3933,16 +3999,76 @@ async function runSetup() {
|
|
|
3933
3999
|
}
|
|
3934
4000
|
}
|
|
3935
4001
|
console.log();
|
|
4002
|
+
let agentStatus = "";
|
|
4003
|
+
const pidFile = (0, import_node_path.join)((0, import_node_os2.homedir)(), ".config", "controlvector", "agent.pid");
|
|
4004
|
+
let agentRunning = false;
|
|
4005
|
+
try {
|
|
4006
|
+
const pid = parseInt((0, import_node_fs.readFileSync)(pidFile, "utf-8").trim(), 10);
|
|
4007
|
+
if (pid > 0) {
|
|
4008
|
+
process.kill(pid, 0);
|
|
4009
|
+
agentRunning = true;
|
|
4010
|
+
agentStatus = `running (PID ${pid})`;
|
|
4011
|
+
console.log(source_default.green(" \u2713") + ` Agent already running (PID ${pid})`);
|
|
4012
|
+
}
|
|
4013
|
+
} catch {
|
|
4014
|
+
}
|
|
4015
|
+
if (!agentRunning) {
|
|
4016
|
+
const readline = await import("node:readline");
|
|
4017
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
4018
|
+
const answer = await new Promise((resolve2) => {
|
|
4019
|
+
rl.question(" Start the CV-Agent daemon? (Y/n): ", (a) => {
|
|
4020
|
+
rl.close();
|
|
4021
|
+
resolve2(a.trim().toLowerCase() || "y");
|
|
4022
|
+
});
|
|
4023
|
+
});
|
|
4024
|
+
if (answer === "y" || answer === "yes" || answer === "") {
|
|
4025
|
+
const machName = (0, import_node_os2.hostname)().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-");
|
|
4026
|
+
console.log(source_default.gray(` Starting agent as "${machName}"...`));
|
|
4027
|
+
try {
|
|
4028
|
+
const { spawn: spawnChild } = await import("node:child_process");
|
|
4029
|
+
const child = spawnChild("cva", [
|
|
4030
|
+
"agent",
|
|
4031
|
+
"--auto-approve",
|
|
4032
|
+
"--machine",
|
|
4033
|
+
machName,
|
|
4034
|
+
"--working-dir",
|
|
4035
|
+
cwd
|
|
4036
|
+
], {
|
|
4037
|
+
detached: true,
|
|
4038
|
+
stdio: "ignore",
|
|
4039
|
+
env: { ...process.env }
|
|
4040
|
+
});
|
|
4041
|
+
child.unref();
|
|
4042
|
+
if (child.pid) {
|
|
4043
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.join)((0, import_node_os2.homedir)(), ".config", "controlvector"), { recursive: true });
|
|
4044
|
+
(0, import_node_fs.writeFileSync)(pidFile, String(child.pid), { mode: 384 });
|
|
4045
|
+
agentStatus = `running (PID ${child.pid})`;
|
|
4046
|
+
console.log(source_default.green(" \u2713") + ` Agent started (PID ${child.pid}) \u2014 executor "${machName}"`);
|
|
4047
|
+
}
|
|
4048
|
+
} catch (err) {
|
|
4049
|
+
console.log(source_default.yellow(` Could not start agent: ${err.message}`));
|
|
4050
|
+
console.log(source_default.gray(" Start manually with: cva agent --auto-approve"));
|
|
4051
|
+
agentStatus = "not started";
|
|
4052
|
+
}
|
|
4053
|
+
} else {
|
|
4054
|
+
agentStatus = "not started";
|
|
4055
|
+
console.log(source_default.gray(" Start anytime with: cva agent --auto-approve"));
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
console.log();
|
|
3936
4059
|
console.log(source_default.bold(" Setup Complete"));
|
|
3937
4060
|
console.log(source_default.gray(" " + "\u2500".repeat(40)));
|
|
3938
4061
|
console.log(` ${source_default.green("\u2713")} Authenticated as: ${source_default.cyan(username)}`);
|
|
3939
4062
|
console.log(` ${source_default.green("\u2713")} Repository: ${source_default.cyan(repoName)}`);
|
|
3940
4063
|
console.log(` ${source_default.green("\u2713")} CLAUDE.md: present`);
|
|
4064
|
+
if (agentStatus.startsWith("running")) {
|
|
4065
|
+
console.log(` ${source_default.green("\u2713")} Agent daemon: ${source_default.cyan(agentStatus)}`);
|
|
4066
|
+
}
|
|
3941
4067
|
console.log(source_default.gray(" " + "\u2500".repeat(40)));
|
|
3942
4068
|
console.log();
|
|
3943
4069
|
console.log(" What's next:");
|
|
3944
|
-
console.log(
|
|
3945
|
-
console.log(`
|
|
4070
|
+
console.log(" Open Claude.ai and try:");
|
|
4071
|
+
console.log(source_default.cyan(` "Create a task in ${repoName} to add a hello world index.html"`));
|
|
3946
4072
|
console.log();
|
|
3947
4073
|
console.log(source_default.gray(` Dashboard: ${appUrl}`));
|
|
3948
4074
|
console.log();
|
|
@@ -4432,11 +4558,78 @@ async function writeConfig(config) {
|
|
|
4432
4558
|
|
|
4433
4559
|
// src/commands/git-safety.ts
|
|
4434
4560
|
var import_node_child_process3 = require("node:child_process");
|
|
4561
|
+
var import_node_fs2 = require("node:fs");
|
|
4562
|
+
var import_node_path2 = require("node:path");
|
|
4563
|
+
var import_node_os3 = require("node:os");
|
|
4564
|
+
var DANGEROUS_PATTERNS = [
|
|
4565
|
+
".claude/",
|
|
4566
|
+
".claude.json",
|
|
4567
|
+
".credentials",
|
|
4568
|
+
".zsh_history",
|
|
4569
|
+
".bash_history",
|
|
4570
|
+
".zsh_sessions/",
|
|
4571
|
+
".ssh/",
|
|
4572
|
+
".gnupg/",
|
|
4573
|
+
".npm/",
|
|
4574
|
+
".config/",
|
|
4575
|
+
".CFUserTextEncoding",
|
|
4576
|
+
"Library/",
|
|
4577
|
+
"Applications/",
|
|
4578
|
+
".Trash/",
|
|
4579
|
+
".DS_Store",
|
|
4580
|
+
"node_modules/",
|
|
4581
|
+
".env",
|
|
4582
|
+
".env.local",
|
|
4583
|
+
".env.production"
|
|
4584
|
+
];
|
|
4435
4585
|
function git(cmd, cwd) {
|
|
4436
4586
|
return (0, import_node_child_process3.execSync)(cmd, { cwd, encoding: "utf8", timeout: 3e4 }).trim();
|
|
4437
4587
|
}
|
|
4588
|
+
function checkWorkspaceSafety(workspaceRoot) {
|
|
4589
|
+
const resolved = (0, import_node_path2.resolve)(workspaceRoot);
|
|
4590
|
+
const home = (0, import_node_path2.resolve)((0, import_node_os3.homedir)());
|
|
4591
|
+
if (resolved === home) {
|
|
4592
|
+
return `Workspace is user HOME directory (${home}). Refusing to auto-commit to prevent indexing personal files.`;
|
|
4593
|
+
}
|
|
4594
|
+
if (home.startsWith(resolved + "/")) {
|
|
4595
|
+
return `Workspace (${resolved}) is a parent of HOME. Refusing to auto-commit.`;
|
|
4596
|
+
}
|
|
4597
|
+
if (!(0, import_node_fs2.existsSync)((0, import_node_path2.join)(resolved, ".git"))) {
|
|
4598
|
+
return `No .git directory in ${resolved}. Not a git repository.`;
|
|
4599
|
+
}
|
|
4600
|
+
return null;
|
|
4601
|
+
}
|
|
4602
|
+
function filterDangerousFiles(statusLines) {
|
|
4603
|
+
const safe = [];
|
|
4604
|
+
const blocked = [];
|
|
4605
|
+
for (const line of statusLines) {
|
|
4606
|
+
const filePath = line.substring(3).trim();
|
|
4607
|
+
const isDangerous = DANGEROUS_PATTERNS.some(
|
|
4608
|
+
(pattern) => filePath.startsWith(pattern) || filePath.includes("/" + pattern)
|
|
4609
|
+
);
|
|
4610
|
+
if (isDangerous) {
|
|
4611
|
+
blocked.push(filePath);
|
|
4612
|
+
} else {
|
|
4613
|
+
safe.push(line);
|
|
4614
|
+
}
|
|
4615
|
+
}
|
|
4616
|
+
return { safe, blocked };
|
|
4617
|
+
}
|
|
4438
4618
|
function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
4439
4619
|
const targetBranch = branch || "main";
|
|
4620
|
+
const safetyError = checkWorkspaceSafety(workspaceRoot);
|
|
4621
|
+
if (safetyError) {
|
|
4622
|
+
console.log(` [git-safety] BLOCKED: ${safetyError}`);
|
|
4623
|
+
return {
|
|
4624
|
+
hadChanges: false,
|
|
4625
|
+
filesAdded: 0,
|
|
4626
|
+
filesModified: 0,
|
|
4627
|
+
filesDeleted: 0,
|
|
4628
|
+
pushed: false,
|
|
4629
|
+
skipped: true,
|
|
4630
|
+
skipReason: safetyError
|
|
4631
|
+
};
|
|
4632
|
+
}
|
|
4440
4633
|
try {
|
|
4441
4634
|
let statusOutput;
|
|
4442
4635
|
try {
|
|
@@ -4444,8 +4637,8 @@ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
|
4444
4637
|
} catch {
|
|
4445
4638
|
return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false, error: "git status failed" };
|
|
4446
4639
|
}
|
|
4447
|
-
const
|
|
4448
|
-
if (
|
|
4640
|
+
const allLines = statusOutput.split("\n").filter(Boolean);
|
|
4641
|
+
if (allLines.length === 0) {
|
|
4449
4642
|
try {
|
|
4450
4643
|
const unpushed = git(`git log origin/${targetBranch}..HEAD --oneline 2>/dev/null`, workspaceRoot);
|
|
4451
4644
|
if (unpushed) {
|
|
@@ -4457,8 +4650,16 @@ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
|
4457
4650
|
}
|
|
4458
4651
|
return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false };
|
|
4459
4652
|
}
|
|
4653
|
+
const { safe: safeLines, blocked } = filterDangerousFiles(allLines);
|
|
4654
|
+
if (blocked.length > 0) {
|
|
4655
|
+
console.log(` [git-safety] Blocked ${blocked.length} sensitive file(s) from staging: ${blocked.slice(0, 5).join(", ")}${blocked.length > 5 ? "..." : ""}`);
|
|
4656
|
+
}
|
|
4657
|
+
if (safeLines.length === 0) {
|
|
4658
|
+
console.log(` [git-safety] All ${allLines.length} changed files were blocked by safety filter`);
|
|
4659
|
+
return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false };
|
|
4660
|
+
}
|
|
4460
4661
|
let added = 0, modified = 0, deleted = 0;
|
|
4461
|
-
for (const line of
|
|
4662
|
+
for (const line of safeLines) {
|
|
4462
4663
|
const code = line.substring(0, 2);
|
|
4463
4664
|
if (code.includes("?")) added++;
|
|
4464
4665
|
else if (code.includes("D")) deleted++;
|
|
@@ -4466,9 +4667,15 @@ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
|
4466
4667
|
else added++;
|
|
4467
4668
|
}
|
|
4468
4669
|
console.log(
|
|
4469
|
-
` [git-safety] ${
|
|
4670
|
+
` [git-safety] ${safeLines.length} safe changes (${added} new, ${modified} modified, ${deleted} deleted) \u2014 committing now`
|
|
4470
4671
|
);
|
|
4471
|
-
|
|
4672
|
+
for (const line of safeLines) {
|
|
4673
|
+
const filePath = line.substring(3).trim();
|
|
4674
|
+
try {
|
|
4675
|
+
git(`git add -- ${JSON.stringify(filePath)}`, workspaceRoot);
|
|
4676
|
+
} catch {
|
|
4677
|
+
}
|
|
4678
|
+
}
|
|
4472
4679
|
const shortId = taskId.substring(0, 8);
|
|
4473
4680
|
const commitMsg = `task: ${taskTitle} [${shortId}]
|
|
4474
4681
|
|
|
@@ -4529,8 +4736,8 @@ Files: ${added} added, ${modified} modified, ${deleted} deleted`;
|
|
|
4529
4736
|
|
|
4530
4737
|
// src/commands/deploy-manifest.ts
|
|
4531
4738
|
var import_node_child_process4 = require("node:child_process");
|
|
4532
|
-
var
|
|
4533
|
-
var
|
|
4739
|
+
var import_node_fs3 = require("node:fs");
|
|
4740
|
+
var import_node_path3 = require("node:path");
|
|
4534
4741
|
function exec(cmd, cwd, timeoutMs = 3e5, env2) {
|
|
4535
4742
|
try {
|
|
4536
4743
|
const stdout = (0, import_node_child_process4.execSync)(cmd, {
|
|
@@ -4573,10 +4780,10 @@ function getHeadCommit(cwd) {
|
|
|
4573
4780
|
}
|
|
4574
4781
|
}
|
|
4575
4782
|
function loadDeployManifest(workspaceRoot) {
|
|
4576
|
-
const manifestPath = (0,
|
|
4577
|
-
if (!(0,
|
|
4783
|
+
const manifestPath = (0, import_node_path3.join)(workspaceRoot, ".cva", "deploy.json");
|
|
4784
|
+
if (!(0, import_node_fs3.existsSync)(manifestPath)) return null;
|
|
4578
4785
|
try {
|
|
4579
|
-
const raw = (0,
|
|
4786
|
+
const raw = (0, import_node_fs3.readFileSync)(manifestPath, "utf8");
|
|
4580
4787
|
const manifest = JSON.parse(raw);
|
|
4581
4788
|
if (!manifest.version || manifest.version < 1) {
|
|
4582
4789
|
console.log(" [deploy] Invalid manifest version");
|
|
@@ -4601,7 +4808,7 @@ async function postTaskDeploy(workspaceRoot, taskId, log) {
|
|
|
4601
4808
|
console.log(` [deploy] Building: ${buildStep.name}`);
|
|
4602
4809
|
log("lifecycle", `Building: ${buildStep.name}`);
|
|
4603
4810
|
const start = Date.now();
|
|
4604
|
-
const cwd = (0,
|
|
4811
|
+
const cwd = (0, import_node_path3.join)(workspaceRoot, buildStep.working_dir || ".");
|
|
4605
4812
|
const timeoutMs = (buildStep.timeout_seconds || 300) * 1e3;
|
|
4606
4813
|
const result = exec(buildStep.command, cwd, timeoutMs);
|
|
4607
4814
|
if (!result.ok) {
|
|
@@ -4692,7 +4899,7 @@ async function rollback(workspaceRoot, targetCommit, manifest, log) {
|
|
|
4692
4899
|
}
|
|
4693
4900
|
if (manifest.build?.steps) {
|
|
4694
4901
|
for (const step of manifest.build.steps) {
|
|
4695
|
-
exec(step.command, (0,
|
|
4902
|
+
exec(step.command, (0, import_node_path3.join)(workspaceRoot, step.working_dir || "."), (step.timeout_seconds || 300) * 1e3);
|
|
4696
4903
|
}
|
|
4697
4904
|
}
|
|
4698
4905
|
if (manifest.service?.restart_command) {
|
|
@@ -4928,7 +5135,7 @@ async function launchAutoApproveMode(prompt, options) {
|
|
|
4928
5135
|
let fullOutput = "";
|
|
4929
5136
|
let lastProgressBytes = 0;
|
|
4930
5137
|
const runOnce = (inputPrompt, isContinue) => {
|
|
4931
|
-
return new Promise((
|
|
5138
|
+
return new Promise((resolve2, reject) => {
|
|
4932
5139
|
const args = isContinue ? ["-p", inputPrompt, "--continue", "--allowedTools", ...ALLOWED_TOOLS] : ["-p", inputPrompt, "--allowedTools", ...ALLOWED_TOOLS];
|
|
4933
5140
|
if (sessionId && !isContinue) {
|
|
4934
5141
|
args.push("--session-id", sessionId);
|
|
@@ -5027,7 +5234,7 @@ ${source_default.red("!")} Claude Code auth failure (stderr): "${authError}"`);
|
|
|
5027
5234
|
});
|
|
5028
5235
|
child.on("close", (code, signal) => {
|
|
5029
5236
|
_activeChild = null;
|
|
5030
|
-
|
|
5237
|
+
resolve2({
|
|
5031
5238
|
exitCode: signal === "SIGKILL" ? 137 : code ?? 1,
|
|
5032
5239
|
stderr,
|
|
5033
5240
|
output: fullOutput,
|
|
@@ -5066,7 +5273,7 @@ ${source_default.red("!")} Claude Code auth failure (stderr): "${authError}"`);
|
|
|
5066
5273
|
return result;
|
|
5067
5274
|
}
|
|
5068
5275
|
async function launchRelayMode(prompt, options) {
|
|
5069
|
-
return new Promise((
|
|
5276
|
+
return new Promise((resolve2, reject) => {
|
|
5070
5277
|
const child = (0, import_node_child_process5.spawn)("claude", [], {
|
|
5071
5278
|
cwd: options.cwd,
|
|
5072
5279
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -5259,9 +5466,9 @@ ${source_default.red("!")} Claude Code auth failure (stderr): "${authError}"`);
|
|
|
5259
5466
|
child.on("close", (code, signal) => {
|
|
5260
5467
|
_activeChild = null;
|
|
5261
5468
|
if (signal === "SIGKILL") {
|
|
5262
|
-
|
|
5469
|
+
resolve2({ exitCode: 137, stderr, output: fullOutput, authFailure });
|
|
5263
5470
|
} else {
|
|
5264
|
-
|
|
5471
|
+
resolve2({ exitCode: code ?? 1, stderr, output: fullOutput, authFailure });
|
|
5265
5472
|
}
|
|
5266
5473
|
});
|
|
5267
5474
|
child.on("error", (err) => {
|
|
@@ -5362,14 +5569,45 @@ async function runAgent(options) {
|
|
|
5362
5569
|
console.log();
|
|
5363
5570
|
process.exit(1);
|
|
5364
5571
|
}
|
|
5572
|
+
if (process.env.CV_DEBUG) {
|
|
5573
|
+
console.log(source_default.gray(` PATH: ${process.env.PATH}`));
|
|
5574
|
+
console.log(source_default.gray(` HOME: ${process.env.HOME}`));
|
|
5575
|
+
console.log(source_default.gray(` SHELL: ${process.env.SHELL}`));
|
|
5576
|
+
}
|
|
5577
|
+
let claudeBinary = "claude";
|
|
5365
5578
|
try {
|
|
5366
5579
|
(0, import_node_child_process5.execSync)("claude --version", { stdio: "pipe", timeout: 5e3 });
|
|
5367
5580
|
} catch {
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5581
|
+
const candidates = [
|
|
5582
|
+
"/usr/local/bin/claude",
|
|
5583
|
+
"/opt/homebrew/bin/claude",
|
|
5584
|
+
`${process.env.HOME}/.npm-global/bin/claude`,
|
|
5585
|
+
`${process.env.HOME}/.nvm/versions/node/*/bin/claude`
|
|
5586
|
+
];
|
|
5587
|
+
let found = false;
|
|
5588
|
+
for (const candidate of candidates) {
|
|
5589
|
+
try {
|
|
5590
|
+
const resolved = (0, import_node_child_process5.execSync)(`ls ${candidate} 2>/dev/null | head -1`, { encoding: "utf8", timeout: 3e3 }).trim();
|
|
5591
|
+
if (resolved) {
|
|
5592
|
+
(0, import_node_child_process5.execSync)(`${resolved} --version`, { stdio: "pipe", timeout: 5e3 });
|
|
5593
|
+
claudeBinary = resolved;
|
|
5594
|
+
found = true;
|
|
5595
|
+
console.log(source_default.yellow("!") + ` Claude Code found at ${resolved} (not on PATH)`);
|
|
5596
|
+
break;
|
|
5597
|
+
}
|
|
5598
|
+
} catch {
|
|
5599
|
+
}
|
|
5600
|
+
}
|
|
5601
|
+
if (!found) {
|
|
5602
|
+
console.log();
|
|
5603
|
+
console.log(source_default.red("Claude Code CLI not found.") + " Install it first:");
|
|
5604
|
+
console.log(` ${source_default.cyan("npm install -g @anthropic-ai/claude-code")}`);
|
|
5605
|
+
console.log();
|
|
5606
|
+
console.log(source_default.gray(` Current PATH: ${process.env.PATH}`));
|
|
5607
|
+
console.log(source_default.gray(" If running via Launch Agent, ensure PATH includes the directory where claude is installed."));
|
|
5608
|
+
console.log();
|
|
5609
|
+
process.exit(1);
|
|
5610
|
+
}
|
|
5373
5611
|
}
|
|
5374
5612
|
const { env: claudeEnv, usingApiKey } = await getClaudeEnv();
|
|
5375
5613
|
const authCheck = await checkClaudeAuth();
|
|
@@ -5410,6 +5648,64 @@ async function runAgent(options) {
|
|
|
5410
5648
|
console.log();
|
|
5411
5649
|
}
|
|
5412
5650
|
}
|
|
5651
|
+
{
|
|
5652
|
+
const isGitRepo = require("fs").existsSync(require("path").join(workingDir, ".git"));
|
|
5653
|
+
if (!isGitRepo) {
|
|
5654
|
+
console.log(source_default.gray(" Bootstrap: initializing git repo..."));
|
|
5655
|
+
try {
|
|
5656
|
+
(0, import_node_child_process5.execSync)("git init && git checkout -b main", { cwd: workingDir, stdio: "pipe" });
|
|
5657
|
+
} catch {
|
|
5658
|
+
}
|
|
5659
|
+
}
|
|
5660
|
+
const claudeMdPath = require("path").join(workingDir, "CLAUDE.md");
|
|
5661
|
+
if (!require("fs").existsSync(claudeMdPath)) {
|
|
5662
|
+
const name = require("path").basename(workingDir);
|
|
5663
|
+
const template = `# ${name}
|
|
5664
|
+
|
|
5665
|
+
## Overview
|
|
5666
|
+
[Describe your project here]
|
|
5667
|
+
|
|
5668
|
+
## Build & Run
|
|
5669
|
+
[How to build and run this project]
|
|
5670
|
+
`;
|
|
5671
|
+
try {
|
|
5672
|
+
require("fs").writeFileSync(claudeMdPath, template);
|
|
5673
|
+
console.log(source_default.gray(" Bootstrap: created CLAUDE.md"));
|
|
5674
|
+
} catch {
|
|
5675
|
+
}
|
|
5676
|
+
}
|
|
5677
|
+
const cvDir = require("path").join(workingDir, ".cv");
|
|
5678
|
+
if (!require("fs").existsSync(cvDir)) {
|
|
5679
|
+
try {
|
|
5680
|
+
require("fs").mkdirSync(cvDir, { recursive: true });
|
|
5681
|
+
} catch {
|
|
5682
|
+
}
|
|
5683
|
+
}
|
|
5684
|
+
if (creds.CV_HUB_PAT) {
|
|
5685
|
+
try {
|
|
5686
|
+
const existing = (0, import_node_child_process5.execSync)('git remote get-url cv-hub 2>/dev/null || echo ""', {
|
|
5687
|
+
cwd: workingDir,
|
|
5688
|
+
encoding: "utf8",
|
|
5689
|
+
timeout: 5e3
|
|
5690
|
+
}).trim();
|
|
5691
|
+
if (!existing) {
|
|
5692
|
+
const name = require("path").basename(workingDir);
|
|
5693
|
+
const gitHost = "git.hub.controlvector.io";
|
|
5694
|
+
let user = "user";
|
|
5695
|
+
try {
|
|
5696
|
+
const sharedPath = require("path").join(require("os").homedir(), ".config", "controlvector", "credentials.json");
|
|
5697
|
+
const shared = JSON.parse(require("fs").readFileSync(sharedPath, "utf-8"));
|
|
5698
|
+
if (shared.username) user = shared.username;
|
|
5699
|
+
} catch {
|
|
5700
|
+
}
|
|
5701
|
+
const remoteUrl = `https://${gitHost}/${user}/${name}.git`;
|
|
5702
|
+
(0, import_node_child_process5.execSync)(`git remote add cv-hub ${remoteUrl}`, { cwd: workingDir, stdio: "pipe" });
|
|
5703
|
+
console.log(source_default.gray(` Bootstrap: remote cv-hub \u2192 ${remoteUrl}`));
|
|
5704
|
+
}
|
|
5705
|
+
} catch {
|
|
5706
|
+
}
|
|
5707
|
+
}
|
|
5708
|
+
}
|
|
5413
5709
|
let detectedRepoId;
|
|
5414
5710
|
try {
|
|
5415
5711
|
const remoteUrl = (0, import_node_child_process5.execSync)("git remote get-url origin 2>/dev/null", {
|
|
@@ -5865,7 +6161,7 @@ async function promptForToken() {
|
|
|
5865
6161
|
input: process.stdin,
|
|
5866
6162
|
output: process.stdout
|
|
5867
6163
|
});
|
|
5868
|
-
return new Promise((
|
|
6164
|
+
return new Promise((resolve2) => {
|
|
5869
6165
|
rl.question("Enter your CV-Hub PAT token: ", (answer) => {
|
|
5870
6166
|
rl.close();
|
|
5871
6167
|
const token = answer.trim();
|
|
@@ -5873,7 +6169,7 @@ async function promptForToken() {
|
|
|
5873
6169
|
console.log(source_default.red("No token provided."));
|
|
5874
6170
|
process.exit(1);
|
|
5875
6171
|
}
|
|
5876
|
-
|
|
6172
|
+
resolve2(token);
|
|
5877
6173
|
});
|
|
5878
6174
|
});
|
|
5879
6175
|
}
|
|
@@ -6152,7 +6448,7 @@ function statusCommand() {
|
|
|
6152
6448
|
|
|
6153
6449
|
// src/index.ts
|
|
6154
6450
|
var program2 = new Command();
|
|
6155
|
-
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.
|
|
6451
|
+
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.9.1" : "1.6.0");
|
|
6156
6452
|
program2.addCommand(setupCommand());
|
|
6157
6453
|
program2.addCommand(agentCommand());
|
|
6158
6454
|
program2.addCommand(authCommand());
|