tryassay 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 (93) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +553 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +80 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/assess.d.ts +6 -0
  7. package/dist/commands/assess.js +267 -0
  8. package/dist/commands/assess.js.map +1 -0
  9. package/dist/commands/describe.d.ts +3 -0
  10. package/dist/commands/describe.js +114 -0
  11. package/dist/commands/describe.js.map +1 -0
  12. package/dist/commands/extract.d.ts +4 -0
  13. package/dist/commands/extract.js +144 -0
  14. package/dist/commands/extract.js.map +1 -0
  15. package/dist/commands/hallucinate.d.ts +3 -0
  16. package/dist/commands/hallucinate.js +100 -0
  17. package/dist/commands/hallucinate.js.map +1 -0
  18. package/dist/commands/init.d.ts +1 -0
  19. package/dist/commands/init.js +39 -0
  20. package/dist/commands/init.js.map +1 -0
  21. package/dist/commands/regenerate.d.ts +3 -0
  22. package/dist/commands/regenerate.js +158 -0
  23. package/dist/commands/regenerate.js.map +1 -0
  24. package/dist/commands/remediate.d.ts +5 -0
  25. package/dist/commands/remediate.js +155 -0
  26. package/dist/commands/remediate.js.map +1 -0
  27. package/dist/commands/report.d.ts +3 -0
  28. package/dist/commands/report.js +84 -0
  29. package/dist/commands/report.js.map +1 -0
  30. package/dist/commands/reverse.d.ts +9 -0
  31. package/dist/commands/reverse.js +115 -0
  32. package/dist/commands/reverse.js.map +1 -0
  33. package/dist/commands/verify.d.ts +4 -0
  34. package/dist/commands/verify.js +112 -0
  35. package/dist/commands/verify.js.map +1 -0
  36. package/dist/lib/anthropic.d.ts +13 -0
  37. package/dist/lib/anthropic.js +60 -0
  38. package/dist/lib/anthropic.js.map +1 -0
  39. package/dist/lib/assessment-reporter.d.ts +5 -0
  40. package/dist/lib/assessment-reporter.js +266 -0
  41. package/dist/lib/assessment-reporter.js.map +1 -0
  42. package/dist/lib/claim-extractor.d.ts +6 -0
  43. package/dist/lib/claim-extractor.js +138 -0
  44. package/dist/lib/claim-extractor.js.map +1 -0
  45. package/dist/lib/code-verifier.d.ts +7 -0
  46. package/dist/lib/code-verifier.js +265 -0
  47. package/dist/lib/code-verifier.js.map +1 -0
  48. package/dist/lib/codebase-indexer.d.ts +15 -0
  49. package/dist/lib/codebase-indexer.js +156 -0
  50. package/dist/lib/codebase-indexer.js.map +1 -0
  51. package/dist/lib/config.d.ts +7 -0
  52. package/dist/lib/config.js +38 -0
  53. package/dist/lib/config.js.map +1 -0
  54. package/dist/lib/constraint-engine.d.ts +2 -0
  55. package/dist/lib/constraint-engine.js +337 -0
  56. package/dist/lib/constraint-engine.js.map +1 -0
  57. package/dist/lib/fs-utils.d.ts +1 -0
  58. package/dist/lib/fs-utils.js +11 -0
  59. package/dist/lib/fs-utils.js.map +1 -0
  60. package/dist/lib/guided-generator.d.ts +2 -0
  61. package/dist/lib/guided-generator.js +195 -0
  62. package/dist/lib/guided-generator.js.map +1 -0
  63. package/dist/lib/inventory-extractor.d.ts +7 -0
  64. package/dist/lib/inventory-extractor.js +238 -0
  65. package/dist/lib/inventory-extractor.js.map +1 -0
  66. package/dist/lib/prompts.d.ts +3 -0
  67. package/dist/lib/prompts.js +50 -0
  68. package/dist/lib/prompts.js.map +1 -0
  69. package/dist/lib/publisher.d.ts +2 -0
  70. package/dist/lib/publisher.js +71 -0
  71. package/dist/lib/publisher.js.map +1 -0
  72. package/dist/lib/remediation-generator.d.ts +2 -0
  73. package/dist/lib/remediation-generator.js +136 -0
  74. package/dist/lib/remediation-generator.js.map +1 -0
  75. package/dist/lib/remediator.d.ts +7 -0
  76. package/dist/lib/remediator.js +209 -0
  77. package/dist/lib/remediator.js.map +1 -0
  78. package/dist/lib/report-generator.d.ts +8 -0
  79. package/dist/lib/report-generator.js +190 -0
  80. package/dist/lib/report-generator.js.map +1 -0
  81. package/dist/lib/requirements-generator.d.ts +14 -0
  82. package/dist/lib/requirements-generator.js +311 -0
  83. package/dist/lib/requirements-generator.js.map +1 -0
  84. package/dist/lib/spec-synthesizer.d.ts +2 -0
  85. package/dist/lib/spec-synthesizer.js +136 -0
  86. package/dist/lib/spec-synthesizer.js.map +1 -0
  87. package/dist/lib/system-prompts.d.ts +12 -0
  88. package/dist/lib/system-prompts.js +254 -0
  89. package/dist/lib/system-prompts.js.map +1 -0
  90. package/dist/types.d.ts +243 -0
  91. package/dist/types.js +2 -0
  92. package/dist/types.js.map +1 -0
  93. package/package.json +49 -0
@@ -0,0 +1,138 @@
1
+ import { getClient, MODEL } from './anthropic.js';
2
+ const EXTRACTION_SYSTEM_PROMPT = `You are a legal and technical claim extractor. Your job is to read a document (Terms of Service, Privacy Policy, API documentation, or user manual) and extract every declarative promise, commitment, or factual claim the document makes.
3
+
4
+ EXTRACTION RULES:
5
+
6
+ 1. A "claim" is any statement that asserts something IS true, WILL happen, or IS provided.
7
+ - GOOD claim: "User data is encrypted at rest using AES-256"
8
+ - GOOD claim: "The API rate limit is 1,000 requests per minute"
9
+ - NOT a claim: "Users should review these terms periodically" (advice, not a promise)
10
+
11
+ 2. Split compound claims into individual claims.
12
+ - "Data is encrypted at rest and in transit" → TWO claims: encryption at rest, encryption in transit
13
+
14
+ 3. Mark each claim as testable or non-testable:
15
+ - Testable: Can be verified by examining code, config, infrastructure, or behavior
16
+ - Non-testable: Legal boilerplate, jurisdictional claims, subjective statements
17
+
18
+ 4. Assign a category:
19
+ - data-privacy: Data collection, storage, sharing, retention, deletion, consent
20
+ - security: Encryption, authentication, access control, vulnerability management
21
+ - functionality: Features, capabilities, performance, limits, SLAs
22
+ - operational: Support, availability, maintenance, backup, disaster recovery
23
+ - legal: Liability, indemnification, dispute resolution, IP, terms changes
24
+
25
+ 5. Assign severity based on impact if the claim is false:
26
+ - critical: Data breach, security failure, regulatory violation, data loss
27
+ - high: Feature completely missing, SLA violation, payment issues
28
+ - medium: Feature partially working, minor data handling issue
29
+ - low: Documentation inaccuracy, cosmetic, non-functional
30
+
31
+ 6. Include the section heading where the claim appears.
32
+
33
+ OUTPUT FORMAT:
34
+ Return a JSON array of claims. Each claim object:
35
+ {
36
+ "id": "CLAIM-001",
37
+ "section": "Section heading where claim appears",
38
+ "category": "data-privacy|security|functionality|operational|legal",
39
+ "severity": "critical|high|medium|low",
40
+ "text": "The exact claim, rephrased as a testable assertion if needed",
41
+ "testable": true|false
42
+ }
43
+
44
+ Number claims sequentially: CLAIM-001, CLAIM-002, etc.
45
+ Return ONLY the JSON array. No markdown fences, no commentary.`;
46
+ const VALID_CATEGORIES = [
47
+ 'data-privacy', 'security', 'functionality', 'operational', 'legal',
48
+ ];
49
+ const VALID_SEVERITIES = [
50
+ 'critical', 'high', 'medium', 'low',
51
+ ];
52
+ function validateClaim(raw, index) {
53
+ if (!raw.text || typeof raw.text !== 'string')
54
+ return null;
55
+ const category = VALID_CATEGORIES.includes(raw.category)
56
+ ? raw.category
57
+ : 'functionality';
58
+ const severity = VALID_SEVERITIES.includes(raw.severity)
59
+ ? raw.severity
60
+ : 'medium';
61
+ return {
62
+ id: raw.id || `CLAIM-${String(index + 1).padStart(3, '0')}`,
63
+ section: raw.section || 'Unknown',
64
+ category,
65
+ severity,
66
+ text: raw.text.trim(),
67
+ testable: typeof raw.testable === 'boolean' ? raw.testable : true,
68
+ };
69
+ }
70
+ export async function extractClaims(document, onProgress) {
71
+ const client = getClient();
72
+ onProgress?.('Sending document to Claude for claim extraction (streaming)...');
73
+ const stream = client.messages.stream({
74
+ model: MODEL,
75
+ max_tokens: 32_000,
76
+ system: EXTRACTION_SYSTEM_PROMPT,
77
+ messages: [
78
+ {
79
+ role: 'user',
80
+ content: `Extract all claims from this document:\n\n${document}`,
81
+ },
82
+ ],
83
+ });
84
+ let rawText = '';
85
+ stream.on('text', (text) => {
86
+ rawText += text;
87
+ });
88
+ const finalMessage = await stream.finalMessage();
89
+ onProgress?.('Parsing extraction results...');
90
+ // Strip markdown code fences if present, then find the JSON array
91
+ let jsonText = rawText.trim();
92
+ // Remove opening code fence
93
+ if (jsonText.startsWith('```')) {
94
+ const firstNewline = jsonText.indexOf('\n');
95
+ if (firstNewline !== -1) {
96
+ jsonText = jsonText.slice(firstNewline + 1);
97
+ }
98
+ }
99
+ // Remove closing code fence
100
+ const lastFence = jsonText.lastIndexOf('```');
101
+ if (lastFence !== -1) {
102
+ jsonText = jsonText.slice(0, lastFence);
103
+ }
104
+ jsonText = jsonText.trim();
105
+ // If response was truncated (no closing ]), try to repair by closing the array
106
+ if (!jsonText.endsWith(']')) {
107
+ // Find the last complete object (ends with })
108
+ const lastCloseBrace = jsonText.lastIndexOf('}');
109
+ if (lastCloseBrace !== -1) {
110
+ jsonText = jsonText.slice(0, lastCloseBrace + 1) + '\n]';
111
+ onProgress?.('Note: Response was truncated. Recovered partial claims.');
112
+ }
113
+ }
114
+ let parsed;
115
+ try {
116
+ parsed = JSON.parse(jsonText);
117
+ }
118
+ catch {
119
+ throw new Error(`Failed to parse Claude's response as JSON.\n` +
120
+ `First 200 chars: ${jsonText.slice(0, 200)}`);
121
+ }
122
+ if (!Array.isArray(parsed)) {
123
+ throw new Error('Expected a JSON array of claims from Claude.');
124
+ }
125
+ const claims = [];
126
+ for (let i = 0; i < parsed.length; i++) {
127
+ const validated = validateClaim(parsed[i], i);
128
+ if (validated) {
129
+ claims.push(validated);
130
+ }
131
+ }
132
+ return {
133
+ claims,
134
+ inputTokens: finalMessage.usage.input_tokens,
135
+ outputTokens: finalMessage.usage.output_tokens,
136
+ };
137
+ }
138
+ //# sourceMappingURL=claim-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claim-extractor.js","sourceRoot":"","sources":["../../src/lib/claim-extractor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { CodebaseIndex } from './codebase-indexer.js';
2
+ import type { Claim, ClaimVerification } from '../types.js';
3
+ export declare function verifyClaims(claims: Claim[], index: CodebaseIndex, onProgress?: (msg: string) => void): Promise<{
4
+ verifications: ClaimVerification[];
5
+ inputTokens: number;
6
+ outputTokens: number;
7
+ }>;
@@ -0,0 +1,265 @@
1
+ import { getClient, MODEL } from './anthropic.js';
2
+ import { readFileContent } from './codebase-indexer.js';
3
+ const VALID_VERDICTS = ['PASS', 'PARTIAL', 'FAIL', 'N/A'];
4
+ // Step 1: Ask Claude which files to examine for a batch of claims
5
+ const FILE_SELECTION_SYSTEM = `You are a code auditor. Given a list of claims and a codebase file tree, identify which files are most likely to contain evidence for or against each claim.
6
+
7
+ For each claim, list 1-5 file paths from the tree that should be examined.
8
+
9
+ OUTPUT FORMAT:
10
+ Return a JSON object mapping claim IDs to arrays of file paths:
11
+ {
12
+ "CLAIM-001": ["src/lib/auth.ts", "src/middleware.ts"],
13
+ "CLAIM-002": ["prisma/schema.prisma", "src/api/users/route.ts"]
14
+ }
15
+
16
+ Return ONLY the JSON. No markdown fences, no commentary.
17
+ Be specific — pick the most relevant files. If no files in the tree could contain evidence, use an empty array.`;
18
+ // Step 2: Ask Claude to evaluate a claim against file contents
19
+ const VERIFICATION_SYSTEM = `You are a compliance auditor verifying whether code implements what a legal document claims.
20
+
21
+ For each claim, evaluate the code evidence and assign a verdict:
22
+ - PASS: The code fully implements what the claim states
23
+ - PARTIAL: The code partially implements it (some aspects missing or incomplete)
24
+ - FAIL: The code does not implement what the claim states, or contradicts it
25
+ - N/A: The claim cannot be verified from code (e.g., business process, legal statement)
26
+
27
+ OUTPUT FORMAT:
28
+ Return a JSON array of verification results:
29
+ [
30
+ {
31
+ "claimId": "CLAIM-001",
32
+ "verdict": "PASS",
33
+ "evidence": [
34
+ {
35
+ "file": "src/lib/encryption.ts",
36
+ "lineNumber": 42,
37
+ "snippet": "const cipher = createCipheriv('aes-256-gcm', key, iv)",
38
+ "confidence": 0.95
39
+ }
40
+ ],
41
+ "reasoning": "AES-256-GCM encryption is implemented for data at rest in the encryption module."
42
+ }
43
+ ]
44
+
45
+ Rules:
46
+ - Evidence snippets should be short (1-3 lines), directly relevant
47
+ - Confidence: 0.0 to 1.0, how sure you are this evidence supports the verdict
48
+ - Reasoning: 1-2 sentences explaining why this verdict
49
+ - Be strict: if the claim says "AES-256" and code uses "AES-128", that's FAIL
50
+ - N/A is for claims that genuinely can't be verified from code
51
+
52
+ Return ONLY the JSON array. No markdown fences.`;
53
+ function parseVerificationResponse(rawText) {
54
+ let jsonText = rawText.trim();
55
+ // Remove code fences
56
+ if (jsonText.startsWith('```')) {
57
+ const firstNewline = jsonText.indexOf('\n');
58
+ if (firstNewline !== -1) {
59
+ jsonText = jsonText.slice(firstNewline + 1);
60
+ }
61
+ }
62
+ const lastFence = jsonText.lastIndexOf('```');
63
+ if (lastFence !== -1) {
64
+ jsonText = jsonText.slice(0, lastFence);
65
+ }
66
+ jsonText = jsonText.trim();
67
+ // Handle truncation
68
+ if (!jsonText.endsWith(']')) {
69
+ const lastBrace = jsonText.lastIndexOf('}');
70
+ if (lastBrace !== -1) {
71
+ jsonText = jsonText.slice(0, lastBrace + 1) + ']';
72
+ }
73
+ }
74
+ const parsed = JSON.parse(jsonText);
75
+ if (!Array.isArray(parsed))
76
+ return [];
77
+ return parsed
78
+ .filter((r) => r.claimId && r.verdict)
79
+ .map((r) => ({
80
+ claimId: r.claimId,
81
+ claim: '',
82
+ verdict: VALID_VERDICTS.includes(r.verdict)
83
+ ? r.verdict
84
+ : 'N/A',
85
+ evidence: (r.evidence || [])
86
+ .filter((e) => e.file && e.snippet)
87
+ .map((e) => ({
88
+ file: e.file,
89
+ lineNumber: e.lineNumber,
90
+ snippet: e.snippet,
91
+ confidence: typeof e.confidence === 'number' ? e.confidence : 0.5,
92
+ })),
93
+ reasoning: r.reasoning || '',
94
+ }));
95
+ }
96
+ function parseFileSelectionResponse(rawText) {
97
+ let jsonText = rawText.trim();
98
+ if (jsonText.startsWith('```')) {
99
+ const firstNewline = jsonText.indexOf('\n');
100
+ if (firstNewline !== -1)
101
+ jsonText = jsonText.slice(firstNewline + 1);
102
+ }
103
+ const lastFence = jsonText.lastIndexOf('```');
104
+ if (lastFence !== -1)
105
+ jsonText = jsonText.slice(0, lastFence);
106
+ jsonText = jsonText.trim();
107
+ // Handle truncation for objects
108
+ if (!jsonText.endsWith('}')) {
109
+ const lastBrace = jsonText.lastIndexOf('}');
110
+ if (lastBrace !== -1)
111
+ jsonText = jsonText.slice(0, lastBrace + 1);
112
+ }
113
+ return JSON.parse(jsonText);
114
+ }
115
+ // Process claims in batches to manage API calls and context
116
+ const BATCH_SIZE = 15;
117
+ export async function verifyClaims(claims, index, onProgress) {
118
+ const client = getClient();
119
+ const testableClaims = claims.filter((c) => c.testable);
120
+ const verifications = [];
121
+ let totalInputTokens = 0;
122
+ let totalOutputTokens = 0;
123
+ // Mark non-testable claims as N/A immediately
124
+ for (const claim of claims) {
125
+ if (!claim.testable) {
126
+ verifications.push({
127
+ claimId: claim.id,
128
+ claim: claim.text,
129
+ verdict: 'N/A',
130
+ evidence: [],
131
+ reasoning: 'Claim is not testable against code.',
132
+ });
133
+ }
134
+ }
135
+ // Build file tree string (truncated to avoid hitting context limits)
136
+ const treeStr = index.fileTree.slice(0, 2000).join('\n');
137
+ // Process testable claims in batches
138
+ for (let i = 0; i < testableClaims.length; i += BATCH_SIZE) {
139
+ const batch = testableClaims.slice(i, i + BATCH_SIZE);
140
+ const batchNum = Math.floor(i / BATCH_SIZE) + 1;
141
+ const totalBatches = Math.ceil(testableClaims.length / BATCH_SIZE);
142
+ onProgress?.(`Batch ${batchNum}/${totalBatches}: selecting files for ${batch.length} claims...`);
143
+ // Step 1: Ask which files to examine
144
+ const claimList = batch
145
+ .map((c) => `${c.id}: ${c.text} [${c.category}]`)
146
+ .join('\n');
147
+ const fileSelResponse = await client.messages.create({
148
+ model: MODEL,
149
+ max_tokens: 4_000,
150
+ system: FILE_SELECTION_SYSTEM,
151
+ messages: [
152
+ {
153
+ role: 'user',
154
+ content: `Claims to verify:\n${claimList}\n\nCodebase file tree:\n${treeStr}`,
155
+ },
156
+ ],
157
+ });
158
+ totalInputTokens += fileSelResponse.usage.input_tokens;
159
+ totalOutputTokens += fileSelResponse.usage.output_tokens;
160
+ const fileSelText = fileSelResponse.content
161
+ .filter((b) => b.type === 'text')
162
+ .map((b) => b.text)
163
+ .join('');
164
+ let fileSelections;
165
+ try {
166
+ fileSelections = parseFileSelectionResponse(fileSelText);
167
+ }
168
+ catch {
169
+ // If parsing fails, use key files as fallback
170
+ fileSelections = {};
171
+ for (const claim of batch) {
172
+ fileSelections[claim.id] = index.keyFiles.slice(0, 5).map((f) => f.path);
173
+ }
174
+ }
175
+ // Step 2: Read all unique files needed for this batch
176
+ const uniqueFiles = new Set();
177
+ for (const files of Object.values(fileSelections)) {
178
+ for (const f of files)
179
+ uniqueFiles.add(f);
180
+ }
181
+ onProgress?.(`Batch ${batchNum}/${totalBatches}: reading ${uniqueFiles.size} files...`);
182
+ const fileContents = new Map();
183
+ for (const filePath of uniqueFiles) {
184
+ const content = await readFileContent(index.rootPath, filePath);
185
+ if (content) {
186
+ fileContents.set(filePath, content);
187
+ }
188
+ }
189
+ // Step 3: Verify claims against file contents
190
+ onProgress?.(`Batch ${batchNum}/${totalBatches}: verifying ${batch.length} claims...`);
191
+ // Build context with file contents for this batch
192
+ let evidenceContext = '';
193
+ for (const [path, content] of fileContents) {
194
+ // Limit each file to keep total context manageable
195
+ const truncated = content.length > 10_000
196
+ ? content.slice(0, 10_000) + '\n[... truncated]'
197
+ : content;
198
+ evidenceContext += `\n--- FILE: ${path} ---\n${truncated}\n`;
199
+ }
200
+ // If total context is too large, truncate
201
+ if (evidenceContext.length > 100_000) {
202
+ evidenceContext = evidenceContext.slice(0, 100_000) + '\n[... context truncated]';
203
+ }
204
+ const verifyResponse = await client.messages.create({
205
+ model: MODEL,
206
+ max_tokens: 8_000,
207
+ system: VERIFICATION_SYSTEM,
208
+ messages: [
209
+ {
210
+ role: 'user',
211
+ content: `Claims to verify:\n${claimList}\n\nCode evidence:\n${evidenceContext}`,
212
+ },
213
+ ],
214
+ });
215
+ totalInputTokens += verifyResponse.usage.input_tokens;
216
+ totalOutputTokens += verifyResponse.usage.output_tokens;
217
+ const verifyText = verifyResponse.content
218
+ .filter((b) => b.type === 'text')
219
+ .map((b) => b.text)
220
+ .join('');
221
+ try {
222
+ const batchResults = parseVerificationResponse(verifyText);
223
+ // Attach claim text
224
+ for (const result of batchResults) {
225
+ const claim = batch.find((c) => c.id === result.claimId);
226
+ if (claim) {
227
+ result.claim = claim.text;
228
+ verifications.push(result);
229
+ }
230
+ }
231
+ // Any claims not covered in response get N/A
232
+ for (const claim of batch) {
233
+ if (!batchResults.some((r) => r.claimId === claim.id)) {
234
+ verifications.push({
235
+ claimId: claim.id,
236
+ claim: claim.text,
237
+ verdict: 'N/A',
238
+ evidence: [],
239
+ reasoning: 'No verification result returned.',
240
+ });
241
+ }
242
+ }
243
+ }
244
+ catch {
245
+ // If verification parsing fails, mark batch as N/A
246
+ for (const claim of batch) {
247
+ verifications.push({
248
+ claimId: claim.id,
249
+ claim: claim.text,
250
+ verdict: 'N/A',
251
+ evidence: [],
252
+ reasoning: 'Failed to parse verification response.',
253
+ });
254
+ }
255
+ }
256
+ }
257
+ // Sort by claim ID
258
+ verifications.sort((a, b) => a.claimId.localeCompare(b.claimId));
259
+ return {
260
+ verifications,
261
+ inputTokens: totalInputTokens,
262
+ outputTokens: totalOutputTokens,
263
+ };
264
+ }
265
+ //# sourceMappingURL=code-verifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-verifier.js","sourceRoot":"","sources":["../../src/lib/code-verifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export interface CodebaseIndex {
2
+ rootPath: string;
3
+ totalFiles: number;
4
+ fileTree: string[];
5
+ frameworks: string[];
6
+ keyFiles: KeyFile[];
7
+ summary: string;
8
+ }
9
+ interface KeyFile {
10
+ path: string;
11
+ reason: string;
12
+ }
13
+ export declare function indexCodebase(rootPath: string, maxFiles?: number): Promise<CodebaseIndex>;
14
+ export declare function readFileContent(rootPath: string, relativePath: string): Promise<string | null>;
15
+ export {};
@@ -0,0 +1,156 @@
1
+ import { readdir, readFile, stat } from 'node:fs/promises';
2
+ import { join, relative, extname } from 'node:path';
3
+ import { exists } from './fs-utils.js';
4
+ const IGNORE_DIRS = new Set([
5
+ 'node_modules', '.git', '.next', '.vercel', '.assay', 'dist', 'build',
6
+ 'out', '.cache', 'coverage', '__pycache__', '.venv', 'venv',
7
+ '.turbo', '.nuxt', '.svelte-kit', 'vendor',
8
+ ]);
9
+ const CODE_EXTENSIONS = new Set([
10
+ '.ts', '.tsx', '.js', '.jsx', '.py', '.rb', '.go', '.rs', '.java',
11
+ '.cs', '.php', '.swift', '.kt', '.scala', '.vue', '.svelte',
12
+ '.sql', '.graphql', '.gql', '.prisma',
13
+ ]);
14
+ const CONFIG_FILES = new Set([
15
+ 'package.json', 'tsconfig.json', 'next.config.js', 'next.config.ts',
16
+ 'next.config.mjs', 'vite.config.ts', 'vite.config.js',
17
+ 'nuxt.config.ts', 'svelte.config.js',
18
+ 'Dockerfile', 'docker-compose.yml', 'docker-compose.yaml',
19
+ '.env.example', '.env.local.example',
20
+ 'prisma/schema.prisma', 'drizzle.config.ts',
21
+ 'supabase/config.toml',
22
+ 'vercel.json', 'fly.toml', 'railway.toml',
23
+ 'wrangler.toml', 'Procfile',
24
+ ]);
25
+ const SCHEMA_PATTERNS = [
26
+ 'schema.prisma', 'schema.sql', 'schema.graphql',
27
+ 'drizzle', 'migrations',
28
+ ];
29
+ async function walkDir(dir, root, files, maxFiles) {
30
+ if (files.length >= maxFiles)
31
+ return;
32
+ let entries;
33
+ try {
34
+ entries = await readdir(dir, { withFileTypes: true });
35
+ }
36
+ catch {
37
+ return;
38
+ }
39
+ for (const entry of entries) {
40
+ if (files.length >= maxFiles)
41
+ return;
42
+ if (entry.isDirectory()) {
43
+ if (IGNORE_DIRS.has(entry.name) || entry.name.startsWith('.'))
44
+ continue;
45
+ await walkDir(join(dir, entry.name), root, files, maxFiles);
46
+ }
47
+ else {
48
+ const relPath = relative(root, join(dir, entry.name));
49
+ files.push(relPath);
50
+ }
51
+ }
52
+ }
53
+ function detectFrameworks(files) {
54
+ const frameworks = [];
55
+ const fileSet = new Set(files);
56
+ if (fileSet.has('package.json'))
57
+ frameworks.push('Node.js');
58
+ if (files.some((f) => f.includes('next.config')))
59
+ frameworks.push('Next.js');
60
+ if (files.some((f) => f.includes('nuxt.config')))
61
+ frameworks.push('Nuxt');
62
+ if (files.some((f) => f.includes('svelte.config')))
63
+ frameworks.push('SvelteKit');
64
+ if (files.some((f) => f.includes('vite.config')))
65
+ frameworks.push('Vite');
66
+ if (fileSet.has('requirements.txt') || fileSet.has('pyproject.toml'))
67
+ frameworks.push('Python');
68
+ if (fileSet.has('Gemfile'))
69
+ frameworks.push('Ruby');
70
+ if (fileSet.has('go.mod'))
71
+ frameworks.push('Go');
72
+ if (fileSet.has('Cargo.toml'))
73
+ frameworks.push('Rust');
74
+ if (files.some((f) => f.includes('prisma/schema.prisma')))
75
+ frameworks.push('Prisma');
76
+ if (files.some((f) => f.includes('supabase/')))
77
+ frameworks.push('Supabase');
78
+ if (fileSet.has('Dockerfile'))
79
+ frameworks.push('Docker');
80
+ if (fileSet.has('vercel.json') || files.some((f) => f.includes('.vercel')))
81
+ frameworks.push('Vercel');
82
+ return frameworks;
83
+ }
84
+ function identifyKeyFiles(files) {
85
+ const keyFiles = [];
86
+ for (const file of files) {
87
+ const basename = file.split('/').pop() || '';
88
+ if (CONFIG_FILES.has(basename)) {
89
+ keyFiles.push({ path: file, reason: 'configuration' });
90
+ continue;
91
+ }
92
+ if (SCHEMA_PATTERNS.some((p) => file.includes(p))) {
93
+ keyFiles.push({ path: file, reason: 'schema/database' });
94
+ continue;
95
+ }
96
+ // API routes
97
+ if (file.includes('/api/') && CODE_EXTENSIONS.has(extname(file))) {
98
+ keyFiles.push({ path: file, reason: 'API route' });
99
+ continue;
100
+ }
101
+ // Auth-related files
102
+ if (/auth|login|session|middleware/i.test(file) &&
103
+ CODE_EXTENSIONS.has(extname(file))) {
104
+ keyFiles.push({ path: file, reason: 'authentication' });
105
+ continue;
106
+ }
107
+ // Config/env handling
108
+ if (/config|env|secret/i.test(basename) && CODE_EXTENSIONS.has(extname(file))) {
109
+ keyFiles.push({ path: file, reason: 'configuration' });
110
+ }
111
+ }
112
+ return keyFiles;
113
+ }
114
+ export async function indexCodebase(rootPath, maxFiles = 5000) {
115
+ if (!(await exists(rootPath))) {
116
+ throw new Error(`Codebase path does not exist: ${rootPath}`);
117
+ }
118
+ const fileInfo = await stat(rootPath);
119
+ if (!fileInfo.isDirectory()) {
120
+ throw new Error(`Path is not a directory: ${rootPath}`);
121
+ }
122
+ const files = [];
123
+ await walkDir(rootPath, rootPath, files, maxFiles);
124
+ const codeFiles = files.filter((f) => CODE_EXTENSIONS.has(extname(f)));
125
+ const frameworks = detectFrameworks(files);
126
+ const keyFiles = identifyKeyFiles(files);
127
+ const summary = [
128
+ `Codebase at: ${rootPath}`,
129
+ `Total files: ${files.length} (${codeFiles.length} code files)`,
130
+ `Frameworks: ${frameworks.join(', ') || 'unknown'}`,
131
+ `Key files: ${keyFiles.length}`,
132
+ ].join('\n');
133
+ return {
134
+ rootPath,
135
+ totalFiles: files.length,
136
+ fileTree: files,
137
+ frameworks,
138
+ keyFiles,
139
+ summary,
140
+ };
141
+ }
142
+ export async function readFileContent(rootPath, relativePath) {
143
+ const fullPath = join(rootPath, relativePath);
144
+ try {
145
+ const content = await readFile(fullPath, 'utf-8');
146
+ // Truncate very large files
147
+ if (content.length > 50_000) {
148
+ return content.slice(0, 50_000) + '\n\n[... truncated at 50K chars]';
149
+ }
150
+ return content;
151
+ }
152
+ catch {
153
+ return null;
154
+ }
155
+ }
156
+ //# sourceMappingURL=codebase-indexer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codebase-indexer.js","sourceRoot":"","sources":["../../src/lib/codebase-indexer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAgBvC,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO;IACrE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM;IAC3D,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ;CAC3C,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO;IACjE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS;IAC3D,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS;CACtC,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC;IAC3B,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,gBAAgB;IACnE,iBAAiB,EAAE,gBAAgB,EAAE,gBAAgB;IACrD,gBAAgB,EAAE,kBAAkB;IACpC,YAAY,EAAE,oBAAoB,EAAE,qBAAqB;IACzD,cAAc,EAAE,oBAAoB;IACpC,sBAAsB,EAAE,mBAAmB;IAC3C,sBAAsB;IACtB,aAAa,EAAE,UAAU,EAAE,cAAc;IACzC,eAAe,EAAE,UAAU;CAC5B,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG;IACtB,eAAe,EAAE,YAAY,EAAE,gBAAgB;IAC/C,SAAS,EAAE,YAAY;CACxB,CAAC;AAEF,KAAK,UAAU,OAAO,CACpB,GAAW,EACX,IAAY,EACZ,KAAe,EACf,QAAgB;IAEhB,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO;IAErC,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ;YAAE,OAAO;QAErC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS;YACxE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC9D,CAAC;aAAM,CAAC;YACN,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YACtD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAe;IACvC,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAE/B,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7E,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1E,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACjF,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1E,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChG,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpD,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvD,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrF,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC5E,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzD,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEtG,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAe;IACvC,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;QAE7C,IAAI,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC;YACvD,SAAS;QACX,CAAC;QAED,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAClD,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;YACzD,SAAS;QACX,CAAC;QAED,aAAa;QACb,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;YACnD,SAAS;QACX,CAAC;QAED,qBAAqB;QACrB,IACE,gCAAgC,CAAC,IAAI,CAAC,IAAI,CAAC;YAC3C,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAClC,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;YACxD,SAAS;QACX,CAAC;QAED,sBAAsB;QACtB,IAAI,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAC9E,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,QAAgB,EAChB,QAAQ,GAAG,IAAI;IAEf,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,iCAAiC,QAAQ,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAEnD,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAEzC,MAAM,OAAO,GAAG;QACd,gBAAgB,QAAQ,EAAE;QAC1B,gBAAgB,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM,cAAc;QAC/D,eAAe,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,EAAE;QACnD,cAAc,QAAQ,CAAC,MAAM,EAAE;KAChC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,OAAO;QACL,QAAQ;QACR,UAAU,EAAE,KAAK,CAAC,MAAM;QACxB,QAAQ,EAAE,KAAK;QACf,UAAU;QACV,QAAQ;QACR,OAAO;KACR,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAgB,EAChB,YAAoB;IAEpB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC9C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClD,4BAA4B;QAC5B,IAAI,OAAO,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC;YAC5B,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,kCAAkC,CAAC;QACvE,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { AssayConfig } from '../types.js';
2
+ export declare function getAssayDir(cwd?: string): string;
3
+ export declare function getConfigPath(cwd?: string): string;
4
+ export declare function getIterationsDir(cwd?: string): string;
5
+ export declare function configExists(cwd?: string): Promise<boolean>;
6
+ export declare function readConfig(cwd?: string): Promise<AssayConfig>;
7
+ export declare function writeConfig(config: AssayConfig, cwd?: string): Promise<void>;
@@ -0,0 +1,38 @@
1
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { exists } from './fs-utils.js';
4
+ const ASSAY_DIR = '.assay';
5
+ const CONFIG_FILE = 'config.json';
6
+ const ITERATIONS_DIR = 'iterations';
7
+ export function getAssayDir(cwd = process.cwd()) {
8
+ return join(cwd, ASSAY_DIR);
9
+ }
10
+ export function getConfigPath(cwd = process.cwd()) {
11
+ return join(getAssayDir(cwd), CONFIG_FILE);
12
+ }
13
+ export function getIterationsDir(cwd = process.cwd()) {
14
+ return join(getAssayDir(cwd), ITERATIONS_DIR);
15
+ }
16
+ export async function configExists(cwd = process.cwd()) {
17
+ return exists(getConfigPath(cwd));
18
+ }
19
+ export async function readConfig(cwd = process.cwd()) {
20
+ const path = getConfigPath(cwd);
21
+ if (!(await exists(path))) {
22
+ throw new Error('No .assay/config.json found. Run `assay init` first.');
23
+ }
24
+ const raw = await readFile(path, 'utf-8');
25
+ return JSON.parse(raw);
26
+ }
27
+ export async function writeConfig(config, cwd = process.cwd()) {
28
+ const assayDir = getAssayDir(cwd);
29
+ const iterDir = getIterationsDir(cwd);
30
+ if (!(await exists(assayDir))) {
31
+ await mkdir(assayDir, { recursive: true });
32
+ }
33
+ if (!(await exists(iterDir))) {
34
+ await mkdir(iterDir, { recursive: true });
35
+ }
36
+ await writeFile(getConfigPath(cwd), JSON.stringify(config, null, 2) + '\n', 'utf-8');
37
+ }
38
+ //# sourceMappingURL=config.js.map