ucode-agent 1.4.0 → 1.5.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.
Files changed (50) hide show
  1. package/README.md +11 -3
  2. package/package.json +3 -2
  3. package/skills/build-app/SKILL.md +19 -1
  4. package/src/core/loop.js +2 -0
  5. package/src/tools/index.js +24 -1
  6. package/src/tools/scaffold.js +130 -0
  7. package/templates/next-shadcn/AGENTS.md +9 -0
  8. package/templates/next-shadcn/README.md +9 -0
  9. package/templates/next-shadcn/TEMPLATE.md +39 -0
  10. package/templates/next-shadcn/_gitignore +41 -0
  11. package/templates/next-shadcn/_package-lock.json +10278 -0
  12. package/templates/next-shadcn/components.json +25 -0
  13. package/templates/next-shadcn/eslint.config.mjs +18 -0
  14. package/templates/next-shadcn/next-env.d.ts +5 -0
  15. package/templates/next-shadcn/next.config.ts +7 -0
  16. package/templates/next-shadcn/package.json +37 -0
  17. package/templates/next-shadcn/postcss.config.mjs +7 -0
  18. package/templates/next-shadcn/src/app/favicon.ico +0 -0
  19. package/templates/next-shadcn/src/app/globals.css +155 -0
  20. package/templates/next-shadcn/src/app/layout.tsx +42 -0
  21. package/templates/next-shadcn/src/app/page.tsx +14 -0
  22. package/templates/next-shadcn/src/components/theme-provider.tsx +7 -0
  23. package/templates/next-shadcn/src/components/theme-toggle.tsx +27 -0
  24. package/templates/next-shadcn/src/components/ui/alert-dialog.tsx +187 -0
  25. package/templates/next-shadcn/src/components/ui/avatar.tsx +108 -0
  26. package/templates/next-shadcn/src/components/ui/badge.tsx +51 -0
  27. package/templates/next-shadcn/src/components/ui/button.tsx +57 -0
  28. package/templates/next-shadcn/src/components/ui/calendar.tsx +221 -0
  29. package/templates/next-shadcn/src/components/ui/card.tsx +102 -0
  30. package/templates/next-shadcn/src/components/ui/checkbox.tsx +28 -0
  31. package/templates/next-shadcn/src/components/ui/command.tsx +196 -0
  32. package/templates/next-shadcn/src/components/ui/dialog.tsx +160 -0
  33. package/templates/next-shadcn/src/components/ui/dropdown-menu.tsx +267 -0
  34. package/templates/next-shadcn/src/components/ui/input-group.tsx +158 -0
  35. package/templates/next-shadcn/src/components/ui/input.tsx +19 -0
  36. package/templates/next-shadcn/src/components/ui/label.tsx +19 -0
  37. package/templates/next-shadcn/src/components/ui/popover.tsx +89 -0
  38. package/templates/next-shadcn/src/components/ui/progress.tsx +82 -0
  39. package/templates/next-shadcn/src/components/ui/scroll-area.tsx +54 -0
  40. package/templates/next-shadcn/src/components/ui/select.tsx +200 -0
  41. package/templates/next-shadcn/src/components/ui/separator.tsx +24 -0
  42. package/templates/next-shadcn/src/components/ui/sheet.tsx +138 -0
  43. package/templates/next-shadcn/src/components/ui/skeleton.tsx +13 -0
  44. package/templates/next-shadcn/src/components/ui/sonner.tsx +49 -0
  45. package/templates/next-shadcn/src/components/ui/switch.tsx +31 -0
  46. package/templates/next-shadcn/src/components/ui/tabs.tsx +81 -0
  47. package/templates/next-shadcn/src/components/ui/textarea.tsx +17 -0
  48. package/templates/next-shadcn/src/components/ui/tooltip.tsx +65 -0
  49. package/templates/next-shadcn/src/lib/utils.ts +1 -0
  50. package/templates/next-shadcn/tsconfig.json +34 -0
package/README.md CHANGED
@@ -111,12 +111,20 @@ chosen model gets another go a few minutes later.
111
111
 
112
112
  ## What it does
113
113
 
114
- **Fourteen tools.** `read_file`, `read_files`, `write_file`, `batch_write`,
115
- `edit_file`, `multi_edit`, `edit_files`, `list_dir`, `glob`, `grep`,
116
- `run_command`, `run_commands`, `look_at_app`, `web_search`. Read-only calls run in parallel,
114
+ **Fifteen tools.** `create_app`, `read_file`, `read_files`, `write_file`,
115
+ `batch_write`, `edit_file`, `multi_edit`, `edit_files`, `list_dir`, `glob`,
116
+ `grep`, `run_command`, `run_commands`, `look_at_app`, `web_search`. Read-only calls run in parallel,
117
117
  and start the moment the model finishes writing them — while the rest of its
118
118
  reply is still arriving. Anything that writes runs on its own, in order.
119
119
 
120
+ **Apps start from a ready-made starter.** Setting up Next.js and shadcn from
121
+ nothing takes about four minutes — `create-next-app` and the shadcn CLI measured
122
+ at 116s and 130s — plus a dozen model round trips. `create_app` copies ucode's
123
+ starter instead: Next.js 16, TypeScript, Tailwind 4, shadcn/ui with 25 common
124
+ components, light/dark mode, toasts and a considered theme, already known to
125
+ build. The copy takes under a second, and its install runs in the background
126
+ while the model writes the first components.
127
+
120
128
  **Parallel workers.** When a build splits into parts that touch different files
121
129
  — the API route, the upload component, the results view — the model hands them
122
130
  to up to three workers that build at the same time, each line in the transcript
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ucode-agent",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
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",
@@ -10,7 +10,8 @@
10
10
  "files": [
11
11
  "ucode.js",
12
12
  "src/",
13
- "skills/"
13
+ "skills/",
14
+ "templates/"
14
15
  ],
15
16
  "scripts": {
16
17
  "start": "node ucode.js",
@@ -31,7 +31,25 @@ If there is a user interface, the `ui-ux` skill is already loaded. Decide the
31
31
  design direction now, not after the logic works. If the app calls a model,
32
32
  load `ai-features`; if it has accounts, keys or uploads, load `security`.
33
33
 
34
- ## 2. Scaffold without being asked questions
34
+ ## 2. Start from the starter
35
+
36
+ **For a Next.js app, call `create_app`** — one step, about a second:
37
+
38
+ ```
39
+ create_app({ folder: "my-app", name: "My App", description: "…" })
40
+ ```
41
+
42
+ It copies ucode's ready-made starter — Next.js 16, TypeScript, Tailwind 4,
43
+ shadcn/ui with 25 common components, light/dark mode, toasts, a considered
44
+ theme — which is already known to build, and starts `npm install` in the
45
+ background. Read the `TEMPLATE.md` it lists, then start writing components
46
+ straight away; commands in that folder wait for the install on their own.
47
+ Re-tint the palette in `globals.css` and swap the font for the app's direction.
48
+
49
+ Never run `create-next-app` or `shadcn init` for a Next.js app — that is
50
+ four minutes and a dozen steps the starter already did.
51
+
52
+ ### Other stacks
35
53
 
36
54
  Nothing you run has a keyboard. A scaffolder that asks "Would you like to use
37
55
  TypeScript?" gets no answer and fails, so give it every answer up front:
package/src/core/loop.js CHANGED
@@ -240,6 +240,8 @@ function systemPrompt({ cwd, skills, mode, check, map, memory }) {
240
240
  ' one at a time when you already know which ones you want.',
241
241
  '- Put independent calls in the same message — several greps, a glob and a read.',
242
242
  ' Read-only calls in one message run at the same time.',
243
+ '- New Next.js app? create_app - one step, never create-next-app or shadcn init. It',
244
+ ' copies a starter that already builds and installs it in the background.',
243
245
  '- batch_write to lay out several new files at once, multi_edit for several changes',
244
246
  ' to one file, edit_files for a change that spans several files.',
245
247
  '- For work with three or more steps, keep a short plan with update_plan - at most',
@@ -9,6 +9,7 @@ import { listDir, glob, grep } from './search.js';
9
9
  import { runCommand, runCommands } from './shell.js';
10
10
  import { webSearch } from './web.js';
11
11
  import { lookAtApp } from './browser.js';
12
+ import { createApp } from './scaffold.js';
12
13
  import { clip, READ_LINES } from './shared.js';
13
14
 
14
15
  export { setRoot, setConfirm, getRoot } from './shared.js';
@@ -18,6 +19,25 @@ const int = (description) => ({ type: 'integer', description });
18
19
  const bool = (description) => ({ type: 'boolean', description });
19
20
 
20
21
  export const tools = [
22
+ {
23
+ name: 'create_app',
24
+ description:
25
+ 'Start a new Next.js + shadcn/ui app from the ready-made ucode starter. This is how every ' +
26
+ 'Next.js app begins - never run create-next-app or shadcn init. It copies a project that ' +
27
+ 'is already known to build (Next.js 16, TypeScript, Tailwind 4, shadcn with 25 common ' +
28
+ 'components, light/dark mode, toasts, a considered theme) into a new empty folder, and ' +
29
+ 'starts installing its packages in the background so you can write components at once. ' +
30
+ 'The result lists everything included.',
31
+ parameters: {
32
+ type: 'object',
33
+ properties: {
34
+ folder: str('A new, empty folder for the app, relative to the project root, e.g. "stride".'),
35
+ name: str('The display name of the app, e.g. "Stride".'),
36
+ description: str('One line about the app, used in the page metadata.'),
37
+ },
38
+ required: ['folder', 'name'],
39
+ },
40
+ },
21
41
  {
22
42
  name: 'read_file',
23
43
  description:
@@ -324,6 +344,7 @@ const run = {
324
344
  run_commands: runCommands,
325
345
  web_search: webSearch,
326
346
  look_at_app: lookAtApp,
347
+ create_app: createApp,
327
348
  };
328
349
 
329
350
  /** Tools that change the project or execute code. */
@@ -337,7 +358,7 @@ export const PARALLEL_SAFE = new Set(['read_file', 'read_files', 'list_dir', 'gl
337
358
  /** Tools withheld in plan mode. Withholding beats asking a model not to. */
338
359
  export const WRITES = new Set([
339
360
  'write_file', 'batch_write', 'edit_file', 'multi_edit', 'edit_files', 'run_command', 'run_commands',
340
- 'delegate',
361
+ 'delegate', 'create_app',
341
362
  ]);
342
363
 
343
364
  /** Tools that change files on disk, which parallel workers take turns at. */
@@ -460,6 +481,8 @@ export function describe(name, args = {}) {
460
481
  return `Running ${clip(args.command, 70)}${args.background ? ' in the background' : ''}`;
461
482
  case 'run_commands':
462
483
  return `Running ${args.commands?.length ?? 0} commands together`;
484
+ case 'create_app':
485
+ return `Creating ${clip(args.name || args.folder, 30)} from the Next.js starter`;
463
486
  case 'look_at_app':
464
487
  return `Looking at ${clip(args.url, 40)} on a phone and a desktop`;
465
488
  case 'web_search':
@@ -0,0 +1,130 @@
1
+ /**
2
+ * scaffold.js — starting an app from a starter that is known to work.
3
+ *
4
+ * Setting up a Next.js + shadcn project from nothing is four minutes of
5
+ * create-next-app and shadcn CLI runs — measured at 116s and 130s — plus a
6
+ * dozen model round trips to drive them and then theme the result. Every app
7
+ * starts from the same place anyway, so ucode ships that place: a project
8
+ * that has already been built and type-checked, copied in one step, with its
9
+ * install starting in the background while the model writes the first
10
+ * component.
11
+ */
12
+
13
+ import { promises as fs } from 'node:fs';
14
+ import path from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+ import { ToolFailure } from '../core/failure.js';
17
+ import { resolveIn, guard, result } from './shared.js';
18
+ import { packageJsonWritten } from './shell.js';
19
+
20
+ const TEMPLATES = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'templates');
21
+
22
+ /** npm silently drops these two names from published packages, so they ship renamed. */
23
+ const RENAME = { _gitignore: '.gitignore', '_package-lock.json': 'package-lock.json' };
24
+
25
+ /** Files the placeholders are filled into. Everything else is copied byte for byte. */
26
+ const TEXT = /\.(?:json|md|mjs|css|tsx?)$/i;
27
+
28
+ export const TEMPLATE_NAMES = ['next-shadcn'];
29
+
30
+ function slug(name) {
31
+ return String(name).toLowerCase().trim().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'app';
32
+ }
33
+
34
+ /** Text that is safe inside a JS string and a JSON string. */
35
+ const plain = (s) => String(s ?? '').replace(/["'`\\<>]/g, '').replace(/\s+/g, ' ').trim();
36
+
37
+ async function copyTree(from, to, fill) {
38
+ await fs.mkdir(to, { recursive: true });
39
+ const copied = [];
40
+ for (const entry of await fs.readdir(from, { withFileTypes: true })) {
41
+ const name = RENAME[entry.name] ?? entry.name;
42
+ const src = path.join(from, entry.name);
43
+ const dest = path.join(to, name);
44
+ if (entry.isDirectory()) {
45
+ copied.push(...(await copyTree(src, dest, fill)).map((f) => `${name}/${f}`));
46
+ } else if (TEXT.test(entry.name) || entry.name in RENAME) {
47
+ let text = await fs.readFile(src, 'utf8');
48
+ for (const [token, value] of Object.entries(fill)) text = text.split(token).join(value);
49
+ await fs.writeFile(dest, text, 'utf8');
50
+ copied.push(name);
51
+ } else {
52
+ await fs.copyFile(src, dest);
53
+ copied.push(name);
54
+ }
55
+ }
56
+ return copied;
57
+ }
58
+
59
+ /**
60
+ * @param {object} o
61
+ * @param {string} o.folder new, empty folder for the app
62
+ * @param {string} o.name display name, e.g. "Stride"
63
+ * @param {string} [o.description]
64
+ * @param {string} [o.template]
65
+ * @param {boolean} [o.install] start the background install (tests turn it off)
66
+ */
67
+ export async function createApp({ folder, name, description, template = 'next-shadcn', install = true }) {
68
+ if (!TEMPLATE_NAMES.includes(template)) {
69
+ throw new ToolFailure({
70
+ kind: 'bad_args',
71
+ attempted: 'creating an app',
72
+ failed: `There is no starter called "${template}".`,
73
+ fix: `Use one of: ${TEMPLATE_NAMES.join(', ')}.`,
74
+ });
75
+ }
76
+
77
+ const target = resolveIn(folder, 'create_app', 'folder');
78
+ const attempted = `creating an app in ${target.show}`;
79
+ if (target.show === '.') {
80
+ throw new ToolFailure({
81
+ kind: 'bad_args',
82
+ attempted,
83
+ failed: 'The app needs its own folder, not the project root.',
84
+ fix: 'Pass a new folder name, e.g. "stride".',
85
+ });
86
+ }
87
+ await guard(target, `create an app in ${target.abs}`);
88
+
89
+ let existing = [];
90
+ try {
91
+ existing = await fs.readdir(target.abs);
92
+ } catch {
93
+ existing = [];
94
+ }
95
+ if (existing.length) {
96
+ throw new ToolFailure({
97
+ kind: 'not_empty',
98
+ attempted,
99
+ failed: `${target.show} already has ${existing.length} item(s) in it: ${existing.slice(0, 5).join(', ')}.`,
100
+ fix: 'Pick a new folder name. If this folder is the app from an earlier attempt, work in it instead of creating it again.',
101
+ });
102
+ }
103
+
104
+ const display = plain(name) || path.basename(target.abs);
105
+ const fill = {
106
+ __APP_NAME__: display,
107
+ __APP_SLUG__: slug(display),
108
+ __APP_DESCRIPTION__: plain(description) || display,
109
+ };
110
+
111
+ const files = await copyTree(path.join(TEMPLATES, template), target.abs, fill);
112
+ await fs.mkdir(path.join(target.abs, 'public'), { recursive: true });
113
+
114
+ if (install) {
115
+ const pkg = path.join(target.abs, 'package.json');
116
+ packageJsonWritten(pkg, await fs.readFile(pkg, 'utf8'));
117
+ }
118
+
119
+ const guide = await fs.readFile(path.join(target.abs, 'TEMPLATE.md'), 'utf8').catch(() => '');
120
+
121
+ return result(
122
+ `Created ${target.show} from the ${template} starter — ${files.length} files, already known to build.\n` +
123
+ (install
124
+ ? `Its packages are installing in the background right now. Keep writing: any command you run in ` +
125
+ `${target.show} waits for that install first, so there is no need to run npm install.\n`
126
+ : '') +
127
+ `Run this app's commands with cwd: "${target.show}" (npm run build, npm run dev).\n\n${guide}`,
128
+ `${files.length} files${install ? ' · installing in the background' : ''}`
129
+ );
130
+ }
@@ -0,0 +1,9 @@
1
+ <!-- BEGIN:nextjs-agent-rules -->
2
+
3
+ # This is NOT the Next.js you know
4
+
5
+ This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
6
+
7
+ This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
8
+
9
+ <!-- END:nextjs-agent-rules -->
@@ -0,0 +1,9 @@
1
+ # __APP_NAME__
2
+
3
+ Built with ucode on Next.js and shadcn/ui.
4
+
5
+ ```bash
6
+ npm run dev
7
+ ```
8
+
9
+ Then open http://localhost:3000
@@ -0,0 +1,39 @@
1
+ # What this project already has
2
+
3
+ It was created by ucode's `create_app` from a starter that is known to build.
4
+ Do not re-run create-next-app or `shadcn init` — everything below is in place.
5
+
6
+ ## Stack
7
+
8
+ - Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS 4
9
+ - shadcn/ui ("base-nova" style, built on `@base-ui/react`), lucide-react icons
10
+ - next-themes (light / dark / system), sonner toasts, date-fns
11
+
12
+ Next 16 changed APIs. `AGENTS.md` explains, and `node_modules/next/dist/docs/`
13
+ has the guides — check them before using an API you are unsure of.
14
+
15
+ ## Already wired
16
+
17
+ - `src/app/layout.tsx` — font (Geist, registered as `--font-sans`), `ThemeProvider`,
18
+ `TooltipProvider`, and `<Toaster />`. Call `toast()` from `sonner` anywhere.
19
+ - `src/components/theme-toggle.tsx` — a light/dark button, ready to place.
20
+ - `src/app/globals.css` — the design tokens. The palette is a starting point:
21
+ re-tint `--primary`, `--accent` and the neutrals for this app's direction.
22
+ `--success` and `--warning` exist alongside `--destructive`
23
+ (`bg-success`, `text-warning`, ...).
24
+ - `src/app/page.tsx` — a placeholder. Replace it.
25
+
26
+ ## Components in `src/components/ui/`
27
+
28
+ alert-dialog, avatar, badge, button, calendar, card, checkbox, command, dialog,
29
+ dropdown-menu, input, input-group, label, popover, progress, scroll-area, select,
30
+ separator, sheet, skeleton, sonner, switch, tabs, textarea, tooltip
31
+
32
+ Anything else: `npx shadcn@latest add <name> -y`.
33
+
34
+ ## Conventions
35
+
36
+ - One component per file, grouped by feature: `src/components/<feature>/`.
37
+ - Shared types in `src/lib/types.ts`, helpers in `src/lib/`.
38
+ - `"use client"` only on components that need state or events.
39
+ - `cn()` from `@/lib/utils` to merge class names.
@@ -0,0 +1,41 @@
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.*
7
+ .yarn/*
8
+ !.yarn/patches
9
+ !.yarn/plugins
10
+ !.yarn/releases
11
+ !.yarn/versions
12
+
13
+ # testing
14
+ /coverage
15
+
16
+ # next.js
17
+ /.next/
18
+ /out/
19
+
20
+ # production
21
+ /build
22
+
23
+ # misc
24
+ .DS_Store
25
+ *.pem
26
+
27
+ # debug
28
+ npm-debug.log*
29
+ yarn-debug.log*
30
+ yarn-error.log*
31
+ .pnpm-debug.log*
32
+
33
+ # env files (can opt-in for committing if needed)
34
+ .env*
35
+
36
+ # vercel
37
+ .vercel
38
+
39
+ # typescript
40
+ *.tsbuildinfo
41
+ next-env.d.ts