@polka-codes/cli 0.9.58 → 0.9.60
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/index.js +95 -41
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35579,7 +35579,7 @@ var {
|
|
|
35579
35579
|
Help
|
|
35580
35580
|
} = import__.default;
|
|
35581
35581
|
// package.json
|
|
35582
|
-
var version = "0.9.
|
|
35582
|
+
var version = "0.9.60";
|
|
35583
35583
|
|
|
35584
35584
|
// src/commands/code.ts
|
|
35585
35585
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
@@ -48181,7 +48181,8 @@ config(en_default());
|
|
|
48181
48181
|
var providerModelSchema = exports_external.object({
|
|
48182
48182
|
provider: exports_external.string().optional(),
|
|
48183
48183
|
model: exports_external.string().optional(),
|
|
48184
|
-
parameters: exports_external.record(exports_external.string(), exports_external.any()).optional()
|
|
48184
|
+
parameters: exports_external.record(exports_external.string(), exports_external.any()).optional(),
|
|
48185
|
+
budget: exports_external.number().positive().optional()
|
|
48185
48186
|
});
|
|
48186
48187
|
var configSchema = exports_external.object({
|
|
48187
48188
|
prices: exports_external.record(exports_external.string(), exports_external.record(exports_external.string(), exports_external.object({
|
|
@@ -48210,9 +48211,7 @@ var configSchema = exports_external.object({
|
|
|
48210
48211
|
command: exports_external.string(),
|
|
48211
48212
|
description: exports_external.string()
|
|
48212
48213
|
}))).optional(),
|
|
48213
|
-
commands: exports_external.
|
|
48214
|
-
default: providerModelSchema.optional()
|
|
48215
|
-
}).catchall(providerModelSchema).optional(),
|
|
48214
|
+
commands: exports_external.record(exports_external.string(), providerModelSchema).optional(),
|
|
48216
48215
|
rules: exports_external.array(exports_external.string()).optional().or(exports_external.string()).optional(),
|
|
48217
48216
|
excludeFiles: exports_external.array(exports_external.string()).optional()
|
|
48218
48217
|
}).strict();
|
|
@@ -77708,7 +77707,9 @@ class ApiProviderConfig {
|
|
|
77708
77707
|
this.commands = config4.commands;
|
|
77709
77708
|
}
|
|
77710
77709
|
getConfigForCommand(command) {
|
|
77711
|
-
const
|
|
77710
|
+
const commandConfig = this.commands?.[command];
|
|
77711
|
+
const defaultConfig = this.commands?.default;
|
|
77712
|
+
const { provider: provider3, model, parameters, budget } = { ...defaultConfig, ...commandConfig };
|
|
77712
77713
|
const finalProvider = provider3 ?? this.defaultProvider;
|
|
77713
77714
|
if (!finalProvider) {
|
|
77714
77715
|
return;
|
|
@@ -77728,7 +77729,8 @@ class ApiProviderConfig {
|
|
|
77728
77729
|
project,
|
|
77729
77730
|
keyFile,
|
|
77730
77731
|
baseUrl,
|
|
77731
|
-
parameters: finalParameters
|
|
77732
|
+
parameters: finalParameters,
|
|
77733
|
+
budget
|
|
77732
77734
|
};
|
|
77733
77735
|
}
|
|
77734
77736
|
}
|
|
@@ -77737,7 +77739,7 @@ class ApiProviderConfig {
|
|
|
77737
77739
|
function addSharedOptions(command) {
|
|
77738
77740
|
return command.option("-c --config <paths>", "Path to config file(s)", (value, prev) => prev.concat(value), []).option("--api-provider <provider>", "API provider").option("--model <model>", "Model ID").option("--api-key <key>", "API key").option("--max-messages <iterations>", "Maximum number of messages to send.", Number.parseInt).option("--budget <budget>", "Budget for the AI service.", Number.parseFloat).option("-v --verbose", "Enable verbose output. Use -v for level 1, -vv for level 2", (_value, prev) => (prev ?? 0) + 1).option("-d --base-dir <path>", "Base directory to run commands in").option("--file <path...>", "File to include in the task").option("--silent", "Enable silent mode").option("-y, --yes", "Skip interactive prompts", false);
|
|
77739
77741
|
}
|
|
77740
|
-
function parseOptions(options, { cwdArg } = {}, home = os2.homedir(), env2 = getEnv()) {
|
|
77742
|
+
function parseOptions(options, { cwdArg, commandName } = {}, home = os2.homedir(), env2 = getEnv()) {
|
|
77741
77743
|
let cwd = cwdArg;
|
|
77742
77744
|
if (options.baseDir) {
|
|
77743
77745
|
process.chdir(options.baseDir);
|
|
@@ -77773,13 +77775,18 @@ function parseOptions(options, { cwdArg } = {}, home = os2.homedir(), env2 = get
|
|
|
77773
77775
|
defaultProvider,
|
|
77774
77776
|
...config4
|
|
77775
77777
|
});
|
|
77778
|
+
const commandConfig = commandName ? providerConfig.getConfigForCommand(commandName) : undefined;
|
|
77776
77779
|
const verbose = options.silent ? -1 : options.verbose ?? 0;
|
|
77780
|
+
const commandSpecificConfig = commandName ? config4.commands?.[commandName] : undefined;
|
|
77781
|
+
const defaultCommandConfig = config4.commands?.default;
|
|
77782
|
+
const mergedCommandConfig = { ...defaultCommandConfig, ...commandSpecificConfig };
|
|
77777
77783
|
return {
|
|
77778
77784
|
maxMessageCount: options.maxMessageCount ?? config4.maxMessageCount ?? 100,
|
|
77779
|
-
budget: options.budget ?? (env2.POLKA_BUDGET ? Number.parseFloat(env2.POLKA_BUDGET) : undefined) ?? config4.budget ?? 10,
|
|
77785
|
+
budget: options.budget ?? mergedCommandConfig.budget ?? (env2.POLKA_BUDGET ? Number.parseFloat(env2.POLKA_BUDGET) : undefined) ?? config4.budget ?? 10,
|
|
77780
77786
|
verbose,
|
|
77781
77787
|
config: config4,
|
|
77782
77788
|
providerConfig,
|
|
77789
|
+
commandConfig,
|
|
77783
77790
|
file: options.file
|
|
77784
77791
|
};
|
|
77785
77792
|
}
|
|
@@ -78531,6 +78538,8 @@ async function generateText(input2, context) {
|
|
|
78531
78538
|
for (let i = 0;i < retryCount; i++) {
|
|
78532
78539
|
const abortController = new AbortController;
|
|
78533
78540
|
let timeout = setTimeout(() => abortController.abort(), requestTimeoutSeconds * 1000);
|
|
78541
|
+
const lastOutputs = [];
|
|
78542
|
+
let repetitionDetected = false;
|
|
78534
78543
|
const usageMeterOnFinishHandler = context.parameters.usageMeter.onFinishHandler(model);
|
|
78535
78544
|
try {
|
|
78536
78545
|
const stream = streamText({
|
|
@@ -78543,6 +78552,20 @@ async function generateText(input2, context) {
|
|
|
78543
78552
|
timeout = setTimeout(() => abortController.abort(), requestTimeoutSeconds * 1000);
|
|
78544
78553
|
switch (chunk.type) {
|
|
78545
78554
|
case "text-delta":
|
|
78555
|
+
lastOutputs.push(chunk.text);
|
|
78556
|
+
if (lastOutputs.length > 20) {
|
|
78557
|
+
lastOutputs.shift();
|
|
78558
|
+
}
|
|
78559
|
+
if (lastOutputs.length === 20) {
|
|
78560
|
+
const firstHalf = lastOutputs.slice(0, 10).join("");
|
|
78561
|
+
const secondHalf = lastOutputs.slice(10).join("");
|
|
78562
|
+
if (firstHalf === secondHalf) {
|
|
78563
|
+
if (firstHalf.length > 20) {
|
|
78564
|
+
repetitionDetected = true;
|
|
78565
|
+
abortController.abort();
|
|
78566
|
+
}
|
|
78567
|
+
}
|
|
78568
|
+
}
|
|
78546
78569
|
agentCallback?.({
|
|
78547
78570
|
kind: "Text" /* Text */,
|
|
78548
78571
|
newText: chunk.text
|
|
@@ -78571,6 +78594,11 @@ async function generateText(input2, context) {
|
|
|
78571
78594
|
return resp.messages;
|
|
78572
78595
|
} catch (error46) {
|
|
78573
78596
|
if (error46.name === "AbortError") {
|
|
78597
|
+
if (repetitionDetected) {
|
|
78598
|
+
console.warn("Repetition detected, retrying...");
|
|
78599
|
+
continue;
|
|
78600
|
+
}
|
|
78601
|
+
console.warn(`Request timed out after ${requestTimeoutSeconds} seconds, retrying...`);
|
|
78574
78602
|
continue;
|
|
78575
78603
|
}
|
|
78576
78604
|
if ("response" in error46) {
|
|
@@ -80233,7 +80261,7 @@ async function runCode(task, _options, command) {
|
|
|
80233
80261
|
if (stdin) {
|
|
80234
80262
|
taskInput = stdin;
|
|
80235
80263
|
}
|
|
80236
|
-
const { file: files } = parseOptions(command.opts());
|
|
80264
|
+
const { file: files } = parseOptions(command.opts(), { commandName: "code" });
|
|
80237
80265
|
const fileContents = [];
|
|
80238
80266
|
if (files) {
|
|
80239
80267
|
for (const file2 of files) {
|
|
@@ -80372,7 +80400,7 @@ ${input2.context}
|
|
|
80372
80400
|
Commit message:
|
|
80373
80401
|
${commitMessage}`);
|
|
80374
80402
|
await tools2.createCommit({ message: commitMessage });
|
|
80375
|
-
return;
|
|
80403
|
+
return commitMessage;
|
|
80376
80404
|
}
|
|
80377
80405
|
}
|
|
80378
80406
|
context.logger.warn("Failed to generate commit message.", result);
|
|
@@ -80653,7 +80681,7 @@ ${defaultContext}`;
|
|
|
80653
80681
|
outputSchema: EpicPlanSchema
|
|
80654
80682
|
}, context);
|
|
80655
80683
|
}
|
|
80656
|
-
async function createAndApprovePlan(task, context) {
|
|
80684
|
+
async function createAndApprovePlan(task, context, saveUsageSnapshot) {
|
|
80657
80685
|
const { logger, step, tools: tools2 } = context;
|
|
80658
80686
|
logger.info(`Phase 2: Creating high-level plan...
|
|
80659
80687
|
`);
|
|
@@ -80683,6 +80711,7 @@ ${result.plan}`);
|
|
|
80683
80711
|
if (feedback.trim() === "") {
|
|
80684
80712
|
logger.info(`High-level plan approved.
|
|
80685
80713
|
`);
|
|
80714
|
+
await saveUsageSnapshot();
|
|
80686
80715
|
return { plan: result.plan, branchName: result.branchName };
|
|
80687
80716
|
}
|
|
80688
80717
|
break;
|
|
@@ -80820,13 +80849,14 @@ async function runPreflightChecks(epicContext, context) {
|
|
|
80820
80849
|
}
|
|
80821
80850
|
async function performReviewAndFixCycle(iterationCount, taskItem, highLevelPlan, context) {
|
|
80822
80851
|
const { logger, step, tools: tools2 } = context;
|
|
80852
|
+
const commitMessages = [];
|
|
80823
80853
|
for (let i = 0;i < MAX_REVIEW_RETRIES; i++) {
|
|
80824
80854
|
const diffResult = await tools2.executeCommand({ command: "git", args: ["diff", "--name-status", "HEAD~1", "HEAD"] });
|
|
80825
80855
|
const changedFiles = parseGitDiffNameStatus(diffResult.stdout).filter(({ path }) => path !== ".epic.yml");
|
|
80826
80856
|
if (changedFiles.length === 0) {
|
|
80827
80857
|
logger.info(`No files were changed. Skipping review.
|
|
80828
80858
|
`);
|
|
80829
|
-
return { passed: true };
|
|
80859
|
+
return { passed: true, commitMessages: [] };
|
|
80830
80860
|
}
|
|
80831
80861
|
logger.info(`
|
|
80832
80862
|
Review iteration ${i + 1}/${MAX_REVIEW_RETRIES}`);
|
|
@@ -80857,7 +80887,7 @@ ${formatReviewToolInput(changeInfo)}`;
|
|
|
80857
80887
|
if (!reviewResult || !reviewResult.specificReviews || reviewResult.specificReviews.length === 0) {
|
|
80858
80888
|
logger.info(`Review passed. No issues found.
|
|
80859
80889
|
`);
|
|
80860
|
-
return { passed: true };
|
|
80890
|
+
return { passed: true, commitMessages };
|
|
80861
80891
|
}
|
|
80862
80892
|
logger.warn(`Warning: Review found ${reviewResult.specificReviews.length} issue(s). Attempting to fix...
|
|
80863
80893
|
`);
|
|
@@ -80888,17 +80918,22 @@ ${reviewSummary}`;
|
|
|
80888
80918
|
}, context);
|
|
80889
80919
|
});
|
|
80890
80920
|
await step(`commit-fix-${iterationCount}-${i}`, async () => {
|
|
80891
|
-
await
|
|
80892
|
-
|
|
80921
|
+
const commitMessage = await commitWorkflow({
|
|
80922
|
+
all: true,
|
|
80923
|
+
context: reviewSummary
|
|
80924
|
+
}, context);
|
|
80925
|
+
if (commitMessage) {
|
|
80926
|
+
commitMessages.push(commitMessage);
|
|
80927
|
+
}
|
|
80893
80928
|
});
|
|
80894
80929
|
if (i === MAX_REVIEW_RETRIES - 1) {
|
|
80895
80930
|
logger.error(`
|
|
80896
80931
|
Max retries (${MAX_REVIEW_RETRIES}) reached. Moving to the next task, but issues might remain.
|
|
80897
80932
|
`);
|
|
80898
|
-
return { passed: false };
|
|
80933
|
+
return { passed: false, commitMessages };
|
|
80899
80934
|
}
|
|
80900
80935
|
}
|
|
80901
|
-
return { passed: false };
|
|
80936
|
+
return { passed: false, commitMessages };
|
|
80902
80937
|
}
|
|
80903
80938
|
async function findNextTask(tools2) {
|
|
80904
80939
|
const openRootTasks = await tools2.listTodoItems({ status: "open" });
|
|
@@ -80914,7 +80949,7 @@ async function findNextTask(tools2) {
|
|
|
80914
80949
|
currentTask = subTasks[0];
|
|
80915
80950
|
}
|
|
80916
80951
|
}
|
|
80917
|
-
async function runImplementationLoop(context, highLevelPlan) {
|
|
80952
|
+
async function runImplementationLoop(context, highLevelPlan, saveUsageSnapshot) {
|
|
80918
80953
|
const { logger, step, tools: tools2 } = context;
|
|
80919
80954
|
const commitMessages = [];
|
|
80920
80955
|
logger.info(`Phase 5: Iterative Implementation Loop...
|
|
@@ -80957,7 +80992,8 @@ Focus only on this item, but use the plan for context.`;
|
|
|
80957
80992
|
await tools2.executeCommand({ command: "git", args: ["commit", "-m", commitMessage] });
|
|
80958
80993
|
});
|
|
80959
80994
|
commitMessages.push(commitMessage);
|
|
80960
|
-
const { passed: reviewPassed } = await performReviewAndFixCycle(iterationCount, nextTask, highLevelPlan, context);
|
|
80995
|
+
const { passed: reviewPassed, commitMessages: fixCommitMessages } = await performReviewAndFixCycle(iterationCount, nextTask, highLevelPlan, context);
|
|
80996
|
+
commitMessages.push(...fixCommitMessages);
|
|
80961
80997
|
const taskElapsed = Date.now() - taskStartTime;
|
|
80962
80998
|
const taskElapsedTime = formatElapsedTime(taskElapsed);
|
|
80963
80999
|
if (reviewPassed) {
|
|
@@ -80980,6 +81016,7 @@ Focus only on this item, but use the plan for context.`;
|
|
|
80980
81016
|
}
|
|
80981
81017
|
logger.info(`
|
|
80982
81018
|
Progress: ${progressMessage}`);
|
|
81019
|
+
await saveUsageSnapshot();
|
|
80983
81020
|
if (!nextTaskItem) {
|
|
80984
81021
|
logger.info(`All tasks complete!
|
|
80985
81022
|
`);
|
|
@@ -81091,7 +81128,7 @@ Max retries (${MAX_REVIEW_RETRIES}) reached for final review. Issues might remai
|
|
|
81091
81128
|
}
|
|
81092
81129
|
var epicWorkflow = async (input2, context) => {
|
|
81093
81130
|
const { logger, tools: tools2 } = context;
|
|
81094
|
-
const { task, saveEpicContext } = input2;
|
|
81131
|
+
const { task, saveEpicContext, saveUsageSnapshot } = input2;
|
|
81095
81132
|
const workflowStartTime = Date.now();
|
|
81096
81133
|
if (!task || task.trim() === "") {
|
|
81097
81134
|
logger.error("Error: Task cannot be empty. Please provide a valid task description.");
|
|
@@ -81110,7 +81147,7 @@ var epicWorkflow = async (input2, context) => {
|
|
|
81110
81147
|
logger.error("Error: Task is missing in epic context. Exiting.");
|
|
81111
81148
|
return;
|
|
81112
81149
|
}
|
|
81113
|
-
const planResult = await createAndApprovePlan(input2.task, context);
|
|
81150
|
+
const planResult = await createAndApprovePlan(input2.task, context, saveUsageSnapshot);
|
|
81114
81151
|
if (!planResult)
|
|
81115
81152
|
return;
|
|
81116
81153
|
input2.plan = planResult.plan;
|
|
@@ -81136,8 +81173,9 @@ var epicWorkflow = async (input2, context) => {
|
|
|
81136
81173
|
if (todos.length === 0) {
|
|
81137
81174
|
await addTodoItemsFromPlan(input2.plan, context);
|
|
81138
81175
|
}
|
|
81139
|
-
const commitMessages = await runImplementationLoop(context, input2.plan);
|
|
81176
|
+
const commitMessages = await runImplementationLoop(context, input2.plan, saveUsageSnapshot);
|
|
81140
81177
|
await performFinalReviewAndFix(context, input2.plan, input2.baseBranch ?? undefined);
|
|
81178
|
+
await saveUsageSnapshot();
|
|
81141
81179
|
await tools2.executeCommand({ command: "git", args: ["rm", "-f", ".epic.yml"] });
|
|
81142
81180
|
const statusResult = await tools2.executeCommand({
|
|
81143
81181
|
command: "git",
|
|
@@ -81596,26 +81634,26 @@ async function runEpic(task2, _options, command) {
|
|
|
81596
81634
|
}
|
|
81597
81635
|
epicContext.task = taskInput;
|
|
81598
81636
|
}
|
|
81599
|
-
let
|
|
81637
|
+
let usageMeter;
|
|
81638
|
+
const saveUsageSnapshot = async () => {
|
|
81639
|
+
if (usageMeter) {
|
|
81640
|
+
const currentUsage = usageMeter.usage;
|
|
81641
|
+
if (!epicContext.usages) {
|
|
81642
|
+
epicContext.usages = [];
|
|
81643
|
+
}
|
|
81644
|
+
epicContext.usages.push({ ...currentUsage, timestamp: Date.now() });
|
|
81645
|
+
}
|
|
81646
|
+
};
|
|
81600
81647
|
const workflowInput = {
|
|
81601
81648
|
...epicContext,
|
|
81602
81649
|
async saveEpicContext(context) {
|
|
81603
|
-
|
|
81604
|
-
|
|
81605
|
-
|
|
81606
|
-
|
|
81607
|
-
|
|
81608
|
-
|
|
81609
|
-
context.usages.push({ ...currentUsage, timestamp: Date.now() });
|
|
81610
|
-
usageAppended = true;
|
|
81611
|
-
} else {
|
|
81612
|
-
context.usages[context.usages.length - 1] = { ...currentUsage, timestamp: Date.now() };
|
|
81613
|
-
}
|
|
81614
|
-
}
|
|
81615
|
-
await saveEpicContext(context);
|
|
81616
|
-
}
|
|
81650
|
+
await saveEpicContext({
|
|
81651
|
+
...epicContext,
|
|
81652
|
+
...context
|
|
81653
|
+
});
|
|
81654
|
+
},
|
|
81655
|
+
saveUsageSnapshot
|
|
81617
81656
|
};
|
|
81618
|
-
let usageMeter;
|
|
81619
81657
|
await runWorkflow(epicWorkflow, workflowInput, {
|
|
81620
81658
|
commandName: "epic",
|
|
81621
81659
|
command,
|
|
@@ -81688,9 +81726,22 @@ async function runMeta(task2, command) {
|
|
|
81688
81726
|
}
|
|
81689
81727
|
epicContext.task = taskInput;
|
|
81690
81728
|
}
|
|
81729
|
+
let usageMeter;
|
|
81730
|
+
const saveUsageSnapshot = async () => {
|
|
81731
|
+
if (usageMeter) {
|
|
81732
|
+
const currentUsage = usageMeter.usage;
|
|
81733
|
+
if (!epicContext.usages) {
|
|
81734
|
+
epicContext.usages = [];
|
|
81735
|
+
}
|
|
81736
|
+
epicContext.usages.push({ ...currentUsage, timestamp: Date.now() });
|
|
81737
|
+
}
|
|
81738
|
+
};
|
|
81691
81739
|
const workflowInput = {
|
|
81692
81740
|
...epicContext,
|
|
81693
|
-
saveEpicContext
|
|
81741
|
+
async saveEpicContext(context) {
|
|
81742
|
+
await saveEpicContext(context);
|
|
81743
|
+
},
|
|
81744
|
+
saveUsageSnapshot
|
|
81694
81745
|
};
|
|
81695
81746
|
await runWorkflow(metaWorkflow, workflowInput, {
|
|
81696
81747
|
commandName: "meta",
|
|
@@ -81701,7 +81752,10 @@ async function runMeta(task2, command) {
|
|
|
81701
81752
|
...opt,
|
|
81702
81753
|
todoItemStore: new EpicTodoItemStore(workflowInput),
|
|
81703
81754
|
memoryStore: new EpicMemoryStore(workflowInput)
|
|
81704
|
-
})
|
|
81755
|
+
}),
|
|
81756
|
+
onUsageMeterCreated: (meter) => {
|
|
81757
|
+
usageMeter = meter;
|
|
81758
|
+
}
|
|
81705
81759
|
});
|
|
81706
81760
|
}
|
|
81707
81761
|
|