urtext 0.1.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 (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +229 -0
  3. package/dist/analyze/blast-radius.d.ts +28 -0
  4. package/dist/analyze/blast-radius.js +163 -0
  5. package/dist/analyze/canonical.d.ts +27 -0
  6. package/dist/analyze/canonical.js +74 -0
  7. package/dist/analyze/citations.d.ts +256 -0
  8. package/dist/analyze/citations.js +945 -0
  9. package/dist/analyze/effects.d.ts +15 -0
  10. package/dist/analyze/effects.js +255 -0
  11. package/dist/analyze/fact.d.ts +42 -0
  12. package/dist/analyze/fact.js +46 -0
  13. package/dist/analyze/guards.d.ts +70 -0
  14. package/dist/analyze/guards.js +211 -0
  15. package/dist/analyze/index.d.ts +26 -0
  16. package/dist/analyze/index.js +52 -0
  17. package/dist/analyze/program.d.ts +15 -0
  18. package/dist/analyze/program.js +229 -0
  19. package/dist/analyze/surface.d.ts +48 -0
  20. package/dist/analyze/surface.js +396 -0
  21. package/dist/bin.d.ts +2 -0
  22. package/dist/bin.js +12 -0
  23. package/dist/cli.d.ts +110 -0
  24. package/dist/cli.js +502 -0
  25. package/dist/extract/diff.d.ts +35 -0
  26. package/dist/extract/diff.js +116 -0
  27. package/dist/extract/git.d.ts +12 -0
  28. package/dist/extract/git.js +247 -0
  29. package/dist/extract/index.d.ts +4 -0
  30. package/dist/extract/index.js +57 -0
  31. package/dist/extract/intent.d.ts +64 -0
  32. package/dist/extract/intent.js +238 -0
  33. package/dist/extract/scope.d.ts +160 -0
  34. package/dist/extract/scope.js +284 -0
  35. package/dist/extract/symbols.d.ts +24 -0
  36. package/dist/extract/symbols.js +230 -0
  37. package/dist/interpret/client.d.ts +27 -0
  38. package/dist/interpret/client.js +80 -0
  39. package/dist/interpret/index.d.ts +41 -0
  40. package/dist/interpret/index.js +86 -0
  41. package/dist/interpret/prompt.d.ts +23 -0
  42. package/dist/interpret/prompt.js +128 -0
  43. package/dist/interpret/schema.d.ts +74 -0
  44. package/dist/interpret/schema.js +103 -0
  45. package/dist/report/conceal.d.ts +63 -0
  46. package/dist/report/conceal.js +129 -0
  47. package/dist/report/coverage.d.ts +43 -0
  48. package/dist/report/coverage.js +56 -0
  49. package/dist/report/html.d.ts +4 -0
  50. package/dist/report/html.js +634 -0
  51. package/dist/report/markdown.d.ts +2 -0
  52. package/dist/report/markdown.js +168 -0
  53. package/dist/report/model.d.ts +303 -0
  54. package/dist/report/model.js +289 -0
  55. package/dist/report/pdf.d.ts +2 -0
  56. package/dist/report/pdf.js +217 -0
  57. package/dist/report/terminal.d.ts +2 -0
  58. package/dist/report/terminal.js +206 -0
  59. package/dist/report/write.d.ts +105 -0
  60. package/dist/report/write.js +160 -0
  61. package/dist/score/index.d.ts +94 -0
  62. package/dist/score/index.js +572 -0
  63. package/dist/score/reach.d.ts +126 -0
  64. package/dist/score/reach.js +320 -0
  65. package/dist/score/reconcile.d.ts +52 -0
  66. package/dist/score/reconcile.js +208 -0
  67. package/dist/types.d.ts +221 -0
  68. package/dist/types.js +10 -0
  69. package/fonts/DejaVuSans-Bold.ttf +0 -0
  70. package/fonts/DejaVuSans-Oblique.ttf +0 -0
  71. package/fonts/DejaVuSans.ttf +0 -0
  72. package/fonts/DejaVuSansMono.ttf +0 -0
  73. package/fonts/LICENSE +187 -0
  74. package/package.json +44 -0
@@ -0,0 +1,256 @@
1
+ import { type AnalysisContext, type Analyzer, type Changeset } from "../types.js";
2
+ /**
3
+ * A repository-relative path followed by a line, or a line range. The path
4
+ * must contain at least one directory separator — see
5
+ * CITATION_GUARD_SEPARATOR in the design's false-positive guards for why a
6
+ * bare `Something.js:14` is not treated as a citation at all.
7
+ *
8
+ * Left to right: a lookbehind rejecting a preceding path character, so a
9
+ * match cannot start in the middle of a longer path; one or more `segment/`
10
+ * groups, which is what makes the separator mandatory; a final segment with
11
+ * a dot and an alphanumeric extension; a colon; a line number with no
12
+ * leading zero; optionally a hyphen and an end line; and a lookahead
13
+ * rejecting a trailing digit, letter, underscore, slash, or hyphen, so a
14
+ * citation is not matched inside a longer line number or a longer path.
15
+ * Capture groups: path, start line, end line or undefined.
16
+ *
17
+ * A trailing period is not rejected, so a citation ending a sentence is
18
+ * extracted like any other — see `test/analyze/citations.test.ts`,
19
+ * "extracts a citation a sentence's closing period touches, in both forms".
20
+ */
21
+ export declare const LINE_CITATION: RegExp;
22
+ /**
23
+ * A backticked repository-relative path, then a quoted phrase — urtext's own
24
+ * comment-contract form, and the most checkable citation form in the
25
+ * repository, because the quoted text either appears in the named file or it
26
+ * does not. The same mandatory-separator path, inside backticks; an optional
27
+ * comma, semicolon, or colon; a short run of whitespace; then a straight or
28
+ * left curly double quote, the phrase, and a straight or right curly close.
29
+ * Capture groups: path, phrase.
30
+ */
31
+ export declare const QUOTED_CITATION: RegExp;
32
+ /**
33
+ * The most code points a quoted phrase, or a stored was/now line, carries.
34
+ * Longer than this is a block quotation rather than a pointer.
35
+ */
36
+ export declare const MAX_QUOTE_CHARS = 240;
37
+ /** Appended to a was/now line the cap cut, so no line merely appears to end. */
38
+ export declare const CITATION_TRUNCATION_MARKER = "\u2026 [line truncated]";
39
+ /** Tracked files scanned as raw text, after masking. */
40
+ export declare const PROSE_EXTENSIONS: readonly [".md", ".markdown", ".txt"];
41
+ /**
42
+ * The pathspecs the candidate-file queries in this file's scan half pass to
43
+ * git. Narrower than `isTypeScriptFile` accepts — it also takes the
44
+ * module-explicit extensions, which no pathspec here names — so a citation
45
+ * written in one of those files is not checked at all. An under-report, the
46
+ * direction every approximation in this feature leans.
47
+ */
48
+ export declare const CITATION_PATHSPECS: readonly ["*.md", "*.markdown", "*.txt", "*.ts", "*.tsx"];
49
+ export type CitationForm = "line" | "quote";
50
+ export interface Citation {
51
+ form: CitationForm;
52
+ /** The path exactly as the prose spelled it, before any resolution. */
53
+ path: string;
54
+ /** Form A only: the cited start line, and the end line for a range. */
55
+ line?: number;
56
+ endLine?: number;
57
+ /** Form B only: the quoted phrase, already normalized. */
58
+ quote?: string;
59
+ /** One-based line in the citing file where `path` sits. */
60
+ citingLine: number;
61
+ /** That line's text, trimmed, for the fact's anchor evidence. */
62
+ citingText: string;
63
+ }
64
+ /**
65
+ * Every whitespace run, newlines included, collapsed to a single space, then
66
+ * trimmed. Matching and comparison both run on normalized text, and the
67
+ * cited file is normalized identically before a containment test. Without
68
+ * this, every comment-contract citation in `src/` would fail: they wrap
69
+ * across continuation lines, so the phrase as written carries newlines and
70
+ * asterisks the cited file never had.
71
+ */
72
+ export declare function normalizeText(text: string): string;
73
+ /**
74
+ * CITATION_GUARD_FENCE. Spans from a line opening a fence through the next
75
+ * line closing that fence — same character, at least as long a run —
76
+ * inclusive, are blanked. A path and line inside a fence is sample output,
77
+ * and treating sample output as an assertion about the repository is the
78
+ * most common false positive available. An unclosed fence blanks to the end
79
+ * of the text: silence about a run of prose costs less than a finding built
80
+ * on a code block nobody closed. Indented blocks are deliberately not
81
+ * masked — they are indistinguishable from list continuations in this
82
+ * repository's prose, and the baseline gate already covers the illustrative
83
+ * ones. A blockquote prefix on the fence line is tolerated: a fence inside a
84
+ * quotation is still a fence, and the indent allowance alone cannot see past
85
+ * the marker. See `test/analyze/citations.test.ts`, "CITATION_GUARD_FENCE: a
86
+ * citation inside a fenced block is not one, and the same text outside it
87
+ * is" and "CITATION_GUARD_FENCE: a fenced block inside a blockquote is masked
88
+ * like any other".
89
+ */
90
+ export declare function maskFences(text: string): string;
91
+ /**
92
+ * CITATION_GUARD_URL. A link to another host is a link to another host, and
93
+ * its path-and-line tail says nothing about this repository. The regexes'
94
+ * lookbehind is a second line of defense, not a substitute: masking is what
95
+ * makes the intent explicit and testable.
96
+ */
97
+ export declare function maskUrls(text: string): string;
98
+ export declare function isProseFile(path: string): boolean;
99
+ export declare function citationsInProse(text: string): Citation[];
100
+ /**
101
+ * Comments only. A path and line inside a string literal is usually a test
102
+ * fixture's expected output, and inside code it is not prose making a claim.
103
+ * Fences are not masked here — a fence is a prose construct — but URLs are,
104
+ * for the same reason they are in prose.
105
+ */
106
+ export declare function citationsInComments(source: string, fileName: string): Citation[];
107
+ /** Every citation in one file, scanned the way that file's kind is scanned. */
108
+ export declare function citationsIn(path: string, text: string): Citation[];
109
+ /**
110
+ * The most citations one run checks, across both modes. Bounds a sweep of a
111
+ * repository whose prose cites code everywhere; the default mode is already
112
+ * bounded by the change and reaches this only on a very large diff. A cap
113
+ * that bites is disclosed — see `citationsCappedNote`.
114
+ */
115
+ export declare const MAX_CITATIONS_CHECKED = 2000;
116
+ /**
117
+ * The most citing files one run opens. Bounds the blame calls, which are the
118
+ * expensive per-file work; the citation cap above bounds the per-citation
119
+ * work. Files are taken in path order so a capped run is deterministic.
120
+ */
121
+ export declare const MAX_CITING_FILES = 320;
122
+ /**
123
+ * The most distinct (revision, cited file) pairs one run reads historically.
124
+ * A repository whose prose cites a hundred files from a hundred different
125
+ * baseline commits would otherwise pay a `git show` per pair with no bound
126
+ * at all; every citation past this is checked existence-only and said so.
127
+ */
128
+ export declare const MAX_BASELINE_READS = 480;
129
+ /**
130
+ * The most basenames passed to one `git grep` invocation. Terms are chunked
131
+ * at this width and the results unioned, so unlike the caps above this one
132
+ * loses nothing and discloses nothing.
133
+ */
134
+ export declare const MAX_GREP_TERMS = 96;
135
+ /**
136
+ * Pluralized inline in the style `review` in `../cli.ts` already uses, and
137
+ * phrased as reasons so they read alongside the existing warnings. They land
138
+ * in `warnings`, which becomes `ReportModel.notes`, which trips the "This
139
+ * review is partial." banner — correctly. A capped run genuinely did not
140
+ * check everything it was asked to.
141
+ */
142
+ export declare function citingFilesCappedNote(scanned: number, found: number): string;
143
+ export declare function citationsCappedNote(checked: number, found: number): string;
144
+ /**
145
+ * No order clause, unlike the two caps above, and the difference is in what
146
+ * this sentence claims rather than in how its budget is spent. The budget is
147
+ * spent front to back like theirs — a citation is refused a historical read
148
+ * only once the distinct-pair allowance is gone, so the ones that degrade are
149
+ * the later ones in the same path order, minus any whose baseline pair had
150
+ * already been read. But this note states no fraction and no share of the
151
+ * repository: every citation it counts was checked, and the sentence says
152
+ * exactly how far that check went. There is no coverage claim here for an
153
+ * order to qualify, so the clause the caps carry would attach to a sentence
154
+ * that never said it had covered anything.
155
+ */
156
+ export declare function baselineReadsCappedNote(unchecked: number): string;
157
+ /**
158
+ * Copy for a shallow repository, where blame answers but cannot be believed.
159
+ * Phrased as a skip rather than a partial check, because that is what it is.
160
+ */
161
+ export declare function shallowRepositoryNote(): string;
162
+ /** Copy for citations whose history could not be read. */
163
+ export declare function blameUnavailableNote(count: number, reason: string): string;
164
+ export type RotKind = "missing_file" | "line_out_of_range" | "quote_absent" | "content_drift";
165
+ export interface CitationRot {
166
+ rot: RotKind;
167
+ citingFile: string;
168
+ citingLine: number;
169
+ citingText: string;
170
+ /** The resolved cited path, not the path as written. */
171
+ citedFile: string;
172
+ citedLine?: number;
173
+ /**
174
+ * The citation's end line, for a range. Absent on `content_drift`, whose
175
+ * `citedLine` is the one line that actually differs rather than the
176
+ * range's start: pairing this end with that start would print a span the
177
+ * prose never wrote. What the prose did write is carried whole in
178
+ * `writtenLine`/`writtenEndLine` below.
179
+ */
180
+ citedEndLine?: number;
181
+ /**
182
+ * `content_drift` only: the citation exactly as the prose wrote it. Carried
183
+ * apart from `citedLine`, which on a drift names the one line whose content
184
+ * differs, because a drift finding has to say both things and must never
185
+ * mix them. The title and body name this pair — it is the string a reader
186
+ * searches their own document for, and the text they will edit — while the
187
+ * evidence points at the line that actually moved. Separate fields, so no
188
+ * sentence can compose a span out of one number from each.
189
+ *
190
+ * On the other three rot kinds `citedLine`/`citedEndLine` already are the
191
+ * citation as written, and these stay absent.
192
+ */
193
+ writtenLine?: number;
194
+ writtenEndLine?: number;
195
+ quote?: string;
196
+ /** content_drift only: the first differing line, both sides, truncated. */
197
+ was?: string;
198
+ now?: string;
199
+ /** Abbreviated baseline commit. Absent means history could not be read. */
200
+ baseline?: string;
201
+ /** line_out_of_range only: the cited file's line count at this revision. */
202
+ lineCount?: number;
203
+ /** The cited line's current text, when the file and line exist now. */
204
+ citedText?: string;
205
+ /** True when the reviewed range touched `citedFile`. Proven, not inferred. */
206
+ citedTouched: boolean;
207
+ }
208
+ export interface CitationScanOptions {
209
+ /**
210
+ * Check every citation in the repository rather than only those pointing
211
+ * into files the reviewed range touched. Set by `--citations`.
212
+ */
213
+ sweep?: boolean;
214
+ /**
215
+ * Called once per cap that bit and once for unreadable history, with the
216
+ * sentence the user is owed. An analyzer returns facts and nothing else, so
217
+ * this is the only channel a disclosure has; `review` in `../cli.ts` passes
218
+ * one that pushes into `warnings`. A caller that passes none gets the facts
219
+ * and no indication anything was skipped — the same contract, and the same
220
+ * hazard, as `runAnalyzers`'s `onFailure`.
221
+ */
222
+ onNote?: (note: string) => void;
223
+ }
224
+ /**
225
+ * The name the design gives this option bag where the analyzer takes it, and
226
+ * an alias rather than a second declaration on purpose: one field list cannot
227
+ * drift against itself, and the scan half of this module already documents
228
+ * every field above.
229
+ */
230
+ export type CitationsOptions = CitationScanOptions;
231
+ export declare function parseBlame(out: string): Map<number, string>;
232
+ export declare function findCitationRot(changeset: Changeset, ctx: AnalysisContext, options?: CitationScanOptions): Promise<CitationRot[]>;
233
+ /**
234
+ * Returns its analyzer under a name it states outright, and that is
235
+ * load-bearing: `runAnalyzers` reports a failed analyzer by
236
+ * `analyzers[i].name` in a warning a user reads, and an arrow returned
237
+ * directly from a factory has no name at all, so a citation analyzer that
238
+ * threw would be disclosed as a numbered anonymous one.
239
+ *
240
+ * The other analyzers get their names for free, from NamedEvaluation of the
241
+ * variable declaration they are assigned to. That mechanism is not enough
242
+ * here. This binding sits one scope in and shadows the module-level
243
+ * singleton below, and a transform that renames shadowed symbols to keep
244
+ * every binding unique — esbuild, which is what runs this repository's tests
245
+ * — rewrites the binding, and the inferred name goes with it, turning the
246
+ * disclosed name into a near-miss of itself. So the name is written down
247
+ * rather than derived. See `test/analyze/citations-rot.test.ts`, "names
248
+ * itself when it throws, so the disclosure never says analyzer #N".
249
+ */
250
+ export declare function makeCitationsAnalyzer(options?: CitationsOptions): Analyzer;
251
+ /**
252
+ * The default-mode instance, and the member of ANALYZERS. Also the identity
253
+ * `review` matches on when it swaps in a configured instance, so it must stay
254
+ * a single shared value rather than being reconstructed per call.
255
+ */
256
+ export declare const citationsAnalyzer: Analyzer;