@controlvector/cv-agent 1.9.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 +143 -31
- package/dist/bundle.cjs.map +3 -3
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +40 -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/package.json +1 -1
package/dist/bundle.cjs
CHANGED
|
@@ -3847,10 +3847,10 @@ async function runSetup() {
|
|
|
3847
3847
|
console.log(` ${source_default.cyan("a")} \u2014 Initialize a new project here: ${cwd}`);
|
|
3848
3848
|
console.log(` ${source_default.cyan("b")} \u2014 Clone an existing repo from CV-Hub`);
|
|
3849
3849
|
console.log();
|
|
3850
|
-
const choice = await new Promise((
|
|
3850
|
+
const choice = await new Promise((resolve2) => {
|
|
3851
3851
|
rl.question(" Choose [a/b]: ", (answer) => {
|
|
3852
3852
|
rl.close();
|
|
3853
|
-
|
|
3853
|
+
resolve2(answer.trim().toLowerCase() || "a");
|
|
3854
3854
|
});
|
|
3855
3855
|
});
|
|
3856
3856
|
if (choice === "b" && token) {
|
|
@@ -3878,10 +3878,10 @@ async function runSetup() {
|
|
|
3878
3878
|
});
|
|
3879
3879
|
console.log();
|
|
3880
3880
|
const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
3881
|
-
const selection = await new Promise((
|
|
3881
|
+
const selection = await new Promise((resolve2) => {
|
|
3882
3882
|
rl2.question(` Select a repo [1-${displayRepos.length}]: `, (answer) => {
|
|
3883
3883
|
rl2.close();
|
|
3884
|
-
|
|
3884
|
+
resolve2(answer.trim());
|
|
3885
3885
|
});
|
|
3886
3886
|
});
|
|
3887
3887
|
const idx = parseInt(selection, 10) - 1;
|
|
@@ -4015,10 +4015,10 @@ async function runSetup() {
|
|
|
4015
4015
|
if (!agentRunning) {
|
|
4016
4016
|
const readline = await import("node:readline");
|
|
4017
4017
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
4018
|
-
const answer = await new Promise((
|
|
4018
|
+
const answer = await new Promise((resolve2) => {
|
|
4019
4019
|
rl.question(" Start the CV-Agent daemon? (Y/n): ", (a) => {
|
|
4020
4020
|
rl.close();
|
|
4021
|
-
|
|
4021
|
+
resolve2(a.trim().toLowerCase() || "y");
|
|
4022
4022
|
});
|
|
4023
4023
|
});
|
|
4024
4024
|
if (answer === "y" || answer === "yes" || answer === "") {
|
|
@@ -4558,11 +4558,78 @@ async function writeConfig(config) {
|
|
|
4558
4558
|
|
|
4559
4559
|
// src/commands/git-safety.ts
|
|
4560
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
|
+
];
|
|
4561
4585
|
function git(cmd, cwd) {
|
|
4562
4586
|
return (0, import_node_child_process3.execSync)(cmd, { cwd, encoding: "utf8", timeout: 3e4 }).trim();
|
|
4563
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
|
+
}
|
|
4564
4618
|
function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
4565
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
|
+
}
|
|
4566
4633
|
try {
|
|
4567
4634
|
let statusOutput;
|
|
4568
4635
|
try {
|
|
@@ -4570,8 +4637,8 @@ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
|
4570
4637
|
} catch {
|
|
4571
4638
|
return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false, error: "git status failed" };
|
|
4572
4639
|
}
|
|
4573
|
-
const
|
|
4574
|
-
if (
|
|
4640
|
+
const allLines = statusOutput.split("\n").filter(Boolean);
|
|
4641
|
+
if (allLines.length === 0) {
|
|
4575
4642
|
try {
|
|
4576
4643
|
const unpushed = git(`git log origin/${targetBranch}..HEAD --oneline 2>/dev/null`, workspaceRoot);
|
|
4577
4644
|
if (unpushed) {
|
|
@@ -4583,8 +4650,16 @@ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
|
4583
4650
|
}
|
|
4584
4651
|
return { hadChanges: false, filesAdded: 0, filesModified: 0, filesDeleted: 0, pushed: false };
|
|
4585
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
|
+
}
|
|
4586
4661
|
let added = 0, modified = 0, deleted = 0;
|
|
4587
|
-
for (const line of
|
|
4662
|
+
for (const line of safeLines) {
|
|
4588
4663
|
const code = line.substring(0, 2);
|
|
4589
4664
|
if (code.includes("?")) added++;
|
|
4590
4665
|
else if (code.includes("D")) deleted++;
|
|
@@ -4592,9 +4667,15 @@ function gitSafetyNet(workspaceRoot, taskTitle, taskId, branch) {
|
|
|
4592
4667
|
else added++;
|
|
4593
4668
|
}
|
|
4594
4669
|
console.log(
|
|
4595
|
-
` [git-safety] ${
|
|
4670
|
+
` [git-safety] ${safeLines.length} safe changes (${added} new, ${modified} modified, ${deleted} deleted) \u2014 committing now`
|
|
4596
4671
|
);
|
|
4597
|
-
|
|
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
|
+
}
|
|
4598
4679
|
const shortId = taskId.substring(0, 8);
|
|
4599
4680
|
const commitMsg = `task: ${taskTitle} [${shortId}]
|
|
4600
4681
|
|
|
@@ -4655,8 +4736,8 @@ Files: ${added} added, ${modified} modified, ${deleted} deleted`;
|
|
|
4655
4736
|
|
|
4656
4737
|
// src/commands/deploy-manifest.ts
|
|
4657
4738
|
var import_node_child_process4 = require("node:child_process");
|
|
4658
|
-
var
|
|
4659
|
-
var
|
|
4739
|
+
var import_node_fs3 = require("node:fs");
|
|
4740
|
+
var import_node_path3 = require("node:path");
|
|
4660
4741
|
function exec(cmd, cwd, timeoutMs = 3e5, env2) {
|
|
4661
4742
|
try {
|
|
4662
4743
|
const stdout = (0, import_node_child_process4.execSync)(cmd, {
|
|
@@ -4699,10 +4780,10 @@ function getHeadCommit(cwd) {
|
|
|
4699
4780
|
}
|
|
4700
4781
|
}
|
|
4701
4782
|
function loadDeployManifest(workspaceRoot) {
|
|
4702
|
-
const manifestPath = (0,
|
|
4703
|
-
if (!(0,
|
|
4783
|
+
const manifestPath = (0, import_node_path3.join)(workspaceRoot, ".cva", "deploy.json");
|
|
4784
|
+
if (!(0, import_node_fs3.existsSync)(manifestPath)) return null;
|
|
4704
4785
|
try {
|
|
4705
|
-
const raw = (0,
|
|
4786
|
+
const raw = (0, import_node_fs3.readFileSync)(manifestPath, "utf8");
|
|
4706
4787
|
const manifest = JSON.parse(raw);
|
|
4707
4788
|
if (!manifest.version || manifest.version < 1) {
|
|
4708
4789
|
console.log(" [deploy] Invalid manifest version");
|
|
@@ -4727,7 +4808,7 @@ async function postTaskDeploy(workspaceRoot, taskId, log) {
|
|
|
4727
4808
|
console.log(` [deploy] Building: ${buildStep.name}`);
|
|
4728
4809
|
log("lifecycle", `Building: ${buildStep.name}`);
|
|
4729
4810
|
const start = Date.now();
|
|
4730
|
-
const cwd = (0,
|
|
4811
|
+
const cwd = (0, import_node_path3.join)(workspaceRoot, buildStep.working_dir || ".");
|
|
4731
4812
|
const timeoutMs = (buildStep.timeout_seconds || 300) * 1e3;
|
|
4732
4813
|
const result = exec(buildStep.command, cwd, timeoutMs);
|
|
4733
4814
|
if (!result.ok) {
|
|
@@ -4818,7 +4899,7 @@ async function rollback(workspaceRoot, targetCommit, manifest, log) {
|
|
|
4818
4899
|
}
|
|
4819
4900
|
if (manifest.build?.steps) {
|
|
4820
4901
|
for (const step of manifest.build.steps) {
|
|
4821
|
-
exec(step.command, (0,
|
|
4902
|
+
exec(step.command, (0, import_node_path3.join)(workspaceRoot, step.working_dir || "."), (step.timeout_seconds || 300) * 1e3);
|
|
4822
4903
|
}
|
|
4823
4904
|
}
|
|
4824
4905
|
if (manifest.service?.restart_command) {
|
|
@@ -5054,7 +5135,7 @@ async function launchAutoApproveMode(prompt, options) {
|
|
|
5054
5135
|
let fullOutput = "";
|
|
5055
5136
|
let lastProgressBytes = 0;
|
|
5056
5137
|
const runOnce = (inputPrompt, isContinue) => {
|
|
5057
|
-
return new Promise((
|
|
5138
|
+
return new Promise((resolve2, reject) => {
|
|
5058
5139
|
const args = isContinue ? ["-p", inputPrompt, "--continue", "--allowedTools", ...ALLOWED_TOOLS] : ["-p", inputPrompt, "--allowedTools", ...ALLOWED_TOOLS];
|
|
5059
5140
|
if (sessionId && !isContinue) {
|
|
5060
5141
|
args.push("--session-id", sessionId);
|
|
@@ -5153,7 +5234,7 @@ ${source_default.red("!")} Claude Code auth failure (stderr): "${authError}"`);
|
|
|
5153
5234
|
});
|
|
5154
5235
|
child.on("close", (code, signal) => {
|
|
5155
5236
|
_activeChild = null;
|
|
5156
|
-
|
|
5237
|
+
resolve2({
|
|
5157
5238
|
exitCode: signal === "SIGKILL" ? 137 : code ?? 1,
|
|
5158
5239
|
stderr,
|
|
5159
5240
|
output: fullOutput,
|
|
@@ -5192,7 +5273,7 @@ ${source_default.red("!")} Claude Code auth failure (stderr): "${authError}"`);
|
|
|
5192
5273
|
return result;
|
|
5193
5274
|
}
|
|
5194
5275
|
async function launchRelayMode(prompt, options) {
|
|
5195
|
-
return new Promise((
|
|
5276
|
+
return new Promise((resolve2, reject) => {
|
|
5196
5277
|
const child = (0, import_node_child_process5.spawn)("claude", [], {
|
|
5197
5278
|
cwd: options.cwd,
|
|
5198
5279
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -5385,9 +5466,9 @@ ${source_default.red("!")} Claude Code auth failure (stderr): "${authError}"`);
|
|
|
5385
5466
|
child.on("close", (code, signal) => {
|
|
5386
5467
|
_activeChild = null;
|
|
5387
5468
|
if (signal === "SIGKILL") {
|
|
5388
|
-
|
|
5469
|
+
resolve2({ exitCode: 137, stderr, output: fullOutput, authFailure });
|
|
5389
5470
|
} else {
|
|
5390
|
-
|
|
5471
|
+
resolve2({ exitCode: code ?? 1, stderr, output: fullOutput, authFailure });
|
|
5391
5472
|
}
|
|
5392
5473
|
});
|
|
5393
5474
|
child.on("error", (err) => {
|
|
@@ -5488,14 +5569,45 @@ async function runAgent(options) {
|
|
|
5488
5569
|
console.log();
|
|
5489
5570
|
process.exit(1);
|
|
5490
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";
|
|
5491
5578
|
try {
|
|
5492
5579
|
(0, import_node_child_process5.execSync)("claude --version", { stdio: "pipe", timeout: 5e3 });
|
|
5493
5580
|
} catch {
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
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
|
+
}
|
|
5499
5611
|
}
|
|
5500
5612
|
const { env: claudeEnv, usingApiKey } = await getClaudeEnv();
|
|
5501
5613
|
const authCheck = await checkClaudeAuth();
|
|
@@ -6049,7 +6161,7 @@ async function promptForToken() {
|
|
|
6049
6161
|
input: process.stdin,
|
|
6050
6162
|
output: process.stdout
|
|
6051
6163
|
});
|
|
6052
|
-
return new Promise((
|
|
6164
|
+
return new Promise((resolve2) => {
|
|
6053
6165
|
rl.question("Enter your CV-Hub PAT token: ", (answer) => {
|
|
6054
6166
|
rl.close();
|
|
6055
6167
|
const token = answer.trim();
|
|
@@ -6057,7 +6169,7 @@ async function promptForToken() {
|
|
|
6057
6169
|
console.log(source_default.red("No token provided."));
|
|
6058
6170
|
process.exit(1);
|
|
6059
6171
|
}
|
|
6060
|
-
|
|
6172
|
+
resolve2(token);
|
|
6061
6173
|
});
|
|
6062
6174
|
});
|
|
6063
6175
|
}
|
|
@@ -6336,7 +6448,7 @@ function statusCommand() {
|
|
|
6336
6448
|
|
|
6337
6449
|
// src/index.ts
|
|
6338
6450
|
var program2 = new Command();
|
|
6339
|
-
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.
|
|
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");
|
|
6340
6452
|
program2.addCommand(setupCommand());
|
|
6341
6453
|
program2.addCommand(agentCommand());
|
|
6342
6454
|
program2.addCommand(authCommand());
|