strom-research 1.23.0 → 1.24.0-beta.2

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.
Files changed (48) hide show
  1. package/CHANGELOG.cs.md +31 -0
  2. package/CHANGELOG.de.md +35 -0
  3. package/CHANGELOG.md +34 -0
  4. package/assets/lang/cs.json +20 -5
  5. package/assets/lang/de.json +20 -5
  6. package/dist/agents/models.js +46 -1
  7. package/dist/brief/history.js +44 -8
  8. package/dist/cli/main.js +14 -4
  9. package/dist/cli/menu-links.js +35 -0
  10. package/dist/cli/ui.js +16 -5
  11. package/dist/commands/fixchecks.js +132 -8
  12. package/dist/commands/fixes.js +2 -31
  13. package/dist/commands/history.js +27 -1
  14. package/dist/commands/links.js +10 -1
  15. package/dist/commands/output.js +5 -2
  16. package/dist/commands/read.js +1 -1
  17. package/dist/commands/readers.js +7 -3
  18. package/dist/commands/session.js +3 -1
  19. package/dist/commands/setup.js +16 -1
  20. package/dist/commands/sources.js +1 -1
  21. package/dist/commands/sync.js +6 -0
  22. package/dist/core/age.js +2 -1
  23. package/dist/core/changelog.js +13 -0
  24. package/dist/core/excerpt.js +2 -1
  25. package/dist/core/git.js +8 -0
  26. package/dist/core/import.js +11 -2
  27. package/dist/core/integrity.js +5 -0
  28. package/dist/core/lineages.js +147 -0
  29. package/dist/core/links.js +21 -2
  30. package/dist/core/live.js +53 -13
  31. package/dist/core/pastevents.js +18 -2
  32. package/dist/core/phrases.js +4 -0
  33. package/dist/core/placeadopt.js +288 -0
  34. package/dist/core/scanlink.js +60 -0
  35. package/dist/core/stromapp.js +20 -0
  36. package/dist/core/sync.js +43 -1
  37. package/dist/core/tree.js +22 -1
  38. package/dist/core/update.js +26 -2
  39. package/dist/gedcom/export.js +29 -6
  40. package/dist/gedcom/validate.js +7 -4
  41. package/dist/runners/antigravity.js +14 -11
  42. package/dist/runners/claude.js +2 -2
  43. package/dist/runners/codex.js +69 -61
  44. package/dist/runners/grok.js +2 -2
  45. package/dist/runners/opencode.js +2 -2
  46. package/dist/runners/room.js +112 -0
  47. package/dist/runners/runner.js +7 -0
  48. package/package.json +5 -2
package/CHANGELOG.cs.md CHANGED
@@ -5,6 +5,37 @@
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.24.0
9
+
10
+ *Připravuje se: bety do 1.24.0-beta.2. Oddíl bude úplný s vydáním.*
11
+
12
+ - **Místa zmapovaná jednou pro všechny výzkumy na tomto počítači.** Místo, které jiný výzkum tady už přiřadil k regionům
13
+ dějin, se převezme, když jde jistě o totéž místo (stejný název a stejné souřadnice, farnost nebo okres) a všechny
14
+ výzkumy ho přiřazují stejně; přechází jen přiřazení místa, nic z rodiny. Místo stejného jména, které jistě totéž
15
+ není, dostane agent jako návrh; sám přiřazuje jen zbytek. `strom history adopt --dry-run` řekne, co by se převzalo.
16
+ - **Opravy výzkumu pro aplikaci Strom.** Most vypíše, co `strom fixes` ve výzkumu nabízí (název, co dělá, kolik
17
+ položek, stav, jestli jde vrátit), zdarma a bez zápisu; odkaz `strom-research://fixes` otevře opravu v terminálu
18
+ výzkumu, kde se oprava i její vrácení vždy nejdřív potvrzuje.
19
+ - **Stránka každého skenu v aplikaci Strom.** Soubor pro Strom dává teď každému prameni stránku jeho skenu v online
20
+ archivu: stránku jeho prvního obrázku, jinak vlastní adresu pramene. Aplikace Strom ji nabídne u výřezů, které soubor
21
+ vynechává, a u pramenů bez výřezu. Jen veřejná webová stránka, nikdy adresa fondu; standardní soubor GEDCOM nechává
22
+ adresu v poznámce. Datum verze výzkumu v souboru pro Strom nese i čas, takže aplikace ukáže den, kdy vznikla, i kolem
23
+ půlnoci.
24
+ - **Číslo domu, věk a období tak, jak byly zapsány.** Slovo pro číslo domu („No.“, „čp.“) se dává jen před samotné číslo;
25
+ ulice s číslem jde do souboru tak, jak je. Věk „05y“ se čte jako 5 let. Obojí se z aplikace Strom vrací stejné,
26
+ není to úprava uživatele. Období, které vede aplikace Strom („1871/1882“, nebo otevřené na jednom konci), přijde do
27
+ výzkumu jako od … do …, nikdy jako datum někde mezi nimi.
28
+ - **Otázka na dějiny říká, kde platí.** Zapnutí nebo vypnutí událostí dějin platí pro všechny výzkumy na tomto
29
+ počítači; otázka to říká, i jak to změnit jen pro jeden výzkum.
30
+ - **Slabý nebo odmítnutý model agenta hlásí doctor.** Když výzkum nemá zvolený model a vlastní výchozí model agenta je
31
+ ten, který výzkum shledal pro svou práci slabým nebo odmítnutým, `strom doctor` varuje a dá příkaz k volbě; sám
32
+ nikdy nevolí.
33
+ - **strom z npm drží svůj kanál.** strom nainstalovaný přes npm zůstává na kanálu, ze kterého byl nainstalován, i po
34
+ vydání; kanál zvolený přes `strom update --channel` se pro něj také pamatuje.
35
+ - **Příkaz, který selže, vrátí jen své.** Příkaz, který cestou uložil a pak selhal, vrátí jen to, co přišlo po jeho
36
+ uložení, takže výzkum nikdy nevypadá změněný mimo strom. Když se zápis odmítne, protože se výzkum změnil mimo strom,
37
+ člověk to čte v jazyce výzkumu i s cestou, která sedí; agent to dál čte anglicky.
38
+
8
39
  ## 1.23.0
9
40
 
10
41
  *30. 10. 2026 · patří k aplikaci Strom 3.18.0.*
package/CHANGELOG.de.md CHANGED
@@ -5,6 +5,41 @@
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.24.0
9
+
10
+ *In Vorbereitung: die Betas bis 1.24.0-beta.2. Der Abschnitt ist mit dem Release vollständig.*
11
+
12
+ - **Orte einmal zugeordnet für jede Forschung auf diesem Computer.** Ein Ort, den eine andere Forschung hier schon den
13
+ Regionen der Geschichte zugeordnet hat, wird übernommen, wenn es sicher derselbe Ort ist (derselbe Name und dieselben
14
+ Koordinaten, Pfarrei oder derselbe Bezirk) und jede Forschung ihn gleich zuordnet; nur die Zuordnung des Orts geht
15
+ über, nichts von der Familie. Ein Ort gleichen Namens, der nicht sicher derselbe ist, geht als Vorschlag an den
16
+ Agenten; er ordnet nur den Rest zu. `strom history adopt --dry-run` sagt, was übernommen würde.
17
+ - **Die Korrekturen einer Forschung für die Strom-App.** Die Brücke listet, was `strom fixes` in der Forschung anbietet
18
+ (Titel, was sie tut, wie viele Einträge, ihr Stand, ob sie sich zurücknehmen lässt), kostenlos und ohne etwas zu
19
+ schreiben; der Link `strom-research://fixes` öffnet eine Korrektur im Terminal der Forschung, wo sie und ihre
20
+ Rücknahme immer zuerst bestätigt werden.
21
+ - **Die Seite jedes Scans in der Strom-App.** Die Strom-Datei gibt jetzt jeder Quelle die Seite ihres Scans im
22
+ Online-Archiv: die Seite ihres ersten Bildes, sonst die eigene Adresse der Quelle. Die Strom-App verlinkt sie bei den
23
+ Ausschnitten, die die Datei auslässt, und bei Quellen ohne Ausschnitt. Nur eine öffentliche Webseite, nie die Adresse
24
+ eines Bestands; die Standard-GEDCOM-Datei behält die Adresse in der Notiz. Das Datum der Version der
25
+ Forschung in der Strom-Datei trägt auch die Uhrzeit, so zeigt die App den Tag ihrer Entstehung auch um Mitternacht.
26
+ - **Hausnummer, Alter und Zeitraum, wie sie geschrieben wurden.** Das Wort für eine Hausnummer („No.“, „čp.“) steht nur vor
27
+ einer Zahl allein; eine Straße mit Nummer geht so in die Datei, wie sie ist. Ein Alter „05y“ gilt als 5 Jahre.
28
+ Beides kommt aus der Strom-App gleich zurück, keine Änderung des Benutzers. Ein Zeitraum der Strom-App
29
+ („1871/1882“ oder an einem Ende offen) kommt als von … bis … in die Forschung, nie als ein Datum irgendwo dazwischen.
30
+ - **Die Frage nach der Geschichte sagt, wo sie gilt.** Die Ereignisse der Geschichte ein- oder auszuschalten gilt für
31
+ jede Forschung auf diesem Computer; die Frage sagt es, und wie es sich für eine Forschung allein ändern lässt.
32
+ - **Ein schwaches oder abgelehntes Modell des Agenten meldet der Doctor.** Ist für die Forschung kein Modell gewählt und
33
+ das eigene Standardmodell des Agenten eines, das die Forschung für ihre Arbeit schwach oder abgelehnt fand, warnt
34
+ `strom doctor` und nennt den Befehl zur Wahl; selbst wählt er nie.
35
+ - **strom aus npm behält seinen Kanal.** Mit npm installiertes strom bleibt auf dem Kanal, aus dem es installiert
36
+ wurde, auch nach einer Veröffentlichung; ein mit `strom update --channel` gewählter Kanal bleibt dafür ebenfalls
37
+ gemerkt.
38
+ - **Ein gescheiterter Befehl nimmt nur das Seine zurück.** Ein Befehl, der unterwegs gespeichert hat und dann scheitert,
39
+ nimmt nur zurück, was nach dieser Speicherung kam, so wirkt die Forschung nie außerhalb von strom geändert. Wird das
40
+ Schreiben abgelehnt, weil die Forschung außerhalb von strom geändert wurde, liest die Person das in der Sprache der
41
+ Forschung, mit dem passenden Weg; ein Agent liest es weiter auf Englisch.
42
+
8
43
  ## 1.23.0
9
44
 
10
45
  *30.10.2026 · gehört zur Strom-App 3.18.0.*
package/CHANGELOG.md CHANGED
@@ -5,6 +5,40 @@ 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.24.0
9
+
10
+ *In preparation: the betas up to 1.24.0-beta.2. The section is complete with the release.*
11
+
12
+ - **Places mapped once for every research on this computer.** A place another research here has mapped to its
13
+ regions of history is taken over when it is surely the same place (the same name and the same coordinates, parish
14
+ or district) and every research maps it alike; only the place's map crosses, nothing of the family. A place of the
15
+ same name that is not surely the same is offered to the agent as a suggestion; the agent maps only the rest.
16
+ `strom history adopt --dry-run` says what would be taken.
17
+ - **The fixes of a research for the Strom app.** The bridge lists what `strom fixes` offers in the research (title,
18
+ what it does, how many items, its state, whether it can be put back), free and without writing anything; the link
19
+ `strom-research://fixes` opens a fix in the research's terminal, where setting it right or putting it back is
20
+ always asked first.
21
+ - **The page of each scan in the Strom app.** The Strom file now gives each source the page of its scan in the online
22
+ archive: the page of its first image, else the source's own address. The Strom app links it for the excerpts the
23
+ file leaves out and for the sources without an excerpt. Only a public web page, never a recordset's address; the
24
+ standard GEDCOM file keeps the address in the note. The date of the research's version in the Strom file carries its
25
+ time too, so the app shows the day it was made also around midnight.
26
+ - **A house number, an age and a period as they were written.** The word for a house number ("No.", "čp.") is put only before
27
+ a number standing alone; a street with its number goes into the file as it is. An age of "05y" is read as 5 years.
28
+ Both come back from the Strom app the same, no edit of the user's. A period the Strom app keeps ("1871/1882", or
29
+ open at one end) comes into the research as from … to …, never as a date somewhere between.
30
+ - **The question about history says where it holds.** Turning the events of history on or off holds for every
31
+ research on this computer; the question says so, and how to change it for one research alone.
32
+ - **A weak or refused model of the agent said by the doctor.** When no model is chosen for the research and the
33
+ agent's own default is one the research found weak or refused for its work, `strom doctor` warns and gives the
34
+ command to choose one; it never chooses itself.
35
+ - **strom from npm keeps its channel.** strom installed with npm stays on the channel it was installed from, also
36
+ after a release; a channel chosen with `strom update --channel` is kept for it too.
37
+ - **A failed command takes back only its own.** A command that saved on the way and then failed takes back only what
38
+ came after its save, so the research never looks changed outside strom. Where writing is refused because the
39
+ research was changed outside strom, the person reads it in the research's language, with the way out that fits; an
40
+ agent still reads it in English.
41
+
8
42
  ## 1.23.0
9
43
 
10
44
  *2026-10-30 · goes with the Strom app 3.18.0.*
@@ -581,7 +581,7 @@
581
581
  "ui.timing": "Jak dlouho trvaly jednotlivé kroky (řádek k odeslání, když je strom pomalý):",
582
582
  "ui.doc.place": "kde výzkum leží",
583
583
  "ui.doc.place.cloud": "{where}: v {what} – každý soubor, do kterého strom nahlédne, může jít nejdřív po síti",
584
- "ui.doc.place.online": "z {looked} prohlédnutých souborů je {online} jen online (každý se při otevření stáhne celý)",
584
+ "ui.doc.place.online": "z {looked, plural, one {# prohlédnutého souboru} other {# prohlédnutých souborů}} {online, plural, one {je # jen online} few {jsou # jen online} other {je # jen online}} (každý se při otevření stáhne celý)",
585
585
  "ui.doc.place.network": "{where}: na síťovém disku ({what}) – každý soubor, do kterého strom nahlédne, se čte po síti, u tisíců skenů pomalu",
586
586
  "ui.doc.place.cloud.fix": "složku nechat vždy v tomto zařízení (OneDrive: v nabídce složky „Vždy zachovat na tomto zařízení“), nebo výzkum přesunout na disk tohoto počítače (strom setup v terminálu ho přesune); strom sám nic nestahuje",
587
587
  "ui.doc.place.network.fix": "výzkum přesunout na disk tohoto počítače (strom setup v terminálu ho přesune)",
@@ -604,6 +604,8 @@
604
604
  "ui.doc.knows": "agenti znají strom",
605
605
  "ui.doc.level": "agent smí",
606
606
  "ui.doc.model": "model",
607
+ "ui.doc.model.own.weak": "model pro výzkum není zvolen: {agent} pracuje se svým, {model} – rychlým či malým, na staré rukopisy slabým",
608
+ "ui.doc.model.own.refused": "model pro výzkum není zvolen: {agent} pracuje se svým, {model}, který v jeho seznamu pro tento účet není – odmítl by ho",
607
609
  "ui.doc.shortcut": "zástupce na ploše",
608
610
  "ui.doc.links": "odkazy z aplikace Strom",
609
611
  "ui.doc.links.ours": "ano",
@@ -754,6 +756,10 @@
754
756
  "log.surname.reject": "Tvary příjmení nejsou jedné rodiny: {forms}",
755
757
  "log.surname.unreject": "Tvary příjmení se smějí znovu navrhnout: {forms}",
756
758
  "log.surname.undo": "Tvary příjmení vráceny, jak byly",
759
+ "log.lineage.founder": "Zakládá vlastní rod: {who}",
760
+ "log.lineage.nocarry": "Rod nenese: {who}",
761
+ "log.lineage.none": "Rod zase podle výpočtu: {who}",
762
+ "log.lineage.undo": "Rod vrácen, jak byl: {who}",
757
763
  "log.participant.fix": "Jména kmotrů a svědků oddělena od poznámek: {n}",
758
764
  "log.participant.unfix": "Jména kmotrů a svědků vrácena, jak byla: {n}",
759
765
  "fix.appshape.resolution": "Platí údaj výzkumu: {value}",
@@ -1396,6 +1402,10 @@
1396
1402
  "ui.sync.do.surname": "zapíše se, jak bylo rozhodnuto v aplikaci",
1397
1403
  "ui.sync.do.surname.kept": "zůstává, jak je ve výzkumu",
1398
1404
  "ui.sync.do.surname.report": "nic se nemění",
1405
+ "ui.sync.lineage.founder": "{n}. Rod: {name} zakládá vlastní rod",
1406
+ "ui.sync.lineage.nocarry": "{n}. Rod: {name} rod nenese",
1407
+ "ui.sync.lineage.none": "{n}. Rod: {name} zase podle výpočtu",
1408
+ "ui.sync.do.lineage": "zapíše se, jak bylo nastaveno v aplikaci",
1399
1409
  "ui.sync.do.part": "doplní se k údaji s odkazem na strom z aplikace",
1400
1410
  "ui.sync.do.part.user": "platí úprava z aplikace (verze výzkumu zůstane v historii)",
1401
1411
  "ui.sync.do.person.cite": "bude ho citovat jméno osoby (vodítko, dokud výzkum záznam nepřečte)",
@@ -1755,6 +1765,10 @@
1755
1765
  "ui.check.streetplaces": "{n, plural, one {# údaj má} few {# údaje mají} other {# údajů má}} v místě ulici nebo číslo domu",
1756
1766
  "ui.error.tree.not-downloaded": "z iCloudu ještě nestaženo: {n, plural, one {# soubor} few {# soubory} other {# souborů}} výzkumu (první: {first}) – nic se nezapsalo",
1757
1767
  "ui.error.tree.not-downloaded.hint": "otevřít složku výzkumu ve Finderu a soubory stáhnout (nebo složku z iCloud Drive vyjmout), pak spustit znovu – ne strom repair",
1768
+ "ui.error.tree.changed-outside": "data výzkumu se změnila mimo strom ({n, plural, one {# problém} few {# problémy} other {# problémů}}): nic se nezapsalo",
1769
+ "ui.error.tree.changed-outside.hint": "strom verify (podrobnosti)\nstrom repair (vrátí poslední zapečetěný stav)",
1770
+ "ui.error.tree.changed-outside.keys.hint": "strom verify (podrobnosti)\nstrom repair, pak strom seal adopt (vrátí jen to, co se udělalo mimo strom – práce obou klíčů tohoto počítače zůstane)",
1771
+ "ui.error.tree.changed-outside.terminal.hint": "strom verify (podrobnosti)\nve vlastním terminálu: strom repair, pak strom seal adopt – ten vidí oba klíče a vrátí jen to, co se udělalo mimo strom",
1758
1772
  "ui.export.file": "{file} ({for}) — {persons, plural, one {# osoba} few {# osoby} other {# osob}} · {families, plural, one {# rodina} few {# rodiny} other {# rodin}} · {facts, plural, one {# údaj} few {# údaje} other {# údajů}} · {sources, plural, one {# pramen} few {# prameny} other {# pramenů}}{skipped}",
1759
1773
  "ui.export.for.strom": "pro aplikaci Strom",
1760
1774
  "ui.export.for.strom.images": "pro aplikaci Strom, s obrázky",
@@ -2829,7 +2843,7 @@
2829
2843
  "ui.history.pace.default": " (výchozí)",
2830
2844
  "ui.history.pace.later": "Jak často, jde později změnit: strom config set experiments.historyPace sometimes|rare|idle.",
2831
2845
  "ui.history.pace.off": "vypnuto – dějiny se nehledají",
2832
- "ui.setup.history": "Hledat i události dějin v místech předků – války, epidemie, sčítání, změny státu (agent je hledá mezi sezeními rodinného výzkumu)? Jak často:",
2846
+ "ui.setup.history": "Hledat i události dějin v místech předků – války, epidemie, sčítání, změny státu (agent je hledá mezi sezeními rodinného výzkumu)? Platí pro všechny výzkumy na tomto počítači. Jak často:",
2833
2847
  "ui.alone.history": "Dějiny: {state}",
2834
2848
  "ui.alone.places": "Dějiny jednotlivých míst automaticky: {state}",
2835
2849
  "ui.alone.places.about": "Dějiny každého místa se souřadnicemi a jeho okolí – požáry, povodně, bitvy poblíž, panství, farnost, škola – se hledají samy, jedno místo po druhém v tempu dějin, nejdřív místa, kde žilo nejvíc lidí. Jednou prohledané místo se znovu nehledá. Jedno místo se hledá na požádání vždy, i když je tohle vypnuté.",
@@ -2839,10 +2853,10 @@
2839
2853
  "ui.alone.regions.tree": "Dějiny regionů v tomto výzkumu: {state}",
2840
2854
  "ui.alone.placeHistory.tree": "Dějiny míst v tomto výzkumu: {state}",
2841
2855
  "ui.alone.tree.about": "Jen pro tento výzkum, přednost před nastavením tohoto počítače. Co se našlo, zůstává.",
2842
- "ui.history.offer": "Novinka této verze: události dějin v místech předků – války, epidemie, sčítání, změny státu, každá se zdrojem; aplikace Strom je ukáže v průběhu života. Zatím se nehledají a samo se nic nezapne.",
2856
+ "ui.history.offer": "Novinka této verze: události dějin v místech předků – války, epidemie, sčítání, změny státu, každá se zdrojem; aplikace Strom je ukáže v průběhu života. Zatím se nehledají a samo se nic nezapne. Volba platí pro všechny výzkumy na tomto počítači.",
2843
2857
  "ui.history.offer.no": "teď ne – nic se nezapne",
2844
- "ui.history.offer.later": "Dějiny se nehledají. Později: Nastavení → Samostatná práce agenta → Dějiny, nebo z aplikace Strom.",
2845
- "ui.history.offer.on": "Dějiny se hledají: {pace}. Jak často, jde změnit: Nastavení → Samostatná práce agenta → Dějiny, nebo z aplikace Strom.",
2858
+ "ui.history.offer.later": "Dějiny se nehledají. Později: Nastavení → Samostatná práce agenta → Dějiny, nebo z aplikace Strom. Jen pro jeden výzkum: Nastavení → Samostatná práce agenta → Dějiny regionů v tomto výzkumu (v menu toho výzkumu), nebo strom config set experiments.history on --for-tree.",
2859
+ "ui.history.offer.on": "Dějiny se hledají ve všech výzkumech na tomto počítači: {pace}. Jak často, jde změnit: Nastavení → Samostatná práce agenta → Dějiny, nebo z aplikace Strom. Jen pro jeden výzkum: Nastavení → Samostatná práce agenta → Dějiny regionů v tomto výzkumu (v menu toho výzkumu), nebo strom config set experiments.history off --for-tree.",
2846
2860
  "ui.consent.history.pace": "Hledat události dějin častěji – {pace}? Stojí to víc času agenta i čerpání předplatného.",
2847
2861
  "ui.consent.history.tree": "Hledat události dějin regionů v místech tohoto výzkumu – války, epidemie, sčítání, změny státu? Agent je hledá průběžně mezi sezeními rodinného výzkumu a stojí to jeho čas i čerpání předplatného.",
2848
2862
  "ui.consent.placeHistory": "Hledat na vyžádání dějiny místa a jeho okolí ve všech výzkumech na tomto počítači – požáry, povodně, bitvy poblíž, panství, farnost, škola? Každé vyžádané místo je jedno sezení agenta; stojí to jeho čas i čerpání předplatného.",
@@ -2871,6 +2885,7 @@
2871
2885
  "ui.link.experiment.archive": "Dokud je výzkum vedený jako archiv, události dějin se nehledají. Nic se nestalo.",
2872
2886
  "ui.link.experiment.waiting": "Na tomto výzkumu se na tomto počítači nepracuje, a tak se události dějin nehledají. Nic se nestalo.",
2873
2887
  "ui.link.what.history.place": "hledat dějiny místa {place} a okolí – požáry, povodně, bitvy poblíž, panství, farnost, škola",
2888
+ "ui.link.what.fixes.undo": "vrátit, co oprava „{name}“ opravila",
2874
2889
  "ui.link.history.place.minutes": "Jedno sezení agenta, zhruba {from}–{to} minut; stojí to čas agenta i čerpání předplatného.",
2875
2890
  "ui.link.history.place.usd": "Taková sezení tu zatím stála {from}–{to}.",
2876
2891
  "ui.link.history.place.done": "Dějiny místa {place} a okolí jsou ve frontě první.",
@@ -581,7 +581,7 @@
581
581
  "ui.timing": "Wie lange die einzelnen Schritte dauerten (die Zeile zum Senden, wenn strom langsam ist):",
582
582
  "ui.doc.place": "wo die Forschung liegt",
583
583
  "ui.doc.place.cloud": "{where}: in {what} – jede Datei, in die strom schaut, kann zuerst über das Netz kommen",
584
- "ui.doc.place.online": "von {looked} angesehenen Dateien sind {online} nur online (jede wird beim Öffnen ganz heruntergeladen)",
584
+ "ui.doc.place.online": "von {looked, plural, one {# angesehenen Datei} other {# angesehenen Dateien}} {online, plural, one {ist # nur online} other {sind # nur online}} (jede wird beim Öffnen ganz heruntergeladen)",
585
585
  "ui.doc.place.network": "{where}: auf einem Netzlaufwerk ({what}) – jede Datei, in die strom schaut, wird über das Netz gelesen, bei Tausenden Scans langsam",
586
586
  "ui.doc.place.cloud.fix": "den Ordner immer auf diesem Gerät behalten (OneDrive: im Menü des Ordners „Immer auf diesem Gerät beibehalten“), oder die Forschung auf eine Platte dieses Computers verschieben (strom setup im Terminal verschiebt sie mit); strom selbst lädt nichts herunter",
587
587
  "ui.doc.place.network.fix": "die Forschung auf eine Platte dieses Computers verschieben (strom setup im Terminal verschiebt sie mit)",
@@ -604,6 +604,8 @@
604
604
  "ui.doc.knows": "Agenten kennen strom",
605
605
  "ui.doc.level": "der Agent darf",
606
606
  "ui.doc.model": "Modell",
607
+ "ui.doc.model.own.weak": "für die Forschung ist kein Modell gewählt: {agent} arbeitet mit dem eigenen, {model} – einem schnellen oder kleinen, für alte Handschriften zu schwach",
608
+ "ui.doc.model.own.refused": "für die Forschung ist kein Modell gewählt: {agent} arbeitet mit dem eigenen, {model}, das in seiner Liste für dieses Konto fehlt – es würde abgelehnt",
607
609
  "ui.doc.shortcut": "Desktop-Verknüpfung",
608
610
  "ui.doc.links": "Links aus der Strom-App",
609
611
  "ui.doc.links.ours": "ja",
@@ -754,6 +756,10 @@
754
756
  "log.surname.reject": "Namensformen keiner gemeinsamen Familie: {forms}",
755
757
  "log.surname.unreject": "Namensformen dürfen wieder vorgeschlagen werden: {forms}",
756
758
  "log.surname.undo": "Namensformen wiederhergestellt, wie sie waren",
759
+ "log.lineage.founder": "Begründet einen eigenen Stamm: {who}",
760
+ "log.lineage.nocarry": "Trägt keinen Stamm: {who}",
761
+ "log.lineage.none": "Stamm wieder wie berechnet: {who}",
762
+ "log.lineage.undo": "Stamm zurückgesetzt, wie er war: {who}",
757
763
  "log.participant.fix": "Namen von Paten und Zeugen von ihren Notizen getrennt: {n}",
758
764
  "log.participant.unfix": "Namen von Paten und Zeugen wiederhergestellt, wie sie waren: {n}",
759
765
  "fix.appshape.resolution": "Der Wert der Forschung bleibt: {value}",
@@ -1396,6 +1402,10 @@
1396
1402
  "ui.sync.do.surname": "übernommen, wie in der App entschieden",
1397
1403
  "ui.sync.do.surname.kept": "bleibt, wie die Forschung es hat",
1398
1404
  "ui.sync.do.surname.report": "nichts ändert sich",
1405
+ "ui.sync.lineage.founder": "{n}. Stamm: {name} begründet einen eigenen Stamm",
1406
+ "ui.sync.lineage.nocarry": "{n}. Stamm: {name} trägt keinen Stamm",
1407
+ "ui.sync.lineage.none": "{n}. Stamm: {name} wieder wie berechnet",
1408
+ "ui.sync.do.lineage": "übernommen, wie in der App festgelegt",
1399
1409
  "ui.sync.do.part": "wird der Angabe hinzugefügt, mit Verweis auf den eigenen Stammbaum",
1400
1410
  "ui.sync.do.part.user": "Die eigene Änderung gilt (die der Forschung bleibt in ihrer Geschichte)",
1401
1411
  "ui.sync.do.person.cite": "der Name der Person zitiert sie (ein Hinweis, bis die Forschung den Eintrag liest)",
@@ -1755,6 +1765,10 @@
1755
1765
  "ui.check.streetplaces": "{n, plural, one {# Angabe hat} other {# Angaben haben}} eine Straße oder eine Nummer im Ort",
1756
1766
  "ui.error.tree.not-downloaded": "noch nicht aus iCloud geladen: {n, plural, one {# Datei} other {# Dateien}} der Forschung (die erste: {first}) – nichts wurde geschrieben",
1757
1767
  "ui.error.tree.not-downloaded.hint": "den Ordner der Forschung im Finder öffnen und die Dateien laden (oder den Ordner aus iCloud Drive herausnehmen), dann erneut ausführen – nicht strom repair",
1768
+ "ui.error.tree.changed-outside": "die Daten der Forschung wurden außerhalb von strom geändert ({n, plural, one {# Problem} other {# Probleme}}): nichts wurde geschrieben",
1769
+ "ui.error.tree.changed-outside.hint": "strom verify (Einzelheiten)\nstrom repair (stellt den letzten versiegelten Stand wieder her)",
1770
+ "ui.error.tree.changed-outside.keys.hint": "strom verify (Einzelheiten)\nstrom repair, dann strom seal adopt (macht nur rückgängig, was außerhalb von strom geschah – die Arbeit beider Schlüssel dieses Computers bleibt)",
1771
+ "ui.error.tree.changed-outside.terminal.hint": "strom verify (Einzelheiten)\nin einem eigenen Terminal: strom repair, dann strom seal adopt – es sieht beide Schlüssel und macht nur rückgängig, was außerhalb von strom geschah",
1758
1772
  "ui.export.file": "{file} ({for}) — {persons, plural, one {# Person} other {# Personen}} · {families, plural, one {# Familie} other {# Familien}} · {facts, plural, one {# Angabe} other {# Angaben}} · {sources, plural, one {# Quelle} other {# Quellen}}{skipped}",
1759
1773
  "ui.export.for.strom": "für die Strom-App",
1760
1774
  "ui.export.for.strom.images": "für die Strom-App, mit Bildern",
@@ -2829,7 +2843,7 @@
2829
2843
  "ui.history.pace.default": " (Standard)",
2830
2844
  "ui.history.pace.later": "Wie oft, lässt sich später ändern: strom config set experiments.historyPace sometimes|rare|idle.",
2831
2845
  "ui.history.pace.off": "aus – keine Geschichte gesucht",
2832
- "ui.setup.history": "Auch die Ereignisse der Geschichte an den Orten der Vorfahren suchen – Kriege, Epidemien, Volkszählungen, Änderungen des Staates (der Agent sucht sie zwischen den Sitzungen der Familienforschung)? Wie oft:",
2846
+ "ui.setup.history": "Auch die Ereignisse der Geschichte an den Orten der Vorfahren suchen – Kriege, Epidemien, Volkszählungen, Änderungen des Staates (der Agent sucht sie zwischen den Sitzungen der Familienforschung)? Gilt für alle Forschungen auf diesem Computer. Wie oft:",
2833
2847
  "ui.alone.history": "Geschichte: {state}",
2834
2848
  "ui.alone.places": "Geschichte der einzelnen Orte automatisch: {state}",
2835
2849
  "ui.alone.places.about": "Die Geschichte jedes Ortes mit Koordinaten und seiner Umgebung – Brände, Hochwasser, Schlachten in der Nähe, Herrschaft, Pfarrei, Schule – wird von selbst gesucht, ein Ort nach dem anderen im Tempo der Geschichte, zuerst die Orte mit den meisten Menschen. Ein einmal durchsuchter Ort wird nicht wieder gesucht. Ein einzelner Ort wird auf Wunsch immer gesucht, auch wenn dies aus ist.",
@@ -2839,10 +2853,10 @@
2839
2853
  "ui.alone.regions.tree": "Geschichte der Regionen in dieser Forschung: {state}",
2840
2854
  "ui.alone.placeHistory.tree": "Geschichte der Orte in dieser Forschung: {state}",
2841
2855
  "ui.alone.tree.about": "Nur für diese Forschung, vor den Einstellungen dieses Computers. Was gefunden wurde, bleibt.",
2842
- "ui.history.offer": "Neu in dieser Version: die Ereignisse der Geschichte an den Orten der Vorfahren – Kriege, Epidemien, Volkszählungen, Änderungen des Staates, jedes mit seiner Quelle; die Strom-App zeigt sie im Lebenslauf. Noch nicht gesucht, nichts schaltet sich von selbst ein.",
2856
+ "ui.history.offer": "Neu in dieser Version: die Ereignisse der Geschichte an den Orten der Vorfahren – Kriege, Epidemien, Volkszählungen, Änderungen des Staates, jedes mit seiner Quelle; die Strom-App zeigt sie im Lebenslauf. Noch nicht gesucht, nichts schaltet sich von selbst ein. Die Wahl gilt für alle Forschungen auf diesem Computer.",
2843
2857
  "ui.history.offer.no": "jetzt nicht – nichts wird eingeschaltet",
2844
- "ui.history.offer.later": "Nicht gesucht. Später: Einstellungen → Der Agent allein → Geschichte, oder aus der Strom-App.",
2845
- "ui.history.offer.on": "Geschichte wird gesucht: {pace}. Wie oft, lässt sich ändern: Einstellungen → Der Agent allein → Geschichte, oder aus der Strom-App.",
2858
+ "ui.history.offer.later": "Nicht gesucht. Später: Einstellungen → Der Agent allein → Geschichte, oder aus der Strom-App. Nur für eine Forschung: Einstellungen → Der Agent allein → Geschichte der Regionen in dieser Forschung (im Menü dieser Forschung), oder strom config set experiments.history on --for-tree.",
2859
+ "ui.history.offer.on": "Geschichte wird in allen Forschungen auf diesem Computer gesucht: {pace}. Wie oft, lässt sich ändern: Einstellungen → Der Agent allein → Geschichte, oder aus der Strom-App. Nur für eine Forschung: Einstellungen → Der Agent allein → Geschichte der Regionen in dieser Forschung (im Menü dieser Forschung), oder strom config set experiments.history off --for-tree.",
2846
2860
  "ui.consent.history.pace": "Öfter nach den Ereignissen der Geschichte suchen – {pace}? Das kostet mehr Zeit des Agenten und mehr von der Nutzung des Abos.",
2847
2861
  "ui.consent.history.tree": "Die Ereignisse der Geschichte der Regionen an den Orten dieser Forschung suchen – Kriege, Epidemien, Volkszählungen, Änderungen des Staates? Der Agent sucht sie ab und zu zwischen den Sitzungen der Familienforschung, und es kostet seine Zeit und die Nutzung des Abos.",
2848
2862
  "ui.consent.placeHistory": "Die Geschichte eines Ortes und seiner Umgebung auf Anfrage suchen, in allen Forschungen auf diesem Computer – Brände, Hochwasser, Schlachten in der Nähe, Herrschaft, Pfarrei, Schule? Jeder angefragte Ort ist eine Sitzung des Agenten; das kostet seine Zeit und die Nutzung des Abos.",
@@ -2871,6 +2885,7 @@
2871
2885
  "ui.link.experiment.archive": "Solange die Forschung als Archiv geführt wird, wird nach Ereignissen der Geschichte nicht gesucht. Es ist nichts geschehen.",
2872
2886
  "ui.link.experiment.waiting": "An dieser Forschung wird auf diesem Computer nicht gearbeitet, daher wird nach Ereignissen der Geschichte nicht gesucht. Es ist nichts geschehen.",
2873
2887
  "ui.link.what.history.place": "die Geschichte von {place} und Umgebung suchen – Brände, Hochwasser, Schlachten in der Nähe, Herrschaft, Pfarrei, Schule",
2888
+ "ui.link.what.fixes.undo": "zurücksetzen, was die Korrektur „{name}“ berichtigt hat",
2874
2889
  "ui.link.history.place.minutes": "Eine Sitzung des Agenten, etwa {from}–{to} Minuten; das kostet Zeit des Agenten und Nutzung des Abos.",
2875
2890
  "ui.link.history.place.usd": "Solche Sitzungen kosteten hier bisher {from}–{to}.",
2876
2891
  "ui.link.history.place.done": "Die Geschichte von {place} und Umgebung steht in der Warteschlange an erster Stelle.",
@@ -9,6 +9,8 @@ import { userHome } from "../core/paths.js";
9
9
  import { findAgent, withoutAgentMarks } from "../core/which.js";
10
10
  import { spawnAgent, stopTree } from "../runners/runner.js";
11
11
  import { PROFILES } from "./profiles.js";
12
+ import { agentSettingsSaid } from "./effort.js";
13
+ import { resolveKey, splitKey } from "../core/modelkey.js";
12
14
  /** Claude Code: its aliases (always the newest of each line). */
13
15
  const CLAUDE = [{ id: "opus" }, { id: "sonnet" }];
14
16
  /**
@@ -77,13 +79,17 @@ export function sameLineNewer(before, now) {
77
79
  return id(before) !== id(now) && line(before) === line(now) && !!line(now) && newer(version(before), version(now)) > 0;
78
80
  }
79
81
  const id = (m) => m.trim().toLowerCase();
82
+ /** Whether a model's name, or its maker's description of it, says it is a fast, small, cheap or older one. */
83
+ export function weakModel(m) {
84
+ return words(m.id).some((w) => WEAK.has(w)) || words(m.label ?? "").some((w) => WEAK.has(w)) || words(m.about ?? "").some((w) => WEAK_ABOUT.has(w));
85
+ }
80
86
  /**
81
87
  * The strong models of a list, in the order to offer them: never a fast, small or cheap one; the strongest line of a
82
88
  * maker first (Opus, Pro, Astra), then the one below (Sonnet, Sol), then the rest; makers in the order the agent lists
83
89
  * them (its own first), the newest version first, at most two of one line, three in all.
84
90
  */
85
91
  export function strongModels(list, max = 3) {
86
- const kept = list.filter((m) => !words(m.id).some((w) => WEAK.has(w)) && !words(m.label ?? "").some((w) => WEAK.has(w)) && !words(m.about ?? "").some((w) => WEAK_ABOUT.has(w)));
92
+ const kept = list.filter((m) => !weakModel(m));
87
93
  const tier = (m) => {
88
94
  const w = [...words(m.id), ...words(m.label ?? "")];
89
95
  return w.some((x) => TOP.has(x)) ? 2 : w.some((x) => HIGH.has(x)) ? 1 : 0;
@@ -255,3 +261,42 @@ export async function modelChoices(agent, env, ms = Number(env.STROM_MODELS_TIME
255
261
  return fallback();
256
262
  return { models: strong, source: "agent", ...(agentDefault ? { agentDefault } : {}) };
257
263
  }
264
+ /** Antigravity's own model when this was written (it names no model in its stream): a Flash one. */
265
+ const OWN_KNOWN = { antigravity: "Gemini Flash" };
266
+ /** Codex's catalog for the account as it keeps it beside its config (every model, hidden ones too; none: unknown). */
267
+ function codexCatalog(env) {
268
+ try {
269
+ const raw = JSON.parse(fs.readFileSync(path.join(env.CODEX_HOME || path.join(userHome(env), ".codex"), "models_cache.json"), "utf8"));
270
+ if (!Array.isArray(raw?.models))
271
+ return [];
272
+ return raw.models
273
+ .filter((m) => typeof m?.slug === "string")
274
+ .map((m) => ({ id: m.slug, ...(m.display_name ? { label: m.display_name } : {}), ...(m.description ? { about: m.description } : {}) }));
275
+ }
276
+ catch {
277
+ return [];
278
+ }
279
+ }
280
+ /**
281
+ * The model an agent takes on its own (none set in strom), as strom knows it without starting anything: Codex's from
282
+ * its config.toml (its profile's too); another agent's as this research saw it run (.strom/metrics/models.json: what
283
+ * the agent alone ran on); Antigravity names none it runs on — its own when this was written is a Flash one (why its
284
+ * readers read with the research's model: core/config.ts models). `weak`: by its name or the maker's description; `refused`: Codex's catalog for the account
285
+ * (models_cache.json) is known and does not hold it — Codex says "the model is not supported" to it. Nothing known:
286
+ * undefined (never a guess).
287
+ */
288
+ export function ownModel(agent, env, aliases) {
289
+ if (agent === "codex") {
290
+ const said = agentSettingsSaid(env, "codex", {});
291
+ if (said.model) {
292
+ const catalog = codexCatalog(env);
293
+ const known = catalog.find((m) => m.id === said.model);
294
+ return { model: said.model, from: "config", weak: weakModel(known ?? { id: said.model }), refused: catalog.length > 0 && !known };
295
+ }
296
+ }
297
+ const seen = aliases ? splitKey(resolveKey(agent, aliases)).model : undefined;
298
+ if (seen)
299
+ return { model: seen, from: "seen", weak: weakModel({ id: seen }), refused: false };
300
+ const known = OWN_KNOWN[agent];
301
+ return known ? { model: known, from: "known", weak: true, refused: false } : undefined;
302
+ }
@@ -3,10 +3,10 @@
3
3
  // records, one line on the local events read in them. Only a brief of such a session speaks of it: the commands are
4
4
  // behind the experiment's gate (hidden), the guide, help and strom commands say nothing of them.
5
5
  import { langName } from "../core/lang.js";
6
- import { foldText } from "../core/text.js";
7
6
  import { match } from "../cli/registry.js";
8
7
  import { synopsis } from "./sheet.js";
9
- import { catalogIds, EVENT_KINDS, forGaps, GAP_DENSITY, gapsOfTask, gapText, HISTORY_PLACES_ORIGIN, HOLD_DAYS, placeNotes, readCatalog, readHidden, readPlaces, coveredBy, regionName, regionOfTask, regionPeriod, REGION_LANGS, LIVED_MARGIN, PRESENT_YEARS, searchedSpans, storyHistory, unmappedPlaces, historyPlaces, } from "../core/pastevents.js";
8
+ import { catalogIds, EVENT_KINDS, forGaps, GAP_DENSITY, gapsOfTask, gapText, HISTORY_PLACES_ORIGIN, HOLD_DAYS, placeNotes, readCatalog, readHidden, readPlaces, coveredBy, regionName, regionOfTask, regionPeriod, REGION_LANGS, LIVED_MARGIN, PRESENT_YEARS, searchedSpans, storyHistory, unmappedPlaces, historyPlaces, placeNamed, } from "../core/pastevents.js";
9
+ import { adoptionPlan, regionArg, SAME_KM } from "../core/placeadopt.js";
10
10
  import { AREA_KM, defaultArea, factsByRecord, placeKeyOf, placeLabel, placeOfTask, placeRecord, readPlaceCatalog, regionEventsAt, yearsAt } from "../core/placehistory.js";
11
11
  /** About so many events a region at most: rather fewer. */
12
12
  export const REGION_DENSITY = 60;
@@ -259,12 +259,7 @@ function placesSections(tree, shared, alone) {
259
259
  const lang = tree.lang;
260
260
  const facts = historyPlaces(tree).facts;
261
261
  const pending = unmappedPlaces(tree);
262
- const records = tree.list("place").filter((p) => !p.retracted);
263
- const byName = (key) => {
264
- const f = foldText(key);
265
- const first = foldText(key.split(",")[0].trim());
266
- return records.find((p) => p.names.some((n) => foldText(n.name) === f)) ?? records.find((p) => p.names.some((n) => foldText(n.name) === first));
267
- };
262
+ const byName = placeNamed(tree.list("place"));
268
263
  const notes = placeNotes(tree);
269
264
  const placeLine = (key) => {
270
265
  const ys = facts.get(key) ?? [];
@@ -308,6 +303,7 @@ function placesSections(tree, shared, alone) {
308
303
  pending.length > PLACES_SHOWN ? ` … ${pending.length - PLACES_SHOWN} more: strom history status` : "",
309
304
  ].filter(Boolean).join("\n"),
310
305
  },
306
+ ...othersSection(tree, shared),
311
307
  {
312
308
  name: "regions",
313
309
  first: true,
@@ -329,6 +325,46 @@ function placesSections(tree, shared, alone) {
329
325
  ]));
330
326
  return out;
331
327
  }
328
+ /** What a place record of another research says of where the place is (its parish, district, coordinates). */
329
+ function signsText(o) {
330
+ const s = o.signs;
331
+ if (!s)
332
+ return "no place record there";
333
+ return [
334
+ s.kind ?? "",
335
+ s.parish?.length ? `parish ${s.parish.join(", ")}` : "",
336
+ s.district?.length ? `district ${s.district.join(", ")}` : "",
337
+ s.coords ? `${s.coords.lat.toFixed(3)}, ${s.coords.lon.toFixed(3)}` : "",
338
+ ]
339
+ .filter(Boolean)
340
+ .join(" · ") || "its record says nothing of where it is";
341
+ }
342
+ /**
343
+ * The places waiting that another research on this computer maps (core/placeadopt.ts): those it maps surely the same
344
+ * (strom history adopt takes them; strom takes them by itself before the next task), and a place of the name it maps
345
+ * where nothing tells it is the same place — the agent decides.
346
+ */
347
+ function othersSection(tree, shared) {
348
+ const plan = adoptionPlan(tree, shared);
349
+ if (!plan.taken.length && !plan.suggested.length)
350
+ return [];
351
+ const map = (place, regions) => `strom history map ${JSON.stringify(place)} ${regions.map((r) => `--region ${regionArg(r)}`).join(" ")}`;
352
+ return [
353
+ {
354
+ name: "other research",
355
+ first: true,
356
+ pointer: "strom history adopt --dry-run",
357
+ text: [
358
+ "## Places another research on this computer maps",
359
+ plan.taken.length
360
+ ? `- the same place surely (coordinates within ${SAME_KM} km, the same parish or district), mapped the same: ${plan.taken.length} — strom history adopt takes them as they are: ${plan.taken.slice(0, PLACES_SHOWN).map((t) => `"${t.place}"`).join(", ")}`
361
+ : "",
362
+ ...plan.suggested.slice(0, PLACES_SHOWN).flatMap((p) => p.options.map((o) => ` "${p.place}" — another research on this computer maps "${p.place}" (${signsText(o)}${o.sure ? `; the same ${o.sure === "coords" ? "coordinates" : o.sure} as here` : ""}) to ${o.regions.map(regionArg).join(", ")}${p.options.length > 1 ? " (the researches differ)" : ""} — use it only if it is the same place: ${map(p.place, o.regions)}`)),
363
+ plan.suggested.length > PLACES_SHOWN ? ` … ${plan.suggested.length - PLACES_SHOWN} more: strom history adopt --dry-run` : "",
364
+ ].filter(Boolean).join("\n"),
365
+ },
366
+ ];
367
+ }
332
368
  /** One short section for a session that reads records while the experiment is on: a local event read in them. */
333
369
  export function localEventsSection() {
334
370
  const line = cmd(["history", "local"]);
package/dist/cli/main.js CHANGED
@@ -30,7 +30,7 @@ import { isAgent } from "../core/which.js";
30
30
  import { noPermission } from "../core/writable.js";
31
31
  import { completePath, pathOf, pathToRemember } from "../core/userpath.js";
32
32
  import { appUrlShown, noticeStromApp } from "../core/stromapp.js";
33
- import { lastChannelOf, pendingTransition, updateChannel, whatsNewUrl } from "../core/update.js";
33
+ import { lastChannelOf, npmChannelKept, pendingTransition, updateChannel, whatsNewUrl } from "../core/update.js";
34
34
  import { backupBefore, backupSaid } from "./backups.js";
35
35
  import { guestOf, installation, ownFolder, refreshTreeShim } from "../core/self.js";
36
36
  import { refreshGlobal } from "../agents/global.js";
@@ -358,7 +358,7 @@ async function mainOf(argv, io, env, cwd) {
358
358
  // settings are backed up first — before this strom opens any of them, before any migration (Milan, 2026-10-07:
359
359
  // "přechod beta ↔ produkce nikdy nepřijde o data"). Made once (strom update made it already, or another strom of
360
360
  // this version a moment ago); in any strom of this version, a bridge too (it may write what the app sends).
361
- const channelNow = updateChannel(env);
361
+ const channelNow = updateChannel(env, undefined, undefined, ctx.settings.config);
362
362
  // an agent's hook (strom net web --hook, before each of its web requests; strom agents delegate --hook, before each
363
363
  // subagent call) does none of a new version's first run: it answers within the hook's time and changes nothing
364
364
  // outside the research's measurements
@@ -395,9 +395,19 @@ async function mainOf(argv, io, env, cwd) {
395
395
  catch {
396
396
  // asked again at the first look at an archive
397
397
  }
398
+ // …and its channel: another one than last time is a change of channel (npm's: a beta installed after strom update
399
+ // --channel stable, or an older release after --channel beta, is a choice made with npm since — taken as it is;
400
+ // the channel before the version is noted: a release older than the last one is a way back to npm's latest)
401
+ if (installation().kind === "npm") {
402
+ const kept = npmChannelKept(ctx.settings.config, VERSION);
403
+ if (kept)
404
+ ctx.settings.config.npmChannel = kept;
405
+ else
406
+ delete ctx.settings.config.npmChannel;
407
+ }
408
+ const channel = updateChannel(env, undefined, undefined, ctx.settings.config);
398
409
  ctx.settings.config.lastVersion = VERSION;
399
- // …and its channel: another one than last time is a change of channel
400
- ctx.settings.config.lastChannel = updateChannel(env);
410
+ ctx.settings.config.lastChannel = channel;
401
411
  ctx.settings.save();
402
412
  refreshGlobal(env);
403
413
  // …and the links from the Strom app lead to this strom again (where strom made them on this person's yes —
@@ -45,6 +45,7 @@ import { SENDABLE, waitGroupOf, waitKindOf } from "../core/waitkind.js";
45
45
  import { openForUser } from "../core/open.js";
46
46
  import { fileRefused } from "../core/taskfiles.js";
47
47
  import { placesPinned } from "../core/pastevents.js";
48
+ import { fixCheck, fixEnv, pendingFixes } from "../commands/fixchecks.js";
48
49
  import { placeEstimate, placeLabel, placeRecord, placesWithCoords, placeTask } from "../core/placehistory.js";
49
50
  /** What the Strom app asks, said before anything is done; true when the person goes on. */
50
51
  export async function asks(ctx, lang, root, what, note, suggested = true) {
@@ -301,6 +302,40 @@ export async function placeSearch(ctx, run, lang, root, id) {
301
302
  return true;
302
303
  return (await run(["run", "--task", task.id])) === 0;
303
304
  }
305
+ /**
306
+ * strom's fixes from a link (strom-research://fixes): what it names shown, and set right or put back only by the person
307
+ * here — strom fixes run and undo ask them themselves (never --yes, never the link's own answer). No fix named: the list,
308
+ * or each that waits in turn, as the menu's Maintenance does it; where nobody reads (an archive, a research waiting for
309
+ * its agent) only the checks that rewrite. A paid check named there is refused before (commands/links.ts).
310
+ */
311
+ export async function fixesDo(ctx, run, lang, root, id, what) {
312
+ const tree = Tree.open(root, ctx.env);
313
+ const check = id ? fixCheck(id) : undefined;
314
+ if (!check) {
315
+ const quiet = actsAsArchive(tree);
316
+ const waits = (pendingFixes(tree, fixEnv(ctx, tree)) ?? []).filter((p) => !quiet || p.check.remedy === "rewrite");
317
+ if (!waits.length)
318
+ return (await run(["fixes"])) === 0;
319
+ if (!quiet || isArchive(tree))
320
+ return (await run(["fixes", "run"])) === 0;
321
+ let done = true;
322
+ for (const p of waits)
323
+ done = (await run(["fixes", "run", p.check.id])) === 0 && done;
324
+ return done;
325
+ }
326
+ // put back: only what rewrites keeps what it set right — shown first, then the person's question here (strom fixes
327
+ // undo itself asks nothing more at a terminal); one that reads again is shown (nothing to put back)
328
+ if (what === "undo") {
329
+ if ((await run(["fixes", "show", check.id])) !== 0 || check.remedy !== "rewrite" || !check.applied?.(tree))
330
+ return false;
331
+ const t = translator(lang);
332
+ if (!(await asks(ctx, lang, root, t("ui.link.what.fixes.undo", { name: t(`ui.fixes.${check.id}.name`) }), undefined, false)))
333
+ return false;
334
+ return (await run(["fixes", "undo", check.id])) === 0;
335
+ }
336
+ const waits = (pendingFixes(tree, fixEnv(ctx, tree)) ?? []).some((p) => p.check.id === check.id);
337
+ return (await run(waits ? ["fixes", "run", check.id] : ["fixes", "show", check.id])) === 0;
338
+ }
304
339
  /** A direction of the research paused, ended (each with the person's reason, if they give one) or taken up again. */
305
340
  export async function directionDo(ctx, run, lang, root, id, what) {
306
341
  const t = translator(lang);