ucode-agent 1.50.0 → 1.51.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ucode-agent",
3
- "version": "1.50.0",
3
+ "version": "1.51.0",
4
4
  "description": "ucode - a terminal coding agent that reads, edits and runs your code, on NVIDIA, Cohere, Nex AGI, DeepSeek and Qwen models.",
5
5
  "type": "module",
6
6
  "main": "ucode.js",
package/src/core/loop.js CHANGED
@@ -1911,6 +1911,31 @@ export class Agent {
1911
1911
  this.reads.set(key, seen + 1);
1912
1912
  }
1913
1913
 
1914
+ // create_app again for the app this turn already made. DeepSeek scaffolded
1915
+ // tasker with no files, then called create_app twice more — once with no
1916
+ // folder or name at all — and was refused both times while the empty
1917
+ // starter sat there. The folder and name are known, so fill them in; with
1918
+ // no files there is nothing to create, and the next step is batch_write.
1919
+ if (call.name === 'create_app' && this.apps?.length === 1) {
1920
+ const made = this.apps[0];
1921
+ const args = (call.args ??= {});
1922
+ if (!args.folder) args.folder = path.relative(this.cwd, made) || '.';
1923
+ if (!args.name) args.name = path.basename(made);
1924
+ const files = args.files;
1925
+ const none = files == null || files === ''
1926
+ || (Array.isArray(files) ? !files.length : typeof files === 'object' && !Object.keys(files).length);
1927
+ if (none && path.resolve(this.cwd, String(args.folder)) === made) {
1928
+ const show = path.relative(this.cwd, made) || '.';
1929
+ throw new ToolFailure({
1930
+ kind: 'already_made',
1931
+ attempted: `creating ${show}`,
1932
+ failed: `${show} was already created this turn, with the starter files in it.`,
1933
+ fix: `Do not call create_app again. Write the app into ${show} with one batch_write, `
1934
+ + 'replacing the starter files with the finished ones.',
1935
+ });
1936
+ }
1937
+ }
1938
+
1914
1939
  // Starting a second app instead of fixing the first.
1915
1940
  //
1916
1941
  // A traced build hit a problem in todo/, abandoned it and made todo-fixed/
@@ -581,6 +581,18 @@ function check(name, args) {
581
581
  return ['the arguments must be a JSON object'];
582
582
  }
583
583
 
584
+ // One file named as "path" where the tool takes a list called "paths".
585
+ // DeepSeek did it to four read_files at once, a whole round trip spent being
586
+ // told a plural it could simply have been given.
587
+ for (const key of schema.required ?? []) {
588
+ const one = key.endsWith('s') ? key.slice(0, -1) : '';
589
+ if (args[key] == null && one && !schema.properties[one] && args[one] != null
590
+ && schema.properties[key]?.type === 'array') {
591
+ args[key] = Array.isArray(args[one]) ? args[one] : [args[one]];
592
+ delete args[one];
593
+ }
594
+ }
595
+
584
596
  for (const key of schema.required ?? []) {
585
597
  if (args[key] === undefined || args[key] === null) problems.push(`"${key}" is required and missing`);
586
598
  }
@@ -709,7 +721,7 @@ export function describe(name, args = {}) {
709
721
  // untrue on screen while the opposite happens on disk.
710
722
  return `Creating ${clip(args.name || args.folder, 30)} from the ` +
711
723
  `${args.template === 'next-shadcn' ? 'Next.js' : 'HTML'} starter` +
712
- `${files ? ` with ${files} file${files === 1 ? '' : 's'}` : ''}`;
724
+ `${files ? ` with ${files} file${files === 1 ? '' : 's'}` : ''}`;
713
725
  }
714
726
  case 'look_at_app':
715
727
  return `Looking at ${clip(args.url, 40)} on a phone and a desktop`;