@stemtrooper/learningcode 0.4.2 → 0.4.4

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
@@ -63,6 +63,34 @@ npm install -g @stemtrooper/learningcode
63
63
  learningcode --version
64
64
  ```
65
65
 
66
+ `node --version` must report **22.19.0 or newer**. If it is older — and
67
+ Ubuntu's own `apt` package is usually Node 18 — install a current one first:
68
+
69
+ ```bash
70
+ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
71
+ source ~/.bashrc
72
+ nvm install 24
73
+ nvm alias default 24
74
+ ```
75
+
76
+ **If the install fails with a permissions error**, npm's global directory belongs
77
+ to root. Move it somewhere you own, then install again:
78
+
79
+ ```bash
80
+ mkdir -p ~/.npm-global
81
+ npm config set prefix ~/.npm-global
82
+ export PATH="$HOME/.npm-global/bin:$PATH"
83
+ echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc
84
+
85
+ npm install -g @stemtrooper/learningcode
86
+ ```
87
+
88
+ The last line is what makes `learningcode` visible in future terminals; without
89
+ it you will think the install failed.
90
+
91
+ **Do not use `sudo npm install -g`.** It appears to work, then writes
92
+ root-owned files that break your next upgrade with a confusing error.
93
+
66
94
  ### Termux (Android)
67
95
 
68
96
  No root and no `proot-distro` needed.
@@ -143,6 +171,10 @@ learningcode --mode json # machine-readable output
143
171
  The TLC brand theme and banner ship with the package, so there is nothing to
144
172
  configure.
145
173
 
174
+ LearningCode suppresses Pi's built-in startup header so the TLC banner appears
175
+ without the Pi logo flashing first. Use `learningcode --verbose` to show Pi's
176
+ startup details and loaded-resource list as well.
177
+
146
178
  ```
147
179
  ██╗ ███████╗ █████╗ ██████╗ ███╗ ██╗██╗███╗ ██╗ ██████╗ ██████╗ ██████╗ ██████╗ ███████╗
148
180
  ██║ ██╔════╝██╔══██╗██╔══██╗████╗ ██║██║████╗ ██║██╔════╝ ██╔════╝██╔═══██╗██╔══██╗██╔════╝
@@ -167,7 +199,9 @@ A footer shows your remaining quota whenever you are connected to Spark:
167
199
  ## Troubleshooting
168
200
 
169
201
  **`Node 22.19.0 or newer is required`**
170
- Upgrade Node, then reinstall: `npm i -g @stemtrooper/learningcode`.
202
+ Upgrade Node, then reinstall: `npm i -g @stemtrooper/learningcode`. On Linux this
203
+ is usually the first thing to check, because Ubuntu ships Node 18 — see
204
+ [macOS / Linux](#macos--linux).
171
205
 
172
206
  **`No Spark API token configured`**
173
207
  You do not have a token yet, or it is not cached. Run `learningcode --login` to
@@ -267,4 +301,4 @@ is also MIT. See [LICENSE](LICENSE).
267
301
 
268
302
  Not affiliated with, endorsed by, or connected to Pi, OpenCode, Anomaly, or any of
269
303
  the model providers reachable through this client. "The Learning Curve", TLC and
270
- Spark are trademarks of The Learning Curve.
304
+ Spark are trademarks of The Learning Curve.
@@ -17,6 +17,7 @@ import {
17
17
  } from "../lib/config.mjs";
18
18
  import { GO_PROVIDER, saveProviderKey } from "../lib/auth.mjs";
19
19
  import { ensureSparkProvider, modelsPath, retargetProvider } from "../lib/models.mjs";
20
+ import { ensureQuietStartup } from "../lib/settings.mjs";
20
21
  import { ensureThemes, preferredTheme } from "../lib/themes.mjs";
21
22
  import { hasToken, looksLikeToken, resolveToken, writeCachedToken } from "../lib/token.mjs";
22
23
 
@@ -45,6 +46,7 @@ Everything else is passed straight through to Pi, so the usual flags work:
45
46
  learningcode -p "explain main.py" one-shot, non-interactive
46
47
  learningcode --mode json machine-readable event stream
47
48
  learningcode --list-models every model Pi can reach
49
+ learningcode --verbose show Pi's startup header and resource list
48
50
 
49
51
  Other providers
50
52
  Any --model other than tlc-spark/... skips the Spark token and the Spark
@@ -230,6 +232,7 @@ async function main() {
230
232
  }
231
233
  await ensureSparkProvider(dir);
232
234
  await ensureThemes(dir);
235
+ await ensureQuietStartup(dir);
233
236
 
234
237
  // `--login` forces a fresh paste even when a token is already cached.
235
238
  const forced = [];
@@ -369,4 +372,4 @@ main().catch((error) => {
369
372
  // Anything the user can act on gets one line. A stack trace is noise for a
370
373
  // student who mistyped a token; keep it for genuine bugs.
371
374
  fail(error instanceof UserError ? error.message : error?.stack || String(error));
372
- });
375
+ });
@@ -0,0 +1,42 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { UserError } from "./config.mjs";
4
+
5
+ /**
6
+ * Pi renders its built-in header before extension `session_start` handlers run.
7
+ * Its supported quietStartup setting prevents that header flashing before the
8
+ * LearningCode banner is installed. Only seed the setting when it is unset, so
9
+ * an explicit choice in the student's LearningCode settings remains theirs.
10
+ */
11
+ export async function ensureQuietStartup(agentDir) {
12
+ const path = join(agentDir, "settings.json");
13
+ let settings;
14
+
15
+ try {
16
+ const contents = await readFile(path, "utf-8");
17
+ try {
18
+ settings = JSON.parse(contents.replace(/^\uFEFF/, ""));
19
+ } catch (error) {
20
+ throw new UserError(`Could not parse Pi settings at ${path}: ${error.message}`);
21
+ }
22
+ } catch (error) {
23
+ if (error instanceof UserError) throw error;
24
+ if (error?.code !== "ENOENT") {
25
+ throw new UserError(`Could not read Pi settings at ${path}: ${error.message}`);
26
+ }
27
+ settings = {};
28
+ }
29
+
30
+ if (!settings || typeof settings !== "object" || Array.isArray(settings)) {
31
+ throw new UserError(`Pi settings at ${path} must contain a JSON object.`);
32
+ }
33
+
34
+ if (typeof settings.quietStartup === "boolean" || settings.quietStartup === "header") {
35
+ return false;
36
+ }
37
+
38
+ settings.quietStartup = true;
39
+ await mkdir(agentDir, { recursive: true });
40
+ await writeFile(path, `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
41
+ return true;
42
+ }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@stemtrooper/learningcode",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "TLC Spark coding agent for students: Pi wired to the Spark OpenAI-compatible endpoint with per-student tokens, quota and seat-queue awareness.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "bin": {
8
- "learningcode": "./bin/learningcode.mjs"
8
+ "learningcode": "bin/learningcode.mjs"
9
9
  },
10
10
  "files": [
11
11
  "assets",