@prodcycle/prodcycle 0.1.5

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/LICENSE ADDED
@@ -0,0 +1,52 @@
1
+ Copyright (c) 2025-2026 ProdCycle, Inc. All rights reserved.
2
+
3
+ This software and associated documentation files (the "Software") are the
4
+ proprietary property of ProdCycle, Inc. and are protected by copyright law.
5
+
6
+ GRANT OF LICENSE
7
+
8
+ Subject to valid license key activation and the terms of your ProdCycle
9
+ subscription agreement, ProdCycle, Inc. grants you a limited, non-exclusive,
10
+ non-transferable, revocable license to use the Software solely for your
11
+ internal business purposes.
12
+
13
+ RESTRICTIONS
14
+
15
+ You may not, without the prior written consent of ProdCycle, Inc.:
16
+
17
+ 1. Copy, modify, or create derivative works of the Software;
18
+ 2. Distribute, sublicense, lease, rent, or lend the Software to any
19
+ third party;
20
+ 3. Reverse engineer, decompile, or disassemble the Software;
21
+ 4. Remove or alter any proprietary notices, labels, or marks on the
22
+ Software;
23
+ 5. Use the Software to build a competing product or service.
24
+
25
+ COMPLIANCE POLICIES
26
+
27
+ The compliance policy definitions (Rego rules, Cedar policies, and framework
28
+ control mappings) included in the `policies/` and `frameworks/` directories
29
+ are provided as part of the Software and are subject to the same license
30
+ terms.
31
+
32
+ DISCLAIMER OF WARRANTIES
33
+
34
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
37
+
38
+ LIMITATION OF LIABILITY
39
+
40
+ IN NO EVENT SHALL PRODCYCLE, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
43
+ SOFTWARE.
44
+
45
+ TERMINATION
46
+
47
+ This license is effective until terminated. ProdCycle, Inc. may terminate this
48
+ license at any time if you fail to comply with any term of this agreement.
49
+ Upon termination, you must destroy all copies of the Software in your
50
+ possession.
51
+
52
+ For licensing inquiries, contact: licensing@prodcycle.com
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # @prodcycle/prodcycle
2
+
3
+ Multi-framework policy-as-code compliance scanner for infrastructure and application code. Scans Terraform, Kubernetes, Docker, `.env`, and application source against SOC 2, HIPAA, and NIST CSF policies.
4
+
5
+ ## Features
6
+
7
+ - **3 compliance frameworks**: SOC 2, HIPAA, NIST CSF
8
+ - **Automated policy enforcement**: Server-side OPA/Rego and Cedar evaluation engines
9
+ - **Infrastructure scanning**: Terraform, Kubernetes manifests, Dockerfiles, `.env` files
10
+ - **Application code scanning**: TypeScript, Python, Go, Java, Ruby
11
+ - **CI/CD integration**: CLI with SARIF output for GitHub Code Scanning
12
+ - **Programmatic API**: Full TypeScript API for custom integrations
13
+ - **Self-remediation**: `gate()` function returns actionable remediation prompts
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install -g @prodcycle/prodcycle
19
+ ```
20
+
21
+ ### GitHub Packages (alternative)
22
+
23
+ ```bash
24
+ echo "@prodcycle:registry=https://npm.pkg.github.com" > .npmrc
25
+ npm login --scope=@prodcycle --registry=https://npm.pkg.github.com
26
+ npm install @prodcycle/prodcycle
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ### CLI
32
+
33
+ ```bash
34
+ # Scan current directory against SOC 2 and HIPAA
35
+ prodcycle . --framework soc2,hipaa
36
+
37
+ # Output as SARIF for GitHub Code Scanning
38
+ prodcycle . --framework soc2 --format sarif --output results.sarif
39
+
40
+ # Set severity threshold (only report HIGH and above)
41
+ prodcycle . --framework hipaa --severity-threshold high
42
+ ```
43
+
44
+ ### Programmatic API
45
+
46
+ ```typescript
47
+ import { scan, gate } from '@prodcycle/prodcycle';
48
+
49
+ // Full Repository Scan
50
+ const { report, findings, exitCode } = await scan({
51
+ repoPath: '/path/to/repo',
52
+ frameworks: ['soc2', 'hipaa'],
53
+ options: {
54
+ severityThreshold: 'high',
55
+ failOn: ['critical', 'high'],
56
+ },
57
+ });
58
+
59
+ console.log(`Found ${findings.length} findings`);
60
+ console.log(`Exit code: ${exitCode}`);
61
+
62
+ // Gate function (for coding agents)
63
+ const result = await gate({
64
+ files: {
65
+ 'src/config.ts': 'export const DB_PASSWORD = "hardcoded-secret";',
66
+ 'terraform/main.tf': 'resource "aws_s3_bucket" "data" { }',
67
+ },
68
+ frameworks: ['soc2', 'hipaa'],
69
+ });
70
+
71
+ if (!result.passed) {
72
+ console.log('Compliance issues found:');
73
+ console.log(result.prompt); // Pre-formatted remediation instructions
74
+ }
75
+ ```
76
+
77
+ ## API Key
78
+
79
+ An API key is required for production use to authenticate with ProdCycle. Set it via environment variable:
80
+
81
+ ```bash
82
+ export PC_API_KEY=pc_your_api_key_here
83
+ ```
84
+
85
+ API keys are created through the ProdCycle dashboard.
86
+
87
+ ## Requirements
88
+
89
+ - Node.js >= 24.0.0
90
+
91
+ ## License
92
+
93
+ MIT
@@ -0,0 +1,26 @@
1
+ export interface ScanOptions {
2
+ severityThreshold?: 'low' | 'medium' | 'high' | 'critical';
3
+ failOn?: ('low' | 'medium' | 'high' | 'critical')[];
4
+ include?: string[];
5
+ exclude?: string[];
6
+ apiKey?: string;
7
+ apiUrl?: string;
8
+ config?: Record<string, unknown>;
9
+ }
10
+ export interface GateOptions {
11
+ files: Record<string, string>;
12
+ frameworks?: string[];
13
+ severityThreshold?: 'low' | 'medium' | 'high' | 'critical';
14
+ failOn?: ('low' | 'medium' | 'high' | 'critical')[];
15
+ apiKey?: string;
16
+ apiUrl?: string;
17
+ config?: Record<string, unknown>;
18
+ }
19
+ export declare class ComplianceApiClient {
20
+ private apiUrl;
21
+ private apiKey;
22
+ constructor(apiUrl?: string, apiKey?: string);
23
+ validate(files: Record<string, string>, frameworks: string[], options?: ScanOptions): Promise<any>;
24
+ hook(files: Record<string, string>, frameworks: string[]): Promise<any>;
25
+ private post;
26
+ }
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ComplianceApiClient = void 0;
4
+ class ComplianceApiClient {
5
+ apiUrl;
6
+ apiKey;
7
+ constructor(apiUrl, apiKey) {
8
+ this.apiUrl = apiUrl || process.env.PC_API_URL || 'https://api.prodcycle.com';
9
+ this.apiKey = apiKey || process.env.PC_API_KEY || '';
10
+ if (!this.apiKey && process.env.NODE_ENV !== 'test') {
11
+ console.warn('Warning: PC_API_KEY is not set. API calls will likely fail.');
12
+ }
13
+ }
14
+ async validate(files, frameworks, options = {}) {
15
+ return this.post('/v1/compliance/validate', {
16
+ files,
17
+ frameworks,
18
+ options: {
19
+ severity_threshold: options.severityThreshold,
20
+ fail_on: options.failOn,
21
+ ...options.config,
22
+ },
23
+ });
24
+ }
25
+ async hook(files, frameworks) {
26
+ return this.post('/v1/compliance/hook', {
27
+ files,
28
+ frameworks,
29
+ });
30
+ }
31
+ async post(endpoint, data) {
32
+ const url = `${this.apiUrl}${endpoint}`;
33
+ try {
34
+ const response = await fetch(url, {
35
+ method: 'POST',
36
+ headers: {
37
+ 'Authorization': `Bearer ${this.apiKey}`,
38
+ 'Content-Type': 'application/json',
39
+ },
40
+ body: JSON.stringify(data),
41
+ });
42
+ const responseData = await response.json();
43
+ if (!response.ok) {
44
+ throw new Error(responseData.error?.message || `API request failed with status ${response.status}`);
45
+ }
46
+ return responseData;
47
+ }
48
+ catch (error) {
49
+ throw new Error(`Failed to connect to ProdCycle API: ${error.message}`);
50
+ }
51
+ }
52
+ }
53
+ exports.ComplianceApiClient = ComplianceApiClient;
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const commander_1 = require("commander");
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const index_1 = require("./index");
41
+ const program = new commander_1.Command();
42
+ program
43
+ .name('prodcycle')
44
+ .description('Multi-framework policy-as-code compliance scanner for infrastructure and application code.')
45
+ .version('0.1.0')
46
+ .argument('[repo_path]', 'Path to the repository to scan', '.')
47
+ .option('--framework <ids>', 'Comma-separated framework IDs to evaluate', 'soc2')
48
+ .option('--format <format>', 'Output format: json, sarif, table, prompt', 'table')
49
+ .option('--severity-threshold <severity>', 'Minimum severity to include in report', 'low')
50
+ .option('--fail-on <levels>', 'Comma-separated severities that cause non-zero exit', 'critical,high')
51
+ .option('--include <patterns>', 'Comma-separated glob patterns to include')
52
+ .option('--exclude <patterns>', 'Comma-separated glob patterns to exclude')
53
+ .option('--output <file>', 'Write report to file')
54
+ .option('--api-url <url>', 'Compliance API base URL (or PC_API_URL env)')
55
+ .option('--api-key <key>', 'API key for compliance API (or PC_API_KEY env)')
56
+ .option('--hook', 'Run as coding agent post-edit hook (reads stdin)')
57
+ .option('--hook-file <path>', 'File path for hook mode (alternative to stdin)')
58
+ .option('--hook-api', 'Run as API-based hook (calls hosted compliance API)')
59
+ .option('--init', 'Set up compliance hooks for coding agents')
60
+ .option('--agent <agents>', 'Comma-separated agents to configure')
61
+ .action(async (repoPath, opts) => {
62
+ try {
63
+ if (opts.hook || opts.hookApi) {
64
+ // Implement hook logic here
65
+ console.log('Hook mode executed.');
66
+ process.exit(0);
67
+ }
68
+ if (opts.init) {
69
+ // Implement init logic here
70
+ console.log('Init mode executed.');
71
+ process.exit(0);
72
+ }
73
+ const frameworks = opts.framework.split(',').map((s) => s.trim());
74
+ const failOn = opts.failOn.split(',').map((s) => s.trim());
75
+ const include = opts.include ? opts.include.split(',') : undefined;
76
+ const exclude = opts.exclude ? opts.exclude.split(',') : undefined;
77
+ console.log(`Scanning ${path.resolve(repoPath)} for ${frameworks.join(', ')}...`);
78
+ const response = await (0, index_1.scan)({
79
+ repoPath,
80
+ frameworks,
81
+ options: {
82
+ severityThreshold: opts.severityThreshold,
83
+ failOn,
84
+ include,
85
+ exclude,
86
+ apiUrl: opts.apiUrl,
87
+ apiKey: opts.apiKey,
88
+ }
89
+ });
90
+ if (opts.format === 'json') {
91
+ const output = JSON.stringify(response, null, 2);
92
+ if (opts.output) {
93
+ fs.writeFileSync(opts.output, output);
94
+ }
95
+ else {
96
+ console.log(output);
97
+ }
98
+ }
99
+ else {
100
+ console.log(`Passed: ${response.passed}`);
101
+ console.log(`Findings: ${response.findings.length}`);
102
+ }
103
+ process.exit(response.exitCode);
104
+ }
105
+ catch (error) {
106
+ console.error(`\u2717 Error: ${error.message}`);
107
+ process.exit(2);
108
+ }
109
+ });
110
+ program.parse();
@@ -0,0 +1 @@
1
+ export declare function formatPrompt(report: any): string;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatPrompt = formatPrompt;
4
+ function formatPrompt(report) {
5
+ if (!report)
6
+ return '';
7
+ return 'Please fix the compliance issues found in this repository.';
8
+ }
@@ -0,0 +1 @@
1
+ export declare function formatSarif(report: any): any;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatSarif = formatSarif;
4
+ function formatSarif(report) {
5
+ return {
6
+ version: '2.1.0',
7
+ runs: [{ tool: { driver: { name: 'ProdCycle Compliance Scanner' } }, results: [] }]
8
+ };
9
+ }
@@ -0,0 +1 @@
1
+ export declare function formatTable(report: any): string;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatTable = formatTable;
4
+ function formatTable(report) {
5
+ // Simplistic table formatter
6
+ if (!report)
7
+ return 'No report data';
8
+ return `Scan Results: ${report.summary?.passed || 0} passed, ${report.summary?.failed || 0} failed.`;
9
+ }
@@ -0,0 +1,50 @@
1
+ import { ScanOptions, GateOptions } from './api-client';
2
+ export * from './api-client';
3
+ export * from './formatters/table';
4
+ export * from './formatters/prompt';
5
+ export * from './formatters/sarif';
6
+ /**
7
+ * Scan a repository by collecting files and sending them to the API
8
+ */
9
+ export declare function scan(params: {
10
+ repoPath: string;
11
+ frameworks?: string[];
12
+ options?: ScanOptions;
13
+ }): Promise<{
14
+ passed: boolean;
15
+ exitCode: number;
16
+ findings: never[];
17
+ report: null;
18
+ summary?: undefined;
19
+ } | {
20
+ passed: any;
21
+ exitCode: number;
22
+ findings: any;
23
+ report: any;
24
+ summary: any;
25
+ }>;
26
+ /**
27
+ * Gate code strings directly without writing to disk
28
+ */
29
+ export declare function gate(options: GateOptions): Promise<{
30
+ passed: any;
31
+ exitCode: number;
32
+ findings: any;
33
+ prompt: any;
34
+ summary: any;
35
+ }>;
36
+ /**
37
+ * Run local hook
38
+ */
39
+ export declare function runHook(params: {
40
+ frameworks?: string[];
41
+ filePath?: string;
42
+ }): Promise<number>;
43
+ /**
44
+ * Run API hook
45
+ */
46
+ export declare function runHookApi(params: {
47
+ apiUrl?: string;
48
+ apiKey?: string;
49
+ frameworks?: string[];
50
+ }): Promise<number>;
package/dist/index.js ADDED
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.scan = scan;
18
+ exports.gate = gate;
19
+ exports.runHook = runHook;
20
+ exports.runHookApi = runHookApi;
21
+ const api_client_1 = require("./api-client");
22
+ const fs_1 = require("./utils/fs");
23
+ __exportStar(require("./api-client"), exports);
24
+ __exportStar(require("./formatters/table"), exports);
25
+ __exportStar(require("./formatters/prompt"), exports);
26
+ __exportStar(require("./formatters/sarif"), exports);
27
+ /**
28
+ * Scan a repository by collecting files and sending them to the API
29
+ */
30
+ async function scan(params) {
31
+ const { repoPath, frameworks = ['soc2'], options = {} } = params;
32
+ // Collect files
33
+ const files = await (0, fs_1.collectFiles)(repoPath, options.include, options.exclude);
34
+ if (Object.keys(files).length === 0) {
35
+ return {
36
+ passed: true,
37
+ exitCode: 0,
38
+ findings: [],
39
+ report: null
40
+ };
41
+ }
42
+ const client = new api_client_1.ComplianceApiClient(options.apiUrl, options.apiKey);
43
+ const response = await client.validate(files, frameworks, options);
44
+ return {
45
+ passed: response.passed,
46
+ exitCode: response.passed ? 0 : 1,
47
+ findings: response.findings || [],
48
+ report: response.report, // The API should return the full report object if requested, or we synthesize it
49
+ summary: response.summary
50
+ };
51
+ }
52
+ /**
53
+ * Gate code strings directly without writing to disk
54
+ */
55
+ async function gate(options) {
56
+ const { files, frameworks = ['soc2'], ...scanOpts } = options;
57
+ const client = new api_client_1.ComplianceApiClient(options.apiUrl, options.apiKey);
58
+ const response = await client.hook(files, frameworks);
59
+ return {
60
+ passed: response.passed,
61
+ exitCode: response.passed ? 0 : 1,
62
+ findings: response.findings || [],
63
+ prompt: response.prompt,
64
+ summary: response.summary
65
+ };
66
+ }
67
+ /**
68
+ * Run local hook
69
+ */
70
+ async function runHook(params) {
71
+ // Logic to read stdin or specific file and call gate
72
+ const { frameworks = ['soc2'], filePath } = params;
73
+ // Implementation details...
74
+ return 0;
75
+ }
76
+ /**
77
+ * Run API hook
78
+ */
79
+ async function runHookApi(params) {
80
+ // Implementation details...
81
+ return 0;
82
+ }
@@ -0,0 +1 @@
1
+ export declare function collectFiles(baseDir: string, includePatterns?: string[], excludePatterns?: string[]): Promise<Record<string, string>>;
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.collectFiles = collectFiles;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const glob_1 = require("glob");
40
+ const MAX_FILE_SIZE = 256 * 1024; // 256 KB
41
+ const MAX_TOTAL_FILES = 500;
42
+ async function collectFiles(baseDir, includePatterns, excludePatterns) {
43
+ // Simple implementation using glob
44
+ const patterns = includePatterns && includePatterns.length > 0 ? includePatterns : ['**/*'];
45
+ const ignore = ['node_modules/**', '.git/**', '.terraform/**', 'dist/**', 'build/**', '**/__pycache__/**'];
46
+ if (excludePatterns && excludePatterns.length > 0) {
47
+ ignore.push(...excludePatterns);
48
+ }
49
+ const matches = await (0, glob_1.glob)(patterns, {
50
+ cwd: baseDir,
51
+ ignore,
52
+ nodir: true,
53
+ });
54
+ const files = {};
55
+ let count = 0;
56
+ for (const match of matches) {
57
+ if (count >= MAX_TOTAL_FILES) {
58
+ console.warn(`Reached max file limit (${MAX_TOTAL_FILES}). Some files were skipped.`);
59
+ break;
60
+ }
61
+ const fullPath = path.join(baseDir, match);
62
+ const stats = fs.statSync(fullPath);
63
+ // Skip large files
64
+ if (stats.size > MAX_FILE_SIZE) {
65
+ continue;
66
+ }
67
+ // Basic heuristic to skip binary files
68
+ const buffer = fs.readFileSync(fullPath);
69
+ if (isBinary(buffer)) {
70
+ continue;
71
+ }
72
+ files[match] = buffer.toString('utf8');
73
+ count++;
74
+ }
75
+ return files;
76
+ }
77
+ function isBinary(buffer) {
78
+ for (let i = 0; i < Math.min(buffer.length, 1024); i++) {
79
+ if (buffer[i] === 0)
80
+ return true;
81
+ }
82
+ return false;
83
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@prodcycle/prodcycle",
3
+ "version": "0.1.5",
4
+ "description": "Multi-framework policy-as-code compliance scanner for infrastructure and application code.",
5
+ "homepage": "https://docs.prodcycle.com",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/prodcycle/prodcycle-cli.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/prodcycle/prodcycle-cli/issues"
12
+ },
13
+ "main": "dist/index.js",
14
+ "types": "dist/index.d.ts",
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "bin": {
19
+ "prodcycle": "dist/cli.js"
20
+ },
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "prepublishOnly": "npm run build"
24
+ },
25
+ "keywords": [
26
+ "compliance",
27
+ "soc2",
28
+ "hipaa",
29
+ "nist",
30
+ "cli",
31
+ "security"
32
+ ],
33
+ "author": "ProdCycle, Inc. <engineering@prodcycle.com>",
34
+ "license": "SEE LICENSE IN LICENSE",
35
+ "dependencies": {
36
+ "commander": "^12.0.0",
37
+ "glob": "^10.3.10",
38
+ "ignore": "^5.3.1"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^22.0.0",
42
+ "typescript": "^5.4.0"
43
+ }
44
+ }