@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 +36 -2
- package/bin/learningcode.mjs +4 -1
- package/lib/settings.mjs +42 -0
- package/package.json +2 -2
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.
|
package/bin/learningcode.mjs
CHANGED
|
@@ -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
|
+
});
|
package/lib/settings.mjs
ADDED
|
@@ -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.
|
|
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": "
|
|
8
|
+
"learningcode": "bin/learningcode.mjs"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"assets",
|