little-coder 1.9.5 → 1.9.6

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/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  All notable changes to little-coder are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and little-coder's public interface (CLI, providers, tools, skills) follows semver starting at `v0.0.1` post-rename.
4
4
 
5
+ ## [v1.9.6] — 2026-06-18
6
+
7
+ ### Fixed
8
+ - **Auto-update silently failed on Windows** ([PR #52](https://github.com/itayinbarr/little-coder/pull/52) by [@i-snyder](https://github.com/i-snyder)). On Windows `npm` is `npm.cmd` (a batch-file shim), and `spawnSync("npm", …)` without `shell: true` returns `ENOENT` before npm ever launches — the user saw `✗ Update failed (npm exit null). Continuing with v1.9.x.`, where the `null` exit code was the tell that npm never ran. The launcher now invokes `npm` via `process.env.COMSPEC /c npm` on Windows (`shell: true` would also work but triggers Node 24+'s `DEP0190` deprecation warning; COMSPEC doesn't). Cross-platform behavior unchanged: POSIX still uses plain `spawnSync("npm", …)`. The failure message is also fixed — when the spawn itself fails (`result.error` is set), the launcher now surfaces `result.error.code` (e.g. `ENOENT`) instead of `result.status` (which is `null` and meaningless), so users diagnosing future spawn failures get an actionable code instead of `npm exit null`.
9
+
10
+ ### Added
11
+ - **`little-coder --update` forces a fresh update check** ([PR #52](https://github.com/itayinbarr/little-coder/pull/52) by [@i-snyder](https://github.com/i-snyder)). The launcher caches the registry "latest" lookup for 12 hours; `--update` bypasses that cache and fetches fresh from npm, then either updates or prints `✓ little-coder is already up to date (v<x>)`. The flag is stripped from argv before forwarding to pi, so `little-coder --update` no longer errors with `Unknown option: --update`. Two new `shouldSkip` tests document the interaction (the flag forces a check; the notice-only mode still applies on non-TTY).
12
+
13
+ ### Notes for upgraders
14
+ - No CLI-flag or public-API breakage. Windows users on v1.9.5 or earlier should manually `npm install -g little-coder@1.9.6` this once; future updates will work via the in-app prompt or `little-coder --update`.
15
+
16
+ ---
17
+
5
18
  ## [v1.9.5] — 2026-06-18
6
19
 
7
20
  ### Changed
@@ -135,7 +135,8 @@ try {
135
135
  // ignore — update-check just won't fire if we can't read the version
136
136
  }
137
137
  if (!isSubagent) {
138
- const exitAfterCheck = await checkForUpdate(currentVersion);
138
+ const forceUpdate = process.argv.includes("--update");
139
+ const exitAfterCheck = await checkForUpdate(currentVersion, { force: forceUpdate });
139
140
  if (exitAfterCheck) {
140
141
  // Successful update happened; user needs to re-run the new binary.
141
142
  process.exit(0);
@@ -148,7 +149,9 @@ if (!isSubagent) {
148
149
  // --system-prompt : load <pkgRoot>/AGENTS.md regardless of cwd
149
150
  //
150
151
  // Strip our own flags before forwarding to pi so it doesn't reject them.
151
- const userArgs = process.argv.slice(2).filter((a) => a !== "--no-update-check");
152
+ const userArgs = process.argv.slice(2).filter(
153
+ (a) => a !== "--no-update-check" && a !== "--update",
154
+ );
152
155
  const agentsMd = join(pkgRoot, "AGENTS.md");
153
156
 
154
157
  // Default the thinking level to "medium" for interactive sessions (pi's own
@@ -123,17 +123,28 @@ function promptYesNo(question) {
123
123
  // Returns `true` if the launcher should NOT proceed to spawn pi (because we
124
124
  // updated and exited / the user opted out and we should re-run). Returns
125
125
  // `false` to let the launcher continue.
126
+ //
127
+ // opts.force — bypass the 12h cache and always fetch the latest version from
128
+ // the registry. Used when the user explicitly passes `--update`. If already
129
+ // at the latest version, prints a short "up to date" notice instead of
130
+ // silently returning.
126
131
  export async function checkForUpdate(currentVersion, opts = {}) {
127
132
  const skip = opts.skip ?? shouldSkip();
128
133
  if (skip === true) return false;
129
134
 
130
- let latest = readCache()?.latest;
135
+ const force = opts.force ?? false;
136
+ let latest = (!force && readCache()?.latest) || null;
131
137
  if (!latest) {
132
138
  latest = await fetchLatest();
133
139
  if (latest) writeCache(latest);
134
140
  }
135
141
  if (!latest) return false;
136
- if (compareSemver(latest, currentVersion) <= 0) return false;
142
+ if (compareSemver(latest, currentVersion) <= 0) {
143
+ if (force) {
144
+ process.stderr.write(`\n ✓ little-coder is already up to date (v${currentVersion}).\n\n`);
145
+ }
146
+ return false;
147
+ }
137
148
 
138
149
  const headline =
139
150
  `\n📦 little-coder v${latest} is available (you have v${currentVersion}).`;
@@ -151,17 +162,25 @@ export async function checkForUpdate(currentVersion, opts = {}) {
151
162
  }
152
163
 
153
164
  process.stderr.write(`\n Running: npm install -g little-coder@${latest}\n\n`);
154
- const result = spawnSync("npm", ["install", "-g", `little-coder@${latest}`], {
155
- stdio: "inherit",
156
- });
165
+ // On Windows `npm` resolves to `npm.cmd`, a batch-file shim that Node's
166
+ // spawnSync cannot execute without shell:true. However, shell:true with
167
+ // array args triggers DEP0190 on Node 24+. Instead, invoke cmd.exe directly
168
+ // via COMSPEC — it resolves `npm` to `npm.cmd` itself, no shell:true needed.
169
+ const npmArgs = ["install", "-g", `little-coder@${latest}`];
170
+ const result = process.platform === "win32"
171
+ ? spawnSync(process.env.COMSPEC || "cmd.exe", ["/c", "npm", ...npmArgs], { stdio: "inherit" })
172
+ : spawnSync("npm", npmArgs, { stdio: "inherit" });
157
173
  if (result.status === 0) {
158
174
  process.stderr.write(
159
175
  `\n ✓ Updated to v${latest}. Re-run \`little-coder\` to use the new version.\n\n`,
160
176
  );
161
177
  return true;
162
178
  }
179
+ const exitDesc = result.error
180
+ ? `could not launch npm (${result.error.code ?? result.error.message})`
181
+ : `npm exit ${result.status}`;
163
182
  process.stderr.write(
164
- `\n ✗ Update failed (npm exit ${result.status}). Continuing with v${currentVersion}.\n\n`,
183
+ `\n ✗ Update failed (${exitDesc}). Continuing with v${currentVersion}.\n\n`,
165
184
  );
166
185
  return false;
167
186
  }
@@ -161,4 +161,12 @@ describe("shouldSkip", () => {
161
161
  it("returns notice-only on non-TTY pipelines", () => {
162
162
  expect(shouldSkip([], noEnv, pipeStdout())).toBe("notice-only");
163
163
  });
164
+
165
+ it("does not skip for --update (it forces the check, not skips it)", () => {
166
+ expect(shouldSkip(["--update"], noEnv, ttyStdout())).toBe(false);
167
+ });
168
+
169
+ it("notice-only still applies with --update on non-TTY", () => {
170
+ expect(shouldSkip(["--update"], noEnv, pipeStdout())).toBe("notice-only");
171
+ });
164
172
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "little-coder",
3
- "version": "1.9.5",
3
+ "version": "1.9.6",
4
4
  "description": "A pi-based coding agent optimized for small local language models. Reproduces the whitepaper's scaffold-model-fit adaptations as pi extensions.",
5
5
  "homepage": "https://github.com/itayinbarr/little-coder",
6
6
  "repository": {