@orkestrel/scaffold 0.0.107 → 0.0.109

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 (164) hide show
  1. package/dist/agents/skills/orkestrel-dispatch/scripts/bench.js +326 -91
  2. package/dist/agents/skills/orkestrel-dispatch/scripts/brief.js +110 -14
  3. package/dist/agents/skills/orkestrel-dispatch/scripts/chain.js +280 -0
  4. package/dist/agents/skills/orkestrel-dispatch/scripts/cite.js +188 -26
  5. package/dist/agents/skills/orkestrel-dispatch/scripts/launch.js +142 -43
  6. package/dist/agents/skills/orkestrel-dispatch/scripts/login.js +240 -62
  7. package/dist/agents/skills/orkestrel-dispatch/scripts/patch.js +323 -0
  8. package/dist/agents/skills/orkestrel-dispatch/scripts/result.js +169 -32
  9. package/dist/agents/skills/orkestrel-dispatch/scripts/sweep.js +368 -27
  10. package/dist/agents/skills/orkestrel-falsify/scripts/mutate.js +560 -0
  11. package/dist/agents/skills/orkestrel-harden/scripts/discovery.js +1052 -205
  12. package/dist/agents/skills/orkestrel-harden/scripts/measure.js +361 -0
  13. package/dist/agents/skills/orkestrel-harden/scripts/titles.js +455 -80
  14. package/dist/agents/skills/orkestrel-parallel/scripts/cases.js +327 -0
  15. package/dist/agents/skills/orkestrel-publish/scripts/compare.js +298 -51
  16. package/dist/agents/skills/orkestrel-publish/scripts/pins.js +170 -13
  17. package/dist/agents/skills/orkestrel-publish/scripts/swap.js +956 -191
  18. package/dist/agents/skills/orkestrel-publish/scripts/wave.js +707 -112
  19. package/dist/agents/skills/orkestrel-publish/scripts/window.js +235 -41
  20. package/dist/agents/skills/orkestrel-scout/scripts/map.js +341 -54
  21. package/dist/agents/templates/brief.md +1 -1
  22. package/dist/bin/main.js +51 -46
  23. package/dist/bin/main.js.map +1 -1
  24. package/dist/host/AGENTS.md +26 -20
  25. package/dist/host/agents/orchestration.md +98 -58
  26. package/dist/host/agents/platforms/posix.md +15 -0
  27. package/dist/host/agents/platforms/windows.md +37 -0
  28. package/dist/host/agents/skills/orkestrel-align/SKILL.md +10 -13
  29. package/dist/host/agents/skills/orkestrel-align/references/fleet.md +4 -10
  30. package/dist/host/agents/skills/orkestrel-align/references/integration.md +1 -1
  31. package/dist/host/agents/skills/orkestrel-build/SKILL.md +16 -48
  32. package/dist/host/agents/skills/orkestrel-build/agents/openai.yaml +1 -1
  33. package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +19 -16
  34. package/dist/host/agents/skills/orkestrel-debrief/agents/openai.yaml +1 -1
  35. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +15 -28
  36. package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +5 -6
  37. package/dist/host/agents/skills/orkestrel-debrief/references/retention.md +12 -14
  38. package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +36 -24
  39. package/dist/host/agents/skills/orkestrel-dispatch/references/launch.md +14 -11
  40. package/dist/host/agents/skills/orkestrel-dispatch/scripts/bench.ts +366 -75
  41. package/dist/host/agents/skills/orkestrel-dispatch/scripts/brief.ts +126 -16
  42. package/dist/host/agents/skills/orkestrel-dispatch/scripts/chain.ts +323 -0
  43. package/dist/host/agents/skills/orkestrel-dispatch/scripts/cite.ts +220 -31
  44. package/dist/host/agents/skills/orkestrel-dispatch/scripts/launch.ts +176 -33
  45. package/dist/host/agents/skills/orkestrel-dispatch/scripts/login.ts +294 -59
  46. package/dist/host/agents/skills/orkestrel-dispatch/scripts/patch.ts +401 -0
  47. package/dist/host/agents/skills/orkestrel-dispatch/scripts/result.ts +199 -34
  48. package/dist/host/agents/skills/orkestrel-dispatch/scripts/sweep.ts +428 -27
  49. package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +10 -8
  50. package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +1 -5
  51. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
  52. package/dist/host/agents/skills/orkestrel-falsify/scripts/mutate.ts +701 -0
  53. package/dist/host/agents/skills/orkestrel-harden/SKILL.md +16 -12
  54. package/dist/host/agents/skills/orkestrel-harden/references/bindings.md +23 -81
  55. package/dist/host/agents/skills/orkestrel-harden/references/centralization.md +4 -5
  56. package/dist/host/agents/skills/orkestrel-harden/references/contract.md +2 -0
  57. package/dist/host/agents/skills/orkestrel-harden/references/document.md +33 -0
  58. package/dist/host/agents/skills/orkestrel-harden/references/equivalence.md +6 -3
  59. package/dist/host/agents/skills/orkestrel-harden/references/hardening.md +8 -4
  60. package/dist/host/agents/skills/orkestrel-harden/references/research.md +2 -0
  61. package/dist/host/agents/skills/orkestrel-harden/scripts/discovery.ts +1272 -279
  62. package/dist/host/agents/skills/orkestrel-harden/scripts/measure.ts +425 -0
  63. package/dist/host/agents/skills/orkestrel-harden/scripts/titles.ts +603 -98
  64. package/dist/host/agents/skills/orkestrel-journey/SKILL.md +75 -98
  65. package/dist/host/agents/skills/orkestrel-journey/references/captures.md +54 -17
  66. package/dist/host/agents/skills/orkestrel-journey/references/decide.md +1 -20
  67. package/dist/host/agents/skills/orkestrel-journey/references/layer.md +21 -31
  68. package/dist/host/agents/skills/orkestrel-journey/references/styles.md +1 -1
  69. package/dist/host/agents/skills/orkestrel-parallel/SKILL.md +91 -30
  70. package/dist/host/agents/skills/orkestrel-parallel/agents/openai.yaml +2 -2
  71. package/dist/host/agents/skills/orkestrel-parallel/references/claude.md +34 -0
  72. package/dist/host/agents/skills/orkestrel-parallel/references/codex.md +29 -0
  73. package/dist/host/agents/skills/orkestrel-parallel/references/cursor.md +29 -0
  74. package/dist/host/agents/skills/orkestrel-parallel/references/t3.md +31 -0
  75. package/dist/host/agents/skills/orkestrel-parallel/scripts/cases.ts +389 -0
  76. package/dist/host/agents/skills/orkestrel-polish/SKILL.md +16 -20
  77. package/dist/host/agents/skills/orkestrel-polish/references/capture-harness.md +16 -26
  78. package/dist/host/agents/skills/orkestrel-publish/SKILL.md +12 -26
  79. package/dist/host/agents/skills/orkestrel-publish/agents/openai.yaml +2 -2
  80. package/dist/host/agents/skills/orkestrel-publish/references/adopt.md +23 -0
  81. package/dist/host/agents/skills/orkestrel-publish/references/release.md +4 -4
  82. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +12 -10
  83. package/dist/host/agents/skills/orkestrel-publish/references/window.md +20 -25
  84. package/dist/host/agents/skills/orkestrel-publish/scripts/compare.ts +348 -59
  85. package/dist/host/agents/skills/orkestrel-publish/scripts/pins.ts +202 -22
  86. package/dist/host/agents/skills/orkestrel-publish/scripts/swap.ts +1061 -191
  87. package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +812 -140
  88. package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +280 -38
  89. package/dist/host/agents/skills/orkestrel-scout/SKILL.md +3 -3
  90. package/dist/host/agents/skills/orkestrel-scout/scripts/map.ts +428 -70
  91. package/dist/host/agents/templates/brief.md +1 -1
  92. package/dist/host/agents/transports/claude-cli.md +3 -3
  93. package/dist/host/agents/transports/codex.md +3 -8
  94. package/dist/host/agents/transports/cursor.md +2 -3
  95. package/dist/host/claude/AGENTS.md +7 -10
  96. package/dist/host/claude/agents/analyst.md +3 -3
  97. package/dist/host/claude/agents/astra.md +3 -3
  98. package/dist/host/claude/agents/builder.md +1 -1
  99. package/dist/host/claude/agents/checker.md +1 -1
  100. package/dist/host/claude/agents/distiller.md +1 -1
  101. package/dist/host/claude/agents/grok.md +1 -1
  102. package/dist/host/claude/agents/opus.md +2 -2
  103. package/dist/host/claude/agents/orkestrel.md +57 -90
  104. package/dist/host/claude/agents/researcher.md +1 -1
  105. package/dist/host/claude/agents/reviewer.md +4 -0
  106. package/dist/host/claude/agents/scout.md +1 -1
  107. package/dist/host/claude/agents/verifier.md +1 -1
  108. package/dist/host/claude/rules/application.md +14 -21
  109. package/dist/host/claude/rules/architecture.md +34 -44
  110. package/dist/host/claude/rules/browser.md +3 -4
  111. package/dist/host/claude/rules/campaign.md +55 -0
  112. package/dist/host/claude/rules/documentation.md +26 -11
  113. package/dist/host/claude/rules/names.md +9 -4
  114. package/dist/host/claude/rules/patterns.md +6 -7
  115. package/dist/host/claude/rules/portability.md +2 -3
  116. package/dist/host/claude/rules/quality.md +4 -5
  117. package/dist/host/claude/rules/styles.md +2 -2
  118. package/dist/host/claude/rules/tests.md +57 -114
  119. package/dist/host/claude/rules/typescript.md +8 -10
  120. package/dist/host/claude/rules/workspace.md +21 -19
  121. package/dist/host/claude/rules/writing.md +3 -3
  122. package/dist/host/claude/skills/orkestrel-dispatch/SKILL.md +1 -1
  123. package/dist/host/claude/skills/orkestrel-harden/SKILL.md +1 -1
  124. package/dist/host/claude/skills/orkestrel-parallel/SKILL.md +1 -1
  125. package/dist/host/claude/skills/orkestrel-publish/SKILL.md +1 -1
  126. package/dist/host/claude/skills/orkestrel-scout/SKILL.md +1 -1
  127. package/dist/host/codex/agents/analyst.toml +9 -3
  128. package/dist/host/codex/agents/astra.toml +3 -4
  129. package/dist/host/codex/agents/builder.toml +1 -1
  130. package/dist/host/codex/agents/opus.toml +3 -4
  131. package/dist/host/codex/agents/orkestrel.toml +1 -1
  132. package/dist/host/codex/agents/researcher.toml +2 -2
  133. package/dist/host/codex/agents/verifier.toml +1 -1
  134. package/dist/host/codex/config.toml +3 -3
  135. package/dist/host/configs/helpers.ts +21 -1
  136. package/dist/host/configs/policy.ts +405 -16
  137. package/dist/host/cursor/rules/orchestration.mdc +1 -0
  138. package/dist/host/dotfiles/oxlintrc.json +141 -5
  139. package/dist/host/guides/agent.md +111 -134
  140. package/dist/host/guides/capture.md +801 -0
  141. package/dist/host/guides/probe.md +27 -8
  142. package/dist/host/guides/scaffold.md +152 -19
  143. package/dist/host/guides/study.md +534 -440
  144. package/dist/host/guides/test.md +111 -44
  145. package/dist/host/manifest.json +199 -242
  146. package/dist/host/tests/config.test.ts +1558 -219
  147. package/dist/host/tests/policy.test.ts +127 -1
  148. package/dist/host/tests/setupPolicy.ts +1389 -39
  149. package/dist/src/core/index.cjs +239 -87
  150. package/dist/src/core/index.cjs.map +1 -1
  151. package/dist/src/core/index.d.cts +123 -39
  152. package/dist/src/core/index.d.ts +123 -39
  153. package/dist/src/core/index.js +234 -86
  154. package/dist/src/core/index.js.map +1 -1
  155. package/dist/src/server/index.cjs +20 -27
  156. package/dist/src/server/index.cjs.map +1 -1
  157. package/dist/src/server/index.js +21 -28
  158. package/dist/src/server/index.js.map +1 -1
  159. package/package.json +11 -11
  160. package/dist/agents/skills/orkestrel-dispatch/scripts/helpers.js +0 -283
  161. package/dist/host/agents/skills/orkestrel-dispatch/references/bench.md +0 -25
  162. package/dist/host/agents/skills/orkestrel-dispatch/scripts/helpers.ts +0 -333
  163. package/dist/host/agents/skills/orkestrel-parallel/references/harnesses.md +0 -19
  164. package/dist/host/guides/supervisor.md +0 -4890
@@ -15,22 +15,49 @@
15
15
  // acceptance line, stops at the first refusal and names the package to resume from, then confirms
16
16
  // each accepted version against the registry. --confirm re-reads the registry for each name@version every 5
17
17
  // seconds until it serves the version or the wait (default 120 seconds) ends. Exit 0 when the mode
18
- // succeeded, 3 when it did not, 64 on usage.
19
- import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
18
+ // succeeded, 3 when it did not, 64 on usage, including a flag the script does not read and the
19
+ // `--flag=value` form.
20
+ import type { SpawnSyncReturns } from 'node:child_process'
21
+ import { spawnSync } from 'node:child_process'
22
+ import { existsSync, mkdirSync, readFileSync, realpathSync, statSync, writeFileSync } from 'node:fs'
20
23
  import { dirname, join, resolve } from 'node:path'
21
- import {
22
- parseJsonRecord,
23
- readList,
24
- readMissingFlags,
25
- readOption,
26
- readString,
27
- runNpm,
28
- } from '../../orkestrel-dispatch/scripts/helpers.ts'
29
24
 
30
- const POLL_MS = 5_000
31
- const ACCEPTED = /^\+ (\S+)@(\S+)\s*$/mu
25
+ /** Holds the milliseconds between two polls of the registry. */
26
+ export const POLL_MS = 5_000
32
27
 
33
- interface Upload {
28
+ /** Matches the acceptance line `npm publish` prints and captures the name and the version. */
29
+ export const ACCEPTED = /^\+ (\S+)@(\S+)\s*$/mu
30
+
31
+ /** Lists every flag the script reads, value flags and switches alike. */
32
+ export const FLAGS: readonly string[] = [
33
+ '--whoami',
34
+ '--login',
35
+ '--publish',
36
+ '--confirm',
37
+ '--wait',
38
+ '--otp',
39
+ '--json',
40
+ ]
41
+
42
+ /** Holds the usage line the script prints when no mode or two modes were given. */
43
+ export const USAGE =
44
+ 'usage: window.ts --whoami [--wait SECONDS] | --login [--wait SECONDS] | --publish DIR... --otp CODE [--json] | --confirm NAME@VERSION... [--wait SECONDS]'
45
+
46
+ /** Describes how to spawn npm on this host without a shell where the CLI entry is present. */
47
+ export interface NpmCommand {
48
+ readonly file: string
49
+ readonly prefix: readonly string[]
50
+ readonly shell: boolean
51
+ }
52
+
53
+ /** Describes the name and version a package manifest declares. */
54
+ export interface ManifestName {
55
+ readonly name: string | undefined
56
+ readonly version: string | undefined
57
+ }
58
+
59
+ /** Describes one package upload and what the registry did with it. */
60
+ export interface Upload {
34
61
  readonly directory: string
35
62
  readonly name: string | undefined
36
63
  readonly version: string | undefined
@@ -41,36 +68,206 @@ interface Upload {
41
68
  readonly journal: string
42
69
  }
43
70
 
44
- interface Published {
71
+ /** Describes the uploads a batch made and whether a refusal stopped it. */
72
+ export interface Published {
45
73
  readonly uploads: readonly Upload[]
46
74
  readonly stopped: boolean
47
75
  }
48
76
 
49
- function waitFor(ms: number): Promise<void> {
77
+ /**
78
+ * Reads the value that follows a flag.
79
+ *
80
+ * @remarks
81
+ * Reads `--flag value` pairs: the first occurrence of a flag wins, a following value that opens
82
+ * with `--` is a missing value, and the `--flag=value` form is not read.
83
+ *
84
+ * @param argv - The arguments after the script path.
85
+ * @param name - The flag, such as `--otp`.
86
+ * @returns The value after the flag, or undefined when the flag is absent, last, or followed by
87
+ * another flag.
88
+ */
89
+ export function readOption(argv: readonly string[], name: string): string | undefined {
90
+ const index = argv.indexOf(name)
91
+ const value = index === -1 ? undefined : argv[index + 1]
92
+ return value === undefined || value.startsWith('--') ? undefined : value
93
+ }
94
+
95
+ /**
96
+ * Names the value flags a caller gave with no value.
97
+ *
98
+ * @param argv - The arguments after the script path.
99
+ * @param names - The flags that take a value.
100
+ * @returns Each named flag that appears last or is followed by another flag, in argument order.
101
+ */
102
+ export function readMissingFlags(
103
+ argv: readonly string[],
104
+ names: readonly string[],
105
+ ): readonly string[] {
106
+ return names.filter((name) => argv.includes(name) && readOption(argv, name) === undefined)
107
+ }
108
+
109
+ /**
110
+ * Names the flags a caller gave that the script does not read.
111
+ *
112
+ * @remarks
113
+ * Reads the arguments before a bare `--`, which opens a command a script passes on. A `--flag=value`
114
+ * argument is unknown, because the script reads no such form.
115
+ *
116
+ * @param argv - The arguments after the script path.
117
+ * @param flags - Every flag the script reads, value flags and switches alike.
118
+ * @returns Each argument that opens with `--` and is not a read flag, in argument order.
119
+ */
120
+ export function readUnknownFlags(
121
+ argv: readonly string[],
122
+ flags: readonly string[],
123
+ ): readonly string[] {
124
+ const end = argv.indexOf('--')
125
+ return (end === -1 ? argv : argv.slice(0, end)).filter(
126
+ (argument) => argument.startsWith('--') && !flags.includes(argument),
127
+ )
128
+ }
129
+
130
+ /**
131
+ * Reads the values that follow a flag up to the next flag.
132
+ *
133
+ * @param argv - The arguments after the script path.
134
+ * @param name - The flag, such as `--publish`.
135
+ * @returns The values between the flag and the next `--` argument, empty when the flag is absent.
136
+ */
137
+ export function readList(argv: readonly string[], name: string): readonly string[] {
138
+ const index = argv.indexOf(name)
139
+ if (index === -1) return []
140
+ const rest = argv.slice(index + 1)
141
+ const end = rest.findIndex((argument) => argument.startsWith('--'))
142
+ return end === -1 ? rest : rest.slice(0, end)
143
+ }
144
+
145
+ /**
146
+ * Parses a whole text as one JSON object.
147
+ *
148
+ * @param text - A JSON document, formatted or not, such as a manifest.
149
+ * @returns The parsed object, or undefined when the text is not a JSON object.
150
+ */
151
+ export function parseJsonRecord(text: string): Record<string, unknown> | undefined {
152
+ try {
153
+ const parsed: unknown = JSON.parse(text)
154
+ if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
155
+ return Object.fromEntries(Object.entries(parsed))
156
+ }
157
+ } catch {}
158
+ return undefined
159
+ }
160
+
161
+ /**
162
+ * Reads a string member of a record.
163
+ *
164
+ * @param record - The record, or undefined.
165
+ * @param key - The member name.
166
+ * @returns The string, or undefined when the member is absent or not a string.
167
+ */
168
+ export function readString(
169
+ record: Record<string, unknown> | undefined,
170
+ key: string,
171
+ ): string | undefined {
172
+ const value = record?.[key]
173
+ return typeof value === 'string' ? value : undefined
174
+ }
175
+
176
+ /**
177
+ * Resolves the npm command for this host.
178
+ *
179
+ * @remarks
180
+ * The `npm-cli.js` entry beside the running Node binary spawns without a shell on every host; the
181
+ * `npm` shim on Windows is a `.cmd` file that needs one, so that path is the fallback alone.
182
+ *
183
+ * @returns The file to spawn, the arguments that precede npm's own, and whether a shell is needed.
184
+ */
185
+ export function resolveNpm(): NpmCommand {
186
+ const cli = join(dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js')
187
+ if (existsSync(cli)) return { file: process.execPath, prefix: [cli], shell: false }
188
+ return { file: 'npm', prefix: [], shell: process.platform === 'win32' }
189
+ }
190
+
191
+ /**
192
+ * Runs npm with the given arguments and returns the finished child.
193
+ *
194
+ * @param args - The npm arguments, such as `['whoami']`.
195
+ * @param cwd - The working directory. Default: the current directory.
196
+ * @returns The spawn result with `utf8` stdout and stderr.
197
+ */
198
+ export function runNpm(args: readonly string[], cwd?: string): SpawnSyncReturns<string> {
199
+ const npm = resolveNpm()
200
+ return spawnSync(npm.file, [...npm.prefix, ...args], {
201
+ encoding: 'utf8',
202
+ windowsHide: true,
203
+ shell: npm.shell,
204
+ maxBuffer: 64 * 1024 * 1024,
205
+ ...(cwd === undefined ? {} : { cwd }),
206
+ })
207
+ }
208
+
209
+ /**
210
+ * Waits for a number of milliseconds.
211
+ *
212
+ * @param ms - The delay.
213
+ * @returns A promise that settles after the delay.
214
+ */
215
+ export function waitFor(ms: number): Promise<void> {
50
216
  return new Promise((settle) => {
51
217
  setTimeout(settle, ms)
52
218
  })
53
219
  }
54
220
 
55
- function readWhoami(): string | undefined {
221
+ /**
222
+ * Reads the account `npm whoami` names once.
223
+ *
224
+ * @returns The user name, or undefined when npm exited nonzero or printed nothing.
225
+ */
226
+ export function readWhoami(): string | undefined {
56
227
  const result = runNpm(['whoami'])
57
228
  const user = result.stdout.trim()
58
229
  return result.status === 0 && user !== '' ? user : undefined
59
230
  }
60
231
 
61
- async function pollWhoami(waitSeconds: number, started = Date.now()): Promise<string | undefined> {
232
+ /**
233
+ * Polls `npm whoami` until it answers or the wait ends.
234
+ *
235
+ * @param waitSeconds - How long to keep polling; 0 reads once.
236
+ * @param started - The millisecond the polling began. Default: now.
237
+ * @returns The user name, or undefined when none answered within the wait.
238
+ */
239
+ export async function pollWhoami(
240
+ waitSeconds: number,
241
+ started = Date.now(),
242
+ ): Promise<string | undefined> {
62
243
  const user = readWhoami()
63
244
  if (user !== undefined || Date.now() - started >= waitSeconds * 1000) return user
64
245
  await waitFor(POLL_MS)
65
246
  return pollWhoami(waitSeconds, started)
66
247
  }
67
248
 
68
- function readServed(name: string, version: string): boolean {
249
+ /**
250
+ * Checks whether the registry serves one version of a package.
251
+ *
252
+ * @param name - The package name.
253
+ * @param version - The version.
254
+ * @returns True if `npm view` reads exactly that version; false otherwise.
255
+ */
256
+ export function readServed(name: string, version: string): boolean {
69
257
  const result = runNpm(['view', `${name}@${version}`, 'version', '--json'])
70
258
  return result.status === 0 && result.stdout.replace(/["\s]/gu, '') === version
71
259
  }
72
260
 
73
- async function confirmServed(
261
+ /**
262
+ * Polls the registry until it serves one version or the wait ends.
263
+ *
264
+ * @param name - The package name.
265
+ * @param version - The version.
266
+ * @param waitSeconds - How long to keep polling; 0 reads once.
267
+ * @param started - The millisecond the polling began. Default: now.
268
+ * @returns True if the registry served the version within the wait; false otherwise.
269
+ */
270
+ export async function confirmServed(
74
271
  name: string,
75
272
  version: string,
76
273
  waitSeconds: number,
@@ -82,20 +279,35 @@ async function confirmServed(
82
279
  return confirmServed(name, version, waitSeconds, started)
83
280
  }
84
281
 
85
- function findGitRoot(directory: string): string {
282
+ /**
283
+ * Finds the nearest directory at or above one that holds a `.git` entry.
284
+ *
285
+ * @param directory - The directory to start from.
286
+ * @returns The nearest directory with a `.git` entry, or the filesystem root when none has one.
287
+ */
288
+ export function findGitRoot(directory: string): string {
86
289
  return existsSync(join(directory, '.git')) || dirname(directory) === directory
87
290
  ? directory
88
291
  : findGitRoot(dirname(directory))
89
292
  }
90
293
 
91
- function readErrorCode(text: string): string | undefined {
294
+ /**
295
+ * Reads the error code npm printed.
296
+ *
297
+ * @param text - npm's standard error.
298
+ * @returns The first `E` code in the text, such as `EOTP` or `E401`, or undefined when none appears.
299
+ */
300
+ export function readErrorCode(text: string): string | undefined {
92
301
  return text.match(/\b(E[A-Z0-9]{3,}|EOTP|E401|E403|E404)\b/u)?.[1]
93
302
  }
94
303
 
95
- function readManifestName(directory: string): {
96
- readonly name: string | undefined
97
- readonly version: string | undefined
98
- } {
304
+ /**
305
+ * Reads the name and version a directory's package manifest declares.
306
+ *
307
+ * @param directory - The directory that holds `package.json`.
308
+ * @returns The name and version, each undefined when the manifest is absent, unreadable, or lacks it.
309
+ */
310
+ export function readManifestName(directory: string): ManifestName {
99
311
  const path = join(directory, 'package.json')
100
312
  if (!existsSync(path)) return { name: undefined, version: undefined }
101
313
  try {
@@ -106,7 +318,15 @@ function readManifestName(directory: string): {
106
318
  }
107
319
  }
108
320
 
109
- function publishPackage(directory: string, otp: string): Upload {
321
+ /**
322
+ * Publishes one package directory and journals the upload.
323
+ *
324
+ * @param directory - The package directory.
325
+ * @param otp - The account's one-time code.
326
+ * @returns The upload: accepted when npm exited 0 and printed the acceptance line, else refused with
327
+ * the error code npm named.
328
+ */
329
+ export function publishPackage(directory: string, otp: string): Upload {
110
330
  const manifest = readManifestName(directory)
111
331
  const journal = `tmp/units/publish-${(manifest.name ?? directory).replace(/[^A-Za-z0-9.-]+/gu, '-')}.log`
112
332
  // The refusal code must survive an inherited silent npm log level.
@@ -139,7 +359,16 @@ function publishPackage(directory: string, otp: string): Upload {
139
359
  return upload
140
360
  }
141
361
 
142
- async function publishAll(
362
+ /**
363
+ * Publishes a batch back to back and confirms each accepted version.
364
+ *
365
+ * @param directories - The package directories, in publish order.
366
+ * @param otp - The account's one-time code.
367
+ * @param waitSeconds - How long to wait for the registry to serve each accepted version.
368
+ * @returns One upload per directory published, ending at the first refusal; a directory after it is
369
+ * not published.
370
+ */
371
+ export async function publishAll(
143
372
  directories: readonly string[],
144
373
  otp: string,
145
374
  waitSeconds: number,
@@ -168,7 +397,20 @@ async function publishAll(
168
397
  return confirmed
169
398
  }
170
399
 
171
- async function main(argv: readonly string[]): Promise<number> {
400
+ /**
401
+ * Runs the mode the arguments name.
402
+ *
403
+ * @param argv - The arguments after the script path.
404
+ * @returns The exit code: 0 when the mode succeeded, 3 when it did not, 64 on usage.
405
+ */
406
+ export async function main(argv: readonly string[]): Promise<number> {
407
+ const unknown = readUnknownFlags(argv, FLAGS)
408
+ if (unknown.length > 0) {
409
+ console.error(
410
+ `window: unknown flag ${unknown.join(', ')}; give each flag and its value as separate arguments\n${USAGE}`,
411
+ )
412
+ return 64
413
+ }
172
414
  const missing = readMissingFlags(argv, ['--wait', '--otp'])
173
415
  if (missing.length > 0) {
174
416
  console.error(`window: ${missing.join(', ')} given with no value`)
@@ -265,23 +507,23 @@ async function main(argv: readonly string[]): Promise<number> {
265
507
  console.error('usage: window.ts --confirm NAME@VERSION... [--wait SECONDS]')
266
508
  return 64
267
509
  }
268
- const readings: Array<{ readonly target: string; readonly served: boolean }> = []
510
+ const results: Array<{ readonly target: string; readonly served: boolean }> = []
269
511
  for (const target of targets) {
270
512
  const at = target.lastIndexOf('@')
271
- readings.push({
513
+ results.push({
272
514
  target,
273
515
  served: await confirmServed(target.slice(0, at), target.slice(at + 1), wait),
274
516
  })
275
517
  }
276
- console.log(JSON.stringify({ mode: 'confirm', readings }))
277
- return readings.every((reading) => reading.served) ? 0 : 3
518
+ console.log(JSON.stringify({ mode: 'confirm', results }))
519
+ return results.every((result) => result.served) ? 0 : 3
278
520
  }
279
- console.error(
280
- 'usage: window.ts --whoami [--wait SECONDS] | --login [--wait SECONDS] | --publish DIR... --otp CODE [--json] | --confirm NAME@VERSION... [--wait SECONDS]',
281
- )
521
+ console.error(USAGE)
282
522
  return 64
283
523
  }
284
524
 
285
- main(process.argv.slice(2)).then((code) => {
286
- process.exitCode = code
287
- })
525
+ if (process.argv[1] !== undefined && realpathSync(process.argv[1]) === import.meta.filename) {
526
+ main(process.argv.slice(2)).then((code) => {
527
+ process.exitCode = code
528
+ })
529
+ }
@@ -10,9 +10,9 @@ A map is mechanical; reading is not. Run the map first, hand it to the reader, a
10
10
 
11
11
  ## Scripts
12
12
 
13
- | Script | Does |
14
- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
- | `scripts/map.ts` | `--tree PATH...` files with line counts and sizes; `--headings PATH...` Markdown headings with lines; `--frontmatter PATH...` every key of a frontmatter block, a TOML file, or a YAML file with its whole value, nested keys dotted (`interface.default_prompt`); `--exports PATH...` `export` lines in TypeScript files (a text reading); `--census TERM...` every line carrying a term (a term that opens with `--` is refused as a missing value, so drop a custom property's leading dashes); `--refs PATH` every line naming a path or its basename; `--anchors PATH...` every `§ Heading` reference whose heading its target lacks. `--paths a,b` narrows a census or refs walk; `--ignore-case`; `--json`; `--out FILE` writes the map instead of printing it. Walks skip `node_modules`, `.git`, `dist`, `tmp`, `.orkestrel`, `.idea`, and `host.json`. |
13
+ | Script | Does |
14
+ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
+ | `scripts/map.ts` | `--tree PATH...` files with line counts and sizes; `--headings PATH...` Markdown headings with lines, outside fenced blocks of backticks or tildes; `--frontmatter PATH...` every key of a frontmatter block, a TOML file, or a YAML file with its whole value, nested keys dotted (`interface.default_prompt`); `--exports PATH...` `export` lines in TypeScript files (a text reading); `--census TERM...` every line carrying a term (a term that opens with `--` is refused as a missing value, so drop a custom property's leading dashes); `--refs PATH` every line naming a path or its basename; `--anchors PATH...` every `§ Heading` reference whose heading its target lacks. `--paths a,b` narrows a census or refs walk; `--ignore-case`; `--json`; `--out FILE` writes the map instead of printing it. Walks skip `node_modules`, `.git`, `dist`, `tmp`, `.orkestrel`, `.idea`, and `host.json`. |
16
16
 
17
17
  ## Map
18
18