decision-guardian 1.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.
- package/LICENSE +21 -0
- package/README.md +792 -0
- package/dist/adapters/github/actions-logger.js +88 -0
- package/dist/adapters/github/comment.js +601 -0
- package/dist/adapters/github/github-provider.js +260 -0
- package/dist/adapters/github/health.js +56 -0
- package/dist/adapters/local/console-logger.js +46 -0
- package/dist/adapters/local/local-git-provider.js +247 -0
- package/dist/cli/commands/check.js +134 -0
- package/dist/cli/commands/init.js +58 -0
- package/dist/cli/commands/template.js +70 -0
- package/dist/cli/formatter.js +68 -0
- package/dist/cli/index.js +12458 -0
- package/dist/cli/licenses.txt +143 -0
- package/dist/cli/paths.js +40 -0
- package/dist/core/content-matchers.js +333 -0
- package/dist/core/health.js +52 -0
- package/dist/core/interfaces/index.js +2 -0
- package/dist/core/interfaces/logger.js +2 -0
- package/dist/core/interfaces/scm-provider.js +5 -0
- package/dist/core/logger.js +20 -0
- package/dist/core/matcher.js +184 -0
- package/dist/core/metrics.js +87 -0
- package/dist/core/parser.js +338 -0
- package/dist/core/rule-evaluator.js +186 -0
- package/dist/core/rule-parser.js +211 -0
- package/dist/core/rule-types.js +22 -0
- package/dist/core/trie.js +83 -0
- package/dist/core/types.js +2 -0
- package/dist/index.js +61142 -0
- package/dist/licenses.txt +758 -0
- package/dist/main.js +290 -0
- package/dist/telemetry/payload.js +25 -0
- package/dist/telemetry/privacy.js +37 -0
- package/dist/telemetry/sender.js +40 -0
- package/dist/version.js +7 -0
- package/package.json +60 -0
- package/templates/advanced-rules.md +94 -0
- package/templates/api.md +70 -0
- package/templates/basic.md +38 -0
- package/templates/database.md +81 -0
- package/templates/security.md +89 -0
|
@@ -0,0 +1,134 @@
|
|
|
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.runCheck = runCheck;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const parser_1 = require("../../core/parser");
|
|
40
|
+
const matcher_1 = require("../../core/matcher");
|
|
41
|
+
const console_logger_1 = require("../../adapters/local/console-logger");
|
|
42
|
+
const local_git_provider_1 = require("../../adapters/local/local-git-provider");
|
|
43
|
+
const metrics_1 = require("../../core/metrics");
|
|
44
|
+
const formatter_1 = require("../formatter");
|
|
45
|
+
const sender_1 = require("../../telemetry/sender");
|
|
46
|
+
const version_1 = require("../../version");
|
|
47
|
+
async function runCheck(opts) {
|
|
48
|
+
const logger = new console_logger_1.ConsoleLogger();
|
|
49
|
+
const startTime = Date.now();
|
|
50
|
+
try {
|
|
51
|
+
const decisionPath = path.resolve(opts.decisionFile);
|
|
52
|
+
const isDir = fs.existsSync(decisionPath) && fs.statSync(decisionPath).isDirectory();
|
|
53
|
+
const parser = new parser_1.DecisionParser();
|
|
54
|
+
let parseResult;
|
|
55
|
+
if (isDir) {
|
|
56
|
+
logger.info(`Scanning directory: ${decisionPath}`);
|
|
57
|
+
parseResult = await parser.parseDirectory(decisionPath);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
if (!fs.existsSync(decisionPath)) {
|
|
61
|
+
logger.error(`Decision file not found: ${decisionPath}`);
|
|
62
|
+
logger.info('Run "decision-guardian init" to create one.');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
logger.info(`Checking: ${decisionPath}`);
|
|
66
|
+
parseResult = await parser.parseFile(decisionPath);
|
|
67
|
+
}
|
|
68
|
+
if (parseResult.warnings.length > 0) {
|
|
69
|
+
parseResult.warnings.forEach((w) => logger.warning(w));
|
|
70
|
+
}
|
|
71
|
+
if (parseResult.errors.length > 0) {
|
|
72
|
+
parseResult.errors.forEach((e) => logger.error(`Line ${e.line}: ${e.message}`));
|
|
73
|
+
if (opts.failOnError) {
|
|
74
|
+
logger.error(`Check failed: ${parseResult.errors.length} parse errors found (fail-on-error enabled)`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (parseResult.decisions.length === 0) {
|
|
79
|
+
logger.warning('No decisions found in the specified path.');
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
logger.info(`Found ${parseResult.decisions.length} decisions`);
|
|
83
|
+
const gitConfig = {
|
|
84
|
+
mode: opts.mode,
|
|
85
|
+
baseBranch: opts.baseBranch,
|
|
86
|
+
cwd: process.cwd(),
|
|
87
|
+
};
|
|
88
|
+
const provider = new local_git_provider_1.LocalGitProvider(gitConfig);
|
|
89
|
+
const fileDiffs = await provider.getFileDiffs();
|
|
90
|
+
metrics_1.metrics.addFilesProcessed(fileDiffs.length);
|
|
91
|
+
if (fileDiffs.length === 0) {
|
|
92
|
+
logger.info('No changed files detected.');
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|
|
95
|
+
logger.info(`${fileDiffs.length} files changed`);
|
|
96
|
+
const matcher = new matcher_1.FileMatcher(parseResult.decisions, logger);
|
|
97
|
+
let matches;
|
|
98
|
+
try {
|
|
99
|
+
matches = await matcher.findMatchesWithDiffs(fileDiffs);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
const fileNames = fileDiffs.map((f) => f.filename);
|
|
103
|
+
matches = await matcher.findMatches(fileNames);
|
|
104
|
+
}
|
|
105
|
+
metrics_1.metrics.addMatchesFound(matches.length);
|
|
106
|
+
metrics_1.metrics.setDuration(Date.now() - startTime);
|
|
107
|
+
const grouped = matcher.groupBySeverity(matches);
|
|
108
|
+
metrics_1.metrics.addCriticalMatches(grouped.critical.length);
|
|
109
|
+
metrics_1.metrics.addWarningMatches(grouped.warning.length);
|
|
110
|
+
metrics_1.metrics.addInfoMatches(grouped.info.length);
|
|
111
|
+
console.log((0, formatter_1.formatMatchesTable)(matches));
|
|
112
|
+
const snapshot = metrics_1.metrics.getSnapshot();
|
|
113
|
+
console.log((0, formatter_1.formatSummary)({
|
|
114
|
+
filesProcessed: snapshot.files_processed,
|
|
115
|
+
decisionsEvaluated: parseResult.decisions.length,
|
|
116
|
+
matchesFound: snapshot.matches_found,
|
|
117
|
+
critical: grouped.critical.length,
|
|
118
|
+
warning: grouped.warning.length,
|
|
119
|
+
info: grouped.info.length,
|
|
120
|
+
durationMs: snapshot.duration_ms,
|
|
121
|
+
}));
|
|
122
|
+
(0, sender_1.sendTelemetry)('cli', snapshot, version_1.VERSION).catch(() => { });
|
|
123
|
+
if (opts.failOnCritical && grouped.critical.length > 0) {
|
|
124
|
+
logger.error(`${grouped.critical.length} critical violations found`);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
process.exit(0);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
131
|
+
logger.error(`Check failed: ${message}`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
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.runInit = runInit;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const paths_1 = require("../paths");
|
|
40
|
+
function runInit(templateName) {
|
|
41
|
+
const targetDir = path.resolve('.decispher');
|
|
42
|
+
const targetFile = path.join(targetDir, 'decisions.md');
|
|
43
|
+
if (fs.existsSync(targetFile)) {
|
|
44
|
+
console.log(`\x1b[33m⚠\x1b[0m ${targetFile} already exists. Skipping.`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const templatePath = path.join((0, paths_1.getTemplatesDir)(), `${templateName}.md`);
|
|
48
|
+
if (!fs.existsSync(templatePath)) {
|
|
49
|
+
console.error(`\x1b[31m✗\x1b[0m Template "${templateName}" not found.`);
|
|
50
|
+
console.log('Available: basic, advanced-rules, security, database, api');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
54
|
+
fs.copyFileSync(templatePath, targetFile);
|
|
55
|
+
console.log(`\x1b[32m✔\x1b[0m Created ${targetFile}`);
|
|
56
|
+
console.log(` Template: ${templateName}`);
|
|
57
|
+
console.log(`\n Edit the file to define your architectural decisions.`);
|
|
58
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
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.runTemplate = runTemplate;
|
|
37
|
+
exports.listTemplates = listTemplates;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const paths_1 = require("../paths");
|
|
41
|
+
const AVAILABLE = ['basic', 'advanced-rules', 'security', 'database', 'api'];
|
|
42
|
+
function runTemplate(name, outputPath) {
|
|
43
|
+
if (!AVAILABLE.includes(name)) {
|
|
44
|
+
console.error(`\x1b[31m✗\x1b[0m Unknown template: "${name}"`);
|
|
45
|
+
listTemplates();
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
const templatePath = path.join((0, paths_1.getTemplatesDir)(), `${name}.md`);
|
|
49
|
+
if (!fs.existsSync(templatePath)) {
|
|
50
|
+
console.error(`\x1b[31m✗\x1b[0m Template file missing: ${templatePath}`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
const content = fs.readFileSync(templatePath, 'utf-8');
|
|
54
|
+
if (outputPath) {
|
|
55
|
+
const resolved = path.resolve(outputPath);
|
|
56
|
+
fs.mkdirSync(path.dirname(resolved), { recursive: true });
|
|
57
|
+
fs.writeFileSync(resolved, content, 'utf-8');
|
|
58
|
+
console.log(`\x1b[32m✔\x1b[0m Written to ${resolved}`);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
console.log(content);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function listTemplates() {
|
|
65
|
+
console.log('\nAvailable templates:');
|
|
66
|
+
for (const name of AVAILABLE) {
|
|
67
|
+
console.log(` • ${name}`);
|
|
68
|
+
}
|
|
69
|
+
console.log('');
|
|
70
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatMatchesTable = formatMatchesTable;
|
|
4
|
+
exports.formatSummary = formatSummary;
|
|
5
|
+
const RESET = '\x1b[0m';
|
|
6
|
+
const BOLD = '\x1b[1m';
|
|
7
|
+
const RED = '\x1b[31m';
|
|
8
|
+
const YELLOW = '\x1b[33m';
|
|
9
|
+
const GREEN = '\x1b[32m';
|
|
10
|
+
const CYAN = '\x1b[36m';
|
|
11
|
+
const GRAY = '\x1b[90m';
|
|
12
|
+
const DIM = '\x1b[2m';
|
|
13
|
+
const SEVERITY_ICON = {
|
|
14
|
+
critical: `${RED}●${RESET}`,
|
|
15
|
+
warning: `${YELLOW}●${RESET}`,
|
|
16
|
+
info: `${CYAN}●${RESET}`,
|
|
17
|
+
};
|
|
18
|
+
function formatMatchesTable(matches) {
|
|
19
|
+
if (matches.length === 0) {
|
|
20
|
+
return `\n ${GREEN}✔${RESET} No decision violations found.\n`;
|
|
21
|
+
}
|
|
22
|
+
const lines = [];
|
|
23
|
+
lines.push('');
|
|
24
|
+
const grouped = groupBySeverity(matches);
|
|
25
|
+
if (grouped.critical.length > 0) {
|
|
26
|
+
lines.push(` ${RED}${BOLD}Critical (${grouped.critical.length})${RESET}`);
|
|
27
|
+
for (const m of grouped.critical)
|
|
28
|
+
lines.push(formatRow(m));
|
|
29
|
+
lines.push('');
|
|
30
|
+
}
|
|
31
|
+
if (grouped.warning.length > 0) {
|
|
32
|
+
lines.push(` ${YELLOW}${BOLD}Warning (${grouped.warning.length})${RESET}`);
|
|
33
|
+
for (const m of grouped.warning)
|
|
34
|
+
lines.push(formatRow(m));
|
|
35
|
+
lines.push('');
|
|
36
|
+
}
|
|
37
|
+
if (grouped.info.length > 0) {
|
|
38
|
+
lines.push(` ${CYAN}${BOLD}Info (${grouped.info.length})${RESET}`);
|
|
39
|
+
for (const m of grouped.info)
|
|
40
|
+
lines.push(formatRow(m));
|
|
41
|
+
lines.push('');
|
|
42
|
+
}
|
|
43
|
+
return lines.join('\n');
|
|
44
|
+
}
|
|
45
|
+
function formatRow(match) {
|
|
46
|
+
const icon = SEVERITY_ICON[match.decision.severity] || SEVERITY_ICON.info;
|
|
47
|
+
const id = `${BOLD}${match.decision.id}${RESET}`;
|
|
48
|
+
const file = `${DIM}${match.file}${RESET}`;
|
|
49
|
+
const pattern = `${GRAY}${match.matchedPattern}${RESET}`;
|
|
50
|
+
return ` ${icon} ${id} ${file} ${pattern}`;
|
|
51
|
+
}
|
|
52
|
+
function formatSummary(stats) {
|
|
53
|
+
const lines = [];
|
|
54
|
+
lines.push(` ${GRAY}─────────────────────────────${RESET}`);
|
|
55
|
+
lines.push(` Files scanned: ${BOLD}${stats.filesProcessed}${RESET}`);
|
|
56
|
+
lines.push(` Decisions checked:${BOLD} ${stats.decisionsEvaluated}${RESET}`);
|
|
57
|
+
lines.push(` Matches: ${BOLD}${stats.matchesFound}${RESET} ${GRAY}(${stats.critical} critical, ${stats.warning} warning, ${stats.info} info)${RESET}`);
|
|
58
|
+
lines.push(` Duration: ${GRAY}${stats.durationMs}ms${RESET}`);
|
|
59
|
+
lines.push('');
|
|
60
|
+
return lines.join('\n');
|
|
61
|
+
}
|
|
62
|
+
function groupBySeverity(matches) {
|
|
63
|
+
return {
|
|
64
|
+
critical: matches.filter((m) => m.decision.severity === 'critical'),
|
|
65
|
+
warning: matches.filter((m) => m.decision.severity === 'warning'),
|
|
66
|
+
info: matches.filter((m) => m.decision.severity === 'info'),
|
|
67
|
+
};
|
|
68
|
+
}
|