strom-research 1.19.0 → 1.20.0-beta.1
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.cs.md +12 -0
- package/CHANGELOG.de.md +13 -0
- package/CHANGELOG.md +12 -0
- package/assets/lang/cs.json +4 -0
- package/assets/lang/de.json +4 -0
- package/dist/cli/context.js +6 -2
- package/dist/cli/main.js +10 -6
- package/dist/cli/menu-parts.js +4 -1
- package/dist/cli/ui.js +4 -0
- package/dist/commands/checks.js +9 -6
- package/dist/commands/start.js +5 -1
- package/dist/core/errors.js +11 -0
- package/dist/core/git.js +3 -0
- package/dist/core/tree.js +1 -1
- package/package.json +4 -1
package/CHANGELOG.cs.md
CHANGED
|
@@ -5,6 +5,18 @@
|
|
|
5
5
|
Změny každé verze ve zkratce. Úplné poznámky a instalační řádky jsou na
|
|
6
6
|
[GitHub Releases](https://github.com/ACiDekCZ/strom-research/releases) (anglicky).
|
|
7
7
|
|
|
8
|
+
## 1.20.0
|
|
9
|
+
|
|
10
|
+
*Připravuje se: bety do 1.20.0-beta.1. Oddíl bude úplný s vydáním.*
|
|
11
|
+
|
|
12
|
+
- **Menu bez nikoho u terminálu to řekne hned.** Spuštěné tam, kde nemá kdo odpovědět (zavřený vstup, okno
|
|
13
|
+
bez terminálu), skončí jednou větou v jazyce výzkumu a tím, co udělat místo toho, už ne chybou programu; otázka,
|
|
14
|
+
jejíž vstup skončí dřív než odpověď, ukončí menu stejně.
|
|
15
|
+
- **`strom check` mluví jedním jazykem.** Pro člověka jsou všechny nálezy, součet i poznámky v jazyce výzkumu, pro
|
|
16
|
+
agenta celé anglicky, nikdy směs obou.
|
|
17
|
+
- **Když sandbox agenta odmítne git, je jmenováno správné místo.** Zpráva uvádí jako odepřenou historii výzkumu
|
|
18
|
+
(jeho složku `.git`), ne program gitu.
|
|
19
|
+
|
|
8
20
|
## 1.19.0
|
|
9
21
|
|
|
10
22
|
*22. 10. 2026 · patří k aplikaci Strom 3.15.0.*
|
package/CHANGELOG.de.md
CHANGED
|
@@ -5,6 +5,19 @@
|
|
|
5
5
|
Die Änderungen jeder Version in Kürze. Die vollständigen Hinweise und die Installationszeilen stehen bei
|
|
6
6
|
[GitHub Releases](https://github.com/ACiDekCZ/strom-research/releases) (auf Englisch).
|
|
7
7
|
|
|
8
|
+
## 1.20.0
|
|
9
|
+
|
|
10
|
+
*In Vorbereitung: die Betas bis 1.20.0-beta.1. Der Abschnitt ist mit dem Release vollständig.*
|
|
11
|
+
|
|
12
|
+
- **Das Menü ohne jemanden am Terminal sagt es sofort.** Dort gestartet, wo niemand antworten kann (geschlossene
|
|
13
|
+
Eingabe, ein Fenster ohne Terminal), endet es mit einem Satz in der Sprache der Forschung und dem, was
|
|
14
|
+
stattdessen zu tun ist, nicht mehr mit einem Fehler des Programms; eine Frage, deren Eingabe vor der Antwort endet,
|
|
15
|
+
beendet das Menü genauso.
|
|
16
|
+
- **`strom check` spricht eine Sprache.** Für einen Menschen stehen alle Befunde, die Summe und die Hinweise in der
|
|
17
|
+
Sprache der Forschung, für einen Agenten alles auf Englisch, nie beides gemischt.
|
|
18
|
+
- **Verweigert die Sandbox des Agenten git, wird der richtige Ort genannt.** Die Meldung nennt die Geschichte der
|
|
19
|
+
Forschung (ihren Ordner `.git`) als verweigerten Ort, nicht das Programm git.
|
|
20
|
+
|
|
8
21
|
## 1.19.0
|
|
9
22
|
|
|
10
23
|
*22.10.2026 · gehört zur Strom-App 3.15.0.*
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ English · [Čeština](CHANGELOG.cs.md) · [Deutsch](CHANGELOG.de.md)
|
|
|
5
5
|
The changes of each version in short. The full notes and the install lines are on
|
|
6
6
|
[GitHub Releases](https://github.com/ACiDekCZ/strom-research/releases).
|
|
7
7
|
|
|
8
|
+
## 1.20.0
|
|
9
|
+
|
|
10
|
+
*In preparation: the betas up to 1.20.0-beta.1. The section is complete with the release.*
|
|
11
|
+
|
|
12
|
+
- **The menu with nobody at a terminal says so at once.** Started where no one can answer (a closed input, a window
|
|
13
|
+
without a terminal), the menu ends with one sentence in the research's language and what to do instead,
|
|
14
|
+
no longer with an error of the program; a question whose input ends before an answer ends the menu the same way.
|
|
15
|
+
- **`strom check` speaks one language.** For a person every finding, the sum and the notes are in the research's
|
|
16
|
+
language; for an agent all of it is in English, never a mix of both.
|
|
17
|
+
- **When the agent's sandbox refuses git, the right place is named.** The message names the research's history (its
|
|
18
|
+
`.git` folder) as the place refused, not git's own program.
|
|
19
|
+
|
|
8
20
|
## 1.19.0
|
|
9
21
|
|
|
10
22
|
*2026-10-22 · goes with the Strom app 3.15.0.*
|
package/assets/lang/cs.json
CHANGED
|
@@ -2413,6 +2413,10 @@
|
|
|
2413
2413
|
"ui.error.record.none": "{id} v rodokmenu není",
|
|
2414
2414
|
"ui.error.record.merged": "{id} je sloučené do {into}",
|
|
2415
2415
|
"ui.error.setting.missing": "chybí nastavení: {keys}",
|
|
2416
|
+
"ui.error.menu.no-terminal": "menu je pro člověka u terminálu – tady žádný není",
|
|
2417
|
+
"ui.error.menu.no-terminal.hint": "otevřít terminál a spustit v něm strom",
|
|
2418
|
+
"ui.error.input.ended": "vstup skončil dřív, než přišla odpověď – není tu koho se zeptat",
|
|
2419
|
+
"ui.error.input.ended.hint": "spustit v terminálu",
|
|
2416
2420
|
"ui.error.conflict.needs-reasoning": "chybí --reasoning",
|
|
2417
2421
|
"ui.error.conflict.needs-reasoning.hint": "strom conflict resolve {id} … --reasoning \"<proč>\"",
|
|
2418
2422
|
"ui.error.sync.not-sync": "{input} není synchronizace",
|
package/assets/lang/de.json
CHANGED
|
@@ -2413,6 +2413,10 @@
|
|
|
2413
2413
|
"ui.error.record.none": "{id} gibt es im Stammbaum nicht",
|
|
2414
2414
|
"ui.error.record.merged": "{id} ist in {into} zusammengeführt",
|
|
2415
2415
|
"ui.error.setting.missing": "es fehlt die Einstellung: {keys}",
|
|
2416
|
+
"ui.error.menu.no-terminal": "das Menü ist für einen Menschen am Terminal – hier ist keiner",
|
|
2417
|
+
"ui.error.menu.no-terminal.hint": "ein Terminal öffnen und strom darin starten",
|
|
2418
|
+
"ui.error.input.ended": "die Eingabe endete vor einer Antwort – hier ist niemand zu fragen",
|
|
2419
|
+
"ui.error.input.ended.hint": "im Terminal starten",
|
|
2416
2420
|
"ui.error.conflict.needs-reasoning": "--reasoning fehlt",
|
|
2417
2421
|
"ui.error.conflict.needs-reasoning.hint": "strom conflict resolve {id} … --reasoning \"<warum>\"",
|
|
2418
2422
|
"ui.error.sync.not-sync": "{input} ist kein Abgleich",
|
package/dist/cli/context.js
CHANGED
|
@@ -8,7 +8,7 @@ import { Settings } from "../core/config.js";
|
|
|
8
8
|
import { configDir, displayPath, expandHome, ISOLATED_HOME } from "../core/paths.js";
|
|
9
9
|
import { backupsDir } from "../core/backup.js";
|
|
10
10
|
import { insideProgram } from "../core/self.js";
|
|
11
|
-
import { Cancelled, NeedsConsentError, NeedsInputError, StromError, UsageError } from "../core/errors.js";
|
|
11
|
+
import { Cancelled, InputEnded, NeedsConsentError, NeedsInputError, StromError, UsageError } from "../core/errors.js";
|
|
12
12
|
import { migrate } from "../core/migrate.js";
|
|
13
13
|
import { Tree, findTreeUpwards, isTreeDir, knownTreesIn } from "../core/tree.js";
|
|
14
14
|
import { SCHEMA_VERSION } from "../core/model.js";
|
|
@@ -101,8 +101,12 @@ export class Context {
|
|
|
101
101
|
}
|
|
102
102
|
else {
|
|
103
103
|
const rl = readline.createInterface({ input: this.io.stdin ?? process.stdin, output: process.stdout });
|
|
104
|
+
// the input ending before an answer (stdin closed, used up) settles the question too — never a question left
|
|
105
|
+
// open; a moment later, so Ctrl-C at a terminal (it closes the line first) stays the person's Ctrl-C
|
|
106
|
+
const ended = new Promise((_, reject) => rl.once("close", () => setImmediate(() => reject(new InputEnded()))));
|
|
107
|
+
ended.catch(() => undefined);
|
|
104
108
|
try {
|
|
105
|
-
answer = await rl.question(prompt);
|
|
109
|
+
answer = await Promise.race([rl.question(prompt), ended]);
|
|
106
110
|
}
|
|
107
111
|
catch (err) {
|
|
108
112
|
// Ctrl-C at the question (Node: "Aborted with Ctrl+C", found on Mac as the menu's "that did not work")
|
package/dist/cli/main.js
CHANGED
|
@@ -70,7 +70,8 @@ function systemError(e, agent) {
|
|
|
70
70
|
case "EACCES":
|
|
71
71
|
case "EPERM":
|
|
72
72
|
case "EROFS":
|
|
73
|
-
|
|
73
|
+
// git not let to start under an agent: said as the research's folder (underAgent)
|
|
74
|
+
return noPermission((agent && e.gitFolder) || e.path, agent);
|
|
74
75
|
case "EISDIR":
|
|
75
76
|
return new UsageError(`a folder was given where a file is expected${where}`, { code: "fs.folder", ...(e.path ? { params: { path: e.path } } : {}) });
|
|
76
77
|
case "ENOTDIR":
|
|
@@ -109,20 +110,23 @@ const REFUSALS = new Set(["fs.no-permission", "git.unwritable", "init.unwritable
|
|
|
109
110
|
* Codex's sandbox ran strom with none of its marks) is named to the person as the likely cause — not certain, so
|
|
110
111
|
* the other causes after it.
|
|
111
112
|
*/
|
|
112
|
-
function underAgent(e, env) {
|
|
113
|
+
function underAgent(e, env, gitFolder) {
|
|
113
114
|
if (!REFUSALS.has(e.code ?? ""))
|
|
114
115
|
return e;
|
|
116
|
+
// git not let to start under an agent: the research's folder is where (found on Windows: "…\Git\cmd\git.exe"),
|
|
117
|
+
// what the agent's sandbox is to let strom use; with no agent the program refused stays said (an antivirus)
|
|
118
|
+
const where = gitFolder ?? e.params?.where;
|
|
115
119
|
if (isAgent(env))
|
|
116
|
-
return noPermission(
|
|
120
|
+
return noPermission(where, env);
|
|
117
121
|
const above = agentInAncestry(env);
|
|
118
122
|
if (above)
|
|
119
|
-
return noPermission(
|
|
120
|
-
return ancestryUnread(env) ? noPermission(
|
|
123
|
+
return noPermission(where, false, process.platform, above);
|
|
124
|
+
return ancestryUnread(env) ? noPermission(where, false, process.platform, "?") : e;
|
|
121
125
|
}
|
|
122
126
|
function printError(io, json, err, debug, lang, env) {
|
|
123
127
|
const agent = isAgent(env) && env;
|
|
124
128
|
const sys = err instanceof StromError ? err : (systemError(err, agent) ?? err);
|
|
125
|
-
const e = sys instanceof StromError ? underAgent(sys, env) : sys;
|
|
129
|
+
const e = sys instanceof StromError ? underAgent(sys, env, err?.gitFolder) : sys;
|
|
126
130
|
if (e instanceof StromError) {
|
|
127
131
|
if (json)
|
|
128
132
|
io.stdout(toJson(e.toJSON()));
|
package/dist/cli/menu-parts.js
CHANGED
|
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import { agentHere } from "../core/state.js";
|
|
5
5
|
import { ui } from "./ui.js";
|
|
6
6
|
import { Tree } from "../core/tree.js";
|
|
7
|
-
import { Cancelled } from "../core/errors.js";
|
|
7
|
+
import { Cancelled, InputEnded } from "../core/errors.js";
|
|
8
8
|
import { findPersons, label } from "../core/people.js";
|
|
9
9
|
import { agentsHere } from "../core/apps.js";
|
|
10
10
|
/** The test (or a script) gave all its answers: nothing more is asked. */
|
|
@@ -144,6 +144,9 @@ export async function guarded(ctx, lang, act) {
|
|
|
144
144
|
// Ctrl-C at its question: stopped by the person, nothing changed — no failure, the menu goes on
|
|
145
145
|
if (err instanceof Cancelled)
|
|
146
146
|
return void ctx.io.stdout(ui(lang, "ui.cancelled") + "\n");
|
|
147
|
+
// nobody left to ask: the menu ends, said once
|
|
148
|
+
if (err instanceof InputEnded)
|
|
149
|
+
throw err;
|
|
147
150
|
ctx.io.stdout(ui(lang, "ui.menu.failed", { reason: err.message }) + "\n");
|
|
148
151
|
await pause(ctx, lang);
|
|
149
152
|
}
|
package/dist/cli/ui.js
CHANGED
|
@@ -2279,6 +2279,10 @@ export const UI = {
|
|
|
2279
2279
|
"ui.error.record.none": "{id} is not in the family tree",
|
|
2280
2280
|
"ui.error.record.merged": "{id} was merged into {into}",
|
|
2281
2281
|
"ui.error.setting.missing": "missing setting: {keys}",
|
|
2282
|
+
"ui.error.menu.no-terminal": "the menu is for a person at a terminal — none here",
|
|
2283
|
+
"ui.error.menu.no-terminal.hint": "open a terminal and run strom there",
|
|
2284
|
+
"ui.error.input.ended": "the input ended before an answer — nobody to ask here",
|
|
2285
|
+
"ui.error.input.ended.hint": "run it at a terminal",
|
|
2282
2286
|
"ui.error.conflict.needs-reasoning": "--reasoning is required",
|
|
2283
2287
|
"ui.error.conflict.needs-reasoning.hint": "strom conflict resolve {id} … --reasoning \"<why>\"",
|
|
2284
2288
|
"ui.error.sync.not-sync": "{input} is not a sync",
|
package/dist/commands/checks.js
CHANGED
|
@@ -62,10 +62,13 @@ register({
|
|
|
62
62
|
tree: true,
|
|
63
63
|
run(ctx) {
|
|
64
64
|
const tree = ctx.tree();
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
// one language from the first line to the last: a person reads it in the research's language (the findings a
|
|
66
|
+
// person may meet too); an agent reads it all in English, as it reads strom's errors — its findings say what to
|
|
67
|
+
// run (found on Mac: English findings above a Czech sum)
|
|
68
|
+
const agent = isAgent(ctx.env);
|
|
69
|
+
const lang = agent ? "en" : tree.lang;
|
|
70
|
+
const checked = report([...verifyFull(tree).findings, ...check(tree), ...guard(tree), ...unwritable(tree, agent)], ui(lang, "ui.check.ok"), lang, !agent);
|
|
71
|
+
// much kept beside the research that can go (an older strom never tidied it): a sentence, in that language —
|
|
69
72
|
// none where .strom cannot be listed (found on Windows: a folder denied to strom ended check with "no permission"
|
|
70
73
|
// instead of its finding; unwritable says it)
|
|
71
74
|
let disk;
|
|
@@ -76,14 +79,14 @@ register({
|
|
|
76
79
|
disk = undefined;
|
|
77
80
|
}
|
|
78
81
|
const out = disk && disk.frees >= TIDY_SAID
|
|
79
|
-
? { ...checked, text: lines(checked.text, ui(
|
|
82
|
+
? { ...checked, text: lines(checked.text, ui(lang, "ui.tidy.hint", { strom: mb(disk.size.strom, lang), size: mb(disk.frees, lang) })), data: { ...checked.data, disk: { strom: disk.size.strom, frees: disk.frees } } }
|
|
80
83
|
: checked;
|
|
81
84
|
// not an error — research to do: who rests on no record of their own
|
|
82
85
|
const unproven = unprovenPeople(tree).filter((u) => !u.living).length;
|
|
83
86
|
// an archive: what to research is no matter of it
|
|
84
87
|
if (!unproven || actsAsArchive(tree))
|
|
85
88
|
return out;
|
|
86
|
-
return { ...out, text: lines(out.text, ui(
|
|
89
|
+
return { ...out, text: lines(out.text, ui(lang, "ui.check.unproven", { n: unproven })), data: { ...out.data, unproven } };
|
|
87
90
|
},
|
|
88
91
|
}, {
|
|
89
92
|
path: ["verify"],
|
package/dist/commands/start.js
CHANGED
|
@@ -8,7 +8,7 @@ import { prependPath, spawnAgent } from "../runners/runner.js";
|
|
|
8
8
|
import { register } from "../cli/registry.js";
|
|
9
9
|
import { lines, numberIn, truncate } from "../cli/format.js";
|
|
10
10
|
import { ui } from "../cli/ui.js";
|
|
11
|
-
import { StromError, UsageError } from "../core/errors.js";
|
|
11
|
+
import { EXIT, StromError, UsageError } from "../core/errors.js";
|
|
12
12
|
import { PROFILES } from "../agents/profiles.js";
|
|
13
13
|
import { conversationArgs, conversationEnv } from "../agents/launch.js";
|
|
14
14
|
import { globalTargets, installGlobal, isInstalled, uninstallGlobal } from "../agents/global.js";
|
|
@@ -579,6 +579,10 @@ register({
|
|
|
579
579
|
run: async (ctx) => {
|
|
580
580
|
if (isAgent(ctx.env))
|
|
581
581
|
throw new UsageError("the menu is for a person at a terminal", { hint: "strom (orientation) — or strom --json" });
|
|
582
|
+
// nobody at a terminal to answer (stdin closed or not one, STROM_NONINTERACTIVE, --yes/--json): said at once —
|
|
583
|
+
// never a menu waiting for an answer that cannot come
|
|
584
|
+
if (!ctx.interactive)
|
|
585
|
+
throw new StromError("the menu is for a person at a terminal — none here", { exitCode: EXIT.needsInput, code: "menu.no-terminal", hint: "strom (orientation) — or strom --json" });
|
|
582
586
|
const { runMenu } = await import("../cli/menu.js");
|
|
583
587
|
const { main } = await import("../cli/main.js");
|
|
584
588
|
await runMenu(ctx, (argv, quiet) => main(argv, quiet ? { ...ctx.io, stdout: () => undefined } : ctx.io, ctx.env, ctx.cwd));
|
package/dist/core/errors.js
CHANGED
|
@@ -72,6 +72,17 @@ export class Cancelled extends StromError {
|
|
|
72
72
|
this.name = "Cancelled";
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* The input ended before an answer (stdin closed, a file or a pipe used up): nobody left to ask — said, never a
|
|
77
|
+
* process that hangs on its question (found on Mac: the menu with stdin closed ended in Node's "unsettled top-level
|
|
78
|
+
* await", exit 13).
|
|
79
|
+
*/
|
|
80
|
+
export class InputEnded extends StromError {
|
|
81
|
+
constructor() {
|
|
82
|
+
super("the input ended before an answer — nobody to ask here", { exitCode: EXIT.needsInput, code: "input.ended", hint: "run it at a terminal" });
|
|
83
|
+
this.name = "InputEnded";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
75
86
|
/** A name matched more than one record. Never pick one silently. */
|
|
76
87
|
export class AmbiguousError extends StromError {
|
|
77
88
|
candidates;
|
package/dist/core/git.js
CHANGED
|
@@ -72,6 +72,9 @@ export function runGit(cwd, args, input, extraEnv = {}) {
|
|
|
72
72
|
const where = fs.existsSync(dotGit) ? dotGit : cwd;
|
|
73
73
|
if (writeRefusal(where))
|
|
74
74
|
throw gitUnwritable(where, "");
|
|
75
|
+
// git itself not let to start (found on Windows: an agent's sandbox, git.exe refused): the research's folder
|
|
76
|
+
// kept with it — under an agent that is what its sandbox is to let strom use (cli/main.ts)
|
|
77
|
+
throw Object.assign(r.error, { gitFolder: where });
|
|
75
78
|
}
|
|
76
79
|
throw gitTimedOut(args, r.error) ?? r.error;
|
|
77
80
|
}
|
package/dist/core/tree.js
CHANGED
|
@@ -27,7 +27,7 @@ import { lastBackup } from "./backup.js";
|
|
|
27
27
|
export const TREE_FILE = "strom.json";
|
|
28
28
|
// between releases the candidate being made (a bridge run from the sources says what it is: the Strom app goes by it);
|
|
29
29
|
// at a release the release's own (package.json)
|
|
30
|
-
export const VERSION = "1.
|
|
30
|
+
export const VERSION = "1.20.0-beta.1";
|
|
31
31
|
const GITIGNORE = `# Strom Research — local state and large files are not versioned
|
|
32
32
|
.strom/
|
|
33
33
|
/media/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strom-research",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0-beta.1",
|
|
4
4
|
"description": "Genealogical research toolkit for AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -57,5 +57,8 @@
|
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@types/node": "^22.20.4",
|
|
59
59
|
"typescript": "^7.0.2"
|
|
60
|
+
},
|
|
61
|
+
"publishConfig": {
|
|
62
|
+
"tag": "beta"
|
|
60
63
|
}
|
|
61
64
|
}
|