ucode-agent 1.42.0 → 1.44.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/README.md +1 -1
- package/package.json +1 -1
- package/src/tools/index.js +5 -1
- package/src/tools/scaffold.js +64 -1
- package/src/tools/shell.js +19 -0
- package/ucode.js +10 -0
package/README.md
CHANGED
package/package.json
CHANGED
package/src/tools/index.js
CHANGED
|
@@ -95,7 +95,11 @@ export const tools = [
|
|
|
95
95
|
'toy, a game, a visualisation, a calculator, a timer. "next-shadcn": Next.js 16, ' +
|
|
96
96
|
'TypeScript, Tailwind 4 and shadcn with 33 components — only when the app genuinely ' +
|
|
97
97
|
'needs routes, a database or many screens, because it costs an install and a build. ' +
|
|
98
|
-
'This is how every Next.js app begins - never run create-next-app or shadcn init.'
|
|
98
|
+
'This is how every Next.js app begins - never run create-next-app or shadcn init. ' +
|
|
99
|
+
'In "next-shadcn" every path in "files" goes under src/: a page is a route only at ' +
|
|
100
|
+
'<app>/src/app/page.tsx or <app>/src/app/<segment>/page.tsx, an API handler only at ' +
|
|
101
|
+
'<app>/src/app/api/<name>/route.ts, and components at <app>/src/components/<feature>/. ' +
|
|
102
|
+
'A page.tsx written anywhere else is an ordinary file the router never serves.',
|
|
99
103
|
parameters: {
|
|
100
104
|
type: 'object',
|
|
101
105
|
properties: {
|
package/src/tools/scaffold.js
CHANGED
|
@@ -173,6 +173,60 @@ async function unprefixOwnFolder(appDir, written) {
|
|
|
173
173
|
return fixed;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Files that legitimately sit at the root of a Next.js project.
|
|
178
|
+
*
|
|
179
|
+
* Everything else a model writes there is app code that has missed src/.
|
|
180
|
+
*/
|
|
181
|
+
const NEXT_ROOT = /^(?:package(?:-lock)?\.json|next\.config\.[mc]?[jt]s|tsconfig\.json|postcss\.config\.[mc]?js|eslint\.config\.[mc]?js|components\.json|README\.md|TEMPLATE\.md|\.gitignore|next-env\.d\.ts)$/i;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Put Next.js files where Next.js looks for them.
|
|
185
|
+
*
|
|
186
|
+
* Routing is folder-based and unforgiving: a page is a route only at
|
|
187
|
+
* src/app/<segment>/page.tsx. A traced build wrote page.tsx at the project
|
|
188
|
+
* root and api/expenses/route.ts beside it — the build passed, every route
|
|
189
|
+
* 404ed, and nothing said why.
|
|
190
|
+
*
|
|
191
|
+
* The guide has the tree in it, and the guide cannot help: it comes back
|
|
192
|
+
* inside the create_app result, and the model chooses these paths in the call
|
|
193
|
+
* that produces that result. It is reading the map after it has parked. So the
|
|
194
|
+
* paths are corrected on the way in, and the result says what moved and why,
|
|
195
|
+
* which is the copy that arrives in time to matter for the next file.
|
|
196
|
+
*
|
|
197
|
+
* Only for next-shadcn, only for files under the app folder, and never for the
|
|
198
|
+
* config files that genuinely belong at the root.
|
|
199
|
+
*/
|
|
200
|
+
function placeForNext(files, folder) {
|
|
201
|
+
const moved = [];
|
|
202
|
+
|
|
203
|
+
const out = files.map((f) => {
|
|
204
|
+
const rel = String(f.path ?? '').split('\\').join('/');
|
|
205
|
+
const prefix = `${folder}/`;
|
|
206
|
+
if (!rel.startsWith(prefix)) return f;
|
|
207
|
+
|
|
208
|
+
const inside = rel.slice(prefix.length);
|
|
209
|
+
if (!inside || inside.startsWith('src/') || inside.startsWith('public/')) return f;
|
|
210
|
+
if (NEXT_ROOT.test(inside)) return f;
|
|
211
|
+
|
|
212
|
+
// app/... is the right tree written one level too high; everything else
|
|
213
|
+
// that is app code belongs under src/ as it stands.
|
|
214
|
+
const under = inside.startsWith('app/') ? `src/${inside}` : `src/app/${inside}`;
|
|
215
|
+
const isRoute = /(?:^|\/)(?:page|layout|loading|error|not-found)\.[jt]sx?$/.test(inside)
|
|
216
|
+
|| /(?:^|\/)route\.[jt]s$/.test(inside);
|
|
217
|
+
const isCode = /^(?:components|lib|hooks|styles|utils)\//.test(inside);
|
|
218
|
+
|
|
219
|
+
if (!isRoute && !isCode) return f;
|
|
220
|
+
const to = isCode ? `${prefix}src/${inside}` : `${prefix}${under}`;
|
|
221
|
+
if (to === rel) return f;
|
|
222
|
+
|
|
223
|
+
moved.push(`${inside} -> ${to.slice(prefix.length)}`);
|
|
224
|
+
return { ...f, path: to };
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
return { files: out, moved };
|
|
228
|
+
}
|
|
229
|
+
|
|
176
230
|
async function keepDesignTokens(appDir, template, written, starterCss) {
|
|
177
231
|
const rel = TOKEN_FILE[template];
|
|
178
232
|
if (!rel || !starterCss) return null;
|
|
@@ -366,7 +420,9 @@ export async function createApp({ folder, name, description, template = 'plain-h
|
|
|
366
420
|
|
|
367
421
|
// The app's own files, written in this same call. Two round trips become
|
|
368
422
|
// one, and round trips are nearly all of the time a build takes.
|
|
369
|
-
|
|
423
|
+
// Next.js only: paths that have missed src/ are corrected before they land.
|
|
424
|
+
const placed = template === 'next-shadcn' ? placeForNext(given, path.basename(target.abs)) : { files: given, moved: [] };
|
|
425
|
+
const mine = placed.files;
|
|
370
426
|
const wrote = mine.length ? await batchWrite({ files: mine }) : null;
|
|
371
427
|
const written = new Set(mine.map((f) => resolveIn(f.path, 'create_app', 'files').abs));
|
|
372
428
|
const keptTokens = await keepDesignTokens(target.abs, template, written, starterCss);
|
|
@@ -387,6 +443,13 @@ export async function createApp({ folder, name, description, template = 'plain-h
|
|
|
387
443
|
`Created ${target.show} from the ${template} starter — ${copied.length} files, already known to build.\n` +
|
|
388
444
|
(look ? `Design: the ${look.name} preset (${look.summary}), font ${look.fonts?.sans ?? 'Geist'}.\n` : '') +
|
|
389
445
|
(wrote ? `\nYour ${mine.length} file${mine.length === 1 ? '' : 's'}:\n${wrote.content}\n` : '') +
|
|
446
|
+
(placed.moved.length
|
|
447
|
+
? `\nMoved into place: ${placed.moved.join(', ')}. In Next.js a page is only a route at `
|
|
448
|
+
+ 'src/app/<segment>/page.tsx, an API handler only at src/app/api/<name>/route.ts, and '
|
|
449
|
+
+ 'components and libraries live under src/. A file written outside src/ builds fine and '
|
|
450
|
+
+ 'is never served, which is the hardest kind of wrong to notice. Write the rest there '
|
|
451
|
+
+ 'directly.\n'
|
|
452
|
+
: '') +
|
|
390
453
|
(relinked.length
|
|
391
454
|
? `\nFixed in ${relinked.join(', ')}: links that began with "${path.basename(target.abs)}/". ` +
|
|
392
455
|
'Paths in "files" are relative to the project root, but a link inside a page is ' +
|
package/src/tools/shell.js
CHANGED
|
@@ -183,6 +183,25 @@ export function serversReadySince(since = 0) {
|
|
|
183
183
|
return readyServers.filter((s) => s.at >= since);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Shut down every dev server this session started.
|
|
188
|
+
*
|
|
189
|
+
* killTree existed and nothing ever called it, so a server outlived the
|
|
190
|
+
* session that started it — every build left one behind. Twenty-four builds
|
|
191
|
+
* in one afternoon left seventy-three node processes running, the oldest for
|
|
192
|
+
* three and a half hours, holding ports the next run then could not use.
|
|
193
|
+
*
|
|
194
|
+
* Best effort and never throws: a process that has already gone is the
|
|
195
|
+
* outcome we wanted anyway.
|
|
196
|
+
*/
|
|
197
|
+
export function stopServers() {
|
|
198
|
+
const stopped = readyServers.splice(0, readyServers.length);
|
|
199
|
+
for (const s of stopped) {
|
|
200
|
+
try { killTree(s.pid); } catch { /* already gone */ }
|
|
201
|
+
}
|
|
202
|
+
return stopped.length;
|
|
203
|
+
}
|
|
204
|
+
|
|
186
205
|
/** Every server started this session, for reading what they have logged. */
|
|
187
206
|
export function runningServers() {
|
|
188
207
|
return readyServers.slice();
|
package/ucode.js
CHANGED
|
@@ -145,6 +145,16 @@ async function main() {
|
|
|
145
145
|
await agent.persist().catch(() => {});
|
|
146
146
|
agent.ui.close();
|
|
147
147
|
process.exitCode = 1;
|
|
148
|
+
} finally {
|
|
149
|
+
// Take the dev servers down with the session that started them, however it
|
|
150
|
+
// ended. killTree was written for this and nothing ever called it, so every
|
|
151
|
+
// build left its server running: twenty-four builds in an afternoon left
|
|
152
|
+
// seventy-three node processes alive, the oldest for three and a half
|
|
153
|
+
// hours, holding the ports the next run wanted.
|
|
154
|
+
try {
|
|
155
|
+
const { stopServers } = await import('./src/tools/shell.js');
|
|
156
|
+
stopServers();
|
|
157
|
+
} catch { /* nothing started, or already gone */ }
|
|
148
158
|
}
|
|
149
159
|
}
|
|
150
160
|
|