delimit-cli 3.11.9 → 3.11.10
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/adapters/codex-security.js +64 -0
- package/adapters/codex-skill.js +78 -0
- package/adapters/cursor-rules.js +97 -0
- package/bin/delimit-setup.js +11 -1
- package/package.json +2 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Delimit Security Skill for Codex CLI
|
|
4
|
+
*
|
|
5
|
+
* Validates that Codex-generated code doesn't introduce security anti-patterns.
|
|
6
|
+
* Runs as a security validation skill.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const SECURITY_PATTERNS = [
|
|
13
|
+
{ pattern: /eval\s*\(/g, severity: 'high', message: 'eval() usage detected — potential code injection' },
|
|
14
|
+
{ pattern: /exec\s*\(/g, severity: 'medium', message: 'exec() usage — verify input sanitization' },
|
|
15
|
+
{ pattern: /shell\s*=\s*True/g, severity: 'high', message: 'subprocess with shell=True — command injection risk' },
|
|
16
|
+
{ pattern: /dangerouslySetInnerHTML/g, severity: 'medium', message: 'dangerouslySetInnerHTML — XSS risk' },
|
|
17
|
+
{ pattern: /password\s*=\s*["'][^"']+["']/gi, severity: 'high', message: 'Hardcoded password detected' },
|
|
18
|
+
{ pattern: /api[_-]?key\s*=\s*["'][A-Za-z0-9]{10,}["']/gi, severity: 'high', message: 'Hardcoded API key detected' },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
function checkSecurity(context) {
|
|
22
|
+
const code = context.code || context.content || '';
|
|
23
|
+
if (!code) return { status: 'clean', findings: [] };
|
|
24
|
+
|
|
25
|
+
const findings = [];
|
|
26
|
+
for (const { pattern, severity, message } of SECURITY_PATTERNS) {
|
|
27
|
+
const matches = code.match(pattern);
|
|
28
|
+
if (matches) {
|
|
29
|
+
findings.push({ severity, message, count: matches.length });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Audit
|
|
34
|
+
try {
|
|
35
|
+
const auditDir = path.join(process.env.HOME || '', '.delimit', 'audit');
|
|
36
|
+
fs.mkdirSync(auditDir, { recursive: true });
|
|
37
|
+
const record = {
|
|
38
|
+
timestamp: new Date().toISOString(),
|
|
39
|
+
source: 'codex-security',
|
|
40
|
+
findings_count: findings.length,
|
|
41
|
+
high_count: findings.filter(f => f.severity === 'high').length,
|
|
42
|
+
};
|
|
43
|
+
const auditFile = path.join(auditDir, `${new Date().toISOString().split('T')[0]}.jsonl`);
|
|
44
|
+
fs.appendFileSync(auditFile, JSON.stringify(record) + '\n');
|
|
45
|
+
} catch {}
|
|
46
|
+
|
|
47
|
+
const hasHigh = findings.some(f => f.severity === 'high');
|
|
48
|
+
return {
|
|
49
|
+
status: hasHigh ? 'flagged' : findings.length > 0 ? 'warnings' : 'clean',
|
|
50
|
+
findings,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const context = process.argv[2] ? JSON.parse(process.argv[2]) : {};
|
|
55
|
+
const result = checkSecurity(context);
|
|
56
|
+
|
|
57
|
+
if (result.status === 'flagged') {
|
|
58
|
+
for (const f of result.findings) {
|
|
59
|
+
console.error(`[Delimit Security] ${f.severity.toUpperCase()}: ${f.message}`);
|
|
60
|
+
}
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
process.exit(0);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Delimit Governance Skill for Codex CLI
|
|
4
|
+
*
|
|
5
|
+
* Runs as a validation skill triggered on pre-code-generation and pre-suggestion.
|
|
6
|
+
* Checks governance state and policy compliance before Codex executes actions.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const DELIMIT_HOME = path.join(process.env.HOME || '', '.delimit');
|
|
13
|
+
const MODE_FILE = path.join(DELIMIT_HOME, 'enforcement_mode');
|
|
14
|
+
|
|
15
|
+
function getMode() {
|
|
16
|
+
try {
|
|
17
|
+
return fs.readFileSync(MODE_FILE, 'utf-8').trim();
|
|
18
|
+
} catch {
|
|
19
|
+
return 'guarded'; // Default
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function checkGovernance(context) {
|
|
24
|
+
const mode = getMode();
|
|
25
|
+
const warnings = [];
|
|
26
|
+
|
|
27
|
+
// Check if governance is initialized
|
|
28
|
+
const policiesFile = path.join(process.cwd(), '.delimit', 'policies.yml');
|
|
29
|
+
if (!fs.existsSync(policiesFile)) {
|
|
30
|
+
warnings.push('No .delimit/policies.yml — run: delimit init');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check for sensitive file access
|
|
34
|
+
const sensitivePatterns = ['.env', 'credentials', '.ssh', 'secrets'];
|
|
35
|
+
const target = context.target || context.file || '';
|
|
36
|
+
for (const pattern of sensitivePatterns) {
|
|
37
|
+
if (target.includes(pattern)) {
|
|
38
|
+
if (mode === 'enforce') {
|
|
39
|
+
return { status: 'blocked', reason: `Access to sensitive path: ${target}` };
|
|
40
|
+
}
|
|
41
|
+
warnings.push(`Accessing sensitive path: ${target}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Audit log
|
|
46
|
+
try {
|
|
47
|
+
const auditDir = path.join(DELIMIT_HOME, 'audit');
|
|
48
|
+
fs.mkdirSync(auditDir, { recursive: true });
|
|
49
|
+
const record = {
|
|
50
|
+
timestamp: new Date().toISOString(),
|
|
51
|
+
source: 'codex-skill',
|
|
52
|
+
mode,
|
|
53
|
+
context: typeof context === 'object' ? JSON.stringify(context).slice(0, 200) : String(context).slice(0, 200),
|
|
54
|
+
warnings,
|
|
55
|
+
};
|
|
56
|
+
const auditFile = path.join(auditDir, `${new Date().toISOString().split('T')[0]}.jsonl`);
|
|
57
|
+
fs.appendFileSync(auditFile, JSON.stringify(record) + '\n');
|
|
58
|
+
} catch {}
|
|
59
|
+
|
|
60
|
+
return { status: 'allowed', mode, warnings };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Entry point — read context from stdin or args
|
|
64
|
+
const context = process.argv[2] ? JSON.parse(process.argv[2]) : {};
|
|
65
|
+
const result = checkGovernance(context);
|
|
66
|
+
|
|
67
|
+
if (result.status === 'blocked') {
|
|
68
|
+
console.error(`[Delimit] BLOCKED: ${result.reason}`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (result.warnings.length > 0) {
|
|
73
|
+
for (const w of result.warnings) {
|
|
74
|
+
console.error(`[Delimit] Warning: ${w}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
process.exit(0);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Delimit Governance Rules for Cursor
|
|
4
|
+
*
|
|
5
|
+
* Cursor doesn't have a hook system like Claude Code or Codex,
|
|
6
|
+
* so governance enforcement happens server-side via MCP tool calls.
|
|
7
|
+
* This adapter manages the .cursorrules and .cursor/rules/ files
|
|
8
|
+
* that guide Cursor's behavior.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
const HOME = process.env.HOME || '';
|
|
15
|
+
const CURSOR_DIR = path.join(HOME, '.cursor');
|
|
16
|
+
const CURSOR_RULES_DIR = path.join(CURSOR_DIR, 'rules');
|
|
17
|
+
const CURSORRULES_FILE = path.join(HOME, '.cursorrules');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Install Delimit governance rules into Cursor.
|
|
21
|
+
* Creates both .cursorrules (legacy) and .cursor/rules/delimit.md (new).
|
|
22
|
+
*/
|
|
23
|
+
function installRules(version) {
|
|
24
|
+
const rules = getDelimitRules(version);
|
|
25
|
+
|
|
26
|
+
// Install to .cursor/rules/delimit.md (new location, Cursor 0.45+)
|
|
27
|
+
if (fs.existsSync(CURSOR_DIR)) {
|
|
28
|
+
fs.mkdirSync(CURSOR_RULES_DIR, { recursive: true });
|
|
29
|
+
const rulesFile = path.join(CURSOR_RULES_DIR, 'delimit.md');
|
|
30
|
+
fs.writeFileSync(rulesFile, rules);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return { installed: true, paths: [CURSORRULES_FILE, path.join(CURSOR_RULES_DIR, 'delimit.md')] };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Remove Delimit rules from Cursor.
|
|
38
|
+
*/
|
|
39
|
+
function uninstallRules() {
|
|
40
|
+
const removed = [];
|
|
41
|
+
|
|
42
|
+
// Remove from .cursor/rules/
|
|
43
|
+
const rulesFile = path.join(CURSOR_RULES_DIR, 'delimit.md');
|
|
44
|
+
if (fs.existsSync(rulesFile)) {
|
|
45
|
+
fs.unlinkSync(rulesFile);
|
|
46
|
+
removed.push(rulesFile);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return { removed };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getDelimitRules(version) {
|
|
53
|
+
return `# Delimit Governance (v${version})
|
|
54
|
+
|
|
55
|
+
## On every session start:
|
|
56
|
+
1. Call \`delimit_ledger_context\` to check for open tasks
|
|
57
|
+
2. Call \`delimit_gov_health\` to check governance status
|
|
58
|
+
|
|
59
|
+
## After editing code:
|
|
60
|
+
- After editing UI/CSS: call \`delimit_design_validate_responsive\`
|
|
61
|
+
- After editing API specs: call \`delimit_lint\`
|
|
62
|
+
- After editing tests: call \`delimit_test_smoke\`
|
|
63
|
+
|
|
64
|
+
## Before deploying:
|
|
65
|
+
1. Call \`delimit_security_audit\`
|
|
66
|
+
2. Call \`delimit_test_smoke\`
|
|
67
|
+
3. Call \`delimit_deploy_plan\`
|
|
68
|
+
|
|
69
|
+
## Before committing:
|
|
70
|
+
- Call \`delimit_repo_diagnose\` to check for common issues
|
|
71
|
+
|
|
72
|
+
## Governance enforcement:
|
|
73
|
+
- All tool calls route through governance automatically (server-side)
|
|
74
|
+
- Critical actions are blocked until approved
|
|
75
|
+
- High-risk actions require approval in enforce mode
|
|
76
|
+
- Check mode: advisory (warn only), guarded (block critical), enforce (block critical + high-risk)
|
|
77
|
+
|
|
78
|
+
## Links
|
|
79
|
+
- Docs: https://delimit.ai/docs
|
|
80
|
+
- GitHub: https://github.com/delimit-ai/delimit-mcp-server
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = { installRules, uninstallRules, getDelimitRules };
|
|
85
|
+
|
|
86
|
+
// CLI entry point
|
|
87
|
+
if (require.main === module) {
|
|
88
|
+
const action = process.argv[2] || 'install';
|
|
89
|
+
const version = process.argv[3] || '3.11.9';
|
|
90
|
+
if (action === 'install') {
|
|
91
|
+
const result = installRules(version);
|
|
92
|
+
console.log(`Installed Delimit rules to Cursor: ${result.paths.join(', ')}`);
|
|
93
|
+
} else if (action === 'uninstall') {
|
|
94
|
+
const result = uninstallRules();
|
|
95
|
+
console.log(`Removed: ${result.removed.join(', ') || 'nothing to remove'}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
package/bin/delimit-setup.js
CHANGED
|
@@ -415,13 +415,23 @@ Run full governance compliance checks. Verify security, policy compliance, evide
|
|
|
415
415
|
}
|
|
416
416
|
}
|
|
417
417
|
|
|
418
|
-
// Cursor rules
|
|
418
|
+
// Cursor rules (legacy .cursorrules + new .cursor/rules/ directory)
|
|
419
419
|
const cursorRules = path.join(os.homedir(), '.cursorrules');
|
|
420
420
|
if (fs.existsSync(path.join(os.homedir(), '.cursor'))) {
|
|
421
421
|
const cursorResult = upsertDelimitSection(cursorRules);
|
|
422
422
|
if (cursorResult.action !== 'unchanged') {
|
|
423
423
|
log(` ${green('✓')} ${cursorResult.action === 'created' ? 'Created' : 'Updated'} ${cursorRules}`);
|
|
424
424
|
}
|
|
425
|
+
// Also install to .cursor/rules/delimit.md (Cursor 0.45+)
|
|
426
|
+
try {
|
|
427
|
+
const cursorRulesDir = path.join(os.homedir(), '.cursor', 'rules');
|
|
428
|
+
fs.mkdirSync(cursorRulesDir, { recursive: true });
|
|
429
|
+
const cursorAdapter = require('../adapters/cursor-rules');
|
|
430
|
+
cursorAdapter.installRules(pkg.version);
|
|
431
|
+
log(` ${green('✓')} Installed governance rules to .cursor/rules/delimit.md`);
|
|
432
|
+
} catch (e) {
|
|
433
|
+
log(` ${dim(' Could not install .cursor/rules: ' + e.message)}`);
|
|
434
|
+
}
|
|
425
435
|
}
|
|
426
436
|
|
|
427
437
|
// Silent: auto-detect API keys and configure models.json (no output)
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "delimit-cli",
|
|
3
3
|
"mcpName": "io.github.delimit-ai/delimit-mcp-server",
|
|
4
|
-
"version": "3.11.
|
|
4
|
+
"version": "3.11.10",
|
|
5
5
|
"description": "Unify Claude Code, Codex, Cursor, and Gemini CLI with persistent context, governance, and multi-model debate.",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"files": [
|
|
8
8
|
"bin/",
|
|
9
9
|
"lib/",
|
|
10
|
+
"adapters/",
|
|
10
11
|
"gateway/",
|
|
11
12
|
"server.json",
|
|
12
13
|
"README.md",
|