Skip to content

Commit 1500e87

Browse files
committed
feat: add --force flag and tweak pkg manager
1 parent 52c5a53 commit 1500e87

File tree

5 files changed

+161
-125
lines changed

5 files changed

+161
-125
lines changed

apps/nefi/src/commands/agent/impl.ts

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ const executionPlanSchema = z.object({
6868
"git-operations",
6969
]),
7070
priority: z.number(),
71-
})
71+
}),
7272
),
7373
analysis: z.string(),
7474
});
@@ -81,6 +81,7 @@ type AgentCommandOptions = {
8181
clipanionContext: Partial<{
8282
usage: boolean;
8383
verbose: boolean;
84+
force: boolean;
8485
}>;
8586
};
8687

@@ -96,10 +97,14 @@ export async function agentCommand({
9697
intro(`Hello, ${await getSystemUserName()}!`);
9798
}
9899

99-
if (!(await isGitWorkingTreeClean())) {
100+
if (!clipanionContext.force && !(await isGitWorkingTreeClean())) {
100101
return;
101102
}
102103

104+
if (clipanionContext.force) {
105+
log.info("Skipped checking if the git working tree is clean");
106+
}
107+
103108
const userInput =
104109
initialResponse ??
105110
(await text({
@@ -113,7 +118,7 @@ export async function agentCommand({
113118
}
114119

115120
const executionPipelineContext = await getExecutionContext(
116-
previousExecutionContext
121+
previousExecutionContext,
117122
);
118123

119124
log.step("Analyzing request and generating base execution plan");
@@ -122,7 +127,7 @@ export async function agentCommand({
122127
spin.start(
123128
initialResponse
124129
? "Regenerating execution plan based on your feedback..."
125-
: "Generating base execution plan..."
130+
: "Generating base execution plan...",
126131
);
127132

128133
try {
@@ -198,7 +203,7 @@ export async function agentCommand({
198203

199204
async function regenerateExecutionPlan(
200205
userFeedbackInput: string,
201-
previousPlan: any
206+
previousPlan: any,
202207
): Promise<{
203208
updatedExecutionPlan: any;
204209
updatedExecutionPlanUsage: any;
@@ -325,7 +330,7 @@ export async function agentCommand({
325330
log.info("\nProposed Execution Steps:");
326331
for (const [index, step] of currentPlan.steps.entries()) {
327332
log.message(
328-
`${pc.bgWhiteBright(" " + pc.black(pc.bold(index + 1)) + " ")} ${step.description} ${pc.gray("(using ")}${pc.gray(step.scriptFile)}${pc.gray(")")}`
333+
`${pc.bgWhiteBright(" " + pc.black(pc.bold(index + 1)) + " ")} ${step.description} ${pc.gray("(using ")}${pc.gray(step.scriptFile)}${pc.gray(")")}`,
329334
);
330335
}
331336

@@ -343,7 +348,7 @@ export async function agentCommand({
343348

344349
if (regenerationAttempts >= MAX_REGENERATION_ATTEMPTS) {
345350
outro(
346-
"It seems that our proposed solution wasn't fully suited for your needs :( Please start nefi again and try to provide more detailed description"
351+
"It seems that our proposed solution wasn't fully suited for your needs :( Please start nefi again and try to provide more detailed description",
347352
);
348353
return;
349354
}
@@ -381,7 +386,7 @@ export async function agentCommand({
381386

382387
// User approved the plan, execute it
383388
const sortedSteps = [...currentPlan.steps].sort(
384-
(a, b) => a.priority - b.priority
389+
(a, b) => a.priority - b.priority,
385390
);
386391
detailedLogger.verboseLog("Sorted execution steps", sortedSteps);
387392

@@ -408,7 +413,7 @@ export async function agentCommand({
408413
) {
409414
detailedLogger.verboseLog(
410415
"Gathering required files",
411-
handler.requirements.requiredFilesByPath
416+
handler.requirements.requiredFilesByPath,
412417
);
413418
for (const fileToRequirePath of handler.requirements
414419
.requiredFilesByPath) {
@@ -420,9 +425,13 @@ export async function agentCommand({
420425
requiredFiles[fileToRequirePath] =
421426
executionPipelineContext.files[fileToRequirePath];
422427

423-
detailedLogger.verboseLog(`Found required file: ${fileToRequirePath}`);
428+
detailedLogger.verboseLog(
429+
`Found required file: ${fileToRequirePath}`,
430+
);
424431
} else {
425-
detailedLogger.verboseLog(`Missing required file: ${fileToRequirePath}`);
432+
detailedLogger.verboseLog(
433+
`Missing required file: ${fileToRequirePath}`,
434+
);
426435
}
427436
}
428437
} else if (
@@ -431,7 +440,7 @@ export async function agentCommand({
431440
) {
432441
detailedLogger.verboseLog(
433442
"Processing file patterns",
434-
handler.requirements.requiredFilesByPathWildcard
443+
handler.requirements.requiredFilesByPathWildcard,
435444
);
436445
const paths = Object.keys(executionPipelineContext.files);
437446

@@ -444,10 +453,13 @@ export async function agentCommand({
444453
[path]:
445454
executionPipelineContext.files[projectFilePath(path)],
446455
}),
447-
{}
456+
{},
448457
);
449458
Object.assign(requiredFiles, matchingFiles);
450-
detailedLogger.verboseLog(`Pattern ${pattern} matched files`, matchingPaths);
459+
detailedLogger.verboseLog(
460+
`Pattern ${pattern} matched files`,
461+
matchingPaths,
462+
);
451463
}
452464

453465
if (
@@ -456,17 +468,20 @@ export async function agentCommand({
456468
) {
457469
detailedLogger.verboseLog(
458470
"Processing excluded patterns",
459-
handler.requirements.excludedFilesByPathWildcard
471+
handler.requirements.excludedFilesByPathWildcard,
460472
);
461473
const excludedPaths = micromatch(
462474
Object.keys(requiredFiles),
463-
handler.requirements.excludedFilesByPathWildcard
475+
handler.requirements.excludedFilesByPathWildcard,
464476
);
465477

466478
for (const path of excludedPaths) {
467479
delete requiredFiles[projectFilePath(path)];
468480
}
469-
detailedLogger.verboseLog("Files after exclusion", Object.keys(requiredFiles));
481+
detailedLogger.verboseLog(
482+
"Files after exclusion",
483+
Object.keys(requiredFiles),
484+
);
470485
}
471486
}
472487
}
@@ -485,7 +500,9 @@ export async function agentCommand({
485500
});
486501

487502
await handler.execute(scriptContext);
488-
detailedLogger.verboseLog(`Completed execution of ${step.scriptFile}`);
503+
detailedLogger.verboseLog(
504+
`Completed execution of ${step.scriptFile}`,
505+
);
489506
}
490507

491508
outro("All operations completed successfully");
@@ -497,16 +514,16 @@ export async function agentCommand({
497514
error.message.toLowerCase().includes("billing")
498515
) {
499516
log.error(
500-
"Unfortunately, your credit balance is too low to access the Anthropic API."
517+
"Unfortunately, your credit balance is too low to access the Anthropic API.",
501518
);
502519
log.info(
503-
`You can go to Plans & Billing section of the ${pc.bold("https://console.anthropic.com/")} to upgrade or purchase credits.`
520+
`You can go to Plans & Billing section of the ${pc.bold("https://console.anthropic.com/")} to upgrade or purchase credits.`,
504521
);
505522
outro("See you later fellow developer o/");
506523
return;
507524
}
508525
outro(
509-
`Error during execution: ${error instanceof Error ? error.message : "Unknown error"}`
526+
`Error during execution: ${error instanceof Error ? error.message : "Unknown error"}`,
510527
);
511528
return;
512529
}
@@ -582,13 +599,13 @@ export async function agentCommand({
582599
encoding: "utf-8",
583600
});
584601
return [relativeFilePath, fileContent];
585-
})
586-
)
602+
}),
603+
),
587604
) as ProjectFiles;
588605
}
589606

590607
async function getExecutionContext(
591-
previousExecutionContext: AgentCommandOptions["previousExecutionContext"]
608+
previousExecutionContext: AgentCommandOptions["previousExecutionContext"],
592609
) {
593610
if (
594611
R.isNullish(previousExecutionContext) ||
@@ -672,10 +689,10 @@ export async function agentCommand({
672689

673690
if (!isGitRepo) {
674691
log.warn(
675-
"This directory is not a git repository. For proper functioning of the program we require git."
692+
"This directory is not a git repository. For proper functioning of the program we require git.",
676693
);
677694
log.info(
678-
`You can initialize git by running:\n${pc.bold("git init")}, ${pc.bold("git add .")} and ${pc.bold("git commit")} to start tracking your files :)`
695+
`You can initialize git by running:\n${pc.bold("git init")}, ${pc.bold("git add .")} and ${pc.bold("git commit")} to start tracking your files :)`,
679696
);
680697
log.info(`Then run ${pc.blazityOrange(pc.bold("npx nefi"))} again!`);
681698
outro("See you later fellow developer o/");
@@ -684,10 +701,10 @@ export async function agentCommand({
684701

685702
if (!isClean) {
686703
log.warn(
687-
"Your git working tree has uncommitted changes. Please commit or stash your changes before using nefi."
704+
"Your git working tree has uncommitted changes. Please commit or stash your changes before using nefi.",
688705
);
689706
log.info(
690-
`You can use ${pc.bold("git stash")} or ${pc.bold("git commit")} and then run ${pc.blazityOrange(pc.bold("npx nefi"))} again!`
707+
`You can use ${pc.bold("git stash")} or ${pc.bold("git commit")} and then run ${pc.blazityOrange(pc.bold("npx nefi"))} again!`,
691708
);
692709
outro("See you later fellow developer o/");
693710
return false;

apps/nefi/src/commands/agent/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ export class AgentCommand extends Command {
88
examples: [["Analyze scripts and execute them in optimal order", "agent"]],
99
});
1010

11+
force = Option.Boolean("--force-write", false, {
12+
description: "Force overwrite of the dirty working tree"
13+
})
14+
1115
usage = Option.Boolean("--usage", false, {
1216
description: "Print the usage of LLM calls",
1317
});
@@ -23,6 +27,7 @@ export class AgentCommand extends Command {
2327
clipanionContext: {
2428
usage: this.usage,
2529
verbose: this.verbose,
30+
force: this.force,
2631
},
2732
});
2833
}

apps/nefi/src/scripts-registry.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,27 @@ export const scriptHandlers: Record<string, ScriptHandler> = {
5555
requirements: {
5656
requiredFilesByPath: [projectFilePath("package.json")],
5757
},
58-
execute: async ({ userRequest, files, detailedLogger }) => {
58+
execute: async ({
59+
userRequest,
60+
executionStepDescription,
61+
files,
62+
detailedLogger,
63+
}) => {
64+
const packageJsonContent = files[projectFilePath("package.json")];
65+
5966
const { operations } = await generatePackageOperations({
6067
userRequest,
61-
packageJsonContent: JSON.stringify(
62-
files[projectFilePath("package.json")]
63-
),
68+
executionStepDescription,
69+
packageJsonContent,
6470
detailedLogger,
6571
});
6672

6773
if (await validatePackageOperations({ operations, detailedLogger })) {
68-
await executePackageOperations({ operations, detailedLogger });
74+
await executePackageOperations({
75+
operations,
76+
detailedLogger,
77+
packageJsonContent,
78+
});
6979
}
7080
},
7181
},

apps/nefi/src/scripts/file-modifier.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ export async function executeProjectFilesAnalysis({
208208
209209
For the files, refer to the <files> section for paths and contents.
210210
211+
The most important rule is that you MUST NOT include package.json for dependency changes (removal or creation), only for scripts field changes.
212+
211213
${xml.build({
212214
rules: {
213215
rule: [

0 commit comments

Comments
 (0)