rvlib-shipkit 1.9.0 → 2.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.
Files changed (108) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/package.json +1 -1
  3. package/src/check.ts +5 -2
  4. package/src/checks/SK001-package-json-shape.check.ts +47 -12
  5. package/src/checks/SK013-one-way-in-policy.check.ts +8 -7
  6. package/src/checks/SK017-vscode-hub-task.check.ts +109 -106
  7. package/src/checks/SK019-tests-run-in-units.check.ts +2 -1
  8. package/src/checks/SK027-agent-wiki-links.check.ts +5 -2
  9. package/src/checks/SK029-explicit-check-files.check.ts +25 -8
  10. package/src/checks/SK034-commit-gates-armed.check.ts +90 -0
  11. package/src/cli/cmd_Ci.ts +3 -3
  12. package/src/cli/cmd_Commands.ts +21 -5
  13. package/src/cli/cmd_Hooks.ts +6 -2
  14. package/src/cli/cmd_Init.ts +1 -1
  15. package/src/cli/cmd_Serve.ts +1 -2
  16. package/src/cli/cmd_Start.ts +1 -1
  17. package/src/cli.ts +2 -4
  18. package/src/core/budget.ts +6 -8
  19. package/src/core/bun-builtins.ts +36 -0
  20. package/src/core/bun-tool-cli.ts +23 -0
  21. package/src/core/canonical-scripts.ts +6 -4
  22. package/src/core/check-ctx.ts +104 -10
  23. package/src/core/check-file-names.ts +2 -3
  24. package/src/core/check-pass.ts +17 -1
  25. package/src/core/check-runner.ts +19 -6
  26. package/src/core/config-rewrite.ts +27 -6
  27. package/src/core/discover-checks.ts +30 -10
  28. package/src/core/floor-resolve.ts +16 -7
  29. package/src/core/job-run.ts +2 -2
  30. package/src/core/layout.ts +4 -1
  31. package/src/core/legacy-shapes.ts +40 -0
  32. package/src/core/one-lib.ts +1 -1
  33. package/src/core/one-way-in-policy.ts +106 -13
  34. package/src/core/pre-commit-discovery.ts +106 -0
  35. package/src/core/retired-checks.ts +2 -0
  36. package/src/core/run-ci.ts +16 -10
  37. package/src/core/run-fix.ts +9 -0
  38. package/src/core/run-fixers.ts +91 -35
  39. package/src/core/runs-matrix.ts +7 -0
  40. package/src/core/slots.ts +1 -1
  41. package/src/core/vendored-read.ts +39 -0
  42. package/src/core/vendored.ts +82 -0
  43. package/src/core/waivers.ts +125 -0
  44. package/src/core/when-block.ts +25 -185
  45. package/src/core/when.ts +30 -13
  46. package/src/core/wire.ts +17 -1
  47. package/src/git-bootstrap.ts +1 -1
  48. package/src/home/attach.ts +3 -5
  49. package/src/home/insights.ts +6 -1
  50. package/src/home/pm-config.ts +2 -2
  51. package/src/home/run-daemon.ts +2 -2
  52. package/src/home/run-unit-action.ts +2 -2
  53. package/src/hooks/commit-message.ts +2 -14
  54. package/src/hooks/hook-gates.ts +38 -0
  55. package/src/hooks/leak-guard.ts +8 -3
  56. package/src/hooks/one-way-in-guard.ts +8 -2
  57. package/src/hooks/root-identity.ts +9 -6
  58. package/src/hooks/run-hooks.ts +72 -41
  59. package/src/hooks/staged.ts +49 -0
  60. package/src/hooks/tree-shrink.ts +41 -0
  61. package/src/init-repo.ts +0 -42
  62. package/src/ledger/check-table.ts +1 -1
  63. package/src/ledger/gate-checks.ts +45 -9
  64. package/src/ledger/ledger.ts +6 -1
  65. package/src/ledger/repo-view.ts +14 -4
  66. package/src/ledger/toolchain-identity.ts +4 -2
  67. package/src/mv/mv-md.ts +6 -1
  68. package/src/problems/extract.ts +27 -5
  69. package/src/problems/problem-line.ts +2 -3
  70. package/src/release/run-release.ts +13 -8
  71. package/src/repo-config.ts +44 -27
  72. package/src/scaffold/files/bunfig.toml +11 -0
  73. package/src/scaffold/files/vscode-settings.json +1 -2
  74. package/src/scaffold/jsonc-spans.ts +16 -0
  75. package/src/scaffold/project-scaffold.ts +43 -77
  76. package/src/serve/hub-app/check-view.tsx +4 -4
  77. package/src/serve/hub-app/check-words.ts +1 -0
  78. package/src/serve/hub-app/checks-svc.ts +1 -2
  79. package/src/serve/hub-app/home.html +0 -1
  80. package/src/serve/hub-app/hub-types.ts +0 -2
  81. package/src/serve/hub-app/index.html +0 -1
  82. package/src/serve/hub-app/project-list.css +1 -2
  83. package/src/serve/hub-app/tree-build.ts +3 -2
  84. package/src/serve/hub-app/tree-svc.ts +2 -2
  85. package/src/serve/hub-port.ts +3 -3
  86. package/src/serve/hub-runtime.ts +0 -3
  87. package/src/serve/serve-hub.ts +1 -16
  88. package/src/serve/tracked-watch.ts +1 -1
  89. package/src/start-repo.ts +15 -3
  90. package/src/suite/judge-suites.ts +29 -28
  91. package/src/suite/select.ts +2 -0
  92. package/src/suite/suite-cli.ts +6 -3
  93. package/src/suite/suite-cpu-preload.ts +7 -11
  94. package/src/suite/test-units.ts +7 -4
  95. package/src/ts/config.ts +12 -5
  96. package/src/ts/imports.ts +3 -1
  97. package/src/tui/build-steps-panel.ts +1 -1
  98. package/src/wiki/scan-md.ts +16 -9
  99. package/src/checks/SK009-commit-ai-tells.check.ts +0 -131
  100. package/src/checks/SK014-prose-ste.check.ts +0 -78
  101. package/src/cli/cmd_Prose.ts +0 -74
  102. package/src/prose/prose-view.ts +0 -90
  103. package/src/prose/run-prose.ts +0 -187
  104. package/src/prose/ste.ts +0 -552
  105. package/src/scaffold/files/vscode-tasks.json +0 -24
  106. package/src/serve/hub-app/prose-hits-st.ts +0 -125
  107. package/src/serve/hub-app/prose-hits-view.tsx +0 -176
  108. package/src/serve/hub-app/prose-hits.css +0 -21
@@ -1,187 +0,0 @@
1
- // `shipkit prose` — the file-walking half of the STE checker. The judgement
2
- // lives in ste.ts; this module resolves WHICH files a repo puts in scope,
3
- // prints the wire lines, and applies the mechanical fixes.
4
- //
5
- // Scope is repo knowledge, never a hardcoded path list: a repo declares
6
- // `prose: { include, exempt }` in repo.config.ts. A repo that declares
7
- // nothing is not checked, and the rule says so instead of failing a repo
8
- // that never opted in.
9
- //
10
- // It reads and writes ONLY through the check context it is handed, so rule
11
- // SK014 is cached on the scope's listings and the files in it.
12
- import { isAbsolute, relative, resolve } from 'path'
13
- import type { CheckCtx } from '../core/check-ctx.ts'
14
- import { checkProse, fixProse, tierOf, type ProseClass, type ProseHit, type ProseTier, type ProseWords } from './ste.ts'
15
-
16
- /** what the walker reads and writes through: a check context */
17
- export type ProseDisk = Pick<CheckCtx, 'root' | 'read' | 'list' | 'write'>
18
-
19
- export type ProseConfig = {
20
- /** globs (repo-relative) that ARE house prose. Absent = the repo is not checked. */
21
- include?: string[]
22
- /** globs never checked: journals, archives, upstream snapshots, generated files */
23
- exempt?: string[]
24
- /** classes this repo does not check, with the reason living in repo.config.ts */
25
- allow?: ProseClass[]
26
- /** the 20-word procedural limit instead of the 25-word descriptive one */
27
- strict?: boolean
28
- /** this repo's own word lists over the house dictionary: `allow` drops
29
- * every hit on a word or phrase, `ban` refuses an extra one (a hard hit,
30
- * with its replacement). They bind this repo only, and the hub's SK014
31
- * page writes them through a managed block inside this object */
32
- words?: ProseWords
33
- /** soft-tier ratchet: the count this repo is allowed to carry. The gate
34
- * fails ABOVE it, so the number can only go down. Absent is the default,
35
- * and it means soft hits warn in `shipkit prose` and never reach a gate
36
- * or an attention surface. A four-digit count nobody can act on is worse
37
- * than no number at all. */
38
- baseline?: number
39
- }
40
-
41
- export type FileReport = {
42
- file: string
43
- hits: ProseHit[]
44
- /** the file as it was judged, kept only when the caller asked, so a page
45
- * can quote the line around each hit without reading the file twice */
46
- text?: string
47
- }
48
-
49
- export type ProseReport = {
50
- files: number
51
- hits: ProseHit[]
52
- byFile: FileReport[]
53
- byTier: Record<ProseTier, number>
54
- byClass: Record<string, number>
55
- fixable: number
56
- /** files rewritten by a --fix run */
57
- fixed: string[]
58
- scoped: boolean // did this repo declare any include globs?
59
- }
60
-
61
- const DEFAULT_EXEMPT = ['**/node_modules/**', '**/CHANGELOG.md', '**/dist/**', '**/.git/**']
62
-
63
- /** a generated file is not house prose, and rewriting it loses the change on
64
- * the next render */
65
- const isGenerated = (text: string): boolean => text.slice(0, 400).includes('<!-- generated')
66
-
67
- /** an explicitly named path, resolved against the repo and REFUSED when it
68
- * lands outside: `--fix` writes, and a checker that rewrites files outside
69
- * the repo it was pointed at is a much worse tool than one that says no. */
70
- export function explicitPath(root: string, p: string): string {
71
- const abs = isAbsolute(p) ? p : resolve(root, p)
72
- const rel = relative(root, abs)
73
- if (rel === '' || rel.startsWith('..')) throw new Error(`FAIL ${p} is outside ${root} — shipkit prose only reads and writes inside the repo`)
74
- return rel
75
- }
76
-
77
- /** does `path` match `pattern` under the dot rule: a hidden segment (one
78
- * starting with a dot) only where the pattern names a hidden segment itself,
79
- * never inside a `**` or under a wildcard segment */
80
- export function hiddenAllowed(pattern: string, path: string): boolean {
81
- const pat = pattern.split('/')
82
- const segs = path.split('/')
83
- const hidden = (s: string): boolean => s.startsWith('.') && s !== '.' && s !== '..'
84
- const star = pat.indexOf('**')
85
- const head = star === -1 ? pat : pat.slice(0, star)
86
- const tail = star === -1 ? [] : pat.slice(pat.lastIndexOf('**') + 1)
87
- for (let i = 0; i < segs.length; i++) {
88
- if (!hidden(segs[i]!)) continue
89
- const fromEnd = segs.length - 1 - i
90
- const p = i < head.length ? head[i] : fromEnd < tail.length ? tail[tail.length - 1 - fromEnd] : undefined
91
- if (p == null || !p.startsWith('.')) return false
92
- }
93
- return true
94
- }
95
-
96
- /** the files in scope: what git sees (tracked, or untracked and not ignored)
97
- * matching an include glob, the dot rule applied, minus every exempt glob */
98
- export function proseFiles(d: Pick<ProseDisk, 'list'>, cfg: ProseConfig): string[] {
99
- const include = cfg.include ?? []
100
- if (include.length === 0) return []
101
- const exempt = [...DEFAULT_EXEMPT, ...(cfg.exempt ?? [])].map((g) => new Bun.Glob(g))
102
- const seen = new Set<string>()
103
- for (const pattern of include)
104
- for (const rel of d.list(pattern)) {
105
- if (!hiddenAllowed(pattern, rel)) continue
106
- if (exempt.some((g) => g.match(rel))) continue
107
- seen.add(rel)
108
- }
109
- return [...seen].sort()
110
- }
111
-
112
- export function runProseCheck(o: { c: ProseDisk; config: ProseConfig; paths?: string[]; fix?: boolean; keepText?: boolean }): ProseReport {
113
- const cfg = o.config
114
- const d = o.c
115
- const files = o.paths != null && o.paths.length > 0 ? o.paths.map((p) => explicitPath(d.root, p)) : proseFiles(d, cfg)
116
- const explicit = o.paths != null && o.paths.length > 0
117
- const opts = { allow: cfg.allow, strict: cfg.strict, words: cfg.words }
118
- const byFile: FileReport[] = []
119
- const hits: ProseHit[] = []
120
- const fixed: string[] = []
121
- for (const rel of files) {
122
- let text: string | null
123
- try {
124
- text = d.read(rel)
125
- } catch (e) {
126
- text = null
127
- if (explicit) throw new Error(`FAIL cannot read ${rel} — ${String(e)}`)
128
- }
129
- if (text == null) {
130
- // a path a HUMAN typed that cannot be read is a loud failure. A
131
- // path the listing produced can vanish between the scan and the
132
- // read, and skipping that one is right.
133
- if (explicit) throw new Error(`FAIL cannot read ${rel} — no such file`)
134
- continue
135
- }
136
- if (isGenerated(text)) continue
137
- if (o.fix === true) {
138
- const out = fixProse(text, opts)
139
- if (out.applied > 0) {
140
- d.write(rel, out.text)
141
- fixed.push(rel)
142
- text = out.text
143
- }
144
- }
145
- const fh = checkProse(text, opts)
146
- if (fh.length > 0) byFile.push({ file: rel, hits: fh, ...(o.keepText === true ? { text } : {}) })
147
- hits.push(...fh)
148
- }
149
- const byTier: Record<ProseTier, number> = { fix: 0, hard: 0, soft: 0 }
150
- const byClass: Record<string, number> = {}
151
- for (const h of hits) {
152
- byTier[h.tier]++
153
- byClass[h.cls] = (byClass[h.cls] ?? 0) + 1
154
- }
155
- return {
156
- files: files.length,
157
- hits,
158
- byFile,
159
- byTier,
160
- byClass,
161
- fixable: hits.filter((h) => h.fix != null).length,
162
- fixed,
163
- scoped: (cfg.include ?? []).length > 0,
164
- }
165
- }
166
-
167
- /** one line per hit, capped: a first STE pass on a real repo finds hundreds,
168
- * and a wall of them hides the mechanical ones that `--fix` clears in a
169
- * second. The per-class totals below the cap keep the report complete. */
170
- export function proseLines(r: ProseReport, o: { cap?: number; tiers?: ProseTier[] } = {}): string[] {
171
- const cap = o.cap ?? 25
172
- const tiers = new Set(o.tiers ?? (['fix', 'hard', 'soft'] as ProseTier[]))
173
- const shown = r.hits.filter((h) => tiers.has(h.tier))
174
- const out: string[] = []
175
- const fileOf = new Map<ProseHit, string>()
176
- for (const f of r.byFile) for (const h of f.hits) fileOf.set(h, f.file)
177
- for (const h of shown.slice(0, cap))
178
- out.push(`${fileOf.get(h) ?? '?'}:${h.line} [${h.cls}] ${JSON.stringify(h.text.trim())} — ${h.why}${h.fix != null ? ` (--fix writes ${JSON.stringify(h.fix.trim())})` : ''}`)
179
- if (shown.length > cap) out.push(`… and ${shown.length - cap} more`)
180
- return out
181
- }
182
-
183
- export const classSummary = (r: ProseReport): string =>
184
- Object.entries(r.byClass)
185
- .sort((a, b) => b[1] - a[1])
186
- .map(([c, n]) => `${c} ${n}${tierOf(c as ProseClass) === 'soft' ? '' : '!'}`)
187
- .join(', ')