@varun-ai07/covenant-mcp 1.2.3 → 1.3.2
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.
- package/README.md +1056 -170
- package/dist/abis/MultiTokenEscrow.json +836 -0
- package/dist/abis/v2/AgentRegistry.json +872 -0
- package/dist/abis/v2/DisputeResolution.json +493 -0
- package/dist/abis/v2/InsurancePool.json +645 -0
- package/dist/abis/v2/ReceiptVerifier.json +394 -0
- package/dist/abis/v2/RevisionManager.json +544 -0
- package/dist/abis/v2/TaskEscrow.json +1018 -0
- package/dist/cli.js +0 -0
- package/dist/config.d.ts +13 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +84 -25
- package/dist/config.js.map +1 -1
- package/dist/lib/did.d.ts +72 -0
- package/dist/lib/did.d.ts.map +1 -0
- package/dist/lib/did.js +115 -0
- package/dist/lib/did.js.map +1 -0
- package/dist/lib/formatResponse.d.ts +33 -0
- package/dist/lib/formatResponse.d.ts.map +1 -0
- package/dist/lib/formatResponse.js +92 -0
- package/dist/lib/formatResponse.js.map +1 -0
- package/dist/lib/schemaHelpers.d.ts +11 -0
- package/dist/lib/schemaHelpers.d.ts.map +1 -0
- package/dist/lib/schemaHelpers.js +11 -0
- package/dist/lib/schemaHelpers.js.map +1 -0
- package/dist/lib/store.d.ts +10 -0
- package/dist/lib/store.d.ts.map +1 -0
- package/dist/lib/store.js +39 -0
- package/dist/lib/store.js.map +1 -0
- package/dist/lib/verify.d.ts +21 -0
- package/dist/lib/verify.d.ts.map +1 -0
- package/dist/lib/verify.js +568 -0
- package/dist/lib/verify.js.map +1 -0
- package/dist/schemas.d.ts +5 -5
- package/dist/schemas.d.ts.map +1 -1
- package/dist/server.d.ts +1 -25
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +53 -37
- package/dist/server.js.map +1 -1
- package/dist/shared-types.d.ts +67 -0
- package/dist/shared-types.d.ts.map +1 -0
- package/dist/shared-types.js +86 -0
- package/dist/shared-types.js.map +1 -0
- package/dist/tools/account-abstraction.d.ts +3 -0
- package/dist/tools/account-abstraction.d.ts.map +1 -0
- package/dist/tools/account-abstraction.js +364 -0
- package/dist/tools/account-abstraction.js.map +1 -0
- package/dist/tools/batches.d.ts.map +1 -1
- package/dist/tools/batches.js +68 -37
- package/dist/tools/batches.js.map +1 -1
- package/dist/tools/bounties.d.ts +3 -0
- package/dist/tools/bounties.d.ts.map +1 -0
- package/dist/tools/bounties.js +304 -0
- package/dist/tools/bounties.js.map +1 -0
- package/dist/tools/bridge.d.ts +3 -0
- package/dist/tools/bridge.d.ts.map +1 -0
- package/dist/tools/bridge.js +190 -0
- package/dist/tools/bridge.js.map +1 -0
- package/dist/tools/collectives.d.ts.map +1 -1
- package/dist/tools/collectives.js +74 -46
- package/dist/tools/collectives.js.map +1 -1
- package/dist/tools/covenant-help.d.ts +3 -0
- package/dist/tools/covenant-help.d.ts.map +1 -0
- package/dist/tools/covenant-help.js +321 -0
- package/dist/tools/covenant-help.js.map +1 -0
- package/dist/tools/cross-chain.d.ts +3 -0
- package/dist/tools/cross-chain.d.ts.map +1 -0
- package/dist/tools/cross-chain.js +77 -0
- package/dist/tools/cross-chain.js.map +1 -0
- package/dist/tools/disputes.d.ts.map +1 -1
- package/dist/tools/disputes.js +39 -33
- package/dist/tools/disputes.js.map +1 -1
- package/dist/tools/escrow.d.ts.map +1 -1
- package/dist/tools/escrow.js +248 -199
- package/dist/tools/escrow.js.map +1 -1
- package/dist/tools/fiat-onramp.d.ts +3 -0
- package/dist/tools/fiat-onramp.d.ts.map +1 -0
- package/dist/tools/fiat-onramp.js +108 -0
- package/dist/tools/fiat-onramp.js.map +1 -0
- package/dist/tools/governance.d.ts +3 -0
- package/dist/tools/governance.d.ts.map +1 -0
- package/dist/tools/governance.js +271 -0
- package/dist/tools/governance.js.map +1 -0
- package/dist/tools/grants.d.ts +3 -0
- package/dist/tools/grants.d.ts.map +1 -0
- package/dist/tools/grants.js +269 -0
- package/dist/tools/grants.js.map +1 -0
- package/dist/tools/insurance.d.ts.map +1 -1
- package/dist/tools/insurance.js +92 -45
- package/dist/tools/insurance.js.map +1 -1
- package/dist/tools/market.d.ts.map +1 -1
- package/dist/tools/market.js +122 -103
- package/dist/tools/market.js.map +1 -1
- package/dist/tools/matching.d.ts +3 -0
- package/dist/tools/matching.d.ts.map +1 -0
- package/dist/tools/matching.js +233 -0
- package/dist/tools/matching.js.map +1 -0
- package/dist/tools/messaging.d.ts +3 -0
- package/dist/tools/messaging.d.ts.map +1 -0
- package/dist/tools/messaging.js +159 -0
- package/dist/tools/messaging.js.map +1 -0
- package/dist/tools/multi-token.d.ts +3 -0
- package/dist/tools/multi-token.d.ts.map +1 -0
- package/dist/tools/multi-token.js +274 -0
- package/dist/tools/multi-token.js.map +1 -0
- package/dist/tools/offchain-coordinator.d.ts +3 -0
- package/dist/tools/offchain-coordinator.d.ts.map +1 -0
- package/dist/tools/offchain-coordinator.js +436 -0
- package/dist/tools/offchain-coordinator.js.map +1 -0
- package/dist/tools/protocol.d.ts.map +1 -1
- package/dist/tools/protocol.js +19 -6
- package/dist/tools/protocol.js.map +1 -1
- package/dist/tools/receipts.d.ts.map +1 -1
- package/dist/tools/receipts.js +39 -39
- package/dist/tools/receipts.js.map +1 -1
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +90 -43
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/reputation-vc.d.ts +3 -0
- package/dist/tools/reputation-vc.d.ts.map +1 -0
- package/dist/tools/reputation-vc.js +438 -0
- package/dist/tools/reputation-vc.js.map +1 -0
- package/dist/tools/revisions.d.ts +3 -0
- package/dist/tools/revisions.d.ts.map +1 -0
- package/dist/tools/revisions.js +108 -0
- package/dist/tools/revisions.js.map +1 -0
- package/dist/tools/router.d.ts +3 -0
- package/dist/tools/router.d.ts.map +1 -0
- package/dist/tools/router.js +104 -0
- package/dist/tools/router.js.map +1 -0
- package/dist/tools/streaming.d.ts +3 -0
- package/dist/tools/streaming.d.ts.map +1 -0
- package/dist/tools/streaming.js +350 -0
- package/dist/tools/streaming.js.map +1 -0
- package/dist/tools/templates.d.ts +3 -0
- package/dist/tools/templates.d.ts.map +1 -0
- package/dist/tools/templates.js +392 -0
- package/dist/tools/templates.js.map +1 -0
- package/dist/tools/training.d.ts +3 -0
- package/dist/tools/training.d.ts.map +1 -0
- package/dist/tools/training.js +304 -0
- package/dist/tools/training.js.map +1 -0
- package/dist/tools/v2-settlement.d.ts +3 -0
- package/dist/tools/v2-settlement.d.ts.map +1 -0
- package/dist/tools/v2-settlement.js +226 -0
- package/dist/tools/v2-settlement.js.map +1 -0
- package/dist/tools/verification.d.ts +3 -0
- package/dist/tools/verification.d.ts.map +1 -0
- package/dist/tools/verification.js +215 -0
- package/dist/tools/verification.js.map +1 -0
- package/dist/tools/verify-deep.d.ts +3 -0
- package/dist/tools/verify-deep.d.ts.map +1 -0
- package/dist/tools/verify-deep.js +125 -0
- package/dist/tools/verify-deep.js.map +1 -0
- package/dist/types.d.ts +16 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -23
- package/dist/types.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const ethAddress = z.string().describe('Full 42-character Ethereum address starting with 0x. Example: "0x715f3b64189EcA51a57567962Cd2278dc7a5e92C". Do NOT pass ENS names. Do NOT abbreviate. Must be exactly 42 characters.');
|
|
3
|
+
export const ethAmount = z.string().describe('Payment amount in ETH as a decimal string. Examples: "0.001" (1 milliETH), "0.01" (10 milliETH). Do NOT pass wei values. Do NOT pass plain numbers. Always a quoted decimal string.');
|
|
4
|
+
export const ethStake = z.string().optional().default("0.001").describe('Stake deposit in ETH as a decimal string. Minimum is "0.001". This is held as a security deposit, not a fee. It is returned when you deregister cleanly.');
|
|
5
|
+
export const ipfsCid = z.string().describe('IPFS content identifier (CID). Starts with "Qm" (CIDv0, 46 chars) or "bafy" (CIDv1). Upload your content to Pinata (pinata.cloud) first, then pass the returned CID here.');
|
|
6
|
+
export const unixDeadline = z.number().describe('Unix timestamp in seconds when the task expires. For 24 hours from now: Math.floor(Date.now()/1000) + 86400. For 48 hours: Math.floor(Date.now()/1000) + 172800. Must be a number (not string) and must be in the future.');
|
|
7
|
+
export const taskId = z.number().describe('Numeric task ID returned by corven_create_task or corven_post_open_task. Example: 42. Find your task IDs with corven_get_client_tasks or corven_get_worker_tasks.');
|
|
8
|
+
export const agentName = z.string().min(1).max(100).describe('Human-readable display name for this agent. Stored permanently on-chain. Examples: "ResearchBot", "DataAnalystPro", "CodeReviewAgent".');
|
|
9
|
+
export const capabilities = z.array(z.string()).min(1).max(10).describe('Array of capability tags this agent can perform. Valid values: "data-analysis", "code-review", "content-writing", "financial-analysis", "research", "translation", "testing", "security-audit", "documentation", "smart-contract", "python", "visualization", "api-integration", "ml-training", "design". Maximum 10 capabilities per agent.');
|
|
10
|
+
export const priority = z.number().min(0).max(3).optional().default(1).describe('Task priority level. 0 = Low, 1 = Medium (default), 2 = High, 3 = Urgent. Use 1 for most tasks.');
|
|
11
|
+
//# sourceMappingURL=schemaHelpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaHelpers.js","sourceRoot":"","sources":["../../src/lib/schemaHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAC3C,sLAAsL,CACvL,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAC1C,qLAAqL,CACtL,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,QAAQ,CACrE,0JAA0J,CAC3J,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CACxC,2KAA2K,CAC5K,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAC7C,2NAA2N,CAC5N,CAAC;AAEF,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CACvC,mKAAmK,CACpK,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAC1D,wIAAwI,CACzI,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,CACrE,8UAA8U,CAC/U,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAC7E,iGAAiG,CAClG,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load a persisted store by name. Returns `defaultValue` when no file exists
|
|
3
|
+
* or the file cannot be parsed.
|
|
4
|
+
*/
|
|
5
|
+
export declare function loadStore<T>(name: string, defaultValue: T): T;
|
|
6
|
+
/**
|
|
7
|
+
* Persist a store to disk immediately. Call after every mutation.
|
|
8
|
+
*/
|
|
9
|
+
export declare function saveStore<T>(name: string, data: T): void;
|
|
10
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/lib/store.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,GAAG,CAAC,CAS7D;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAIxD"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local JSON file persistence for in-memory MCP stores.
|
|
3
|
+
*
|
|
4
|
+
* Data lives under <cwd>/.covenant-data/<name>.json so it survives server
|
|
5
|
+
* restarts while keeping the in-memory Map / Set semantics during runtime.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
const DATA_DIR = join(process.cwd(), ".covenant-data");
|
|
10
|
+
function ensureDir() {
|
|
11
|
+
if (!existsSync(DATA_DIR)) {
|
|
12
|
+
mkdirSync(DATA_DIR, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Load a persisted store by name. Returns `defaultValue` when no file exists
|
|
17
|
+
* or the file cannot be parsed.
|
|
18
|
+
*/
|
|
19
|
+
export function loadStore(name, defaultValue) {
|
|
20
|
+
ensureDir();
|
|
21
|
+
const filePath = join(DATA_DIR, `${name}.json`);
|
|
22
|
+
if (!existsSync(filePath))
|
|
23
|
+
return defaultValue;
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return defaultValue;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Persist a store to disk immediately. Call after every mutation.
|
|
33
|
+
*/
|
|
34
|
+
export function saveStore(name, data) {
|
|
35
|
+
ensureDir();
|
|
36
|
+
const filePath = join(DATA_DIR, `${name}.json`);
|
|
37
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/lib/store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,gBAAgB,CAAC,CAAC;AAEvD,SAAS,SAAS;IAChB,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAI,IAAY,EAAE,YAAe;IACxD,SAAS,EAAE,CAAC;IACZ,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;IAChD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,YAAY,CAAC;IAC/C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAM,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,YAAY,CAAC;IACtB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAI,IAAY,EAAE,IAAO;IAChD,SAAS,EAAE,CAAC;IACZ,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;IAChD,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
interface VerificationResult {
|
|
2
|
+
score: number;
|
|
3
|
+
verdict: "pass" | "fail" | "partial";
|
|
4
|
+
checks: CheckResult[];
|
|
5
|
+
summary: string;
|
|
6
|
+
recommendations: string[];
|
|
7
|
+
evidenceHash: string;
|
|
8
|
+
repoUrl: string;
|
|
9
|
+
timestamp: number;
|
|
10
|
+
}
|
|
11
|
+
interface CheckResult {
|
|
12
|
+
dimension: string;
|
|
13
|
+
score: number;
|
|
14
|
+
details: string;
|
|
15
|
+
issues: string[];
|
|
16
|
+
passed: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function cloneRepo(url: string): Promise<string>;
|
|
19
|
+
export declare function verifyProject(repoUrl: string, requirements: string, depth?: "quick" | "standard" | "deep"): Promise<VerificationResult>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/lib/verify.ts"],"names":[],"mappings":"AAOA,UAAU,kBAAkB;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACrC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,WAAW;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CACjB;AAID,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI5D;AAID,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,KAAK,GAAE,OAAO,GAAG,UAAU,GAAG,MAAmB,GAChD,OAAO,CAAC,kBAAkB,CAAC,CAgE7B"}
|
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
import { readFileSync, readdirSync, statSync, existsSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { createHash } from "crypto";
|
|
5
|
+
// ─── Clone ───────────────────────────────────────────────────
|
|
6
|
+
export async function cloneRepo(url) {
|
|
7
|
+
const tmpDir = `/tmp/covenant-verify-${Date.now()}`;
|
|
8
|
+
execSync(`git clone --depth 1 ${url} ${tmpDir}`, { timeout: 60000 });
|
|
9
|
+
return tmpDir;
|
|
10
|
+
}
|
|
11
|
+
// ─── Main Verification ───────────────────────────────────────
|
|
12
|
+
export async function verifyProject(repoUrl, requirements, depth = "standard") {
|
|
13
|
+
const repoDir = await cloneRepo(repoUrl);
|
|
14
|
+
const checks = [];
|
|
15
|
+
checks.push(await checkCodeQuality(repoDir));
|
|
16
|
+
checks.push(await checkArchitecture(repoDir));
|
|
17
|
+
checks.push(await checkSecurity(repoDir));
|
|
18
|
+
checks.push(await checkPerformance(repoDir));
|
|
19
|
+
checks.push(await checkTesting(repoDir));
|
|
20
|
+
checks.push(await checkDocumentation(repoDir));
|
|
21
|
+
checks.push(await checkDependencies(repoDir));
|
|
22
|
+
checks.push(await checkBestPractices(repoDir));
|
|
23
|
+
// Weighted score
|
|
24
|
+
const weights = {
|
|
25
|
+
code_quality: 0.20,
|
|
26
|
+
architecture: 0.15,
|
|
27
|
+
security: 0.20,
|
|
28
|
+
performance: 0.15,
|
|
29
|
+
testing: 0.15,
|
|
30
|
+
documentation: 0.05,
|
|
31
|
+
dependencies: 0.05,
|
|
32
|
+
best_practices: 0.05,
|
|
33
|
+
};
|
|
34
|
+
let totalScore = 0;
|
|
35
|
+
for (const check of checks) {
|
|
36
|
+
totalScore += check.score * (weights[check.dimension] || 0.1);
|
|
37
|
+
}
|
|
38
|
+
const score = Math.round(totalScore);
|
|
39
|
+
const verdict = score >= 70 ? "pass" : score >= 40 ? "partial" : "fail";
|
|
40
|
+
const summary = generateSummary(checks, score, requirements);
|
|
41
|
+
const recommendations = generateRecommendations(checks);
|
|
42
|
+
const report = {
|
|
43
|
+
checks,
|
|
44
|
+
score,
|
|
45
|
+
verdict,
|
|
46
|
+
summary,
|
|
47
|
+
recommendations,
|
|
48
|
+
timestamp: Date.now(),
|
|
49
|
+
};
|
|
50
|
+
const evidenceHash = createHash("sha256")
|
|
51
|
+
.update(JSON.stringify(report))
|
|
52
|
+
.digest("hex");
|
|
53
|
+
// Cleanup
|
|
54
|
+
try {
|
|
55
|
+
execSync(`rm -rf ${repoDir}`);
|
|
56
|
+
}
|
|
57
|
+
catch { /* best-effort cleanup */ }
|
|
58
|
+
return {
|
|
59
|
+
score,
|
|
60
|
+
verdict,
|
|
61
|
+
checks,
|
|
62
|
+
summary,
|
|
63
|
+
recommendations,
|
|
64
|
+
evidenceHash,
|
|
65
|
+
repoUrl,
|
|
66
|
+
timestamp: Date.now(),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// ─── Checkers ────────────────────────────────────────────────
|
|
70
|
+
async function checkCodeQuality(dir) {
|
|
71
|
+
const issues = [];
|
|
72
|
+
let score = 100;
|
|
73
|
+
const loc = countLines(dir);
|
|
74
|
+
if (loc < 10) {
|
|
75
|
+
issues.push("Very few lines of code");
|
|
76
|
+
score -= 30;
|
|
77
|
+
}
|
|
78
|
+
const consoleLogs = countPattern(dir, /console\.log\(/g);
|
|
79
|
+
if (consoleLogs > 5) {
|
|
80
|
+
issues.push(`${consoleLogs} console.log statements found`);
|
|
81
|
+
score -= 10;
|
|
82
|
+
}
|
|
83
|
+
const anyTypes = countPattern(dir, /:\s*any/g);
|
|
84
|
+
if (anyTypes > 10) {
|
|
85
|
+
issues.push(`${anyTypes} uses of 'any' type`);
|
|
86
|
+
score -= 15;
|
|
87
|
+
}
|
|
88
|
+
const todos = countPattern(dir, /TODO|FIXME|HACK/gi);
|
|
89
|
+
if (todos > 0) {
|
|
90
|
+
issues.push(`${todos} TODO/FIXME comments`);
|
|
91
|
+
score -= 5;
|
|
92
|
+
}
|
|
93
|
+
// Simple unused import heuristic
|
|
94
|
+
const files = getFiles(dir, [".ts", ".tsx", ".js", ".jsx"]);
|
|
95
|
+
let unusedImports = 0;
|
|
96
|
+
for (const file of files.slice(0, 100)) {
|
|
97
|
+
// cap at 100 files for perf
|
|
98
|
+
const content = readFileSync(file, "utf-8");
|
|
99
|
+
const imports = content.match(/import\s+.*\s+from\s+['"](.+)['"]/g) || [];
|
|
100
|
+
for (const imp of imports) {
|
|
101
|
+
const match = imp.match(/import\s+\{\s*(.+?)\s*\}/);
|
|
102
|
+
if (match) {
|
|
103
|
+
const symbols = match[1].split(",").map((s) => s.trim());
|
|
104
|
+
for (const sym of symbols) {
|
|
105
|
+
if (sym.length > 0 && !content.includes(sym.replace("type ", ""))) {
|
|
106
|
+
unusedImports++;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (unusedImports > 5) {
|
|
113
|
+
issues.push(`${unusedImports} potentially unused imports`);
|
|
114
|
+
score -= 5;
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
dimension: "code_quality",
|
|
118
|
+
score: Math.max(0, score),
|
|
119
|
+
details: `LOC: ${loc}, Console logs: ${consoleLogs}, Any types: ${anyTypes}, TODOs: ${todos}`,
|
|
120
|
+
issues,
|
|
121
|
+
passed: score >= 70,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async function checkArchitecture(dir) {
|
|
125
|
+
const issues = [];
|
|
126
|
+
let score = 100;
|
|
127
|
+
const hasSrc = existsSync(join(dir, "src")) || existsSync(join(dir, "app"));
|
|
128
|
+
const hasTests = existsSync(join(dir, "test")) ||
|
|
129
|
+
existsSync(join(dir, "tests")) ||
|
|
130
|
+
existsSync(join(dir, "__tests__"));
|
|
131
|
+
const hasDocs = existsSync(join(dir, "docs")) || existsSync(join(dir, "README.md"));
|
|
132
|
+
const hasConfig = existsSync(join(dir, "package.json")) ||
|
|
133
|
+
existsSync(join(dir, "tsconfig.json"));
|
|
134
|
+
if (!hasSrc && !hasConfig) {
|
|
135
|
+
issues.push("No src/ or config found");
|
|
136
|
+
score -= 20;
|
|
137
|
+
}
|
|
138
|
+
if (!hasTests) {
|
|
139
|
+
issues.push("No test directory found");
|
|
140
|
+
score -= 15;
|
|
141
|
+
}
|
|
142
|
+
if (!hasDocs) {
|
|
143
|
+
issues.push("No documentation found");
|
|
144
|
+
score -= 10;
|
|
145
|
+
}
|
|
146
|
+
const fileCount = countFiles(dir);
|
|
147
|
+
if (fileCount < 3) {
|
|
148
|
+
issues.push("Very few files");
|
|
149
|
+
score -= 20;
|
|
150
|
+
}
|
|
151
|
+
if (fileCount > 500) {
|
|
152
|
+
issues.push("Very large codebase (500+ files)");
|
|
153
|
+
score -= 5;
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
dimension: "architecture",
|
|
157
|
+
score: Math.max(0, score),
|
|
158
|
+
details: `Files: ${fileCount}, Has src: ${hasSrc}, Has tests: ${hasTests}, Has docs: ${hasDocs}`,
|
|
159
|
+
issues,
|
|
160
|
+
passed: score >= 70,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
async function checkSecurity(dir) {
|
|
164
|
+
const issues = [];
|
|
165
|
+
let score = 100;
|
|
166
|
+
const secretPatterns = [
|
|
167
|
+
/API_KEY\s*=\s*['"][^'"]+['"]/gi,
|
|
168
|
+
/SECRET\s*=\s*['"][^'"]+['"]/gi,
|
|
169
|
+
/PASSWORD\s*=\s*['"][^'"]+['"]/gi,
|
|
170
|
+
/PRIVATE_KEY\s*=\s*['"][^'"]+['"]/gi,
|
|
171
|
+
];
|
|
172
|
+
for (const pattern of secretPatterns) {
|
|
173
|
+
const matches = countPattern(dir, pattern);
|
|
174
|
+
if (matches > 0) {
|
|
175
|
+
issues.push(`${matches} potential hardcoded secrets`);
|
|
176
|
+
score -= 20;
|
|
177
|
+
break; // one deduction is enough for secrets
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (existsSync(join(dir, ".env"))) {
|
|
181
|
+
issues.push(".env file committed to repo");
|
|
182
|
+
score -= 15;
|
|
183
|
+
}
|
|
184
|
+
const evals = countPattern(dir, /\beval\s*\(/g);
|
|
185
|
+
if (evals > 0) {
|
|
186
|
+
issues.push(`${evals} eval() usage`);
|
|
187
|
+
score -= 15;
|
|
188
|
+
}
|
|
189
|
+
const innerHTML = countPattern(dir, /innerHTML/g);
|
|
190
|
+
if (innerHTML > 0) {
|
|
191
|
+
issues.push(`${innerHTML} innerHTML usage (XSS risk)`);
|
|
192
|
+
score -= 10;
|
|
193
|
+
}
|
|
194
|
+
const sqlInjection = countPattern(dir, /query\s*\(\s*['"`].*\$\{/g);
|
|
195
|
+
if (sqlInjection > 0) {
|
|
196
|
+
issues.push(`${sqlInjection} potential SQL injection`);
|
|
197
|
+
score -= 20;
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
dimension: "security",
|
|
201
|
+
score: Math.max(0, score),
|
|
202
|
+
details: `Hardcoded secrets checked, eval: ${evals}, innerHTML: ${innerHTML}`,
|
|
203
|
+
issues,
|
|
204
|
+
passed: score >= 70,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
async function checkPerformance(dir) {
|
|
208
|
+
const issues = [];
|
|
209
|
+
let score = 100;
|
|
210
|
+
// Check for build output
|
|
211
|
+
const buildDirs = ["dist", "build", ".next"];
|
|
212
|
+
for (const bd of buildDirs) {
|
|
213
|
+
const bdPath = join(dir, bd);
|
|
214
|
+
if (existsSync(bdPath)) {
|
|
215
|
+
const size = getDirSize(bdPath);
|
|
216
|
+
const sizeMB = size / (1024 * 1024);
|
|
217
|
+
if (sizeMB > 10) {
|
|
218
|
+
issues.push(`Bundle size: ${sizeMB.toFixed(1)}MB (very large)`);
|
|
219
|
+
score -= 20;
|
|
220
|
+
}
|
|
221
|
+
else if (sizeMB > 5) {
|
|
222
|
+
issues.push(`Bundle size: ${sizeMB.toFixed(1)}MB (large)`);
|
|
223
|
+
score -= 10;
|
|
224
|
+
}
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const largeFiles = findLargeFiles(dir, 1024 * 1024);
|
|
229
|
+
if (largeFiles.length > 0) {
|
|
230
|
+
issues.push(`${largeFiles.length} files larger than 1MB`);
|
|
231
|
+
score -= 10;
|
|
232
|
+
}
|
|
233
|
+
const asyncFiles = countPattern(dir, /async\s+function|async\s*\(/g);
|
|
234
|
+
const totalFiles = countFiles(dir);
|
|
235
|
+
if (totalFiles > 10 && asyncFiles === 0) {
|
|
236
|
+
issues.push("No async/await usage found");
|
|
237
|
+
score -= 5;
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
dimension: "performance",
|
|
241
|
+
score: Math.max(0, score),
|
|
242
|
+
details: `Large files: ${largeFiles.length}, Async usage: ${asyncFiles}`,
|
|
243
|
+
issues,
|
|
244
|
+
passed: score >= 70,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
async function checkTesting(dir) {
|
|
248
|
+
const issues = [];
|
|
249
|
+
let score = 100;
|
|
250
|
+
const hasTests = existsSync(join(dir, "test")) ||
|
|
251
|
+
existsSync(join(dir, "tests")) ||
|
|
252
|
+
existsSync(join(dir, "__tests__")) ||
|
|
253
|
+
existsSync(join(dir, "spec"));
|
|
254
|
+
if (!hasTests) {
|
|
255
|
+
issues.push("No test files found");
|
|
256
|
+
score -= 50;
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
const testFiles = getFiles(dir, [
|
|
260
|
+
".test.ts",
|
|
261
|
+
".test.js",
|
|
262
|
+
".spec.ts",
|
|
263
|
+
".spec.js",
|
|
264
|
+
".test.tsx",
|
|
265
|
+
]);
|
|
266
|
+
const sourceFiles = getFiles(dir, [".ts", ".tsx", ".js", ".jsx"]);
|
|
267
|
+
const coverage = sourceFiles.length > 0
|
|
268
|
+
? (testFiles.length / sourceFiles.length) * 100
|
|
269
|
+
: 0;
|
|
270
|
+
if (coverage < 30) {
|
|
271
|
+
issues.push(`Low test coverage: ${coverage.toFixed(0)}%`);
|
|
272
|
+
score -= 20;
|
|
273
|
+
}
|
|
274
|
+
else if (coverage < 60) {
|
|
275
|
+
issues.push(`Moderate test coverage: ${coverage.toFixed(0)}%`);
|
|
276
|
+
score -= 10;
|
|
277
|
+
}
|
|
278
|
+
let assertionCount = 0;
|
|
279
|
+
for (const testFile of testFiles.slice(0, 50)) {
|
|
280
|
+
const content = readFileSync(testFile, "utf-8");
|
|
281
|
+
assertionCount += (content.match(/expect\(/g) || []).length;
|
|
282
|
+
}
|
|
283
|
+
if (testFiles.length > 0 && assertionCount / testFiles.length < 3) {
|
|
284
|
+
issues.push("Low assertion density in tests");
|
|
285
|
+
score -= 10;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
dimension: "testing",
|
|
290
|
+
score: Math.max(0, score),
|
|
291
|
+
details: `Tests found: ${hasTests}`,
|
|
292
|
+
issues,
|
|
293
|
+
passed: score >= 70,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
async function checkDocumentation(dir) {
|
|
297
|
+
const issues = [];
|
|
298
|
+
let score = 100;
|
|
299
|
+
const readmePath = join(dir, "README.md");
|
|
300
|
+
if (existsSync(readmePath)) {
|
|
301
|
+
const readme = readFileSync(readmePath, "utf-8");
|
|
302
|
+
if (readme.length < 100) {
|
|
303
|
+
issues.push("README is too short (<100 chars)");
|
|
304
|
+
score -= 20;
|
|
305
|
+
}
|
|
306
|
+
if (!readme.includes("#")) {
|
|
307
|
+
issues.push("README has no headers");
|
|
308
|
+
score -= 10;
|
|
309
|
+
}
|
|
310
|
+
if (!readme.includes("```")) {
|
|
311
|
+
issues.push("README has no code examples");
|
|
312
|
+
score -= 5;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
issues.push("No README.md found");
|
|
317
|
+
score -= 30;
|
|
318
|
+
}
|
|
319
|
+
const docComments = countPattern(dir, /\/\*\*[\s\S]*?\*\//g);
|
|
320
|
+
if (docComments < 5) {
|
|
321
|
+
issues.push("Few code documentation comments");
|
|
322
|
+
score -= 10;
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
dimension: "documentation",
|
|
326
|
+
score: Math.max(0, score),
|
|
327
|
+
details: `README: ${existsSync(readmePath)}, Doc comments: ${docComments}`,
|
|
328
|
+
issues,
|
|
329
|
+
passed: score >= 70,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
async function checkDependencies(dir) {
|
|
333
|
+
const issues = [];
|
|
334
|
+
let score = 100;
|
|
335
|
+
const pkgPath = join(dir, "package.json");
|
|
336
|
+
if (existsSync(pkgPath)) {
|
|
337
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
338
|
+
const deps = Object.keys(pkg.dependencies || {});
|
|
339
|
+
const devDeps = Object.keys(pkg.devDependencies || {});
|
|
340
|
+
if (deps.length > 50) {
|
|
341
|
+
issues.push(`${deps.length} dependencies (may be bloated)`);
|
|
342
|
+
score -= 10;
|
|
343
|
+
}
|
|
344
|
+
if (deps.length === 0 && devDeps.length === 0) {
|
|
345
|
+
issues.push("No dependencies declared");
|
|
346
|
+
score -= 10;
|
|
347
|
+
}
|
|
348
|
+
const deprecated = ["lodash", "moment", "request"];
|
|
349
|
+
for (const v of deprecated) {
|
|
350
|
+
if (deps.includes(v)) {
|
|
351
|
+
issues.push(`Uses ${v} (consider alternatives)`);
|
|
352
|
+
score -= 5;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
const hasLock = existsSync(join(dir, "package-lock.json")) ||
|
|
357
|
+
existsSync(join(dir, "yarn.lock")) ||
|
|
358
|
+
existsSync(join(dir, "pnpm-lock.yaml"));
|
|
359
|
+
if (!hasLock) {
|
|
360
|
+
issues.push("No lock file found");
|
|
361
|
+
score -= 10;
|
|
362
|
+
}
|
|
363
|
+
return {
|
|
364
|
+
dimension: "dependencies",
|
|
365
|
+
score: Math.max(0, score),
|
|
366
|
+
details: `Dependencies: ${existsSync(pkgPath) ? "found" : "none"}, Lock file: ${hasLock}`,
|
|
367
|
+
issues,
|
|
368
|
+
passed: score >= 70,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
async function checkBestPractices(dir) {
|
|
372
|
+
const issues = [];
|
|
373
|
+
let score = 100;
|
|
374
|
+
if (!existsSync(join(dir, ".gitignore"))) {
|
|
375
|
+
issues.push("No .gitignore found");
|
|
376
|
+
score -= 10;
|
|
377
|
+
}
|
|
378
|
+
if (existsSync(join(dir, "tsconfig.json"))) {
|
|
379
|
+
const tsconfig = JSON.parse(readFileSync(join(dir, "tsconfig.json"), "utf-8"));
|
|
380
|
+
if (!tsconfig.compilerOptions?.strict) {
|
|
381
|
+
issues.push("TypeScript strict mode not enabled");
|
|
382
|
+
score -= 10;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const hasLint = existsSync(join(dir, ".eslintrc")) ||
|
|
386
|
+
existsSync(join(dir, ".eslintrc.js")) ||
|
|
387
|
+
existsSync(join(dir, ".eslintrc.json")) ||
|
|
388
|
+
existsSync(join(dir, "eslint.config.js"));
|
|
389
|
+
if (!hasLint) {
|
|
390
|
+
issues.push("No ESLint configuration found");
|
|
391
|
+
score -= 10;
|
|
392
|
+
}
|
|
393
|
+
const tryCatch = countPattern(dir, /try\s*\{/g);
|
|
394
|
+
const asyncFiles = countPattern(dir, /async\s+function|async\s*\(/g);
|
|
395
|
+
if (asyncFiles > 5 && tryCatch < asyncFiles * 0.3) {
|
|
396
|
+
issues.push("Low error handling coverage");
|
|
397
|
+
score -= 10;
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
dimension: "best_practices",
|
|
401
|
+
score: Math.max(0, score),
|
|
402
|
+
details: `Gitignore: ${existsSync(join(dir, ".gitignore"))}, Lint: ${hasLint}`,
|
|
403
|
+
issues,
|
|
404
|
+
passed: score >= 70,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
// ─── Helpers ─────────────────────────────────────────────────
|
|
408
|
+
const SKIP_DIRS = new Set([
|
|
409
|
+
"node_modules",
|
|
410
|
+
".git",
|
|
411
|
+
"dist",
|
|
412
|
+
"build",
|
|
413
|
+
".next",
|
|
414
|
+
"coverage",
|
|
415
|
+
"__pycache__",
|
|
416
|
+
]);
|
|
417
|
+
function countLines(dir) {
|
|
418
|
+
let total = 0;
|
|
419
|
+
const files = getFiles(dir, [
|
|
420
|
+
".ts",
|
|
421
|
+
".tsx",
|
|
422
|
+
".js",
|
|
423
|
+
".jsx",
|
|
424
|
+
".py",
|
|
425
|
+
".go",
|
|
426
|
+
".rs",
|
|
427
|
+
".sol",
|
|
428
|
+
]);
|
|
429
|
+
for (const file of files) {
|
|
430
|
+
const content = readFileSync(file, "utf-8");
|
|
431
|
+
total += content.split("\n").length;
|
|
432
|
+
}
|
|
433
|
+
return total;
|
|
434
|
+
}
|
|
435
|
+
function countPattern(dir, pattern) {
|
|
436
|
+
let count = 0;
|
|
437
|
+
const files = getFiles(dir, [".ts", ".tsx", ".js", ".jsx", ".py", ".sol"]);
|
|
438
|
+
for (const file of files.slice(0, 200)) {
|
|
439
|
+
const content = readFileSync(file, "utf-8");
|
|
440
|
+
const matches = content.match(pattern);
|
|
441
|
+
if (matches)
|
|
442
|
+
count += matches.length;
|
|
443
|
+
}
|
|
444
|
+
return count;
|
|
445
|
+
}
|
|
446
|
+
function getFiles(dir, extensions) {
|
|
447
|
+
const files = [];
|
|
448
|
+
let entries;
|
|
449
|
+
try {
|
|
450
|
+
entries = readdirSync(dir);
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
return files;
|
|
454
|
+
}
|
|
455
|
+
for (const entry of entries) {
|
|
456
|
+
if (SKIP_DIRS.has(entry) || entry.startsWith("."))
|
|
457
|
+
continue;
|
|
458
|
+
const fullPath = join(dir, entry);
|
|
459
|
+
let stat;
|
|
460
|
+
try {
|
|
461
|
+
stat = statSync(fullPath);
|
|
462
|
+
}
|
|
463
|
+
catch {
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
if (stat.isDirectory()) {
|
|
467
|
+
files.push(...getFiles(fullPath, extensions));
|
|
468
|
+
}
|
|
469
|
+
else if (extensions.some((ext) => entry.endsWith(ext))) {
|
|
470
|
+
files.push(fullPath);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return files;
|
|
474
|
+
}
|
|
475
|
+
function countFiles(dir) {
|
|
476
|
+
return getFiles(dir, [
|
|
477
|
+
".ts",
|
|
478
|
+
".tsx",
|
|
479
|
+
".js",
|
|
480
|
+
".jsx",
|
|
481
|
+
".py",
|
|
482
|
+
".go",
|
|
483
|
+
".rs",
|
|
484
|
+
".sol",
|
|
485
|
+
".md",
|
|
486
|
+
".json",
|
|
487
|
+
".yaml",
|
|
488
|
+
".yml",
|
|
489
|
+
]).length;
|
|
490
|
+
}
|
|
491
|
+
function getDirSize(dir) {
|
|
492
|
+
let size = 0;
|
|
493
|
+
let entries;
|
|
494
|
+
try {
|
|
495
|
+
entries = readdirSync(dir);
|
|
496
|
+
}
|
|
497
|
+
catch {
|
|
498
|
+
return 0;
|
|
499
|
+
}
|
|
500
|
+
for (const entry of entries) {
|
|
501
|
+
if (SKIP_DIRS.has(entry))
|
|
502
|
+
continue;
|
|
503
|
+
const fullPath = join(dir, entry);
|
|
504
|
+
let stat;
|
|
505
|
+
try {
|
|
506
|
+
stat = statSync(fullPath);
|
|
507
|
+
}
|
|
508
|
+
catch {
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
if (stat.isDirectory()) {
|
|
512
|
+
size += getDirSize(fullPath);
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
size += stat.size;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return size;
|
|
519
|
+
}
|
|
520
|
+
function findLargeFiles(dir, maxSize) {
|
|
521
|
+
const large = [];
|
|
522
|
+
let entries;
|
|
523
|
+
try {
|
|
524
|
+
entries = readdirSync(dir);
|
|
525
|
+
}
|
|
526
|
+
catch {
|
|
527
|
+
return large;
|
|
528
|
+
}
|
|
529
|
+
for (const entry of entries) {
|
|
530
|
+
if (SKIP_DIRS.has(entry))
|
|
531
|
+
continue;
|
|
532
|
+
const fullPath = join(dir, entry);
|
|
533
|
+
let stat;
|
|
534
|
+
try {
|
|
535
|
+
stat = statSync(fullPath);
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
if (stat.isDirectory()) {
|
|
541
|
+
large.push(...findLargeFiles(fullPath, maxSize));
|
|
542
|
+
}
|
|
543
|
+
else if (stat.size > maxSize) {
|
|
544
|
+
large.push(fullPath);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return large;
|
|
548
|
+
}
|
|
549
|
+
function generateSummary(checks, score, requirements) {
|
|
550
|
+
const passed = checks.filter((c) => c.passed).length;
|
|
551
|
+
const total = checks.length;
|
|
552
|
+
const topIssues = checks.flatMap((c) => c.issues).slice(0, 3);
|
|
553
|
+
return (`Score: ${score}/100 (${passed}/${total} checks passed). ` +
|
|
554
|
+
`Requirements: ${requirements}. ` +
|
|
555
|
+
(topIssues.length > 0
|
|
556
|
+
? `Top issues: ${topIssues.join("; ")}`
|
|
557
|
+
: "No major issues found."));
|
|
558
|
+
}
|
|
559
|
+
function generateRecommendations(checks) {
|
|
560
|
+
const recs = [];
|
|
561
|
+
for (const check of checks) {
|
|
562
|
+
if (!check.passed) {
|
|
563
|
+
recs.push(`Improve ${check.dimension}: ${check.issues[0] || "needs work"}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return recs.slice(0, 5);
|
|
567
|
+
}
|
|
568
|
+
//# sourceMappingURL=verify.js.map
|