@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.
Files changed (2) hide show
  1. package/dist/index.js +95 -41
  2. 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.58";
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.object({
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 { provider: provider3, model, parameters } = this.commands?.[command] ?? this.commands?.default ?? {};
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 tools2.executeCommand({ command: "git", args: ["add", "."] });
80892
- await tools2.executeCommand({ command: "git", args: ["commit", "--amend", "--no-edit"] });
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 usageAppended = false;
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
- if (usageMeter) {
81604
- const currentUsage = usageMeter.usage;
81605
- if (!context.usages) {
81606
- context.usages = [];
81607
- }
81608
- if (!usageAppended) {
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polka-codes/cli",
3
- "version": "0.9.58",
3
+ "version": "0.9.60",
4
4
  "license": "AGPL-3.0",
5
5
  "author": "github@polka.codes",
6
6
  "type": "module",