@withtruing/companion 0.13.2 → 0.14.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 (57) hide show
  1. package/README.md +25 -26
  2. package/dist/anthropic-output-schema.js +41 -0
  3. package/dist/api-key-caller.js +9 -2
  4. package/dist/claude-cli-caller.js +107 -8
  5. package/dist/cli.js +837 -207
  6. package/dist/codex-binary.js +96 -0
  7. package/dist/codex-cli-caller.js +90 -16
  8. package/dist/config.js +177 -2
  9. package/dist/git-range.js +88 -46
  10. package/dist/http-client.js +95 -10
  11. package/dist/investigation-reader.js +12 -6
  12. package/dist/managed-repo.js +132 -0
  13. package/dist/observed-sources.js +132 -6
  14. package/dist/reader-result-contract.js +296 -2
  15. package/dist/repository-provider.js +608 -0
  16. package/dist/schema-entities.js +18 -18
  17. package/dist/service.js +7 -2
  18. package/dist/surface-mapper.js +53 -11
  19. package/dist/text-model.js +91 -8
  20. package/dist/whole-read/archive.js +196 -0
  21. package/dist/whole-read/checks.js +297 -0
  22. package/dist/whole-read/claims.js +196 -0
  23. package/dist/whole-read/coverage-ledger.js +51 -0
  24. package/dist/whole-read/decode.js +234 -0
  25. package/dist/whole-read/drafting.js +502 -0
  26. package/dist/whole-read/generated-rules.js +68 -0
  27. package/dist/whole-read/go-proof.js +255 -0
  28. package/dist/whole-read/grouping-prompt.js +19 -0
  29. package/dist/whole-read/grouping.js +247 -0
  30. package/dist/whole-read/identity.js +103 -0
  31. package/dist/whole-read/import-graph.js +293 -0
  32. package/dist/whole-read/inventory-collector.js +55 -0
  33. package/dist/whole-read/inventory-rules.js +141 -0
  34. package/dist/whole-read/lockfiles.js +947 -0
  35. package/dist/whole-read/notes.js +216 -0
  36. package/dist/whole-read/npm-proof.js +272 -0
  37. package/dist/whole-read/owner-decision-detectors.js +270 -0
  38. package/dist/whole-read/packing.js +612 -0
  39. package/dist/whole-read/pages.js +429 -0
  40. package/dist/whole-read/phase-policy.js +54 -0
  41. package/dist/whole-read/prd-build.js +279 -0
  42. package/dist/whole-read/prd-fidelity.js +14 -0
  43. package/dist/whole-read/python-proof.js +489 -0
  44. package/dist/whole-read/read-prompt.js +67 -0
  45. package/dist/whole-read/redaction.js +411 -0
  46. package/dist/whole-read/relationship-mapping.js +98 -0
  47. package/dist/whole-read/sanitize.js +266 -0
  48. package/dist/whole-read/secret-rules.js +26 -0
  49. package/dist/whole-read/structure.js +300 -0
  50. package/dist/whole-read/types.js +2 -0
  51. package/dist/whole-read-claims.js +12 -0
  52. package/dist/whole-read-git.js +340 -0
  53. package/dist/whole-read-pool.js +80 -0
  54. package/dist/whole-read-runner.js +945 -0
  55. package/dist/whole-read-shared-source.js +194 -0
  56. package/dist/whole-read-temp.js +479 -0
  57. package/package.json +8 -2
@@ -0,0 +1,270 @@
1
+ export const LARGE_DATA_MINIMUM_BYTES = 256 * 1024;
2
+ export const MINIFIED_MINIMUM_BYTES = 32 * 1024;
3
+ const RULES_VERSION = 'inventory-rules@1';
4
+ function parsesJson(text) {
5
+ try {
6
+ JSON.parse(text);
7
+ return true;
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ }
13
+ function parsesNdjson(text, lines) {
14
+ if (lines.length === 0)
15
+ return false;
16
+ return lines.every((line) => {
17
+ const value = text.slice(line.startCharacter, line.contentEndCharacter);
18
+ return value.trim().length > 0 && parsesJson(value);
19
+ });
20
+ }
21
+ function parsesDelimited(text, delimiter) {
22
+ let quoted = false;
23
+ let atFieldStart = true;
24
+ let sawDelimiter = false;
25
+ for (let index = 0; index < text.length; index += 1) {
26
+ const character = text[index];
27
+ if (quoted) {
28
+ if (character === '"') {
29
+ if (text[index + 1] === '"')
30
+ index += 1;
31
+ else
32
+ quoted = false;
33
+ }
34
+ continue;
35
+ }
36
+ if (character === '"') {
37
+ if (!atFieldStart)
38
+ return false;
39
+ quoted = true;
40
+ atFieldStart = false;
41
+ }
42
+ else if (character === delimiter) {
43
+ sawDelimiter = true;
44
+ atFieldStart = true;
45
+ }
46
+ else if (character === '\n') {
47
+ atFieldStart = true;
48
+ }
49
+ else if (character === '\r') {
50
+ if (text[index + 1] !== '\n')
51
+ return false;
52
+ }
53
+ else
54
+ atFieldStart = false;
55
+ }
56
+ return !quoted && sawDelimiter;
57
+ }
58
+ const XML_NAME = /^[A-Za-z_][A-Za-z0-9._:-]*/;
59
+ function parsesXml(text) {
60
+ if (/<!DOCTYPE/i.test(text) || /<!ENTITY/i.test(text))
61
+ return false;
62
+ const tags = [];
63
+ let rootCount = 0;
64
+ let position = 0;
65
+ while (position < text.length) {
66
+ const next = text.indexOf('<', position);
67
+ const content = text.slice(position, next < 0 ? text.length : next);
68
+ if (/[&](?!amp;|lt;|gt;|apos;|quot;|#[0-9]+;|#x[0-9a-fA-F]+;)/.test(content)
69
+ || (tags.length === 0 && content.trim().length > 0))
70
+ return false;
71
+ if (next < 0)
72
+ return rootCount === 1 && tags.length === 0;
73
+ if (text.startsWith('<!--', next)) {
74
+ const end = text.indexOf('-->', next + 4);
75
+ if (end < 0 || text.slice(next + 4, end).includes('--'))
76
+ return false;
77
+ position = end + 3;
78
+ continue;
79
+ }
80
+ if (text.startsWith('<![CDATA[', next)) {
81
+ const end = text.indexOf(']]>', next + 9);
82
+ if (end < 0)
83
+ return false;
84
+ position = end + 3;
85
+ continue;
86
+ }
87
+ if (text.startsWith('<?', next)) {
88
+ const end = text.indexOf('?>', next + 2);
89
+ if (end < 0)
90
+ return false;
91
+ position = end + 2;
92
+ continue;
93
+ }
94
+ const close = text.indexOf('>', next + 1);
95
+ if (close < 0)
96
+ return false;
97
+ const token = text.slice(next + 1, close);
98
+ if (token.startsWith('/')) {
99
+ const name = token.slice(1).trim();
100
+ if (!XML_NAME.test(name) || XML_NAME.exec(name)[0] !== name || tags.pop() !== name)
101
+ return false;
102
+ }
103
+ else {
104
+ const body = token.endsWith('/') ? token.slice(0, -1).trimEnd() : token;
105
+ const match = XML_NAME.exec(body);
106
+ if (match === null)
107
+ return false;
108
+ const name = match[0];
109
+ const attributes = body.slice(name.length);
110
+ if (!/^(?:\s+[A-Za-z_][A-Za-z0-9._:-]*\s*=\s*(?:"[^"<]*"|'[^'<]*'))*\s*$/.test(attributes)
111
+ || /[&](?!amp;|lt;|gt;|apos;|quot;|#[0-9]+;|#x[0-9a-fA-F]+;)/.test(attributes))
112
+ return false;
113
+ if (tags.length === 0 && ++rootCount > 1)
114
+ return false;
115
+ if (!token.endsWith('/'))
116
+ tags.push(name);
117
+ }
118
+ position = close + 1;
119
+ }
120
+ return tags.length === 0;
121
+ }
122
+ function sqlStatements(text) {
123
+ const statements = [];
124
+ let start = 0;
125
+ let quote = null;
126
+ let lineComment = false;
127
+ let blockComment = false;
128
+ for (let index = 0; index < text.length; index += 1) {
129
+ const character = text[index];
130
+ const next = text[index + 1];
131
+ if (lineComment) {
132
+ if (character === '\n')
133
+ lineComment = false;
134
+ continue;
135
+ }
136
+ if (blockComment) {
137
+ if (character === '*' && next === '/') {
138
+ blockComment = false;
139
+ index += 1;
140
+ }
141
+ continue;
142
+ }
143
+ if (quote !== null) {
144
+ if (character === quote) {
145
+ if (next === quote)
146
+ index += 1;
147
+ else
148
+ quote = null;
149
+ }
150
+ continue;
151
+ }
152
+ if (character === '-' && next === '-') {
153
+ lineComment = true;
154
+ index += 1;
155
+ continue;
156
+ }
157
+ if (character === '/' && next === '*') {
158
+ blockComment = true;
159
+ index += 1;
160
+ continue;
161
+ }
162
+ if (character === "'" || character === '"') {
163
+ quote = character;
164
+ continue;
165
+ }
166
+ if (character === ';') {
167
+ statements.push(text.slice(start, index));
168
+ start = index + 1;
169
+ }
170
+ }
171
+ if (quote !== null || blockComment)
172
+ return null;
173
+ statements.push(text.slice(start));
174
+ return statements;
175
+ }
176
+ function stripSqlComments(statement) {
177
+ return statement.replace(/--[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\//g, ' ').trim();
178
+ }
179
+ function parsesDataSql(text) {
180
+ const statements = sqlStatements(text);
181
+ if (statements === null)
182
+ return false;
183
+ let count = 0;
184
+ for (const statement of statements) {
185
+ const source = stripSqlComments(statement);
186
+ if (source.length === 0)
187
+ continue;
188
+ const command = /^([A-Za-z]+)/.exec(source)?.[1]?.toUpperCase();
189
+ if (command === 'INSERT') {
190
+ if (!/^INSERT\s+INTO\s+[^\s(]+(?:\s*\([^)]*\))?\s+VALUES\s*\(/is.test(source))
191
+ return false;
192
+ }
193
+ else if (command === 'COPY') {
194
+ if (!/^COPY\s+.+\s+(?:FROM|TO)\s+(?!PROGRAM\b)\S+/is.test(source))
195
+ return false;
196
+ }
197
+ else
198
+ return false;
199
+ count += 1;
200
+ }
201
+ return count > 0;
202
+ }
203
+ function startsWithSqlCommand(text) {
204
+ const statements = sqlStatements(text);
205
+ if (statements === null)
206
+ return false;
207
+ for (const statement of statements) {
208
+ const command = /^([A-Za-z]+)/.exec(stripSqlComments(statement))?.[1]?.toUpperCase();
209
+ if (command !== undefined)
210
+ return new Set(['ALTER', 'COPY', 'CREATE', 'DELETE', 'DROP', 'INSERT', 'SELECT', 'UPDATE']).has(command);
211
+ }
212
+ return false;
213
+ }
214
+ function dataFormat(text, lines) {
215
+ if (parsesJson(text))
216
+ return 'json';
217
+ if (parsesNdjson(text, lines))
218
+ return 'ndjson';
219
+ if (parsesXml(text))
220
+ return 'xml';
221
+ if (startsWithSqlCommand(text))
222
+ return parsesDataSql(text) ? 'sql' : null;
223
+ if (parsesDelimited(text, ','))
224
+ return 'csv';
225
+ if (parsesDelimited(text, '\t'))
226
+ return 'tsv';
227
+ return null;
228
+ }
229
+ /** Deterministic ISO-8859-1 decoding; this intentionally is not Windows-1252. */
230
+ export function decodeLatin1(bytes) {
231
+ let decoded = '';
232
+ for (const byte of bytes)
233
+ decoded += String.fromCharCode(byte);
234
+ return decoded;
235
+ }
236
+ /** Returns possible owner decisions only. It neither changes disposition nor selects a choice. */
237
+ export function detectOwnerDecisions(input) {
238
+ const canonicalBytes = input.canonicalBytes.byteLength;
239
+ if (input.baseClassification === 'unknown_format')
240
+ return [{
241
+ entryId: input.entryId, kind: 'unknown_format', choices: ['set aside as binary'], rulesVersion: RULES_VERSION,
242
+ detection: { canonicalBytes },
243
+ }];
244
+ if (input.baseClassification === 'unsupported_encoding')
245
+ return [{
246
+ entryId: input.entryId, kind: 'unsupported_encoding', choices: ['read as Latin-1', 'set aside'], rulesVersion: RULES_VERSION,
247
+ detection: { canonicalBytes },
248
+ }];
249
+ if (input.decoded.status !== 'decoded')
250
+ return [];
251
+ const candidates = [];
252
+ if (canonicalBytes >= LARGE_DATA_MINIMUM_BYTES) {
253
+ const format = dataFormat(input.decoded.text, input.decoded.mapping.lines);
254
+ if (format !== null)
255
+ candidates.push({
256
+ entryId: input.entryId, kind: 'large_data', choices: ['read', 'set aside as data'], rulesVersion: RULES_VERSION,
257
+ detection: { canonicalBytes, format },
258
+ });
259
+ }
260
+ if (!input.libraryMatchEstablished && canonicalBytes >= MINIFIED_MINIMUM_BYTES && input.decoded.mapping.lines.length > 0) {
261
+ const contentLength = input.decoded.mapping.lines.reduce((total, line) => total + line.contentEndCharacter - line.startCharacter, 0);
262
+ const meanLineLength = contentLength / input.decoded.mapping.lines.length;
263
+ if (meanLineLength >= 500)
264
+ candidates.push({
265
+ entryId: input.entryId, kind: 'minified', choices: ['read', 'set aside as build output'], rulesVersion: RULES_VERSION,
266
+ detection: { canonicalBytes, meanLineLength, lineCount: input.decoded.mapping.lines.length },
267
+ });
268
+ }
269
+ return candidates;
270
+ }