@prestack/thread 0.0.3 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/CHANGELOG.md +310 -0
  2. package/dist/core/{asking.d.ts → answers.d.ts} +6 -14
  3. package/dist/core/{asking.js → answers.js} +13 -332
  4. package/dist/core/{asking.ts → answers.ts} +14 -357
  5. package/dist/core/attach.js +2 -2
  6. package/dist/core/attach.ts +2 -3
  7. package/dist/core/chain.js +4 -11
  8. package/dist/core/chain.ts +4 -3
  9. package/dist/core/client.d.ts +57 -0
  10. package/dist/core/client.js +243 -0
  11. package/dist/core/client.ts +279 -0
  12. package/dist/core/descend.d.ts +2 -0
  13. package/dist/core/descend.js +109 -16
  14. package/dist/core/descend.ts +120 -16
  15. package/dist/core/files.d.ts +45 -0
  16. package/dist/core/files.js +145 -0
  17. package/dist/core/files.ts +193 -0
  18. package/dist/core/init.d.ts +1 -0
  19. package/dist/core/init.js +10 -2
  20. package/dist/core/init.ts +8 -2
  21. package/dist/core/invoked.js +1 -1
  22. package/dist/core/invoked.ts +1 -1
  23. package/dist/core/rows.d.ts +2 -0
  24. package/dist/core/rows.js +65 -0
  25. package/dist/core/rows.ts +58 -0
  26. package/dist/core/tree.js +1 -1
  27. package/dist/core/tree.ts +1 -1
  28. package/dist/derivation/attach.js +0 -12
  29. package/dist/derivation/attach.ts +0 -12
  30. package/dist/derivation/generators/build.js +8 -7
  31. package/dist/derivation/generators/build.ts +8 -7
  32. package/dist/derivation/generators/release.d.ts +1 -1
  33. package/dist/derivation/generators/release.js +15 -23
  34. package/dist/derivation/generators/release.ts +15 -15
  35. package/dist/integrations/attach.js +1 -19
  36. package/dist/integrations/attach.ts +1 -19
  37. package/dist/integrations/generators/read.js +2 -2
  38. package/dist/integrations/generators/read.ts +4 -4
  39. package/dist/integrations/generators/vault.js +4 -12
  40. package/dist/integrations/generators/vault.ts +4 -4
  41. package/dist/integrations/module.js +5 -3
  42. package/dist/integrations/module.ts +5 -3
  43. package/dist/integrations/vault.js +5 -5
  44. package/dist/integrations/vault.ts +5 -5
  45. package/dist/integrity/attach.js +0 -12
  46. package/dist/integrity/attach.ts +0 -12
  47. package/dist/integrity/check-threads.js +1 -1
  48. package/dist/integrity/check-threads.ts +1 -1
  49. package/dist/integrity/constraint.d.ts +11 -2
  50. package/dist/integrity/constraint.js +163 -20
  51. package/dist/integrity/constraint.ts +174 -22
  52. package/dist/integrity/generators/compiler.js +7 -1
  53. package/dist/integrity/generators/compiler.ts +10 -1
  54. package/dist/integrity/generators/gate.js +23 -18
  55. package/dist/integrity/generators/gate.ts +29 -9
  56. package/dist/integrity/generators/publish.js +14 -26
  57. package/dist/integrity/generators/publish.ts +14 -17
  58. package/dist/integrity/generators/trial.d.ts +2 -2
  59. package/dist/integrity/generators/trial.js +108 -50
  60. package/dist/integrity/generators/trial.ts +107 -48
  61. package/dist/integrity/lint.js +3 -3
  62. package/dist/integrity/lint.ts +3 -3
  63. package/dist/integrity/lsp/diagnostics.js +1 -1
  64. package/dist/integrity/lsp/diagnostics.ts +1 -1
  65. package/dist/integrity/lsp/lint.js +1 -1
  66. package/dist/integrity/lsp/lint.ts +1 -1
  67. package/dist/integrity/slopnator.d.ts +1 -14
  68. package/dist/integrity/slopnator.js +8 -40
  69. package/dist/integrity/slopnator.ts +9 -51
  70. package/dist/integrity/slopnators/sl_broken_lineage.js +4 -2
  71. package/dist/integrity/slopnators/sl_broken_lineage.ts +4 -2
  72. package/dist/integrity/slopnators/sl_declared_elsewhere.js +24 -3
  73. package/dist/integrity/slopnators/sl_declared_elsewhere.ts +24 -3
  74. package/dist/integrity/slopnators/sl_derived_drift.js +10 -4
  75. package/dist/integrity/slopnators/sl_derived_drift.ts +10 -4
  76. package/dist/integrity/slopnators/sl_inert_island.js +7 -13
  77. package/dist/integrity/slopnators/sl_inert_island.ts +7 -16
  78. package/dist/integrity/slopnators/sl_inert_slopnator.js +8 -8
  79. package/dist/integrity/slopnators/sl_inert_slopnator.ts +8 -8
  80. package/dist/integrity/slopnators/sl_missing_row.js +14 -9
  81. package/dist/integrity/slopnators/sl_missing_row.ts +14 -10
  82. package/dist/integrity/slopnators/sl_no_child_process.js +8 -8
  83. package/dist/integrity/slopnators/sl_no_child_process.ts +8 -8
  84. package/dist/integrity/slopnators/sl_no_import_cycle.js +1 -1
  85. package/dist/integrity/slopnators/sl_no_import_cycle.ts +1 -1
  86. package/dist/integrity/slopnators/sl_no_such_command.js +20 -5
  87. package/dist/integrity/slopnators/sl_no_such_command.ts +22 -5
  88. package/dist/integrity/slopnators/sl_only_takes_a_literal.js +8 -8
  89. package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +8 -8
  90. package/dist/integrity/slopnators/sl_stale_path.js +91 -18
  91. package/dist/integrity/slopnators/sl_stale_path.ts +97 -18
  92. package/dist/integrity/slopnators/sl_twice_meant.js +25 -2
  93. package/dist/integrity/slopnators/sl_twice_meant.ts +28 -2
  94. package/dist/integrity/slopnators/sl_twice_parented.js +26 -5
  95. package/dist/integrity/slopnators/sl_twice_parented.ts +26 -5
  96. package/dist/integrity/slopnators/sl_undeclared_node.js +18 -2
  97. package/dist/integrity/slopnators/sl_undeclared_node.ts +18 -2
  98. package/dist/integrity/slopnators/sl_undeclared_operation.js +3 -1
  99. package/dist/integrity/slopnators/sl_undeclared_operation.ts +3 -1
  100. package/dist/integrity/slopnators/sl_undeclared_variant.js +6 -2
  101. package/dist/integrity/slopnators/sl_undeclared_variant.ts +6 -2
  102. package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -4
  103. package/dist/integrity/slopnators/sl_ungoverned_island.ts +3 -4
  104. package/dist/integrity/slopnators/sl_unkept_rows.js +1 -1
  105. package/dist/integrity/slopnators/sl_unkept_rows.ts +1 -1
  106. package/dist/integrity/slopnators/sl_unpaired_member.js +13 -8
  107. package/dist/integrity/slopnators/sl_unpaired_member.ts +16 -8
  108. package/dist/integrity/slopnators/sl_unpaired_slopnator.js +16 -3
  109. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +16 -3
  110. package/dist/integrity/slopnators/sl_unthreaded.d.ts +1 -0
  111. package/dist/integrity/slopnators/sl_unthreaded.js +18 -10
  112. package/dist/integrity/slopnators/sl_unthreaded.ts +19 -11
  113. package/dist/integrity/slopnators/sl_untranslated_key.js +1 -1
  114. package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -1
  115. package/dist/integrity/slopnators/sl_unversioned.js +16 -2
  116. package/dist/integrity/slopnators/sl_unversioned.ts +16 -2
  117. package/dist/integrity/sweep.js +17 -3
  118. package/dist/integrity/sweep.ts +20 -3
  119. package/dist/knowledge/attach.js +0 -6
  120. package/dist/knowledge/attach.ts +0 -6
  121. package/dist/knowledge/generators/docsgen.js +12 -3
  122. package/dist/knowledge/generators/docsgen.ts +12 -2
  123. package/dist/knowledge/generators/glossary.js +1 -1
  124. package/dist/knowledge/generators/glossary.ts +1 -1
  125. package/dist/operations/attach.js +2 -26
  126. package/dist/operations/attach.ts +2 -27
  127. package/dist/operations/dispatch.js +2 -3
  128. package/dist/operations/dispatch.ts +2 -3
  129. package/dist/operations/generators/capability.js +4 -7
  130. package/dist/operations/generators/capability.ts +3 -6
  131. package/dist/operations/generators/compile.js +8 -13
  132. package/dist/operations/generators/compile.ts +8 -4
  133. package/dist/operations/generators/permits.js +4 -13
  134. package/dist/operations/generators/permits.ts +3 -3
  135. package/dist/operations/generators/seed.js +5 -2
  136. package/dist/operations/generators/seed.ts +6 -2
  137. package/dist/operations/generators/trial.js +10 -10
  138. package/dist/operations/generators/trial.ts +10 -10
  139. package/dist/operations/http.js +4 -1
  140. package/dist/operations/http.ts +6 -1
  141. package/package.json +2 -2
  142. package/dist/core/cli.d.ts +0 -2
  143. package/dist/core/cli.js +0 -1761
  144. package/dist/core/cli.ts +0 -2004
  145. package/dist/core/generators/meta.d.ts +0 -2
  146. package/dist/core/generators/meta.js +0 -829
  147. package/dist/core/generators/meta.ts +0 -1006
  148. package/dist/derivation/generators/derivation.d.ts +0 -3
  149. package/dist/derivation/generators/derivation.js +0 -273
  150. package/dist/derivation/generators/derivation.ts +0 -347
  151. package/dist/derivation/makers/release.d.ts +0 -1
  152. package/dist/derivation/makers/release.js +0 -107
  153. package/dist/derivation/makers/release.ts +0 -135
  154. package/dist/integrations/generators/secret.d.ts +0 -1
  155. package/dist/integrations/generators/secret.js +0 -233
  156. package/dist/integrations/generators/secret.ts +0 -289
  157. package/dist/integrations/generators/target.d.ts +0 -1
  158. package/dist/integrations/generators/target.js +0 -775
  159. package/dist/integrations/generators/target.ts +0 -944
  160. package/dist/integrity/generators/rule.d.ts +0 -2
  161. package/dist/integrity/generators/rule.js +0 -213
  162. package/dist/integrity/generators/rule.ts +0 -271
  163. package/dist/integrity/generators/seal.d.ts +0 -4
  164. package/dist/integrity/generators/seal.js +0 -101
  165. package/dist/integrity/generators/seal.ts +0 -117
  166. package/dist/integrity/sealed.d.ts +0 -1
  167. package/dist/integrity/sealed.js +0 -56
  168. package/dist/integrity/sealed.ts +0 -56
  169. package/dist/knowledge/generators/skills.d.ts +0 -1
  170. package/dist/knowledge/generators/skills.js +0 -222
  171. package/dist/knowledge/generators/skills.ts +0 -263
  172. package/dist/operations/generators/grant.d.ts +0 -1
  173. package/dist/operations/generators/grant.js +0 -267
  174. package/dist/operations/generators/grant.ts +0 -336
  175. package/dist/operations/generators/operation.d.ts +0 -1
  176. package/dist/operations/generators/operation.js +0 -811
  177. package/dist/operations/generators/operation.ts +0 -972
  178. package/dist/operations/makers/capability.d.ts +0 -1
  179. package/dist/operations/makers/capability.js +0 -117
  180. package/dist/operations/makers/capability.ts +0 -166
  181. package/dist/operations/makers/trial.d.ts +0 -1
  182. package/dist/operations/makers/trial.js +0 -413
  183. package/dist/operations/makers/trial.ts +0 -520
@@ -1,22 +1,10 @@
1
- var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
- if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
- return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
- return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
- });
6
- }
7
- return path;
8
- };
9
- import { createHash, randomBytes } from 'node:crypto';
10
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
11
- import { tmpdir } from 'node:os';
12
- import { dirname, join, relative } from 'node:path';
13
- import { argv, env, exit, stderr, stdin, stdout } from 'node:process';
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, readFileSync, writeFileSync } from "./files.js";
3
+ import { join, relative } from 'node:path';
4
+ import { argv, exit, stderr, stdin, stdout } from 'node:process';
14
5
  import { createInterface } from 'node:readline';
15
- import { pathToFileURL } from 'node:url';
16
- import { CONSTRAINTS } from "../integrity/constraint.js";
17
- import { LISTS, descend, listFields, listShape, shapeFields, shapesNamed } from "./descend.js";
18
- import { quoted } from "./rows.js";
19
- const RECEIPTS = join(tmpdir(), 'thread-create');
6
+ import { LISTS, listFields, listShape, shapesNamed } from "./descend.js";
7
+ import { quoted, valuesIn } from "./rows.js";
20
8
  export const RECEIPT = '--receipt';
21
9
  export const ENDS = '.';
22
10
  export const FROM = '--from';
@@ -100,66 +88,6 @@ export function scoped(given, opens) {
100
88
  return { own, each };
101
89
  }
102
90
  const locate = (one) => one.at > 0 ? `line ${String(one.at)}` : `\`--${one.key}\``;
103
- let meanings;
104
- function coined(word) {
105
- if (meanings === undefined) {
106
- const found = new Map();
107
- try {
108
- for (const [id, held] of descend().coined) {
109
- const first = held.said[0];
110
- if (first !== undefined && first !== '')
111
- found.set(id, first);
112
- }
113
- }
114
- catch {
115
- found.clear();
116
- }
117
- meanings = found;
118
- }
119
- return meanings.get(word);
120
- }
121
- export function asksOf(noun, shape, said, computed) {
122
- const fields = typeof shape === 'string' ? shapeFields(shape).map((one) => ({ ...one, boolean: false })) : shape;
123
- if (fields.length === 0) {
124
- refuse(`nothing installed here states what a ${noun} is made of, so \`thread create ${noun}\` has no questions to ` +
125
- `ask. It reads that from the shape a ${noun} is declared with rather than from a list kept beside it. ` +
126
- 'Run `npx thread attach` to see whether the package that states it is taken on.');
127
- }
128
- const spoken = { ...said };
129
- for (const one of fields) {
130
- if (spoken[one.name] !== undefined || computed.includes(one.name))
131
- continue;
132
- const meaning = coined(one.name) ?? (one.guide === '' ? undefined : one.guide);
133
- if (meaning === undefined)
134
- continue;
135
- spoken[one.name] = { guide: meaning };
136
- }
137
- const mute = fields
138
- .filter((one) => one.required && !computed.includes(one.name) && spoken[one.name] === undefined)
139
- .map((one) => one.name);
140
- if (mute.length > 0) {
141
- const one = mute.length === 1;
142
- refuse(`every ${noun} states ${mute.join(', ')}, and \`thread create ${noun}\` never asks for ${one ? 'it' : 'them'}. ` +
143
- `It would write a ${noun} with no ${mute.join(' and no ')}, and tell you it worked. ` +
144
- `Give ${one ? 'it' : 'each'} a sentence where this command lists what it asks, or name ${one ? 'it' : 'them'} ` +
145
- 'as something the command fills in.');
146
- }
147
- return Object.keys(spoken).map((one) => {
148
- const told = spoken[one];
149
- const known = fields.find((each) => each.name === one);
150
- const spare = told.optional ?? (known !== undefined && !known.required);
151
- return {
152
- at: one,
153
- guide: known?.boolean === true ? `${told.guide} — yes or no` : told.guide,
154
- ...(told.asks === undefined ? {} : { asks: told.asks }),
155
- ...(spare ? { optional: true } : {}),
156
- ...(known?.list === true ? { list: true } : {}),
157
- ...(known?.map === true ? { map: true } : {}),
158
- ...(known?.boolean === true ? { boolean: true } : {}),
159
- ...(known?.worded === true ? { worded: true } : {}),
160
- };
161
- });
162
- }
163
91
  export const named = (all) => all.map((one) => one.at);
164
92
  export const prompted = (one) => one.asks ?? `${one.at} — ${one.guide}${one.optional === true ? ', or blank' : ''}: `;
165
93
  export const mapped = (one, said) => one.map === true && !(one.worded === true && !said.includes('='));
@@ -364,7 +292,6 @@ export function seeding(kids, root) {
364
292
  `\`${TYPED} read ${one.at}\` prints what is there.`);
365
293
  continue;
366
294
  }
367
- mkdirSync(dirname(into), { recursive: true });
368
295
  writeFileSync(into, one.body.endsWith('\n') ? one.body : `${one.body}\n`, 'utf8');
369
296
  said.push(`wrote ${one.at} with the body you gave it.`);
370
297
  }
@@ -457,16 +384,11 @@ export async function bodied(what, shows, piped, all, line) {
457
384
  const written = held.join('\n').trim();
458
385
  return written === '' ? undefined : written;
459
386
  }
460
- export function guidance(all) {
461
- const width = Math.max(...all.map((one) => one.at.length)) + 3;
462
- const under = ' '.repeat(width + 2);
463
- return all.flatMap((one) => {
464
- const lines = wrapped(one.optional === true ? `${one.guide.replace(/\.$/, '')}. Leave it blank and it is not stated` : one.guide, '');
465
- return [` ${`${one.at}:`.padEnd(width)}${lines[0] ?? ''}`, ...lines.slice(1).map((each) => `${under}${each}`)];
466
- });
467
- }
468
387
  export function only(given, allowed, where) {
469
- for (const one of ownAnswers(given)) {
388
+ unasked(ownAnswers(given), allowed, where);
389
+ }
390
+ export function unasked(given, allowed, where) {
391
+ for (const one of given) {
470
392
  if (CARRIES.includes(one.key) || one.key === WRITES)
471
393
  continue;
472
394
  if (allowed.includes(one.key))
@@ -517,7 +439,6 @@ export function marked(parts) {
517
439
  const citesOf = (held) => Array.isArray(held)
518
440
  ? held.filter((one) => one !== null && typeof one === 'object' && typeof one.on === 'string' && typeof one.said === 'string')
519
441
  : [];
520
- const HELD = ['holds', 'declares'];
521
442
  const NAMED_SHAPE = /^[A-Za-z_][A-Za-z0-9_]*$/;
522
443
  const rowLike = (one) => one !== null && typeof one === 'object' && typeof one.id === 'string' && typeof one.describes === 'string';
523
444
  const kinded = (shape) => shape
@@ -567,7 +488,7 @@ export async function declared(tree) {
567
488
  walked.add(at);
568
489
  let read;
569
490
  try {
570
- read = (await import(__rewriteRelativeImportExtension(pathToFileURL(join(tree.at, at)).href)));
491
+ read = valuesIn(readFileSync(join(tree.at, at)));
571
492
  }
572
493
  catch {
573
494
  return;
@@ -576,7 +497,7 @@ export async function declared(tree) {
576
497
  const node = value;
577
498
  const lists = Array.isArray(value)
578
499
  ? [{ list: name, rows: value }]
579
- : HELD.flatMap((list) => {
500
+ : ['holds', 'declares'].flatMap((list) => {
580
501
  const rows = node?.[list];
581
502
  return Array.isArray(rows) ? [{ list, rows }] : [];
582
503
  });
@@ -673,68 +594,6 @@ export function nouns(all, verb = 'create') {
673
594
  }
674
595
  return found;
675
596
  }
676
- const spends = (root, nonce) => join(RECEIPTS, `${createHash('sha256').update(root).digest('hex').slice(0, 16)}-${nonce}.txt`);
677
- const stamp = (listing) => createHash('sha256').update(listing).digest('hex').slice(0, 12);
678
- function beingMade() {
679
- const words = argv.slice(2).filter((one) => !one.startsWith('-'));
680
- if (words[0] === 'create' && typeof words[1] === 'string' && words[1] !== '')
681
- return words[1];
682
- const file = (argv[1] ?? '').split('\\').join('/').split('/').at(-1) ?? '';
683
- return file.replace(/\.[^.]+$/, '');
684
- }
685
- export const SKILLS_AT = 'thread/knowledge';
686
- export function skillOf(noun) {
687
- return `${SKILLS_AT}/${noun}.md`;
688
- }
689
- function readTheSkill() {
690
- const noun = env.THREAD_NOUN ?? beingMade();
691
- if (noun === '')
692
- return [];
693
- return [
694
- '',
695
- `READ ${skillOf(noun)} BEFORE YOU WRITE ANYTHING. It is this command's own output, kept as a`,
696
- 'file, and it lists everything that already exists. Building a second copy of something the tree',
697
- 'already has is the most expensive mistake available here and it has already been made: thirteen',
698
- 'elements were declared in one app that every one of them already shipped.',
699
- '',
700
- 'If that file is not there, run `npx thread skills` and it will be.',
701
- ];
702
- }
703
- export function issue(root, listing) {
704
- const nonce = randomBytes(6).toString('hex');
705
- mkdirSync(RECEIPTS, { recursive: true });
706
- writeFileSync(spends(root, nonce), `${stamp(listing)}\n`, 'utf8');
707
- const said = readTheSkill();
708
- if (said.length > 0)
709
- stdout.write(`${said.join('\n')}\n`);
710
- return nonce;
711
- }
712
- export function spend(root, listing, offered, sheet) {
713
- if (offered === undefined || offered.startsWith('-')) {
714
- refuse(`\`${RECEIPT}\` was handed nothing to check. Run \`thread create\` with no arguments — it lists what is ` +
715
- 'here and issues a receipt to hand back.');
716
- }
717
- const path = spends(root, offered);
718
- if (!existsSync(path)) {
719
- refuse(`\`${offered}\` is not a receipt this tree issued, or it has been spent already — one run per receipt. ` +
720
- 'Run `thread create` again and use the one it prints.');
721
- }
722
- let when = '';
723
- try {
724
- when = readFileSync(path, 'utf8').trim();
725
- }
726
- catch {
727
- when = '';
728
- }
729
- process.on('exit', (code) => {
730
- if (code === 0)
731
- rmSync(path, { force: true });
732
- });
733
- if (when !== stamp(listing)) {
734
- refuse(`${sheet} has changed since that receipt was issued, so what you read is not what is here. ` +
735
- 'Run `thread create` again.');
736
- }
737
- }
738
597
  export function asking(held) {
739
598
  const piped = answered();
740
599
  const rl = piped ? undefined : createInterface({ input: stdin, output: stdout });
@@ -848,191 +707,13 @@ export function handed() {
848
707
  }
849
708
  return stood;
850
709
  }
851
- export const TYPED = 'npx thread';
852
- const folded = (said, pad, room = 92) => {
853
- const out = [];
854
- let line = '';
855
- for (const word of said.split(/\s+/)) {
856
- if (line === '')
857
- line = word;
858
- else if (`${line} ${word}`.length <= room)
859
- line = `${line} ${word}`;
860
- else {
861
- out.push(line);
862
- line = word;
863
- }
864
- }
865
- if (line !== '')
866
- out.push(line);
867
- return out.map((one) => `${pad}${one}`);
868
- };
869
- const parented = (() => {
870
- const one = CONSTRAINTS.find((each) => each.id === 'twice-parented');
871
- if (one === undefined) {
872
- return [
873
- '',
874
- 'THIS PAGE CANNOT STATE THE RULE. It reads what may declare a file out of the `twice-parented`',
875
- 'rule, and no rule by that name ships any more. Whatever governs this now, it is not said here —',
876
- `\`${TYPED} create rule\` lists every rule that does exist.`,
877
- ];
878
- }
879
- return [
880
- '',
881
- 'which one you pick is a rule rather than a preference, and a check refuses the tree over it:',
882
- '',
883
- ` ${one.id}`,
884
- ...folded(`bans ${one.bans}.`, ' '),
885
- ...folded(one.instead.replace(/^./, (each) => each.toUpperCase()), ' '),
886
- ];
887
- })();
888
- const NEXT = {
889
- declares: 'It writes the file from that, and nothing else runs. A file already on disk is left alone and the row simply names it.',
890
- derives: `It writes NO file at \`at:\`, because the command in \`by:\` writes that one — which is why there is no ${asFlag(BODY)} here to give. If this tree has no command answering \`by:\`, that generator is declared first and ${asFlag(WRITES)} is what goes in it.`,
891
- seeds: `TWO files come out of this and a different hand writes each, which is the whole reason there are two bodies. If this tree has no command answering \`by:\`, that generator is declared here too, so one run leaves the row, the generator and the file.`,
892
- };
893
- export const VERBS = [
894
- 'nothing is in this tree until a row names it, and which of three lists the row sits in says who',
895
- 'wrote the file. Pick the one that is true and the rest of this framework follows from it:',
896
- '',
897
- ' declares a person wrote it. Exactly one row may ever declare an address, and a second is',
898
- ' refused: a file with two owners has two answers for where it came from and the tree',
899
- ' can give neither.',
900
- ' derives a command wrote it. Delete the file, run the command again, and the same bytes come',
901
- ' back. Many rows may derive one address, because a derivation owns nothing — it states',
902
- ' `by:`, the command that writes it, and `runs:`, what catches it drifting.',
903
- ' seeds a command wrote the shape and a person fills in the part no machine can supply. Many',
904
- ' rows may seed one address as well, and it is proved by shape rather than by bytes,',
905
- ' because half of what is on disk is your own writing.',
906
- '',
907
- `a file in none of them is not in this app at all — \`${TYPED} descent\` names every one it reached`,
908
- 'and every one it did not, and nothing undeclared is compiled in or shipped.',
909
- '',
910
- 'and the list decides what this command does next, not just what the row says. What it does next is',
911
- 'ASK FOR BODIES, and there are two of them, which are never the same text and never the same file:',
912
- '',
913
- ` ${asFlag(BODY).padEnd(10)} the body of the file the row names. A person wrote it.`,
914
- ` ${asFlag(WRITES).padEnd(10)} the body of the GENERATOR — the code that writes a template into that file.`,
915
- '',
916
- 'give each as text on the command. Neither may be taken out of a file, because a file is not a',
917
- 'receipt: nothing here checked it and no descent reached it. Single-quote a body that holds quotes.',
918
- '',
919
- 'so each list takes a different set of them, and giving one where the other belongs is the mistake',
920
- 'this section exists to stop:',
921
- '',
922
- ...LISTS.flatMap((list) => {
923
- const takes = [
924
- ...(WRITTEN.includes(list) ? [asFlag(WRITES)] : []),
925
- ...(BODIED.includes(list) ? [asFlag(BODY)] : []),
926
- ];
927
- const said = folded(`takes ${takes.length === 0 ? 'no body at all' : takes.join(' and ')}. ${NEXT[list] ?? ''}`, ' ');
928
- return said.map((line, at) => (at === 0 ? ` ${list.padEnd(11)}${line.trim()}` : line));
929
- }),
930
- ...parented,
931
- ];
932
- export const grows = (sheetAt, noun) => {
933
- const shaped = noun.split('-').map((one) => `${one.slice(0, 1).toUpperCase()}${one.slice(1)}`).join('');
934
- return [
935
- `this is also how the CLI grows. A list of rows in ${sheetAt} becomes a command: declare`,
936
- `\`export const THINGS: readonly Thing[] = []\` beside what is already there and \`${TYPED} create thing\``,
937
- 'answers from that moment, asking for whatever a `Thing` states and writing the row into that list.',
938
- '',
939
- `The same is true of what you have now: \`${TYPED} create ${noun}\` exists because ${sheetAt} declares`,
940
- `\`${shaped}\`. Nothing was added to a list of commands anywhere — the CLI reads what your tree says.`,
941
- ];
942
- };
943
- export const VALUED = 'THREAD_VALUED';
944
- export function answering(noun) {
945
- return [
946
- ...(env[VALUED] === '1' ? [] : [...VERBS, '']),
947
- 'what `new` asks. Give each answer as a flag on one command and close with the receipt:',
948
- '',
949
- ` ${TYPED} create ${noun} new --<answer> <value> ... ${RECEIPT} <receipt>`,
950
- '',
951
- 'Flags need no terminal, no pipe and no file, so that form works everywhere. The two others',
952
- 'each need something you may not have: one answer to a line on stdin needs a shell that can',
953
- 'redirect, and `--answers <path>` needs a way to write the file first. Use them when you have',
954
- 'them; the line above is the one that never depends on it.',
955
- '',
956
- 'Where a question repeats — a list of things, each with answers of its own — there are no',
957
- 'separate flag names for the inner ones. Give the group\'s own flag again to begin the next,',
958
- 'then its answers after it, reusing the same words. They are read in the order you write them,',
959
- 'and everything before the first group flag belongs to the thing itself.',
960
- '',
961
- 'the answers, in order:',
962
- ];
963
- }
710
+ export const TYPED = 'thread';
964
711
  export const CARRIED = [
965
712
  { verb: 'read', typed: `${TYPED} read <row>`, does: 'prints every field a row states' },
966
713
  { verb: 'edit', typed: `${TYPED} edit <row> --<field> <value>`, does: 'changes any field it was declared with' },
967
714
  { verb: 'remove', typed: `${TYPED} edit <row> --remove`, does: 'takes the row out of the tree' },
968
715
  ];
969
716
  const elsewhere = CARRIED.map((one) => `\`${one.typed}\` ${one.does}`);
970
- export const ELSEWHERE = [
971
- 'reading a row, changing one of its answers and taking it out are done in one place, whatever',
972
- 'declared it \u2014 name the row by its id or by the mark printed beside it above:',
973
- '',
974
- ...CARRIED.map((one) => ` ${one.typed.padEnd(40)}${one.does}`),
975
- ];
976
- const opening = (said) => said.split(/(?<=\.)\s/)[0] ?? said;
977
- const article = (word) => (/^[aeiou]/.test(word) ? 'an' : 'a');
978
- export function taught(noun, told, commands) {
979
- const one = told.get(noun);
980
- const opens = [
981
- [`${TYPED} read <id>`, 'the row, and the file it names'],
982
- [`${TYPED} create ${noun}`, 'this list again, with a fresh receipt'],
983
- ...(commands.has('thread inspect')
984
- ? [[`${TYPED} inspect <word>`, 'every row whose name or sentence carries the word']]
985
- : []),
986
- ];
987
- const top = one?.top;
988
- const siblings = top === undefined
989
- ? []
990
- : [...told]
991
- .filter(([, each]) => each.top?.id === top.id && each.describes !== '')
992
- .sort(([left], [right]) => (left < right ? -1 : 1));
993
- const pad = Math.max(0, ...siblings.map(([word]) => word.length)) + 3;
994
- return [
995
- 'DO NOT CREATE WHAT ALREADY EXISTS. Everything listed below is declared and yours to use. A thing',
996
- 'you cannot find is usually one of these under a word you have not met \u2014 read the sentences, not',
997
- 'the names.',
998
- '',
999
- 'IF NOTHING FITS, SAY SO AND STOP. Do not coin a name: a name is a meaning, and a meaning is',
1000
- 'declared rather than invented. Being told the list is short a thing is a useful answer.',
1001
- '',
1002
- 'NOW GO AND OPEN ONE. A sentence is a summary, and every expensive mistake here was made from a',
1003
- 'summary. These print the real thing, and none of them writes anything:',
1004
- '',
1005
- ...opens.map(([said, does]) => ` ${said.padEnd(30)}${does}`),
1006
- '',
1007
- ...(one === undefined || one.describes === ''
1008
- ? []
1009
- : [
1010
- `what ${article(noun)} ${noun} is:`,
1011
- '',
1012
- ...wrapped(one.describes, ' '),
1013
- ...one.cites.flatMap((said) => ['', ...wrapped(`said on ${said.on}: "${said.said}"`, ' ')]),
1014
- '',
1015
- ]),
1016
- ...(top === undefined || siblings.length < 2
1017
- ? []
1018
- : [
1019
- `${top.id} \u2014 ${opening(top.describes)}`,
1020
- 'what it makes, and what each one is. Pick the one that is true before declaring anything:',
1021
- '',
1022
- ...siblings.flatMap(([word, each]) => {
1023
- const lines = wrapped(opening(each.describes), '');
1024
- return [` ${word.padEnd(pad)}${lines[0] ?? ''}`, ...lines.slice(1).map((line) => `${' '.repeat(pad + 2)}${line}`)];
1025
- }),
1026
- '',
1027
- ]),
1028
- ];
1029
- }
1030
- export function reminded(noun) {
1031
- const lists = noun === undefined || noun === 'meta'
1032
- ? `\`${TYPED} create\` lists what this tree makes, and any noun run bare lists what exists, each with its sentence`
1033
- : `\`${TYPED} create ${noun}\` lists what exists, each with its sentence`;
1034
- return `thread: before the next thing, ${lists}. Do not create what already exists, and if nothing fits, say so and stop.`;
1035
- }
1036
717
  export function wordsIn(offered) {
1037
718
  const values = new Set([argv[argv.indexOf(RECEIPT) + 1], argv[argv.indexOf(ANSWERS) + 1], ...flagged().map((one) => one.value)]);
1038
719
  const words = argv.slice(2).filter((one) => !one.startsWith('-') && !values.has(one));