ucode-agent 1.44.0 → 1.47.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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # ucode
2
2
 
3
3
  A coding agent that lives in your terminal. It reads your code, edits it, runs
4
- your commands, and keeps every conversation on disk. It runs on NVIDIA and
5
- Cohere models, all of them free.
4
+ your commands, and keeps every conversation on disk. It runs on NVIDIA,
5
+ Cohere and Nex AGI models, all of them free.
6
6
 
7
7
  It opens on a quiet screen — the name, the place to type, and the version in the
8
8
  corner:
@@ -27,7 +27,7 @@ corner:
27
27
  add a dark mode toggle that remembers the choice
28
28
 
29
29
 
30
- v1.44.0
30
+ v1.46.0
31
31
  ```
32
32
 
33
33
  A light crosses the wordmark once as it opens, and the three lines under the box
@@ -87,8 +87,8 @@ Needs Node 22 or newer.
87
87
 
88
88
  ## The models
89
89
 
90
- Five, and no picker full of names nobody recognises. NVIDIA and Cohere both
91
- serve capable models free, and both handle tool calling
90
+ Six, and no picker full of names nobody recognises. NVIDIA, Cohere and Nex AGI
91
+ all serve capable models free, and all handle tool calling
92
92
  properly, which is the thing an agent actually depends on.
93
93
 
94
94
  | Model | Context | For |
@@ -98,6 +98,7 @@ properly, which is the thing an agent actually depends on.
98
98
  | Nemotron 3 Super | 262k | strong all-rounder, quick to start |
99
99
  | Nemotron 3 Nano Omni | 256k | small, fast, reasoning tuned |
100
100
  | **North Mini Code** ★ | 256k | the default — built for code and interface work, quick to answer |
101
+ | Nex N2.5 Pro | 262k | new agentic coder, on trial — can stall on big builds |
101
102
 
102
103
  `/model` shows them and switches. `ucode -m cohere/north-mini-code:free`
103
104
  starts on one.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ucode-agent",
3
- "version": "1.44.0",
4
- "description": "ucode - a terminal coding agent that reads, edits and runs your code, on NVIDIA and Cohere models.",
3
+ "version": "1.47.0",
4
+ "description": "ucode - a terminal coding agent that reads, edits and runs your code, on NVIDIA, Cohere and Nex AGI models.",
5
5
  "type": "module",
6
6
  "main": "ucode.js",
7
7
  "bin": {
@@ -38,7 +38,8 @@
38
38
  "llm",
39
39
  "nvidia",
40
40
  "nemotron",
41
- "cohere"
41
+ "cohere",
42
+ "nex-agi"
42
43
  ],
43
44
  "author": "om dixit",
44
45
  "license": "ISC",
@@ -53,6 +54,7 @@
53
54
  "playwright-core": "^1.63.0"
54
55
  },
55
56
  "devDependencies": {
57
+ "@types/node": "^26.5.1",
56
58
  "typescript": "^5.9.3"
57
59
  },
58
60
  "publishConfig": {
@@ -0,0 +1,77 @@
1
+ /**
2
+ * csscheck.js — a custom property that was never defined.
3
+ *
4
+ * `padding: var(--space-md)` where nothing ever declared `--space-md` does not
5
+ * warn, does not fall back and does not appear in the console. The browser
6
+ * throws the whole declaration away at parse time, so the padding is simply
7
+ * not there — and the page still renders, still looks deliberate, and is
8
+ * wrong in a way that reads as a design choice rather than a bug.
9
+ *
10
+ * It came out of a build here: a stylesheet that declared --space-xs, --space,
11
+ * --space-sm, --space-lg and --space-xl, then used --space-md four times.
12
+ * Nothing in the checks looked at CSS, so nothing said a word.
13
+ *
14
+ * The check is one whole app at a time rather than one file at a time,
15
+ * because the definition and the use are routinely in different files: a
16
+ * single-page app keeps its tokens in styles.css and reaches for them from an
17
+ * inline <style>, and reporting that as undefined would be worse than not
18
+ * looking. Whatever is passed in is the world; a name defined anywhere in it
19
+ * counts as defined everywhere.
20
+ */
21
+
22
+ /** Definitions: `--name:` at the start of a declaration. */
23
+ const DEFINED = /(?:^|[;{\s])(--[A-Za-z0-9_-]+)\s*:/g;
24
+ /** Uses: `var(--name)` — and whether a fallback follows the name. */
25
+ const USED = /var\(\s*(--[A-Za-z0-9_-]+)\s*([,)])/g;
26
+ /** A name handed to CSS from script is defined, just not in a stylesheet. */
27
+ const FROM_JS = /setProperty\(\s*['"`](--[A-Za-z0-9_-]+)/g;
28
+ /** Inline styles in a page are part of that page's CSS. */
29
+ const STYLE_TAGS = /<style\b[^>]*>([\s\S]*?)<\/style\s*>/gi;
30
+
31
+ /** Comments hold examples and dead rules; neither defines anything. */
32
+ const uncommented = (css) => css.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, ' '));
33
+
34
+ /** The CSS in a source file: a stylesheet whole, a page's <style> blocks only. */
35
+ function cssOf(rel, text) {
36
+ if (!/\.html?$/i.test(rel)) return uncommented(text);
37
+ // Everything outside a <style> is blanked rather than dropped, so a line
38
+ // number in the result is still a line number in the file.
39
+ let out = text.replace(/[^\n]/g, ' ');
40
+ for (const m of text.matchAll(STYLE_TAGS)) {
41
+ const at = m.index + m[0].indexOf(m[1]);
42
+ out = out.slice(0, at) + uncommented(m[1]) + out.slice(at + m[1].length);
43
+ }
44
+ return out;
45
+ }
46
+
47
+ /**
48
+ * Every `var(--x)` in `sources` with no `--x` defined anywhere in them and no
49
+ * fallback of its own.
50
+ *
51
+ * @param {{rel: string, text: string}[]} sources every file of one app that
52
+ * holds CSS — stylesheets, pages with inline <style>, and any script that
53
+ * sets a property, which defines names without declaring them.
54
+ * @returns {{rel: string, line: number, name: string}[]}
55
+ */
56
+ export function checkCss(sources = []) {
57
+ const files = sources.map(({ rel, text }) => ({ rel, css: cssOf(rel, String(text ?? '')) }));
58
+
59
+ const defined = new Set();
60
+ for (const { css } of files) for (const m of css.matchAll(DEFINED)) defined.add(m[1]);
61
+ for (const { text } of sources) for (const m of String(text ?? '').matchAll(FROM_JS)) defined.add(m[1]);
62
+
63
+ const problems = [];
64
+ const seen = new Set();
65
+ for (const { rel, css } of files) {
66
+ for (const m of css.matchAll(USED)) {
67
+ const [, name, next] = m;
68
+ if (next === ',') continue; // it carries its own fallback
69
+ if (defined.has(name)) continue;
70
+ const key = `${rel}:${name}`;
71
+ if (seen.has(key)) continue; // once per name per file, not once per use
72
+ seen.add(key);
73
+ problems.push({ rel, name, line: css.slice(0, m.index).split('\n').length });
74
+ }
75
+ }
76
+ return problems;
77
+ }
@@ -100,7 +100,12 @@ export async function save(session, { home = HOME } = {}) {
100
100
  const temp = `${target}.${process.pid}.tmp`;
101
101
 
102
102
  try {
103
- await fs.writeFile(temp, JSON.stringify(session, null, 2), 'utf8');
103
+ // Compact, not pretty. The whole file is rewritten after every tool
104
+ // result, and on a long build with file contents in it the indentation was
105
+ // roughly half of what got written each time — for a file read by programs,
106
+ // not by people. `node -e "console.log(require('./x.json').messages)"` or
107
+ // /resume reads it either way.
108
+ await fs.writeFile(temp, JSON.stringify(session), 'utf8');
104
109
  await fs.rename(temp, target);
105
110
  } catch (err) {
106
111
  await fs.rm(temp, { force: true }).catch(() => {});
@@ -0,0 +1,69 @@
1
+ /**
2
+ * jslogic.js — the logic mistakes, in the system prompt, every turn.
3
+ *
4
+ * Skills are disclosed progressively and that is right for almost everything:
5
+ * a body loads when the work matches it. This does not, because the failures
6
+ * below do not announce themselves. They arrive in the middle of a build
7
+ * nobody called "a JavaScript task" — an empty state that stops coming back,
8
+ * a listener attached to a row that has since been replaced — and by the time
9
+ * the work looks like it needs a guide, the guide is being read too late.
10
+ *
11
+ * So it is always on, and the price of that is length: every word here is
12
+ * re-read by the provider on every step of every build. It is kept to the
13
+ * mistakes that have actually shipped from here, in the shortest form that
14
+ * still says what to do instead. Anything a parser could catch belongs in a
15
+ * check — see htmlcheck.js — not in this list. Nothing general about
16
+ * JavaScript belongs here at all: the model already knows it, and a paragraph
17
+ * confirming what it knows costs the same as one it does not.
18
+ *
19
+ * The vanilla section is drawn from builds that came out of ucode looking
20
+ * finished and were not. The framework section is the same failures one level
21
+ * up.
22
+ */
23
+
24
+ export const JS_LOGIC = `## The logic mistakes that ship looking finished
25
+
26
+ Every one of these renders. None of them throws where you can see it. Check
27
+ each against what you are writing, before you write it.
28
+
29
+ In a page:
30
+
31
+ - **Empty is a state, not a starting position.** A render function that clears
32
+ its container decides what zero items looks like, in that same function.
33
+ Markup sitting in index.html, or one render call at start-up, survives the
34
+ first paint and then never comes back: add an item, delete it, and the list
35
+ is blank for good.
36
+ - **User text goes in with textContent.** Into innerHTML, a task named
37
+ \`<img src=x onerror=alert(1)>\` runs, and one reading \`5 < 6\` silently eats
38
+ the rest of the row. Build rows with createElement and set .textContent.
39
+ - **Every var(--x) needs a --x in :root.** An undefined custom property does
40
+ not fall back and does not warn — the browser discards the whole declaration,
41
+ so the padding is simply missing. Use only names you defined.
42
+ - **One listener on the container, not one per row.** Re-rendering throws the
43
+ rows away and their listeners with them. Listen on the list and read
44
+ event.target.closest('[data-id]').
45
+ - **An id that goes through the DOM comes back a string.** dataset.id is
46
+ "1738", never 1738, so === against a numeric id is always false. Make ids
47
+ strings when you create them.
48
+ - **Saved is not loaded.** Writing localStorage is half of it: read it as the
49
+ page boots, before the first render, and confirm a refresh keeps the state.
50
+ - **A missing element kills every line after it.** getElementById returns null
51
+ for a typo, .addEventListener on it throws, and the rest of the module never
52
+ runs — so every later handler in that file is silently never attached. One
53
+ typo, and nothing on the page works.
54
+
55
+ In React or Next, the same failures one level up:
56
+
57
+ - **Setting state does not change the variable you are holding.** Two
58
+ setCount(count + 1) in one handler add one. Pass a function when the next
59
+ value depends on the last.
60
+ - **State is replaced, never edited.** items.push(x) then setItems(items) is
61
+ the same reference, so nothing re-renders.
62
+ - **Hooks run every render, in order** — never inside an if, a loop, or after
63
+ an early return.
64
+ - **An effect that sets what it depends on is a loop**, and one with no
65
+ dependency array runs after every render.
66
+ - **key={index} is not identity.** On a list that reorders or filters, it moves
67
+ state onto the wrong row.
68
+ - **"use client" is per file, at the top.** State, an event handler or a
69
+ browser API in a server file fails at build, not at run.`;