delimit-cli 1.0.0 ā 2.1.1
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/.github/workflows/api-governance.yml +43 -0
- package/README.md +70 -113
- package/adapters/codex-skill.js +87 -0
- package/adapters/cursor-extension.js +190 -0
- package/adapters/gemini-action.js +93 -0
- package/adapters/openai-function.js +112 -0
- package/adapters/xai-plugin.js +151 -0
- package/bin/delimit-cli.js +921 -0
- package/bin/delimit.js +237 -1
- package/delimit.yml +19 -0
- package/hooks/evidence-status.sh +12 -0
- package/hooks/git/commit-msg +4 -0
- package/hooks/git/pre-commit +4 -0
- package/hooks/git/pre-push +4 -0
- package/hooks/install-hooks.sh +583 -0
- package/hooks/message-auth-hook.js +9 -0
- package/hooks/message-governance-hook.js +9 -0
- package/hooks/models/claude-post.js +4 -0
- package/hooks/models/claude-pre.js +4 -0
- package/hooks/models/codex-post.js +4 -0
- package/hooks/models/codex-pre.js +4 -0
- package/hooks/models/cursor-post.js +4 -0
- package/hooks/models/cursor-pre.js +4 -0
- package/hooks/models/gemini-post.js +4 -0
- package/hooks/models/gemini-pre.js +4 -0
- package/hooks/models/openai-post.js +4 -0
- package/hooks/models/openai-pre.js +4 -0
- package/hooks/models/windsurf-post.js +4 -0
- package/hooks/models/windsurf-pre.js +4 -0
- package/hooks/models/xai-post.js +4 -0
- package/hooks/models/xai-pre.js +4 -0
- package/hooks/post-bash-hook.js +13 -0
- package/hooks/post-mcp-hook.js +13 -0
- package/hooks/post-response-hook.js +4 -0
- package/hooks/post-tool-hook.js +126 -0
- package/hooks/post-write-hook.js +13 -0
- package/hooks/pre-bash-hook.js +30 -0
- package/hooks/pre-mcp-hook.js +13 -0
- package/hooks/pre-read-hook.js +13 -0
- package/hooks/pre-search-hook.js +13 -0
- package/hooks/pre-submit-hook.js +4 -0
- package/hooks/pre-task-hook.js +13 -0
- package/hooks/pre-tool-hook.js +121 -0
- package/hooks/pre-web-hook.js +13 -0
- package/hooks/pre-write-hook.js +31 -0
- package/hooks/test-hooks.sh +12 -0
- package/hooks/update-delimit.sh +6 -0
- package/lib/agent.js +509 -0
- package/lib/api-engine.js +156 -0
- package/lib/auth-setup.js +891 -0
- package/lib/decision-engine.js +474 -0
- package/lib/hooks-installer.js +416 -0
- package/lib/platform-adapters.js +353 -0
- package/lib/proxy-handler.js +114 -0
- package/package.json +38 -30
- package/scripts/infect.js +128 -0
- package/test-decision-engine.js +181 -0
- package/test-hook.js +27 -0
- package/dist/commands/validate.d.ts +0 -2
- package/dist/commands/validate.d.ts.map +0 -1
- package/dist/commands/validate.js +0 -106
- package/dist/commands/validate.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -71
- package/dist/index.js.map +0 -1
- package/dist/types/index.d.ts +0 -39
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -3
- package/dist/types/index.js.map +0 -1
- package/dist/utils/api.d.ts +0 -3
- package/dist/utils/api.d.ts.map +0 -1
- package/dist/utils/api.js +0 -64
- package/dist/utils/api.js.map +0 -1
- package/dist/utils/file.d.ts +0 -7
- package/dist/utils/file.d.ts.map +0 -1
- package/dist/utils/file.js +0 -69
- package/dist/utils/file.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -14
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -28
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/masker.d.ts +0 -14
- package/dist/utils/masker.d.ts.map +0 -1
- package/dist/utils/masker.js +0 -89
- package/dist/utils/masker.js.map +0 -1
- package/src/commands/validate.ts +0 -150
- package/src/index.ts +0 -80
- package/src/types/index.ts +0 -41
- package/src/utils/api.ts +0 -68
- package/src/utils/file.ts +0 -71
- package/src/utils/logger.ts +0 -27
- package/src/utils/masker.ts +0 -101
- package/test-sensitive.yaml +0 -109
- package/tsconfig.json +0 -23
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delimit⢠Platform Adapters
|
|
3
|
+
* Handles different naming conventions and integration methods for various AI platforms
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
10
|
+
class PlatformAdapter {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.HOME = os.homedir();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* GitHub Codex uses "Skills" instead of hooks
|
|
17
|
+
*/
|
|
18
|
+
setupCodexSkills() {
|
|
19
|
+
const codexConfig = {
|
|
20
|
+
configPath: path.join(this.HOME, '.codex', 'skills'),
|
|
21
|
+
configFile: 'skills.json',
|
|
22
|
+
format: 'skill'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const skills = {
|
|
26
|
+
"skills": [
|
|
27
|
+
{
|
|
28
|
+
"name": "delimit-governance",
|
|
29
|
+
"description": "Governance validation skill",
|
|
30
|
+
"type": "validation",
|
|
31
|
+
"handler": "/home/delimit/npm-delimit/adapters/codex-skill.js",
|
|
32
|
+
"triggers": ["pre-code-generation", "pre-suggestion"],
|
|
33
|
+
"enabled": true
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "delimit-security",
|
|
37
|
+
"description": "Security validation skill",
|
|
38
|
+
"type": "security",
|
|
39
|
+
"handler": "/home/delimit/npm-delimit/adapters/codex-security.js",
|
|
40
|
+
"enabled": true
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"commands": {
|
|
44
|
+
"governance": {
|
|
45
|
+
"description": "Check governance status",
|
|
46
|
+
"handler": "/home/delimit/npm-delimit/bin/delimit-cli.js",
|
|
47
|
+
"args": ["status"]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Create Codex skills directory
|
|
53
|
+
fs.mkdirSync(codexConfig.configPath, { recursive: true });
|
|
54
|
+
fs.writeFileSync(
|
|
55
|
+
path.join(codexConfig.configPath, codexConfig.configFile),
|
|
56
|
+
JSON.stringify(skills, null, 2)
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
console.log('ā Codex Skills configured');
|
|
60
|
+
return codexConfig;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Google Gemini uses "Extensions" and "Actions"
|
|
65
|
+
*/
|
|
66
|
+
setupGeminiExtensions() {
|
|
67
|
+
const geminiConfig = {
|
|
68
|
+
configPath: path.join(this.HOME, '.gemini', 'extensions'),
|
|
69
|
+
configFile: 'extensions.yaml',
|
|
70
|
+
format: 'extension'
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const extensions = `# Gemini Extensions Configuration
|
|
74
|
+
extensions:
|
|
75
|
+
- id: delimit-governance
|
|
76
|
+
name: Delimit Governance Extension
|
|
77
|
+
version: 2.0.0
|
|
78
|
+
actions:
|
|
79
|
+
- id: validate-code
|
|
80
|
+
name: Validate Code
|
|
81
|
+
trigger: before_code_generation
|
|
82
|
+
handler: /home/delimit/npm-delimit/adapters/gemini-action.js
|
|
83
|
+
- id: collect-evidence
|
|
84
|
+
name: Collect Evidence
|
|
85
|
+
trigger: after_response
|
|
86
|
+
handler: /home/delimit/npm-delimit/adapters/gemini-evidence.js
|
|
87
|
+
commands:
|
|
88
|
+
- command: "@governance"
|
|
89
|
+
description: Check governance status
|
|
90
|
+
action: run_script
|
|
91
|
+
script: /home/delimit/npm-delimit/bin/delimit-cli.js
|
|
92
|
+
args: ["status"]
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
// Create Gemini extensions directory
|
|
96
|
+
fs.mkdirSync(geminiConfig.configPath, { recursive: true });
|
|
97
|
+
fs.writeFileSync(
|
|
98
|
+
path.join(geminiConfig.configPath, geminiConfig.configFile),
|
|
99
|
+
extensions
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
console.log('ā Gemini Extensions configured');
|
|
103
|
+
return geminiConfig;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* xAI Grok uses "Plugins"
|
|
108
|
+
*/
|
|
109
|
+
setupXAIPlugins() {
|
|
110
|
+
const xaiConfig = {
|
|
111
|
+
configPath: path.join(this.HOME, '.xai', 'plugins'),
|
|
112
|
+
configFile: 'plugins.toml',
|
|
113
|
+
format: 'plugin'
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const plugins = `# xAI Grok Plugins Configuration
|
|
117
|
+
|
|
118
|
+
[[plugins]]
|
|
119
|
+
name = "delimit-governance"
|
|
120
|
+
version = "2.0.0"
|
|
121
|
+
description = "Delimit Governance Plugin"
|
|
122
|
+
entry_point = "/home/delimit/npm-delimit/adapters/xai-plugin.js"
|
|
123
|
+
|
|
124
|
+
[plugins.hooks]
|
|
125
|
+
pre_prompt = true
|
|
126
|
+
post_response = true
|
|
127
|
+
code_validation = true
|
|
128
|
+
|
|
129
|
+
[plugins.commands]
|
|
130
|
+
governance = { cmd = "/home/delimit/npm-delimit/bin/delimit-cli.js", args = ["status"] }
|
|
131
|
+
audit = { cmd = "/home/delimit/npm-delimit/bin/delimit-cli.js", args = ["audit"] }
|
|
132
|
+
`;
|
|
133
|
+
|
|
134
|
+
// Create xAI plugins directory
|
|
135
|
+
fs.mkdirSync(xaiConfig.configPath, { recursive: true });
|
|
136
|
+
fs.writeFileSync(
|
|
137
|
+
path.join(xaiConfig.configPath, xaiConfig.configFile),
|
|
138
|
+
plugins
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
console.log('ā xAI Plugins configured');
|
|
142
|
+
return xaiConfig;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* OpenAI uses "Functions" and "Tools"
|
|
147
|
+
*/
|
|
148
|
+
setupOpenAITools() {
|
|
149
|
+
const openaiConfig = {
|
|
150
|
+
configPath: path.join(this.HOME, '.openai', 'tools'),
|
|
151
|
+
configFile: 'tools.json',
|
|
152
|
+
format: 'function'
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const tools = {
|
|
156
|
+
"tools": [
|
|
157
|
+
{
|
|
158
|
+
"type": "function",
|
|
159
|
+
"function": {
|
|
160
|
+
"name": "delimit_governance_check",
|
|
161
|
+
"description": "Check governance compliance",
|
|
162
|
+
"parameters": {
|
|
163
|
+
"type": "object",
|
|
164
|
+
"properties": {
|
|
165
|
+
"action": {
|
|
166
|
+
"type": "string",
|
|
167
|
+
"description": "The action to validate"
|
|
168
|
+
},
|
|
169
|
+
"context": {
|
|
170
|
+
"type": "object",
|
|
171
|
+
"description": "Context for validation"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
"handler": "/home/delimit/npm-delimit/adapters/openai-function.js"
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
"plugins": [
|
|
180
|
+
{
|
|
181
|
+
"name": "delimit-governance",
|
|
182
|
+
"schema_version": "v1",
|
|
183
|
+
"api": {
|
|
184
|
+
"url": "http://localhost:7823/api",
|
|
185
|
+
"is_user_authenticated": false
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Create OpenAI tools directory
|
|
192
|
+
fs.mkdirSync(openaiConfig.configPath, { recursive: true });
|
|
193
|
+
fs.writeFileSync(
|
|
194
|
+
path.join(openaiConfig.configPath, openaiConfig.configFile),
|
|
195
|
+
JSON.stringify(tools, null, 2)
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
console.log('ā OpenAI Tools configured');
|
|
199
|
+
return openaiConfig;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Cursor uses "Extensions" (VSCode-based)
|
|
204
|
+
*/
|
|
205
|
+
setupCursorExtensions() {
|
|
206
|
+
const cursorConfig = {
|
|
207
|
+
configPath: path.join(this.HOME, '.cursor', 'extensions'),
|
|
208
|
+
configFile: 'extensions.json',
|
|
209
|
+
format: 'vscode-extension'
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const extensions = {
|
|
213
|
+
"extensions": [
|
|
214
|
+
{
|
|
215
|
+
"id": "delimit.governance",
|
|
216
|
+
"name": "Delimit Governance",
|
|
217
|
+
"version": "2.0.0",
|
|
218
|
+
"main": "/home/delimit/npm-delimit/adapters/cursor-extension.js",
|
|
219
|
+
"activationEvents": [
|
|
220
|
+
"onCommand:delimit.checkGovernance",
|
|
221
|
+
"onLanguage:javascript",
|
|
222
|
+
"onLanguage:typescript",
|
|
223
|
+
"onLanguage:python"
|
|
224
|
+
],
|
|
225
|
+
"contributes": {
|
|
226
|
+
"commands": [
|
|
227
|
+
{
|
|
228
|
+
"command": "delimit.checkGovernance",
|
|
229
|
+
"title": "Delimit: Check Governance"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"command": "delimit.switchMode",
|
|
233
|
+
"title": "Delimit: Switch Mode"
|
|
234
|
+
}
|
|
235
|
+
],
|
|
236
|
+
"configuration": {
|
|
237
|
+
"type": "object",
|
|
238
|
+
"properties": {
|
|
239
|
+
"delimit.mode": {
|
|
240
|
+
"type": "string",
|
|
241
|
+
"default": "advisory",
|
|
242
|
+
"enum": ["advisory", "guarded", "enforce"]
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
]
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// Create Cursor extensions directory
|
|
252
|
+
fs.mkdirSync(cursorConfig.configPath, { recursive: true });
|
|
253
|
+
fs.writeFileSync(
|
|
254
|
+
path.join(cursorConfig.configPath, cursorConfig.configFile),
|
|
255
|
+
JSON.stringify(extensions, null, 2)
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
console.log('ā Cursor Extensions configured');
|
|
259
|
+
return cursorConfig;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Windsurf uses "Workflows" and "Automations"
|
|
264
|
+
*/
|
|
265
|
+
setupWindsurfWorkflows() {
|
|
266
|
+
const windsurfConfig = {
|
|
267
|
+
configPath: path.join(this.HOME, '.windsurf', 'workflows'),
|
|
268
|
+
configFile: 'workflows.yaml',
|
|
269
|
+
format: 'workflow'
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const workflows = `# Windsurf Workflows Configuration
|
|
273
|
+
workflows:
|
|
274
|
+
- name: delimit-governance
|
|
275
|
+
description: Governance validation workflow
|
|
276
|
+
version: 2.0.0
|
|
277
|
+
triggers:
|
|
278
|
+
- event: pre_generation
|
|
279
|
+
handler: /home/delimit/npm-delimit/adapters/windsurf-trigger.js
|
|
280
|
+
- event: post_generation
|
|
281
|
+
handler: /home/delimit/npm-delimit/adapters/windsurf-validate.js
|
|
282
|
+
automations:
|
|
283
|
+
- name: validate_code
|
|
284
|
+
trigger: on_code_change
|
|
285
|
+
action: run_validation
|
|
286
|
+
- name: collect_evidence
|
|
287
|
+
trigger: on_commit
|
|
288
|
+
action: store_evidence
|
|
289
|
+
commands:
|
|
290
|
+
governance:
|
|
291
|
+
description: Check governance status
|
|
292
|
+
script: /home/delimit/npm-delimit/bin/delimit-cli.js
|
|
293
|
+
args: [status]
|
|
294
|
+
`;
|
|
295
|
+
|
|
296
|
+
// Create Windsurf workflows directory
|
|
297
|
+
fs.mkdirSync(windsurfConfig.configPath, { recursive: true });
|
|
298
|
+
fs.writeFileSync(
|
|
299
|
+
path.join(windsurfConfig.configPath, windsurfConfig.configFile),
|
|
300
|
+
workflows
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
console.log('ā Windsurf Workflows configured');
|
|
304
|
+
return windsurfConfig;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Anthropic Claude uses standard "hooks"
|
|
309
|
+
*/
|
|
310
|
+
setupClaudeHooks() {
|
|
311
|
+
// This is already handled by the main hooks.json
|
|
312
|
+
console.log('ā Claude hooks already configured');
|
|
313
|
+
return {
|
|
314
|
+
configPath: path.join(this.HOME, '.claude', 'hooks'),
|
|
315
|
+
configFile: 'hooks.json',
|
|
316
|
+
format: 'hook'
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Detect installed AI tools and setup appropriate configurations
|
|
322
|
+
*/
|
|
323
|
+
async setupAllPlatforms() {
|
|
324
|
+
const results = {};
|
|
325
|
+
|
|
326
|
+
// Check for each platform and set up if found
|
|
327
|
+
const platforms = [
|
|
328
|
+
{ name: 'claude', check: '.claude', setup: () => this.setupClaudeHooks() },
|
|
329
|
+
{ name: 'codex', check: '.codex', setup: () => this.setupCodexSkills() },
|
|
330
|
+
{ name: 'gemini', check: '.gemini', setup: () => this.setupGeminiExtensions() },
|
|
331
|
+
{ name: 'xai', check: '.xai', setup: () => this.setupXAIPlugins() },
|
|
332
|
+
{ name: 'openai', check: '.openai', setup: () => this.setupOpenAITools() },
|
|
333
|
+
{ name: 'cursor', check: '.cursor', setup: () => this.setupCursorExtensions() },
|
|
334
|
+
{ name: 'windsurf', check: '.windsurf', setup: () => this.setupWindsurfWorkflows() }
|
|
335
|
+
];
|
|
336
|
+
|
|
337
|
+
for (const platform of platforms) {
|
|
338
|
+
const platformPath = path.join(this.HOME, platform.check);
|
|
339
|
+
// Always create the configuration, even if platform isn't installed yet
|
|
340
|
+
// This ensures it will work when the platform is installed later
|
|
341
|
+
try {
|
|
342
|
+
results[platform.name] = platform.setup();
|
|
343
|
+
console.log(`ā ${platform.name} configuration created`);
|
|
344
|
+
} catch (error) {
|
|
345
|
+
console.error(`ā ļø Failed to configure ${platform.name}: ${error.message}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return results;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
module.exports = PlatformAdapter;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Delimit Proxy Handler - Intercepts AI tool commands
|
|
5
|
+
* This module wraps AI tools (claude, gemini, codex) with governance
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { spawn } = require('child_process');
|
|
9
|
+
const axios = require('axios');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
|
|
13
|
+
const AGENT_URL = `http://127.0.0.1:${process.env.DELIMIT_AGENT_PORT || 7823}`;
|
|
14
|
+
|
|
15
|
+
async function proxyAITool(tool, args) {
|
|
16
|
+
console.log(`\x1b[34m\x1b[1m[Delimit]\x1b[0m Governance check for ${tool}...`);
|
|
17
|
+
|
|
18
|
+
// Gather context for governance decision
|
|
19
|
+
const context = {
|
|
20
|
+
command: `ai-tool-${tool}`,
|
|
21
|
+
pwd: process.cwd(),
|
|
22
|
+
gitBranch: 'unknown',
|
|
23
|
+
files: [],
|
|
24
|
+
tool: tool,
|
|
25
|
+
args: args.join(' ')
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Try to get Git branch if in repo
|
|
29
|
+
try {
|
|
30
|
+
const { execSync } = require('child_process');
|
|
31
|
+
context.gitBranch = execSync('git branch --show-current 2>/dev/null').toString().trim() || 'unknown';
|
|
32
|
+
} catch (e) {}
|
|
33
|
+
|
|
34
|
+
// Check if agent is running
|
|
35
|
+
let decision;
|
|
36
|
+
try {
|
|
37
|
+
const response = await axios.post(`${AGENT_URL}/evaluate`, context);
|
|
38
|
+
decision = response.data;
|
|
39
|
+
} catch (e) {
|
|
40
|
+
// Agent not running - allow with warning
|
|
41
|
+
console.log(`\x1b[33m\x1b[1m[Delimit WARNING]\x1b[0m Agent not running - proceeding without governance`);
|
|
42
|
+
decision = { action: 'allow' };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Display decision
|
|
46
|
+
if (decision.message) {
|
|
47
|
+
console.log(decision.message);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Handle the decision
|
|
51
|
+
if (decision.action === 'block') {
|
|
52
|
+
console.error(`\x1b[31m\x1b[1m[Delimit BLOCKED]\x1b[0m ${tool} execution blocked by governance policy`);
|
|
53
|
+
console.error(`Run 'delimit explain last' for details`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
} else if (decision.action === 'prompt') {
|
|
56
|
+
// In non-interactive mode, treat as block
|
|
57
|
+
console.error(`\x1b[33m\x1b[1m[Delimit GUARDED]\x1b[0m ${tool} requires confirmation`);
|
|
58
|
+
console.error(`Use --force flag to bypass or switch to advisory mode`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Find the real tool
|
|
63
|
+
const originalPath = process.env.PATH.replace(/[^:]*delimit[^:]*:?/g, '');
|
|
64
|
+
const realTool = findExecutable(tool, originalPath);
|
|
65
|
+
|
|
66
|
+
if (!realTool) {
|
|
67
|
+
console.error(`\x1b[31m\x1b[1m[Delimit ERROR]\x1b[0m Original ${tool} not found`);
|
|
68
|
+
process.exit(127);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Execute the real tool
|
|
72
|
+
console.log(`\x1b[32m\x1b[1m[Delimit ALLOWED]\x1b[0m Executing ${tool}...`);
|
|
73
|
+
const child = spawn(realTool, args, {
|
|
74
|
+
stdio: 'inherit',
|
|
75
|
+
env: { ...process.env, DELIMIT_WRAPPED: 'true' }
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
child.on('exit', (code) => {
|
|
79
|
+
process.exit(code || 0);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function findExecutable(command, searchPath) {
|
|
84
|
+
const paths = searchPath.split(':');
|
|
85
|
+
for (const dir of paths) {
|
|
86
|
+
const fullPath = path.join(dir, command);
|
|
87
|
+
try {
|
|
88
|
+
fs.accessSync(fullPath, fs.constants.X_OK);
|
|
89
|
+
return fullPath;
|
|
90
|
+
} catch (e) {
|
|
91
|
+
// Continue searching
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Export for use as module
|
|
98
|
+
module.exports = { proxyAITool };
|
|
99
|
+
|
|
100
|
+
// If run directly, handle as CLI
|
|
101
|
+
if (require.main === module) {
|
|
102
|
+
const tool = process.argv[2];
|
|
103
|
+
const args = process.argv.slice(3);
|
|
104
|
+
|
|
105
|
+
if (!tool) {
|
|
106
|
+
console.error('Usage: proxy-handler <tool> [args...]');
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
proxyAITool(tool, args).catch(err => {
|
|
111
|
+
console.error(`\x1b[31m\x1b[1m[Delimit ERROR]\x1b[0m`, err.message);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
});
|
|
114
|
+
}
|
package/package.json
CHANGED
|
@@ -1,50 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "delimit-cli",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "
|
|
3
|
+
"version": "2.1.1",
|
|
4
|
+
"description": "ESLint for API contracts ā detect breaking changes, enforce semver, and generate migration guides for OpenAPI specs",
|
|
5
|
+
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"delimit": "./bin/delimit.js"
|
|
7
|
+
"delimit": "./bin/delimit-cli.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
10
|
+
"postinstall": "echo 'Run \"delimit install\" to set up governance'",
|
|
11
|
+
"install": "bash ./hooks/install-hooks.sh install",
|
|
12
|
+
"install-mcp": "bash ./hooks/install-hooks.sh mcp-only",
|
|
13
|
+
"test-mcp": "bash ./hooks/install-hooks.sh troubleshoot",
|
|
14
|
+
"fix-mcp": "bash ./hooks/install-hooks.sh fix-mcp",
|
|
15
|
+
"test": "echo 'Governance is context-aware' && exit 0"
|
|
14
16
|
},
|
|
15
17
|
"keywords": [
|
|
16
|
-
"api",
|
|
17
18
|
"openapi",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
19
|
+
"swagger",
|
|
20
|
+
"api",
|
|
21
|
+
"breaking-changes",
|
|
22
|
+
"semver",
|
|
23
|
+
"lint",
|
|
24
|
+
"linter",
|
|
25
|
+
"api-governance",
|
|
26
|
+
"api-contracts",
|
|
27
|
+
"ci-cd",
|
|
28
|
+
"github-actions",
|
|
29
|
+
"migration",
|
|
30
|
+
"diff",
|
|
31
|
+
"schema-validation",
|
|
32
|
+
"api-versioning",
|
|
33
|
+
"eslint",
|
|
21
34
|
"delimit",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
35
|
+
"openapi-diff",
|
|
36
|
+
"api-linter",
|
|
37
|
+
"contract-testing"
|
|
24
38
|
],
|
|
25
|
-
"author": "Delimit.ai",
|
|
39
|
+
"author": "Delimit AI <hello@delimit.ai>",
|
|
26
40
|
"license": "MIT",
|
|
27
|
-
"homepage": "https://
|
|
41
|
+
"homepage": "https://delimit.ai",
|
|
28
42
|
"repository": {
|
|
29
43
|
"type": "git",
|
|
30
|
-
"url": "https://github.com/delimit-ai/delimit
|
|
31
|
-
},
|
|
32
|
-
"bugs": {
|
|
33
|
-
"url": "https://github.com/delimit-ai/delimit-cli/issues"
|
|
44
|
+
"url": "https://github.com/delimit-ai/delimit.git"
|
|
34
45
|
},
|
|
35
46
|
"dependencies": {
|
|
36
|
-
"
|
|
47
|
+
"commander": "^9.0.0",
|
|
48
|
+
"axios": "^1.0.0",
|
|
37
49
|
"chalk": "^4.1.2",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"@types/js-yaml": "^4.0.9",
|
|
43
|
-
"@types/node": "^20.8.0",
|
|
44
|
-
"ts-node": "^10.9.1",
|
|
45
|
-
"typescript": "^5.2.2"
|
|
50
|
+
"inquirer": "^8.2.0",
|
|
51
|
+
"express": "^4.18.0",
|
|
52
|
+
"js-yaml": "^4.1.0",
|
|
53
|
+
"minimatch": "^5.1.0"
|
|
46
54
|
},
|
|
47
55
|
"engines": {
|
|
48
|
-
"node": ">=
|
|
56
|
+
"node": ">=14.0.0"
|
|
49
57
|
}
|
|
50
58
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const HOME_DIR = os.homedir();
|
|
9
|
+
const DELIMIT_HOME = path.join(HOME_DIR, '.delimit');
|
|
10
|
+
const SHIM_DIR = path.join(DELIMIT_HOME, 'shims');
|
|
11
|
+
const HOOKS_DIR = path.join(DELIMIT_HOME, 'hooks');
|
|
12
|
+
const BIN_DIR = path.join(DELIMIT_HOME, 'bin');
|
|
13
|
+
|
|
14
|
+
console.log('\nšµ Installing Delimit Governance Layer...');
|
|
15
|
+
console.log('ā ļø WARNING: This will modify your system permanently.\n');
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
// 1. Create directory structure
|
|
19
|
+
[DELIMIT_HOME, SHIM_DIR, HOOKS_DIR, BIN_DIR].forEach(dir => {
|
|
20
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
21
|
+
});
|
|
22
|
+
console.log('ā Created ~/.delimit directory structure');
|
|
23
|
+
|
|
24
|
+
// 2. Copy the main Delimit CLI
|
|
25
|
+
const cliSource = path.join(__dirname, '..', 'bin', 'delimit.js');
|
|
26
|
+
const cliDest = path.join(BIN_DIR, 'delimit');
|
|
27
|
+
fs.copyFileSync(cliSource, cliDest);
|
|
28
|
+
fs.chmodSync(cliDest, '755');
|
|
29
|
+
console.log('ā Installed Delimit CLI');
|
|
30
|
+
|
|
31
|
+
// 3. Install global Git hooks
|
|
32
|
+
const preCommitHook = `#!/bin/sh
|
|
33
|
+
# Delimit Governance Hook - Pre-commit
|
|
34
|
+
${cliDest} pre-commit-check`;
|
|
35
|
+
|
|
36
|
+
const prePushHook = `#!/bin/sh
|
|
37
|
+
# Delimit Governance Hook - Pre-push
|
|
38
|
+
${cliDest} pre-push-check`;
|
|
39
|
+
|
|
40
|
+
fs.writeFileSync(path.join(HOOKS_DIR, 'pre-commit'), preCommitHook);
|
|
41
|
+
fs.writeFileSync(path.join(HOOKS_DIR, 'pre-push'), prePushHook);
|
|
42
|
+
fs.chmodSync(path.join(HOOKS_DIR, 'pre-commit'), '755');
|
|
43
|
+
fs.chmodSync(path.join(HOOKS_DIR, 'pre-push'), '755');
|
|
44
|
+
|
|
45
|
+
execSync(`git config --global core.hooksPath ${HOOKS_DIR}`);
|
|
46
|
+
console.log('ā Installed global Git hooks');
|
|
47
|
+
|
|
48
|
+
// 4. Create AI tool shims
|
|
49
|
+
const aiTools = ['claude', 'gemini', 'codex', 'copilot', 'gh', 'openai', 'anthropic'];
|
|
50
|
+
aiTools.forEach(tool => {
|
|
51
|
+
const shimContent = `#!/bin/sh
|
|
52
|
+
# Delimit Governance Shim for ${tool}
|
|
53
|
+
exec ${cliDest} proxy --tool=${tool} -- "$@"`;
|
|
54
|
+
|
|
55
|
+
const shimPath = path.join(SHIM_DIR, tool);
|
|
56
|
+
fs.writeFileSync(shimPath, shimContent);
|
|
57
|
+
fs.chmodSync(shimPath, '755');
|
|
58
|
+
});
|
|
59
|
+
console.log(`ā Created ${aiTools.length} AI tool shims`);
|
|
60
|
+
|
|
61
|
+
// 5. Inject into shell profiles
|
|
62
|
+
const shellProfiles = [
|
|
63
|
+
'.bashrc',
|
|
64
|
+
'.zshrc',
|
|
65
|
+
'.profile',
|
|
66
|
+
'.bash_profile'
|
|
67
|
+
].map(f => path.join(HOME_DIR, f));
|
|
68
|
+
|
|
69
|
+
const pathInjection = `
|
|
70
|
+
# Delimit Governance Layer - DO NOT REMOVE
|
|
71
|
+
export PATH="${SHIM_DIR}:$PATH"
|
|
72
|
+
export DELIMIT_ACTIVE=true
|
|
73
|
+
|
|
74
|
+
# Show governance status on shell start
|
|
75
|
+
if [ -t 1 ]; then
|
|
76
|
+
echo -e "\\033[34m\\033[1m[Delimit]\\033[0m Governance active. All AI tools and Git operations are monitored."
|
|
77
|
+
fi
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
let injected = false;
|
|
81
|
+
shellProfiles.forEach(profilePath => {
|
|
82
|
+
if (fs.existsSync(profilePath)) {
|
|
83
|
+
const content = fs.readFileSync(profilePath, 'utf8');
|
|
84
|
+
if (!content.includes('Delimit Governance Layer')) {
|
|
85
|
+
fs.appendFileSync(profilePath, pathInjection);
|
|
86
|
+
console.log(`ā Injected into ${path.basename(profilePath)}`);
|
|
87
|
+
injected = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (!injected) {
|
|
93
|
+
// Create a .profile if nothing exists
|
|
94
|
+
const profilePath = path.join(HOME_DIR, '.profile');
|
|
95
|
+
fs.writeFileSync(profilePath, pathInjection);
|
|
96
|
+
console.log('ā Created .profile with governance');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 6. Create global command link
|
|
100
|
+
try {
|
|
101
|
+
const globalBin = '/usr/local/bin/delimit';
|
|
102
|
+
if (fs.existsSync('/usr/local/bin')) {
|
|
103
|
+
if (fs.existsSync(globalBin)) {
|
|
104
|
+
fs.unlinkSync(globalBin);
|
|
105
|
+
}
|
|
106
|
+
fs.symlinkSync(cliDest, globalBin);
|
|
107
|
+
console.log('ā Created global delimit command');
|
|
108
|
+
}
|
|
109
|
+
} catch (e) {
|
|
110
|
+
// Ignore if can't create global link
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
console.log('\n' + 'ā'.repeat(60));
|
|
114
|
+
console.log('š¢ DELIMIT GOVERNANCE LAYER INSTALLED SUCCESSFULLY');
|
|
115
|
+
console.log('ā'.repeat(60));
|
|
116
|
+
console.log('\nā” IMPORTANT: Restart your terminal or run:');
|
|
117
|
+
console.log(' source ~/.bashrc (or ~/.zshrc)\n');
|
|
118
|
+
console.log('š Check status with: delimit status');
|
|
119
|
+
console.log('š Documentation: https://delimit.ai\n');
|
|
120
|
+
console.log('ā ļø WARNING: Governance is now mandatory.');
|
|
121
|
+
console.log(' All AI tools and Git operations are monitored.\n');
|
|
122
|
+
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error('\nā Installation failed:', error.message);
|
|
125
|
+
console.error('\nTry installing globally with sudo:');
|
|
126
|
+
console.error(' sudo npm install -g delimit\n');
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|