ucode-agent 1.41.0 → 1.41.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/core/loop.js +49 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ucode-agent",
3
- "version": "1.41.0",
3
+ "version": "1.41.1",
4
4
  "description": "ucode - a terminal coding agent that reads, edits and runs your code, on NVIDIA and Cohere models.",
5
5
  "type": "module",
6
6
  "main": "ucode.js",
package/src/core/loop.js CHANGED
@@ -1105,6 +1105,7 @@ export class Agent {
1105
1105
  this.lookedThisTurn = false;
1106
1106
  this.reads = new Map();
1107
1107
  this.declines = 0;
1108
+ this.apps = [];
1108
1109
  forgetReviews(); // a new request: its apps get a fresh design review
1109
1110
  const images = await this.attachImages(input);
1110
1111
  this.push(images.length
@@ -1682,6 +1683,13 @@ export class Agent {
1682
1683
  // a designer's review, and now the app actually driven — and it was the
1683
1684
  // quietest line on screen, saying only that it had happened. Its verdict
1684
1685
  // goes on the same line, the way a change carries its two numbers.
1686
+ // Which app folders this turn actually made, so a second one can be
1687
+ // refused before it is built and any leftovers can be counted at the end.
1688
+ if (call.name === 'create_app' && call.args?.folder) {
1689
+ const made = path.resolve(this.cwd, String(call.args.folder));
1690
+ if (!(this.apps ??= []).includes(made)) this.apps.push(made);
1691
+ }
1692
+
1685
1693
  if (call.name === 'look_at_app') {
1686
1694
  const found = /^(\d+) problem/.exec(out.summary ?? '');
1687
1695
  this.ui.runStat?.(found ? `${found[1]} to fix` : 'clean');
@@ -1784,6 +1792,30 @@ export class Agent {
1784
1792
  this.reads.set(key, seen + 1);
1785
1793
  }
1786
1794
 
1795
+ // Starting a second app instead of fixing the first.
1796
+ //
1797
+ // A traced build hit a problem in todo/, abandoned it and made todo-fixed/
1798
+ // — three create_app calls, two folders, one broken, the app typed twice.
1799
+ // Starting over is never the cheap way out of a problem in a file, and it
1800
+ // leaves the user to work out which folder is the real one.
1801
+ if (call.name === 'create_app' && call.args?.folder && (this.apps ?? []).length) {
1802
+ const wanted = path.resolve(this.cwd, String(call.args.folder));
1803
+ const already = this.apps.filter((f) => f !== wanted);
1804
+ if (already.length && !this.apps.includes(wanted)) {
1805
+ const show = already.map((f) => path.basename(f)).join(', ');
1806
+ throw new ToolFailure({
1807
+ kind: 'already_building',
1808
+ attempted: `creating ${call.args.folder}`,
1809
+ failed: `You already made ${show} this turn, and it is still there.`,
1810
+ fix:
1811
+ `Fix ${show} instead of starting again — whatever is wrong with it is a smaller `
1812
+ + 'job than writing the whole app a second time, and a half-finished folder left '
1813
+ + `beside the real one is worse than either. If ${show} genuinely cannot be saved, `
1814
+ + 'delete it first, then create this one.',
1815
+ });
1816
+ }
1817
+ }
1818
+
1787
1819
  if (call.name === 'load_skill') return this.loadSkill(call.args?.name);
1788
1820
  if (call.name === 'update_plan') return this.updatePlan(call.args?.items);
1789
1821
  if (call.name === 'delegate') return this.delegate(call.args?.tasks);
@@ -2082,6 +2114,23 @@ export class Agent {
2082
2114
  const live = await this.liveErrors();
2083
2115
  if (live) problems.push(live);
2084
2116
 
2117
+ // Two app folders, one of them abandoned.
2118
+ //
2119
+ // A build hit a problem in todo/, started todo-fixed/, then said in its
2120
+ // reply that it had removed the duplicate — and had not. Both were still
2121
+ // on disk for the user to sort out, and the claim that they were not is
2122
+ // the failure this whole checking pass exists to catch: saying a thing is
2123
+ // done when it is not. So it is checked rather than believed.
2124
+ const apps = this.apps ?? [];
2125
+ if (apps.length > 1) {
2126
+ const names = apps.map((f) => path.basename(f));
2127
+ problems.push(
2128
+ `There are ${apps.length} app folders here now: ${names.join(', ')}. Only one of them is `
2129
+ + 'the app. Delete the ones you are not shipping — actually delete them, do not just say '
2130
+ + 'you have — and make sure the one you keep is the one that works.',
2131
+ );
2132
+ }
2133
+
2085
2134
  // Then look at it, in the same pass that type-checks — not when the model
2086
2135
  // remembers to. A check that runs only when it is asked for reports
2087
2136
  // nothing on exactly the builds that needed it, and this is the only one