strom-research 1.0.0

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 (130) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +142 -0
  3. package/assets/lang/cs.json +302 -0
  4. package/assets/lang/de.json +302 -0
  5. package/assets/method/core.md +43 -0
  6. package/assets/method/enrich.md +11 -0
  7. package/assets/method/intake.md +30 -0
  8. package/assets/method/link.md +28 -0
  9. package/assets/method/locate.md +28 -0
  10. package/assets/method/narrate.md +13 -0
  11. package/assets/method/reading.md +62 -0
  12. package/assets/method/recording.md +59 -0
  13. package/assets/method/request.md +10 -0
  14. package/assets/method/verify.md +17 -0
  15. package/assets/plugins/README.md +23 -0
  16. package/assets/plugins/connectors/DISCOVERY.md +159 -0
  17. package/assets/plugins/connectors/README.md +376 -0
  18. package/assets/plugins/connectors/sdk.ts +168 -0
  19. package/assets/plugins/connectors/template.ts +38 -0
  20. package/assets/plugins/gitignore +4 -0
  21. package/dist/agents/files.js +313 -0
  22. package/dist/agents/global.js +257 -0
  23. package/dist/agents/launch.js +36 -0
  24. package/dist/agents/profiles.js +95 -0
  25. package/dist/brief/brief.js +345 -0
  26. package/dist/cli/commit.js +44 -0
  27. package/dist/cli/context.js +311 -0
  28. package/dist/cli/execute.js +154 -0
  29. package/dist/cli/fixes.js +78 -0
  30. package/dist/cli/format.js +53 -0
  31. package/dist/cli/help.js +59 -0
  32. package/dist/cli/main.js +152 -0
  33. package/dist/cli/menu.js +212 -0
  34. package/dist/cli/registry.js +96 -0
  35. package/dist/cli/ui.js +266 -0
  36. package/dist/cli/wizard.js +142 -0
  37. package/dist/cli.js +14 -0
  38. package/dist/commands/analysis.js +622 -0
  39. package/dist/commands/batch.js +181 -0
  40. package/dist/commands/checks.js +153 -0
  41. package/dist/commands/connectors.js +1377 -0
  42. package/dist/commands/guide.js +160 -0
  43. package/dist/commands/index.js +19 -0
  44. package/dist/commands/intake.js +234 -0
  45. package/dist/commands/media.js +406 -0
  46. package/dist/commands/meta.js +195 -0
  47. package/dist/commands/output.js +117 -0
  48. package/dist/commands/people.js +664 -0
  49. package/dist/commands/read.js +199 -0
  50. package/dist/commands/research.js +139 -0
  51. package/dist/commands/session.js +605 -0
  52. package/dist/commands/setup.js +465 -0
  53. package/dist/commands/sources.js +634 -0
  54. package/dist/commands/start.js +383 -0
  55. package/dist/commands/story.js +75 -0
  56. package/dist/commands/tasks.js +436 -0
  57. package/dist/commands/trees.js +128 -0
  58. package/dist/core/actions.js +852 -0
  59. package/dist/core/age.js +95 -0
  60. package/dist/core/apps.js +74 -0
  61. package/dist/core/assets.js +34 -0
  62. package/dist/core/awake.js +33 -0
  63. package/dist/core/browser.js +281 -0
  64. package/dist/core/calibration.js +48 -0
  65. package/dist/core/check.js +112 -0
  66. package/dist/core/chromium.js +88 -0
  67. package/dist/core/config.js +348 -0
  68. package/dist/core/connector.js +811 -0
  69. package/dist/core/deps.js +73 -0
  70. package/dist/core/dialog.js +61 -0
  71. package/dist/core/errors.js +89 -0
  72. package/dist/core/evidence.js +58 -0
  73. package/dist/core/frontier.js +219 -0
  74. package/dist/core/gdate.js +77 -0
  75. package/dist/core/git.js +300 -0
  76. package/dist/core/guard.js +124 -0
  77. package/dist/core/http2.js +76 -0
  78. package/dist/core/import.js +541 -0
  79. package/dist/core/install.js +28 -0
  80. package/dist/core/integrity.js +219 -0
  81. package/dist/core/json.js +87 -0
  82. package/dist/core/lang.js +70 -0
  83. package/dist/core/live.js +244 -0
  84. package/dist/core/lock.js +112 -0
  85. package/dist/core/logins.js +67 -0
  86. package/dist/core/media.js +223 -0
  87. package/dist/core/model.js +101 -0
  88. package/dist/core/net.js +366 -0
  89. package/dist/core/open.js +29 -0
  90. package/dist/core/paths.js +84 -0
  91. package/dist/core/people.js +283 -0
  92. package/dist/core/phrases.js +85 -0
  93. package/dist/core/queue.js +113 -0
  94. package/dist/core/reader.js +76 -0
  95. package/dist/core/records.js +105 -0
  96. package/dist/core/roles.js +30 -0
  97. package/dist/core/schema.js +261 -0
  98. package/dist/core/seal.js +77 -0
  99. package/dist/core/self.js +40 -0
  100. package/dist/core/session.js +155 -0
  101. package/dist/core/shortcut.js +90 -0
  102. package/dist/core/stories.js +61 -0
  103. package/dist/core/stromapp.js +138 -0
  104. package/dist/core/text.js +104 -0
  105. package/dist/core/tree.js +507 -0
  106. package/dist/core/uninstall.js +128 -0
  107. package/dist/core/update.js +193 -0
  108. package/dist/core/validate.js +260 -0
  109. package/dist/core/views.js +164 -0
  110. package/dist/core/which.js +51 -0
  111. package/dist/core/workers.js +42 -0
  112. package/dist/gedcom/export.js +454 -0
  113. package/dist/gedcom/labels.js +103 -0
  114. package/dist/gedcom/lines.js +91 -0
  115. package/dist/gedcom/parse.js +53 -0
  116. package/dist/gedcom/validate.js +183 -0
  117. package/dist/image/image.js +223 -0
  118. package/dist/image/index.js +114 -0
  119. package/dist/image/jpeg-decode.js +552 -0
  120. package/dist/image/jpeg-encode.js +254 -0
  121. package/dist/image/png.js +241 -0
  122. package/dist/runners/antigravity.js +70 -0
  123. package/dist/runners/claude.js +179 -0
  124. package/dist/runners/codex.js +45 -0
  125. package/dist/runners/index.js +13 -0
  126. package/dist/runners/jsonl.js +86 -0
  127. package/dist/runners/opencode.js +50 -0
  128. package/dist/runners/runner.js +63 -0
  129. package/dist/runners/script.js +58 -0
  130. package/package.json +44 -0
package/dist/cli/ui.js ADDED
@@ -0,0 +1,266 @@
1
+ // What a person reads: the guided menu, the setup wizard, the window of a
2
+ // consent, the first message to the agent. In the user's language (the
3
+ // research language), English built in, other languages in the same catalogs
4
+ // as the research phrases (assets/lang/<code>.json, keys ui.*).
5
+ import { localized } from "../core/phrases.js";
6
+ export const UI = {
7
+ "ui.yes": "y",
8
+ "ui.no": "n",
9
+ "ui.enter": "Press Enter to go back to the menu.",
10
+ "ui.choose": "Choose",
11
+ "ui.bad.choice": "Please type one of the numbers.",
12
+ "ui.dialog.title": "Strom research",
13
+ "ui.dialog.yes": "Allow",
14
+ "ui.dialog.no": "Don't allow",
15
+ "ui.dialog.refused": "You did not allow it — nothing was changed.",
16
+ "ui.consent.level.auto": "Let the agent work on its own? It will ask you only about risky steps.",
17
+ "ui.consent.level.full": "Let the agent do everything except what strom forbids, without asking you?\n\nOnly for a computer where nothing else of value is kept.",
18
+ "ui.consent.connector": "Allow the downloader \"{name}\" to fetch images for the research?",
19
+ "ui.consent.connector.revoke": "Stop the downloader \"{name}\"?",
20
+ "ui.consent.host": "Allow automatic downloading from {host}?",
21
+ "ui.consent.host.revoke": "Stop automatic downloading from {host}?",
22
+ "ui.consent.connector.hosts": "It will reach only {hosts} — slowly, through strom's limiter.",
23
+ "ui.consent.connector.changed": "Its code changed since you allowed it.",
24
+ "ui.consent.connector.direct": "Warning: its code reaches the network itself, past strom's limiter.",
25
+ "ui.consent.connector.terms": "The archive's terms of use: {terms}",
26
+ "ui.consent.host.unblock": "{host} refused us. Allow it again — only if the archive said it is fine?",
27
+ "ui.consent.connectors.off": "Let downloaders run without asking you first?",
28
+ "ui.menu.title": "Strom research {version}",
29
+ "ui.menu.tree": "Family tree: {name} · {persons} people",
30
+ "ui.menu.waiting": "Waiting for you: {count}",
31
+ "ui.menu.working": "The agent is working on its own right now.",
32
+ "ui.menu.chat": "Continue the research with the agent",
33
+ "ui.menu.start": "Start the research with the agent",
34
+ "ui.menu.otheragent": "Talk with another agent this time",
35
+ "ui.menu.run": "Let the agent work on its own",
36
+ "ui.menu.waitlist": "What waits for you",
37
+ "ui.menu.results": "Results",
38
+ "ui.menu.settings": "Settings",
39
+ "ui.menu.doctor": "Check the installation",
40
+ "ui.menu.trees": "Another family tree",
41
+ "ui.menu.newtree": "Create a family tree",
42
+ "ui.menu.quit": "Quit",
43
+ "ui.menu.bye": "See you next time. Start again with: strom",
44
+ "ui.run.how": "How many tasks should the agent work on? (it stops earlier when there is nothing to do)",
45
+ "ui.run.start": "The agent works on its own now. You can stop it with Ctrl-C — nothing is lost.",
46
+ "ui.waiting.none": "Nothing waits for you.",
47
+ "ui.waiting.open": "Open the folder for the images?",
48
+ "ui.results.files": "Results (updated after every session):",
49
+ "ui.results.none": "No results yet — they appear after the first session.",
50
+ "ui.results.app": "For the Strom app: {file} — in the app choose Import and pick this file.",
51
+ "ui.results.app.maybe": "Do you use the Strom app? Import {file} into it.",
52
+ "ui.results.open": "Open the folder with the results?",
53
+ "ui.trees.pick": "Which family tree?",
54
+ "ui.trees.new": "a new family tree",
55
+ "ui.tree.name": "What should your family tree be called? (e.g. the family surname)",
56
+ "ui.tree.default": "My family",
57
+ "ui.chat.open": "Opening the conversation with the agent ({agent}). When you are done, type {exit}.",
58
+ "ui.chat.first": "Hello, I want to start researching my ancestors. Run `strom`, explain to me in a few sentences how we will work together, and ask me whom we are looking for and what we already know.",
59
+ "ui.chat.continue": "Let's continue the research. Run `strom`, tell me briefly where the research stands and what waits for me, suggest what to do next, and tell me in a sentence what I can ask you about the tree.",
60
+ "ui.setup.welcome": "Welcome to Strom research. Let's set up a few things — press Enter to accept the suggested answer.",
61
+ "ui.setup.again": "Settings — press Enter to keep the current value.",
62
+ "ui.setup.lang": "Language (cs, en, de, pl, …)",
63
+ "ui.setup.home": "Where to keep the research",
64
+ "ui.setup.git.ok": "✓ Git (history of the research): {version}",
65
+ "ui.setup.git.missing": "Git is missing — strom keeps the history of the research in it, so nothing gets lost.",
66
+ "ui.setup.git.wait": "Finish the installation in the window that opened, then press Enter.",
67
+ "ui.setup.git.manual": "Install it with: {command} — then start strom again.",
68
+ "ui.setup.agent.ok": "✓ AI agent: {name}",
69
+ "ui.setup.agent.pick": "Which AI agent should do the research?",
70
+ "ui.setup.agent.none": "No AI agent found — strom needs one: the agent does the research with you.",
71
+ "ui.setup.agent.install": "Install Claude Code now (recommended; you need a Claude subscription)",
72
+ "ui.setup.agent.guide": "Show me which agents there are (opens a page in the browser)",
73
+ "ui.setup.agent.later": "Later",
74
+ "ui.setup.agent.login": "The first time the agent starts, you log in — a browser window opens.",
75
+ "ui.setup.agent.manual": "Install an AI agent (how: {url}), then start strom again.",
76
+ "ui.setup.model": "Which model should do the research?",
77
+ "ui.setup.model.opus": "Opus — best at reading old handwriting (recommended)",
78
+ "ui.setup.model.sonnet": "Sonnet — faster; printed documents and everyday work",
79
+ "ui.setup.model.own": "Leave it to the agent",
80
+ "ui.setup.level": "What may the agent do without asking you?",
81
+ "ui.setup.level.ask": "Ask me about everything outside the research",
82
+ "ui.setup.level.auto": "Work on its own — ask only about risky steps (recommended)",
83
+ "ui.setup.level.full": "Everything without asking — only on a computer with nothing else of value",
84
+ "ui.setup.level.warn": "The agent will be able to run any program and change any of your files; strom still guards the research itself.",
85
+ "ui.setup.level.sure": "Really allow everything?",
86
+ "ui.setup.shortcut": "Put a \"Strom research\" shortcut on the desktop?",
87
+ "ui.setup.shortcut.done": "✓ Shortcut: {file}",
88
+ "ui.setup.skill": "✓ {agent} now knows strom in any folder.",
89
+ "ui.setup.app": "✓ The Strom app is on this computer — results will be ready to import into it.",
90
+ "ui.setup.done": "All set.",
91
+ "ui.app.opened": "Opening the Strom app ({where}).",
92
+ "ui.app.browser": "in your browser",
93
+ "ui.app.url": "Open it here: {url}",
94
+ "ui.app.installed": "The Strom app is installed ({kind}).",
95
+ "ui.app.install": "The Strom app opened in your browser. To install it as an app, click the install icon at the right end of the address bar (Chrome, Edge); in Safari choose File → Add to Dock.",
96
+ "ui.menu.app": "Open the Strom app",
97
+ "ui.chat.agent.missing": "{agent} is not installed — run the settings (strom setup) to install it.",
98
+ "ui.shortcut.done": "Shortcut on the desktop: {file}",
99
+ "ui.o.title": "strom {version} — genealogical research toolkit for AI agents",
100
+ "ui.o.rule": "The evidence lives here; change it ONLY through strom commands. New here: strom guide",
101
+ "ui.o.home": "home",
102
+ "ui.o.notsetup": "(not set up)",
103
+ "ui.o.tree": "tree",
104
+ "ui.o.lang": "lang",
105
+ "ui.o.langnote": "talk to the user and write research texts in this language",
106
+ "ui.o.data": "data",
107
+ "ui.o.counts": "{persons} persons · {families} families · check: {check}",
108
+ "ui.o.ok": "ok",
109
+ "ui.o.errors": "{n} errors",
110
+ "ui.o.warnings": "{n} warnings",
111
+ "ui.o.research": "research",
112
+ "ui.o.research.none": "research (none yet)",
113
+ "ui.o.trees": "trees",
114
+ "ui.o.working": "working now (other agents — never take their tasks)",
115
+ "ui.o.since": "since {time}",
116
+ "ui.o.on": "on {task}",
117
+ "ui.o.next": "next",
118
+ "ui.why.setup.human": "strom is not set up yet — it asks where to keep the research",
119
+ "ui.why.setup.agent": "strom is not set up yet: this creates {home} (tell the user; --home <dir> for another place)",
120
+ "ui.why.init": "there is no family tree yet",
121
+ "ui.why.pick": "several trees exist — pick one (it is remembered)",
122
+ "ui.why.check": "the evidence has problems (or was changed outside strom)",
123
+ "ui.why.runleft": "session {id} was left open by a run that stopped — the next run closes it and goes on",
124
+ "ui.why.running": "strom run is working (session {id}) — let it finish, or Ctrl-C in its window",
125
+ "ui.why.open.human": "session {id} is open — an agent is working, or it stopped without closing",
126
+ "ui.why.open.agent": "session {id} is open{on} — continue, then close it",
127
+ "ui.why.research": "the tree has no research yet",
128
+ "ui.why.waiting": "the research waits for you — see \"waiting for you\" above",
129
+ "ui.why.intake": "record first what the user knows (then tasks follow from it)",
130
+ "ui.why.run": "{n} task(s) in the queue — an AI agent works them",
131
+ "ui.why.session": "{n} task(s) in the queue",
132
+ "ui.why.frontier": "no open tasks — see what the research still lacks",
133
+ "ui.wait.title": "waiting for you ({n})",
134
+ "ui.wait.images": "images {images} of {book}",
135
+ "ui.wait.save": "save them into {dir}",
136
+ "ui.wait.how1": "scans go into the inbox, a folder per book, each named by its image number as the viewer counts it",
137
+ "ui.wait.how2": "(9.jpg; a zoomed-in part of it: 9a.jpg) — the next run registers them and the task comes back by itself",
138
+ "ui.run.summary": "{n} session(s) · stopped: {reason}",
139
+ "ui.run.stop.done": "done",
140
+ "ui.run.stop.user": "stopped by you",
141
+ "ui.run.stop.time": "time limit",
142
+ "ui.run.stop.empty": "no open tasks",
143
+ "ui.run.stop.problems": "the evidence has problems — strom check",
144
+ "ui.run.stop.denied": "the agent was not allowed to run strom — its permissions are wrong (.claude/settings.json of the tree); nothing more was started",
145
+ "ui.run.stop.limit": "subscription limit{resets}",
146
+ "ui.run.stop.resets": " (resets {at})",
147
+ "ui.run.stop.auth": "the agent is not logged in — run {command} once and log in",
148
+ "ui.run.stop.failed": "the agent failed: {detail}",
149
+ "ui.run.stopping": "■ stopping — the session is closed and its task goes back to the queue; Ctrl-C again to quit at once",
150
+ "ui.run.gone": "· {session} closed — its run had stopped{back}",
151
+ "ui.run.back": "; {task} is back in the queue",
152
+ "ui.run.start.session": "▶ {session} · {task} {what}",
153
+ "ui.run.parked": " · {task} parked: {n} sessions in a row recorded nothing",
154
+ "ui.run.denied": " · permissions refused {n}×: {what}",
155
+ "ui.tree.created": "✓ The family tree \"{name}\" is ready: {dir}",
156
+ "ui.chat.trust": "The first time, Claude Code asks whether you trust this folder. It is your family tree's folder, made by strom — confirm with \"Yes, proceed\".",
157
+ "ui.doc.program": "program",
158
+ "ui.doc.git": "git (history)",
159
+ "ui.doc.home": "research folder",
160
+ "ui.doc.shared": "scans and catalog",
161
+ "ui.doc.trees": "family trees",
162
+ "ui.doc.agent": "AI agents",
163
+ "ui.doc.knows": "agents know strom",
164
+ "ui.doc.level": "the agent may",
165
+ "ui.doc.model": "model",
166
+ "ui.doc.shortcut": "desktop shortcut",
167
+ "ui.doc.app": "Strom app",
168
+ "ui.doc.tree": "this family tree",
169
+ "ui.doc.innode": "strom {version} in Node {node}",
170
+ "ui.doc.oldnode": "Node {node} is too old (22.18 or newer)",
171
+ "ui.doc.missing": "missing",
172
+ "ui.doc.notsetup": "not set up yet",
173
+ "ui.doc.none": "none",
174
+ "ui.doc.yes": "yes",
175
+ "ui.doc.default": "default",
176
+ "ui.doc.noagent": "no AI agent installed",
177
+ "ui.doc.knows.yes": "yes, in any folder",
178
+ "ui.doc.knows.no": "not yet: {agents}",
179
+ "ui.doc.app.yes": "you use it",
180
+ "ui.doc.app.seen": "found on this computer",
181
+ "ui.doc.app.no": "not wanted",
182
+ "ui.doc.app.unknown": "not found",
183
+ "ui.doc.tree.ok": "{name}: data consistent and sealed",
184
+ "ui.doc.tree.bad": "{name}: {n} problem(s)",
185
+ "ui.doc.allok": "Everything needed is in place.",
186
+ "ui.doc.problems": "{n} problem(s) — do what the arrows say (strom setup offers to install what is missing).",
187
+ "ui.menu.fix": "Fix it now?",
188
+ "ui.chat.app.open": "The conversation opens in the {app} app. Confirm the family tree's folder there, then send the prepared message (Enter).",
189
+ "ui.chat.app.closed": "The {app} app did not open. Start it yourself, open the folder {dir} and write: {message}",
190
+ "ui.chat.app.auto": "So that the agent works on its own: in the menu under the message box choose \"Auto\" — once, Claude remembers it for this folder.",
191
+ "ui.chat.app.full": "So that the agent may do everything: in the menu under the message box choose \"Bypass permissions\" (turn it on in Claude's settings first).",
192
+ "ui.setup.where": "Where do you want to talk with the agent?",
193
+ "ui.setup.where.app": "In the {app} app — the easiest (recommended)",
194
+ "ui.setup.where.terminal": "In the terminal ({agent}) — for experienced users",
195
+ "ui.doc.where": "where you talk",
196
+ "ui.doc.where.app": "the {app} app",
197
+ "ui.doc.where.terminal": "the terminal ({agent})",
198
+ "ui.run.needscli": "Working on its own needs {agent} in the terminal (the app alone cannot) — strom setup offers to install it.",
199
+ "ui.fix.git.download": "Download git for strom (about {mb} MB — the official Git for Windows, used by strom only, no installation)?",
200
+ "ui.fix.git.window": "Git comes with Apple's command line tools. A window of the system installs them (a few minutes). Open it?",
201
+ "ui.fix.git.downloading": "Downloading git…",
202
+ "ui.fix.git.inwindow": "Finish the installation in the window of the system, then run strom doctor again.",
203
+ "ui.fix.failed": "It did not work: {detail}",
204
+ "ui.fix.app": "Open the Strom app in the browser, to install it?",
205
+ "ui.fix.nothing": "Nothing strom can fix by itself — see the arrows.",
206
+ "ui.why.git.human": "git is missing — strom keeps the history of the research in it",
207
+ "ui.why.git.agent": "git is missing (strom keeps the research history in it): tell the user, then run this — the user says yes in a window of the system",
208
+ "ui.o.results": "results",
209
+ "ui.o.results.installed": "{file} — for the Strom app, installed here: strom app opens it (in the app: Import; on the web: {url})",
210
+ "ui.o.results.offer": "{file} — for the Strom app, not installed here yet: offer the user to install it from {url} (strom app install opens it there)",
211
+ "ui.o.results.plain": "{file} — for any family tree program",
212
+ "ui.o.results.installed.live": "{file} — for the Strom app, installed here: strom app opens this research in it, and while you work the app follows it live (what you record shows there by itself) — offer it once",
213
+ "ui.o.results.offer.live": "{file} — for the Strom app, not installed here yet: offer the user to watch the research grow in it live — installed from {url} (strom app install opens it there and says where to click), then strom app",
214
+ "ui.chat.handover": "The research goes on in its own conversation: a new window opened with {agent} in your family tree's folder. Continue there — this conversation can end.",
215
+ "ui.chat.handover.failed": "A new window could not be opened here. Start the research yourself: run strom in a terminal (or the Strom research shortcut on the desktop).",
216
+ "ui.chat.handover.end": "Continue there — this conversation can end.",
217
+ "ui.why.handover": "the research belongs in its own conversation in the tree folder (its permissions, no asking at every step): this opens it — tell the user to continue there; go on here only if they want",
218
+ "ui.app.research": "Opening the research {name} in the Strom app ({where}): the first time as a new tree, after that the same tree, updated.",
219
+ "ui.app.following": "The Strom app follows the research {name} while it goes on ({where}): what is found shows there by itself.",
220
+ "ui.o.stories": "stories",
221
+ "ui.o.stories.tell": "on (the default): when the research starts, tell the user in a sentence that you will also write stories of their ancestors for the family book in the Strom app, and that they may say no — strom config set stories no (yes: strom config set stories yes)",
222
+ "ui.o.stories.on": "on: strom proposes a story once records tell a life (narrate tasks)",
223
+ "ui.o.stories.off": "off (the user said no) — do not bring it up",
224
+ "ui.setup.model.strong": "The research needs a strong model — it reads old handwriting and weighs the records: in {agent}, choose its best model, not a fast or mini one.",
225
+ "ui.chat.app.model": "In {app}, choose its best model for the research (not a fast or mini one): it reads old handwriting.",
226
+ "ui.setup.stories": "Should the agent also write stories of your ancestors — a few paragraphs about each life, from the records it finds, for the family book in the Strom app?",
227
+ "ui.setup.stories.yes": "Yes (recommended)",
228
+ "ui.setup.stories.no": "No — just the family tree and its sources",
229
+ "ui.uninstall.agent": "what {agent} knows of strom",
230
+ "ui.uninstall.shortcut": "the shortcut",
231
+ "ui.uninstall.git": "strom's own git",
232
+ "ui.uninstall.path": "the installer's PATH entry",
233
+ "ui.uninstall.program": "the program",
234
+ "ui.uninstall.list": "strom takes this off your computer:",
235
+ "ui.uninstall.keeps": "Your research stays ({home}), and so do your settings with the keys that seal it ({config}) — delete them yourself only if you never want them again.",
236
+ "ui.uninstall.sure": "Uninstall strom?",
237
+ "ui.uninstall.kept": "Nothing was removed.",
238
+ "ui.uninstall.window": "Uninstall strom from this computer? Your research stays.",
239
+ "ui.uninstall.nothing": "Nothing of strom's to take off this computer.",
240
+ "ui.uninstall.npm": "The program itself (installed with npm): npm uninstall -g strom-research",
241
+ "ui.uninstall.done": "strom is uninstalled. Thank you for researching with it.",
242
+ "ui.menu.update.line": "A new version of strom is out: {version}.",
243
+ "ui.menu.update": "Update strom to {version}",
244
+ "ui.doc.update": "new version",
245
+ "ui.doc.update.new": "{version} is out",
246
+ "ui.update.unknown": "The newest version of strom could not be learned.",
247
+ "ui.update.current": "strom {version} is the newest version.",
248
+ "ui.update.available": "A new version is out: {version} (this is {current}) — strom update installs it.",
249
+ "ui.update.npm": "A new version is out: {version}. strom is installed with npm here: npm install -g strom-research@latest",
250
+ "ui.update.sure": "Update strom from {current} to {version}? Your research stays as it is.",
251
+ "ui.update.later": "Not now — strom update any time.",
252
+ "ui.update.downloading": "Downloading strom {version}…",
253
+ "ui.update.failed": "The update did not work: {detail}. strom stays as it was.",
254
+ "ui.update.done": "✓ strom is updated: {previous} → {version}.",
255
+ "ui.o.update": "update",
256
+ "ui.o.update.new": "{version} is out (this is {current}) — tell the user; strom update installs it with their yes",
257
+ "ui.app.drag": "The Strom app opened. Drag the file {file}{shown} into its window — the first time it becomes a new tree, after that the same tree is updated. With Chrome or Edge on this computer, strom hands it over by itself.",
258
+ "ui.app.drag.shown": " (shown in the file window)",
259
+ "ui.app.nolive": "Following the research live needs Chrome or Edge — Safari does not let a web page reach this computer.",
260
+ "ui.doc.installed": "strom {version} on its own Node {node} (official, from nodejs.org)",
261
+ "ui.update.source": "A new version is out: {version}. This strom runs from its sources: git pull",
262
+ };
263
+ /** A text for the person, in their language. */
264
+ export function ui(lang, key, values = {}) {
265
+ return localized(lang, key, UI[key], values);
266
+ }
@@ -0,0 +1,142 @@
1
+ // The setup wizard: what a person answers once, and can change any time by
2
+ // running it again (every answer is pre-filled with the current value). In
3
+ // their language; Enter takes the suggestion. It checks and offers to install
4
+ // git and an AI agent, and settles the model and what the agent may do alone.
5
+ // Quietly it teaches the installed agents about strom and notices the Strom app.
6
+ import fs from "node:fs";
7
+ import path from "node:path";
8
+ import { ui } from "./ui.js";
9
+ import { isValidLang, langName } from "../core/lang.js";
10
+ import { gitVersion } from "../core/git.js";
11
+ import { fixGit, offerAgent } from "./fixes.js";
12
+ import { agentsHere, DESKTOP_APPS } from "../core/apps.js";
13
+ import { PROFILES } from "../agents/profiles.js";
14
+ import { writeStored, PERMISSION_LEVELS } from "../core/config.js";
15
+ import { globalTargets, installGlobal } from "../agents/global.js";
16
+ import { noticeStromApp, researchUrl } from "../core/stromapp.js";
17
+ import { createShortcut } from "../core/shortcut.js";
18
+ import { desktopDir } from "../core/paths.js";
19
+ import { ensureShared } from "../commands/setup.js";
20
+ /** The shortcut's name, in the user's language. */
21
+ export function shortcutName(lang) {
22
+ return ui(lang, "ui.dialog.title");
23
+ }
24
+ function shortcutExists(ctx, lang) {
25
+ const dir = desktopDir(ctx.env);
26
+ const name = shortcutName(lang);
27
+ return [`${name}.command`, `${name}.cmd`, "strom-research.desktop"].some((f) => fs.existsSync(path.join(dir, f)));
28
+ }
29
+ export async function setupWizard(ctx) {
30
+ const s = ctx.settings;
31
+ const cfg = s.config;
32
+ const out = (line = "") => ctx.io.stdout(line + "\n");
33
+ const first = !cfg.home;
34
+ let lang = cfg.lang ?? s.lang().value;
35
+ out(ui(lang, first ? "ui.setup.welcome" : "ui.setup.again"));
36
+ out();
37
+ // 1. Language — everything after is in it.
38
+ for (;;) {
39
+ const a = (await ctx.ask(ui(lang, "ui.setup.lang"), lang)).toLowerCase();
40
+ if (isValidLang(a)) {
41
+ lang = a;
42
+ break;
43
+ }
44
+ }
45
+ // 2. Where the research lives.
46
+ const home = ctx.resolvePath(await ctx.ask(ui(lang, "ui.setup.home"), ctx.display(s.home()?.value ?? s.suggestedHome())));
47
+ cfg.lang = lang;
48
+ cfg.home = home;
49
+ s.save();
50
+ fs.mkdirSync(home, { recursive: true });
51
+ fs.mkdirSync(s.trees().value, { recursive: true });
52
+ ensureShared(s.shared().value);
53
+ // 3. Git (strom doctor --fix does the same).
54
+ const git = gitVersion(ctx.env);
55
+ if (git)
56
+ out(ui(lang, "ui.setup.git.ok", { version: git }));
57
+ else {
58
+ out(ui(lang, "ui.setup.git.missing"));
59
+ await fixGit(ctx, out);
60
+ }
61
+ // 4. The AI agent — its CLI or its desktop app — and where the person talks with it.
62
+ let here = agentsHere(ctx.env);
63
+ let installed = here.map((a) => a.id);
64
+ if (installed.length === 0) {
65
+ await offerAgent(ctx, out);
66
+ here = agentsHere(ctx.env);
67
+ installed = here.map((a) => a.id);
68
+ }
69
+ let agent;
70
+ if (installed.length === 1)
71
+ agent = installed[0];
72
+ else if (installed.length > 1) {
73
+ const current = Math.max(0, installed.indexOf(cfg.agent ?? "claude"));
74
+ const i = await ctx.choose(ui(lang, "ui.setup.agent.pick"), installed.map((id) => ({ label: PROFILES[id].name })), current);
75
+ agent = installed[i ?? current];
76
+ }
77
+ if (agent) {
78
+ cfg.agent = agent;
79
+ out(ui(lang, "ui.setup.agent.ok", { name: PROFILES[agent].name }));
80
+ // Both the app and the CLI: the person chooses (the app is the easiest); one of them: that one.
81
+ const has = here.find((a) => a.id === agent);
82
+ if (has.app && has.cli) {
83
+ const app = DESKTOP_APPS[agent].name;
84
+ const current = cfg.agentWhere === "terminal" ? 1 : 0;
85
+ const i = await ctx.choose(ui(lang, "ui.setup.where"), [{ label: ui(lang, "ui.setup.where.app", { app }) }, { label: ui(lang, "ui.setup.where.terminal", { agent: PROFILES[agent].name }) }], current);
86
+ cfg.agentWhere = (i ?? current) === 1 ? "terminal" : "app";
87
+ }
88
+ }
89
+ else
90
+ out(ui(lang, "ui.setup.agent.manual", { url: researchUrl(lang, "agents") }));
91
+ // 5. The model (Claude Code: the one choice that matters for reading old hands).
92
+ if (agent === "claude") {
93
+ const models = ["opus", "sonnet", undefined];
94
+ const current = cfg.models?.claude?.lead;
95
+ const suggested = current === undefined ? (first ? 0 : 2) : Math.max(0, models.indexOf(current));
96
+ const i = await ctx.choose(ui(lang, "ui.setup.model"), [{ label: ui(lang, "ui.setup.model.opus") }, { label: ui(lang, "ui.setup.model.sonnet") }, { label: ui(lang, "ui.setup.model.own") }], suggested);
97
+ writeStored(cfg, "model.lead", "claude", models[i ?? suggested]);
98
+ }
99
+ else if (agent)
100
+ out(ui(lang, "ui.setup.model.strong", { agent: PROFILES[agent].name }));
101
+ // 5b. Stories of the ancestors for the family book: on unless the person says no.
102
+ const si = await ctx.choose(ui(lang, "ui.setup.stories"), [{ label: ui(lang, "ui.setup.stories.yes") }, { label: ui(lang, "ui.setup.stories.no") }], cfg.stories === "no" ? 1 : 0);
103
+ cfg.stories = (si ?? (cfg.stories === "no" ? 1 : 0)) === 1 ? "no" : "yes";
104
+ // 6. What the agent may do without asking.
105
+ const now = s.agentPermissions();
106
+ const labels = { ask: ui(lang, "ui.setup.level.ask"), auto: ui(lang, "ui.setup.level.auto"), full: ui(lang, "ui.setup.level.full") };
107
+ const li = await ctx.choose(ui(lang, "ui.setup.level"), PERMISSION_LEVELS.map((l) => ({ label: labels[l] })), PERMISSION_LEVELS.indexOf(now));
108
+ let level = PERMISSION_LEVELS[li ?? PERMISSION_LEVELS.indexOf(now)];
109
+ if (level === "full" && now !== "full") {
110
+ out(ui(lang, "ui.setup.level.warn"));
111
+ if (!(await ctx.confirm(ui(lang, "ui.setup.level.sure"), false)))
112
+ level = now;
113
+ }
114
+ cfg.agentPermissions = level;
115
+ s.save();
116
+ // 7. A shortcut on the desktop (asked once; refreshed quietly after).
117
+ if (!shortcutExists(ctx, lang)) {
118
+ if (await ctx.confirm(ui(lang, "ui.setup.shortcut"), true)) {
119
+ try {
120
+ for (const f of createShortcut(shortcutName(lang), ctx.env))
121
+ out(ui(lang, "ui.setup.shortcut.done", { file: ctx.display(f) }));
122
+ }
123
+ catch {
124
+ // no desktop folder we may write to: the menu is still one command away
125
+ }
126
+ }
127
+ }
128
+ else
129
+ createShortcut(shortcutName(lang), ctx.env);
130
+ // Quietly: the installed agents learn about strom; the Strom app is noticed.
131
+ const taught = new Set();
132
+ for (const t of globalTargets(ctx.env).filter((t) => installed.includes(t.agent)))
133
+ if (installGlobal(t))
134
+ taught.add(t.agent);
135
+ for (const a of taught)
136
+ out(ui(lang, "ui.setup.skill", { agent: PROFILES[a].name }));
137
+ if (noticeStromApp(s, ctx.env, { look: true }))
138
+ out(ui(lang, "ui.setup.app"));
139
+ out();
140
+ out(`${ui(lang, "ui.setup.done")} (${langName(lang, lang)} · ${ctx.display(home)})`);
141
+ return { home, lang, ...(agent ? { agent } : {}), permissions: level };
142
+ }
package/dist/cli.js ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ // strom — genealogical research toolkit for AI agents.
3
+ import fs from "node:fs";
4
+ import { main } from "./cli/main.js";
5
+ // Windows consoles: make sure non-ASCII names print correctly.
6
+ if (process.platform === "win32")
7
+ process.stdout.setDefaultEncoding?.("utf8");
8
+ const code = await main(process.argv.slice(2), {
9
+ stdout: (s) => process.stdout.write(s),
10
+ stderr: (s) => process.stderr.write(s),
11
+ tty: Boolean(process.stdin.isTTY && process.stdout.isTTY),
12
+ ...(process.stdin.isTTY ? {} : { stdinText: () => fs.readFileSync(0, "utf8") }),
13
+ }, process.env, process.cwd());
14
+ process.exitCode = code;