@rely-ai/caliber 0.8.1 → 0.9.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/bin.js +206 -30
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -3965,6 +3965,11 @@ async function initCommand(options) {
|
|
|
3965
3965
|
const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
|
|
3966
3966
|
genSpinner.succeed(`Setup generated ${chalk4.dim(`in ${timeStr}`)}`);
|
|
3967
3967
|
printSetupSummary(generatedSetup);
|
|
3968
|
+
const sessionHistory = [];
|
|
3969
|
+
sessionHistory.push({
|
|
3970
|
+
role: "assistant",
|
|
3971
|
+
content: summarizeSetup("Initial generation", generatedSetup)
|
|
3972
|
+
});
|
|
3968
3973
|
console.log(title.bold(" Step 4/4 \u2014 Review\n"));
|
|
3969
3974
|
const setupFiles = collectSetupFiles(generatedSetup);
|
|
3970
3975
|
const staged = stageFiles(setupFiles, process.cwd());
|
|
@@ -3973,11 +3978,11 @@ async function initCommand(options) {
|
|
|
3973
3978
|
const wantsReview = await promptWantsReview();
|
|
3974
3979
|
if (wantsReview) {
|
|
3975
3980
|
const reviewMethod = await promptReviewMethod();
|
|
3976
|
-
openReview(reviewMethod, staged.stagedFiles);
|
|
3981
|
+
await openReview(reviewMethod, staged.stagedFiles);
|
|
3977
3982
|
}
|
|
3978
3983
|
let action = await promptReviewAction();
|
|
3979
3984
|
while (action === "refine") {
|
|
3980
|
-
generatedSetup = await refineLoop(generatedSetup, targetAgent);
|
|
3985
|
+
generatedSetup = await refineLoop(generatedSetup, targetAgent, sessionHistory);
|
|
3981
3986
|
if (!generatedSetup) {
|
|
3982
3987
|
cleanupStaging();
|
|
3983
3988
|
console.log(chalk4.dim("Refinement cancelled. No files were modified."));
|
|
@@ -3988,11 +3993,7 @@ async function initCommand(options) {
|
|
|
3988
3993
|
console.log(chalk4.dim(` ${chalk4.green(`${restaged.newFiles} new`)} / ${chalk4.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
|
|
3989
3994
|
`));
|
|
3990
3995
|
printSetupSummary(generatedSetup);
|
|
3991
|
-
|
|
3992
|
-
if (wantsReviewAgain) {
|
|
3993
|
-
const reviewMethod = await promptReviewMethod();
|
|
3994
|
-
openReview(reviewMethod, restaged.stagedFiles);
|
|
3995
|
-
}
|
|
3996
|
+
await openReview("terminal", restaged.stagedFiles);
|
|
3996
3997
|
action = await promptReviewAction();
|
|
3997
3998
|
}
|
|
3998
3999
|
cleanupStaging();
|
|
@@ -4092,8 +4093,7 @@ async function initCommand(options) {
|
|
|
4092
4093
|
console.log(` ${title("caliber undo")} Revert all changes from this run`);
|
|
4093
4094
|
console.log("");
|
|
4094
4095
|
}
|
|
4095
|
-
async function refineLoop(currentSetup, _targetAgent) {
|
|
4096
|
-
const history = [];
|
|
4096
|
+
async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
|
|
4097
4097
|
while (true) {
|
|
4098
4098
|
const message = await promptInput2("\nWhat would you like to change?");
|
|
4099
4099
|
if (!message || message.toLowerCase() === "done" || message.toLowerCase() === "accept") {
|
|
@@ -4102,19 +4102,29 @@ async function refineLoop(currentSetup, _targetAgent) {
|
|
|
4102
4102
|
if (message.toLowerCase() === "cancel") {
|
|
4103
4103
|
return null;
|
|
4104
4104
|
}
|
|
4105
|
+
const isValid = await classifyRefineIntent(message);
|
|
4106
|
+
if (!isValid) {
|
|
4107
|
+
console.log(chalk4.dim(" This doesn't look like a config change request."));
|
|
4108
|
+
console.log(chalk4.dim(" Describe what to add, remove, or modify in your configs."));
|
|
4109
|
+
console.log(chalk4.dim(' Type "done" to accept the current setup.\n'));
|
|
4110
|
+
continue;
|
|
4111
|
+
}
|
|
4105
4112
|
const refineSpinner = ora("Refining setup...").start();
|
|
4106
4113
|
const refineMessages = new SpinnerMessages(refineSpinner, REFINE_MESSAGES);
|
|
4107
4114
|
refineMessages.start();
|
|
4108
4115
|
const refined = await refineSetup(
|
|
4109
4116
|
currentSetup,
|
|
4110
4117
|
message,
|
|
4111
|
-
|
|
4118
|
+
sessionHistory
|
|
4112
4119
|
);
|
|
4113
4120
|
refineMessages.stop();
|
|
4114
4121
|
if (refined) {
|
|
4115
4122
|
currentSetup = refined;
|
|
4116
|
-
|
|
4117
|
-
|
|
4123
|
+
sessionHistory.push({ role: "user", content: message });
|
|
4124
|
+
sessionHistory.push({
|
|
4125
|
+
role: "assistant",
|
|
4126
|
+
content: summarizeSetup("Applied changes", refined)
|
|
4127
|
+
});
|
|
4118
4128
|
refineSpinner.succeed("Setup updated");
|
|
4119
4129
|
printSetupSummary(refined);
|
|
4120
4130
|
console.log(chalk4.dim('Type "done" to accept, or describe more changes.'));
|
|
@@ -4124,6 +4134,29 @@ async function refineLoop(currentSetup, _targetAgent) {
|
|
|
4124
4134
|
}
|
|
4125
4135
|
}
|
|
4126
4136
|
}
|
|
4137
|
+
function summarizeSetup(action, setup) {
|
|
4138
|
+
const descriptions = setup.fileDescriptions;
|
|
4139
|
+
const files = descriptions ? Object.entries(descriptions).map(([path23, desc]) => ` ${path23}: ${desc}`).join("\n") : Object.keys(setup).filter((k) => k !== "targetAgent" && k !== "fileDescriptions").join(", ");
|
|
4140
|
+
return `${action}. Files:
|
|
4141
|
+
${files}`;
|
|
4142
|
+
}
|
|
4143
|
+
async function classifyRefineIntent(message) {
|
|
4144
|
+
const fastModel = process.env.ANTHROPIC_SMALL_FAST_MODEL;
|
|
4145
|
+
try {
|
|
4146
|
+
const result = await llmJsonCall({
|
|
4147
|
+
system: `You classify whether a user message is a valid request to modify AI agent config files (CLAUDE.md, .cursorrules, skills).
|
|
4148
|
+
Valid: requests to add, remove, change, or restructure config content. Examples: "add testing commands", "remove the terraform section", "make CLAUDE.md shorter".
|
|
4149
|
+
Invalid: questions, requests to show/display something, general chat, or anything that isn't a concrete config change.
|
|
4150
|
+
Return {"valid": true} or {"valid": false}. Nothing else.`,
|
|
4151
|
+
prompt: message,
|
|
4152
|
+
maxTokens: 20,
|
|
4153
|
+
...fastModel ? { model: fastModel } : {}
|
|
4154
|
+
});
|
|
4155
|
+
return result.valid === true;
|
|
4156
|
+
} catch {
|
|
4157
|
+
return true;
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4127
4160
|
function promptInput2(question) {
|
|
4128
4161
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
4129
4162
|
return new Promise((resolve2) => {
|
|
@@ -4183,34 +4216,177 @@ async function promptReviewMethod() {
|
|
|
4183
4216
|
});
|
|
4184
4217
|
return select2({ message: "How would you like to review the changes?", choices });
|
|
4185
4218
|
}
|
|
4186
|
-
function openReview(method, stagedFiles) {
|
|
4219
|
+
async function openReview(method, stagedFiles) {
|
|
4187
4220
|
if (method === "cursor" || method === "vscode") {
|
|
4188
4221
|
openDiffsInEditor(method, stagedFiles.map((f) => ({
|
|
4189
4222
|
originalPath: f.originalPath,
|
|
4190
4223
|
proposedPath: f.proposedPath
|
|
4191
4224
|
})));
|
|
4192
4225
|
console.log(chalk4.dim(" Diffs opened in your editor.\n"));
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4226
|
+
return;
|
|
4227
|
+
}
|
|
4228
|
+
const fileInfos = stagedFiles.map((file) => {
|
|
4229
|
+
const proposed = fs17.readFileSync(file.proposedPath, "utf-8");
|
|
4230
|
+
const current = file.currentPath ? fs17.readFileSync(file.currentPath, "utf-8") : "";
|
|
4231
|
+
const patch = createTwoFilesPatch(
|
|
4232
|
+
file.isNew ? "/dev/null" : file.relativePath,
|
|
4233
|
+
file.relativePath,
|
|
4234
|
+
current,
|
|
4235
|
+
proposed
|
|
4236
|
+
);
|
|
4237
|
+
let added = 0, removed = 0;
|
|
4238
|
+
for (const line of patch.split("\n")) {
|
|
4239
|
+
if (line.startsWith("+") && !line.startsWith("+++")) added++;
|
|
4240
|
+
if (line.startsWith("-") && !line.startsWith("---")) removed++;
|
|
4241
|
+
}
|
|
4242
|
+
return {
|
|
4243
|
+
relativePath: file.relativePath,
|
|
4244
|
+
isNew: file.isNew,
|
|
4245
|
+
added,
|
|
4246
|
+
removed,
|
|
4247
|
+
lines: proposed.split("\n").length,
|
|
4248
|
+
patch
|
|
4249
|
+
};
|
|
4250
|
+
});
|
|
4251
|
+
await interactiveDiffExplorer(fileInfos);
|
|
4252
|
+
}
|
|
4253
|
+
async function interactiveDiffExplorer(files) {
|
|
4254
|
+
if (!process.stdin.isTTY) {
|
|
4255
|
+
for (const f of files) {
|
|
4256
|
+
const icon = f.isNew ? chalk4.green("+") : chalk4.yellow("~");
|
|
4257
|
+
const stats = f.isNew ? chalk4.dim(`${f.lines} lines`) : `${chalk4.green(`+${f.added}`)} ${chalk4.red(`-${f.removed}`)}`;
|
|
4258
|
+
console.log(` ${icon} ${f.relativePath} ${stats}`);
|
|
4259
|
+
}
|
|
4260
|
+
console.log("");
|
|
4261
|
+
return;
|
|
4262
|
+
}
|
|
4263
|
+
const { stdin, stdout } = process;
|
|
4264
|
+
let cursor = 0;
|
|
4265
|
+
let viewing = null;
|
|
4266
|
+
let scrollOffset = 0;
|
|
4267
|
+
let lineCount = 0;
|
|
4268
|
+
function getTermHeight() {
|
|
4269
|
+
return (stdout.rows || 24) - 4;
|
|
4270
|
+
}
|
|
4271
|
+
function renderFileList() {
|
|
4272
|
+
const lines = [];
|
|
4273
|
+
lines.push(chalk4.bold(" Review changes"));
|
|
4274
|
+
lines.push("");
|
|
4275
|
+
for (let i = 0; i < files.length; i++) {
|
|
4276
|
+
const f = files[i];
|
|
4277
|
+
const ptr = i === cursor ? chalk4.cyan(">") : " ";
|
|
4278
|
+
const icon = f.isNew ? chalk4.green("+") : chalk4.yellow("~");
|
|
4279
|
+
const stats = f.isNew ? chalk4.dim(`${f.lines} lines`) : `${chalk4.green(`+${f.added}`)} ${chalk4.red(`-${f.removed}`)}`;
|
|
4280
|
+
lines.push(` ${ptr} ${icon} ${f.relativePath} ${stats}`);
|
|
4281
|
+
}
|
|
4282
|
+
lines.push("");
|
|
4283
|
+
lines.push(chalk4.dim(" \u2191\u2193 navigate \u23CE view diff q done"));
|
|
4284
|
+
return lines.join("\n");
|
|
4285
|
+
}
|
|
4286
|
+
function renderDiff(index) {
|
|
4287
|
+
const f = files[index];
|
|
4288
|
+
const lines = [];
|
|
4289
|
+
const header = f.isNew ? ` ${chalk4.green("+")} ${f.relativePath} ${chalk4.dim("(new file)")}` : ` ${chalk4.yellow("~")} ${f.relativePath} ${chalk4.green(`+${f.added}`)} ${chalk4.red(`-${f.removed}`)}`;
|
|
4290
|
+
lines.push(header);
|
|
4291
|
+
lines.push(chalk4.dim(" " + "\u2500".repeat(60)));
|
|
4292
|
+
const patchLines = f.patch.split("\n");
|
|
4293
|
+
const bodyLines = patchLines.slice(4);
|
|
4294
|
+
const maxVisible = getTermHeight() - 4;
|
|
4295
|
+
const visibleLines = bodyLines.slice(scrollOffset, scrollOffset + maxVisible);
|
|
4296
|
+
for (const line of visibleLines) {
|
|
4297
|
+
if (line.startsWith("+")) {
|
|
4298
|
+
lines.push(chalk4.green(" " + line));
|
|
4299
|
+
} else if (line.startsWith("-")) {
|
|
4300
|
+
lines.push(chalk4.red(" " + line));
|
|
4301
|
+
} else if (line.startsWith("@@")) {
|
|
4302
|
+
lines.push(chalk4.cyan(" " + line));
|
|
4205
4303
|
} else {
|
|
4206
|
-
|
|
4207
|
-
console.log(` ${chalk4.green("+")} ${file.relativePath} ${chalk4.dim(`${lines} lines`)}`);
|
|
4304
|
+
lines.push(chalk4.dim(" " + line));
|
|
4208
4305
|
}
|
|
4209
4306
|
}
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4307
|
+
const totalBody = bodyLines.length;
|
|
4308
|
+
if (totalBody > maxVisible) {
|
|
4309
|
+
const pct = Math.round((scrollOffset + maxVisible) / totalBody * 100);
|
|
4310
|
+
lines.push(chalk4.dim(` \u2500\u2500 ${Math.min(pct, 100)}% \u2500\u2500`));
|
|
4311
|
+
}
|
|
4312
|
+
lines.push("");
|
|
4313
|
+
lines.push(chalk4.dim(" \u2191\u2193 scroll \u23B5/esc back to file list"));
|
|
4314
|
+
return lines.join("\n");
|
|
4315
|
+
}
|
|
4316
|
+
function draw(initial) {
|
|
4317
|
+
if (!initial && lineCount > 0) {
|
|
4318
|
+
stdout.write(`\x1B[${lineCount}A`);
|
|
4319
|
+
}
|
|
4320
|
+
stdout.write("\x1B[0J");
|
|
4321
|
+
const output = viewing !== null ? renderDiff(viewing) : renderFileList();
|
|
4322
|
+
stdout.write(output + "\n");
|
|
4323
|
+
lineCount = output.split("\n").length;
|
|
4213
4324
|
}
|
|
4325
|
+
return new Promise((resolve2) => {
|
|
4326
|
+
console.log("");
|
|
4327
|
+
draw(true);
|
|
4328
|
+
stdin.setRawMode(true);
|
|
4329
|
+
stdin.resume();
|
|
4330
|
+
stdin.setEncoding("utf8");
|
|
4331
|
+
function cleanup() {
|
|
4332
|
+
stdin.removeListener("data", onData);
|
|
4333
|
+
stdin.setRawMode(false);
|
|
4334
|
+
stdin.pause();
|
|
4335
|
+
}
|
|
4336
|
+
function onData(key) {
|
|
4337
|
+
if (viewing !== null) {
|
|
4338
|
+
const f = files[viewing];
|
|
4339
|
+
const totalBody = f.patch.split("\n").length - 4;
|
|
4340
|
+
const maxVisible = getTermHeight() - 4;
|
|
4341
|
+
switch (key) {
|
|
4342
|
+
case "\x1B[A":
|
|
4343
|
+
scrollOffset = Math.max(0, scrollOffset - 1);
|
|
4344
|
+
draw(false);
|
|
4345
|
+
break;
|
|
4346
|
+
case "\x1B[B":
|
|
4347
|
+
scrollOffset = Math.min(Math.max(0, totalBody - maxVisible), scrollOffset + 1);
|
|
4348
|
+
draw(false);
|
|
4349
|
+
break;
|
|
4350
|
+
case " ":
|
|
4351
|
+
case "\x1B":
|
|
4352
|
+
viewing = null;
|
|
4353
|
+
scrollOffset = 0;
|
|
4354
|
+
draw(false);
|
|
4355
|
+
break;
|
|
4356
|
+
case "q":
|
|
4357
|
+
case "":
|
|
4358
|
+
cleanup();
|
|
4359
|
+
console.log("");
|
|
4360
|
+
resolve2();
|
|
4361
|
+
break;
|
|
4362
|
+
}
|
|
4363
|
+
} else {
|
|
4364
|
+
switch (key) {
|
|
4365
|
+
case "\x1B[A":
|
|
4366
|
+
cursor = (cursor - 1 + files.length) % files.length;
|
|
4367
|
+
draw(false);
|
|
4368
|
+
break;
|
|
4369
|
+
case "\x1B[B":
|
|
4370
|
+
cursor = (cursor + 1) % files.length;
|
|
4371
|
+
draw(false);
|
|
4372
|
+
break;
|
|
4373
|
+
case "\r":
|
|
4374
|
+
case "\n":
|
|
4375
|
+
viewing = cursor;
|
|
4376
|
+
scrollOffset = 0;
|
|
4377
|
+
draw(false);
|
|
4378
|
+
break;
|
|
4379
|
+
case "q":
|
|
4380
|
+
case "":
|
|
4381
|
+
cleanup();
|
|
4382
|
+
console.log("");
|
|
4383
|
+
resolve2();
|
|
4384
|
+
break;
|
|
4385
|
+
}
|
|
4386
|
+
}
|
|
4387
|
+
}
|
|
4388
|
+
stdin.on("data", onData);
|
|
4389
|
+
});
|
|
4214
4390
|
}
|
|
4215
4391
|
async function promptReviewAction() {
|
|
4216
4392
|
return select2({
|
package/package.json
CHANGED