rvlib-shipkit 0.11.0 → 0.13.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 (60) hide show
  1. package/CHANGELOG.md +166 -0
  2. package/README.md +17 -3
  3. package/package.json +5 -2
  4. package/src/asks/asks-queue.ts +8 -0
  5. package/src/assets/kit-config.ts +2 -3
  6. package/src/assets/roll-workspace.ts +2 -1
  7. package/src/assets/run-assets.ts +0 -11
  8. package/src/assets/server/studio-server.ts +7 -24
  9. package/src/cli/cmd_Assets.ts +3 -14
  10. package/src/cli/cmd_Check.ts +22 -4
  11. package/src/cli/cmd_Reflect.ts +116 -0
  12. package/src/cli/cmd_Rules.ts +32 -0
  13. package/src/cli/cmd_Serve.ts +1 -1
  14. package/src/cli.ts +5 -2
  15. package/src/collections/actions.ts +19 -0
  16. package/src/collections/collection-types.ts +13 -0
  17. package/src/collections/graphs.ts +34 -0
  18. package/src/collections/pings.ts +5 -1
  19. package/src/collections/posts.ts +20 -1
  20. package/src/collections/providers.ts +163 -50
  21. package/src/contracts/apply-contracts.ts +47 -0
  22. package/src/contracts/run-contracts.ts +8 -3
  23. package/src/core/canonical-scripts.ts +46 -0
  24. package/src/core/check-suite.ts +6 -3
  25. package/src/core/locked-write.ts +39 -0
  26. package/src/core/motherhome.ts +87 -0
  27. package/src/core/run-check.ts +28 -15
  28. package/src/core/run-ci.ts +4 -0
  29. package/src/core/write-status.ts +7 -26
  30. package/src/init-repo.ts +24 -11
  31. package/src/reflect/prompt-corpus.ts +305 -0
  32. package/src/repo-config.ts +20 -38
  33. package/src/rules/SK001-package-json-shape.ts +156 -0
  34. package/src/rules/SK002-contracts-fulfillness.ts +19 -0
  35. package/src/rules/SK003-claude-md-shape.ts +64 -0
  36. package/src/rules/SK004-gitignore-policy.ts +105 -0
  37. package/src/rules/SK005-rvlib-versions.ts +99 -0
  38. package/src/rules/SK006-npm-publish-safety.ts +42 -0
  39. package/src/rules/SK007-github-visibility.ts +63 -0
  40. package/src/rules/rule-registry.ts +32 -0
  41. package/src/rules/rule.ts +62 -0
  42. package/src/rules/run-fix.ts +57 -0
  43. package/src/serve/hub-app/col-card.tsx +192 -0
  44. package/src/serve/hub-app/cols-svc.ts +136 -0
  45. package/src/serve/hub-app/geometry-view.tsx +155 -0
  46. package/src/serve/hub-app/health-svc.ts +49 -0
  47. package/src/serve/hub-app/hub-app.tsx +236 -215
  48. package/src/serve/hub-app/hub-st.ts +169 -0
  49. package/src/serve/hub-app/hub-types.ts +72 -0
  50. package/src/serve/hub-app/hub.css +286 -59
  51. package/src/serve/hub-app/index.html +1 -1
  52. package/src/serve/hub-app/md.ts +24 -0
  53. package/src/serve/hub-app/media-svc.ts +429 -0
  54. package/src/serve/hub-app/media-view.tsx +299 -0
  55. package/src/serve/run-serve.ts +23 -9
  56. package/src/serve/serve-hub.ts +62 -47
  57. package/src/shipkit.ts +9 -5
  58. package/src/assets/server/studio-page.ts +0 -687
  59. package/src/core/package-json-check.ts +0 -88
  60. package/src/serve/hub-pages.ts +0 -38
@@ -1,88 +0,0 @@
1
- // built-in `package.json` probe for `shipkit check` — enforces the canonical
2
- // shape (meta/rv-conventions/package-json.md): private:true, no license,
3
- // capability-required script names FAIL, banned aliases + missing baseline
4
- // trio WARN. Doctor surface only — never wired into the ci gate. Pure local
5
- // fs, no TTL.
6
- import { existsSync } from 'fs'
7
- import { join } from 'path'
8
- import type { CheckCtx } from './check-ctx.ts'
9
- import type { RepoConfig } from '../repo-config.ts'
10
-
11
- type Pkg = { name?: string; private?: boolean; license?: string; scripts?: Record<string, string> }
12
-
13
- // alias → { canonical, bodyMatch } — WARN only when the body proves the
14
- // alias really is the canonical capability (a `serve` that serves a dist/
15
- // folder must not be told to rename)
16
- const ALIASES: Record<string, { canonical: string; bodyMatch: RegExp }> = {
17
- 'serve': { canonical: 'hub', bodyMatch: /(shipkit|cli\.ts) serve\b/ },
18
- 'identity': { canonical: 'assets:identity', bodyMatch: /assets(:| )identity\b/ },
19
- 'pretty': { canonical: 'fmt', bodyMatch: /./ },
20
- 'check-types': { canonical: 'typecheck', bodyMatch: /./ },
21
- }
22
-
23
- const BASELINE = ['test', 'lint', 'fmt'] as const
24
-
25
- export async function evaluatePackageJson(t: CheckCtx, root: string, c: RepoConfig): Promise<void> {
26
- const spec = `${c.docsBase ?? ''}meta/rv-conventions/package-json.md`
27
- let pkg: Pkg
28
- try {
29
- pkg = (await Bun.file(join(root, 'package.json')).json()) as Pkg
30
- } catch (e) {
31
- t.fail(`package.json unreadable (${String(e).slice(0, 120)})`, spec)
32
- return
33
- }
34
- const scripts = pkg.scripts ?? {}
35
- // rvlib-* as a probe ROOT is theoretical (libs live as sub-packages and
36
- // the probe only runs where repo.config.ts loads) — field inversion is
37
- // still handled so a future lib-root repo fails correctly, not absurdly
38
- const isLib = pkg.name?.startsWith('rvlib-') === true
39
-
40
- // published rvlib-* roots invert the field rules (distribute/npm owns them)
41
- if (isLib) {
42
- if (pkg.license !== 'MIT') t.fail(`package.json: rvlib-* package must carry license MIT`, spec)
43
- if (pkg.private === true) t.fail(`package.json: rvlib-* package must not be private`, spec)
44
- } else {
45
- if (pkg.private !== true) t.fail(`package.json: "private": true is required`, spec)
46
- if (pkg.license != null) t.fail(`package.json: license field is banned on project repos (rvlib-* sub-packages excepted)`, spec)
47
- }
48
-
49
- // aliases first — an alias satisfies the requirement but earns a WARN,
50
- // never a double report (alias WARN + missing-canonical FAIL). A banned
51
- // alias lingering NEXT TO its canonical still WARNs (sweep on sight),
52
- // it just doesn't satisfy anything.
53
- const aliased = new Set<string>()
54
- for (const [name, a] of Object.entries(ALIASES)) {
55
- const body = scripts[name]
56
- if (body == null || !a.bodyMatch.test(body)) continue
57
- if (scripts[a.canonical] == null) aliased.add(a.canonical)
58
- t.warn(`package.json: script '${name}' is the house '${a.canonical}' — ${scripts[a.canonical] == null ? 'rename it' : `delete it ('${a.canonical}' exists)`}`, spec)
59
- }
60
-
61
- const has = (name: string): boolean => scripts[name] != null || aliased.has(name)
62
-
63
- // capability → required script names (FAIL); repo.config.ts existence is
64
- // implied — this probe only runs under `shipkit check`
65
- const required: Array<{ name: string; why: string }> = [
66
- { name: 'ci', why: 'shipkit repo' },
67
- { name: 'check', why: 'shipkit repo' },
68
- { name: 'contracts', why: 'shipkit repo' },
69
- { name: 'tui', why: 'shipkit repo' },
70
- { name: 'hub', why: 'shipkit repo' },
71
- { name: 'mv', why: 'shipkit repo' },
72
- ]
73
- if (existsSync(join(root, '.rv-assets/kit.ts'))) required.push({ name: 'assets', why: '.rv-assets/kit.ts exists' })
74
- if (existsSync(join(root, '.rv-identity/brand.ts'))) required.push({ name: 'assets:identity', why: '.rv-identity/brand.ts exists' })
75
- if (existsSync(join(root, '.rv-comfy'))) required.push({ name: 'comfy:up', why: '.rv-comfy/ exists' })
76
- if (existsSync(join(root, 'deploy.sh'))) required.push({ name: 'up', why: 'deploy.sh exists' })
77
- if (existsSync(join(root, c.asks?.dir ?? 'asks'))) required.push({ name: 'asks', why: 'ask queue exists' })
78
-
79
- const missing = required.filter((r) => !has(r.name))
80
- for (const m of missing) t.fail(`package.json: missing script '${m.name}' (${m.why})`, spec)
81
-
82
- const missingBaseline = BASELINE.filter((n) => !has(n))
83
- if (missingBaseline.length > 0)
84
- t.warn(`package.json: missing baseline script(s): ${missingBaseline.join(', ')}`, spec)
85
-
86
- if (!t.failed && !t.warned)
87
- t.ok(`package.json shape ok (private, no license, ${required.length} required scripts present)`)
88
- }
@@ -1,38 +0,0 @@
1
- // Shared hub-page plumbing. The hub UI itself is the Bun-native React app
2
- // in hub-app/ (HTML import, served by serve-hub.ts — build/webapp
3
- // doctrine). This module keeps what the LEGACY pages (graph pages, studio
4
- // SPA) still need until they migrate: the server-injected rail, and the
5
- // attention section types the collector + React app share.
6
- import type { HubTool } from './serve-hub.ts'
7
-
8
- const esc = (s: string): string => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;')
9
-
10
- // ── the rail: thin fixed tab bar, injected into legacy hub-served pages ───
11
- const RAIL_ICONS: Record<string, string> = { '/': '⚡', '/graph/wiki': '🕸', '/graph/agent': '🤖', '/assets/': '🖼', '/status': '📊' }
12
-
13
- const RAIL_CSS = `
14
- .skrail{position:fixed;left:0;top:0;bottom:0;width:46px;z-index:9999;display:flex;flex-direction:column;
15
- align-items:center;padding-top:10px;gap:4px;background:#17140f;border-right:1px solid #332b22}
16
- .skrail a{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:9px;
17
- font-size:17px;text-decoration:none;border-left:2px solid transparent;filter:grayscale(35%);opacity:.75;position:relative}
18
- .skrail a:hover{background:#262019;filter:none;opacity:1}
19
- .skrail a.on{background:#262019;border-left-color:#e88c28;filter:none;opacity:1}
20
- .skrail a i{position:absolute;top:4px;right:4px;width:8px;height:8px;border-radius:50%;background:#e05252}
21
- body{padding-left:46px}`
22
-
23
- /** Inject the rail into a legacy hub-served HTML document (post-<body>,
24
- * fixed position — the page needs no layout knowledge). The React app
25
- * renders its own identical rail from /hub.json. */
26
- export function withRail(html: string, o: { tools: HubTool[]; active: string }): string {
27
- const items = [{ path: '/', label: 'attention', desc: 'everything red' }, ...o.tools.filter((t) => t.path !== '/')]
28
- .map((t) => {
29
- const on = o.active === t.path || (t.path !== '/' && o.active.startsWith(t.path))
30
- const dot = t.path === '/' ? '<i id=skdot style="display:none"></i>' : ''
31
- return `<a class="${on ? 'on' : ''}" href="${esc(t.path)}" title="${esc(t.label)} — ${esc(t.desc)}">${RAIL_ICONS[t.path] ?? '·'}${dot}</a>`
32
- })
33
- .join('')
34
- // the red dot fetches client-side: a passive page nav must never run the
35
- // needs projection server-side (cost + heal side-effects)
36
- const dotScript = `<script>fetch('/api/needs.json').then(r=>r.json()).then(j=>{const n=(j.needs||[]).filter(x=>x.severity==='red').length;if(n>0)document.getElementById('skdot').style.display=''}).catch(()=>{})</script>`
37
- return html.replace(/<body([^>]*)>/, `<body$1><style>${RAIL_CSS}</style><nav class=skrail>${items}</nav>${dotScript}`)
38
- }