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.
- package/LICENSE +373 -0
- package/README.md +142 -0
- package/assets/lang/cs.json +302 -0
- package/assets/lang/de.json +302 -0
- package/assets/method/core.md +43 -0
- package/assets/method/enrich.md +11 -0
- package/assets/method/intake.md +30 -0
- package/assets/method/link.md +28 -0
- package/assets/method/locate.md +28 -0
- package/assets/method/narrate.md +13 -0
- package/assets/method/reading.md +62 -0
- package/assets/method/recording.md +59 -0
- package/assets/method/request.md +10 -0
- package/assets/method/verify.md +17 -0
- package/assets/plugins/README.md +23 -0
- package/assets/plugins/connectors/DISCOVERY.md +159 -0
- package/assets/plugins/connectors/README.md +376 -0
- package/assets/plugins/connectors/sdk.ts +168 -0
- package/assets/plugins/connectors/template.ts +38 -0
- package/assets/plugins/gitignore +4 -0
- package/dist/agents/files.js +313 -0
- package/dist/agents/global.js +257 -0
- package/dist/agents/launch.js +36 -0
- package/dist/agents/profiles.js +95 -0
- package/dist/brief/brief.js +345 -0
- package/dist/cli/commit.js +44 -0
- package/dist/cli/context.js +311 -0
- package/dist/cli/execute.js +154 -0
- package/dist/cli/fixes.js +78 -0
- package/dist/cli/format.js +53 -0
- package/dist/cli/help.js +59 -0
- package/dist/cli/main.js +152 -0
- package/dist/cli/menu.js +212 -0
- package/dist/cli/registry.js +96 -0
- package/dist/cli/ui.js +266 -0
- package/dist/cli/wizard.js +142 -0
- package/dist/cli.js +14 -0
- package/dist/commands/analysis.js +622 -0
- package/dist/commands/batch.js +181 -0
- package/dist/commands/checks.js +153 -0
- package/dist/commands/connectors.js +1377 -0
- package/dist/commands/guide.js +160 -0
- package/dist/commands/index.js +19 -0
- package/dist/commands/intake.js +234 -0
- package/dist/commands/media.js +406 -0
- package/dist/commands/meta.js +195 -0
- package/dist/commands/output.js +117 -0
- package/dist/commands/people.js +664 -0
- package/dist/commands/read.js +199 -0
- package/dist/commands/research.js +139 -0
- package/dist/commands/session.js +605 -0
- package/dist/commands/setup.js +465 -0
- package/dist/commands/sources.js +634 -0
- package/dist/commands/start.js +383 -0
- package/dist/commands/story.js +75 -0
- package/dist/commands/tasks.js +436 -0
- package/dist/commands/trees.js +128 -0
- package/dist/core/actions.js +852 -0
- package/dist/core/age.js +95 -0
- package/dist/core/apps.js +74 -0
- package/dist/core/assets.js +34 -0
- package/dist/core/awake.js +33 -0
- package/dist/core/browser.js +281 -0
- package/dist/core/calibration.js +48 -0
- package/dist/core/check.js +112 -0
- package/dist/core/chromium.js +88 -0
- package/dist/core/config.js +348 -0
- package/dist/core/connector.js +811 -0
- package/dist/core/deps.js +73 -0
- package/dist/core/dialog.js +61 -0
- package/dist/core/errors.js +89 -0
- package/dist/core/evidence.js +58 -0
- package/dist/core/frontier.js +219 -0
- package/dist/core/gdate.js +77 -0
- package/dist/core/git.js +300 -0
- package/dist/core/guard.js +124 -0
- package/dist/core/http2.js +76 -0
- package/dist/core/import.js +541 -0
- package/dist/core/install.js +28 -0
- package/dist/core/integrity.js +219 -0
- package/dist/core/json.js +87 -0
- package/dist/core/lang.js +70 -0
- package/dist/core/live.js +244 -0
- package/dist/core/lock.js +112 -0
- package/dist/core/logins.js +67 -0
- package/dist/core/media.js +223 -0
- package/dist/core/model.js +101 -0
- package/dist/core/net.js +366 -0
- package/dist/core/open.js +29 -0
- package/dist/core/paths.js +84 -0
- package/dist/core/people.js +283 -0
- package/dist/core/phrases.js +85 -0
- package/dist/core/queue.js +113 -0
- package/dist/core/reader.js +76 -0
- package/dist/core/records.js +105 -0
- package/dist/core/roles.js +30 -0
- package/dist/core/schema.js +261 -0
- package/dist/core/seal.js +77 -0
- package/dist/core/self.js +40 -0
- package/dist/core/session.js +155 -0
- package/dist/core/shortcut.js +90 -0
- package/dist/core/stories.js +61 -0
- package/dist/core/stromapp.js +138 -0
- package/dist/core/text.js +104 -0
- package/dist/core/tree.js +507 -0
- package/dist/core/uninstall.js +128 -0
- package/dist/core/update.js +193 -0
- package/dist/core/validate.js +260 -0
- package/dist/core/views.js +164 -0
- package/dist/core/which.js +51 -0
- package/dist/core/workers.js +42 -0
- package/dist/gedcom/export.js +454 -0
- package/dist/gedcom/labels.js +103 -0
- package/dist/gedcom/lines.js +91 -0
- package/dist/gedcom/parse.js +53 -0
- package/dist/gedcom/validate.js +183 -0
- package/dist/image/image.js +223 -0
- package/dist/image/index.js +114 -0
- package/dist/image/jpeg-decode.js +552 -0
- package/dist/image/jpeg-encode.js +254 -0
- package/dist/image/png.js +241 -0
- package/dist/runners/antigravity.js +70 -0
- package/dist/runners/claude.js +179 -0
- package/dist/runners/codex.js +45 -0
- package/dist/runners/index.js +13 -0
- package/dist/runners/jsonl.js +86 -0
- package/dist/runners/opencode.js +50 -0
- package/dist/runners/runner.js +63 -0
- package/dist/runners/script.js +58 -0
- package/package.json +44 -0
|
@@ -0,0 +1,664 @@
|
|
|
1
|
+
// person add · list · show · edit · name add · family add · show · child · event add · note add · cite
|
|
2
|
+
import { register } from "../cli/registry.js";
|
|
3
|
+
import { lines, moreLine, paginate, table, truncate } from "../cli/format.js";
|
|
4
|
+
import { addChild, addEvent, addFamily, addName, addNote, addPerson, citeEvent, citeRecord, editEvent, editFamily, editPerson, findEventOwner, mergeFamilies, mergePersons, parseParticipant, retractEvent, retractPerson } from "../core/actions.js";
|
|
5
|
+
import { listOpt, normId, requireRecord } from "../core/records.js";
|
|
6
|
+
import { SINGLE_KINDS } from "../core/actions.js";
|
|
7
|
+
import { UsageError } from "../core/errors.js";
|
|
8
|
+
import { INFORMATION } from "../core/evidence.js";
|
|
9
|
+
import { EVENT_KINDS, NAME_KINDS } from "../core/model.js";
|
|
10
|
+
import { ancestorGenerations, displayName, familiesAsChild, familiesAsPartner, findPersons, formatName, label, lifespan, relationTo, resolvePerson, } from "../core/people.js";
|
|
11
|
+
import { resolveResearch } from "./research.js";
|
|
12
|
+
import { foldText } from "../core/text.js";
|
|
13
|
+
function list(v) {
|
|
14
|
+
if (v === undefined)
|
|
15
|
+
return [];
|
|
16
|
+
return (Array.isArray(v) ? v : [v]).map(String);
|
|
17
|
+
}
|
|
18
|
+
function resolveAll(tree, refs) {
|
|
19
|
+
return refs.map((r) => resolvePerson(tree, r).id);
|
|
20
|
+
}
|
|
21
|
+
const CITE_OPTIONS = [
|
|
22
|
+
{ name: "cite", type: "string", value: "<S…>", description: "the source these facts come from (makes them probable)" },
|
|
23
|
+
{ name: "locator", type: "string", value: "<where>", description: "where in the source: folio, entry, line" },
|
|
24
|
+
{ name: "quote", type: "string", value: "<text>", description: "the words of the record" },
|
|
25
|
+
{ name: "information", type: "string", value: "<kind>", description: "for these facts the source is primary (written at the time by someone who knew), secondary or unknown" },
|
|
26
|
+
{ name: "status", type: "string", value: "<status>", description: "lead (default without --cite), possible, probable, proven" },
|
|
27
|
+
];
|
|
28
|
+
const AGE_OPTION = {
|
|
29
|
+
name: "age",
|
|
30
|
+
type: "string",
|
|
31
|
+
multiple: true,
|
|
32
|
+
value: "<who:age>",
|
|
33
|
+
description: 'age of a partner as the record gives it: "P0001:25", "husband:25 let", "wife:22" (repeatable)',
|
|
34
|
+
};
|
|
35
|
+
/** "P0001:25", "Anna Svobodová:22", "husband:25" → person ID → age, among the partners of a family. */
|
|
36
|
+
function partnerAges(tree, specs, partners) {
|
|
37
|
+
if (specs.length === 0)
|
|
38
|
+
return undefined;
|
|
39
|
+
const out = {};
|
|
40
|
+
for (const spec of specs) {
|
|
41
|
+
const m = /^(.+?)\s*:\s*(.+)$/.exec(spec.trim());
|
|
42
|
+
if (!m)
|
|
43
|
+
throw new UsageError(`--age "${spec}": whose age? e.g. --age P0001:25 or husband:25`);
|
|
44
|
+
// "muž", "muz" and "Mann" alike: compared without diacritics
|
|
45
|
+
const who = foldText(m[1]);
|
|
46
|
+
let id;
|
|
47
|
+
if (["husband", "groom", "man", "muz", "zenich", "father", "otec", "mann", "brautigam", "vater"].includes(who))
|
|
48
|
+
id = partners.find((p) => tree.get(p)?.sex === "M");
|
|
49
|
+
else if (["wife", "bride", "woman", "zena", "nevesta", "mother", "matka", "frau", "braut", "mutter"].includes(who))
|
|
50
|
+
id = partners.find((p) => tree.get(p)?.sex === "F");
|
|
51
|
+
else
|
|
52
|
+
id = resolvePerson(tree, m[1].trim()).id;
|
|
53
|
+
if (!id || !partners.includes(id))
|
|
54
|
+
throw new UsageError(`--age "${spec}": not a partner of this family`, { hint: `partners: ${partners.join(", ") || "none"}` });
|
|
55
|
+
out[id] = m[2].trim();
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
/** A citation from --cite/--locator/--quote, or undefined. */
|
|
60
|
+
function citationOf(opts) {
|
|
61
|
+
if ((opts.locator || opts.quote) && !opts.cite)
|
|
62
|
+
throw new UsageError("--locator/--quote belong to a citation: add --cite <S…>");
|
|
63
|
+
if (!opts.cite) {
|
|
64
|
+
if (opts.information)
|
|
65
|
+
throw new UsageError("--information belongs to a citation: add --cite <S…>");
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
source: normId(opts.cite, "source"),
|
|
70
|
+
...(opts.locator ? { locator: String(opts.locator) } : {}),
|
|
71
|
+
...(opts.quote ? { quote: String(opts.quote) } : {}),
|
|
72
|
+
...(opts.information ? { information: informationOpt(opts.information) } : {}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function informationOpt(v) {
|
|
76
|
+
if (!INFORMATION.includes(v))
|
|
77
|
+
throw new UsageError(`invalid --information "${String(v)}"`, { hint: INFORMATION.join(", ") });
|
|
78
|
+
return v;
|
|
79
|
+
}
|
|
80
|
+
function written(tree) {
|
|
81
|
+
return lines(...tree.written.map((o) => o.summary), tree.dryRun ? "(dry run — nothing written)" : undefined);
|
|
82
|
+
}
|
|
83
|
+
function citesOf(citations) {
|
|
84
|
+
return (citations ?? []).map((c) => c.source + (c.locator ? ` ${c.locator}` : "") + (c.information ? ` (${c.information})` : "")).join(", ");
|
|
85
|
+
}
|
|
86
|
+
function nameLine(n) {
|
|
87
|
+
const cites = citesOf(n.citations);
|
|
88
|
+
return `${formatName(n)}${n.kind ? ` (${n.kind})` : ""}${cites ? ` ← ${cites}` : ""}`;
|
|
89
|
+
}
|
|
90
|
+
function eventLine(e) {
|
|
91
|
+
const cites = citesOf(e.citations);
|
|
92
|
+
const who = (e.participants ?? []).map((p) => `${p.role} ${p.person ?? p.name}`).join(", ");
|
|
93
|
+
return [
|
|
94
|
+
e.id,
|
|
95
|
+
e.kind,
|
|
96
|
+
e.label ?? "",
|
|
97
|
+
e.date ?? "",
|
|
98
|
+
[e.place, e.house ? `house ${e.house}` : ""].filter(Boolean).join(", "),
|
|
99
|
+
e.value ?? "",
|
|
100
|
+
e.age ? `age ${e.age}` : "",
|
|
101
|
+
e.ages ? `ages ${Object.entries(e.ages).map(([p, a]) => `${p} ${a}`).join(", ")}` : "",
|
|
102
|
+
`[${e.status}]`,
|
|
103
|
+
cites ? `← ${cites}` : "",
|
|
104
|
+
who ? `with ${who}` : "",
|
|
105
|
+
e.retracted ? `(retracted: ${e.retracted.reason})` : "",
|
|
106
|
+
]
|
|
107
|
+
.filter(Boolean)
|
|
108
|
+
.join(" ");
|
|
109
|
+
}
|
|
110
|
+
register({
|
|
111
|
+
path: ["person", "add"],
|
|
112
|
+
summary: "Add a person (facts without a record are leads)",
|
|
113
|
+
group: "people",
|
|
114
|
+
tree: true,
|
|
115
|
+
writes: true,
|
|
116
|
+
description: "--cite goes to the birth/death given here; without them it cites the name (a record naming a parent or grandparent).",
|
|
117
|
+
args: [{ name: "name", description: 'full name, surname in slashes: "Jan /Novák/"', required: true }],
|
|
118
|
+
options: [
|
|
119
|
+
{ name: "sex", type: "string", value: "M|F|U", description: "sex (default U)" },
|
|
120
|
+
{ name: "born", type: "string", value: "<date>", description: "birth date (lead)" },
|
|
121
|
+
{ name: "born-place", type: "string", value: "<place>", description: "birth place (lead)" },
|
|
122
|
+
{ name: "died", type: "string", value: "<date>", description: "death date (lead)" },
|
|
123
|
+
{ name: "died-place", type: "string", value: "<place>", description: "death place (lead)" },
|
|
124
|
+
{ name: "note", type: "string", value: "<text>", description: "short note" },
|
|
125
|
+
...CITE_OPTIONS,
|
|
126
|
+
],
|
|
127
|
+
examples: [
|
|
128
|
+
'strom person add "Josef /Novák/" --sex M --born "ABT 1870" --note "Father of Jan, miller (family memory)"',
|
|
129
|
+
'strom person add "Jan /Novák/" --sex M --born "24 JUN 1885" --born-place "Kamenice nad Lipou" --cite S0001 --locator "fol. 12, č. 3"',
|
|
130
|
+
'strom person add "Šimon /Ševčík/" --sex M --cite S0002 --locator "fol. 45, č. 12" --information secondary',
|
|
131
|
+
],
|
|
132
|
+
run(ctx, { args, opts }) {
|
|
133
|
+
const tree = ctx.tree();
|
|
134
|
+
// Without --born/--died the citation is on the name: the record names this person.
|
|
135
|
+
if (opts.status && !opts.born && !opts["born-place"] && !opts.died && !opts["died-place"])
|
|
136
|
+
throw new UsageError("--status belongs to the birth or death given here (--born/--died)");
|
|
137
|
+
const p = addPerson(tree, {
|
|
138
|
+
name: args[0],
|
|
139
|
+
sex: opts.sex,
|
|
140
|
+
born: opts.born,
|
|
141
|
+
bornPlace: opts["born-place"],
|
|
142
|
+
died: opts.died,
|
|
143
|
+
diedPlace: opts["died-place"],
|
|
144
|
+
note: opts.note,
|
|
145
|
+
citation: citationOf(opts),
|
|
146
|
+
status: opts.status,
|
|
147
|
+
});
|
|
148
|
+
const similar = findPersons(tree, formatName(p.names[0])).filter((x) => x.id !== p.id);
|
|
149
|
+
const warn = similar.length ? `note: similar name already in the tree: ${similar.slice(0, 3).map(label).join(" · ")}` : undefined;
|
|
150
|
+
return { text: lines(written(tree), warn), data: { person: p, similar: similar.map((x) => x.id) } };
|
|
151
|
+
},
|
|
152
|
+
}, {
|
|
153
|
+
path: ["person", "list"],
|
|
154
|
+
summary: "List people (filter by name, research, generation)",
|
|
155
|
+
group: "people",
|
|
156
|
+
tree: true,
|
|
157
|
+
args: [{ name: "filter", description: "part of a name (diacritics optional)" }],
|
|
158
|
+
options: [
|
|
159
|
+
{ name: "research", type: "string", value: "<G…>", description: "only people in scope of this research, with generations" },
|
|
160
|
+
{ name: "generation", type: "string", value: "<n>", description: "only this generation (with --research)" },
|
|
161
|
+
{ name: "full", type: "boolean", description: "--json: whole records instead of one row each" },
|
|
162
|
+
],
|
|
163
|
+
examples: ["strom person list", "strom person list visek", "strom person list --research G0001 --generation 3"],
|
|
164
|
+
run(ctx, { args, opts }) {
|
|
165
|
+
const tree = ctx.tree();
|
|
166
|
+
let people = tree.list("person").filter((p) => !p.retracted);
|
|
167
|
+
if (args[0])
|
|
168
|
+
people = findPersons(tree, args[0]);
|
|
169
|
+
let gens;
|
|
170
|
+
if (opts.research) {
|
|
171
|
+
const r = resolveResearch(tree, opts.research);
|
|
172
|
+
gens = ancestorGenerations(tree, r.focus);
|
|
173
|
+
people = people.filter((p) => gens.has(p.id));
|
|
174
|
+
if (opts.generation)
|
|
175
|
+
people = people.filter((p) => gens.get(p.id) === Number(opts.generation));
|
|
176
|
+
people.sort((a, b) => gens.get(a.id) - gens.get(b.id) || a.id.localeCompare(b.id));
|
|
177
|
+
}
|
|
178
|
+
const page = paginate(people, ctx.limit, ctx.page);
|
|
179
|
+
const rows = page.items.map((p) => [
|
|
180
|
+
p.id,
|
|
181
|
+
displayName(p),
|
|
182
|
+
lifespan(p),
|
|
183
|
+
gens ? `G${gens.get(p.id)}` : "",
|
|
184
|
+
`${p.events.length} ev`,
|
|
185
|
+
p.events.some((e) => e.status === "proven" || e.status === "probable") ? "" : "leads only",
|
|
186
|
+
]);
|
|
187
|
+
const text = people.length === 0 ? "no people match" : lines(table(rows), moreLine(page, "strom person list"));
|
|
188
|
+
return {
|
|
189
|
+
text,
|
|
190
|
+
data: {
|
|
191
|
+
total: page.total,
|
|
192
|
+
page: page.page,
|
|
193
|
+
pages: page.pages,
|
|
194
|
+
persons: page.items.map((p) => opts.full
|
|
195
|
+
? { ...p, generation: gens?.get(p.id) }
|
|
196
|
+
: { id: p.id, name: displayName(p), lifespan: lifespan(p), sex: p.sex, events: p.events.length, ...(gens ? { generation: gens.get(p.id) } : {}) }),
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
}, {
|
|
201
|
+
path: ["person", "show"],
|
|
202
|
+
summary: "Everything about one person: facts, family, notes",
|
|
203
|
+
group: "people",
|
|
204
|
+
tree: true,
|
|
205
|
+
args: [{ name: "person", description: "ID (P0001) or name", required: true }],
|
|
206
|
+
examples: ["strom person show P0001", 'strom person show "Antonín Víšek"'],
|
|
207
|
+
run(ctx, { args }) {
|
|
208
|
+
const tree = ctx.tree();
|
|
209
|
+
const p = resolvePerson(tree, args[0]);
|
|
210
|
+
const parents = familiesAsChild(tree, p.id);
|
|
211
|
+
const partnerships = familiesAsPartner(tree, p.id);
|
|
212
|
+
const researches = tree.list("research").filter((r) => ancestorGenerations(tree, r.focus).has(p.id));
|
|
213
|
+
const out = [
|
|
214
|
+
`${p.id} ${displayName(p)}${lifespan(p) ? ` (${lifespan(p)})` : ""} sex ${p.sex}`,
|
|
215
|
+
...(p.names.length > 1 || p.names.some((n) => n.citations?.length) ? ["", "names", ...p.names.map((n) => ` ${nameLine(n)}`)] : []),
|
|
216
|
+
"",
|
|
217
|
+
"facts",
|
|
218
|
+
p.events.length ? table(p.events.map((e) => [` ${eventLine(e)}`])) : " (none)",
|
|
219
|
+
...p.events.filter((e) => e.note).map((e) => ` ${e.id} note: ${truncate(e.note, 160)}`),
|
|
220
|
+
"",
|
|
221
|
+
"family",
|
|
222
|
+
];
|
|
223
|
+
for (const f of parents) {
|
|
224
|
+
const link = f.children.find((c) => c.person === p.id);
|
|
225
|
+
const names = f.partners.map((id) => {
|
|
226
|
+
const x = tree.get(id);
|
|
227
|
+
const rel = relationTo(link, id);
|
|
228
|
+
return `${x ? label(x) : id}${rel !== "birth" && link.relations ? ` [${rel}]` : ""}`;
|
|
229
|
+
});
|
|
230
|
+
out.push(` parents ${f.id}: ${names.join(" & ") || "(unknown)"}${link.relation !== "birth" && !link.relations ? ` [${link.relation}]` : ""}`);
|
|
231
|
+
}
|
|
232
|
+
if (parents.length === 0)
|
|
233
|
+
out.push(" parents (unknown)");
|
|
234
|
+
for (const f of partnerships) {
|
|
235
|
+
const partner = f.partners.filter((id) => id !== p.id).map((id) => tree.get(id)).filter(Boolean).map((x) => label(x));
|
|
236
|
+
out.push(` partner ${f.id}: ${partner.join(", ") || "(unknown)"}`);
|
|
237
|
+
for (const e of f.events)
|
|
238
|
+
out.push(` ${eventLine(e)}`);
|
|
239
|
+
for (const c of f.children) {
|
|
240
|
+
const child = tree.get(c.person);
|
|
241
|
+
if (child)
|
|
242
|
+
out.push(` child ${label(child)}${c.relation !== "birth" ? ` [${c.relation}]` : ""}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (researches.length)
|
|
246
|
+
out.push("", `in research ${researches.map((r) => `${r.id} ${r.name}`).join(" · ")}`);
|
|
247
|
+
if (p.notes.length)
|
|
248
|
+
out.push("", "notes", ...p.notes.map((n) => ` ${n.at.slice(0, 10)} ${n.text}`));
|
|
249
|
+
return {
|
|
250
|
+
text: lines(...out),
|
|
251
|
+
data: { person: p, parentFamilies: parents, partnerFamilies: partnerships, researches: researches.map((r) => r.id) },
|
|
252
|
+
};
|
|
253
|
+
},
|
|
254
|
+
}, {
|
|
255
|
+
path: ["person", "edit"],
|
|
256
|
+
summary: "Correct a person: sex, the spelling of the name they are shown by",
|
|
257
|
+
group: "people",
|
|
258
|
+
tree: true,
|
|
259
|
+
writes: true,
|
|
260
|
+
description: "Another name (a birth surname, a married name, another spelling) is added with strom name add, not edited here.",
|
|
261
|
+
args: [{ name: "person", description: "ID (P0001) or name", required: true }],
|
|
262
|
+
options: [
|
|
263
|
+
{ name: "sex", type: "string", value: "M|F|U", description: "sex" },
|
|
264
|
+
{ name: "name", type: "string", value: "<name>", description: 'the corrected name, surname in slashes: "Jan /Novák/"' },
|
|
265
|
+
],
|
|
266
|
+
examples: ["strom person edit P0004 --sex M", 'strom person edit P0001 --name "Antonín /Víšek/" --reason "misread: the register has Víšek"'],
|
|
267
|
+
run(ctx, { args, opts }) {
|
|
268
|
+
const tree = ctx.tree();
|
|
269
|
+
const p = editPerson(tree, resolvePerson(tree, args[0]).id, { sex: opts.sex, name: opts.name }, opts.reason);
|
|
270
|
+
return { text: written(tree), data: { person: p } };
|
|
271
|
+
},
|
|
272
|
+
}, {
|
|
273
|
+
path: ["person", "retract"],
|
|
274
|
+
summary: "Withdraw a person who turned out not to exist (kept, marked retracted) — a duplicate is merged instead",
|
|
275
|
+
group: "people",
|
|
276
|
+
tree: true,
|
|
277
|
+
writes: true,
|
|
278
|
+
args: [{ name: "person", description: "ID or name", required: true }],
|
|
279
|
+
examples: ['strom person retract P0004 --reason "misread: the entry names Anna, not Anton"'],
|
|
280
|
+
run(ctx, { args, opts }) {
|
|
281
|
+
const tree = ctx.tree();
|
|
282
|
+
const p = retractPerson(tree, resolvePerson(tree, args[0]).id, opts.reason);
|
|
283
|
+
return { text: written(tree), data: { person: p } };
|
|
284
|
+
},
|
|
285
|
+
}, {
|
|
286
|
+
path: ["person", "merge"],
|
|
287
|
+
summary: "Two records of one person: everything of the second goes to the first",
|
|
288
|
+
group: "people",
|
|
289
|
+
tree: true,
|
|
290
|
+
writes: true,
|
|
291
|
+
description: "Names, facts, notes, families and every reference (tasks, hypotheses, godparents …) move to the first; the\n" +
|
|
292
|
+
"second stays, retracted, as \"merged into\". Facts are not merged: two births afterwards are a conflict to\n" +
|
|
293
|
+
"resolve (cite one, retract the other). Different birth parents: merge those families first.",
|
|
294
|
+
args: [
|
|
295
|
+
{ name: "keep", description: "the person that stays (ID or name)", required: true },
|
|
296
|
+
{ name: "other", description: "the duplicate (ID or name)", required: true },
|
|
297
|
+
],
|
|
298
|
+
examples: ['strom person merge P0001 P0004 --reason "the same man: born 1811 in Týnec, son of Jakub"'],
|
|
299
|
+
run(ctx, { args, opts }) {
|
|
300
|
+
const tree = ctx.tree();
|
|
301
|
+
const keep = resolvePerson(tree, args[0]).id;
|
|
302
|
+
const other = resolvePerson(tree, args[1]).id;
|
|
303
|
+
const r = mergePersons(tree, keep, other, opts.reason);
|
|
304
|
+
const kinds = r.person.events.filter((e) => !e.retracted).map((e) => e.kind);
|
|
305
|
+
const twice = [...new Set(kinds.filter((k, i) => SINGLE_KINDS.has(k) && kinds.indexOf(k) !== i))];
|
|
306
|
+
const warn = twice.length ? `note: ${keep} now has ${twice.join(", ")} twice — keep the better one (strom cite …) and retract the other (strom event retract E… --reason …)` : undefined;
|
|
307
|
+
return { text: lines(written(tree), warn), data: { person: r.person, repointed: r.repointed } };
|
|
308
|
+
},
|
|
309
|
+
}, {
|
|
310
|
+
path: ["name", "add"],
|
|
311
|
+
summary: "Another name of a person — the birth surname a record gives, a married name, another spelling — with its record",
|
|
312
|
+
group: "people",
|
|
313
|
+
tree: true,
|
|
314
|
+
writes: true,
|
|
315
|
+
description: "A name without a surname (\"Markéta\") is completed by the full one and the person is shown by it; the same\n" +
|
|
316
|
+
"name again only adds its citation. A woman is shown by her birth name; --kind married keeps the married one aside.",
|
|
317
|
+
args: [
|
|
318
|
+
{ name: "person", description: "ID (P0001) or name", required: true },
|
|
319
|
+
{ name: "name", description: 'the name, surname in slashes: "Markéta /Růžičková/"', required: true },
|
|
320
|
+
],
|
|
321
|
+
options: [
|
|
322
|
+
{ name: "kind", type: "string", value: "<kind>", description: `${NAME_KINDS.join(", ")} (default: plain)` },
|
|
323
|
+
{ name: "primary", type: "boolean", description: "show the person by this name" },
|
|
324
|
+
...CITE_OPTIONS.filter((o) => o.name !== "status"),
|
|
325
|
+
],
|
|
326
|
+
examples: [
|
|
327
|
+
'strom name add P0002 "Markéta /Růžičková/" --kind birth --cite S0002 --locator "fol. 45, č. 12" --quote "Margaretha, Tochter nach Johann Ružička"',
|
|
328
|
+
'strom name add P0002 "Marie /Nováková/" --kind married',
|
|
329
|
+
],
|
|
330
|
+
run(ctx, { args, opts }) {
|
|
331
|
+
const tree = ctx.tree();
|
|
332
|
+
const p = resolvePerson(tree, args[0]);
|
|
333
|
+
const r = addName(tree, p.id, { name: args[1], kind: opts.kind, citation: citationOf(opts), primary: Boolean(opts.primary) });
|
|
334
|
+
return { text: written(tree), data: { person: r.person, name: r.name } };
|
|
335
|
+
},
|
|
336
|
+
}, {
|
|
337
|
+
path: ["family", "add"],
|
|
338
|
+
summary: "Add a family: partners and/or children",
|
|
339
|
+
group: "people",
|
|
340
|
+
tree: true,
|
|
341
|
+
writes: true,
|
|
342
|
+
description: "--cite goes to the marriage given here; without one it cites the family itself — the record that names\n" +
|
|
343
|
+
"these parents (a grandchild's baptism naming the grandparents). A child's own baptism is cited on its CHR/BIRT.",
|
|
344
|
+
options: [
|
|
345
|
+
{ name: "partner", type: "string", multiple: true, value: "<who>", description: "a partner (repeat for two)" },
|
|
346
|
+
{ name: "child", type: "string", multiple: true, value: "<who>", description: "a child (repeatable)" },
|
|
347
|
+
{ name: "relation", type: "string", value: "<rel>", description: "children's relation: birth (default), adopted, step, foster, unknown" },
|
|
348
|
+
{ name: "married", type: "string", value: "<date>", description: "marriage date (lead)" },
|
|
349
|
+
{ name: "married-place", type: "string", value: "<place>", description: "marriage place (lead)" },
|
|
350
|
+
{ name: "note", type: "string", value: "<text>", description: "short note" },
|
|
351
|
+
AGE_OPTION,
|
|
352
|
+
...CITE_OPTIONS,
|
|
353
|
+
],
|
|
354
|
+
examples: [
|
|
355
|
+
"strom family add --partner P0002 --partner P0003 --child P0001",
|
|
356
|
+
'strom family add --partner P0002 --partner P0003 --married "12 FEB 1898" --age husband:28 --age wife:22 --cite S0002',
|
|
357
|
+
'strom family add --partner "Josef Novák" --married "1898"',
|
|
358
|
+
'strom family add --partner P0001 --partner P0002 --married "12 FEB 1910" --married-place "Kamenice nad Lipou" --cite S0001',
|
|
359
|
+
'strom family add --partner P0003 --child P0002 --cite S0002 --locator "fol. 45, č. 12" --information secondary',
|
|
360
|
+
],
|
|
361
|
+
run(ctx, { opts }) {
|
|
362
|
+
const tree = ctx.tree();
|
|
363
|
+
const partners = resolveAll(tree, list(opts.partner));
|
|
364
|
+
if ((opts.status || listOpt(opts.age).length) && !opts.married && !opts["married-place"])
|
|
365
|
+
throw new UsageError("--status and --age belong to the marriage: give --married <date> (or --married-place)", {
|
|
366
|
+
hint: "without a marriage --cite cites the family itself: the record that names these parents",
|
|
367
|
+
});
|
|
368
|
+
// The same couple twice is almost always a duplicate.
|
|
369
|
+
const existing = partners.length === 2 ? familiesAsPartner(tree, partners[0]).find((f) => f.partners.includes(partners[1])) : undefined;
|
|
370
|
+
const f = addFamily(tree, {
|
|
371
|
+
partners,
|
|
372
|
+
children: resolveAll(tree, list(opts.child)),
|
|
373
|
+
relation: opts.relation,
|
|
374
|
+
married: opts.married,
|
|
375
|
+
marriedPlace: opts["married-place"],
|
|
376
|
+
note: opts.note,
|
|
377
|
+
citation: citationOf(opts),
|
|
378
|
+
status: opts.status,
|
|
379
|
+
ages: partnerAges(tree, listOpt(opts.age), partners),
|
|
380
|
+
});
|
|
381
|
+
const warn = existing ? `note: ${existing.id} already joins these partners — if it is the same couple, add to it: strom family child ${existing.id} <child> · strom event add ${existing.id} MARR …` : undefined;
|
|
382
|
+
return { text: lines(written(tree), warn), data: { family: f, ...(existing ? { sameCouple: existing.id } : {}) } };
|
|
383
|
+
},
|
|
384
|
+
}, {
|
|
385
|
+
path: ["family", "show"],
|
|
386
|
+
summary: "One family: partners, facts with their IDs, children",
|
|
387
|
+
group: "people",
|
|
388
|
+
tree: true,
|
|
389
|
+
args: [{ name: "family", description: "family ID (F0001)", required: true }],
|
|
390
|
+
run(ctx, { args }) {
|
|
391
|
+
const tree = ctx.tree();
|
|
392
|
+
const f = requireRecord(tree, args[0], "family");
|
|
393
|
+
const partners = f.partners.map((id) => tree.get(id)).filter(Boolean).map((p) => label(p));
|
|
394
|
+
const out = [
|
|
395
|
+
`${f.id} ${partners.join(" & ") || "(no partners known)"}`,
|
|
396
|
+
f.citations?.length ? `evidence of the family ← ${citesOf(f.citations)}` : undefined,
|
|
397
|
+
"",
|
|
398
|
+
"facts",
|
|
399
|
+
f.events.length ? table(f.events.map((e) => [` ${eventLine(e)}`])) : " (none)",
|
|
400
|
+
...f.events.filter((e) => e.note).map((e) => ` ${e.id} note: ${truncate(e.note, 160)}`),
|
|
401
|
+
"",
|
|
402
|
+
"children",
|
|
403
|
+
...(f.children.length
|
|
404
|
+
? f.children.map((c) => {
|
|
405
|
+
const child = tree.get(c.person);
|
|
406
|
+
const own = Object.entries(c.relations ?? {}).map(([who, rel]) => `${rel} of ${who}`);
|
|
407
|
+
return ` ${child ? label(child) : c.person}${c.relation !== "birth" || own.length ? ` [${[c.relation !== "birth" ? c.relation : "", ...own].filter(Boolean).join(", ")}]` : ""}`;
|
|
408
|
+
})
|
|
409
|
+
: [" (none)"]),
|
|
410
|
+
...(f.notes.length ? ["", "notes", ...f.notes.map((n) => ` ${n.at.slice(0, 10)} ${n.text}`)] : []),
|
|
411
|
+
];
|
|
412
|
+
return { text: lines(...out), data: { family: f } };
|
|
413
|
+
},
|
|
414
|
+
}, {
|
|
415
|
+
path: ["family", "edit"],
|
|
416
|
+
summary: "Change who belongs to a family and how: the partner found later, a stepchild of one parent, a wrong link",
|
|
417
|
+
group: "people",
|
|
418
|
+
tree: true,
|
|
419
|
+
writes: true,
|
|
420
|
+
description: "--relation with --parent is the child's relation to that parent only (a stepchild of the husband, the wife's own).\n" +
|
|
421
|
+
"Everything but adding a partner needs --reason.",
|
|
422
|
+
args: [{ name: "family", description: "family ID (F0001)", required: true }],
|
|
423
|
+
options: [
|
|
424
|
+
{ name: "partner", type: "string", value: "<who>", description: "add the other partner" },
|
|
425
|
+
{ name: "child", type: "string", value: "<who>", description: "the child whose relation changes (with --relation)" },
|
|
426
|
+
{ name: "relation", type: "string", value: "<rel>", description: "birth, adopted, step, foster, unknown" },
|
|
427
|
+
{ name: "parent", type: "string", value: "<who>", description: "the relation is to this partner only" },
|
|
428
|
+
{ name: "remove", type: "string", value: "<who>", description: "take out a person linked by mistake" },
|
|
429
|
+
],
|
|
430
|
+
examples: [
|
|
431
|
+
"strom family edit F0002 --partner P0003",
|
|
432
|
+
'strom family edit F0002 --child P0006 --relation step --parent P0004 --reason "the baptism names Antonín as stepfather"',
|
|
433
|
+
],
|
|
434
|
+
run(ctx, { args, opts }) {
|
|
435
|
+
const tree = ctx.tree();
|
|
436
|
+
const who = (v) => (typeof v === "string" ? resolvePerson(tree, v).id : undefined);
|
|
437
|
+
const f = editFamily(tree, normId(args[0], "family"), { partner: who(opts.partner), child: who(opts.child), relation: opts.relation, parent: who(opts.parent), remove: who(opts.remove) }, opts.reason);
|
|
438
|
+
return { text: written(tree), data: { family: f } };
|
|
439
|
+
},
|
|
440
|
+
}, {
|
|
441
|
+
path: ["family", "merge"],
|
|
442
|
+
summary: "Two records of one couple: children, facts and citations of the second go to the first",
|
|
443
|
+
group: "people",
|
|
444
|
+
tree: true,
|
|
445
|
+
writes: true,
|
|
446
|
+
args: [
|
|
447
|
+
{ name: "keep", description: "the family that stays (F…)", required: true },
|
|
448
|
+
{ name: "other", description: "the duplicate (F…)", required: true },
|
|
449
|
+
],
|
|
450
|
+
examples: ['strom family merge F0001 F0002 --reason "the same couple, recorded twice"'],
|
|
451
|
+
run(ctx, { args, opts }) {
|
|
452
|
+
const tree = ctx.tree();
|
|
453
|
+
const r = mergeFamilies(tree, normId(args[0], "family"), normId(args[1], "family"), opts.reason);
|
|
454
|
+
return { text: written(tree), data: { family: r.family, repointed: r.repointed } };
|
|
455
|
+
},
|
|
456
|
+
}, {
|
|
457
|
+
path: ["family", "child"],
|
|
458
|
+
summary: "Add a child to an existing family (with the record naming its parents)",
|
|
459
|
+
group: "people",
|
|
460
|
+
tree: true,
|
|
461
|
+
writes: true,
|
|
462
|
+
args: [
|
|
463
|
+
{ name: "family", description: "family ID (F0001)", required: true },
|
|
464
|
+
{ name: "child", description: "child (ID or name)", required: true },
|
|
465
|
+
],
|
|
466
|
+
options: [{ name: "relation", type: "string", value: "<rel>", description: "birth (default), adopted, step, foster, unknown" }, ...CITE_OPTIONS.filter((o) => o.name !== "status")],
|
|
467
|
+
examples: ['strom family child F0001 P0003 --cite S0002 --locator "fol. 45, č. 12"'],
|
|
468
|
+
run(ctx, { args, opts }) {
|
|
469
|
+
const tree = ctx.tree();
|
|
470
|
+
const f = addChild(tree, normId(args[0], "family"), resolvePerson(tree, args[1]).id, opts.relation, citationOf(opts));
|
|
471
|
+
return { text: written(tree), data: { family: f } };
|
|
472
|
+
},
|
|
473
|
+
}, {
|
|
474
|
+
path: ["event", "add"],
|
|
475
|
+
summary: "Add a fact (birth, baptism, death, marriage, occupation, ...) to a person or family",
|
|
476
|
+
group: "people",
|
|
477
|
+
tree: true,
|
|
478
|
+
writes: true,
|
|
479
|
+
description: `Kinds: ${Object.keys(EVENT_KINDS).join(" ")} (or words like birth, death, occupation).\nWithout a citation a fact is a lead; proven/probable need a source.`,
|
|
480
|
+
args: [
|
|
481
|
+
{ name: "who", description: "person (ID or name) or family ID", required: true },
|
|
482
|
+
{ name: "kind", description: "event kind, e.g. BIRT, CHR, DEAT, MARR, OCCU, RESI", required: true },
|
|
483
|
+
],
|
|
484
|
+
options: [
|
|
485
|
+
{ name: "date", type: "string", value: "<date>", description: 'GEDCOM date: "24 JUN 1783", "ABT 1783", "BET 1811 AND 1812"' },
|
|
486
|
+
{ name: "place", type: "string", value: "<place>", description: 'the settlement: "Týnec nad Labem" — never with a house number' },
|
|
487
|
+
{ name: "house", type: "string", value: "<no.>", description: 'house number or address in the place, as the record gives it: "13"' },
|
|
488
|
+
{ name: "cause", type: "string", value: "<text>", description: "cause of death as the record gives it" },
|
|
489
|
+
{ name: "value", type: "string", value: "<text>", description: "value of attribute events, e.g. the occupation" },
|
|
490
|
+
{ name: "label", type: "string", value: "<text>", description: "what happened (required for EVEN)" },
|
|
491
|
+
{ name: "age", type: "string", multiple: true, value: "<age|who:age>", description: 'age as the record gives it: "61", "27 let 3 měsíce"; for a family event per partner: "P0001:25", "wife:22"' },
|
|
492
|
+
{ name: "cite", type: "string", value: "<S…>", description: "source proving it (makes it probable by default)" },
|
|
493
|
+
{ name: "locator", type: "string", value: "<where>", description: "where in the source: folio, entry, line" },
|
|
494
|
+
{ name: "quote", type: "string", value: "<text>", description: "the words of the record" },
|
|
495
|
+
{ name: "information", type: "string", value: "<kind>", description: "for this fact the source is primary, secondary or unknown (default: the source's)" },
|
|
496
|
+
{ name: "with", type: "string", multiple: true, value: "<role:who>", description: "participant: godparent:Marie Dvořáková, witness:P0012 — roles godparent, witness, officiant, midwife, informant, other (repeatable)" },
|
|
497
|
+
{ name: "status", type: "string", value: "<status>", description: "lead (default without citation), possible, probable, proven, disproven" },
|
|
498
|
+
{ name: "note", type: "string", value: "<text>", description: "short note on this fact" },
|
|
499
|
+
],
|
|
500
|
+
examples: [
|
|
501
|
+
'strom event add P0001 CHR --date "25 JUN 1905" --place "Týnec nad Labem" --house 12 --cite S0001 --locator "fol. 45, č. 12" --with "godparent:Marie Dvořáková" --status proven',
|
|
502
|
+
'strom event add P0002 OCCU --value "miller" --status lead',
|
|
503
|
+
'strom event add P0002 EVEN --label "Fire of the farmhouse" --date "NOV 1905"',
|
|
504
|
+
],
|
|
505
|
+
run(ctx, { args, opts }) {
|
|
506
|
+
const tree = ctx.tree();
|
|
507
|
+
const ref = args[0];
|
|
508
|
+
const owner = /^F\d+$/i.test(ref) ? normId(ref) : resolvePerson(tree, ref).id;
|
|
509
|
+
const cited = citationOf(opts);
|
|
510
|
+
const citations = cited ? [cited] : [];
|
|
511
|
+
const ages = listOpt(opts.age);
|
|
512
|
+
let age;
|
|
513
|
+
let partnerAge;
|
|
514
|
+
if (owner.startsWith("F"))
|
|
515
|
+
partnerAge = partnerAges(tree, ages, tree.get(owner)?.partners ?? []);
|
|
516
|
+
else if (ages.length > 1)
|
|
517
|
+
throw new UsageError("one --age for a person's event");
|
|
518
|
+
else
|
|
519
|
+
age = ages[0];
|
|
520
|
+
const r = addEvent(tree, owner, {
|
|
521
|
+
kind: args[1],
|
|
522
|
+
date: opts.date,
|
|
523
|
+
place: opts.place,
|
|
524
|
+
house: opts.house,
|
|
525
|
+
cause: opts.cause,
|
|
526
|
+
value: opts.value,
|
|
527
|
+
label: opts.label,
|
|
528
|
+
age,
|
|
529
|
+
ages: partnerAge,
|
|
530
|
+
participants: listOpt(opts.with).length ? opts.with.map((w) => parseParticipant(tree, w)) : undefined,
|
|
531
|
+
citations,
|
|
532
|
+
status: opts.status,
|
|
533
|
+
note: opts.note,
|
|
534
|
+
});
|
|
535
|
+
const warn = r.same
|
|
536
|
+
? `note: ${r.owner.id} already has ${r.same.kind} ${r.same.id} (${[r.same.date, r.same.place].filter(Boolean).join(", ") || "no date"}, ${r.same.status}). ` +
|
|
537
|
+
`The same fact from another record → strom cite ${r.same.id} <S…> (and retract ${r.event.id}); records that disagree → strom conflict add …`
|
|
538
|
+
: undefined;
|
|
539
|
+
return { text: lines(written(tree), warn), data: { event: r.event, owner: r.owner.id, ...(r.same ? { same: r.same.id } : {}) } };
|
|
540
|
+
},
|
|
541
|
+
}, {
|
|
542
|
+
path: ["note", "add"],
|
|
543
|
+
summary: "Add a short note to a person, family or research",
|
|
544
|
+
group: "people",
|
|
545
|
+
tree: true,
|
|
546
|
+
writes: true,
|
|
547
|
+
args: [
|
|
548
|
+
{ name: "id", description: "record ID (P0001, F0001, G0001) or person name", required: true },
|
|
549
|
+
{ name: "text", description: "the note (max 500 characters)", required: true },
|
|
550
|
+
],
|
|
551
|
+
run(ctx, { args }) {
|
|
552
|
+
const tree = ctx.tree();
|
|
553
|
+
const ref = args[0];
|
|
554
|
+
const id = /^[A-Z]\d+$/i.test(ref) ? normId(ref) : resolvePerson(tree, ref).id;
|
|
555
|
+
const note = addNote(tree, id, args[1]);
|
|
556
|
+
return { text: written(tree), data: { id, note } };
|
|
557
|
+
},
|
|
558
|
+
});
|
|
559
|
+
register({
|
|
560
|
+
path: ["cite"],
|
|
561
|
+
summary: "Attach a source to a fact (a lead becomes probable unless --status says otherwise), a person's name or a family",
|
|
562
|
+
group: "people",
|
|
563
|
+
tree: true,
|
|
564
|
+
writes: true,
|
|
565
|
+
description: "A fact (E…) gets the citation and its status; a person (P…) — the name they are shown by; a family (F…) — the family itself.",
|
|
566
|
+
args: [
|
|
567
|
+
{ name: "what", description: "event ID (E0001), person (P0001) or family (F0001)", required: true },
|
|
568
|
+
{ name: "source", description: "source ID (S0001)", required: true },
|
|
569
|
+
],
|
|
570
|
+
options: [
|
|
571
|
+
{ name: "locator", type: "string", value: "<where>", description: "where in the source: folio, entry, line" },
|
|
572
|
+
{ name: "quote", type: "string", value: "<text>", description: "the words of the record" },
|
|
573
|
+
{ name: "information", type: "string", value: "<kind>", description: "for this fact the source is primary, secondary or unknown (default: the source's)" },
|
|
574
|
+
{ name: "status", type: "string", value: "<status>", description: "new status: probable (default) or proven — proven only when you read the record yourself" },
|
|
575
|
+
],
|
|
576
|
+
examples: ['strom cite E0001 S0001 --locator "fol. 45, č. 12" --status proven', 'strom cite F0001 S0001 --locator "fol. 45, č. 12" --information secondary'],
|
|
577
|
+
run(ctx, { args, opts }) {
|
|
578
|
+
const tree = ctx.tree();
|
|
579
|
+
const citation = {
|
|
580
|
+
source: normId(args[1], "source"),
|
|
581
|
+
...(opts.locator ? { locator: String(opts.locator) } : {}),
|
|
582
|
+
...(opts.quote ? { quote: String(opts.quote) } : {}),
|
|
583
|
+
...(opts.information ? { information: informationOpt(opts.information) } : {}),
|
|
584
|
+
};
|
|
585
|
+
const id = normId(args[0]);
|
|
586
|
+
if (/^[PF]\d+$/.test(id)) {
|
|
587
|
+
if (opts.status)
|
|
588
|
+
throw new UsageError("--status is for facts (E…)", { hint: "a name or a family has no status: its citations say how well it is attested" });
|
|
589
|
+
const rec = citeRecord(tree, id, citation);
|
|
590
|
+
return { text: written(tree), data: { [rec.type]: rec } };
|
|
591
|
+
}
|
|
592
|
+
const e = citeEvent(tree, id, citation, opts.status);
|
|
593
|
+
return { text: written(tree), data: { event: e } };
|
|
594
|
+
},
|
|
595
|
+
}, {
|
|
596
|
+
path: ["event", "edit"],
|
|
597
|
+
summary: "Change a fact, or add what a record says about it (ages, house, witnesses) — changes need a reason",
|
|
598
|
+
group: "people",
|
|
599
|
+
tree: true,
|
|
600
|
+
writes: true,
|
|
601
|
+
args: [{ name: "event", description: "event ID (E0001)", required: true }],
|
|
602
|
+
options: [
|
|
603
|
+
{ name: "date", type: "string", value: "<date>", description: "new date" },
|
|
604
|
+
{ name: "place", type: "string", value: "<place>", description: "new place (the settlement)" },
|
|
605
|
+
{ name: "house", type: "string", value: "<no.>", description: 'house number in the place ("" removes it)' },
|
|
606
|
+
{ name: "cause", type: "string", value: "<text>", description: "cause of death as the record gives it" },
|
|
607
|
+
{ name: "value", type: "string", value: "<text>", description: "new value" },
|
|
608
|
+
{ name: "label", type: "string", value: "<text>", description: "new label" },
|
|
609
|
+
{ name: "age", type: "string", multiple: true, value: "<age|who:age>", description: 'age as the record gives it; a family event per partner: "husband:27", "wife:17"' },
|
|
610
|
+
{ name: "with", type: "string", multiple: true, value: "<role:who>", description: "add a participant: witness:Jan Novák, godparent:P0012, officiant:…, midwife:… (repeatable)" },
|
|
611
|
+
{ name: "without", type: "string", multiple: true, value: "<role:who>", description: "take off a participant recorded wrongly — with --reason; a wrong role: --without witness:X --with godparent:X" },
|
|
612
|
+
{ name: "status", type: "string", value: "<status>", description: "new status" },
|
|
613
|
+
{ name: "note", type: "string", value: "<text>", description: "replace the note on this fact" },
|
|
614
|
+
],
|
|
615
|
+
description: "Filling in what was unknown (an age, the house, the witnesses a record names) needs no reason; changing\nwhat was known, or the status, does.",
|
|
616
|
+
examples: [
|
|
617
|
+
'strom event edit E0002 --date "BET 1811 AND 1812" --reason "age 27 at marriage 1839 (S0002)"',
|
|
618
|
+
'strom event edit E0004 --age husband:27 --age wife:22 --house 21 --with "witness:Jan Dvořák" --with "officiant:P. Josef Kříž"',
|
|
619
|
+
'strom event edit E0001 --place "Týnec nad Labem" --house 13 --reason "the house number out of the place"',
|
|
620
|
+
'strom event edit E0001 --without "witness:Marie Dvořáková" --with "godparent:Marie Dvořáková" --reason "the record names her the godmother"',
|
|
621
|
+
],
|
|
622
|
+
run(ctx, { args, opts }) {
|
|
623
|
+
const tree = ctx.tree();
|
|
624
|
+
const id = normId(args[0]);
|
|
625
|
+
const { owner } = findEventOwner(tree, id);
|
|
626
|
+
const ages = listOpt(opts.age);
|
|
627
|
+
let age;
|
|
628
|
+
let partnerAge;
|
|
629
|
+
if (owner.type === "family")
|
|
630
|
+
partnerAge = partnerAges(tree, ages, owner.partners);
|
|
631
|
+
else if (ages.length > 1)
|
|
632
|
+
throw new UsageError("one --age for a person's event");
|
|
633
|
+
else
|
|
634
|
+
age = ages[0];
|
|
635
|
+
const e = editEvent(tree, id, {
|
|
636
|
+
age,
|
|
637
|
+
ages: partnerAge,
|
|
638
|
+
participants: listOpt(opts.with).map((w) => parseParticipant(tree, w)),
|
|
639
|
+
drop: listOpt(opts.without).map((w) => parseParticipant(tree, w)),
|
|
640
|
+
date: opts.date,
|
|
641
|
+
place: opts.place,
|
|
642
|
+
house: opts.house,
|
|
643
|
+
cause: opts.cause,
|
|
644
|
+
value: opts.value,
|
|
645
|
+
label: opts.label,
|
|
646
|
+
status: opts.status,
|
|
647
|
+
note: opts.note,
|
|
648
|
+
}, opts.reason);
|
|
649
|
+
return { text: written(tree), data: { event: e } };
|
|
650
|
+
},
|
|
651
|
+
}, {
|
|
652
|
+
path: ["event", "retract"],
|
|
653
|
+
summary: "Withdraw a fact that turned out wrong (it is kept, marked retracted)",
|
|
654
|
+
group: "people",
|
|
655
|
+
tree: true,
|
|
656
|
+
writes: true,
|
|
657
|
+
args: [{ name: "event", description: "event ID (E0001)", required: true }],
|
|
658
|
+
examples: ['strom event retract E0003 --reason "namesake: this Jan was born in No. 37, ours in No. 12"'],
|
|
659
|
+
run(ctx, { args, opts }) {
|
|
660
|
+
const tree = ctx.tree();
|
|
661
|
+
const e = retractEvent(tree, normId(args[0]), opts.reason);
|
|
662
|
+
return { text: written(tree), data: { event: e } };
|
|
663
|
+
},
|
|
664
|
+
});
|