rvlib-shipkit 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/package.json +1 -1
  3. package/src/agent-time/mask.ts +43 -0
  4. package/src/agent-time/render.ts +187 -0
  5. package/src/agent-time/report.ts +335 -0
  6. package/src/agent-time/shape.ts +271 -0
  7. package/src/agent-time/signals.ts +84 -0
  8. package/src/agent-time/store.ts +379 -0
  9. package/src/agent-time/timeline.ts +218 -0
  10. package/src/agent-time/turns.ts +135 -0
  11. package/src/cli/cmd_Commands.ts +5 -0
  12. package/src/cli/cmd_Review.ts +948 -0
  13. package/src/cli/cmd_Time.ts +112 -0
  14. package/src/cli/cmd_Trailer.ts +259 -6
  15. package/src/cli/kit/verb.ts +14 -4
  16. package/src/cli.ts +6 -4
  17. package/src/collections/collection-types.ts +6 -1
  18. package/src/collections/providers.ts +3 -0
  19. package/src/collections/review.ts +70 -0
  20. package/src/core/layout.ts +2 -0
  21. package/src/reflect/prompt-corpus.ts +15 -119
  22. package/src/reflect/transcript.ts +254 -0
  23. package/src/review/anim.ts +228 -0
  24. package/src/review/auto.ts +141 -0
  25. package/src/review/format.ts +150 -0
  26. package/src/review/http.ts +23 -0
  27. package/src/review/marks.ts +43 -0
  28. package/src/review/media.ts +45 -0
  29. package/src/review/model.ts +854 -0
  30. package/src/review/project.ts +110 -0
  31. package/src/review/role.ts +48 -0
  32. package/src/review/routes.ts +228 -0
  33. package/src/review/store.ts +1002 -0
  34. package/src/review/threads.ts +237 -0
  35. package/src/review/trailer-desk.ts +385 -0
  36. package/src/review/trailer-model.ts +183 -0
  37. package/src/review/trailer-routes.ts +136 -0
  38. package/src/review/unit.ts +165 -0
  39. package/src/review/watchers.ts +54 -0
  40. package/src/serve/embed.ts +35 -0
  41. package/src/serve/hub-app/activity-view.tsx +1 -1
  42. package/src/serve/hub-app/activity.css +0 -4
  43. package/src/serve/hub-app/dock-view.tsx +5 -4
  44. package/src/serve/hub-app/hash-route.ts +28 -1
  45. package/src/serve/hub-app/header-bar.tsx +3 -1
  46. package/src/serve/hub-app/home-st.ts +10 -2
  47. package/src/serve/hub-app/home-strip.tsx +3 -2
  48. package/src/serve/hub-app/home.html +1 -0
  49. package/src/serve/hub-app/hub-app.tsx +80 -11
  50. package/src/serve/hub-app/hub.css +45 -13
  51. package/src/serve/hub-app/icons.tsx +9 -0
  52. package/src/serve/hub-app/index.html +1 -0
  53. package/src/serve/hub-app/insights-view.tsx +4 -3
  54. package/src/serve/hub-app/keys.ts +14 -10
  55. package/src/serve/hub-app/layout-st.ts +63 -2
  56. package/src/serve/hub-app/planned-view.tsx +3 -2
  57. package/src/serve/hub-app/planned.css +0 -1
  58. package/src/serve/hub-app/project-list.css +22 -4
  59. package/src/serve/hub-app/project-list.tsx +41 -8
  60. package/src/serve/hub-app/projects-svc.ts +4 -0
  61. package/src/serve/hub-app/repo-sections.ts +15 -3
  62. package/src/serve/hub-app/repo-st.ts +45 -3
  63. package/src/serve/hub-app/review/clip-st.ts +208 -0
  64. package/src/serve/hub-app/review/kind-view.tsx +171 -0
  65. package/src/serve/hub-app/review/kit.tsx +113 -0
  66. package/src/serve/hub-app/review/review-answer.tsx +239 -0
  67. package/src/serve/hub-app/review/review-board.tsx +46 -0
  68. package/src/serve/hub-app/review/review-list.tsx +156 -0
  69. package/src/serve/hub-app/review/review-page.tsx +101 -0
  70. package/src/serve/hub-app/review/review-st.ts +779 -0
  71. package/src/serve/hub-app/review/review-views.tsx +503 -0
  72. package/src/serve/hub-app/review/trailer-page.tsx +416 -0
  73. package/src/serve/hub-app/review/trailer-st.ts +602 -0
  74. package/src/serve/hub-app/review/trailer-timeline.tsx +207 -0
  75. package/src/serve/hub-app/review.css +329 -0
  76. package/src/serve/hub-app/theme-st.ts +56 -0
  77. package/src/serve/hub-app/tree-build.ts +4 -0
  78. package/src/serve/hub-app/tree-row.tsx +1 -1
  79. package/src/serve/hub-app/tree-svc.ts +4 -0
  80. package/src/serve/hub-runtime.ts +5 -3
  81. package/src/serve/same-origin.ts +5 -1
  82. package/src/serve/serve-hub.ts +8 -0
  83. package/src/shipkit-files.ts +9 -5
  84. package/src/trailer/adapter.ts +58 -3
  85. package/src/trailer/build.ts +300 -41
  86. package/src/trailer/ffmpeg.ts +22 -0
  87. package/src/trailer/footage.ts +109 -40
  88. package/src/trailer/grammar.ts +444 -69
  89. package/src/trailer/graph.ts +199 -14
  90. package/src/trailer/holes.ts +72 -0
  91. package/src/trailer/load-unit.ts +20 -3
  92. package/src/trailer/look.ts +17 -3
  93. package/src/trailer/passage.ts +129 -0
  94. package/src/trailer/plan.ts +80 -21
  95. package/src/trailer/prune.ts +193 -0
  96. package/src/trailer/score/compose.ts +240 -0
  97. package/src/trailer/score/dsp.ts +272 -0
  98. package/src/trailer/score/palette.ts +188 -0
  99. package/src/trailer/score/pattern.ts +91 -0
  100. package/src/trailer/score/score.ts +214 -0
  101. package/src/trailer/score/sound.ts +112 -0
  102. package/src/trailer/score/synth.ts +361 -0
  103. package/src/trailer/states.ts +76 -0
  104. package/src/trailer/stitch.ts +92 -9
  105. package/src/trailer/titles.ts +123 -18
  106. package/src/trailer/vocab.ts +23 -4
  107. package/src/trailer/words.ts +254 -0
@@ -58,6 +58,7 @@ import { hubHandler, type CheckTap, type HubDeps, type HubHandler } from './serv
58
58
  import type { projectGutter } from './project-gutter-route.ts'
59
59
  import { defaultHubPort } from './hub-port.ts'
60
60
  import { LAYOUT } from '../core/layout.ts'
61
+ import { storeDir, storeStamp } from '../review/store.ts'
61
62
  export { defaultHubPort }
62
63
 
63
64
  export type ServeIo = { print: (l: string) => void; printErr: (l: string) => void }
@@ -347,9 +348,10 @@ export async function resolveHubDeps(root: string, boot: RepoConfig, io: ServeIo
347
348
  graphs,
348
349
  studio,
349
350
  refresh,
350
- // the config and the run record, by their stats: what the warm rows
351
- // and the runs page read besides the tracked files the watch sees
352
- stamp: () => `${configStamp(root)}|${record === '' ? '' : recordStamp(record)}`,
351
+ // the config, the run record and the review store (gitignored, its
352
+ // own watch counts its writes): what the warm rows and the runs page
353
+ // read besides the tracked files the watch sees
354
+ stamp: () => `${configStamp(root)}|${record === '' ? '' : recordStamp(record)}|${storeStamp(storeDir(root))}`,
353
355
  runCli,
354
356
  // the run happens IN THIS PROCESS, so every check result reaches the
355
357
  // page as it finishes. A shelled run could only ever deliver one wall
@@ -10,5 +10,9 @@ export function sameOrigin(req: Request): boolean {
10
10
  if (site != null && site !== 'same-origin' && site !== 'none') return false
11
11
  const origin = req.headers.get('origin')
12
12
  if (origin == null) return true
13
- try { return new URL(origin).host === new URL(req.url).host } catch { return false }
13
+ // behind a proxy that frames the page under its own origin (pkm-multi's admin), the host the browser asked is the
14
+ // proxy's, which it passes on. A browser cannot forge the header: a page of another site that sets it is held at
15
+ // its preflight, which this server never answers
16
+ const host = req.headers.get('x-forwarded-host') ?? new URL(req.url).host
17
+ try { return new URL(origin).host === host } catch { return false }
14
18
  }
@@ -32,6 +32,8 @@ import { sameOrigin } from './same-origin.ts'
32
32
  import { LAYOUT } from '../core/layout.ts'
33
33
  import { WarmCell } from './warm-cell.ts'
34
34
  import { readmeRoutes } from './readme-route.ts'
35
+ import { reviewRoutes } from '../review/routes.ts'
36
+ import { embedRoute } from './embed.ts'
35
37
 
36
38
  export { sameOrigin }
37
39
 
@@ -338,6 +340,12 @@ export function hubHandler(d: HubDeps, o: { base?: string; projects?: Gutter } =
338
340
  // the empty page shows the README, its images served from the repo
339
341
  const readme = await readmeRoutes(d.root, path)
340
342
  if (readme != null) return readme
343
+ // the review or trailer page alone, for another tool's frame (embed.ts)
344
+ const embed = await embedRoute(path)
345
+ if (embed != null) return embed
346
+ // the one feedback store and the trailer view (review/routes.ts)
347
+ const review = await reviewRoutes(d.root, req, path)
348
+ if (review != null) return review
341
349
  if (path === '/api/status.json') {
342
350
  const p = join(d.root, LAYOUT.status)
343
351
  return Response.json({ statusMd: existsSync(p) ? await Bun.file(p).text() : null })
@@ -8,15 +8,19 @@
8
8
  // on the home page
9
9
  // trailer({...}) what `sk trailer` cannot read from a file: the
10
10
  // sounds the repo resolves by id (trailer/adapter.ts)
11
+ // review({...}) the repo's parts of the review store: its subject
12
+ // kinds, topics, sources, auto rules, the dispatcher
13
+ // (review/unit.ts)
11
14
  //
12
15
  // the one constraint a reader breaks first: every kind is IMPORTED by the
13
16
  // process that reads it (discovery, pm's config child, the daemon), so a file
14
17
  // keeps its top level cheap and puts heavy work inside a function.
15
18
  import { check, type CheckDef } from './check.ts'
19
+ import { review, type AutoRule, type KindDef, type ReviewDef, type ReviewSource, type ReviewUnit } from './review/unit.ts'
16
20
  import { trailer, type TrailerDef, type TrailerUnit } from './trailer/adapter.ts'
17
21
 
18
- export { check, trailer }
19
- export type { TrailerDef, TrailerUnit }
22
+ export { check, review, trailer }
23
+ export type { AutoRule, KindDef, ReviewDef, ReviewSource, ReviewUnit, TrailerDef, TrailerUnit }
20
24
 
21
25
  /** one button beside a global process: a command, or a function of the file */
22
26
  export type ProcessAction = {
@@ -73,7 +77,7 @@ export type WidgetDef = {
73
77
  export type GlobalProcessUnit = GlobalProcessDef & { kind: 'globalProcess' }
74
78
  export type WidgetUnit = WidgetDef & { kind: 'widget' }
75
79
 
76
- export const UNIT_KINDS = ['globalProcess', 'widget', 'trailer'] as const
80
+ export const UNIT_KINDS = ['globalProcess', 'widget', 'trailer', 'review'] as const
77
81
 
78
82
  const KEBAB = /^[a-z0-9][a-z0-9-]*$/
79
83
  const words = (v: unknown): boolean => Array.isArray(v) && v.length > 0 && v.every((w) => typeof w === 'string')
@@ -127,9 +131,9 @@ export function widget(d: WidgetDef): WidgetUnit {
127
131
  }
128
132
 
129
133
  /** the kind a default export says it is: a unit, else a check */
130
- export function unitKind(d: unknown): 'globalProcess' | 'widget' | 'trailer' | 'check' {
134
+ export function unitKind(d: unknown): (typeof UNIT_KINDS)[number] | 'check' {
131
135
  const k = (d as { kind?: unknown } | null)?.kind
132
- return k === 'globalProcess' || k === 'widget' || k === 'trailer' ? k : 'check'
136
+ return UNIT_KINDS.includes(k as (typeof UNIT_KINDS)[number]) ? (k as (typeof UNIT_KINDS)[number]) : 'check'
133
137
  }
134
138
 
135
139
  export type { CheckDef }
@@ -1,6 +1,9 @@
1
- // what a repo adds to the trailer core, all optional. Filming is never here: a film line calls the project CLI's
2
- // film rows (vocab.ts). A `trailer({...})` unit in any `<name>.shipkit.ts` resolves the sounds that are not files.
3
- // Discovered like every unit file (load-unit.ts), and read only when a cut names such a sound
1
+ // what a repo adds to the trailer core, all optional (wiki build/trailer.md, The trailer unit). Filming is never here:
2
+ // a film line calls the project CLI's film rows (vocab.ts). A `trailer({...})` unit in any `<name>.shipkit.ts`
3
+ // resolves the sounds that are not files, names the paths a take's world is, checks the content the language cannot
4
+ // know, refines one film line's take, and turns on the guards that read the repo's own words. Discovered like every
5
+ // unit file (load-unit.ts)
6
+ import type { TakeLength } from './grammar.ts'
4
7
 
5
8
  export interface TrailerSounds {
6
9
  /** a music id to an audio file of at least `seconds`, cached by the repo */
@@ -11,13 +14,65 @@ export interface TrailerSounds {
11
14
  voice?(text: string): Promise<string>
12
15
  }
13
16
 
17
+ /** a film line of the project CLI as the unit sees it: its words as written, the text of each `@<file>` word */
18
+ export interface TrailerFilmLine {
19
+ line: number
20
+ /** the shot or footage block it films */
21
+ shot: string
22
+ argv: string[]
23
+ output: 'still' | 'clip'
24
+ files: Record<string, string>
25
+ }
26
+
27
+ /** what `check` reads: the cut's film lines and the sounds it names by id */
28
+ export interface TrailerCheckInput {
29
+ file: string
30
+ dir: string
31
+ size: [number, number]
32
+ fps: number
33
+ films: TrailerFilmLine[]
34
+ /** each music cue that names an id; `opening`: the header's, the one the cut starts on */
35
+ music: { line: number; id: string; take?: string; from: number; opening: boolean }[]
36
+ sfx: { line: number; id: string }[]
37
+ voices: { line: number; text: string }[]
38
+ }
39
+
40
+ /** an error at its line; `warn`: said, never blocking */
41
+ export interface TrailerFinding {
42
+ line: number
43
+ message: string
44
+ warn?: true
45
+ }
46
+
47
+ /** one film line's take over its row's defaults: its version, how long it films, a fixed world, what its key reads */
48
+ export interface TakeAnswer {
49
+ version?: number | string
50
+ length?: TakeLength
51
+ /** the take's world for good (`rev:<commit>`): never stale */
52
+ world?: string
53
+ /** what the key reads instead of the words */
54
+ recipe?: unknown
55
+ }
56
+
14
57
  export interface TrailerDef {
15
58
  sound?: TrailerSounds
59
+ /** the paths a take's world is (`['src', 'public']`); default the whole tree, trailer/ and .shipkit/ left out */
60
+ stamp?: string[]
61
+ /** in process, before filming, in a few ms: the content the language cannot know */
62
+ check?(c: TrailerCheckInput): TrailerFinding[] | Promise<TrailerFinding[]>
63
+ /** one film line's take, when its words change what its row says */
64
+ take?(f: TrailerFilmLine & { version: number | string | null; length: TakeLength }): TakeAnswer | undefined
65
+ /** the `states` a film prints: parts a cut may land on (`ignore`), parts a shot never opens or ends on (`underWay`) */
66
+ states?: { ignore?: string[]; underWay?: string[] }
67
+ /** a clip take that drops its picture for a few frames is filmed again once, then refused (holes.ts) */
68
+ holes?: boolean
16
69
  }
17
70
 
18
71
  export type TrailerUnit = TrailerDef & { kind: 'trailer' }
19
72
 
20
73
  export function trailer(d: TrailerDef): TrailerUnit {
21
74
  for (const [k, f] of Object.entries(d.sound ?? {})) if (typeof f !== 'function') throw new Error(`trailer: sound.${k} is a function returning an audio file`)
75
+ for (const k of ['check', 'take'] as const) if (d[k] !== undefined && typeof d[k] !== 'function') throw new Error(`trailer: ${k} is a function`)
76
+ if (d.stamp !== undefined && (!Array.isArray(d.stamp) || d.stamp.some((p) => typeof p !== 'string' || p === '' || p.startsWith('/')))) throw new Error('trailer: stamp is a list of repo relative paths')
22
77
  return { ...d, kind: 'trailer' }
23
78
  }